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
}
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.
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
}