Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b575443c4 | ||
|
|
aff68c35a4 | ||
|
|
90fd01423a | ||
|
|
c9f892cd6a | ||
|
|
cccd675148 |
2
.github/generate_change_log.sh
vendored
2
.github/generate_change_log.sh
vendored
@@ -15,7 +15,7 @@ while read line; do
|
||||
if [[ $line == *"$version_prefix"* ]] && [ $start == 1 ]; then
|
||||
break;
|
||||
fi
|
||||
if [ $start == 1 ] && [[ $line != "" ]]; then
|
||||
if [ $start == 1 ]; then
|
||||
CHANGE_LOG+="$line\n"
|
||||
fi
|
||||
done < ${change_log_file}
|
||||
|
||||
183
.github/workflows/pre-release.yml
vendored
Normal file
183
.github/workflows/pre-release.yml
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
name: Pre Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'pre-*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Release
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
os: [ubuntu-18.04, macos-11, windows-2019]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
# In order:
|
||||
# * Module download cache
|
||||
# * Build cache (Linux)
|
||||
# * Build cache (Mac)
|
||||
# * Build cache (Windows)
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
~/Library/Caches/go-build
|
||||
%LocalAppData%\go-build
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
# ==============================
|
||||
# Linux/Macos/Windows Build
|
||||
# ==============================
|
||||
|
||||
- name: Build Binary for ${{matrix.os}}
|
||||
run: make geth
|
||||
|
||||
# ==============================
|
||||
# Upload artifacts
|
||||
# ==============================
|
||||
|
||||
- name: Upload Linux Build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
with:
|
||||
name: linux
|
||||
path: ./build/bin/geth
|
||||
|
||||
- name: Upload MacOS Build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.os == 'macos-11'
|
||||
with:
|
||||
name: macos
|
||||
path: ./build/bin/geth
|
||||
|
||||
- name: Upload Windows Build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.os == 'windows-2019'
|
||||
with:
|
||||
name: windows
|
||||
path: ./build/bin/geth.exe
|
||||
|
||||
release:
|
||||
name: Release
|
||||
needs: build
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Set Env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# ==============================
|
||||
# Download artifacts
|
||||
# ==============================
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux
|
||||
path: ./linux
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: macos
|
||||
path: ./macos
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: windows
|
||||
path: ./windows
|
||||
|
||||
- name: Download Config File
|
||||
run: |
|
||||
. ./.github/release.env
|
||||
echo "mainnet.zip url: $MAINNET_FILE_URL"
|
||||
echo "testnet.zip url: $TESTNET_FILE_URL"
|
||||
curl -L $MAINNET_FILE_URL -o ./mainnet.zip
|
||||
curl -L $TESTNET_FILE_URL -o ./testnet.zip
|
||||
|
||||
# ==============================
|
||||
# Create release
|
||||
# ==============================
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
body: |
|
||||
versing: ${{ env.RELEASE_VERSION}}
|
||||
git commit: ${{ github.sha }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
|
||||
# Check downloaded files
|
||||
- run: ls
|
||||
|
||||
- name: Upload Release Asset - Linux
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./linux/geth
|
||||
asset_name: geth_linux
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Release Asset - MacOS
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./macos/geth
|
||||
asset_name: geth_mac
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Release Asset - Windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./windows/geth.exe
|
||||
asset_name: geth_windows.exe
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Release Asset - MAINNET.ZIP
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./mainnet.zip
|
||||
asset_name: mainnet.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload Release Asset - TESTNET.ZIP
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./testnet.zip
|
||||
asset_name: testnet.zip
|
||||
asset_content_type: application/zip
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -15,14 +15,14 @@ jobs:
|
||||
os: [ubuntu-18.04, macos-11, windows-2019]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
# In order:
|
||||
|
||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,4 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## v1.1.6
|
||||
BUGFIX
|
||||
* [\#582](https://github.com/binance-chain/bsc/pull/582) the DoS vulnerabilities fixed in go-ethereum v1.10.9
|
||||
|
||||
IMPROVEMENT
|
||||
* [\#578](https://github.com/binance-chain/bsc/pull/578) reduce memory allocation and upgrade snappy version
|
||||
|
||||
FEATURES
|
||||
* [\#570](https://github.com/binance-chain/bsc/pull/570) reannounce local pending transactions
|
||||
|
||||
## v1.1.5
|
||||
BUGFIX
|
||||
* [\#509](https://github.com/binance-chain/bsc/pull/509) fix graceful shutdown bug
|
||||
|
||||
@@ -90,6 +90,7 @@ var (
|
||||
utils.TxPoolAccountQueueFlag,
|
||||
utils.TxPoolGlobalQueueFlag,
|
||||
utils.TxPoolLifetimeFlag,
|
||||
utils.TxPoolReannounceTimeFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.ExitWhenSyncedFlag,
|
||||
utils.GCModeFlag,
|
||||
|
||||
@@ -108,6 +108,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
||||
utils.TxPoolAccountQueueFlag,
|
||||
utils.TxPoolGlobalQueueFlag,
|
||||
utils.TxPoolLifetimeFlag,
|
||||
utils.TxPoolReannounceTimeFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -398,6 +398,11 @@ var (
|
||||
Usage: "Maximum amount of time non-executable transaction are queued",
|
||||
Value: ethconfig.Defaults.TxPool.Lifetime,
|
||||
}
|
||||
TxPoolReannounceTimeFlag = cli.DurationFlag{
|
||||
Name: "txpool.reannouncetime",
|
||||
Usage: "Duration for announcing local pending transactions again (default = 10 years, minimum = 1 minute)",
|
||||
Value: ethconfig.Defaults.TxPool.ReannounceTime,
|
||||
}
|
||||
// Performance tuning settings
|
||||
CacheFlag = cli.IntFlag{
|
||||
Name: "cache",
|
||||
@@ -1410,6 +1415,9 @@ func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
|
||||
if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) {
|
||||
cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(TxPoolReannounceTimeFlag.Name) {
|
||||
cfg.ReannounceTime = ctx.GlobalDuration(TxPoolReannounceTimeFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func setEthash(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||
|
||||
@@ -24,6 +24,9 @@ import (
|
||||
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
|
||||
type NewTxsEvent struct{ Txs []*types.Transaction }
|
||||
|
||||
// ReannoTxsEvent is posted when a batch of local pending transactions exceed a specified duration.
|
||||
type ReannoTxsEvent struct{ Txs []*types.Transaction }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ const (
|
||||
// more expensive to propagate; larger transactions also take more resources
|
||||
// to validate whether they fit into the pool or not.
|
||||
txMaxSize = 4 * txSlotSize // 128KB
|
||||
|
||||
// txReannoMaxNum is the maximum number of transactions a reannounce action can include.
|
||||
txReannoMaxNum = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -88,6 +91,7 @@ var (
|
||||
var (
|
||||
evictionInterval = time.Minute // Time interval to check for evictable transactions
|
||||
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
|
||||
reannounceInterval = time.Minute // Time interval to check for reannounce transactions
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -152,7 +156,8 @@ type TxPoolConfig struct {
|
||||
AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account
|
||||
GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts
|
||||
|
||||
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
|
||||
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
|
||||
ReannounceTime time.Duration // Duration for announcing local pending transactions again
|
||||
}
|
||||
|
||||
// DefaultTxPoolConfig contains the default configurations for the transaction
|
||||
@@ -169,7 +174,8 @@ var DefaultTxPoolConfig = TxPoolConfig{
|
||||
AccountQueue: 64,
|
||||
GlobalQueue: 1024,
|
||||
|
||||
Lifetime: 3 * time.Hour,
|
||||
Lifetime: 3 * time.Hour,
|
||||
ReannounceTime: 10 * 365 * 24 * time.Hour,
|
||||
}
|
||||
|
||||
// sanitize checks the provided user configurations and changes anything that's
|
||||
@@ -208,6 +214,10 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
|
||||
conf.Lifetime = DefaultTxPoolConfig.Lifetime
|
||||
}
|
||||
if conf.ReannounceTime < time.Minute {
|
||||
log.Warn("Sanitizing invalid txpool reannounce time", "provided", conf.ReannounceTime, "updated", time.Minute)
|
||||
conf.ReannounceTime = time.Minute
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
@@ -219,14 +229,15 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||
// current state) and future transactions. Transactions move between those
|
||||
// two states over time as they are received and processed.
|
||||
type TxPool struct {
|
||||
config TxPoolConfig
|
||||
chainconfig *params.ChainConfig
|
||||
chain blockChain
|
||||
gasPrice *big.Int
|
||||
txFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
signer types.Signer
|
||||
mu sync.RWMutex
|
||||
config TxPoolConfig
|
||||
chainconfig *params.ChainConfig
|
||||
chain blockChain
|
||||
gasPrice *big.Int
|
||||
txFeed event.Feed
|
||||
reannoTxFeed event.Feed // Event feed for announcing transactions again
|
||||
scope event.SubscriptionScope
|
||||
signer types.Signer
|
||||
mu sync.RWMutex
|
||||
|
||||
istanbul bool // Fork indicator whether we are in the istanbul stage.
|
||||
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||
@@ -323,14 +334,16 @@ func (pool *TxPool) loop() {
|
||||
var (
|
||||
prevPending, prevQueued, prevStales int
|
||||
// Start the stats reporting and transaction eviction tickers
|
||||
report = time.NewTicker(statsReportInterval)
|
||||
evict = time.NewTicker(evictionInterval)
|
||||
journal = time.NewTicker(pool.config.Rejournal)
|
||||
report = time.NewTicker(statsReportInterval)
|
||||
evict = time.NewTicker(evictionInterval)
|
||||
reannounce = time.NewTicker(reannounceInterval)
|
||||
journal = time.NewTicker(pool.config.Rejournal)
|
||||
// Track the previous head headers for transaction reorgs
|
||||
head = pool.chain.CurrentBlock()
|
||||
)
|
||||
defer report.Stop()
|
||||
defer evict.Stop()
|
||||
defer reannounce.Stop()
|
||||
defer journal.Stop()
|
||||
|
||||
for {
|
||||
@@ -378,6 +391,33 @@ func (pool *TxPool) loop() {
|
||||
}
|
||||
pool.mu.Unlock()
|
||||
|
||||
case <-reannounce.C:
|
||||
pool.mu.RLock()
|
||||
reannoTxs := func() []*types.Transaction {
|
||||
txs := make([]*types.Transaction, 0)
|
||||
for addr, list := range pool.pending {
|
||||
if !pool.locals.contains(addr) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, tx := range list.Flatten() {
|
||||
// Default ReannounceTime is 10 years, won't announce by default.
|
||||
if time.Since(tx.Time()) < pool.config.ReannounceTime {
|
||||
break
|
||||
}
|
||||
txs = append(txs, tx)
|
||||
if len(txs) >= txReannoMaxNum {
|
||||
return txs
|
||||
}
|
||||
}
|
||||
}
|
||||
return txs
|
||||
}()
|
||||
pool.mu.RUnlock()
|
||||
if len(reannoTxs) > 0 {
|
||||
pool.reannoTxFeed.Send(ReannoTxsEvent{reannoTxs})
|
||||
}
|
||||
|
||||
// Handle local transaction journal rotation
|
||||
case <-journal.C:
|
||||
if pool.journal != nil {
|
||||
@@ -412,6 +452,12 @@ func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscripti
|
||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeReannoTxsEvent registers a subscription of ReannoTxsEvent and
|
||||
// starts sending event to the given channel.
|
||||
func (pool *TxPool) SubscribeReannoTxsEvent(ch chan<- ReannoTxsEvent) event.Subscription {
|
||||
return pool.scope.Track(pool.reannoTxFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// GasPrice returns the current gas price enforced by the transaction pool.
|
||||
func (pool *TxPool) GasPrice() *big.Int {
|
||||
pool.mu.RLock()
|
||||
|
||||
@@ -1933,6 +1933,47 @@ func TestTransactionSlotCount(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the local pending transaction announced again correctly.
|
||||
func TestTransactionPendingReannouce(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
// This ReannounceTime will be modified to time.Minute when creating tx_pool.
|
||||
config.ReannounceTime = time.Second
|
||||
reannounceInterval = time.Second
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
// Modify ReannounceTime to trigger quicker.
|
||||
pool.config.ReannounceTime = time.Second
|
||||
defer pool.Stop()
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
account := crypto.PubkeyToAddress(key.PublicKey)
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
events := make(chan ReannoTxsEvent, testTxPoolConfig.AccountQueue)
|
||||
sub := pool.reannoTxFeed.Subscribe(events)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Generate a batch of transactions and add to tx_pool locally.
|
||||
txs := make([]*types.Transaction, 0, testTxPoolConfig.AccountQueue)
|
||||
for i := uint64(0); i < testTxPoolConfig.AccountQueue; i++ {
|
||||
txs = append(txs, transaction(i, 100000, key))
|
||||
}
|
||||
pool.AddLocals(txs)
|
||||
|
||||
select {
|
||||
case ev := <-events:
|
||||
t.Logf("received reannouce event, txs length: %d", len(ev.Txs))
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Errorf("reannouce event not fired")
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the speed of validating the contents of the pending queue of the
|
||||
// transaction pool.
|
||||
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
|
||||
|
||||
@@ -82,6 +82,11 @@ type TxData interface {
|
||||
setSignatureValues(chainID, v, r, s *big.Int)
|
||||
}
|
||||
|
||||
// Time returns transaction's time
|
||||
func (tx *Transaction) Time() time.Time {
|
||||
return tx.time
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
if tx.Type() == LegacyTxType {
|
||||
|
||||
@@ -73,6 +73,10 @@ type txPool interface {
|
||||
// SubscribeNewTxsEvent should return an event subscription of
|
||||
// NewTxsEvent and send events to the given channel.
|
||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||
|
||||
// SubscribeReannoTxsEvent should return an event subscription of
|
||||
// ReannoTxsEvent and send events to the given channel.
|
||||
SubscribeReannoTxsEvent(chan<- core.ReannoTxsEvent) event.Subscription
|
||||
}
|
||||
|
||||
// handlerConfig is the collection of initialization parameters to create a full
|
||||
@@ -120,6 +124,8 @@ type handler struct {
|
||||
eventMux *event.TypeMux
|
||||
txsCh chan core.NewTxsEvent
|
||||
txsSub event.Subscription
|
||||
reannoTxsCh chan core.ReannoTxsEvent
|
||||
reannoTxsSub event.Subscription
|
||||
minedBlockSub *event.TypeMuxSubscription
|
||||
|
||||
whitelist map[uint64]common.Hash
|
||||
@@ -432,6 +438,12 @@ func (h *handler) Start(maxPeers int) {
|
||||
h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh)
|
||||
go h.txBroadcastLoop()
|
||||
|
||||
// announce local pending transactions again
|
||||
h.wg.Add(1)
|
||||
h.reannoTxsCh = make(chan core.ReannoTxsEvent, txChanSize)
|
||||
h.reannoTxsSub = h.txpool.SubscribeReannoTxsEvent(h.reannoTxsCh)
|
||||
go h.txReannounceLoop()
|
||||
|
||||
// broadcast mined blocks
|
||||
h.wg.Add(1)
|
||||
h.minedBlockSub = h.eventMux.Subscribe(core.NewMinedBlockEvent{})
|
||||
@@ -445,6 +457,7 @@ func (h *handler) Start(maxPeers int) {
|
||||
|
||||
func (h *handler) Stop() {
|
||||
h.txsSub.Unsubscribe() // quits txBroadcastLoop
|
||||
h.reannoTxsSub.Unsubscribe() // quits txReannounceLoop
|
||||
h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
|
||||
|
||||
// Quit chainSync and txsync64.
|
||||
@@ -549,6 +562,31 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) {
|
||||
"tx packs", directPeers, "broadcast txs", directCount)
|
||||
}
|
||||
|
||||
// ReannounceTransactions will announce a batch of local pending transactions
|
||||
// to a square root of all peers.
|
||||
func (h *handler) ReannounceTransactions(txs types.Transactions) {
|
||||
var (
|
||||
annoCount int // Count of announcements made
|
||||
annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
|
||||
)
|
||||
|
||||
// Announce transactions hash to a batch of peers
|
||||
peersCount := uint(math.Sqrt(float64(h.peers.len())))
|
||||
peers := h.peers.headPeers(peersCount)
|
||||
for _, tx := range txs {
|
||||
for _, peer := range peers {
|
||||
annos[peer] = append(annos[peer], tx.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
for peer, hashes := range annos {
|
||||
annoCount += len(hashes)
|
||||
peer.AsyncSendPooledTransactionHashes(hashes)
|
||||
}
|
||||
log.Debug("Transaction reannounce", "txs", len(txs),
|
||||
"announce packs", peersCount, "announced hashes", annoCount)
|
||||
}
|
||||
|
||||
// minedBroadcastLoop sends mined blocks to connected peers.
|
||||
func (h *handler) minedBroadcastLoop() {
|
||||
defer h.wg.Done()
|
||||
@@ -573,3 +611,16 @@ func (h *handler) txBroadcastLoop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// txReannounceLoop announces local pending transactions to connected peers again.
|
||||
func (h *handler) txReannounceLoop() {
|
||||
defer h.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case event := <-h.reannoTxsCh:
|
||||
h.ReannounceTransactions(event.Txs)
|
||||
case <-h.reannoTxsSub.Err():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,6 +450,59 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that local pending transactions get propagated to peers.
|
||||
func TestTransactionPendingReannounce(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a source handler to announce transactions from and a sink handler
|
||||
// to receive them.
|
||||
source := newTestHandler()
|
||||
defer source.close()
|
||||
|
||||
sink := newTestHandler()
|
||||
defer sink.close()
|
||||
sink.handler.acceptTxs = 1 // mark synced to accept transactions
|
||||
|
||||
sourcePipe, sinkPipe := p2p.MsgPipe()
|
||||
defer sourcePipe.Close()
|
||||
defer sinkPipe.Close()
|
||||
|
||||
sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sourcePipe, source.txpool)
|
||||
sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, sink.txpool)
|
||||
defer sourcePeer.Close()
|
||||
defer sinkPeer.Close()
|
||||
|
||||
go source.handler.runEthPeer(sourcePeer, func(peer *eth.Peer) error {
|
||||
return eth.Handle((*ethHandler)(source.handler), peer)
|
||||
})
|
||||
go sink.handler.runEthPeer(sinkPeer, func(peer *eth.Peer) error {
|
||||
return eth.Handle((*ethHandler)(sink.handler), peer)
|
||||
})
|
||||
|
||||
// Subscribe transaction pools
|
||||
txCh := make(chan core.NewTxsEvent, 1024)
|
||||
sub := sink.txpool.SubscribeNewTxsEvent(txCh)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
txs := make([]*types.Transaction, 64)
|
||||
for nonce := range txs {
|
||||
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil)
|
||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
|
||||
txs[nonce] = tx
|
||||
}
|
||||
source.txpool.ReannouceTransactions(txs)
|
||||
|
||||
for arrived := 0; arrived < len(txs); {
|
||||
select {
|
||||
case event := <-txCh:
|
||||
arrived += len(event.Txs)
|
||||
case <-time.NewTimer(time.Second).C:
|
||||
t.Errorf("sink: transaction propagation timed out: have %d, want %d", arrived, len(txs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that post eth protocol handshake, clients perform a mutual checkpoint
|
||||
// challenge to validate each other's chains. Hash mismatches, or missing ones
|
||||
// during a fast sync should lead to the peer getting dropped.
|
||||
|
||||
@@ -48,8 +48,9 @@ var (
|
||||
type testTxPool struct {
|
||||
pool map[common.Hash]*types.Transaction // Hash map of collected transactions
|
||||
|
||||
txFeed event.Feed // Notification feed to allow waiting for inclusion
|
||||
lock sync.RWMutex // Protects the transaction pool
|
||||
txFeed event.Feed // Notification feed to allow waiting for inclusion
|
||||
reannoTxFeed event.Feed // Notification feed to trigger reannouce
|
||||
lock sync.RWMutex // Protects the transaction pool
|
||||
}
|
||||
|
||||
// newTestTxPool creates a mock transaction pool.
|
||||
@@ -90,6 +91,18 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
|
||||
return make([]error, len(txs))
|
||||
}
|
||||
|
||||
// ReannouceTransactions announce the transactions to some peers.
|
||||
func (p *testTxPool) ReannouceTransactions(txs []*types.Transaction) []error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
p.pool[tx.Hash()] = tx
|
||||
}
|
||||
p.reannoTxFeed.Send(core.ReannoTxsEvent{Txs: txs})
|
||||
return make([]error, len(txs))
|
||||
}
|
||||
|
||||
// Pending returns all the transactions known to the pool
|
||||
func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||
p.lock.RLock()
|
||||
@@ -112,6 +125,12 @@ func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subs
|
||||
return p.txFeed.Subscribe(ch)
|
||||
}
|
||||
|
||||
// SubscribeReannoTxsEvent should return an event subscription of ReannoTxsEvent and
|
||||
// send events to the given channel.
|
||||
func (p *testTxPool) SubscribeReannoTxsEvent(ch chan<- core.ReannoTxsEvent) event.Subscription {
|
||||
return p.reannoTxFeed.Subscribe(ch)
|
||||
}
|
||||
|
||||
// testHandler is a live implementation of the Ethereum protocol handler, just
|
||||
// preinitialized with some sane testing defaults and the transaction pool mocked
|
||||
// out.
|
||||
|
||||
@@ -266,6 +266,22 @@ func (ps *peerSet) peer(id string) *ethPeer {
|
||||
return ps.peers[id]
|
||||
}
|
||||
|
||||
// headPeers retrieves a specified number list of peers.
|
||||
func (ps *peerSet) headPeers(num uint) []*ethPeer {
|
||||
ps.lock.RLock()
|
||||
defer ps.lock.RUnlock()
|
||||
|
||||
if num > uint(len(ps.peers)) {
|
||||
num = uint(len(ps.peers))
|
||||
}
|
||||
|
||||
list := make([]*ethPeer, 0, num)
|
||||
for _, p := range ps.peers {
|
||||
list = append(list, p)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// peersWithoutBlock retrieves a list of peers that do not have a given block in
|
||||
// their set of known hashes so it might be propagated to them.
|
||||
func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer {
|
||||
|
||||
@@ -469,7 +469,7 @@ func handleMessage(backend Backend, peer *Peer) error {
|
||||
// Storage slots requested, open the storage trie and retrieve from there
|
||||
account, err := snap.Account(common.BytesToHash(pathset[0]))
|
||||
loads++ // always account database reads, even for failures
|
||||
if err != nil {
|
||||
if err != nil || account == nil {
|
||||
break
|
||||
}
|
||||
stTrie, err := trie.NewSecure(common.BytesToHash(account.Root), triedb)
|
||||
|
||||
4
go.mod
4
go.mod
@@ -32,7 +32,7 @@ require (
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
|
||||
github.com/go-stack/stack v1.8.0
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
|
||||
github.com/google/uuid v1.1.5
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
@@ -65,7 +65,7 @@ require (
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||
github.com/tendermint/go-amino v0.14.1
|
||||
github.com/tendermint/iavl v0.12.0
|
||||
github.com/tendermint/tendermint v0.31.11
|
||||
|
||||
8
go.sum
8
go.sum
@@ -186,8 +186,8 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 h1:ur2rms48b3Ep1dxh7aUV2FZEQ8jEVO2F6ILKx8ofkAg=
|
||||
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
@@ -387,8 +387,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk=
|
||||
github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso=
|
||||
github.com/tendermint/iavl v0.12.0 h1:xcaFAr+ycqCj7WN1RzL2EfcBioRDOHcU1oWcg83K028=
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 1 // Minor version component of the current release
|
||||
VersionPatch = 5 // Patch version component of the current release
|
||||
VersionPatch = 6 // Patch version component of the current release
|
||||
VersionMeta = "" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
||||
@@ -174,6 +174,10 @@ func (t *Trie) TryGetNode(path []byte) ([]byte, int, error) {
|
||||
}
|
||||
|
||||
func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, newnode node, resolved int, err error) {
|
||||
// If non-existent path requested, abort
|
||||
if origNode == nil {
|
||||
return nil, nil, 0, nil
|
||||
}
|
||||
// If we reached the requested path, return the current node
|
||||
if pos >= len(path) {
|
||||
// Although we most probably have the original node expanded, encoding
|
||||
@@ -193,10 +197,6 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new
|
||||
}
|
||||
// Path still needs to be traversed, descend into children
|
||||
switch n := (origNode).(type) {
|
||||
case nil:
|
||||
// Non-existent path requested, abort
|
||||
return nil, nil, 0, nil
|
||||
|
||||
case valueNode:
|
||||
// Path prematurely ended, abort
|
||||
return nil, nil, 0, nil
|
||||
|
||||
Reference in New Issue
Block a user