mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
cmd, core, eth: journal local transactions to disk
This commit is contained in:
parent
24f8e26377
commit
b13eadf122
6 changed files with 307 additions and 38 deletions
|
|
@ -67,6 +67,8 @@ var (
|
||||||
utils.EthashDatasetsInMemoryFlag,
|
utils.EthashDatasetsInMemoryFlag,
|
||||||
utils.EthashDatasetsOnDiskFlag,
|
utils.EthashDatasetsOnDiskFlag,
|
||||||
utils.TxPoolNoLocalsFlag,
|
utils.TxPoolNoLocalsFlag,
|
||||||
|
utils.TxPoolJournalFlag,
|
||||||
|
utils.TxPoolRejournalFlag,
|
||||||
utils.TxPoolPriceLimitFlag,
|
utils.TxPoolPriceLimitFlag,
|
||||||
utils.TxPoolPriceBumpFlag,
|
utils.TxPoolPriceBumpFlag,
|
||||||
utils.TxPoolAccountSlotsFlag,
|
utils.TxPoolAccountSlotsFlag,
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Name: "TRANSACTION POOL",
|
Name: "TRANSACTION POOL",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.TxPoolNoLocalsFlag,
|
utils.TxPoolNoLocalsFlag,
|
||||||
|
utils.TxPoolJournalFlag,
|
||||||
|
utils.TxPoolRejournalFlag,
|
||||||
utils.TxPoolPriceLimitFlag,
|
utils.TxPoolPriceLimitFlag,
|
||||||
utils.TxPoolPriceBumpFlag,
|
utils.TxPoolPriceBumpFlag,
|
||||||
utils.TxPoolAccountSlotsFlag,
|
utils.TxPoolAccountSlotsFlag,
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,16 @@ var (
|
||||||
Name: "txpool.nolocals",
|
Name: "txpool.nolocals",
|
||||||
Usage: "Disables price exemptions for locally submitted transactions",
|
Usage: "Disables price exemptions for locally submitted transactions",
|
||||||
}
|
}
|
||||||
|
TxPoolJournalFlag = cli.StringFlag{
|
||||||
|
Name: "txpool.journal",
|
||||||
|
Usage: "Disk journal for local transaction to survive node restarts",
|
||||||
|
Value: core.DefaultTxPoolConfig.Journal,
|
||||||
|
}
|
||||||
|
TxPoolRejournalFlag = cli.DurationFlag{
|
||||||
|
Name: "txpool.rejournal",
|
||||||
|
Usage: "Time interval to regenerate the local transaction journal",
|
||||||
|
Value: core.DefaultTxPoolConfig.Rejournal,
|
||||||
|
}
|
||||||
TxPoolPriceLimitFlag = cli.Uint64Flag{
|
TxPoolPriceLimitFlag = cli.Uint64Flag{
|
||||||
Name: "txpool.pricelimit",
|
Name: "txpool.pricelimit",
|
||||||
Usage: "Minimum gas price limit to enforce for acceptance into the pool",
|
Usage: "Minimum gas price limit to enforce for acceptance into the pool",
|
||||||
|
|
@ -838,6 +848,12 @@ func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
|
||||||
if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
|
if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
|
||||||
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
|
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(TxPoolJournalFlag.Name) {
|
||||||
|
cfg.Journal = ctx.GlobalString(TxPoolJournalFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(TxPoolRejournalFlag.Name) {
|
||||||
|
cfg.Rejournal = ctx.GlobalDuration(TxPoolRejournalFlag.Name)
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) {
|
if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) {
|
||||||
cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name)
|
cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
147
core/tx_pool.go
147
core/tx_pool.go
|
|
@ -19,7 +19,9 @@ package core
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -31,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -99,7 +102,9 @@ type stateFn func() (*state.StateDB, error)
|
||||||
|
|
||||||
// TxPoolConfig are the configuration parameters of the transaction pool.
|
// TxPoolConfig are the configuration parameters of the transaction pool.
|
||||||
type TxPoolConfig struct {
|
type TxPoolConfig struct {
|
||||||
NoLocals bool // Whether local transaction handling should be disabled
|
NoLocals bool // Whether local transaction handling should be disabled
|
||||||
|
Journal string // Journal of local transactions to survive node restarts
|
||||||
|
Rejournal time.Duration // Time interval to regenerate the local transaction journal
|
||||||
|
|
||||||
PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
|
PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
|
||||||
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
|
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
|
||||||
|
|
@ -115,6 +120,9 @@ type TxPoolConfig struct {
|
||||||
// DefaultTxPoolConfig contains the default configurations for the transaction
|
// DefaultTxPoolConfig contains the default configurations for the transaction
|
||||||
// pool.
|
// pool.
|
||||||
var DefaultTxPoolConfig = TxPoolConfig{
|
var DefaultTxPoolConfig = TxPoolConfig{
|
||||||
|
Journal: "transactions.rlp",
|
||||||
|
Rejournal: time.Hour,
|
||||||
|
|
||||||
PriceLimit: 1,
|
PriceLimit: 1,
|
||||||
PriceBump: 10,
|
PriceBump: 10,
|
||||||
|
|
||||||
|
|
@ -130,6 +138,10 @@ var DefaultTxPoolConfig = TxPoolConfig{
|
||||||
// unreasonable or unworkable.
|
// unreasonable or unworkable.
|
||||||
func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||||
conf := *config
|
conf := *config
|
||||||
|
if conf.Rejournal < time.Second {
|
||||||
|
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
|
||||||
|
conf.Rejournal = time.Second
|
||||||
|
}
|
||||||
if conf.PriceLimit < 1 {
|
if conf.PriceLimit < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
|
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
|
||||||
conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
|
conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
|
||||||
|
|
@ -157,10 +169,12 @@ type TxPool struct {
|
||||||
gasPrice *big.Int
|
gasPrice *big.Int
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
events *event.TypeMuxSubscription
|
events *event.TypeMuxSubscription
|
||||||
locals *accountSet
|
|
||||||
signer types.Signer
|
signer types.Signer
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
locals *accountSet // Set of local transaction to exepmt from evicion rules
|
||||||
|
journal io.WriteCloser // Journal of local transaction to back up to disk
|
||||||
|
|
||||||
pending map[common.Address]*txList // All currently processable transactions
|
pending map[common.Address]*txList // All currently processable transactions
|
||||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
queue map[common.Address]*txList // Queued but non-processable transactions
|
||||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||||
|
|
@ -198,6 +212,9 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, eventMux *e
|
||||||
pool.priced = newTxPricedList(&pool.all)
|
pool.priced = newTxPricedList(&pool.all)
|
||||||
pool.resetState()
|
pool.resetState()
|
||||||
|
|
||||||
|
if err := pool.rotateJournal(); err != nil {
|
||||||
|
log.Warn("Failed to load transaction journal", "err", err)
|
||||||
|
}
|
||||||
// Start the event loop and return
|
// Start the event loop and return
|
||||||
pool.wg.Add(1)
|
pool.wg.Add(1)
|
||||||
go pool.loop()
|
go pool.loop()
|
||||||
|
|
@ -220,6 +237,9 @@ func (pool *TxPool) loop() {
|
||||||
evict := time.NewTicker(evictionInterval)
|
evict := time.NewTicker(evictionInterval)
|
||||||
defer evict.Stop()
|
defer evict.Stop()
|
||||||
|
|
||||||
|
journal := time.NewTicker(pool.config.Rejournal)
|
||||||
|
defer journal.Stop()
|
||||||
|
|
||||||
// Keep waiting for and reacting to the various events
|
// Keep waiting for and reacting to the various events
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -271,6 +291,14 @@ func (pool *TxPool) loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
|
|
||||||
|
// Handle local transaction journal rotation
|
||||||
|
case <-journal.C:
|
||||||
|
pool.mu.Lock()
|
||||||
|
if err := pool.rotateJournal(); err != nil {
|
||||||
|
log.Warn("Failed to rotate local tx journal", "err", err)
|
||||||
|
}
|
||||||
|
pool.mu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,6 +332,9 @@ func (pool *TxPool) Stop() {
|
||||||
pool.events.Unsubscribe()
|
pool.events.Unsubscribe()
|
||||||
pool.wg.Wait()
|
pool.wg.Wait()
|
||||||
|
|
||||||
|
if pool.journal != nil {
|
||||||
|
pool.journal.Close()
|
||||||
|
}
|
||||||
log.Info("Transaction pool stopped")
|
log.Info("Transaction pool stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -494,13 +525,20 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
|
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
|
||||||
return old != nil, nil
|
return old != nil, nil
|
||||||
}
|
}
|
||||||
// New transaction isn't replacing a pending one, push into queue and potentially mark local
|
// New transaction isn't replacing a pending one, push into queue
|
||||||
replace, err := pool.enqueueTx(hash, tx)
|
replace, err := pool.enqueueTx(hash, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
// Mark local addresses and journal local transactions
|
||||||
|
local = local || pool.locals.contains(from)
|
||||||
if local {
|
if local {
|
||||||
pool.locals.add(from)
|
pool.locals.add(from)
|
||||||
|
if pool.journal != nil {
|
||||||
|
if err := rlp.Encode(pool.journal, tx); err != nil {
|
||||||
|
log.Warn("Failed to journal local transaction", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
|
log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
|
||||||
return replace, nil
|
return replace, nil
|
||||||
|
|
@ -927,6 +965,107 @@ func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rotateJournal regenerates the local transaction journal based on the current
|
||||||
|
// contents of the transaction pool. If the pool doesn't have a journal opened,
|
||||||
|
// it will first pull in the contents of any existing one from disk.
|
||||||
|
func (pool *TxPool) rotateJournal() error {
|
||||||
|
// If local transactions or journaling are disabled, skip
|
||||||
|
if pool.config.NoLocals || pool.config.Journal == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// If there's no journal open (first run), parse any existing one
|
||||||
|
if pool.journal == nil {
|
||||||
|
// Skip the parsing if the journal file doens't exist at all
|
||||||
|
if _, err := os.Stat(pool.config.Journal); !os.IsNotExist(err) {
|
||||||
|
// Open the journal for loading any past transactions
|
||||||
|
journal, err := os.Open(pool.config.Journal)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Inject all transactions from the journal into the pool
|
||||||
|
stream := rlp.NewStream(journal, 0)
|
||||||
|
total, dropped := 0, 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
tx := new(types.Transaction)
|
||||||
|
if err := stream.Decode(tx); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
total++
|
||||||
|
if err := pool.addTx(tx, true); err != nil {
|
||||||
|
log.Debug("Failed to add journaled transaction", "err", err)
|
||||||
|
dropped++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
|
||||||
|
|
||||||
|
// All transactions added, set the active journal
|
||||||
|
pool.journal = journal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Close the current journal (if any is open)
|
||||||
|
if pool.journal != nil {
|
||||||
|
if err := pool.journal.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pool.journal = nil
|
||||||
|
}
|
||||||
|
// Generate a new journal with the contents of the current pool
|
||||||
|
replacement, err := os.OpenFile(pool.config.Journal+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accounts, journaled := 0, 0
|
||||||
|
for addr := range pool.locals.accounts {
|
||||||
|
// Flatten and serialize all the local executable transactions
|
||||||
|
pending := pool.pending[addr]
|
||||||
|
if pending != nil {
|
||||||
|
txs := pending.Flatten()
|
||||||
|
for _, tx := range txs {
|
||||||
|
if err = rlp.Encode(replacement, tx); err != nil {
|
||||||
|
replacement.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
journaled += len(txs)
|
||||||
|
}
|
||||||
|
// Flatten and serialize all the local non-executable transactions
|
||||||
|
queued := pool.queue[addr]
|
||||||
|
if queued != nil {
|
||||||
|
txs := queued.Flatten()
|
||||||
|
for _, tx := range txs {
|
||||||
|
if err = rlp.Encode(replacement, tx); err != nil {
|
||||||
|
replacement.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
journaled += len(txs)
|
||||||
|
}
|
||||||
|
// Bump the address counter if we did have transactions
|
||||||
|
if pending != nil || queued != nil {
|
||||||
|
accounts++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replacement.Close()
|
||||||
|
|
||||||
|
// Replace the live journal with the newly generated one
|
||||||
|
if err = os.Rename(pool.config.Journal+".new", pool.config.Journal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
journal, err := os.OpenFile(pool.config.Journal, os.O_WRONLY|os.O_APPEND, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pool.journal = journal
|
||||||
|
log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", accounts)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// addressByHeartbeat is an account address tagged with its last activity timestamp.
|
// addressByHeartbeat is an account address tagged with its last activity timestamp.
|
||||||
type addressByHeartbeat struct {
|
type addressByHeartbeat struct {
|
||||||
address common.Address
|
address common.Address
|
||||||
|
|
@ -939,7 +1078,7 @@ func (a addresssByHeartbeat) Len() int { return len(a) }
|
||||||
func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
|
func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
|
||||||
func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
// accountSet is simply a set of addresses to check for existance, and a signer
|
// accountSet is simply a set of addresses to check for existence, and a signer
|
||||||
// capable of deriving addresses from transactions.
|
// capable of deriving addresses from transactions.
|
||||||
type accountSet struct {
|
type accountSet struct {
|
||||||
accounts map[common.Address]struct{}
|
accounts map[common.Address]struct{}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ package core
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -33,6 +35,15 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// testTxPoolConfig is a transaction pool configuration without stateful disk
|
||||||
|
// sideeffects used during testing.
|
||||||
|
var testTxPoolConfig TxPoolConfig
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testTxPoolConfig = DefaultTxPoolConfig
|
||||||
|
testTxPoolConfig.Journal = ""
|
||||||
|
}
|
||||||
|
|
||||||
func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
|
func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
|
||||||
return pricedTransaction(nonce, gaslimit, big.NewInt(1), key)
|
return pricedTransaction(nonce, gaslimit, big.NewInt(1), key)
|
||||||
}
|
}
|
||||||
|
|
@ -47,8 +58,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
return pool, key
|
return pool, key
|
||||||
}
|
}
|
||||||
|
|
@ -125,9 +135,8 @@ func TestStateChangeDuringPoolReset(t *testing.T) {
|
||||||
|
|
||||||
gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
|
gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
|
||||||
|
|
||||||
pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, mux, stateFunc, gasLimitFunc)
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, mux, stateFunc, gasLimitFunc)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
nonce := pool.State().GetNonce(address)
|
nonce := pool.State().GetNonce(address)
|
||||||
if nonce != 0 {
|
if nonce != 0 {
|
||||||
|
|
@ -618,25 +627,25 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
|
||||||
pool.resetState()
|
pool.resetState()
|
||||||
|
|
||||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||||
for i := uint64(1); i <= DefaultTxPoolConfig.AccountQueue+5; i++ {
|
for i := uint64(1); i <= testTxPoolConfig.AccountQueue+5; i++ {
|
||||||
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
|
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
|
||||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||||
}
|
}
|
||||||
if len(pool.pending) != 0 {
|
if len(pool.pending) != 0 {
|
||||||
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0)
|
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0)
|
||||||
}
|
}
|
||||||
if i <= DefaultTxPoolConfig.AccountQueue {
|
if i <= testTxPoolConfig.AccountQueue {
|
||||||
if pool.queue[account].Len() != int(i) {
|
if pool.queue[account].Len() != int(i) {
|
||||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i)
|
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if pool.queue[account].Len() != int(DefaultTxPoolConfig.AccountQueue) {
|
if pool.queue[account].Len() != int(testTxPoolConfig.AccountQueue) {
|
||||||
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), DefaultTxPoolConfig.AccountQueue)
|
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), testTxPoolConfig.AccountQueue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(pool.all) != int(DefaultTxPoolConfig.AccountQueue) {
|
if len(pool.all) != int(testTxPoolConfig.AccountQueue) {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), DefaultTxPoolConfig.AccountQueue)
|
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -657,13 +666,12 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
config := DefaultTxPoolConfig
|
config := testTxPoolConfig
|
||||||
config.NoLocals = nolocals
|
config.NoLocals = nolocals
|
||||||
config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible)
|
config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible)
|
||||||
|
|
||||||
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a number of test accounts and fund them (last one will be the local)
|
// Create a number of test accounts and fund them (last one will be the local)
|
||||||
state, _ := pool.currentState()
|
state, _ := pool.currentState()
|
||||||
|
|
@ -748,13 +756,12 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
config := DefaultTxPoolConfig
|
config := testTxPoolConfig
|
||||||
config.Lifetime = 250 * time.Millisecond
|
config.Lifetime = 250 * time.Millisecond
|
||||||
config.NoLocals = nolocals
|
config.NoLocals = nolocals
|
||||||
|
|
||||||
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create two test accounts to ensure remotes expire but locals do not
|
// Create two test accounts to ensure remotes expire but locals do not
|
||||||
local, _ := crypto.GenerateKey()
|
local, _ := crypto.GenerateKey()
|
||||||
|
|
@ -817,7 +824,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||||
pool.resetState()
|
pool.resetState()
|
||||||
|
|
||||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||||
for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
|
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
|
||||||
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
|
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
|
||||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -828,8 +835,8 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
|
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(pool.all) != int(DefaultTxPoolConfig.AccountQueue+5) {
|
if len(pool.all) != int(testTxPoolConfig.AccountQueue+5) {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), DefaultTxPoolConfig.AccountQueue+5)
|
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue+5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -845,7 +852,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||||
state1, _ := pool1.currentState()
|
state1, _ := pool1.currentState()
|
||||||
state1.AddBalance(account1, big.NewInt(1000000))
|
state1.AddBalance(account1, big.NewInt(1000000))
|
||||||
|
|
||||||
for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
|
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
|
||||||
if err := pool1.AddRemote(transaction(origin+i, big.NewInt(100000), key1)); err != nil {
|
if err := pool1.AddRemote(transaction(origin+i, big.NewInt(100000), key1)); err != nil {
|
||||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -857,7 +864,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||||
state2.AddBalance(account2, big.NewInt(1000000))
|
state2.AddBalance(account2, big.NewInt(1000000))
|
||||||
|
|
||||||
txns := []*types.Transaction{}
|
txns := []*types.Transaction{}
|
||||||
for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
|
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
|
||||||
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
|
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
|
||||||
}
|
}
|
||||||
pool2.AddRemotes(txns)
|
pool2.AddRemotes(txns)
|
||||||
|
|
@ -888,12 +895,11 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
config := DefaultTxPoolConfig
|
config := testTxPoolConfig
|
||||||
config.GlobalSlots = config.AccountSlots * 10
|
config.GlobalSlots = config.AccountSlots * 10
|
||||||
|
|
||||||
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a number of test accounts and fund them
|
// Create a number of test accounts and fund them
|
||||||
state, _ := pool.currentState()
|
state, _ := pool.currentState()
|
||||||
|
|
@ -935,14 +941,13 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
config := DefaultTxPoolConfig
|
config := testTxPoolConfig
|
||||||
config.AccountSlots = 2
|
config.AccountSlots = 2
|
||||||
config.AccountQueue = 2
|
config.AccountQueue = 2
|
||||||
config.GlobalSlots = 8
|
config.GlobalSlots = 8
|
||||||
|
|
||||||
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a number of test accounts and fund them
|
// Create a number of test accounts and fund them
|
||||||
state, _ := pool.currentState()
|
state, _ := pool.currentState()
|
||||||
|
|
@ -970,12 +975,11 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
config := DefaultTxPoolConfig
|
config := testTxPoolConfig
|
||||||
config.GlobalSlots = 0
|
config.GlobalSlots = 0
|
||||||
|
|
||||||
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a number of test accounts and fund them
|
// Create a number of test accounts and fund them
|
||||||
state, _ := pool.currentState()
|
state, _ := pool.currentState()
|
||||||
|
|
@ -1019,9 +1023,8 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a number of test accounts and fund them
|
// Create a number of test accounts and fund them
|
||||||
state, _ := pool.currentState()
|
state, _ := pool.currentState()
|
||||||
|
|
@ -1104,13 +1107,12 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
config := DefaultTxPoolConfig
|
config := testTxPoolConfig
|
||||||
config.GlobalSlots = 2
|
config.GlobalSlots = 2
|
||||||
config.GlobalQueue = 2
|
config.GlobalQueue = 2
|
||||||
|
|
||||||
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a number of test accounts and fund them
|
// Create a number of test accounts and fund them
|
||||||
state, _ := pool.currentState()
|
state, _ := pool.currentState()
|
||||||
|
|
@ -1192,9 +1194,8 @@ func TestTransactionReplacement(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
pool.resetState()
|
|
||||||
|
|
||||||
// Create a test account to add transactions with
|
// Create a test account to add transactions with
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
@ -1204,7 +1205,7 @@ func TestTransactionReplacement(t *testing.T) {
|
||||||
|
|
||||||
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
|
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
|
||||||
price := int64(100)
|
price := int64(100)
|
||||||
threshold := (price * (100 + int64(DefaultTxPoolConfig.PriceBump))) / 100
|
threshold := (price * (100 + int64(testTxPoolConfig.PriceBump))) / 100
|
||||||
|
|
||||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), key)); err != nil {
|
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), key)); err != nil {
|
||||||
t.Fatalf("failed to add original cheap pending transaction: %v", err)
|
t.Fatalf("failed to add original cheap pending transaction: %v", err)
|
||||||
|
|
@ -1250,6 +1251,113 @@ func TestTransactionReplacement(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that local transactions are journaled to disk, but remote transactions
|
||||||
|
// get discarded between restarts.
|
||||||
|
func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) }
|
||||||
|
func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
|
||||||
|
|
||||||
|
func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||||
|
// Create a temporary file for the journal
|
||||||
|
file, err := ioutil.TempFile("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temporary journal: %v", err)
|
||||||
|
}
|
||||||
|
journal := file.Name()
|
||||||
|
defer os.Remove(journal)
|
||||||
|
|
||||||
|
// Clean up the temporary file, we only need the path for now
|
||||||
|
file.Close()
|
||||||
|
os.Remove(journal)
|
||||||
|
|
||||||
|
// Create the original pool to inject transaction into the journal
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
|
|
||||||
|
config := testTxPoolConfig
|
||||||
|
config.NoLocals = nolocals
|
||||||
|
config.Journal = journal
|
||||||
|
config.Rejournal = time.Second
|
||||||
|
|
||||||
|
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
|
|
||||||
|
// Create two test accounts to ensure remotes expire but locals do not
|
||||||
|
local, _ := crypto.GenerateKey()
|
||||||
|
remote, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
|
statedb, _ = pool.currentState()
|
||||||
|
statedb.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
|
||||||
|
statedb.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
|
||||||
|
|
||||||
|
// Add three local and a remote transactions and ensure they are queued up
|
||||||
|
if err := pool.AddLocal(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||||
|
t.Fatalf("failed to add local transaction: %v", err)
|
||||||
|
}
|
||||||
|
if err := pool.AddLocal(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||||
|
t.Fatalf("failed to add local transaction: %v", err)
|
||||||
|
}
|
||||||
|
if err := pool.AddLocal(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||||
|
t.Fatalf("failed to add local transaction: %v", err)
|
||||||
|
}
|
||||||
|
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), remote)); err != nil {
|
||||||
|
t.Fatalf("failed to add remote transaction: %v", err)
|
||||||
|
}
|
||||||
|
pending, queued := pool.stats()
|
||||||
|
if pending != 4 {
|
||||||
|
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4)
|
||||||
|
}
|
||||||
|
if queued != 0 {
|
||||||
|
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
|
||||||
|
}
|
||||||
|
if err := validateTxPoolInternals(pool); err != nil {
|
||||||
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
|
}
|
||||||
|
// Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
|
||||||
|
pool.Stop()
|
||||||
|
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
|
||||||
|
pool = NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
|
|
||||||
|
pending, queued = pool.stats()
|
||||||
|
if queued != 0 {
|
||||||
|
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
|
||||||
|
}
|
||||||
|
if nolocals {
|
||||||
|
if pending != 0 {
|
||||||
|
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if pending != 2 {
|
||||||
|
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := validateTxPoolInternals(pool); err != nil {
|
||||||
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
|
}
|
||||||
|
// Bump the nonce temporarily and ensure the newly invalidated transaction is removed
|
||||||
|
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2)
|
||||||
|
pool.resetState()
|
||||||
|
time.Sleep(2 * config.Rejournal)
|
||||||
|
pool.Stop()
|
||||||
|
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
|
||||||
|
pool = NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||||
|
|
||||||
|
pending, queued = pool.stats()
|
||||||
|
if pending != 0 {
|
||||||
|
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
|
||||||
|
}
|
||||||
|
if nolocals {
|
||||||
|
if queued != 0 {
|
||||||
|
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if queued != 1 {
|
||||||
|
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := validateTxPoolInternals(pool); err != nil {
|
||||||
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Benchmarks the speed of validating the contents of the pending queue of the
|
// Benchmarks the speed of validating the contents of the pending queue of the
|
||||||
// transaction pool.
|
// transaction pool.
|
||||||
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
|
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
|
||||||
|
|
|
||||||
|
|
@ -148,8 +148,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
core.WriteChainConfig(chainDb, genesisHash, chainConfig)
|
core.WriteChainConfig(chainDb, genesisHash, chainConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
newPool := core.NewTxPool(config.TxPool, eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
|
if config.TxPool.Journal != "" {
|
||||||
eth.txPool = newPool
|
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
|
||||||
|
}
|
||||||
|
eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
|
||||||
|
|
||||||
maxPeers := config.MaxPeers
|
maxPeers := config.MaxPeers
|
||||||
if config.LightServ > 0 {
|
if config.LightServ > 0 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue