Repository: decred/dcrd
Pull request #1973 (2019-10-28 00:16:51)

blockchain: removed unused params
while here, remove whitespace and replace an always true
comparison



Patch:
diff --git a/blockchain/scriptval.go b/blockchain/scriptval.go
index 9fd5b535..e602ba73 100644
--- a/blockchain/scriptval.go
+++ b/blockchain/scriptval.go
@@ -216,7 +216,6 @@ func ValidateTransactionScripts(tx *dcrutil.Tx, utxoView *UtxoViewpoint, flags t
 
 	// Validate all of the inputs.
 	return newTxValidator(utxoView, flags, sigCache).Validate(txValItems)
-
 }
 
 // checkBlockScripts executes and validates the scripts for all transactions in
diff --git a/blockchain/stakeversion.go b/blockchain/stakeversion.go
index a767a15d..af9c84e7 100644
--- a/blockchain/stakeversion.go
+++ b/blockchain/stakeversion.go
@@ -82,7 +82,7 @@ func (b *BlockChain) isStakeMajorityVersion(minVer uint32, prevNode *blockNode)
 	// Walk blockchain backwards to calculate version.
 	node := b.findStakeVersionPriorNode(prevNode)
 	if node == nil {
-		return 0 >= minVer
+		return 0 == minVer
 	}
 
 	// Generate map key and look up cached result.
diff --git a/blockchain/thresholdstate.go b/blockchain/thresholdstate.go
index a0beecff..36d86a05 100644
--- a/blockchain/thresholdstate.go
+++ b/blockchain/thresholdstate.go
@@ -620,7 +620,6 @@ func (b *BlockChain) isLNFeaturesAgendaActive(prevNode *blockNode) (bool, error)
 	// here because there is only one possible choice that can be active for
 	// the agenda, which is yes, so there is no need to check it.
 	return state.State == ThresholdActive, nil
-
 }
 
 // IsLNFeaturesAgendaActive returns whether or not the LN features agenda vote,
