1 package mempool
2 3 import (
4 "encoding/hex"
5 "github.com/p9c/p9/pkg/amt"
6 "github.com/p9c/p9/pkg/btcaddr"
7 "reflect"
8 "runtime"
9 "sync"
10 "testing"
11 "time"
12 13 "github.com/p9c/p9/pkg/blockchain"
14 "github.com/p9c/p9/pkg/chaincfg"
15 "github.com/p9c/p9/pkg/chainhash"
16 "github.com/p9c/p9/pkg/ecc"
17 "github.com/p9c/p9/pkg/txscript"
18 "github.com/p9c/p9/pkg/util"
19 "github.com/p9c/p9/pkg/wire"
20 )
21 22 // fakeChain is used by the pool harness to provide generated test utxos and a current faked chain height to the pool
23 // callbacks. This, in turn, allows transactions to appear as though they are spending completely valid utxos.
24 type fakeChain struct {
25 sync.RWMutex
26 utxos *blockchain.UtxoViewpoint
27 currentHeight int32
28 medianTimePast time.Time
29 }
30 31 // FetchUtxoView loads utxo details about the inputs referenced by the passed transaction from the point of view of the
32 // fake chain. It also attempts to fetch the utxos for the outputs of the transaction itself so the returned view can be
33 // examined for duplicate transactions. This function is safe for concurrent access however the returned view is NOT.
34 func (s *fakeChain) FetchUtxoView(tx *util.Tx) (*blockchain.UtxoViewpoint, error) {
35 s.RLock()
36 defer s.RUnlock()
37 // All entries are cloned to ensure modifications to the returned view do not affect the fake chain's view. Add an
38 // entry for the tx itself to the new view.
39 viewpoint := blockchain.NewUtxoViewpoint()
40 prevOut := wire.OutPoint{Hash: *tx.Hash()}
41 for txOutIdx := range tx.MsgTx().TxOut {
42 prevOut.Index = uint32(txOutIdx)
43 entry := s.utxos.LookupEntry(prevOut)
44 viewpoint.Entries()[prevOut] = entry.Clone()
45 }
46 // Add entries for all of the inputs to the tx to the new view.
47 for _, txIn := range tx.MsgTx().TxIn {
48 entry := s.utxos.LookupEntry(txIn.PreviousOutPoint)
49 viewpoint.Entries()[txIn.PreviousOutPoint] = entry.Clone()
50 }
51 return viewpoint, nil
52 }
53 54 // BestHeight returns the current height associated with the fake chain instance.
55 func (s *fakeChain) BestHeight() int32 {
56 s.RLock()
57 height := s.currentHeight
58 s.RUnlock()
59 return height
60 }
61 62 // SetHeight sets the current height associated with the fake chain instance.
63 func (s *fakeChain) SetHeight(height int32) {
64 s.Lock()
65 s.currentHeight = height
66 s.Unlock()
67 }
68 69 // MedianTimePast returns the current median time past associated with the fake chain instance.
70 func (s *fakeChain) MedianTimePast() time.Time {
71 s.RLock()
72 mtp := s.medianTimePast
73 s.RUnlock()
74 return mtp
75 }
76 77 // SetMedianTimePast sets the current median time past associated with the fake chain instance.
78 func (s *fakeChain) SetMedianTimePast(mtp time.Time) {
79 s.Lock()
80 s.medianTimePast = mtp
81 s.Unlock()
82 }
83 84 // // CalcSequenceLock returns the current sequence lock for the passed transaction associated with the fake chain
85 // // instance.
86 // func (s *fakeChain) CalcSequenceLock(tx *util.Tx,
87 // view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
88 // return &blockchain.SequenceLock{
89 // Seconds: -1,
90 // BlockHeight: -1,
91 // }, nil
92 // }
93 94 // spendableOutput is a convenience type that houses a particular utxo and the amount associated with it.
95 type spendableOutput struct {
96 outPoint wire.OutPoint
97 amount amt.Amount
98 }
99 100 // txOutToSpendableOut returns a spendable output given a transaction and index of the output to use. This is useful as
101 // a convenience when creating test transactions.
102 func txOutToSpendableOut(tx *util.Tx, outputNum uint32) spendableOutput {
103 return spendableOutput{
104 outPoint: wire.OutPoint{Hash: *tx.Hash(), Index: outputNum},
105 amount: amt.Amount(tx.MsgTx().TxOut[outputNum].Value),
106 }
107 }
108 109 // poolHarness provides a harness that includes functionality for creating and signing transactions as well as a fake
110 // chain that provides utxos for use in generating valid transactions.
111 type poolHarness struct {
112 // signKey is the signing key used for creating transactions throughout the tests. payAddr is the p2sh address for
113 // the signing key and is used for the payment address throughout the tests.
114 signKey *ecc.PrivateKey
115 payAddr btcaddr.Address
116 payScript []byte
117 chainParams *chaincfg.Params
118 chain *fakeChain
119 txPool *TxPool
120 }
121 122 // CreateCoinbaseTx returns a coinbase transaction with the requested number of outputs paying an appropriate subsidy
123 // based on the passed block height to the address associated with the harness. It automatically uses a standard
124 // signature script that starts with the block height that is required by version 2 blocks.
125 func (p *poolHarness) CreateCoinbaseTx(blockHeight int32, numOutputs uint32, version int32) (*util.Tx, error) {
126 // Create standard coinbase script.
127 extraNonce := int64(0)
128 coinbaseScript, e := txscript.NewScriptBuilder().
129 AddInt64(int64(blockHeight)).AddInt64(extraNonce).Script()
130 if e != nil {
131 return nil, e
132 }
133 tx := wire.NewMsgTx(wire.TxVersion)
134 tx.AddTxIn(
135 &wire.TxIn{
136 // Coinbase transactions have no inputs, so previous outpoint is zero hash and max index.
137 PreviousOutPoint: *wire.NewOutPoint(
138 &chainhash.Hash{},
139 wire.MaxPrevOutIndex,
140 ),
141 SignatureScript: coinbaseScript,
142 Sequence: wire.MaxTxInSequenceNum,
143 },
144 )
145 totalInput := blockchain.CalcBlockSubsidy(blockHeight, p.chainParams, version)
146 amountPerOutput := totalInput / int64(numOutputs)
147 remainder := totalInput - amountPerOutput*int64(numOutputs)
148 for i := uint32(0); i < numOutputs; i++ {
149 // Ensure the final output accounts for any remainder that might be left from splitting the input amount.
150 amount := amountPerOutput
151 if i == numOutputs-1 {
152 amount = amountPerOutput + remainder
153 }
154 tx.AddTxOut(
155 &wire.TxOut{
156 PkScript: p.payScript,
157 Value: amount,
158 },
159 )
160 }
161 return util.NewTx(tx), nil
162 }
163 164 // CreateSignedTx creates a new signed transaction that consumes the provided inputs and generates the provided number
165 // of outputs by evenly splitting the total input amount. All outputs will be to the payment script associated with the
166 // harness and all inputs are assumed to do the same.
167 func (p *poolHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint32) (*util.Tx, error) {
168 // Calculate the total input amount and split it amongst the requested number of outputs.
169 var totalInput amt.Amount
170 for _, input := range inputs {
171 totalInput += input.amount
172 }
173 amountPerOutput := int64(totalInput) / int64(numOutputs)
174 remainder := int64(totalInput) - amountPerOutput*int64(numOutputs)
175 tx := wire.NewMsgTx(wire.TxVersion)
176 for _, input := range inputs {
177 tx.AddTxIn(
178 &wire.TxIn{
179 PreviousOutPoint: input.outPoint,
180 SignatureScript: nil,
181 Sequence: wire.MaxTxInSequenceNum,
182 },
183 )
184 }
185 for i := uint32(0); i < numOutputs; i++ {
186 // Ensure the final output accounts for any remainder that might be left from splitting the input amount.
187 amount := amountPerOutput
188 if i == numOutputs-1 {
189 amount = amountPerOutput + remainder
190 }
191 tx.AddTxOut(
192 &wire.TxOut{
193 PkScript: p.payScript,
194 Value: amount,
195 },
196 )
197 }
198 // Sign the new transaction.
199 for i := range tx.TxIn {
200 sigScript, e := txscript.SignatureScript(
201 tx, i, p.payScript,
202 txscript.SigHashAll, p.signKey, true,
203 )
204 if e != nil {
205 return nil, e
206 }
207 tx.TxIn[i].SignatureScript = sigScript
208 }
209 return util.NewTx(tx), nil
210 }
211 212 // CreateTxChain creates a chain of zero-fee transactions (each subsequent transaction spends the entire amount from the
213 // previous one) with the first one spending the provided outpoint. Each transaction spends the entire amount of the
214 // previous one and as such does not include any fees.
215 func (p *poolHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint32) ([]*util.Tx, error) {
216 txChain := make([]*util.Tx, 0, numTxns)
217 prevOutPoint := firstOutput.outPoint
218 spendableAmount := firstOutput.amount
219 for i := uint32(0); i < numTxns; i++ {
220 // Create the transaction using the previous transaction output and paying the full amount to the payment
221 // address associated with the harness.
222 tx := wire.NewMsgTx(wire.TxVersion)
223 tx.AddTxIn(
224 &wire.TxIn{
225 PreviousOutPoint: prevOutPoint,
226 SignatureScript: nil,
227 Sequence: wire.MaxTxInSequenceNum,
228 },
229 )
230 tx.AddTxOut(
231 &wire.TxOut{
232 PkScript: p.payScript,
233 Value: int64(spendableAmount),
234 },
235 )
236 // Sign the new transaction.
237 sigScript, e := txscript.SignatureScript(
238 tx, 0, p.payScript,
239 txscript.SigHashAll, p.signKey, true,
240 )
241 if e != nil {
242 return nil, e
243 }
244 tx.TxIn[0].SignatureScript = sigScript
245 txChain = append(txChain, util.NewTx(tx))
246 // Next transaction uses outputs from this one.
247 prevOutPoint = wire.OutPoint{Hash: tx.TxHash(), Index: 0}
248 }
249 return txChain, nil
250 }
251 252 // newPoolHarness returns a new instance of a pool harness initialized with a fake chain and a TxPool bound to it that
253 // is configured with a policy suitable for testing. Also the fake chain is populated with the returned spendable
254 // outputs so the caller can easily create new valid transactions which podbuild off of it.
255 func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutput, error) {
256 // Use a hard coded key pair for deterministic results.
257 keyBytes, e := hex.DecodeString(
258 "700868df1838811ffbdf918fb482c1f7e" +
259 "ad62db4b97bd7012c23e726485e577d",
260 )
261 if e != nil {
262 return nil, nil, e
263 }
264 signKey, signPub := ecc.PrivKeyFromBytes(ecc.S256(), keyBytes)
265 // Generate associated pay-to-script-hash address and resulting payment script.
266 pubKeyBytes := signPub.SerializeCompressed()
267 payPubKeyAddr, e := btcaddr.NewPubKey(pubKeyBytes, chainParams)
268 if e != nil {
269 return nil, nil, e
270 }
271 payAddr := payPubKeyAddr.PubKeyHash()
272 pkScript, e := txscript.PayToAddrScript(payAddr)
273 if e != nil {
274 return nil, nil, e
275 }
276 // Create a new fake chain and harness bound to it.
277 chain := &fakeChain{utxos: blockchain.NewUtxoViewpoint()}
278 harness := poolHarness{
279 signKey: signKey,
280 payAddr: payAddr,
281 payScript: pkScript,
282 chainParams: chainParams,
283 chain: chain,
284 txPool: New(
285 &Config{
286 Policy: Policy{
287 DisableRelayPriority: true,
288 FreeTxRelayLimit: 15.0,
289 MaxOrphanTxs: 5,
290 MaxOrphanTxSize: 1000,
291 MaxSigOpCostPerTx: blockchain.MaxBlockSigOpsCost / 4,
292 MinRelayTxFee: 1000, // 1 Satoshi per byte
293 MaxTxVersion: 1,
294 },
295 ChainParams: chainParams,
296 FetchUtxoView: chain.FetchUtxoView,
297 BestHeight: chain.BestHeight,
298 MedianTimePast: chain.MedianTimePast,
299 // CalcSequenceLock: chain.CalcSequenceLock,
300 SigCache: nil,
301 AddrIndex: nil,
302 },
303 ),
304 }
305 // Create a single coinbase transaction and add it to the harness chain's utxo set and set the harness chain height
306 // such that the coinbase will mature in the next block. This ensures the txpool accepts transactions which spend
307 // immature coinbases that will become mature in the next block.
308 numOutputs := uint32(1)
309 outputs := make([]spendableOutput, 0, numOutputs)
310 curHeight := harness.chain.BestHeight()
311 coinbase, e := harness.CreateCoinbaseTx(curHeight+1, numOutputs, 0)
312 if e != nil {
313 return nil, nil, e
314 }
315 harness.chain.utxos.AddTxOuts(coinbase, curHeight+1)
316 for i := uint32(0); i < numOutputs; i++ {
317 outputs = append(outputs, txOutToSpendableOut(coinbase, i))
318 }
319 harness.chain.SetHeight(int32(chainParams.CoinbaseMaturity) + curHeight)
320 harness.chain.SetMedianTimePast(time.Now())
321 return &harness, outputs, nil
322 }
323 324 // testContext houses a test-related state that is useful to pass to helper functions as a single argument.
325 type testContext struct {
326 t *testing.T
327 harness *poolHarness
328 }
329 330 // testPoolMembership tests the transaction pool associated with the provided test context to determine if the passed
331 // transaction matches the provided orphan pool and transaction pool status. It also further determines if it should be
332 // reported as available by the HaveTransaction function based upon the two flags and tests that condition as well.
333 func testPoolMembership(tc *testContext, tx *util.Tx, inOrphanPool, inTxPool bool) {
334 txHash := tx.Hash()
335 gotOrphanPool := tc.harness.txPool.IsOrphanInPool(txHash)
336 if inOrphanPool != gotOrphanPool {
337 _, file, line, _ := runtime.Caller(1)
338 tc.t.Fatalf(
339 "%s:%d -- IsOrphanInPool: want %v, got %v", file,
340 line, inOrphanPool, gotOrphanPool,
341 )
342 }
343 gotTxPool := tc.harness.txPool.IsTransactionInPool(txHash)
344 if inTxPool != gotTxPool {
345 _, file, line, _ := runtime.Caller(1)
346 tc.t.Fatalf(
347 "%s:%d -- IsTransactionInPool: want %v, got %v",
348 file, line, inTxPool, gotTxPool,
349 )
350 }
351 gotHaveTx := tc.harness.txPool.HaveTransaction(txHash)
352 wantHaveTx := inOrphanPool || inTxPool
353 if wantHaveTx != gotHaveTx {
354 _, file, line, _ := runtime.Caller(1)
355 tc.t.Fatalf(
356 "%s:%d -- HaveTransaction: want %v, got %v", file,
357 line, wantHaveTx, gotHaveTx,
358 )
359 }
360 }
361 362 // TestSimpleOrphanChain ensures that a simple chain of orphans is handled properly. In particular, it generates a chain
363 // of single input, single output transactions and inserts them while skipping the first linking transaction so they are
364 // all orphans. Finally, it adds the linking transaction and ensures the entire orphan chain is moved to the transaction
365 // pool.
366 func TestSimpleOrphanChain(t *testing.T) {
367 t.Parallel()
368 harness, spendableOuts, e := newPoolHarness(&chaincfg.MainNetParams)
369 if e != nil {
370 t.Fatalf("unable to create test pool: %v", e)
371 }
372 tc := &testContext{t, harness}
373 // Create a chain of transactions rooted with the first spendable output provided by the harness.
374 maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
375 chainedTxns, e := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
376 if e != nil {
377 t.Fatalf("unable to create transaction chain: %v", e)
378 }
379 // Ensure the orphans are accepted (only up to the maximum allowed so none are evicted).
380 for _, tx := range chainedTxns[1 : maxOrphans+1] {
381 var acceptedTxns []*TxDesc
382 acceptedTxns, e = harness.txPool.ProcessTransaction(
383 nil, tx, true,
384 false, 0,
385 )
386 if e != nil {
387 t.Fatalf(
388 "ProcessTransaction: failed to accept valid "+
389 "orphan %v", e,
390 )
391 }
392 // Ensure no transactions were reported as accepted.
393 if len(acceptedTxns) != 0 {
394 t.Fatalf(
395 "ProcessTransaction: reported %d accepted "+
396 "transactions from what should be an orphan",
397 len(acceptedTxns),
398 )
399 }
400 // Ensure the transaction is in the orphan pool, is not in the transaction pool, and is reported as available.
401 testPoolMembership(tc, tx, true, false)
402 }
403 // Add the transaction which completes the orphan chain and ensure they all get accepted. Notice the accept orphans
404 // flag is also false here to ensure it has no bearing on whether or not already existing orphans in the pool are
405 // linked.
406 acceptedTxns, e := harness.txPool.ProcessTransaction(
407 nil, chainedTxns[0],
408 false, false, 0,
409 )
410 if e != nil {
411 t.Fatalf(
412 "ProcessTransaction: failed to accept valid "+
413 "orphan %v", e,
414 )
415 }
416 if len(acceptedTxns) != len(chainedTxns) {
417 t.Fatalf(
418 "ProcessTransaction: reported accepted transactions "+
419 "length does not match expected -- got %d, want %d",
420 len(acceptedTxns), len(chainedTxns),
421 )
422 }
423 for _, txD := range acceptedTxns {
424 // Ensure the transaction is no longer in the orphan pool, is now in the transaction pool, and is reported as
425 // available.
426 testPoolMembership(tc, txD.Tx, false, true)
427 }
428 }
429 430 // TestOrphanReject ensures that orphans are properly rejected when the allow orphans flag is not set on
431 // ProcessTransaction.
432 func TestOrphanReject(t *testing.T) {
433 t.Parallel()
434 harness, outputs, e := newPoolHarness(&chaincfg.MainNetParams)
435 if e != nil {
436 t.Fatalf("unable to create test pool: %v", e)
437 }
438 tc := &testContext{t, harness}
439 // Create a chain of transactions rooted with the first spendable output provided by the harness.
440 maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
441 chainedTxns, e := harness.CreateTxChain(outputs[0], maxOrphans+1)
442 if e != nil {
443 t.Fatalf("unable to create transaction chain: %v", e)
444 }
445 // Ensure orphans are rejected when the allow orphans flag is not set.
446 for _, tx := range chainedTxns[1:] {
447 acceptedTxns, e := harness.txPool.ProcessTransaction(
448 nil, tx, false,
449 false, 0,
450 )
451 if e == nil {
452 t.Fatalf(
453 "ProcessTransaction: did not fail on orphan "+
454 "%v when allow orphans flag is false", tx.Hash(),
455 )
456 }
457 expectedErr := RuleError{}
458 if reflect.TypeOf(e) != reflect.TypeOf(expectedErr) {
459 t.Fatalf(
460 "ProcessTransaction: wrong error got: <%T> %v, "+
461 "want: <%T>", e, e, expectedErr,
462 )
463 }
464 code, extracted := extractRejectCode(e)
465 if !extracted {
466 t.Fatalf(
467 "ProcessTransaction: failed to extract reject "+
468 "code from error %q", e,
469 )
470 }
471 if code != wire.RejectDuplicate {
472 t.Fatalf(
473 "ProcessTransaction: unexpected reject code "+
474 "-- got %v, want %v", code, wire.RejectDuplicate,
475 )
476 }
477 // Ensure no transactions were reported as accepted.
478 if len(acceptedTxns) != 0 {
479 t.Fatalf(
480 "ProcessTransaction: reported %d accepted "+
481 "transactions from failed orphan attempt",
482 len(acceptedTxns),
483 )
484 }
485 // Ensure the transaction is not in the orphan pool, not in the transaction pool, and not reported as available
486 testPoolMembership(tc, tx, false, false)
487 }
488 }
489 490 // TestOrphanEviction ensures that exceeding the maximum number of orphans evicts entries to make room for the new ones.
491 func TestOrphanEviction(t *testing.T) {
492 t.Parallel()
493 harness, outputs, e := newPoolHarness(&chaincfg.MainNetParams)
494 if e != nil {
495 t.Fatalf("unable to create test pool: %v", e)
496 }
497 tc := &testContext{t, harness}
498 // Create a chain of transactions rooted with the first spendable output provided by the harness that is long enough
499 // to be able to force several orphan evictions.
500 maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
501 chainedTxns, e := harness.CreateTxChain(outputs[0], maxOrphans+5)
502 if e != nil {
503 t.Fatalf("unable to create transaction chain: %v", e)
504 }
505 // Add enough orphans to exceed the max allowed while ensuring they are all accepted. This will cause an eviction.
506 for _, tx := range chainedTxns[1:] {
507 acceptedTxns, e := harness.txPool.ProcessTransaction(
508 nil, tx, true,
509 false, 0,
510 )
511 if e != nil {
512 t.Fatalf(
513 "ProcessTransaction: failed to accept valid "+
514 "orphan %v", e,
515 )
516 }
517 // Ensure no transactions were reported as accepted.
518 if len(acceptedTxns) != 0 {
519 t.Fatalf(
520 "ProcessTransaction: reported %d accepted "+
521 "transactions from what should be an orphan",
522 len(acceptedTxns),
523 )
524 }
525 // Ensure the transaction is in the orphan pool, is not in the transaction pool, and is reported as available.
526 testPoolMembership(tc, tx, true, false)
527 }
528 // Figure out which transactions were evicted and make sure the number evicted matches the expected number.
529 var evictedTxns []*util.Tx
530 for _, tx := range chainedTxns[1:] {
531 if !harness.txPool.IsOrphanInPool(tx.Hash()) {
532 evictedTxns = append(evictedTxns, tx)
533 }
534 }
535 expectedEvictions := len(chainedTxns) - 1 - int(maxOrphans)
536 if len(evictedTxns) != expectedEvictions {
537 t.Fatalf(
538 "unexpected number of evictions -- got %d, want %d",
539 len(evictedTxns), expectedEvictions,
540 )
541 }
542 // Ensure none of the evicted transactions ended up in the transaction pool.
543 for _, tx := range evictedTxns {
544 testPoolMembership(tc, tx, false, false)
545 }
546 }
547 548 // TestBasicOrphanRemoval ensure that orphan removal works as expected when an orphan that doesn't exist is removed both
549 // when there is another orphan that redeems it and when there is not.
550 func TestBasicOrphanRemoval(t *testing.T) {
551 t.Parallel()
552 const maxOrphans = 4
553 harness, spendableOuts, e := newPoolHarness(&chaincfg.MainNetParams)
554 if e != nil {
555 t.Fatalf("unable to create test pool: %v", e)
556 }
557 harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
558 tc := &testContext{t, harness}
559 // Create a chain of transactions rooted with the first spendable output provided by the harness.
560 chainedTxns, e := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
561 if e != nil {
562 t.Fatalf("unable to create transaction chain: %v", e)
563 }
564 // Ensure the orphans are accepted (only up to the maximum allowed so none are evicted).
565 for _, tx := range chainedTxns[1 : maxOrphans+1] {
566 var acceptedTxns []*TxDesc
567 acceptedTxns, e = harness.txPool.ProcessTransaction(
568 nil, tx, true,
569 false, 0,
570 )
571 if e != nil {
572 t.Fatalf(
573 "ProcessTransaction: failed to accept valid "+
574 "orphan %v", e,
575 )
576 }
577 // Ensure no transactions were reported as accepted.
578 if len(acceptedTxns) != 0 {
579 t.Fatalf(
580 "ProcessTransaction: reported %d accepted "+
581 "transactions from what should be an orphan",
582 len(acceptedTxns),
583 )
584 }
585 // Ensure the transaction is in the orphan pool, not in the transaction pool, and reported as available.
586 testPoolMembership(tc, tx, true, false)
587 }
588 // Attempt to remove an orphan that has no redeemers and is not present, and ensure the state of all other orphans
589 // are unaffected.
590 nonChainedOrphanTx, e := harness.CreateSignedTx(
591 []spendableOutput{
592 {
593 amount: amt.Amount(5000000000),
594 outPoint: wire.OutPoint{Hash: chainhash.Hash{}, Index: 0},
595 },
596 }, 1,
597 )
598 if e != nil {
599 t.Fatalf("unable to create signed tx: %v", e)
600 }
601 harness.txPool.RemoveOrphan(nonChainedOrphanTx)
602 testPoolMembership(tc, nonChainedOrphanTx, false, false)
603 for _, tx := range chainedTxns[1 : maxOrphans+1] {
604 testPoolMembership(tc, tx, true, false)
605 }
606 // Attempt to remove an orphan that has a existing redeemer but itself is not present and ensure the state of all
607 // other orphans (including the one that redeems it) are unaffected.
608 harness.txPool.RemoveOrphan(chainedTxns[0])
609 testPoolMembership(tc, chainedTxns[0], false, false)
610 for _, tx := range chainedTxns[1 : maxOrphans+1] {
611 testPoolMembership(tc, tx, true, false)
612 }
613 // Remove each orphan one-by-one and ensure they are removed as expected.
614 for _, tx := range chainedTxns[1 : maxOrphans+1] {
615 harness.txPool.RemoveOrphan(tx)
616 testPoolMembership(tc, tx, false, false)
617 }
618 }
619 620 // TestOrphanChainRemoval ensure that orphan chains (orphans that spend outputs from other orphans) are removed as
621 // expected.
622 func TestOrphanChainRemoval(t *testing.T) {
623 t.Parallel()
624 const maxOrphans = 10
625 harness, spendableOuts, e := newPoolHarness(&chaincfg.MainNetParams)
626 if e != nil {
627 t.Fatalf("unable to create test pool: %v", e)
628 }
629 harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
630 tc := &testContext{t, harness}
631 // Create a chain of transactions rooted with the first spendable output provided by the harness.
632 chainedTxns, e := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
633 if e != nil {
634 t.Fatalf("unable to create transaction chain: %v", e)
635 }
636 // Ensure the orphans are accepted (only up to the maximum allowed so none are evicted).
637 for _, tx := range chainedTxns[1 : maxOrphans+1] {
638 acceptedTxns, e := harness.txPool.ProcessTransaction(
639 nil, tx, true,
640 false, 0,
641 )
642 if e != nil {
643 t.Fatalf(
644 "ProcessTransaction: failed to accept valid "+
645 "orphan %v", e,
646 )
647 }
648 // Ensure no transactions were reported as accepted.
649 if len(acceptedTxns) != 0 {
650 t.Fatalf(
651 "ProcessTransaction: reported %d accepted "+
652 "transactions from what should be an orphan",
653 len(acceptedTxns),
654 )
655 }
656 // Ensure the transaction is in the orphan pool, not in the transaction pool, and reported as available.
657 testPoolMembership(tc, tx, true, false)
658 }
659 // Remove the first orphan that starts the orphan chain without the remove redeemer flag set and ensure that only
660 // the first orphan was removed.
661 harness.txPool.mtx.Lock()
662 harness.txPool.removeOrphan(chainedTxns[1], false)
663 harness.txPool.mtx.Unlock()
664 testPoolMembership(tc, chainedTxns[1], false, false)
665 for _, tx := range chainedTxns[2 : maxOrphans+1] {
666 testPoolMembership(tc, tx, true, false)
667 }
668 // Remove the first remaining orphan that starts the orphan chain with the remove redeemer flag set and ensure they
669 // are all removed.
670 harness.txPool.mtx.Lock()
671 harness.txPool.removeOrphan(chainedTxns[2], true)
672 harness.txPool.mtx.Unlock()
673 for _, tx := range chainedTxns[2 : maxOrphans+1] {
674 testPoolMembership(tc, tx, false, false)
675 }
676 }
677 678 // TestMultiInputOrphanDoubleSpend ensures that orphans that spend from an output that is spend by another transaction
679 // entering the pool are removed.
680 func TestMultiInputOrphanDoubleSpend(t *testing.T) {
681 t.Parallel()
682 const maxOrphans = 4
683 harness, outputs, e := newPoolHarness(&chaincfg.MainNetParams)
684 if e != nil {
685 t.Fatalf("unable to create test pool: %v", e)
686 }
687 harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
688 tc := &testContext{t, harness}
689 // Create a chain of transactions rooted with the first spendable output provided by the harness.
690 chainedTxns, e := harness.CreateTxChain(outputs[0], maxOrphans+1)
691 if e != nil {
692 t.Fatalf("unable to create transaction chain: %v", e)
693 }
694 // Start by adding the orphan transactions from the generated chain except the final one.
695 for _, tx := range chainedTxns[1:maxOrphans] {
696 var acceptedTxns []*TxDesc
697 acceptedTxns, e = harness.txPool.ProcessTransaction(
698 nil, tx, true,
699 false, 0,
700 )
701 if e != nil {
702 t.Fatalf(
703 "ProcessTransaction: failed to accept valid "+
704 "orphan %v", e,
705 )
706 }
707 if len(acceptedTxns) != 0 {
708 t.Fatalf(
709 "ProcessTransaction: reported %d accepted transactions "+
710 "from what should be an orphan", len(acceptedTxns),
711 )
712 }
713 testPoolMembership(tc, tx, true, false)
714 }
715 // Ensure a transaction that contains a double spend of the same output as the second orphan that was just added as
716 // well as a valid spend from that last orphan in the chain generated above (and is not in the orphan pool) is
717 // accepted to the orphan pool. This must be allowed since it would otherwise be possible for a malicious actor to
718 // disrupt tx chains.
719 doubleSpendTx, e := harness.CreateSignedTx(
720 []spendableOutput{
721 txOutToSpendableOut(chainedTxns[1], 0),
722 txOutToSpendableOut(chainedTxns[maxOrphans], 0),
723 }, 1,
724 )
725 if e != nil {
726 t.Fatalf("unable to create signed tx: %v", e)
727 }
728 acceptedTxns, e := harness.txPool.ProcessTransaction(
729 nil, doubleSpendTx,
730 true, false, 0,
731 )
732 if e != nil {
733 t.Fatalf(
734 "ProcessTransaction: failed to accept valid orphan %v",
735 e,
736 )
737 }
738 if len(acceptedTxns) != 0 {
739 t.Fatalf(
740 "ProcessTransaction: reported %d accepted transactions "+
741 "from what should be an orphan", len(acceptedTxns),
742 )
743 }
744 testPoolMembership(tc, doubleSpendTx, true, false)
745 // Add the transaction which completes the orphan chain and ensure the chain gets accepted. Notice the accept
746 // orphans flag is also false here to ensure it has no bearing on whether or not already existing orphans in the
747 // pool are linked. This will cause the shared output to become a concrete spend which will in turn must cause the
748 // double spending orphan to be removed.
749 acceptedTxns, e = harness.txPool.ProcessTransaction(
750 nil, chainedTxns[0],
751 false, false, 0,
752 )
753 if e != nil {
754 t.Fatalf("ProcessTransaction: failed to accept valid tx %v", e)
755 }
756 if len(acceptedTxns) != maxOrphans {
757 t.Fatalf(
758 "ProcessTransaction: reported accepted transactions "+
759 "length does not match expected -- got %d, want %d",
760 len(acceptedTxns), maxOrphans,
761 )
762 }
763 for _, txD := range acceptedTxns {
764 // Ensure the transaction is no longer in the orphan pool, is in the transaction pool, and is reported as
765 // available.
766 testPoolMembership(tc, txD.Tx, false, true)
767 }
768 // Ensure the double spending orphan is no longer in the orphan pool and was not moved to the transaction pool.
769 testPoolMembership(tc, doubleSpendTx, false, false)
770 }
771 772 // TestCheckSpend tests that CheckSpend returns the expected spends found in the mempool.
773 func TestCheckSpend(t *testing.T) {
774 t.Parallel()
775 harness, outputs, e := newPoolHarness(&chaincfg.MainNetParams)
776 if e != nil {
777 t.Fatalf("unable to create test pool: %v", e)
778 }
779 // The mempool is empty, so none of the spendable outputs should have a spend there.
780 for _, op := range outputs {
781 spend := harness.txPool.CheckSpend(op.outPoint)
782 if spend != nil {
783 t.Fatalf("Unexpeced spend found in pool: %v", spend)
784 }
785 }
786 // Create a chain of transactions rooted with the first spendable output provided by the harness.
787 const txChainLength = 5
788 chainedTxns, e := harness.CreateTxChain(outputs[0], txChainLength)
789 if e != nil {
790 t.Fatalf("unable to create transaction chain: %v", e)
791 }
792 for _, tx := range chainedTxns {
793 _, e := harness.txPool.ProcessTransaction(
794 nil, tx, true,
795 false, 0,
796 )
797 if e != nil {
798 t.Fatalf(
799 "ProcessTransaction: failed to accept "+
800 "tx: %v", e,
801 )
802 }
803 }
804 // The first tx in the chain should be the spend of the spendable output.
805 op := outputs[0].outPoint
806 spend := harness.txPool.CheckSpend(op)
807 if spend != chainedTxns[0] {
808 t.Fatalf(
809 "expected %v to be spent by %v, instead "+
810 "got %v", op, chainedTxns[0], spend,
811 )
812 }
813 // Now all but the last tx should be spent by the next.
814 for i := 0; i < len(chainedTxns)-1; i++ {
815 op = wire.OutPoint{
816 Hash: *chainedTxns[i].Hash(),
817 Index: 0,
818 }
819 expSpend := chainedTxns[i+1]
820 spend = harness.txPool.CheckSpend(op)
821 if spend != expSpend {
822 t.Fatalf(
823 "expected %v to be spent by %v, instead "+
824 "got %v", op, expSpend, spend,
825 )
826 }
827 }
828 // The last tx should have no spend.
829 op = wire.OutPoint{
830 Hash: *chainedTxns[txChainLength-1].Hash(),
831 Index: 0,
832 }
833 spend = harness.txPool.CheckSpend(op)
834 if spend != nil {
835 t.Fatalf("Unexpeced spend found in pool: %v", spend)
836 }
837 }
838