mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[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:
parent
9507ed0a97
commit
b7850b382d
@ -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),
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user