1 // Copyright (c) 2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
15 "github.com/btcsuite/btcd/blockchain"
16 "github.com/btcsuite/btcd/btcec"
17 "github.com/btcsuite/btcd/chaincfg"
18 "github.com/btcsuite/btcd/chaincfg/chainhash"
19 "github.com/btcsuite/btcd/txscript"
20 "github.com/btcsuite/btcd/wire"
21 "github.com/btcsuite/btcutil"
24 // fakeChain is used by the pool harness to provide generated test utxos and
25 // a current faked chain height to the pool callbacks. This, in turn, allows
26 // transations to be appear as though they are spending completely valid utxos.
27 type fakeChain struct {
29 utxos *blockchain.UtxoViewpoint
31 medianTimePast time.Time
34 // FetchUtxoView loads utxo details about the input transactions referenced by
35 // the passed transaction from the point of view of the fake chain.
36 // It also attempts to fetch the utxo details for the transaction itself so the
37 // returned view can be examined for duplicate unspent transaction outputs.
39 // This function is safe for concurrent access however the returned view is NOT.
40 func (s *fakeChain) FetchUtxoView(tx *btcutil.Tx) (*blockchain.UtxoViewpoint, error) {
44 // All entries are cloned to ensure modifications to the returned view
45 // do not affect the fake chain's view.
47 // Add an entry for the tx itself to the new view.
48 viewpoint := blockchain.NewUtxoViewpoint()
49 entry := s.utxos.LookupEntry(tx.Hash())
50 viewpoint.Entries()[*tx.Hash()] = entry.Clone()
52 // Add entries for all of the inputs to the tx to the new view.
53 for _, txIn := range tx.MsgTx().TxIn {
54 originHash := &txIn.PreviousOutPoint.Hash
55 entry := s.utxos.LookupEntry(originHash)
56 viewpoint.Entries()[*originHash] = entry.Clone()
62 // BestHeight returns the current height associated with the fake chain
64 func (s *fakeChain) BestHeight() int32 {
66 height := s.currentHeight
71 // SetHeight sets the current height associated with the fake chain instance.
72 func (s *fakeChain) SetHeight(height int32) {
74 s.currentHeight = height
78 // MedianTimePast returns the current median time past associated with the fake
80 func (s *fakeChain) MedianTimePast() time.Time {
82 mtp := s.medianTimePast
87 // SetMedianTimePast sets the current median time past associated with the fake
89 func (s *fakeChain) SetMedianTimePast(mtp time.Time) {
91 s.medianTimePast = mtp
95 // CalcSequenceLock returns the current sequence lock for the passed
96 // transaction associated with the fake chain instance.
97 func (s *fakeChain) CalcSequenceLock(tx *btcutil.Tx,
98 view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
100 return &blockchain.SequenceLock{
106 // spendableOutput is a convenience type that houses a particular utxo and the
107 // amount associated with it.
108 type spendableOutput struct {
109 outPoint wire.OutPoint
110 amount btcutil.Amount
113 // txOutToSpendableOut returns a spendable output given a transaction and index
114 // of the output to use. This is useful as a convenience when creating test
116 func txOutToSpendableOut(tx *btcutil.Tx, outputNum uint32) spendableOutput {
117 return spendableOutput{
118 outPoint: wire.OutPoint{Hash: *tx.Hash(), Index: outputNum},
119 amount: btcutil.Amount(tx.MsgTx().TxOut[outputNum].Value),
123 // poolHarness provides a harness that includes functionality for creating and
124 // signing transactions as well as a fake chain that provides utxos for use in
125 // generating valid transactions.
126 type poolHarness struct {
127 // signKey is the signing key used for creating transactions throughout
130 // payAddr is the p2sh address for the signing key and is used for the
131 // payment address throughout the tests.
132 signKey *btcec.PrivateKey
133 payAddr btcutil.Address
135 chainParams *chaincfg.Params
141 // CreateCoinbaseTx returns a coinbase transaction with the requested number of
142 // outputs paying an appropriate subsidy based on the passed block height to the
143 // address associated with the harness. It automatically uses a standard
144 // signature script that starts with the block height that is required by
146 func (p *poolHarness) CreateCoinbaseTx(blockHeight int32, numOutputs uint32) (*btcutil.Tx, error) {
147 // Create standard coinbase script.
148 extraNonce := int64(0)
149 coinbaseScript, err := txscript.NewScriptBuilder().
150 AddInt64(int64(blockHeight)).AddInt64(extraNonce).Script()
155 tx := wire.NewMsgTx(wire.TxVersion)
156 tx.AddTxIn(&wire.TxIn{
157 // Coinbase transactions have no inputs, so previous outpoint is
158 // zero hash and max index.
159 PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
160 wire.MaxPrevOutIndex),
161 SignatureScript: coinbaseScript,
162 Sequence: wire.MaxTxInSequenceNum,
164 totalInput := blockchain.CalcBlockSubsidy(blockHeight, p.chainParams)
165 amountPerOutput := totalInput / int64(numOutputs)
166 remainder := totalInput - amountPerOutput*int64(numOutputs)
167 for i := uint32(0); i < numOutputs; i++ {
168 // Ensure the final output accounts for any remainder that might
169 // be left from splitting the input amount.
170 amount := amountPerOutput
171 if i == numOutputs-1 {
172 amount = amountPerOutput + remainder
174 tx.AddTxOut(&wire.TxOut{
175 PkScript: p.payScript,
180 return btcutil.NewTx(tx), nil
183 // CreateSignedTx creates a new signed transaction that consumes the provided
184 // inputs and generates the provided number of outputs by evenly splitting the
185 // total input amount. All outputs will be to the payment script associated
186 // with the harness and all inputs are assumed to do the same.
187 func (p *poolHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint32) (*btcutil.Tx, error) {
188 // Calculate the total input amount and split it amongst the requested
189 // number of outputs.
190 var totalInput btcutil.Amount
191 for _, input := range inputs {
192 totalInput += input.amount
194 amountPerOutput := int64(totalInput) / int64(numOutputs)
195 remainder := int64(totalInput) - amountPerOutput*int64(numOutputs)
197 tx := wire.NewMsgTx(wire.TxVersion)
198 for _, input := range inputs {
199 tx.AddTxIn(&wire.TxIn{
200 PreviousOutPoint: input.outPoint,
201 SignatureScript: nil,
202 Sequence: wire.MaxTxInSequenceNum,
205 for i := uint32(0); i < numOutputs; i++ {
206 // Ensure the final output accounts for any remainder that might
207 // be left from splitting the input amount.
208 amount := amountPerOutput
209 if i == numOutputs-1 {
210 amount = amountPerOutput + remainder
212 tx.AddTxOut(&wire.TxOut{
213 PkScript: p.payScript,
218 // Sign the new transaction.
219 for i := range tx.TxIn {
220 sigScript, err := txscript.SignatureScript(tx, i, p.payScript,
221 txscript.SigHashAll, p.signKey, true)
225 tx.TxIn[i].SignatureScript = sigScript
228 return btcutil.NewTx(tx), nil
231 // CreateTxChain creates a chain of zero-fee transactions (each subsequent
232 // transaction spends the entire amount from the previous one) with the first
233 // one spending the provided outpoint. Each transaction spends the entire
234 // amount of the previous one and as such does not include any fees.
235 func (p *poolHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint32) ([]*btcutil.Tx, error) {
236 txChain := make([]*btcutil.Tx, 0, numTxns)
237 prevOutPoint := firstOutput.outPoint
238 spendableAmount := firstOutput.amount
239 for i := uint32(0); i < numTxns; i++ {
240 // Create the transaction using the previous transaction output
241 // and paying the full amount to the payment address associated
243 tx := wire.NewMsgTx(wire.TxVersion)
244 tx.AddTxIn(&wire.TxIn{
245 PreviousOutPoint: prevOutPoint,
246 SignatureScript: nil,
247 Sequence: wire.MaxTxInSequenceNum,
249 tx.AddTxOut(&wire.TxOut{
250 PkScript: p.payScript,
251 Value: int64(spendableAmount),
254 // Sign the new transaction.
255 sigScript, err := txscript.SignatureScript(tx, 0, p.payScript,
256 txscript.SigHashAll, p.signKey, true)
260 tx.TxIn[0].SignatureScript = sigScript
262 txChain = append(txChain, btcutil.NewTx(tx))
264 // Next transaction uses outputs from this one.
265 prevOutPoint = wire.OutPoint{Hash: tx.TxHash(), Index: 0}
271 // newPoolHarness returns a new instance of a pool harness initialized with a
272 // fake chain and a TxPool bound to it that is configured with a policy suitable
273 // for testing. Also, the fake chain is populated with the returned spendable
274 // outputs so the caller can easily create new valid transactions which build
276 func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutput, error) {
277 // Use a hard coded key pair for deterministic results.
278 keyBytes, err := hex.DecodeString("700868df1838811ffbdf918fb482c1f7e" +
279 "ad62db4b97bd7012c23e726485e577d")
283 signKey, signPub := btcec.PrivKeyFromBytes(btcec.S256(), keyBytes)
285 // Generate associated pay-to-script-hash address and resulting payment
287 pubKeyBytes := signPub.SerializeCompressed()
288 payPubKeyAddr, err := btcutil.NewAddressPubKey(pubKeyBytes, chainParams)
292 payAddr := payPubKeyAddr.AddressPubKeyHash()
293 pkScript, err := txscript.PayToAddrScript(payAddr)
298 // Create a new fake chain and harness bound to it.
299 chain := &fakeChain{utxos: blockchain.NewUtxoViewpoint()}
300 harness := poolHarness{
304 chainParams: chainParams,
309 DisableRelayPriority: true,
310 FreeTxRelayLimit: 15.0,
312 MaxOrphanTxSize: 1000,
313 MaxSigOpCostPerTx: blockchain.MaxBlockSigOpsCost / 4,
314 MinRelayTxFee: 1000, // 1 Satoshi per byte
317 ChainParams: chainParams,
318 FetchUtxoView: chain.FetchUtxoView,
319 BestHeight: chain.BestHeight,
320 MedianTimePast: chain.MedianTimePast,
321 CalcSequenceLock: chain.CalcSequenceLock,
327 // Create a single coinbase transaction and add it to the harness
328 // chain's utxo set and set the harness chain height such that the
329 // coinbase will mature in the next block. This ensures the txpool
330 // accepts transactions which spend immature coinbases that will become
331 // mature in the next block.
332 numOutputs := uint32(1)
333 outputs := make([]spendableOutput, 0, numOutputs)
334 curHeight := harness.chain.BestHeight()
335 coinbase, err := harness.CreateCoinbaseTx(curHeight+1, numOutputs)
339 harness.chain.utxos.AddTxOuts(coinbase, curHeight+1)
340 for i := uint32(0); i < numOutputs; i++ {
341 outputs = append(outputs, txOutToSpendableOut(coinbase, i))
343 harness.chain.SetHeight(int32(chainParams.CoinbaseMaturity) + curHeight)
344 harness.chain.SetMedianTimePast(time.Now())
346 return &harness, outputs, nil
349 // testContext houses a test-related state that is useful to pass to helper
350 // functions as a single argument.
351 type testContext struct {
356 // testPoolMembership tests the transaction pool associated with the provided
357 // test context to determine if the passed transaction matches the provided
358 // orphan pool and transaction pool status. It also further determines if it
359 // should be reported as available by the HaveTransaction function based upon
360 // the two flags and tests that condition as well.
361 func testPoolMembership(tc *testContext, tx *btcutil.Tx, inOrphanPool, inTxPool bool) {
363 gotOrphanPool := tc.harness.txPool.IsOrphanInPool(txHash)
364 if inOrphanPool != gotOrphanPool {
365 _, file, line, _ := runtime.Caller(1)
366 tc.t.Fatalf("%s:%d -- IsOrphanInPool: want %v, got %v", file,
367 line, inOrphanPool, gotOrphanPool)
370 gotTxPool := tc.harness.txPool.IsTransactionInPool(txHash)
371 if inTxPool != gotTxPool {
372 _, file, line, _ := runtime.Caller(1)
373 tc.t.Fatalf("%s:%d -- IsTransactionInPool: want %v, got %v",
374 file, line, inTxPool, gotTxPool)
377 gotHaveTx := tc.harness.txPool.HaveTransaction(txHash)
378 wantHaveTx := inOrphanPool || inTxPool
379 if wantHaveTx != gotHaveTx {
380 _, file, line, _ := runtime.Caller(1)
381 tc.t.Fatalf("%s:%d -- HaveTransaction: want %v, got %v", file,
382 line, wantHaveTx, gotHaveTx)
386 // TestSimpleOrphanChain ensures that a simple chain of orphans is handled
387 // properly. In particular, it generates a chain of single input, single output
388 // transactions and inserts them while skipping the first linking transaction so
389 // they are all orphans. Finally, it adds the linking transaction and ensures
390 // the entire orphan chain is moved to the transaction pool.
391 func TestSimpleOrphanChain(t *testing.T) {
394 harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
396 t.Fatalf("unable to create test pool: %v", err)
398 tc := &testContext{t, harness}
400 // Create a chain of transactions rooted with the first spendable output
401 // provided by the harness.
402 maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
403 chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
405 t.Fatalf("unable to create transaction chain: %v", err)
408 // Ensure the orphans are accepted (only up to the maximum allowed so
409 // none are evicted).
410 for _, tx := range chainedTxns[1 : maxOrphans+1] {
411 acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
414 t.Fatalf("ProcessTransaction: failed to accept valid "+
418 // Ensure no transactions were reported as accepted.
419 if len(acceptedTxns) != 0 {
420 t.Fatalf("ProcessTransaction: reported %d accepted "+
421 "transactions from what should be an orphan",
425 // Ensure the transaction is in the orphan pool, is not in the
426 // transaction pool, and is reported as available.
427 testPoolMembership(tc, tx, true, false)
430 // Add the transaction which completes the orphan chain and ensure they
431 // all get accepted. Notice the accept orphans flag is also false here
432 // to ensure it has no bearing on whether or not already existing
433 // orphans in the pool are linked.
434 acceptedTxns, err := harness.txPool.ProcessTransaction(chainedTxns[0],
437 t.Fatalf("ProcessTransaction: failed to accept valid "+
440 if len(acceptedTxns) != len(chainedTxns) {
441 t.Fatalf("ProcessTransaction: reported accepted transactions "+
442 "length does not match expected -- got %d, want %d",
443 len(acceptedTxns), len(chainedTxns))
445 for _, txD := range acceptedTxns {
446 // Ensure the transaction is no longer in the orphan pool, is
447 // now in the transaction pool, and is reported as available.
448 testPoolMembership(tc, txD.Tx, false, true)
452 // TestOrphanReject ensures that orphans are properly rejected when the allow
453 // orphans flag is not set on ProcessTransaction.
454 func TestOrphanReject(t *testing.T) {
457 harness, outputs, err := newPoolHarness(&chaincfg.MainNetParams)
459 t.Fatalf("unable to create test pool: %v", err)
461 tc := &testContext{t, harness}
463 // Create a chain of transactions rooted with the first spendable output
464 // provided by the harness.
465 maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
466 chainedTxns, err := harness.CreateTxChain(outputs[0], maxOrphans+1)
468 t.Fatalf("unable to create transaction chain: %v", err)
471 // Ensure orphans are rejected when the allow orphans flag is not set.
472 for _, tx := range chainedTxns[1:] {
473 acceptedTxns, err := harness.txPool.ProcessTransaction(tx, false,
476 t.Fatalf("ProcessTransaction: did not fail on orphan "+
477 "%v when allow orphans flag is false", tx.Hash())
479 expectedErr := RuleError{}
480 if reflect.TypeOf(err) != reflect.TypeOf(expectedErr) {
481 t.Fatalf("ProcessTransaction: wrong error got: <%T> %v, "+
482 "want: <%T>", err, err, expectedErr)
484 code, extracted := extractRejectCode(err)
486 t.Fatalf("ProcessTransaction: failed to extract reject "+
487 "code from error %q", err)
489 if code != wire.RejectDuplicate {
490 t.Fatalf("ProcessTransaction: unexpected reject code "+
491 "-- got %v, want %v", code, wire.RejectDuplicate)
494 // Ensure no transactions were reported as accepted.
495 if len(acceptedTxns) != 0 {
496 t.Fatal("ProcessTransaction: reported %d accepted "+
497 "transactions from failed orphan attempt",
501 // Ensure the transaction is not in the orphan pool, not in the
502 // transaction pool, and not reported as available
503 testPoolMembership(tc, tx, false, false)
507 // TestOrphanEviction ensures that exceeding the maximum number of orphans
508 // evicts entries to make room for the new ones.
509 func TestOrphanEviction(t *testing.T) {
512 harness, outputs, err := newPoolHarness(&chaincfg.MainNetParams)
514 t.Fatalf("unable to create test pool: %v", err)
516 tc := &testContext{t, harness}
518 // Create a chain of transactions rooted with the first spendable output
519 // provided by the harness that is long enough to be able to force
520 // several orphan evictions.
521 maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
522 chainedTxns, err := harness.CreateTxChain(outputs[0], maxOrphans+5)
524 t.Fatalf("unable to create transaction chain: %v", err)
527 // Add enough orphans to exceed the max allowed while ensuring they are
528 // all accepted. This will cause an eviction.
529 for _, tx := range chainedTxns[1:] {
530 acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
533 t.Fatalf("ProcessTransaction: failed to accept valid "+
537 // Ensure no transactions were reported as accepted.
538 if len(acceptedTxns) != 0 {
539 t.Fatalf("ProcessTransaction: reported %d accepted "+
540 "transactions from what should be an orphan",
544 // Ensure the transaction is in the orphan pool, is not in the
545 // transaction pool, and is reported as available.
546 testPoolMembership(tc, tx, true, false)
549 // Figure out which transactions were evicted and make sure the number
550 // evicted matches the expected number.
551 var evictedTxns []*btcutil.Tx
552 for _, tx := range chainedTxns[1:] {
553 if !harness.txPool.IsOrphanInPool(tx.Hash()) {
554 evictedTxns = append(evictedTxns, tx)
557 expectedEvictions := len(chainedTxns) - 1 - int(maxOrphans)
558 if len(evictedTxns) != expectedEvictions {
559 t.Fatalf("unexpected number of evictions -- got %d, want %d",
560 len(evictedTxns), expectedEvictions)
563 // Ensure none of the evicted transactions ended up in the transaction
565 for _, tx := range evictedTxns {
566 testPoolMembership(tc, tx, false, false)
570 // TestBasicOrphanRemoval ensure that orphan removal works as expected when an
571 // orphan that doesn't exist is removed both when there is another orphan that
572 // redeems it and when there is not.
573 func TestBasicOrphanRemoval(t *testing.T) {
577 harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
579 t.Fatalf("unable to create test pool: %v", err)
581 harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
582 tc := &testContext{t, harness}
584 // Create a chain of transactions rooted with the first spendable output
585 // provided by the harness.
586 chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
588 t.Fatalf("unable to create transaction chain: %v", err)
591 // Ensure the orphans are accepted (only up to the maximum allowed so
592 // none are evicted).
593 for _, tx := range chainedTxns[1 : maxOrphans+1] {
594 acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
597 t.Fatalf("ProcessTransaction: failed to accept valid "+
601 // Ensure no transactions were reported as accepted.
602 if len(acceptedTxns) != 0 {
603 t.Fatalf("ProcessTransaction: reported %d accepted "+
604 "transactions from what should be an orphan",
608 // Ensure the transaction is in the orphan pool, not in the
609 // transaction pool, and reported as available.
610 testPoolMembership(tc, tx, true, false)
613 // Attempt to remove an orphan that has no redeemers and is not present,
614 // and ensure the state of all other orphans are unaffected.
615 nonChainedOrphanTx, err := harness.CreateSignedTx([]spendableOutput{{
616 amount: btcutil.Amount(5000000000),
617 outPoint: wire.OutPoint{Hash: chainhash.Hash{}, Index: 0},
620 t.Fatalf("unable to create signed tx: %v", err)
623 harness.txPool.RemoveOrphan(nonChainedOrphanTx)
624 testPoolMembership(tc, nonChainedOrphanTx, false, false)
625 for _, tx := range chainedTxns[1 : maxOrphans+1] {
626 testPoolMembership(tc, tx, true, false)
629 // Attempt to remove an orphan that has a existing redeemer but itself
630 // is not present and ensure the state of all other orphans (including
631 // the one that redeems it) are unaffected.
632 harness.txPool.RemoveOrphan(chainedTxns[0])
633 testPoolMembership(tc, chainedTxns[0], false, false)
634 for _, tx := range chainedTxns[1 : maxOrphans+1] {
635 testPoolMembership(tc, tx, true, false)
638 // Remove each orphan one-by-one and ensure they are removed as
640 for _, tx := range chainedTxns[1 : maxOrphans+1] {
641 harness.txPool.RemoveOrphan(tx)
642 testPoolMembership(tc, tx, false, false)
646 // TestOrphanChainRemoval ensure that orphan chains (orphans that spend outputs
647 // from other orphans) are removed as expected.
648 func TestOrphanChainRemoval(t *testing.T) {
651 const maxOrphans = 10
652 harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
654 t.Fatalf("unable to create test pool: %v", err)
656 harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
657 tc := &testContext{t, harness}
659 // Create a chain of transactions rooted with the first spendable output
660 // provided by the harness.
661 chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
663 t.Fatalf("unable to create transaction chain: %v", err)
666 // Ensure the orphans are accepted (only up to the maximum allowed so
667 // none are evicted).
668 for _, tx := range chainedTxns[1 : maxOrphans+1] {
669 acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
672 t.Fatalf("ProcessTransaction: failed to accept valid "+
676 // Ensure no transactions were reported as accepted.
677 if len(acceptedTxns) != 0 {
678 t.Fatalf("ProcessTransaction: reported %d accepted "+
679 "transactions from what should be an orphan",
683 // Ensure the transaction is in the orphan pool, not in the
684 // transaction pool, and reported as available.
685 testPoolMembership(tc, tx, true, false)
688 // Remove the first orphan that starts the orphan chain without the
689 // remove redeemer flag set and ensure that only the first orphan was
691 harness.txPool.mtx.Lock()
692 harness.txPool.removeOrphan(chainedTxns[1], false)
693 harness.txPool.mtx.Unlock()
694 testPoolMembership(tc, chainedTxns[1], false, false)
695 for _, tx := range chainedTxns[2 : maxOrphans+1] {
696 testPoolMembership(tc, tx, true, false)
699 // Remove the first remaining orphan that starts the orphan chain with
700 // the remove redeemer flag set and ensure they are all removed.
701 harness.txPool.mtx.Lock()
702 harness.txPool.removeOrphan(chainedTxns[2], true)
703 harness.txPool.mtx.Unlock()
704 for _, tx := range chainedTxns[2 : maxOrphans+1] {
705 testPoolMembership(tc, tx, false, false)
709 // TestMultiInputOrphanDoubleSpend ensures that orphans that spend from an
710 // output that is spend by another transaction entering the pool are removed.
711 func TestMultiInputOrphanDoubleSpend(t *testing.T) {
715 harness, outputs, err := newPoolHarness(&chaincfg.MainNetParams)
717 t.Fatalf("unable to create test pool: %v", err)
719 harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
720 tc := &testContext{t, harness}
722 // Create a chain of transactions rooted with the first spendable output
723 // provided by the harness.
724 chainedTxns, err := harness.CreateTxChain(outputs[0], maxOrphans+1)
726 t.Fatalf("unable to create transaction chain: %v", err)
729 // Start by adding the orphan transactions from the generated chain
730 // except the final one.
731 for _, tx := range chainedTxns[1:maxOrphans] {
732 acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
735 t.Fatalf("ProcessTransaction: failed to accept valid "+
738 if len(acceptedTxns) != 0 {
739 t.Fatalf("ProcessTransaction: reported %d accepted transactions "+
740 "from what should be an orphan", len(acceptedTxns))
742 testPoolMembership(tc, tx, true, false)
745 // Ensure a transaction that contains a double spend of the same output
746 // as the second orphan that was just added as well as a valid spend
747 // from that last orphan in the chain generated above (and is not in the
748 // orphan pool) is accepted to the orphan pool. This must be allowed
749 // since it would otherwise be possible for a malicious actor to disrupt
751 doubleSpendTx, err := harness.CreateSignedTx([]spendableOutput{
752 txOutToSpendableOut(chainedTxns[1], 0),
753 txOutToSpendableOut(chainedTxns[maxOrphans], 0),
756 t.Fatalf("unable to create signed tx: %v", err)
758 acceptedTxns, err := harness.txPool.ProcessTransaction(doubleSpendTx,
761 t.Fatalf("ProcessTransaction: failed to accept valid orphan %v",
764 if len(acceptedTxns) != 0 {
765 t.Fatalf("ProcessTransaction: reported %d accepted transactions "+
766 "from what should be an orphan", len(acceptedTxns))
768 testPoolMembership(tc, doubleSpendTx, true, false)
770 // Add the transaction which completes the orphan chain and ensure the
771 // chain gets accepted. Notice the accept orphans flag is also false
772 // here to ensure it has no bearing on whether or not already existing
773 // orphans in the pool are linked.
775 // This will cause the shared output to become a concrete spend which
776 // will in turn must cause the double spending orphan to be removed.
777 acceptedTxns, err = harness.txPool.ProcessTransaction(chainedTxns[0],
780 t.Fatalf("ProcessTransaction: failed to accept valid tx %v", err)
782 if len(acceptedTxns) != maxOrphans {
783 t.Fatalf("ProcessTransaction: reported accepted transactions "+
784 "length does not match expected -- got %d, want %d",
785 len(acceptedTxns), maxOrphans)
787 for _, txD := range acceptedTxns {
788 // Ensure the transaction is no longer in the orphan pool, is
789 // in the transaction pool, and is reported as available.
790 testPoolMembership(tc, txD.Tx, false, true)
793 // Ensure the double spending orphan is no longer in the orphan pool and
794 // was not moved to the transaction pool.
795 testPoolMembership(tc, doubleSpendTx, false, false)