From 123ff368f4cb0c0a9125e5450dfb806b9ec85efc Mon Sep 17 00:00:00 2001 From: David Hill <dhill@mindcry.org> Date: Mon, 11 Apr 2016 17:37:52 -0400 Subject: [PATCH] mempool: Create and use mempoolPolicy. (#571) mempoolPolicy contains the values that configure the mempool policy. This decouples the values from the internals of btcd to move closer to a mempool package. --- config.go | 46 ++++++++++++++------------- mempool.go | 91 +++++++++++++++++++++++++++++------------------------- server.go | 18 ++++++----- 3 files changed, 84 insertions(+), 71 deletions(-) diff --git a/config.go b/config.go index 92b9798d..cd1f25c6 100644 --- a/config.go +++ b/config.go @@ -26,27 +26,29 @@ import ( ) const ( - defaultConfigFilename = "btcd.conf" - defaultDataDirname = "data" - defaultLogLevel = "info" - defaultLogDirname = "logs" - defaultLogFilename = "btcd.log" - defaultMaxPeers = 125 - defaultBanDuration = time.Hour * 24 - defaultBanThreshold = 100 - defaultMaxRPCClients = 10 - defaultMaxRPCWebsockets = 25 - defaultVerifyEnabled = false - defaultDbType = "leveldb" - defaultFreeTxRelayLimit = 15.0 - defaultBlockMinSize = 0 - defaultBlockMaxSize = 750000 - blockMaxSizeMin = 1000 - blockMaxSizeMax = wire.MaxBlockPayload - 1000 - defaultBlockPrioritySize = 50000 - defaultGenerate = false - defaultAddrIndex = false - defaultSigCacheMaxSize = 50000 + defaultConfigFilename = "btcd.conf" + defaultDataDirname = "data" + defaultLogLevel = "info" + defaultLogDirname = "logs" + defaultLogFilename = "btcd.log" + defaultMaxPeers = 125 + defaultBanDuration = time.Hour * 24 + defaultBanThreshold = 100 + defaultMaxRPCClients = 10 + defaultMaxRPCWebsockets = 25 + defaultVerifyEnabled = false + defaultDbType = "leveldb" + defaultFreeTxRelayLimit = 15.0 + defaultBlockMinSize = 0 + defaultBlockMaxSize = 750000 + blockMaxSizeMin = 1000 + blockMaxSizeMax = wire.MaxBlockPayload - 1000 + defaultBlockPrioritySize = 50000 + defaultGenerate = false + defaultAddrIndex = false + defaultMaxOrphanTransactions = 1000 + defaultMaxOrphanTxSize = 5000 + defaultSigCacheMaxSize = 50000 ) var ( @@ -341,8 +343,8 @@ func loadConfig() (*config, []string, error) { BlockMinSize: defaultBlockMinSize, BlockMaxSize: defaultBlockMaxSize, BlockPrioritySize: defaultBlockPrioritySize, + MaxOrphanTxs: defaultMaxOrphanTransactions, SigCacheMaxSize: defaultSigCacheMaxSize, - MaxOrphanTxs: maxOrphanTransactions, Generate: defaultGenerate, AddrIndex: defaultAddrIndex, } diff --git a/mempool.go b/mempool.go index 8c276a84..8872d8a3 100644 --- a/mempool.go +++ b/mempool.go @@ -26,20 +26,6 @@ const ( // mempoolHeight is the height used for the "block" height field of the // contextual transaction information provided in a transaction store. mempoolHeight = 0x7fffffff - - // maxOrphanTransactions is the maximum number of orphan transactions - // that can be queued. - maxOrphanTransactions = 1000 - - // maxOrphanTxSize is the maximum size allowed for orphan transactions. - // This helps prevent memory exhaustion attacks from sending a lot of - // of big orphans. - maxOrphanTxSize = 5000 - - // maxSigOpsPerTx is the maximum number of signature operations - // in a single transaction we will relay or mine. It is a fraction - // of the max signature operations for a block. - maxSigOpsPerTx = blockchain.MaxSigOpsPerBlock / 5 ) // mempoolTxDesc is a descriptor containing a transaction in the mempool along @@ -54,10 +40,6 @@ type mempoolTxDesc struct { // mempoolConfig is a descriptor containing the memory pool configuration. type mempoolConfig struct { - // DisableRelayPriority defines whether to relay free or low-fee - // transactions that do not have enough priority to be relayed. - DisableRelayPriority bool - // EnableAddrIndex defines whether the address index should be enabled. EnableAddrIndex bool @@ -65,21 +47,13 @@ type mempoolConfig struct { // transacation information. FetchTransactionStore func(*btcutil.Tx, bool) (blockchain.TxStore, error) - // FreeTxRelayLimit defines the given amount in thousands of bytes - // per minute that transactions with no fee are rate limited to. - FreeTxRelayLimit float64 - - // MaxOrphanTxs defines the maximum number of orphan transactions to - // keep in memory. - MaxOrphanTxs int - - // MinRelayTxFee defines the minimum transaction fee in BTC/kB to be - // considered a non-zero fee. - MinRelayTxFee btcutil.Amount - // NewestSha defines the function to retrieve the newest sha NewestSha func() (*wire.ShaHash, int32, error) + // Policy defines the various mempool configuration options related + // to policy. + Policy mempoolPolicy + // RelayNtfnChan defines the channel to send newly accepted transactions // to. If unset or set to nil, notifications will not be sent. RelayNtfnChan chan *btcutil.Tx @@ -91,6 +65,36 @@ type mempoolConfig struct { TimeSource blockchain.MedianTimeSource } +// mempoolPolicy houses the policy (configuration parameters) which is used to +// control the mempool. +type mempoolPolicy struct { + // DisableRelayPriority defines whether to relay free or low-fee + // transactions that do not have enough priority to be relayed. + DisableRelayPriority bool + + // FreeTxRelayLimit defines the given amount in thousands of bytes + // per minute that transactions with no fee are rate limited to. + FreeTxRelayLimit float64 + + // MaxOrphanTxs is the maximum number of orphan transactions + // that can be queued. + MaxOrphanTxs int + + // MaxOrphanTxSize is the maximum size allowed for orphan transactions. + // This helps prevent memory exhaustion attacks from sending a lot of + // of big orphans. + MaxOrphanTxSize int + + // MaxSigOpsPerTx is the maximum number of signature operations + // in a single transaction we will relay or mine. It is a fraction + // of the max signature operations for a block. + MaxSigOpsPerTx int + + // MinRelayTxFee defines the minimum transaction fee in BTC/kB to be + // considered a non-zero fee. + MinRelayTxFee btcutil.Amount +} + // txMemPool is used as a source of transactions that need to be mined into // blocks and relayed to other peers. It is safe for concurrent access from // multiple peers. @@ -156,7 +160,9 @@ func (mp *txMemPool) RemoveOrphan(txHash *wire.ShaHash) { // // This function MUST be called with the mempool lock held (for writes). func (mp *txMemPool) limitNumOrphans() error { - if len(mp.orphans)+1 > mp.cfg.MaxOrphanTxs && mp.cfg.MaxOrphanTxs > 0 { + if len(mp.orphans)+1 > mp.cfg.Policy.MaxOrphanTxs && + mp.cfg.Policy.MaxOrphanTxs > 0 { + // Generate a cryptographically random hash. randHashBytes := make([]byte, wire.HashSize) _, err := rand.Read(randHashBytes) @@ -222,13 +228,13 @@ func (mp *txMemPool) maybeAddOrphan(tx *btcutil.Tx) error { // // Note that the number of orphan transactions in the orphan pool is // also limited, so this equates to a maximum memory used of - // maxOrphanTxSize * mp.cfg.MaxOrphanTxs (which is ~5MB using the default - // values at the time this comment was written). + // mp.cfg.Policy.MaxOrphanTxSize * mp.cfg.Policy.MaxOrphanTxs (which is ~5MB + // using the default values at the time this comment was written). serializedLen := tx.MsgTx().SerializeSize() - if serializedLen > maxOrphanTxSize { + if serializedLen > mp.cfg.Policy.MaxOrphanTxSize { str := fmt.Sprintf("orphan transaction size of %d bytes is "+ "larger than max allowed size of %d bytes", - serializedLen, maxOrphanTxSize) + serializedLen, mp.cfg.Policy.MaxOrphanTxSize) return txRuleError(wire.RejectNonstandard, str) } @@ -654,7 +660,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo // forbid their relaying. if !activeNetParams.RelayNonStdTxs { err := checkTransactionStandard(tx, nextBlockHeight, - mp.cfg.TimeSource, mp.cfg.MinRelayTxFee) + mp.cfg.TimeSource, mp.cfg.Policy.MinRelayTxFee) if err != nil { // Attempt to extract a reject code from the error so // it can be retained. When not possible, fall back to @@ -767,9 +773,9 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo return nil, err } numSigOps += blockchain.CountSigOps(tx) - if numSigOps > maxSigOpsPerTx { + if numSigOps > mp.cfg.Policy.MaxSigOpsPerTx { str := fmt.Sprintf("transaction %v has too many sigops: %d > %d", - txHash, numSigOps, maxSigOpsPerTx) + txHash, numSigOps, mp.cfg.Policy.MaxSigOpsPerTx) return nil, txRuleError(wire.RejectNonstandard, str) } @@ -785,7 +791,8 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo // transaction does not exceeed 1000 less than the reserved space for // high-priority transactions, don't require a fee for it. serializedSize := int64(tx.MsgTx().SerializeSize()) - minFee := calcMinRequiredTxRelayFee(serializedSize, mp.cfg.MinRelayTxFee) + minFee := calcMinRequiredTxRelayFee(serializedSize, + mp.cfg.Policy.MinRelayTxFee) if serializedSize >= (defaultBlockPrioritySize-1000) && txFee < minFee { str := fmt.Sprintf("transaction %v has %d fees which is under "+ "the required amount of %d", txHash, txFee, @@ -797,7 +804,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo // in the next block. Transactions which are being added back to the // memory pool from blocks that have been disconnected during a reorg // are exempted. - if isNew && !mp.cfg.DisableRelayPriority && txFee < minFee { + if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee { currentPriority := calcPriority(tx.MsgTx(), txStore, nextBlockHeight) if currentPriority <= minHighPriority { @@ -819,7 +826,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo mp.lastPennyUnix = nowUnix // Are we still over the limit? - if mp.pennyTotal >= mp.cfg.FreeTxRelayLimit*10*1000 { + if mp.pennyTotal >= mp.cfg.Policy.FreeTxRelayLimit*10*1000 { str := fmt.Sprintf("transaction %v has been rejected "+ "by the rate limiter due to low fees", txHash) return nil, txRuleError(wire.RejectInsufficientFee, str) @@ -829,7 +836,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo mp.pennyTotal += float64(serializedSize) txmpLog.Tracef("rate limit: curTotal %v, nextTotal: %v, "+ "limit %v", oldTotal, mp.pennyTotal, - mp.cfg.FreeTxRelayLimit*10*1000) + mp.cfg.Policy.FreeTxRelayLimit*10*1000) } // Verify crypto signatures for each input and reject the transaction if diff --git a/server.go b/server.go index e1c08537..27ae18ff 100644 --- a/server.go +++ b/server.go @@ -2480,16 +2480,20 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param s.blockManager = bm txC := mempoolConfig{ - DisableRelayPriority: cfg.NoRelayPriority, EnableAddrIndex: cfg.AddrIndex, FetchTransactionStore: s.blockManager.blockChain.FetchTransactionStore, - FreeTxRelayLimit: cfg.FreeTxRelayLimit, - MaxOrphanTxs: cfg.MaxOrphanTxs, - MinRelayTxFee: cfg.minRelayTxFee, NewestSha: s.db.NewestSha, - RelayNtfnChan: s.relayNtfnChan, - SigCache: s.sigCache, - TimeSource: s.timeSource, + Policy: mempoolPolicy{ + DisableRelayPriority: cfg.NoRelayPriority, + FreeTxRelayLimit: cfg.FreeTxRelayLimit, + MaxOrphanTxs: cfg.MaxOrphanTxs, + MaxOrphanTxSize: defaultMaxOrphanTxSize, + MaxSigOpsPerTx: blockchain.MaxSigOpsPerBlock / 5, + MinRelayTxFee: cfg.minRelayTxFee, + }, + RelayNtfnChan: s.relayNtfnChan, + SigCache: s.sigCache, + TimeSource: s.timeSource, } s.txMemPool = newTxMemPool(&txC)