diff --git a/blockchain/validate.go b/blockchain/validate.go
index 37f9e36b..d1e168fc 100644
--- a/blockchain/validate.go
+++ b/blockchain/validate.go
@@ -2587,7 +2587,7 @@ func checkNumSigOps(tx *dcrutil.Tx, view *UtxoViewpoint, index int, txTree bool,
 // checkStakeBaseAmounts calculates the total amount given as subsidy from
 // single stakebase transactions (votes) within a block.  This function skips a
 // ton of checks already performed by CheckTransactionInputs.
-func checkStakeBaseAmounts(subsidyCache *standalone.SubsidyCache, height int64, params *chaincfg.Params, txs []*dcrutil.Tx, view *UtxoViewpoint) error {
+func checkStakeBaseAmounts(subsidyCache *standalone.SubsidyCache, height int64, txs []*dcrutil.Tx, view *UtxoViewpoint) error {
 	for _, tx := range txs {
 		msgTx := tx.MsgTx()
 		if stake.IsSSGen(msgTx) {
@@ -2662,7 +2662,7 @@ func getStakeBaseAmounts(txs []*dcrutil.Tx, view *UtxoViewpoint) (int64, error)
 
 // getStakeTreeFees determines the amount of fees for in the stake tx tree of
 // some node given a transaction store.
-func getStakeTreeFees(subsidyCache *standalone.SubsidyCache, height int64, params *chaincfg.Params, txs []*dcrutil.Tx, view *UtxoViewpoint) (dcrutil.Amount, error) {
+func getStakeTreeFees(subsidyCache *standalone.SubsidyCache, height int64, txs []*dcrutil.Tx, view *UtxoViewpoint) (dcrutil.Amount, error) {
 	totalInputs := int64(0)
 	totalOutputs := int64(0)
 	for _, tx := range txs {
@@ -2824,7 +2824,7 @@ func (b *BlockChain) checkTransactionsAndConnect(inputFees dcrutil.Amount, node
 		}
 
 		err := checkStakeBaseAmounts(b.subsidyCache, node.height,
-			b.chainParams, txs, view)
+			txs, view)
 		if err != nil {
 			return err
 		}
@@ -2998,7 +2998,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B
 	}
 
 	stakeTreeFees, err := getStakeTreeFees(b.subsidyCache, node.height,
-		b.chainParams, block.STransactions(), view)
+		block.STransactions(), view)
 	if err != nil {
 		log.Tracef("getStakeTreeFees failed for TxTreeStake: %v", err)
 		return err
diff --git a/config.go b/config.go
index cf8a63c2..fa662461 100644
--- a/config.go
+++ b/config.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/decred/dcrd/connmgr/v2"
+	"github.com/decred/dcrd/connmgr/v3"
 	"github.com/decred/dcrd/database/v2"
 	_ "github.com/decred/dcrd/database/v2/ffldb"
 	"github.com/decred/dcrd/dcrutil/v3"
diff --git a/connmgr/go.mod b/connmgr/go.mod
index 30de2993..8d7ec5b4 100644
--- a/connmgr/go.mod
+++ b/connmgr/go.mod
@@ -1,4 +1,4 @@
-module github.com/decred/dcrd/connmgr/v2
+module github.com/decred/dcrd/connmgr/v3
 
 go 1.11
 
diff --git a/go.mod b/go.mod
index 056e9d3c..ad791653 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/decred/dcrd/certgen v1.1.0
 	github.com/decred/dcrd/chaincfg/chainhash v1.0.2
 	github.com/decred/dcrd/chaincfg/v2 v2.3.0
-	github.com/decred/dcrd/connmgr/v2 v2.1.0
+	github.com/decred/dcrd/connmgr/v3 v3.0.0-00010101000000-000000000000
 	github.com/decred/dcrd/crypto/ripemd160 v1.0.0
 	github.com/decred/dcrd/database/v2 v2.0.1
 	github.com/decred/dcrd/dcrec v1.0.0
@@ -49,7 +49,7 @@ replace (
 	github.com/decred/dcrd/certgen => ./certgen
 	github.com/decred/dcrd/chaincfg/chainhash => ./chaincfg/chainhash
 	github.com/decred/dcrd/chaincfg/v2 => ./chaincfg
-	github.com/decred/dcrd/connmgr/v2 => ./connmgr
+	github.com/decred/dcrd/connmgr/v3 => ./connmgr
 	github.com/decred/dcrd/crypto/blake256 => ./crypto/blake256
 	github.com/decred/dcrd/crypto/ripemd160 => ./crypto/ripemd160
 	github.com/decred/dcrd/database/v2 => ./database
diff --git a/log.go b/log.go
index e7fc401b..654be62f 100644
--- a/log.go
+++ b/log.go
@@ -14,7 +14,7 @@ import (
 	"github.com/decred/dcrd/blockchain/stake/v3"
 	"github.com/decred/dcrd/blockchain/v3"
 	"github.com/decred/dcrd/blockchain/v3/indexers"
-	"github.com/decred/dcrd/connmgr/v2"
+	"github.com/decred/dcrd/connmgr/v3"
 	"github.com/decred/dcrd/database/v2"
 	"github.com/decred/dcrd/fees/v2"
 	"github.com/decred/dcrd/internal/rpcserver"
diff --git a/mempool/log.go b/mempool/log.go
index 1e4cd8e2..22616948 100644
--- a/mempool/log.go
+++ b/mempool/log.go
@@ -29,3 +29,12 @@ func DisableLog() {
 func UseLogger(logger slog.Logger) {
 	log = logger
 }
+
+// pickNoun returns the singular or plural form of a noun depending
+// on the count n.
+func pickNoun(n int, singular, plural string) string {
+	if n == 1 {
+		return singular
+	}
+	return plural
+}
diff --git a/mempool/mempool.go b/mempool/mempool.go
index f9373fc1..bb354451 100644
--- a/mempool/mempool.go
+++ b/mempool/mempool.go
@@ -56,6 +56,15 @@ const (
 	// maxNullDataOutputs is the maximum number of OP_RETURN null data
 	// pushes in a transaction, after which it is considered non-standard.
 	maxNullDataOutputs = 4
+
+	// orphanTTL is the maximum amount of time an orphan is allowed to
+	// stay in the orphan pool before it expires and is evicted during the
+	// next scan.
+	orphanTTL = time.Minute * 15
+
+	// orphanExpireScanInterval is the minimum amount of time in between
+	// scans of the orphan pool to evict expired transactions.
+	orphanExpireScanInterval = time.Minute * 5
 )
 
 // Config is a descriptor containing the memory pool configuration.
@@ -217,6 +226,14 @@ type VerboseTxDesc struct {
 	Depends []*TxDesc
 }
 
+// orphanTx is a normal transaction that references an ancestor transaction
+// that is not yet available.  It also contains additional information related
+// to it such as an expiration time to help prevent caching the orphan forever.
+type orphanTx struct {
+	tx         *dcrutil.Tx
+	expiration time.Time
+}
+
 // TxPool 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.
@@ -224,10 +241,11 @@ type TxPool struct {
 	// The following variables must only be used atomically.
 	lastUpdated int64 // last time pool was updated.
 
-	mtx           sync.RWMutex
-	cfg           Config
-	pool          map[chainhash.Hash]*TxDesc
-	orphans       map[chainhash.Hash]*dcrutil.Tx
+	mtx  sync.RWMutex
+	cfg  Config
+	pool map[chainhash.Hash]*TxDesc
+
+	orphans       map[chainhash.Hash]*orphanTx
 	orphansByPrev map[wire.OutPoint]map[chainhash.Hash]*dcrutil.Tx
 	outpoints     map[wire.OutPoint]*dcrutil.Tx
 
@@ -237,6 +255,12 @@ type TxPool struct {
 
 	pennyTotal    float64 // exponentially decaying total for penny spends.
 	lastPennyUnix int64   // unix time of last ``penny spend''
+
+	// nextExpireScan is the time after which the orphan pool will be
+	// scanned in order to evict orphans.  This is NOT a hard deadline as
+	// the scan will only run when an orphan is added to the pool as opposed
+	// to on an unconditional timer.
+	nextExpireScan time.Time
 }
 
 // insertVote inserts a vote into the map of block votes.
@@ -329,10 +353,9 @@ var _ mining.TxSource = (*TxPool)(nil)
 //
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *TxPool) removeOrphan(tx *dcrutil.Tx, removeRedeemers bool) {
-	txHash := tx.Hash()
-
 	// Nothing to do if passed tx is not an orphan.
-	tx, exists := mp.orphans[*txHash]
+	txHash := tx.Hash()
+	otx, exists := mp.orphans[*txHash]
 	if !exists {
 		return
 	}
@@ -340,7 +363,7 @@ func (mp *TxPool) removeOrphan(tx *dcrutil.Tx, removeRedeemers bool) {
 	log.Tracef("Removing orphan transaction %v", txHash)
 
 	// Remove the reference from the previous orphan index.
-	for _, txIn := range tx.MsgTx().TxIn {
+	for _, txIn := range otx.tx.MsgTx().TxIn {
 		orphans, exists := mp.orphansByPrev[txIn.PreviousOutPoint]
 		if exists {
 			delete(orphans, *txHash)
@@ -389,6 +412,32 @@ func (mp *TxPool) RemoveOrphan(tx *dcrutil.Tx) {
 //
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *TxPool) limitNumOrphans() {
+	// Scan through the orphan pool and remove any expired orphans when it's
+	// time.  This is done for efficiency so the scan only happens periodically
+	// instead of on every orphan added to the pool.
+	if now := time.Now(); now.After(mp.nextExpireScan) {
+		origNumOrphans := len(mp.orphans)
+		for _, otx := range mp.orphans {
+			if now.After(otx.expiration) {
+				// Remove redeemers too because the missing parents are very
+				// unlikely to ever materialize since the orphan has already
+				// been around more than long enough for them to be delivered.
+				mp.removeOrphan(otx.tx, true)
+			}
+		}
+
+		// Set next expiration scan to occur after the scan interval.
+		mp.nextExpireScan = now.Add(orphanExpireScanInterval)
+
+		numOrphans := len(mp.orphans)
+		if numExpired := origNumOrphans - numOrphans; numExpired > 0 {
+			log.Debugf("Expired %d %s (remaining: %d)", numExpired,
+				pickNoun(numExpired, "orphan", "orphans"), numOrphans)
+		}
+	}
+
+	// Nothing to do if adding another orphan will not cause the pool to
+	// exceed the limit.
 	if len(mp.orphans)+1 <= mp.cfg.Policy.MaxOrphanTxs {
 		return
 	}
@@ -399,8 +448,10 @@ func (mp *TxPool) limitNumOrphans() {
 	// is not important here because an adversary would have to be
 	// able to pull off preimage attacks on the hashing function in
 	// order to target eviction of specific entries anyways.
-	for _, tx := range mp.orphans {
-		mp.removeOrphan(tx, false)
+	for _, otx := range mp.orphans {
+		// Don't remove redeemers in the case of a random eviction since
+		// it is quite possible it might be needed again shortly.
+		mp.removeOrphan(otx.tx, false)
 		break
 	}
 }
@@ -414,11 +465,15 @@ func (mp *TxPool) addOrphan(tx *dcrutil.Tx) {
 		return
 	}
 
-	// Limit the number orphan transactions to prevent memory exhaustion.  A
-	// random orphan is evicted to make room if needed.
+	// Limit the number orphan transactions to prevent memory exhaustion.
+	// This will periodically remove any expired orphans and evict a random
+	// orphan if space is still needed.
 	mp.limitNumOrphans()
 
-	mp.orphans[*tx.Hash()] = tx
+	mp.orphans[*tx.Hash()] = &orphanTx{
+		tx:         tx,
+		expiration: time.Now().Add(orphanTTL),
+	}
 	for _, txIn := range tx.MsgTx().TxIn {
 		if _, exists := mp.orphansByPrev[txIn.PreviousOutPoint]; !exists {
 			mp.orphansByPrev[txIn.PreviousOutPoint] =
@@ -1695,11 +1750,12 @@ func (mp *TxPool) LastUpdated() time.Time {
 // transactions until they are mined into a block.
 func New(cfg *Config) *TxPool {
 	return &TxPool{
-		cfg:           *cfg,
-		pool:          make(map[chainhash.Hash]*TxDesc),
-		orphans:       make(map[chainhash.Hash]*dcrutil.Tx),
-		orphansByPrev: make(map[wire.OutPoint]map[chainhash.Hash]*dcrutil.Tx),
-		outpoints:     make(map[wire.OutPoint]*dcrutil.Tx),
-		votes:         make(map[chainhash.Hash][]mining.VoteDesc),
+		cfg:            *cfg,
+		pool:           make(map[chainhash.Hash]*TxDesc),
+		orphans:        make(map[chainhash.Hash]*orphanTx),
+		orphansByPrev:  make(map[wire.OutPoint]map[chainhash.Hash]*dcrutil.Tx),
+		outpoints:      make(map[wire.OutPoint]*dcrutil.Tx),
+		votes:          make(map[chainhash.Hash][]mining.VoteDesc),
+		nextExpireScan: time.Now().Add(orphanExpireScanInterval),
 	}
 }
diff --git a/server.go b/server.go
index 7c111571..f1b5fb2f 100644
--- a/server.go
+++ b/server.go
@@ -29,7 +29,7 @@ import (
 	"github.com/decred/dcrd/blockchain/v3/indexers"
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v2"
-	"github.com/decred/dcrd/connmgr/v2"
+	"github.com/decred/dcrd/connmgr/v3"
 	"github.com/decred/dcrd/database/v2"
 	"github.com/decred/dcrd/dcrutil/v3"
 	"github.com/decred/dcrd/fees/v2"


Test patch:
diff --git a/blockchain/chainio_test.go b/blockchain/chainio_test.go
index 3379833b..f9e5cad1 100644
--- a/blockchain/chainio_test.go
+++ b/blockchain/chainio_test.go
@@ -1303,7 +1303,6 @@ func TestUtxoSerialization(t *testing.T) {
 					"%v", i, test.name, outputIndex,
 					gotSpent, wantSpent)
 				continue
-
 			}
 		}
 		if len(utxoEntry.sparseOutputs) != numUnspent {
@@ -1459,7 +1458,6 @@ func TestBestChainStateSerialization(t *testing.T) {
 				"mismatched state - got %v, want %v", i,
 				test.name, state, test.state)
 			continue
-
 		}
 	}
 }
diff --git a/blockchain/chainview_test.go b/blockchain/chainview_test.go
index d79ed3d9..f2030ccc 100644
--- a/blockchain/chainview_test.go
+++ b/blockchain/chainview_test.go
@@ -367,7 +367,6 @@ testLoop:
 					continue testLoop
 				}
 			}
-
 		}
 	}
 }
diff --git a/blockchain/sequencelock_test.go b/blockchain/sequencelock_test.go
index 71f060ad..c2b84dec 100644
--- a/blockchain/sequencelock_test.go
+++ b/blockchain/sequencelock_test.go
@@ -484,7 +484,6 @@ func TestLockTimeToSequence(t *testing.T) {
 		if err != nil && !test.invalid {
 			t.Errorf("%s: unexpected error: %v", test.name, err)
 			continue
-
 		}
 		if err == nil && test.invalid {
 			t.Errorf("%s: did not receive expected error", test.name)
@@ -496,6 +495,5 @@ func TestLockTimeToSequence(t *testing.T) {
 				test.name, gotSequence, test.expected)
 			continue
 		}
-
 	}
 }
diff --git a/blockchain/stakeversion_test.go b/blockchain/stakeversion_test.go
index 107f6e01..01677c59 100644
--- a/blockchain/stakeversion_test.go
+++ b/blockchain/stakeversion_test.go
@@ -25,7 +25,7 @@ func (b *BlockChain) isVoterMajorityVersion(minVer uint32, prevNode *blockNode)
 	// Walk blockchain backwards to calculate version.
 	node := b.findStakeVersionPriorNode(prevNode)
 	if node == nil {
-		return 0 >= minVer
+		return 0 == minVer
 	}
 
 	// Generate map key and look up cached result.
@@ -206,7 +206,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 			t.Fatalf("invalid StakeVersion expected %d -> true",
 				version)
 		}
-
 	}
 	if bc.isStakeMajorityVersion(5, node) {
 		t.Fatalf("invalid StakeVersion expected 5 -> false")
@@ -231,7 +230,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 			t.Fatalf("invalid StakeVersion expected %d -> true",
 				version)
 		}
-
 	}
 	if bc.isStakeMajorityVersion(5, node) {
 		t.Fatalf("invalid StakeVersion expected 5 -> false")
@@ -254,7 +252,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 			t.Fatalf("invalid StakeVersion expected %d -> true",
 				version)
 		}
-
 	}
 	if bc.isStakeMajorityVersion(6, node) {
 		t.Fatalf("invalid StakeVersion expected 6 -> false")
@@ -268,7 +265,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 		node = newFakeNode(node, 3, sv, 0, time.Now())
 		appendFakeVotes(node, params.TicketsPerBlock, 4, 0)
 		bc.bestChain.SetTip(node)
-
 	}
 
 	// Versions up to and including v5 should still be considered the
@@ -280,7 +276,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 			t.Fatalf("invalid StakeVersion expected %d -> true",
 				version)
 		}
-
 	}
 	if bc.isStakeMajorityVersion(6, node) {
 		t.Fatalf("invalid StakeVersion expected 6 -> false")
@@ -294,7 +289,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 		node = newFakeNode(node, 3, sv, 0, time.Now())
 		appendFakeVotes(node, params.TicketsPerBlock, 4, 0)
 		bc.bestChain.SetTip(node)
-
 	}
 
 	// Versions up to and including v5 should still be considered the
@@ -306,7 +300,6 @@ func TestCalcStakeVersionCorners(t *testing.T) {
 			t.Fatalf("invalid StakeVersion expected %d -> true",
 				version)
 		}
-
 	}
 	if bc.isStakeMajorityVersion(6, node) {
 		t.Fatalf("invalid StakeVersion expected 6 -> false")