[DEV-339] Check how mempool handles dependent transactions, see if there's any problem there (if so - fix it), and tell the team about it (#169)

* [DEV-339] Handling of dependent transactions in mempool

* [DEV-339] Small fixes after code review

* [DEV-339] Fixed compilation

* [DEV-339] Removed extra loop in addTransaction function

* [DEV-339] Changed addTransaction do not loop on inputs second time
This commit is contained in:
Evgeny Khirin 2019-01-23 16:38:46 +02:00 committed by stasatdaglabs
parent 9507ed0a97
commit b7850b382d
2 changed files with 170 additions and 62 deletions

View File

@ -147,6 +147,11 @@ type TxDesc struct {
// StartingPriority is the priority of the transaction when it was added
// to the pool.
StartingPriority float64
// depCount is not 0 for dependent transaction. Dependent transaction is
// one that is accepted to pool, but cannot be mined in next block because it
// depends on outputs of accepted, but still not mined transaction
depCount int
}
// orphanTx is normal transaction that references an ancestor transaction
@ -168,6 +173,8 @@ type TxPool struct {
mtx sync.RWMutex
cfg Config
pool map[daghash.Hash]*TxDesc
depends map[daghash.Hash]*TxDesc
dependsByPrev map[wire.OutPoint]map[daghash.Hash]*TxDesc
orphans map[daghash.Hash]*orphanTx
orphansByPrev map[wire.OutPoint]map[daghash.Hash]*util.Tx
outpoints map[wire.OutPoint]*util.Tx
@ -390,8 +397,7 @@ func (mp *TxPool) isTransactionInPool(hash *daghash.Hash) bool {
if _, exists := mp.pool[*hash]; exists {
return true
}
return false
return mp.isInDependPool(hash)
}
// IsTransactionInPool returns whether or not the passed transaction already
@ -407,6 +413,29 @@ func (mp *TxPool) IsTransactionInPool(hash *daghash.Hash) bool {
return inPool
}
// isInDependPool returns whether or not the passed transaction already
// exists in the depend pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *TxPool) isInDependPool(hash *daghash.Hash) bool {
if _, exists := mp.depends[*hash]; exists {
return true
}
return false
}
// IsInDependPool returns whether or not the passed transaction already
// exists in the main pool.
//
// This function is safe for concurrent access.
func (mp *TxPool) IsInDependPool(hash *daghash.Hash) bool {
// Protect concurrent access.
mp.mtx.RLock()
defer mp.mtx.RUnlock()
return mp.isInDependPool(hash)
}
// isOrphanInPool returns whether or not the passed transaction already exists
// in the orphan pool.
//
@ -470,7 +499,7 @@ func (mp *TxPool) removeTransaction(tx *util.Tx, removeRedeemers bool, restoreIn
}
// Remove the transaction if needed.
if txDesc, exists := mp.pool[*txID]; exists {
if txDesc, exists := mp.fetchTransaction(txID); exists {
// Remove unconfirmed address index entries associated with the
// transaction if enabled.
if mp.cfg.AddrIndex != nil {
@ -488,10 +517,46 @@ func (mp *TxPool) removeTransaction(tx *util.Tx, removeRedeemers bool, restoreIn
entry := blockdag.NewUTXOEntry(prevOut, false, mining.UnminedHeight)
diff.AddEntry(txIn.PreviousOutPoint, entry)
}
if prevTxDesc, exists := mp.depends[txIn.PreviousOutPoint.TxID]; exists {
prevOut := prevTxDesc.Tx.MsgTx().TxOut[txIn.PreviousOutPoint.Index]
entry := blockdag.NewUTXOEntry(prevOut, false, mining.UnminedHeight)
diff.AddEntry(txIn.PreviousOutPoint, entry)
}
}
delete(mp.outpoints, txIn.PreviousOutPoint)
}
delete(mp.pool, *txID)
if txDesc.depCount == 0 {
delete(mp.pool, *txID)
} else {
delete(mp.depends, *txID)
}
// Process dependent transactions
prevOut := wire.OutPoint{TxID: *txID}
for txOutIdx := range tx.MsgTx().TxOut {
// Skip to the next available output if there are none.
prevOut.Index = uint32(txOutIdx)
depends, exists := mp.dependsByPrev[prevOut]
if !exists {
continue
}
// Move independent transactions into main pool
for _, txD := range depends {
txD.depCount--
if txD.depCount == 0 {
// Transaction may be already removed by recursive calls, if removeRedeemers is true.
// So avoid moving it into main pool
if _, ok := mp.depends[*txD.Tx.ID()]; ok {
delete(mp.depends, *txD.Tx.ID())
mp.pool[*txD.Tx.ID()] = txD
}
}
}
delete(mp.dependsByPrev, prevOut)
}
var err error
mp.mpUTXOSet, err = mp.mpUTXOSet.WithDiff(diff)
if err != nil {
@ -540,7 +605,7 @@ func (mp *TxPool) RemoveDoubleSpends(tx *util.Tx) {
// helper for maybeAcceptTransaction.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *TxPool) addTransaction(tx *util.Tx, height int32, fee uint64) *TxDesc {
func (mp *TxPool) addTransaction(tx *util.Tx, height int32, fee uint64, parentsInPool []*wire.OutPoint) *TxDesc {
mp.cfg.DAG.UTXORLock()
defer mp.cfg.DAG.UTXORUnlock()
// Add the transaction to the pool and mark the referenced outpoints
@ -554,9 +619,21 @@ func (mp *TxPool) addTransaction(tx *util.Tx, height int32, fee uint64) *TxDesc
FeePerKB: fee * 1000 / uint64(tx.MsgTx().SerializeSize()),
},
StartingPriority: mining.CalcPriority(tx.MsgTx(), mp.mpUTXOSet, height),
depCount: len(parentsInPool),
}
if len(parentsInPool) == 0 {
mp.pool[*tx.ID()] = txD
} else {
mp.depends[*tx.ID()] = txD
for _, previousOutPoint := range parentsInPool {
if _, exists := mp.dependsByPrev[*previousOutPoint]; !exists {
mp.dependsByPrev[*previousOutPoint] = make(map[daghash.Hash]*TxDesc)
}
mp.dependsByPrev[*previousOutPoint][*tx.ID()] = txD
}
}
mp.pool[*tx.ID()] = txD
for _, txIn := range tx.MsgTx().TxIn {
mp.outpoints[txIn.PreviousOutPoint] = tx
}
@ -588,7 +665,7 @@ func (mp *TxPool) checkPoolDoubleSpend(tx *util.Tx) error {
if txR, exists := mp.outpoints[txIn.PreviousOutPoint]; exists {
str := fmt.Sprintf("output %v already spent by "+
"transaction %v in the memory pool",
txIn.PreviousOutPoint, txR.Hash())
txIn.PreviousOutPoint, txR.ID())
return txRuleError(wire.RejectDuplicate, str)
}
}
@ -607,6 +684,15 @@ func (mp *TxPool) CheckSpend(op wire.OutPoint) *util.Tx {
return txR
}
// This function MUST be called with the mempool lock held (for reads).
func (mp *TxPool) fetchTransaction(txID *daghash.Hash) (*TxDesc, bool) {
txDesc, exists := mp.pool[*txID]
if !exists {
txDesc, exists = mp.depends[*txID]
}
return txDesc, exists
}
// FetchTransaction returns the requested transaction from the transaction pool.
// This only fetches from the main transaction pool and does not include
// orphans.
@ -615,10 +701,9 @@ func (mp *TxPool) CheckSpend(op wire.OutPoint) *util.Tx {
func (mp *TxPool) FetchTransaction(txID *daghash.Hash) (*util.Tx, error) {
// Protect concurrent access.
mp.mtx.RLock()
txDesc, exists := mp.pool[*txID]
mp.mtx.RUnlock()
defer mp.mtx.RUnlock()
if exists {
if txDesc, exists := mp.fetchTransaction(txID); exists {
return txDesc.Tx, nil
}
@ -746,6 +831,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
// is not handled by this function, and the caller should use
// maybeAddOrphan if this behavior is desired.
var missingParents []*daghash.Hash
var parentsInPool []*wire.OutPoint
for _, txIn := range tx.MsgTx().TxIn {
if _, ok := mp.mpUTXOSet.Get(txIn.PreviousOutPoint); !ok {
// Must make a copy of the hash here since the iterator
@ -755,6 +841,9 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
hashCopy := txIn.PreviousOutPoint.TxID
missingParents = append(missingParents, &hashCopy)
}
if mp.isTransactionInPool(&txIn.PreviousOutPoint.TxID) {
parentsInPool = append(parentsInPool, &txIn.PreviousOutPoint)
}
}
if len(missingParents) > 0 {
return missingParents, nil, nil
@ -901,7 +990,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
}
// Add to transaction pool.
txD := mp.addTransaction(tx, bestHeight, txFee)
txD := mp.addTransaction(tx, bestHeight, txFee, parentsInPool)
log.Debugf("Accepted transaction %v (pool size: %v)", txID,
len(mp.pool))
@ -1106,6 +1195,16 @@ func (mp *TxPool) Count() int {
return count
}
// DepCount returns the number of dependent transactions in the main pool. It does not
// include the orphan pool.
//
// This function is safe for concurrent access.
func (mp *TxPool) DepCount() int {
mp.mtx.RLock()
defer mp.mtx.RUnlock()
return len(mp.depends)
}
// TxIDs returns a slice of IDs for all of the transactions in the memory
// pool.
//
@ -1250,6 +1349,8 @@ func New(cfg *Config) *TxPool {
return &TxPool{
cfg: *cfg,
pool: make(map[daghash.Hash]*TxDesc),
depends: make(map[daghash.Hash]*TxDesc),
dependsByPrev: make(map[wire.OutPoint]map[daghash.Hash]*TxDesc),
orphans: make(map[daghash.Hash]*orphanTx),
orphansByPrev: make(map[wire.OutPoint]map[daghash.Hash]*util.Tx),
nextExpireScan: time.Now().Add(orphanExpireScanInterval),

View File

@ -358,7 +358,7 @@ type testContext struct {
// orphan pool and transaction pool status. It also further determines if it
// should be reported as available by the HaveTransaction function based upon
// the two flags and tests that condition as well.
func testPoolMembership(tc *testContext, tx *util.Tx, inOrphanPool, inTxPool bool) {
func testPoolMembership(tc *testContext, tx *util.Tx, inOrphanPool, inTxPool bool, isDepends bool) {
txID := tx.ID()
gotOrphanPool := tc.harness.txPool.IsOrphanInPool(txID)
if inOrphanPool != gotOrphanPool {
@ -374,6 +374,13 @@ func testPoolMembership(tc *testContext, tx *util.Tx, inOrphanPool, inTxPool boo
file, line, inTxPool, gotTxPool)
}
gotIsDepends := tc.harness.txPool.IsInDependPool(txID)
if isDepends != gotIsDepends {
_, file, line, _ := runtime.Caller(1)
tc.t.Fatalf("%s:%d -- IsInDependPool: want %v, got %v",
file, line, isDepends, gotIsDepends)
}
gotHaveTx := tc.harness.txPool.HaveTransaction(txID)
wantHaveTx := inOrphanPool || inTxPool
if wantHaveTx != gotHaveTx {
@ -389,7 +396,7 @@ func testPoolMembership(tc *testContext, tx *util.Tx, inOrphanPool, inTxPool boo
if count != len(txIDs) || count != len(txDescs) || count != len(txMiningDescs) {
tc.t.Error("mempool.TxIDs(), mempool.TxDescs() and mempool.MiningDescs() have different length")
}
if inTxPool {
if inTxPool && !isDepends {
wasFound := false
for _, txI := range txIDs {
if *txID == *txI {
@ -490,7 +497,7 @@ func TestProcessTransaction(t *testing.T) {
if err != nil {
t.Errorf("ProcessTransaction: unexpected error: %v", err)
}
testPoolMembership(tc, orphanedTx, false, false)
testPoolMembership(tc, orphanedTx, false, false, false)
harness.txPool.cfg.Policy.MaxOrphanTxs = 5
_, err = harness.txPool.ProcessTransaction(orphanedTx, true, false, 0)
@ -850,8 +857,8 @@ func TestDoubleSpends(t *testing.T) {
t.Fatalf("unable to create transaction: %v", err)
}
harness.txPool.ProcessTransaction(tx2, true, false, 0)
testPoolMembership(tc, tx1, false, true)
testPoolMembership(tc, tx2, false, true)
testPoolMembership(tc, tx1, false, true, false)
testPoolMembership(tc, tx2, false, true, false)
//Spends the same outpoint as tx2
tx3, err := harness.createTx(spendableOuts[0], 2, 1) //We put here different fee to create different transaction hash
@ -867,15 +874,15 @@ func TestDoubleSpends(t *testing.T) {
if code, _ := extractRejectCode(err); code != wire.RejectDuplicate {
t.Errorf("Unexpected error code. Expected %v but got %v", wire.RejectDuplicate, code)
}
testPoolMembership(tc, tx3, false, false)
testPoolMembership(tc, tx3, false, false, false)
//Then we assume tx3 is already in the DAG, so we need to remove
//transactions that spends the same outpoints from the mempool
harness.txPool.RemoveDoubleSpends(tx3)
//Ensures that only the transaction that double spends the same
//funds as tx3 is removed, and the other one remains unaffected
testPoolMembership(tc, tx1, false, false)
testPoolMembership(tc, tx2, false, true)
testPoolMembership(tc, tx1, false, false, false)
testPoolMembership(tc, tx2, false, true, false)
}
//TestFetchTransaction checks that FetchTransaction
@ -896,7 +903,7 @@ func TestFetchTransaction(t *testing.T) {
t.Fatalf("unable to create signed tx: %v", err)
}
harness.txPool.ProcessTransaction(orphanedTx, true, false, 0)
testPoolMembership(tc, orphanedTx, true, false)
testPoolMembership(tc, orphanedTx, true, false, false)
fetchedorphanedTx, err := harness.txPool.FetchTransaction(orphanedTx.ID())
if fetchedorphanedTx != nil {
t.Fatalf("FetchTransaction: expected fetchedorphanedTx to be nil")
@ -910,7 +917,7 @@ func TestFetchTransaction(t *testing.T) {
t.Fatalf("unable to create transaction: %v", err)
}
harness.txPool.ProcessTransaction(tx, true, false, 0)
testPoolMembership(tc, tx, false, true)
testPoolMembership(tc, tx, false, true, false)
fetchedTx, err := harness.txPool.FetchTransaction(tx.ID())
if !reflect.DeepEqual(fetchedTx, tx) {
t.Fatalf("FetchTransaction: returned a transaction, but not the right one")
@ -961,7 +968,7 @@ func TestSimpleOrphanChain(t *testing.T) {
// Ensure the transaction is in the orphan pool, is not in the
// transaction pool, and is reported as available.
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Add the transaction which completes the orphan chain and ensure they
@ -982,7 +989,7 @@ func TestSimpleOrphanChain(t *testing.T) {
for _, txD := range acceptedTxns {
// Ensure the transaction is no longer in the orphan pool, is
// now in the transaction pool, and is reported as available.
testPoolMembership(tc, txD.Tx, false, true)
testPoolMembership(tc, txD.Tx, false, true, txD.Tx != chainedTxns[0])
}
}
@ -1036,7 +1043,7 @@ func TestOrphanReject(t *testing.T) {
// Ensure the transaction is not in the orphan pool, not in the
// transaction pool, and not reported as available
testPoolMembership(tc, tx, false, false)
testPoolMembership(tc, tx, false, false, false)
}
}
@ -1067,8 +1074,8 @@ func TestOrphanExpiration(t *testing.T) {
false, 0)
//First check that expired orphan transactions are not removed before nextExpireScan
testPoolMembership(tc, tx1, true, false)
testPoolMembership(tc, expiredTx, true, false)
testPoolMembership(tc, tx1, true, false, false)
testPoolMembership(tc, expiredTx, true, false, false)
//Force nextExpireScan to be in the past
harness.txPool.nextExpireScan = time.Unix(0, 0)
@ -1081,9 +1088,9 @@ func TestOrphanExpiration(t *testing.T) {
harness.txPool.ProcessTransaction(tx2, true,
false, 0)
//Check that only expired orphan transactions are removed
testPoolMembership(tc, tx1, true, false)
testPoolMembership(tc, tx2, true, false)
testPoolMembership(tc, expiredTx, false, false)
testPoolMembership(tc, tx1, true, false, false)
testPoolMembership(tc, tx2, true, false, false)
testPoolMembership(tc, expiredTx, false, false, false)
}
//TestMaxOrphanTxSize ensures that a transaction that is
@ -1107,12 +1114,12 @@ func TestMaxOrphanTxSize(t *testing.T) {
harness.txPool.ProcessTransaction(tx, true,
false, 0)
testPoolMembership(tc, tx, false, false)
testPoolMembership(tc, tx, false, false, false)
harness.txPool.cfg.Policy.MaxOrphanTxSize = math.MaxInt32
harness.txPool.ProcessTransaction(tx, true,
false, 0)
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
@ -1128,26 +1135,26 @@ func TestRemoveTransaction(t *testing.T) {
t.Fatalf("unable to create transaction chain: %v", err)
}
for _, tx := range chainedTxns {
for i, tx := range chainedTxns {
_, err := harness.txPool.ProcessTransaction(tx, true,
false, 0)
if err != nil {
t.Fatalf("ProcessTransaction: %v", err)
}
testPoolMembership(tc, tx, false, true)
testPoolMembership(tc, tx, false, true, i != 0)
}
//Checks that when removeRedeemers is false, the specified transaction is the only transaction that gets removed
harness.txPool.RemoveTransaction(chainedTxns[3], false, true)
testPoolMembership(tc, chainedTxns[3], false, false)
testPoolMembership(tc, chainedTxns[4], false, true)
testPoolMembership(tc, chainedTxns[3], false, false, false)
testPoolMembership(tc, chainedTxns[4], false, true, false)
//Checks that when removeRedeemers is true, all of the transaction that are dependent on it get removed
harness.txPool.RemoveTransaction(chainedTxns[1], true, true)
testPoolMembership(tc, chainedTxns[0], false, true)
testPoolMembership(tc, chainedTxns[1], false, false)
testPoolMembership(tc, chainedTxns[2], false, false)
testPoolMembership(tc, chainedTxns[0], false, true, false)
testPoolMembership(tc, chainedTxns[1], false, false, false)
testPoolMembership(tc, chainedTxns[2], false, false, false)
fakeWithDiffErr := "error from WithDiff"
guard := monkey.Patch((*blockdag.DiffUTXOSet).WithDiff, func(_ *blockdag.DiffUTXOSet, _ *blockdag.UTXODiff) (blockdag.UTXOSet, error) {
@ -1198,7 +1205,7 @@ func TestOrphanEviction(t *testing.T) {
// Ensure the transaction is in the orphan pool, is not in the
// transaction pool, and is reported as available.
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Figure out which transactions were evicted and make sure the number
@ -1218,7 +1225,7 @@ func TestOrphanEviction(t *testing.T) {
// Ensure none of the evicted transactions ended up in the transaction
// pool.
for _, tx := range evictedTxns {
testPoolMembership(tc, tx, false, false)
testPoolMembership(tc, tx, false, false, false)
}
}
@ -1271,10 +1278,10 @@ func TestRemoveOrphansByTag(t *testing.T) {
false, 2)
harness.txPool.RemoveOrphansByTag(1)
testPoolMembership(tc, orphanedTx1, false, false)
testPoolMembership(tc, orphanedTx2, false, false)
testPoolMembership(tc, orphanedTx3, false, false)
testPoolMembership(tc, orphanedTx4, true, false)
testPoolMembership(tc, orphanedTx1, false, false, false)
testPoolMembership(tc, orphanedTx2, false, false, false)
testPoolMembership(tc, orphanedTx3, false, false, false)
testPoolMembership(tc, orphanedTx4, true, false, false)
}
// TestBasicOrphanRemoval ensure that orphan removal works as expected when an
@ -1316,7 +1323,7 @@ func TestBasicOrphanRemoval(t *testing.T) {
// Ensure the transaction is in the orphan pool, not in the
// transaction pool, and reported as available.
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Attempt to remove an orphan that has no redeemers and is not present,
@ -1330,25 +1337,25 @@ func TestBasicOrphanRemoval(t *testing.T) {
}
harness.txPool.RemoveOrphan(nonChainedOrphanTx)
testPoolMembership(tc, nonChainedOrphanTx, false, false)
testPoolMembership(tc, nonChainedOrphanTx, false, false, false)
for _, tx := range chainedTxns[1 : maxOrphans+1] {
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Attempt to remove an orphan that has a existing redeemer but itself
// is not present and ensure the state of all other orphans (including
// the one that redeems it) are unaffected.
harness.txPool.RemoveOrphan(chainedTxns[0])
testPoolMembership(tc, chainedTxns[0], false, false)
testPoolMembership(tc, chainedTxns[0], false, false, false)
for _, tx := range chainedTxns[1 : maxOrphans+1] {
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Remove each orphan one-by-one and ensure they are removed as
// expected.
for _, tx := range chainedTxns[1 : maxOrphans+1] {
harness.txPool.RemoveOrphan(tx)
testPoolMembership(tc, tx, false, false)
testPoolMembership(tc, tx, false, false, false)
}
}
@ -1390,7 +1397,7 @@ func TestOrphanChainRemoval(t *testing.T) {
// Ensure the transaction is in the orphan pool, not in the
// transaction pool, and reported as available.
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Remove the first orphan that starts the orphan chain without the
@ -1399,9 +1406,9 @@ func TestOrphanChainRemoval(t *testing.T) {
harness.txPool.mtx.Lock()
harness.txPool.removeOrphan(chainedTxns[1], false)
harness.txPool.mtx.Unlock()
testPoolMembership(tc, chainedTxns[1], false, false)
testPoolMembership(tc, chainedTxns[1], false, false, false)
for _, tx := range chainedTxns[2 : maxOrphans+1] {
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Remove the first remaining orphan that starts the orphan chain with
@ -1410,7 +1417,7 @@ func TestOrphanChainRemoval(t *testing.T) {
harness.txPool.removeOrphan(chainedTxns[2], true)
harness.txPool.mtx.Unlock()
for _, tx := range chainedTxns[2 : maxOrphans+1] {
testPoolMembership(tc, tx, false, false)
testPoolMembership(tc, tx, false, false, false)
}
}
@ -1446,7 +1453,7 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) {
t.Fatalf("ProcessTransaction: reported %d accepted transactions "+
"from what should be an orphan", len(acceptedTxns))
}
testPoolMembership(tc, tx, true, false)
testPoolMembership(tc, tx, true, false, false)
}
// Ensure a transaction that contains a double spend of the same output
@ -1472,7 +1479,7 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) {
t.Fatalf("ProcessTransaction: reported %d accepted transactions "+
"from what should be an orphan", len(acceptedTxns))
}
testPoolMembership(tc, doubleSpendTx, true, false)
testPoolMembership(tc, doubleSpendTx, true, false, false)
// Add the transaction which completes the orphan chain and ensure the
// chain gets accepted. Notice the accept orphans flag is also false
@ -1494,12 +1501,12 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) {
for _, txD := range acceptedTxns {
// Ensure the transaction is no longer in the orphan pool, is
// in the transaction pool, and is reported as available.
testPoolMembership(tc, txD.Tx, false, true)
testPoolMembership(tc, txD.Tx, false, true, txD.Tx != chainedTxns[0])
}
// Ensure the double spending orphan is no longer in the orphan pool and
// was not moved to the transaction pool.
testPoolMembership(tc, doubleSpendTx, false, false)
testPoolMembership(tc, doubleSpendTx, false, false, false)
}
// TestCheckSpend tests that CheckSpend returns the expected spends found in
@ -1590,7 +1597,7 @@ func TestCount(t *testing.T) {
if err != nil {
t.Errorf("ProcessTransaction: unexpected error: %v", err)
}
if harness.txPool.Count() != i+1 {
if harness.txPool.Count()+harness.txPool.DepCount() != i+1 {
t.Errorf("TestCount: txPool expected to have %v transactions but got %v", i+1, harness.txPool.Count())
}
}
@ -1599,7 +1606,7 @@ func TestCount(t *testing.T) {
if err != nil {
t.Fatalf("harness.CreateTxChain: unexpected error: %v", err)
}
if harness.txPool.Count() != 2 {
if harness.txPool.Count()+harness.txPool.DepCount() != 2 {
t.Errorf("TestCount: txPool expected to have 2 transactions but got %v", harness.txPool.Count())
}
}
@ -1708,7 +1715,7 @@ func TestHandleNewBlock(t *testing.T) {
t.Fatalf("ProcessTransaction: unexpected error: %v", err)
}
// ensure that transaction added to orphan pool
testPoolMembership(tc, orphanTx, true, false)
testPoolMembership(tc, orphanTx, true, false, false)
// Add one more transaction to block
blockTx2, err := harness.CreateSignedTx(spendableOuts[1:], 1)
@ -1768,7 +1775,7 @@ func TestHandleNewBlock(t *testing.T) {
}
// ensure that orphan transaction moved to main pool
testPoolMembership(tc, orphanTx, false, true)
testPoolMembership(tc, orphanTx, false, true, false)
}
// dummyBlock defines a block on the block DAG. It is used to test block operations.