Merge pull request #14523 from karalabe/txpool-cli-flags

cmd, core, eth: configurable txpool parameters
This commit is contained in:
Péter Szilágyi 2017-05-29 11:42:48 +03:00 committed by GitHub
commit dd06c85843
10 changed files with 241 additions and 105 deletions

View File

@ -66,6 +66,13 @@ var (
utils.EthashDatasetDirFlag, utils.EthashDatasetDirFlag,
utils.EthashDatasetsInMemoryFlag, utils.EthashDatasetsInMemoryFlag,
utils.EthashDatasetsOnDiskFlag, utils.EthashDatasetsOnDiskFlag,
utils.TxPoolPriceLimitFlag,
utils.TxPoolPriceBumpFlag,
utils.TxPoolAccountSlotsFlag,
utils.TxPoolGlobalSlotsFlag,
utils.TxPoolAccountQueueFlag,
utils.TxPoolGlobalQueueFlag,
utils.TxPoolLifetimeFlag,
utils.FastSyncFlag, utils.FastSyncFlag,
utils.LightModeFlag, utils.LightModeFlag,
utils.SyncModeFlag, utils.SyncModeFlag,

View File

@ -92,6 +92,18 @@ var AppHelpFlagGroups = []flagGroup{
utils.EthashDatasetsOnDiskFlag, utils.EthashDatasetsOnDiskFlag,
}, },
}, },
{
Name: "TRANSACTION POOL",
Flags: []cli.Flag{
utils.TxPoolPriceLimitFlag,
utils.TxPoolPriceBumpFlag,
utils.TxPoolAccountSlotsFlag,
utils.TxPoolGlobalSlotsFlag,
utils.TxPoolAccountQueueFlag,
utils.TxPoolGlobalQueueFlag,
utils.TxPoolLifetimeFlag,
},
},
{ {
Name: "PERFORMANCE TUNING", Name: "PERFORMANCE TUNING",
Flags: []cli.Flag{ Flags: []cli.Flag{

View File

@ -123,35 +123,6 @@ var (
Name: "nousb", Name: "nousb",
Usage: "Disables monitoring for and managine USB hardware wallets", Usage: "Disables monitoring for and managine USB hardware wallets",
} }
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
}
EthashCachesInMemoryFlag = cli.IntFlag{
Name: "ethash.cachesinmem",
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
Value: eth.DefaultConfig.EthashCachesInMem,
}
EthashCachesOnDiskFlag = cli.IntFlag{
Name: "ethash.cachesondisk",
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
Value: eth.DefaultConfig.EthashCachesOnDisk,
}
EthashDatasetDirFlag = DirectoryFlag{
Name: "ethash.dagdir",
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir},
}
EthashDatasetsInMemoryFlag = cli.IntFlag{
Name: "ethash.dagsinmem",
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsInMem,
}
EthashDatasetsOnDiskFlag = cli.IntFlag{
Name: "ethash.dagsondisk",
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsOnDisk,
}
NetworkIdFlag = cli.Uint64Flag{ NetworkIdFlag = cli.Uint64Flag{
Name: "networkid", Name: "networkid",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)", Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)",
@ -207,6 +178,72 @@ var (
Name: "lightkdf", Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
} }
// Ethash settings
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
}
EthashCachesInMemoryFlag = cli.IntFlag{
Name: "ethash.cachesinmem",
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
Value: eth.DefaultConfig.EthashCachesInMem,
}
EthashCachesOnDiskFlag = cli.IntFlag{
Name: "ethash.cachesondisk",
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
Value: eth.DefaultConfig.EthashCachesOnDisk,
}
EthashDatasetDirFlag = DirectoryFlag{
Name: "ethash.dagdir",
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir},
}
EthashDatasetsInMemoryFlag = cli.IntFlag{
Name: "ethash.dagsinmem",
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsInMem,
}
EthashDatasetsOnDiskFlag = cli.IntFlag{
Name: "ethash.dagsondisk",
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsOnDisk,
}
// Transaction pool settings
TxPoolPriceLimitFlag = cli.Uint64Flag{
Name: "txpool.pricelimit",
Usage: "Minimum gas price limit to enforce for acceptance into the pool",
Value: eth.DefaultConfig.TxPool.PriceLimit,
}
TxPoolPriceBumpFlag = cli.Uint64Flag{
Name: "txpool.pricebump",
Usage: "Price bump percentage to replace an already existing transaction",
Value: eth.DefaultConfig.TxPool.PriceBump,
}
TxPoolAccountSlotsFlag = cli.Uint64Flag{
Name: "txpool.accountslots",
Usage: "Minimum number of executable transaction slots guaranteed per account",
Value: eth.DefaultConfig.TxPool.AccountSlots,
}
TxPoolGlobalSlotsFlag = cli.Uint64Flag{
Name: "txpool.globalslots",
Usage: "Maximum number of executable transaction slots for all accounts",
Value: eth.DefaultConfig.TxPool.GlobalSlots,
}
TxPoolAccountQueueFlag = cli.Uint64Flag{
Name: "txpool.accountqueue",
Usage: "Maximum number of non-executable transaction slots permitted per account",
Value: eth.DefaultConfig.TxPool.AccountQueue,
}
TxPoolGlobalQueueFlag = cli.Uint64Flag{
Name: "txpool.globalqueue",
Usage: "Maximum number of non-executable transaction slots for all accounts",
Value: eth.DefaultConfig.TxPool.GlobalQueue,
}
TxPoolLifetimeFlag = cli.DurationFlag{
Name: "txpool.lifetime",
Usage: "Maximum amount of time non-executable transaction are queued",
Value: eth.DefaultConfig.TxPool.Lifetime,
}
// Performance tuning settings // Performance tuning settings
CacheFlag = cli.IntFlag{ CacheFlag = cli.IntFlag{
Name: "cache", Name: "cache",
@ -784,6 +821,30 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
} }
} }
func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) {
cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name)
}
if ctx.GlobalIsSet(TxPoolPriceBumpFlag.Name) {
cfg.PriceBump = ctx.GlobalUint64(TxPoolPriceBumpFlag.Name)
}
if ctx.GlobalIsSet(TxPoolAccountSlotsFlag.Name) {
cfg.AccountSlots = ctx.GlobalUint64(TxPoolAccountSlotsFlag.Name)
}
if ctx.GlobalIsSet(TxPoolGlobalSlotsFlag.Name) {
cfg.GlobalSlots = ctx.GlobalUint64(TxPoolGlobalSlotsFlag.Name)
}
if ctx.GlobalIsSet(TxPoolAccountQueueFlag.Name) {
cfg.AccountQueue = ctx.GlobalUint64(TxPoolAccountQueueFlag.Name)
}
if ctx.GlobalIsSet(TxPoolGlobalQueueFlag.Name) {
cfg.GlobalQueue = ctx.GlobalUint64(TxPoolGlobalQueueFlag.Name)
}
if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) {
cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name)
}
}
func setEthash(ctx *cli.Context, cfg *eth.Config) { func setEthash(ctx *cli.Context, cfg *eth.Config) {
if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { if ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name)
@ -826,6 +887,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
setEtherbase(ctx, ks, cfg) setEtherbase(ctx, ks, cfg)
setGPO(ctx, &cfg.GPO) setGPO(ctx, &cfg.GPO)
setTxPool(ctx, &cfg.TxPool)
setEthash(ctx, cfg) setEthash(ctx, cfg)
switch { switch {

View File

@ -246,11 +246,11 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
// //
// If the new transaction is accepted into the list, the lists' cost threshold // If the new transaction is accepted into the list, the lists' cost threshold
// is also potentially updated. // is also potentially updated.
func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) { func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
// If there's an older better transaction, abort // If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce()) old := l.txs.Get(tx.Nonce())
if old != nil { if old != nil {
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+minPriceBumpPercent)), big.NewInt(100)) threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
if threshold.Cmp(tx.GasPrice()) >= 0 { if threshold.Cmp(tx.GasPrice()) >= 0 {
return false, nil return false, nil
} }

View File

@ -38,7 +38,7 @@ func TestStrictTxListAdd(t *testing.T) {
// Insert the transactions in a random order // Insert the transactions in a random order
list := newTxList(true) list := newTxList(true)
for _, v := range rand.Perm(len(txs)) { for _, v := range rand.Perm(len(txs)) {
list.Add(txs[v]) list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
} }
// Verify internal state // Verify internal state
if len(list.txs.items) != len(txs) { if len(list.txs.items) != len(txs) {

View File

@ -48,12 +48,6 @@ var (
) )
var ( var (
minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address
maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft)
maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address
maxQueuedTotal = uint64(1024) // Max limit of queued transactions from all accounts
maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued
minPriceBumpPercent = int64(10) // Minimum price bump needed to replace an old transaction
evictionInterval = time.Minute // Time interval to check for evictable transactions evictionInterval = time.Minute // Time interval to check for evictable transactions
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
) )
@ -78,6 +72,48 @@ var (
type stateFn func() (*state.StateDB, error) type stateFn func() (*state.StateDB, error)
// TxPoolConfig are the configuration parameters of the transaction pool.
type TxPoolConfig struct {
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)
AccountSlots uint64 // Minimum number of executable transaction slots guaranteed per account
GlobalSlots uint64 // Maximum number of executable transaction slots for all accounts
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
}
// DefaultTxPoolConfig contains the default configurations for the transaction
// pool.
var DefaultTxPoolConfig = TxPoolConfig{
PriceLimit: 1,
PriceBump: 10,
AccountSlots: 16,
GlobalSlots: 4096,
AccountQueue: 64,
GlobalQueue: 1024,
Lifetime: 3 * time.Hour,
}
// sanitize checks the provided user configurations and changes anything that's
// unreasonable or unworkable.
func (config *TxPoolConfig) sanitize() TxPoolConfig {
conf := *config
if conf.PriceLimit < 1 {
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
}
if conf.PriceBump < 1 {
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
conf.PriceBump = DefaultTxPoolConfig.PriceBump
}
return conf
}
// TxPool contains all currently known transactions. Transactions // TxPool contains all currently known transactions. Transactions
// enter the pool when they are received from the network or submitted // enter the pool when they are received from the network or submitted
// locally. They exit the pool when they are included in the blockchain. // locally. They exit the pool when they are included in the blockchain.
@ -86,7 +122,8 @@ type stateFn func() (*state.StateDB, error)
// current state) and future transactions. Transactions move between those // current state) and future transactions. Transactions move between those
// two states over time as they are received and processed. // two states over time as they are received and processed.
type TxPool struct { type TxPool struct {
config *params.ChainConfig config TxPoolConfig
chainconfig *params.ChainConfig
currentState stateFn // The state function which will allow us to do some pre checks currentState stateFn // The state function which will allow us to do some pre checks
pendingState *state.ManagedState pendingState *state.ManagedState
gasLimit func() *big.Int // The current gas limit function callback gasLimit func() *big.Int // The current gas limit function callback
@ -109,10 +146,17 @@ type TxPool struct {
homestead bool homestead bool
} }
func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { // NewTxPool creates a new transaction pool to gather, sort and filter inbound
// trnsactions from the network.
func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
// Sanitize the input to ensure no vulnerable gas prices are set
config = (&config).sanitize()
// Create the transaction pool with its initial settings
pool := &TxPool{ pool := &TxPool{
config: config, config: config,
signer: types.NewEIP155Signer(config.ChainId), chainconfig: chainconfig,
signer: types.NewEIP155Signer(chainconfig.ChainId),
pending: make(map[common.Address]*txList), pending: make(map[common.Address]*txList),
queue: make(map[common.Address]*txList), queue: make(map[common.Address]*txList),
beats: make(map[common.Address]time.Time), beats: make(map[common.Address]time.Time),
@ -120,7 +164,7 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
eventMux: eventMux, eventMux: eventMux,
currentState: currentStateFn, currentState: currentStateFn,
gasLimit: gasLimitFn, gasLimit: gasLimitFn,
gasPrice: big.NewInt(1), gasPrice: new(big.Int).SetUint64(config.PriceLimit),
pendingState: nil, pendingState: nil,
locals: newTxSet(), locals: newTxSet(),
events: eventMux.Subscribe(ChainHeadEvent{}, RemovedTransactionEvent{}), events: eventMux.Subscribe(ChainHeadEvent{}, RemovedTransactionEvent{}),
@ -129,6 +173,7 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
pool.priced = newTxPricedList(&pool.all) pool.priced = newTxPricedList(&pool.all)
pool.resetState() pool.resetState()
// Start the various events loops and return
pool.wg.Add(2) pool.wg.Add(2)
go pool.eventLoop() go pool.eventLoop()
go pool.expirationLoop() go pool.expirationLoop()
@ -159,7 +204,7 @@ func (pool *TxPool) eventLoop() {
case ChainHeadEvent: case ChainHeadEvent:
pool.mu.Lock() pool.mu.Lock()
if ev.Block != nil { if ev.Block != nil {
if pool.config.IsHomestead(ev.Block.Number()) { if pool.chainconfig.IsHomestead(ev.Block.Number()) {
pool.homestead = true pool.homestead = true
} }
} }
@ -388,7 +433,7 @@ func (pool *TxPool) add(tx *types.Transaction) (bool, error) {
return false, err return false, err
} }
// If the transaction pool is full, discard underpriced transactions // If the transaction pool is full, discard underpriced transactions
if uint64(len(pool.all)) >= maxPendingTotal+maxQueuedTotal { if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
// If the new transaction is underpriced, don't accept it // If the new transaction is underpriced, don't accept it
if pool.priced.Underpriced(tx, pool.locals) { if pool.priced.Underpriced(tx, pool.locals) {
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice()) log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
@ -396,7 +441,7 @@ func (pool *TxPool) add(tx *types.Transaction) (bool, error) {
return false, ErrUnderpriced return false, ErrUnderpriced
} }
// New transaction is better than our worse ones, make room for it // New transaction is better than our worse ones, make room for it
drop := pool.priced.Discard(len(pool.all)-int(maxPendingTotal+maxQueuedTotal-1), pool.locals) drop := pool.priced.Discard(len(pool.all)-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
for _, tx := range drop { for _, tx := range drop {
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice()) log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
underpricedTxCounter.Inc(1) underpricedTxCounter.Inc(1)
@ -407,7 +452,7 @@ func (pool *TxPool) add(tx *types.Transaction) (bool, error) {
from, _ := types.Sender(pool.signer, tx) // already validated from, _ := types.Sender(pool.signer, tx) // already validated
if list := pool.pending[from]; list != nil && list.Overlaps(tx) { if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
// Nonce already pending, check if required price bump is met // Nonce already pending, check if required price bump is met
inserted, old := list.Add(tx) inserted, old := list.Add(tx, pool.config.PriceBump)
if !inserted { if !inserted {
pendingDiscardCounter.Inc(1) pendingDiscardCounter.Inc(1)
return false, ErrReplaceUnderpriced return false, ErrReplaceUnderpriced
@ -442,7 +487,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
if pool.queue[from] == nil { if pool.queue[from] == nil {
pool.queue[from] = newTxList(false) pool.queue[from] = newTxList(false)
} }
inserted, old := pool.queue[from].Add(tx) inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
if !inserted { if !inserted {
// An older transaction was better, discard this // An older transaction was better, discard this
queuedDiscardCounter.Inc(1) queuedDiscardCounter.Inc(1)
@ -469,7 +514,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
} }
list := pool.pending[addr] list := pool.pending[addr]
inserted, old := list.Add(tx) inserted, old := list.Add(tx, pool.config.PriceBump)
if !inserted { if !inserted {
// An older transaction was better, discard this // An older transaction was better, discard this
delete(pool.all, hash) delete(pool.all, hash)
@ -644,7 +689,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
pool.promoteTx(addr, hash, tx) pool.promoteTx(addr, hash, tx)
} }
// Drop all transactions over the allowed limit // Drop all transactions over the allowed limit
for _, tx := range list.Cap(int(maxQueuedPerAccount)) { for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
hash := tx.Hash() hash := tx.Hash()
log.Trace("Removed cap-exceeding queued transaction", "hash", hash) log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
delete(pool.all, hash) delete(pool.all, hash)
@ -663,13 +708,13 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
for _, list := range pool.pending { for _, list := range pool.pending {
pending += uint64(list.Len()) pending += uint64(list.Len())
} }
if pending > maxPendingTotal { if pending > pool.config.GlobalSlots {
pendingBeforeCap := pending pendingBeforeCap := pending
// Assemble a spam order to penalize large transactors first // Assemble a spam order to penalize large transactors first
spammers := prque.New() spammers := prque.New()
for addr, list := range pool.pending { for addr, list := range pool.pending {
// Only evict transactions from high rollers // Only evict transactions from high rollers
if uint64(list.Len()) > minPendingPerAccount { if uint64(list.Len()) > pool.config.AccountSlots {
// Skip local accounts as pools should maintain backlogs for themselves // Skip local accounts as pools should maintain backlogs for themselves
for _, tx := range list.txs.items { for _, tx := range list.txs.items {
if !pool.locals.contains(tx.Hash()) { if !pool.locals.contains(tx.Hash()) {
@ -681,7 +726,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
} }
// Gradually drop transactions from offenders // Gradually drop transactions from offenders
offenders := []common.Address{} offenders := []common.Address{}
for pending > maxPendingTotal && !spammers.Empty() { for pending > pool.config.GlobalSlots && !spammers.Empty() {
// Retrieve the next offender if not local address // Retrieve the next offender if not local address
offender, _ := spammers.Pop() offender, _ := spammers.Pop()
offenders = append(offenders, offender.(common.Address)) offenders = append(offenders, offender.(common.Address))
@ -692,7 +737,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
threshold := pool.pending[offender.(common.Address)].Len() threshold := pool.pending[offender.(common.Address)].Len()
// Iteratively reduce all offenders until below limit or threshold reached // Iteratively reduce all offenders until below limit or threshold reached
for pending > maxPendingTotal && pool.pending[offenders[len(offenders)-2]].Len() > threshold { for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
for i := 0; i < len(offenders)-1; i++ { for i := 0; i < len(offenders)-1; i++ {
list := pool.pending[offenders[i]] list := pool.pending[offenders[i]]
list.Cap(list.Len() - 1) list.Cap(list.Len() - 1)
@ -702,8 +747,8 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
} }
} }
// If still above threshold, reduce to limit or min allowance // If still above threshold, reduce to limit or min allowance
if pending > maxPendingTotal && len(offenders) > 0 { if pending > pool.config.GlobalSlots && len(offenders) > 0 {
for pending > maxPendingTotal && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > minPendingPerAccount { for pending > pool.config.GlobalSlots && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > pool.config.AccountSlots {
for _, addr := range offenders { for _, addr := range offenders {
list := pool.pending[addr] list := pool.pending[addr]
list.Cap(list.Len() - 1) list.Cap(list.Len() - 1)
@ -714,7 +759,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
pendingRLCounter.Inc(int64(pendingBeforeCap - pending)) pendingRLCounter.Inc(int64(pendingBeforeCap - pending))
} }
// If we've queued more transactions than the hard limit, drop oldest ones // If we've queued more transactions than the hard limit, drop oldest ones
if queued > maxQueuedTotal { if queued > pool.config.GlobalQueue {
// Sort all accounts with queued transactions by heartbeat // Sort all accounts with queued transactions by heartbeat
addresses := make(addresssByHeartbeat, 0, len(pool.queue)) addresses := make(addresssByHeartbeat, 0, len(pool.queue))
for addr := range pool.queue { for addr := range pool.queue {
@ -723,7 +768,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
sort.Sort(addresses) sort.Sort(addresses)
// Drop transactions until the total is below the limit // Drop transactions until the total is below the limit
for drop := queued - maxQueuedTotal; drop > 0; { for drop := queued - pool.config.GlobalQueue; drop > 0; {
addr := addresses[len(addresses)-1] addr := addresses[len(addresses)-1]
list := pool.queue[addr.address] list := pool.queue[addr.address]
@ -800,7 +845,7 @@ func (pool *TxPool) expirationLoop() {
case <-evict.C: case <-evict.C:
pool.mu.Lock() pool.mu.Lock()
for addr := range pool.queue { for addr := range pool.queue {
if time.Since(pool.beats[addr]) > maxQueuedLifetime { if time.Since(pool.beats[addr]) > pool.config.Lifetime {
for _, tx := range pool.queue[addr].Flatten() { for _, tx := range pool.queue[addr].Flatten() {
pool.removeTx(tx.Hash()) pool.removeTx(tx.Hash())
} }

View File

@ -46,7 +46,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
newPool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) newPool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
newPool.resetState() newPool.resetState()
return newPool, key return newPool, key
@ -95,7 +95,7 @@ func TestStateChangeDuringPoolReset(t *testing.T) {
gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) } gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
txpool := NewTxPool(params.TestChainConfig, mux, stateFunc, gasLimitFunc) txpool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, mux, stateFunc, gasLimitFunc)
txpool.resetState() txpool.resetState()
nonce := txpool.State().GetNonce(address) nonce := txpool.State().GetNonce(address)
@ -533,25 +533,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 <= maxQueuedPerAccount+5; i++ { for i := uint64(1); i <= DefaultTxPoolConfig.AccountQueue+5; i++ {
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil { if err := pool.Add(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 <= maxQueuedPerAccount { if i <= DefaultTxPoolConfig.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(maxQueuedPerAccount) { if pool.queue[account].Len() != int(DefaultTxPoolConfig.AccountQueue) {
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), maxQueuedPerAccount) t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), DefaultTxPoolConfig.AccountQueue)
} }
} }
} }
if len(pool.all) != int(maxQueuedPerAccount) { if len(pool.all) != int(DefaultTxPoolConfig.AccountQueue) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount) t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), DefaultTxPoolConfig.AccountQueue)
} }
} }
@ -559,14 +559,14 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
// some threshold, the higher transactions are dropped to prevent DOS attacks. // some threshold, the higher transactions are dropped to prevent DOS attacks.
func TestTransactionQueueGlobalLimiting(t *testing.T) { func TestTransactionQueueGlobalLimiting(t *testing.T) {
// Reduce the queue limits to shorten test time // Reduce the queue limits to shorten test time
defer func(old uint64) { maxQueuedTotal = old }(maxQueuedTotal) defer func(old uint64) { DefaultTxPoolConfig.GlobalQueue = old }(DefaultTxPoolConfig.GlobalQueue)
maxQueuedTotal = maxQueuedPerAccount * 3 DefaultTxPoolConfig.GlobalQueue = DefaultTxPoolConfig.AccountQueue * 3
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
@ -580,7 +580,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
// Generate and queue a batch of transactions // Generate and queue a batch of transactions
nonces := make(map[common.Address]uint64) nonces := make(map[common.Address]uint64)
txs := make(types.Transactions, 0, 3*maxQueuedTotal) txs := make(types.Transactions, 0, 3*DefaultTxPoolConfig.GlobalQueue)
for len(txs) < cap(txs) { for len(txs) < cap(txs) {
key := keys[rand.Intn(len(keys))] key := keys[rand.Intn(len(keys))]
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
@ -593,13 +593,13 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
queued := 0 queued := 0
for addr, list := range pool.queue { for addr, list := range pool.queue {
if list.Len() > int(maxQueuedPerAccount) { if list.Len() > int(DefaultTxPoolConfig.AccountQueue) {
t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), maxQueuedPerAccount) t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), DefaultTxPoolConfig.AccountQueue)
} }
queued += list.Len() queued += list.Len()
} }
if queued > int(maxQueuedTotal) { if queued > int(DefaultTxPoolConfig.GlobalQueue) {
t.Fatalf("total transactions overflow allowance: %d > %d", queued, maxQueuedTotal) t.Fatalf("total transactions overflow allowance: %d > %d", queued, DefaultTxPoolConfig.GlobalQueue)
} }
} }
@ -608,9 +608,9 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
// on shuffling them around. // on shuffling them around.
func TestTransactionQueueTimeLimiting(t *testing.T) { func TestTransactionQueueTimeLimiting(t *testing.T) {
// Reduce the queue limits to shorten test time // Reduce the queue limits to shorten test time
defer func(old time.Duration) { maxQueuedLifetime = old }(maxQueuedLifetime) defer func(old time.Duration) { DefaultTxPoolConfig.Lifetime = old }(DefaultTxPoolConfig.Lifetime)
defer func(old time.Duration) { evictionInterval = old }(evictionInterval) defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
maxQueuedLifetime = time.Second DefaultTxPoolConfig.Lifetime = time.Second
evictionInterval = time.Second evictionInterval = time.Second
// Create a test account and fund it // Create a test account and fund it
@ -621,7 +621,7 @@ func TestTransactionQueueTimeLimiting(t *testing.T) {
state.AddBalance(account, big.NewInt(1000000)) state.AddBalance(account, big.NewInt(1000000))
// Queue up a batch of transactions // Queue up a batch of transactions
for i := uint64(1); i <= maxQueuedPerAccount; i++ { for i := uint64(1); i <= DefaultTxPoolConfig.AccountQueue; i++ {
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil { if err := pool.Add(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)
} }
@ -646,7 +646,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 < maxQueuedPerAccount+5; i++ { for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil { if err := pool.Add(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)
} }
@ -657,8 +657,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(maxQueuedPerAccount+5) { if len(pool.all) != int(DefaultTxPoolConfig.AccountQueue+5) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount+5) t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), DefaultTxPoolConfig.AccountQueue+5)
} }
} }
@ -674,7 +674,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 < maxQueuedPerAccount+5; i++ { for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
if err := pool1.Add(transaction(origin+i, big.NewInt(100000), key1)); err != nil { if err := pool1.Add(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)
} }
@ -686,7 +686,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 < maxQueuedPerAccount+5; i++ { for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2)) txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
} }
pool2.AddBatch(txns) pool2.AddBatch(txns)
@ -708,14 +708,14 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
// attacks. // attacks.
func TestTransactionPendingGlobalLimiting(t *testing.T) { func TestTransactionPendingGlobalLimiting(t *testing.T) {
// Reduce the queue limits to shorten test time // Reduce the queue limits to shorten test time
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal) defer func(old uint64) { DefaultTxPoolConfig.GlobalSlots = old }(DefaultTxPoolConfig.GlobalSlots)
maxPendingTotal = minPendingPerAccount * 10 DefaultTxPoolConfig.GlobalSlots = DefaultTxPoolConfig.AccountSlots * 10
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
@ -732,7 +732,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
txs := types.Transactions{} txs := types.Transactions{}
for _, key := range keys { for _, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
for j := 0; j < int(maxPendingTotal)/len(keys)*2; j++ { for j := 0; j < int(DefaultTxPoolConfig.GlobalSlots)/len(keys)*2; j++ {
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key)) txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
nonces[addr]++ nonces[addr]++
} }
@ -744,8 +744,8 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
for _, list := range pool.pending { for _, list := range pool.pending {
pending += list.Len() pending += list.Len()
} }
if pending > int(maxPendingTotal) { if pending > int(DefaultTxPoolConfig.GlobalSlots) {
t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, maxPendingTotal) t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, DefaultTxPoolConfig.GlobalSlots)
} }
} }
@ -754,14 +754,14 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
// the transactions are still kept. // the transactions are still kept.
func TestTransactionPendingMinimumAllowance(t *testing.T) { func TestTransactionPendingMinimumAllowance(t *testing.T) {
// Reduce the queue limits to shorten test time // Reduce the queue limits to shorten test time
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal) defer func(old uint64) { DefaultTxPoolConfig.GlobalSlots = old }(DefaultTxPoolConfig.GlobalSlots)
maxPendingTotal = 0 DefaultTxPoolConfig.GlobalSlots = 0
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
@ -778,7 +778,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
txs := types.Transactions{} txs := types.Transactions{}
for _, key := range keys { for _, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
for j := 0; j < int(minPendingPerAccount)*2; j++ { for j := 0; j < int(DefaultTxPoolConfig.AccountSlots)*2; j++ {
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key)) txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
nonces[addr]++ nonces[addr]++
} }
@ -787,8 +787,8 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
pool.AddBatch(txs) pool.AddBatch(txs)
for addr, list := range pool.pending { for addr, list := range pool.pending {
if list.Len() != int(minPendingPerAccount) { if list.Len() != int(DefaultTxPoolConfig.AccountSlots) {
t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), minPendingPerAccount) t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), DefaultTxPoolConfig.AccountSlots)
} }
} }
} }
@ -803,7 +803,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
@ -874,17 +874,17 @@ func TestTransactionPoolRepricing(t *testing.T) {
// Note, local transactions are never allowed to be dropped. // Note, local transactions are never allowed to be dropped.
func TestTransactionPoolUnderpricing(t *testing.T) { func TestTransactionPoolUnderpricing(t *testing.T) {
// Reduce the queue limits to shorten test time // Reduce the queue limits to shorten test time
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal) defer func(old uint64) { DefaultTxPoolConfig.GlobalSlots = old }(DefaultTxPoolConfig.GlobalSlots)
maxPendingTotal = 2 DefaultTxPoolConfig.GlobalSlots = 2
defer func(old uint64) { maxQueuedTotal = old }(maxQueuedTotal) defer func(old uint64) { DefaultTxPoolConfig.GlobalQueue = old }(DefaultTxPoolConfig.GlobalQueue)
maxQueuedTotal = 2 DefaultTxPoolConfig.GlobalQueue = 2
// Create the pool to test the pricing enforcement with // Create the pool to test the pricing enforcement with
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
@ -960,7 +960,7 @@ func TestTransactionReplacement(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a a test account to add transactions with // Create a a test account to add transactions with
@ -971,7 +971,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 + minPriceBumpPercent)) / 100 threshold := (price * (100 + int64(DefaultTxPoolConfig.PriceBump))) / 100
if err := pool.Add(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), key)); err != nil { if err := pool.Add(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)

View File

@ -150,7 +150,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
core.WriteChainConfig(chainDb, genesisHash, chainConfig) core.WriteChainConfig(chainDb, genesisHash, chainConfig)
} }
newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) newPool := core.NewTxPool(config.TxPool, eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
eth.txPool = newPool eth.txPool = newPool
maxPeers := config.MaxPeers maxPeers := config.MaxPeers

View File

@ -44,6 +44,7 @@ var DefaultConfig = Config{
DatabaseCache: 128, DatabaseCache: 128,
GasPrice: big.NewInt(18 * params.Shannon), GasPrice: big.NewInt(18 * params.Shannon),
TxPool: core.DefaultTxPoolConfig,
GPO: gasprice.Config{ GPO: gasprice.Config{
Blocks: 10, Blocks: 10,
Percentile: 50, Percentile: 50,
@ -99,6 +100,9 @@ type Config struct {
EthashDatasetsInMem int EthashDatasetsInMem int
EthashDatasetsOnDisk int EthashDatasetsOnDisk int
// Transaction pool options
TxPool core.TxPoolConfig
// Gas Price Oracle options // Gas Price Oracle options
GPO gasprice.Config GPO gasprice.Config

View File

@ -33,6 +33,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
EthashDatasetDir string EthashDatasetDir string
EthashDatasetsInMem int EthashDatasetsInMem int
EthashDatasetsOnDisk int EthashDatasetsOnDisk int
TxPool core.TxPoolConfig
GPO gasprice.Config GPO gasprice.Config
EnablePreimageRecording bool EnablePreimageRecording bool
DocRoot string `toml:"-"` DocRoot string `toml:"-"`
@ -60,6 +61,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.EthashDatasetDir = c.EthashDatasetDir enc.EthashDatasetDir = c.EthashDatasetDir
enc.EthashDatasetsInMem = c.EthashDatasetsInMem enc.EthashDatasetsInMem = c.EthashDatasetsInMem
enc.EthashDatasetsOnDisk = c.EthashDatasetsOnDisk enc.EthashDatasetsOnDisk = c.EthashDatasetsOnDisk
enc.TxPool = c.TxPool
enc.GPO = c.GPO enc.GPO = c.GPO
enc.EnablePreimageRecording = c.EnablePreimageRecording enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.DocRoot = c.DocRoot enc.DocRoot = c.DocRoot
@ -90,6 +92,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
EthashDatasetDir *string EthashDatasetDir *string
EthashDatasetsInMem *int EthashDatasetsInMem *int
EthashDatasetsOnDisk *int EthashDatasetsOnDisk *int
TxPool *core.TxPoolConfig
GPO *gasprice.Config GPO *gasprice.Config
EnablePreimageRecording *bool EnablePreimageRecording *bool
DocRoot *string `toml:"-"` DocRoot *string `toml:"-"`
@ -158,6 +161,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.EthashDatasetsOnDisk != nil { if dec.EthashDatasetsOnDisk != nil {
c.EthashDatasetsOnDisk = *dec.EthashDatasetsOnDisk c.EthashDatasetsOnDisk = *dec.EthashDatasetsOnDisk
} }
if dec.TxPool != nil {
c.TxPool = *dec.TxPool
}
if dec.GPO != nil { if dec.GPO != nil {
c.GPO = *dec.GPO c.GPO = *dec.GPO
} }