lbcd/mempool/mempool_test.go

797 lines
27 KiB
Go
Raw Normal View History

mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package mempool
import (
"encoding/hex"
"reflect"
"runtime"
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
"sync"
"testing"
"time"
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// fakeChain is used by the pool harness to provide generated test utxos and
// a current faked chain height to the pool callbacks. This, in turn, allows
// transations to be appear as though they are spending completely valid utxos.
type fakeChain struct {
sync.RWMutex
utxos *blockchain.UtxoViewpoint
currentHeight int32
medianTimePast time.Time
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
}
// FetchUtxoView loads utxo details about the input transactions referenced by
// the passed transaction from the point of view of the fake chain.
// It also attempts to fetch the utxo details for the transaction itself so the
// returned view can be examined for duplicate unspent transaction outputs.
//
// This function is safe for concurrent access however the returned view is NOT.
func (s *fakeChain) FetchUtxoView(tx *btcutil.Tx) (*blockchain.UtxoViewpoint, error) {
s.RLock()
defer s.RUnlock()
// All entries are cloned to ensure modifications to the returned view
// do not affect the fake chain's view.
// Add an entry for the tx itself to the new view.
viewpoint := blockchain.NewUtxoViewpoint()
entry := s.utxos.LookupEntry(tx.Hash())
viewpoint.Entries()[*tx.Hash()] = entry.Clone()
// Add entries for all of the inputs to the tx to the new view.
for _, txIn := range tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
entry := s.utxos.LookupEntry(originHash)
viewpoint.Entries()[*originHash] = entry.Clone()
}
return viewpoint, nil
}
// BestHeight returns the current height associated with the fake chain
// instance.
func (s *fakeChain) BestHeight() int32 {
s.RLock()
height := s.currentHeight
s.RUnlock()
return height
}
// SetHeight sets the current height associated with the fake chain instance.
func (s *fakeChain) SetHeight(height int32) {
s.Lock()
s.currentHeight = height
s.Unlock()
}
// MedianTimePast returns the current median time past associated with the fake
// chain instance.
func (s *fakeChain) MedianTimePast() time.Time {
s.RLock()
mtp := s.medianTimePast
s.RUnlock()
return mtp
}
// SetMedianTimePast sets the current median time past associated with the fake
// chain instance.
func (s *fakeChain) SetMedianTimePast(mtp time.Time) {
s.Lock()
s.medianTimePast = mtp
s.Unlock()
}
// CalcSequenceLock returns the current sequence lock for the passed
// transaction associated with the fake chain instance.
func (s *fakeChain) CalcSequenceLock(tx *btcutil.Tx,
view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
return &blockchain.SequenceLock{
Seconds: -1,
BlockHeight: -1,
}, nil
}
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
// spendableOutput is a convenience type that houses a particular utxo and the
// amount associated with it.
type spendableOutput struct {
outPoint wire.OutPoint
amount btcutil.Amount
}
// txOutToSpendableOut returns a spendable output given a transaction and index
// of the output to use. This is useful as a convenience when creating test
// transactions.
func txOutToSpendableOut(tx *btcutil.Tx, outputNum uint32) spendableOutput {
return spendableOutput{
outPoint: wire.OutPoint{Hash: *tx.Hash(), Index: outputNum},
amount: btcutil.Amount(tx.MsgTx().TxOut[outputNum].Value),
}
}
// poolHarness provides a harness that includes functionality for creating and
// signing transactions as well as a fake chain that provides utxos for use in
// generating valid transactions.
type poolHarness struct {
// signKey is the signing key used for creating transactions throughout
// the tests.
//
// payAddr is the p2sh address for the signing key and is used for the
// payment address throughout the tests.
signKey *btcec.PrivateKey
payAddr btcutil.Address
payScript []byte
chainParams *chaincfg.Params
chain *fakeChain
txPool *TxPool
}
// CreateCoinbaseTx returns a coinbase transaction with the requested number of
// outputs paying an appropriate subsidy based on the passed block height to the
// address associated with the harness. It automatically uses a standard
// signature script that starts with the block height that is required by
// version 2 blocks.
func (p *poolHarness) CreateCoinbaseTx(blockHeight int32, numOutputs uint32) (*btcutil.Tx, error) {
// Create standard coinbase script.
extraNonce := int64(0)
coinbaseScript, err := txscript.NewScriptBuilder().
AddInt64(int64(blockHeight)).AddInt64(extraNonce).Script()
if err != nil {
return nil, err
}
tx := wire.NewMsgTx(wire.TxVersion)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
tx.AddTxIn(&wire.TxIn{
// Coinbase transactions have no inputs, so previous outpoint is
// zero hash and max index.
PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
wire.MaxPrevOutIndex),
SignatureScript: coinbaseScript,
Sequence: wire.MaxTxInSequenceNum,
})
totalInput := blockchain.CalcBlockSubsidy(blockHeight, p.chainParams)
amountPerOutput := totalInput / int64(numOutputs)
remainder := totalInput - amountPerOutput*int64(numOutputs)
for i := uint32(0); i < numOutputs; i++ {
// Ensure the final output accounts for any remainder that might
// be left from splitting the input amount.
amount := amountPerOutput
if i == numOutputs-1 {
amount = amountPerOutput + remainder
}
tx.AddTxOut(&wire.TxOut{
PkScript: p.payScript,
Value: amount,
})
}
return btcutil.NewTx(tx), nil
}
// CreateSignedTx creates a new signed transaction that consumes the provided
// inputs and generates the provided number of outputs by evenly splitting the
// total input amount. All outputs will be to the payment script associated
// with the harness and all inputs are assumed to do the same.
func (p *poolHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint32) (*btcutil.Tx, error) {
// Calculate the total input amount and split it amongst the requested
// number of outputs.
var totalInput btcutil.Amount
for _, input := range inputs {
totalInput += input.amount
}
amountPerOutput := int64(totalInput) / int64(numOutputs)
remainder := int64(totalInput) - amountPerOutput*int64(numOutputs)
tx := wire.NewMsgTx(wire.TxVersion)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
for _, input := range inputs {
tx.AddTxIn(&wire.TxIn{
PreviousOutPoint: input.outPoint,
SignatureScript: nil,
Sequence: wire.MaxTxInSequenceNum,
})
}
for i := uint32(0); i < numOutputs; i++ {
// Ensure the final output accounts for any remainder that might
// be left from splitting the input amount.
amount := amountPerOutput
if i == numOutputs-1 {
amount = amountPerOutput + remainder
}
tx.AddTxOut(&wire.TxOut{
PkScript: p.payScript,
Value: amount,
})
}
// Sign the new transaction.
for i := range tx.TxIn {
sigScript, err := txscript.SignatureScript(tx, i, p.payScript,
txscript.SigHashAll, p.signKey, true)
if err != nil {
return nil, err
}
tx.TxIn[i].SignatureScript = sigScript
}
return btcutil.NewTx(tx), nil
}
// CreateTxChain creates a chain of zero-fee transactions (each subsequent
// transaction spends the entire amount from the previous one) with the first
// one spending the provided outpoint. Each transaction spends the entire
// amount of the previous one and as such does not include any fees.
func (p *poolHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint32) ([]*btcutil.Tx, error) {
txChain := make([]*btcutil.Tx, 0, numTxns)
prevOutPoint := firstOutput.outPoint
spendableAmount := firstOutput.amount
for i := uint32(0); i < numTxns; i++ {
// Create the transaction using the previous transaction output
// and paying the full amount to the payment address associated
// with the harness.
tx := wire.NewMsgTx(wire.TxVersion)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
tx.AddTxIn(&wire.TxIn{
PreviousOutPoint: prevOutPoint,
SignatureScript: nil,
Sequence: wire.MaxTxInSequenceNum,
})
tx.AddTxOut(&wire.TxOut{
PkScript: p.payScript,
Value: int64(spendableAmount),
})
// Sign the new transaction.
sigScript, err := txscript.SignatureScript(tx, 0, p.payScript,
txscript.SigHashAll, p.signKey, true)
if err != nil {
return nil, err
}
tx.TxIn[0].SignatureScript = sigScript
txChain = append(txChain, btcutil.NewTx(tx))
// Next transaction uses outputs from this one.
prevOutPoint = wire.OutPoint{Hash: tx.TxHash(), Index: 0}
}
return txChain, nil
}
// newPoolHarness returns a new instance of a pool harness initialized with a
// fake chain and a TxPool bound to it that is configured with a policy suitable
// for testing. Also, the fake chain is populated with the returned spendable
// outputs so the caller can easily create new valid transactions which build
// off of it.
func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutput, error) {
// Use a hard coded key pair for deterministic results.
keyBytes, err := hex.DecodeString("700868df1838811ffbdf918fb482c1f7e" +
"ad62db4b97bd7012c23e726485e577d")
if err != nil {
return nil, nil, err
}
signKey, signPub := btcec.PrivKeyFromBytes(btcec.S256(), keyBytes)
// Generate associated pay-to-script-hash address and resulting payment
// script.
pubKeyBytes := signPub.SerializeCompressed()
payPubKeyAddr, err := btcutil.NewAddressPubKey(pubKeyBytes, chainParams)
if err != nil {
return nil, nil, err
}
payAddr := payPubKeyAddr.AddressPubKeyHash()
pkScript, err := txscript.PayToAddrScript(payAddr)
if err != nil {
return nil, nil, err
}
// Create a new fake chain and harness bound to it.
chain := &fakeChain{utxos: blockchain.NewUtxoViewpoint()}
harness := poolHarness{
signKey: signKey,
payAddr: payAddr,
payScript: pkScript,
chainParams: chainParams,
chain: chain,
txPool: New(&Config{
Policy: Policy{
DisableRelayPriority: true,
FreeTxRelayLimit: 15.0,
MaxOrphanTxs: 5,
MaxOrphanTxSize: 1000,
MaxSigOpsPerTx: blockchain.MaxSigOpsPerBlock / 5,
MinRelayTxFee: 1000, // 1 Satoshi per byte
MaxTxVersion: 1,
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
},
ChainParams: chainParams,
FetchUtxoView: chain.FetchUtxoView,
BestHeight: chain.BestHeight,
MedianTimePast: chain.MedianTimePast,
CalcSequenceLock: chain.CalcSequenceLock,
SigCache: nil,
AddrIndex: nil,
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
}),
}
// Create a single coinbase transaction and add it to the harness
// chain's utxo set and set the harness chain height such that the
// coinbase will mature in the next block. This ensures the txpool
// accepts transactions which spend immature coinbases that will become
// mature in the next block.
numOutputs := uint32(1)
outputs := make([]spendableOutput, 0, numOutputs)
curHeight := harness.chain.BestHeight()
coinbase, err := harness.CreateCoinbaseTx(curHeight+1, numOutputs)
if err != nil {
return nil, nil, err
}
harness.chain.utxos.AddTxOuts(coinbase, curHeight+1)
for i := uint32(0); i < numOutputs; i++ {
outputs = append(outputs, txOutToSpendableOut(coinbase, i))
}
harness.chain.SetHeight(int32(chainParams.CoinbaseMaturity) + curHeight)
harness.chain.SetMedianTimePast(time.Now())
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
return &harness, outputs, nil
}
// testContext houses a test-related state that is useful to pass to helper
// functions as a single argument.
type testContext struct {
t *testing.T
harness *poolHarness
}
// testPoolMembership tests the transaction pool associated with the provided
// test context to determine if the passed transaction matches the provided
// 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 *btcutil.Tx, inOrphanPool, inTxPool bool) {
txHash := tx.Hash()
gotOrphanPool := tc.harness.txPool.IsOrphanInPool(txHash)
if inOrphanPool != gotOrphanPool {
_, file, line, _ := runtime.Caller(1)
tc.t.Fatalf("%s:%d -- IsOrphanInPool: want %v, got %v", file,
line, inOrphanPool, gotOrphanPool)
}
gotTxPool := tc.harness.txPool.IsTransactionInPool(txHash)
if inTxPool != gotTxPool {
_, file, line, _ := runtime.Caller(1)
tc.t.Fatalf("%s:%d -- IsTransactionInPool: want %v, got %v",
file, line, inTxPool, gotTxPool)
}
gotHaveTx := tc.harness.txPool.HaveTransaction(txHash)
wantHaveTx := inOrphanPool || inTxPool
if wantHaveTx != gotHaveTx {
_, file, line, _ := runtime.Caller(1)
tc.t.Fatalf("%s:%d -- HaveTransaction: want %v, got %v", file,
line, wantHaveTx, gotHaveTx)
}
}
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
// TestSimpleOrphanChain ensures that a simple chain of orphans is handled
// properly. In particular, it generates a chain of single input, single output
// transactions and inserts them while skipping the first linking transaction so
// they are all orphans. Finally, it adds the linking transaction and ensures
// the entire orphan chain is moved to the transaction pool.
func TestSimpleOrphanChain(t *testing.T) {
t.Parallel()
harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
tc := &testContext{t, harness}
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
// Create a chain of transactions rooted with the first spendable output
// provided by the harness.
maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
if err != nil {
t.Fatalf("unable to create transaction chain: %v", err)
}
// Ensure the orphans are accepted (only up to the maximum allowed so
// none are evicted).
for _, tx := range chainedTxns[1 : maxOrphans+1] {
acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
false, 0)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"orphan %v", err)
}
// Ensure no transactions were reported as accepted.
if len(acceptedTxns) != 0 {
t.Fatalf("ProcessTransaction: reported %d accepted "+
"transactions from what should be an orphan",
len(acceptedTxns))
}
// Ensure the transaction is in the orphan pool, is not in the
// transaction pool, and is reported as available.
testPoolMembership(tc, tx, true, false)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
}
// Add the transaction which completes the orphan chain and ensure they
// all get accepted. Notice the accept orphans flag is also false here
// to ensure it has no bearing on whether or not already existing
// orphans in the pool are linked.
acceptedTxns, err := harness.txPool.ProcessTransaction(chainedTxns[0],
false, false, 0)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"orphan %v", err)
}
if len(acceptedTxns) != len(chainedTxns) {
t.Fatalf("ProcessTransaction: reported accepted transactions "+
"length does not match expected -- got %d, want %d",
len(acceptedTxns), len(chainedTxns))
}
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)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
}
}
// TestOrphanReject ensures that orphans are properly rejected when the allow
// orphans flag is not set on ProcessTransaction.
func TestOrphanReject(t *testing.T) {
t.Parallel()
harness, outputs, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
tc := &testContext{t, harness}
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
// Create a chain of transactions rooted with the first spendable output
// provided by the harness.
maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
chainedTxns, err := harness.CreateTxChain(outputs[0], maxOrphans+1)
if err != nil {
t.Fatalf("unable to create transaction chain: %v", err)
}
// Ensure orphans are rejected when the allow orphans flag is not set.
for _, tx := range chainedTxns[1:] {
acceptedTxns, err := harness.txPool.ProcessTransaction(tx, false,
false, 0)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
if err == nil {
t.Fatalf("ProcessTransaction: did not fail on orphan "+
"%v when allow orphans flag is false", tx.Hash())
}
expectedErr := RuleError{}
if reflect.TypeOf(err) != reflect.TypeOf(expectedErr) {
t.Fatalf("ProcessTransaction: wrong error got: <%T> %v, "+
"want: <%T>", err, err, expectedErr)
}
code, extracted := extractRejectCode(err)
if !extracted {
t.Fatalf("ProcessTransaction: failed to extract reject "+
"code from error %q", err)
}
if code != wire.RejectDuplicate {
t.Fatalf("ProcessTransaction: unexpected reject code "+
"-- got %v, want %v", code, wire.RejectDuplicate)
}
// Ensure no transactions were reported as accepted.
if len(acceptedTxns) != 0 {
t.Fatal("ProcessTransaction: reported %d accepted "+
"transactions from failed orphan attempt",
len(acceptedTxns))
}
// Ensure the transaction is not in the orphan pool, not in the
// transaction pool, and not reported as available
testPoolMembership(tc, tx, false, false)
mempool: Add basic test harness infrastructure. This adds a basic test harness infrastructure for the mempool package which aims to make writing tests for it much easier. The harness provides functionality for creating and signing transactions as well as a fake chain that provides utxos for use in generating valid transactions and allows an arbitrary chain height to be set. In order to simplify transaction creation, a single signing key and payment address is used throughout and a convenience type for spendable outputs is provided. The harness is initialized with a spendable coinbase output by default and the fake chain height set to the maturity height needed to ensure the provided output is in fact spendable as well as a policy that is suitable for testing. Since tests are in the same package and each harness provides a unique pool and fake chain instance, the tests can safely reach into the pool policy, or any other state, and change it for a given harness without affecting the others. In order to be able to make use of the existing blockchain.Viewpoint type, a Clone method has been to the UtxoEntry type which allows the fake chain instance to keep a single view with the actual available unspent utxos while the mempool ends up fetching a subset of the view with the specifically requested entries cloned. To demo the harness, this also contains a couple of tests which make use of it: - TestSimpleOrphanChain -- Ensures an entire chain of orphans is properly accepted and connects up when the missing parent transaction is added - TestOrphanRejects -- Ensure orphans are actually rejected when the flag on ProcessTransactions is set to reject them
2016-08-23 05:53:22 +02:00
}
}
// TestOrphanEviction ensures that exceeding the maximum number of orphans
// evicts entries to make room for the new ones.
func TestOrphanEviction(t *testing.T) {
t.Parallel()
harness, outputs, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
tc := &testContext{t, harness}
// Create a chain of transactions rooted with the first spendable output
// provided by the harness that is long enough to be able to force
// several orphan evictions.
maxOrphans := uint32(harness.txPool.cfg.Policy.MaxOrphanTxs)
chainedTxns, err := harness.CreateTxChain(outputs[0], maxOrphans+5)
if err != nil {
t.Fatalf("unable to create transaction chain: %v", err)
}
// Add enough orphans to exceed the max allowed while ensuring they are
// all accepted. This will cause an eviction.
for _, tx := range chainedTxns[1:] {
acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
false, 0)
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"orphan %v", err)
}
// Ensure no transactions were reported as accepted.
if len(acceptedTxns) != 0 {
t.Fatalf("ProcessTransaction: reported %d accepted "+
"transactions from what should be an orphan",
len(acceptedTxns))
}
// Ensure the transaction is in the orphan pool, is not in the
// transaction pool, and is reported as available.
testPoolMembership(tc, tx, true, false)
}
// Figure out which transactions were evicted and make sure the number
// evicted matches the expected number.
var evictedTxns []*btcutil.Tx
for _, tx := range chainedTxns[1:] {
if !harness.txPool.IsOrphanInPool(tx.Hash()) {
evictedTxns = append(evictedTxns, tx)
}
}
expectedEvictions := len(chainedTxns) - 1 - int(maxOrphans)
if len(evictedTxns) != expectedEvictions {
t.Fatalf("unexpected number of evictions -- got %d, want %d",
len(evictedTxns), expectedEvictions)
}
// Ensure none of the evicted transactions ended up in the transaction
// pool.
for _, tx := range evictedTxns {
testPoolMembership(tc, tx, false, false)
}
}
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
// TestBasicOrphanRemoval ensure that orphan removal works as expected when an
// orphan that doesn't exist is removed both when there is another orphan that
// redeems it and when there is not.
func TestBasicOrphanRemoval(t *testing.T) {
t.Parallel()
const maxOrphans = 4
harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
tc := &testContext{t, harness}
// Create a chain of transactions rooted with the first spendable output
// provided by the harness.
chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
if err != nil {
t.Fatalf("unable to create transaction chain: %v", err)
}
// Ensure the orphans are accepted (only up to the maximum allowed so
// none are evicted).
for _, tx := range chainedTxns[1 : maxOrphans+1] {
acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
false, 0)
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"orphan %v", err)
}
// Ensure no transactions were reported as accepted.
if len(acceptedTxns) != 0 {
t.Fatalf("ProcessTransaction: reported %d accepted "+
"transactions from what should be an orphan",
len(acceptedTxns))
}
// Ensure the transaction is in the orphan pool, not in the
// transaction pool, and reported as available.
testPoolMembership(tc, tx, true, false)
}
// Attempt to remove an orphan that has no redeemers and is not present,
// and ensure the state of all other orphans are unaffected.
nonChainedOrphanTx, err := harness.CreateSignedTx([]spendableOutput{{
amount: btcutil.Amount(5000000000),
outPoint: wire.OutPoint{Hash: chainhash.Hash{}, Index: 0},
}}, 1)
if err != nil {
t.Fatalf("unable to create signed tx: %v", err)
}
harness.txPool.RemoveOrphan(nonChainedOrphanTx)
testPoolMembership(tc, nonChainedOrphanTx, false, false)
for _, tx := range chainedTxns[1 : maxOrphans+1] {
testPoolMembership(tc, tx, true, 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)
for _, tx := range chainedTxns[1 : maxOrphans+1] {
testPoolMembership(tc, tx, true, 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)
}
}
// TestOrphanChainRemoval ensure that orphan chains (orphans that spend outputs
// from other orphans) are removed as expected.
func TestOrphanChainRemoval(t *testing.T) {
t.Parallel()
const maxOrphans = 10
harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
tc := &testContext{t, harness}
// Create a chain of transactions rooted with the first spendable output
// provided by the harness.
chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
if err != nil {
t.Fatalf("unable to create transaction chain: %v", err)
}
// Ensure the orphans are accepted (only up to the maximum allowed so
// none are evicted).
for _, tx := range chainedTxns[1 : maxOrphans+1] {
acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
false, 0)
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"orphan %v", err)
}
// Ensure no transactions were reported as accepted.
if len(acceptedTxns) != 0 {
t.Fatalf("ProcessTransaction: reported %d accepted "+
"transactions from what should be an orphan",
len(acceptedTxns))
}
// Ensure the transaction is in the orphan pool, not in the
// transaction pool, and reported as available.
testPoolMembership(tc, tx, true, false)
}
// Remove the first orphan that starts the orphan chain without the
// remove redeemer flag set and ensure that only the first orphan was
// removed.
harness.txPool.mtx.Lock()
harness.txPool.removeOrphan(chainedTxns[1], false)
harness.txPool.mtx.Unlock()
testPoolMembership(tc, chainedTxns[1], false, false)
for _, tx := range chainedTxns[2 : maxOrphans+1] {
testPoolMembership(tc, tx, true, false)
}
// Remove the first remaining orphan that starts the orphan chain with
// the remove redeemer flag set and ensure they are all removed.
harness.txPool.mtx.Lock()
harness.txPool.removeOrphan(chainedTxns[2], true)
harness.txPool.mtx.Unlock()
for _, tx := range chainedTxns[2 : maxOrphans+1] {
testPoolMembership(tc, tx, false, false)
}
}
// TestMultiInputOrphanDoubleSpend ensures that orphans that spend from an
// output that is spend by another transaction entering the pool are removed.
func TestMultiInputOrphanDoubleSpend(t *testing.T) {
t.Parallel()
const maxOrphans = 4
harness, outputs, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans
tc := &testContext{t, harness}
// Create a chain of transactions rooted with the first spendable output
// provided by the harness.
chainedTxns, err := harness.CreateTxChain(outputs[0], maxOrphans+1)
if err != nil {
t.Fatalf("unable to create transaction chain: %v", err)
}
// Start by adding the orphan transactions from the generated chain
// except the final one.
for _, tx := range chainedTxns[1:maxOrphans] {
acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
false, 0)
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"orphan %v", err)
}
if len(acceptedTxns) != 0 {
t.Fatalf("ProcessTransaction: reported %d accepted transactions "+
"from what should be an orphan", len(acceptedTxns))
}
testPoolMembership(tc, tx, true, false)
}
// Ensure a transaction that contains a double spend of the same output
// as the second orphan that was just added as well as a valid spend
// from that last orphan in the chain generated above (and is not in the
// orphan pool) is accepted to the orphan pool. This must be allowed
// since it would otherwise be possible for a malicious actor to disrupt
// tx chains.
doubleSpendTx, err := harness.CreateSignedTx([]spendableOutput{
txOutToSpendableOut(chainedTxns[1], 0),
txOutToSpendableOut(chainedTxns[maxOrphans], 0),
}, 1)
if err != nil {
t.Fatalf("unable to create signed tx: %v", err)
}
acceptedTxns, err := harness.txPool.ProcessTransaction(doubleSpendTx,
true, false, 0)
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid orphan %v",
err)
}
if len(acceptedTxns) != 0 {
t.Fatalf("ProcessTransaction: reported %d accepted transactions "+
"from what should be an orphan", len(acceptedTxns))
}
testPoolMembership(tc, doubleSpendTx, true, false)
// Add the transaction which completes the orphan chain and ensure the
// chain gets accepted. Notice the accept orphans flag is also false
// here to ensure it has no bearing on whether or not already existing
// orphans in the pool are linked.
//
// This will cause the shared output to become a concrete spend which
// will in turn must cause the double spending orphan to be removed.
acceptedTxns, err = harness.txPool.ProcessTransaction(chainedTxns[0],
false, false, 0)
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid tx %v", err)
}
if len(acceptedTxns) != maxOrphans {
t.Fatalf("ProcessTransaction: reported accepted transactions "+
"length does not match expected -- got %d, want %d",
len(acceptedTxns), maxOrphans)
}
for _, txD := range acceptedTxns {
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
// 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)
mempool: Stricter orphan evaluation and eviction. This modifies the way orphan removal and processing is done to more aggressively remove orphans that can no longer be valid due to other transactions being added or removed from the primary transaction pool. The net effect of these changes is that orphan pool will typically be much smaller which greatly improves its effectiveness. Previously, it would typically quickly reach the max allowed worst-case usage and effectively stay there forever. The following is a summary of the changes: - Modify the map that tracks which orphans redeem a given transaction to instead track by the specific outpoints that are redeemed - Modify the various orphan removal and processing functions to accept the full transaction rather than just its hash - Introduce a new flag on removeOrphans which specifies whether or not to remove the transactions that redeem the orphan being removed as well which is necessary since only some paths require it - Add a new function named removeOrphanDoubleSpends that is invoked whenever a transaction is added to the main pool and thus the outputs they spent become concrete spends - Introduce a new flag on maybeAcceptTransaction which specifies whether or not duplicate orphans should be rejected since only some paths require it - Modify processOrphans as follows: - Make use of the modified map - Use newly available flags and logic work more strictly work with tx chains - Recursively remove any orphans that also redeem any outputs redeemed by the accepted transactions - Several new tests to ensure proper functionality - Removing an orphan that doesn't exist is removed both when there is another orphan that redeems it and when there is not - Removing orphans works properly with orphan chains per the new remove redeemers flag - Removal of multi-input orphans that double spend an output when a concrete redeemer enters the transaction pool
2016-08-23 19:26:26 +02:00
}
// 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)
}