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)