From e992d55822f85c7839a6a62e7cb6a1abefacce86 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 28 Oct 2016 12:48:54 -0500 Subject: [PATCH] mempool: Associated tag with orphan txns. This allows a caller-provided tag to be associated with orphan transactions. This is useful since the caller can use the tag for purposes such as keeping track of which peers orphans were first seen from. Also, since a parameter is required now anyways, it associates the peer ID with processed transactions from remote peers. --- blockmanager.go | 2 +- mempool/mempool.go | 17 ++++++++++++----- mempool/mempool_test.go | 18 +++++++++--------- rpcserver.go | 4 +++- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/blockmanager.go b/blockmanager.go index b1f14f1a..7cac6b19 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -413,7 +413,7 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) { // memory pool, orphan handling, etc. allowOrphans := cfg.MaxOrphanTxs > 0 acceptedTxs, err := b.server.txMemPool.ProcessTransaction(tmsg.tx, - allowOrphans, true) + allowOrphans, true, mempool.Tag(tmsg.peer.ID())) // Remove transaction from request maps. Either the mempool/chain // already knows about it and as such we shouldn't have any more diff --git a/mempool/mempool.go b/mempool/mempool.go index 0cc75744..7221a394 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -40,6 +40,11 @@ const ( orphanExpireScanInterval = time.Minute * 5 ) +// Tag represents an identifier to use for tagging orphan transactions. The +// caller may choose any scheme it desires, however it is common to use peer IDs +// so that orphans can be identified by which peer first relayed them. +type Tag uint64 + // Config is a descriptor containing the memory pool configuration. type Config struct { // Policy defines the various mempool configuration options related @@ -132,6 +137,7 @@ type TxDesc struct { // to it such as an expiration time to help prevent caching the orphan forever. type orphanTx struct { tx *btcutil.Tx + tag Tag expiration time.Time } @@ -268,7 +274,7 @@ func (mp *TxPool) limitNumOrphans() error { // addOrphan adds an orphan transaction to the orphan pool. // // This function MUST be called with the mempool lock held (for writes). -func (mp *TxPool) addOrphan(tx *btcutil.Tx) { +func (mp *TxPool) addOrphan(tx *btcutil.Tx, tag Tag) { // Nothing to do if no orphans are allowed. if mp.cfg.Policy.MaxOrphanTxs <= 0 { return @@ -281,6 +287,7 @@ func (mp *TxPool) addOrphan(tx *btcutil.Tx) { mp.orphans[*tx.Hash()] = &orphanTx{ tx: tx, + tag: tag, expiration: time.Now().Add(orphanTTL), } for _, txIn := range tx.MsgTx().TxIn { @@ -298,7 +305,7 @@ func (mp *TxPool) addOrphan(tx *btcutil.Tx) { // maybeAddOrphan potentially adds an orphan to the orphan pool. // // This function MUST be called with the mempool lock held (for writes). -func (mp *TxPool) maybeAddOrphan(tx *btcutil.Tx) error { +func (mp *TxPool) maybeAddOrphan(tx *btcutil.Tx, tag Tag) error { // Ignore orphan transactions that are too large. This helps avoid // a memory exhaustion attack based on sending a lot of really large // orphans. In the case there is a valid transaction larger than this, @@ -318,7 +325,7 @@ func (mp *TxPool) maybeAddOrphan(tx *btcutil.Tx) error { } // Add the orphan if the none of the above disqualified it. - mp.addOrphan(tx) + mp.addOrphan(tx, tag) return nil } @@ -981,7 +988,7 @@ func (mp *TxPool) ProcessOrphans(acceptedTx *btcutil.Tx) []*TxDesc { // the passed one being accepted. // // This function is safe for concurrent access. -func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) ([]*TxDesc, error) { +func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool, tag Tag) ([]*TxDesc, error) { log.Tracef("Processing transaction %v", tx.Hash()) // Protect concurrent access. @@ -1030,7 +1037,7 @@ func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool } // Potentially add the orphan transaction to the orphan pool. - err = mp.maybeAddOrphan(tx) + err = mp.maybeAddOrphan(tx, tag) if err != nil { return nil, err } diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index a962842a..dc3d7aca 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -409,7 +409,7 @@ func TestSimpleOrphanChain(t *testing.T) { // none are evicted). for _, tx := range chainedTxns[1 : maxOrphans+1] { acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true, - false) + false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid "+ "orphan %v", err) @@ -432,7 +432,7 @@ func TestSimpleOrphanChain(t *testing.T) { // to ensure it has no bearing on whether or not already existing // orphans in the pool are linked. acceptedTxns, err := harness.txPool.ProcessTransaction(chainedTxns[0], - false, false) + false, false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid "+ "orphan %v", err) @@ -471,7 +471,7 @@ func TestOrphanReject(t *testing.T) { // Ensure orphans are rejected when the allow orphans flag is not set. for _, tx := range chainedTxns[1:] { acceptedTxns, err := harness.txPool.ProcessTransaction(tx, false, - false) + false, 0) if err == nil { t.Fatalf("ProcessTransaction: did not fail on orphan "+ "%v when allow orphans flag is false", tx.Hash()) @@ -528,7 +528,7 @@ func TestOrphanEviction(t *testing.T) { // all accepted. This will cause an eviction. for _, tx := range chainedTxns[1:] { acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true, - false) + false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid "+ "orphan %v", err) @@ -592,7 +592,7 @@ func TestBasicOrphanRemoval(t *testing.T) { // none are evicted). for _, tx := range chainedTxns[1 : maxOrphans+1] { acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true, - false) + false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid "+ "orphan %v", err) @@ -667,7 +667,7 @@ func TestOrphanChainRemoval(t *testing.T) { // none are evicted). for _, tx := range chainedTxns[1 : maxOrphans+1] { acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true, - false) + false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid "+ "orphan %v", err) @@ -730,7 +730,7 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) { // except the final one. for _, tx := range chainedTxns[1:maxOrphans] { acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true, - false) + false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid "+ "orphan %v", err) @@ -756,7 +756,7 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) { t.Fatalf("unable to create signed tx: %v", err) } acceptedTxns, err := harness.txPool.ProcessTransaction(doubleSpendTx, - true, false) + true, false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid orphan %v", err) @@ -775,7 +775,7 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) { // This will cause the shared output to become a concrete spend which // will in turn must cause the double spending orphan to be removed. acceptedTxns, err = harness.txPool.ProcessTransaction(chainedTxns[0], - false, false) + false, false, 0) if err != nil { t.Fatalf("ProcessTransaction: failed to accept valid tx %v", err) } diff --git a/rpcserver.go b/rpcserver.go index 5c5547aa..510b165a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3424,8 +3424,10 @@ func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan st } } + // Use 0 for the tag to represent local node. tx := btcutil.NewTx(&msgTx) - acceptedTxs, err := s.server.txMemPool.ProcessTransaction(tx, false, false) + acceptedTxs, err := s.server.txMemPool.ProcessTransaction(tx, false, + false, 0) if err != nil { // When the error is a rule error, it means the transaction was // simply rejected as opposed to something actually going wrong,