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}
	chainedTxns, err := harness.CreateTxChain(spendableOuts[0], maxOrphans+1)
	if err != nil {
		t.Fatalf("unable to create transaction chain: %v", err)
	}
	for _, tx := range chainedTxns[1 : maxOrphans+1] {
		acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
			false, 0)
		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)
	}
	nonChainedOrphanTx, err := harness.CreateSignedTx([]spendableOutput{{
		amount:   provautil.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)
	}
	harness.txPool.RemoveOrphan(chainedTxns[0])
	testPoolMembership(tc, chainedTxns[0], false, false)
	for _, tx := range chainedTxns[1 : maxOrphans+1] {
		testPoolMembership(tc, tx, true, false)
	}
	for _, tx := range chainedTxns[1 : maxOrphans+1] {
		harness.txPool.RemoveOrphan(tx)
		testPoolMembership(tc, tx, false, false)
	}
}
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.
package mempool
import (
	"encoding/hex"
	"github.com/bitgo/prova/blockchain"
	"github.com/bitgo/prova/btcec"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/chaincfg/chainhash"
	"github.com/bitgo/prova/provautil"
	"github.com/bitgo/prova/txscript"
	"github.com/bitgo/prova/wire"
	"reflect"
	"runtime"
	"sync"
	"testing"
	"time"
)
type fakeChain struct {
	sync.RWMutex
	utxos          *blockchain.UtxoViewpoint
	currentHeight  uint32
	medianTimePast time.Time
}
func (s *fakeChain) FetchUtxoView(tx *provautil.Tx) (*blockchain.UtxoViewpoint, error) {
	s.RLock()
	defer s.RUnlock()
	viewpoint := blockchain.NewUtxoViewpoint()
	entry := s.utxos.LookupEntry(tx.Hash())
	viewpoint.Entries()[*tx.Hash()] = entry.Clone()
	for _, txIn := range tx.MsgTx().TxIn {
		originHash := &txIn.PreviousOutPoint.Hash
		entry := s.utxos.LookupEntry(originHash)
		viewpoint.Entries()[*originHash] = entry.Clone()
	}
	return viewpoint, nil
}
func hexToBytes(s string) []byte {
	b, err := hex.DecodeString(s)
	if err != nil {
		panic("invalid hex in source file: " + s)
	}
	return b
}
func (s *fakeChain) ThreadTips() map[provautil.ThreadID]*wire.OutPoint {
	return make(map[provautil.ThreadID]*wire.OutPoint)
}
func (s *fakeChain) LastKeyID() btcec.KeyID {
	return btcec.KeyID(0)
}
func (s *fakeChain) TotalSupply() uint64 {
	return uint64(0)
}
func (s *fakeChain) AdminKeySets() map[btcec.KeySetType]btcec.PublicKeySet {
	return make(map[btcec.KeySetType]btcec.PublicKeySet)
}
func (s *fakeChain) KeyIDs() btcec.KeyIdMap {
	keyId1 := btcec.KeyIDFromAddressBuffer([]byte{0, 0, 1, 0})
	pubKey1, _ := btcec.ParsePubKey(hexToBytes("025ceeba2ab4a635df2c0301a3d773da06ac5a18a7c3e0d09a795d7e57d233edf1"), btcec.S256())
	keyId2 := btcec.KeyIDFromAddressBuffer([]byte{1, 0, 0, 0})
	pubKey2, _ := btcec.ParsePubKey(hexToBytes("038ef4a121bcaf1b1f175557a12896f8bc93b095e84817f90e9a901cd2113a8202"), btcec.S256())
	return map[btcec.KeyID]*btcec.PublicKey{keyId1: pubKey1, keyId2: pubKey2}
}
func (s *fakeChain) BestHeight() uint32 {
	s.RLock()
	height := s.currentHeight
	s.RUnlock()
	return height
}
func (s *fakeChain) SetHeight(height uint32) {
	s.Lock()
	s.currentHeight = height
	s.Unlock()
}
func (s *fakeChain) MedianTimePast() time.Time {
	s.RLock()
	mtp := s.medianTimePast
	s.RUnlock()
	return mtp
}
func (s *fakeChain) SetMedianTimePast(mtp time.Time) {
	s.Lock()
	s.medianTimePast = mtp
	s.Unlock()
}
func (s *fakeChain) CalcSequenceLock(tx *provautil.Tx,
	view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
	return &blockchain.SequenceLock{
		Seconds:     -1,
		BlockHeight: -1,
	}, nil
}
type spendableOutput struct {
	outPoint wire.OutPoint
	amount   provautil.Amount
}
func txOutToSpendableOut(tx *provautil.Tx, outputNum uint32) spendableOutput {
	return spendableOutput{
		outPoint: wire.OutPoint{Hash: *tx.Hash(), Index: outputNum},
		amount:   provautil.Amount(tx.MsgTx().TxOut[outputNum].Value),
	}
}
type poolHarness struct {
	privKey1    *btcec.PrivateKey
	privKey2    *btcec.PrivateKey
	payAddr     provautil.Address
	payScript   []byte
	chainParams *chaincfg.Params
	chain  *fakeChain
	txPool *TxPool
}
func (p *poolHarness) CreateCoinbaseTx(blockHeight uint32, numOutputs uint32) (*provautil.Tx, error) {
	coinbaseScript, err := txscript.NewScriptBuilder().
		AddInt64(int64(blockHeight)).Script()
	if err != nil {
		return nil, err
	}
	tx := wire.NewMsgTx(wire.TxVersion)
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
			wire.MaxPrevOutIndex),
		SignatureScript: coinbaseScript,
		Sequence:        wire.MaxTxInSequenceNum,
	})
	totalInput := int64(558)  
	amountPerOutput := totalInput / int64(numOutputs)
	remainder := totalInput - amountPerOutput*int64(numOutputs)
	for i := uint32(0); i < numOutputs; i++ {
		amount := amountPerOutput
		if i == numOutputs-1 {
			amount = amountPerOutput + remainder
		}
		tx.AddTxOut(&wire.TxOut{
			PkScript: p.payScript,
			Value:    amount,
		})
	}
	return provautil.NewTx(tx), nil
}
func (p *poolHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint32) (*provautil.Tx, error) {
	var totalInput provautil.Amount
	for _, input := range inputs {
		totalInput += input.amount
	}
	amountPerOutput := int64(totalInput) / int64(numOutputs)
	remainder := int64(totalInput) - amountPerOutput*int64(numOutputs)
	tx := wire.NewMsgTx(wire.TxVersion)
	for _, input := range inputs {
		tx.AddTxIn(&wire.TxIn{
			PreviousOutPoint: input.outPoint,
			SignatureScript:  nil,
			Sequence:         wire.MaxTxInSequenceNum,
		})
	}
	for i := uint32(0); i < numOutputs; i++ {
		amount := amountPerOutput
		if i == numOutputs-1 {
			amount = amountPerOutput + remainder
		}
		tx.AddTxOut(&wire.TxOut{
			PkScript: p.payScript,
			Value:    amount,
		})
	}
	lookupKey := func(a provautil.Address) ([]txscript.PrivateKey, error) {
		return []txscript.PrivateKey{
			txscript.PrivateKey{p.privKey1, true},
			txscript.PrivateKey{p.privKey2, true},
		}, nil
	}
	for i := range tx.TxIn {
		sigScript, err := txscript.SignTxOutput(p.chainParams, tx,
			i, int64(inputs[i].amount), p.payScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil)
		if err != nil {
			return nil, err
		}
		tx.TxIn[i].SignatureScript = sigScript
	}
	return provautil.NewTx(tx), nil
}
func (p *poolHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint32) ([]*provautil.Tx, error) {
	txChain := make([]*provautil.Tx, 0, numTxns)
	prevOutPoint := firstOutput.outPoint
	spendableAmount := firstOutput.amount
	lookupKey := func(a provautil.Address) ([]txscript.PrivateKey, error) {
		return []txscript.PrivateKey{
			txscript.PrivateKey{p.privKey1, true},
			txscript.PrivateKey{p.privKey2, true},
		}, nil
	}
	for i := uint32(0); i < numTxns; i++ {
		tx := wire.NewMsgTx(wire.TxVersion)
		tx.AddTxIn(&wire.TxIn{
			PreviousOutPoint: prevOutPoint,
			SignatureScript:  nil,
			Sequence:         wire.MaxTxInSequenceNum,
		})
		tx.AddTxOut(&wire.TxOut{
			PkScript: p.payScript,
			Value:    int64(spendableAmount),
		})
		sigScript, err := txscript.SignTxOutput(p.chainParams, tx,
			0, tx.TxOut[0].Value, p.payScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil)
		if err != nil {
			return nil, err
		}
		tx.TxIn[0].SignatureScript = sigScript
		txChain = append(txChain, provautil.NewTx(tx))
		prevOutPoint = wire.OutPoint{Hash: tx.TxHash(), Index: 0}
	}
	return txChain, nil
}
func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutput, error) {
	privKey1, pubKey1 := btcec.PrivKeyFromBytes(btcec.S256(), []byte{
		0x2b, 0x8c, 0x52, 0xb7, 0x7b, 0x32, 0x7c, 0x75,
		0x5b, 0x9b, 0x37, 0x55, 0x00, 0xd3, 0xf4, 0xb2,
		0xda, 0x9b, 0x0a, 0x1f, 0xf6, 0x5f, 0x68, 0x91,
		0xd3, 0x11, 0xfe, 0x94, 0x29, 0x5b, 0xc2, 0x6a,
	})
	privKey2, _ := btcec.PrivKeyFromBytes(btcec.S256(), []byte{
		0xea, 0xf0, 0x2c, 0xa3, 0x48, 0xc5, 0x24, 0xe6,
		0x39, 0x26, 0x55, 0xba, 0x4d, 0x29, 0x60, 0x3c,
		0xd1, 0xa7, 0x34, 0x7d, 0x9d, 0x65, 0xcf, 0xe9,
		0x3c, 0xe1, 0xeb, 0xff, 0xdc, 0xa2, 0x26, 0x94,
	})
	pkHash := provautil.Hash160(pubKey1.SerializeCompressed())
	keyId1 := btcec.KeyIDFromAddressBuffer([]byte{1, 0, 0, 0})
	keyId2 := btcec.KeyIDFromAddressBuffer([]byte{0, 0, 1, 0})
	payAddr, err := provautil.NewAddressProva(pkHash, []btcec.KeyID{keyId1, keyId2}, chainParams)
	if err != nil {
		return nil, nil, err
	}
	pkScript, err := txscript.PayToAddrScript(payAddr)
	if err != nil {
		return nil, nil, err
	}
	chain := &fakeChain{utxos: blockchain.NewUtxoViewpoint()}
	harness := poolHarness{
		privKey1:    privKey1,
		privKey2:    privKey2,
		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,  
				MaxTxVersion:         1,
			},
			ChainParams:      chainParams,
			FetchUtxoView:    chain.FetchUtxoView,
			ThreadTips:       chain.ThreadTips,
			LastKeyID:        chain.LastKeyID,
			TotalSupply:      chain.TotalSupply,
			GetKeyIDs:        chain.KeyIDs,
			GetAdminKeySets:  chain.AdminKeySets,
			BestHeight:       chain.BestHeight,
			MedianTimePast:   chain.MedianTimePast,
			CalcSequenceLock: chain.CalcSequenceLock,
			SigCache:         nil,
			HashCache:        txscript.NewHashCache(200),
			TimeSource:       blockchain.NewMedianTime(),
			AddrIndex:        nil,
		}),
	}
	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(uint32(chainParams.CoinbaseMaturity) + curHeight)
	harness.chain.SetMedianTimePast(time.Now())
	return &harness, outputs, nil
}
type testContext struct {
	t       *testing.T
	harness *poolHarness
}
func testPoolMembership(tc *testContext, tx *provautil.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)
	}
}
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}
	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)
	}
	for _, tx := range chainedTxns[1 : maxOrphans+1] {
		acceptedTxns, err := harness.txPool.ProcessTransaction(tx, true,
			false, 0)
		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)
	}
	acceptedTxns, err := harness.txPool.ProcessTransaction(chainedTxns[0],
		false, false, 0)
	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 {
		testPoolMembership(tc, txD.Tx, false, true)
	}
}
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}
	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)
	}
	for _, tx := range chainedTxns[1:] {
		acceptedTxns, err := harness.txPool.ProcessTransaction(tx, false,
			false, 0)
		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)
		}
		if len(acceptedTxns) != 0 {
			t.Fatal("ProcessTransaction: reported %d accepted "+
				"transactions from failed orphan attempt",
				len(acceptedTxns))
		}
		testPoolMembership(tc, tx, false, false)
	}
}
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}
	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)
	}
	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)
		}
		if len(acceptedTxns) != 0 {
			t.Fatalf("ProcessTransaction: reported %d accepted "+
				"transactions from what should be an orphan",
				len(acceptedTxns))
		}
		testPoolMembership(tc, tx, true, false)
	}
	var evictedTxns []*provautil.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)
	}
	for _, tx := range evictedTxns {
		testPoolMembership(tc, tx, false, false)
	}
}