func (mp *TxPool) maybeAcceptTransaction(tx *provautil.Tx, isNew, rateLimit bool, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) {
	txHash := tx.Hash()
	if mp.isTransactionInPool(txHash) || (rejectDupOrphans &&
		mp.isOrphanInPool(txHash)) {
		str := fmt.Sprintf("already have transaction %v", txHash)
		return nil, nil, txRuleError(wire.RejectDuplicate, str)
	}
	err := blockchain.CheckTransactionSanity(tx)
	if err != nil {
		if cerr, ok := err.(blockchain.RuleError); ok {
			return nil, nil, chainRuleError(cerr)
		}
		return nil, nil, err
	}
	if blockchain.IsCoinBase(tx) {
		str := fmt.Sprintf("transaction %v is an individual coinbase",
			txHash)
		return nil, nil, txRuleError(wire.RejectInvalid, str)
	}
	if tx.MsgTx().LockTime > math.MaxInt32 {
		str := fmt.Sprintf("transaction %v has a lock time after "+
			"2038 which is not accepted yet", txHash)
		return nil, nil, txRuleError(wire.RejectNonstandard, str)
	}
	bestHeight := mp.cfg.BestHeight()
	nextBlockHeight := bestHeight + 1
	medianTimePast := mp.cfg.MedianTimePast()
	if !mp.cfg.Policy.AcceptNonStd {
		err = checkTransactionStandard(tx, nextBlockHeight,
			medianTimePast, mp.cfg.Policy.MinRelayTxFee,
			mp.cfg.Policy.MaxTxVersion)
		if err != nil {
			rejectCode, found := extractRejectCode(err)
			if !found {
				rejectCode = wire.RejectNonstandard
			}
			str := fmt.Sprintf("transaction %v is not standard: %v",
				txHash, err)
			return nil, nil, txRuleError(rejectCode, str)
		}
	}
	err = mp.checkPoolDoubleSpend(tx)
	if err != nil {
		return nil, nil, err
	}
	utxoView, err := mp.fetchInputUtxos(tx)
	if err != nil {
		if cerr, ok := err.(blockchain.RuleError); ok {
			return nil, nil, chainRuleError(cerr)
		}
		return nil, nil, err
	}
	keyView := blockchain.NewKeyViewpoint()
	keyView.SetThreadTips(mp.cfg.ThreadTips())
	keyView.SetTotalSupply(mp.cfg.TotalSupply())
	keyView.SetLastKeyID(mp.cfg.LastKeyID())
	keyView.SetKeyIDs(mp.cfg.GetKeyIDs())
	keyView.SetKeys(mp.cfg.GetAdminKeySets())
	txEntry := utxoView.LookupEntry(txHash)
	if txEntry != nil && !txEntry.IsFullySpent() {
		return nil, nil, txRuleError(wire.RejectDuplicate,
			"transaction already exists")
	}
	delete(utxoView.Entries(), *txHash)
	var missingParents []*chainhash.Hash
	for originHash, entry := range utxoView.Entries() {
		if entry == nil || entry.IsFullySpent() {
			hashCopy := originHash
			missingParents = append(missingParents, &hashCopy)
		}
	}
	if len(missingParents) > 0 {
		return missingParents, nil, nil
	}
	sequenceLock, err := mp.cfg.CalcSequenceLock(tx, utxoView)
	if err != nil {
		if cerr, ok := err.(blockchain.RuleError); ok {
			return nil, nil, chainRuleError(cerr)
		}
		return nil, nil, err
	}
	if !blockchain.SequenceLockActive(sequenceLock, int32(nextBlockHeight),
		medianTimePast) {
		return nil, nil, txRuleError(wire.RejectNonstandard,
			"transaction's sequence locks on inputs not met")
	}
	txFee, err := blockchain.CheckTransactionInputs(tx, nextBlockHeight,
		utxoView, mp.cfg.ChainParams)
	if err != nil {
		if cerr, ok := err.(blockchain.RuleError); ok {
			return nil, nil, chainRuleError(cerr)
		}
		return nil, nil, err
	}
	err = blockchain.CheckTransactionOutputs(tx, keyView, mp.cfg.ChainParams)
	if err != nil {
		return nil, nil, err
	}
	if !mp.cfg.Policy.AcceptNonStd {
		err := checkInputsStandard(tx, utxoView)
		if err != nil {
			rejectCode, found := extractRejectCode(err)
			if !found {
				rejectCode = wire.RejectNonstandard
			}
			str := fmt.Sprintf("transaction %v has a non-standard "+
				"input: %v", txHash, err)
			return nil, nil, txRuleError(rejectCode, str)
		}
	}
	numSigOps, err := blockchain.CountP2SHSigOps(tx, false, utxoView)
	if err != nil {
		if cerr, ok := err.(blockchain.RuleError); ok {
			return nil, nil, chainRuleError(cerr)
		}
		return nil, nil, err
	}
	numSigOps += blockchain.CountSigOps(tx)
	if numSigOps > mp.cfg.Policy.MaxSigOpsPerTx {
		str := fmt.Sprintf("transaction %v has too many sigops: %d > %d",
			txHash, numSigOps, mp.cfg.Policy.MaxSigOpsPerTx)
		return nil, nil, txRuleError(wire.RejectNonstandard, str)
	}
	serializedSize := int64(tx.MsgTx().SerializeSize())
	minFee := calcMinRequiredTxRelayFee(serializedSize,
		mp.cfg.Policy.MinRelayTxFee)
	if serializedSize >= (DefaultBlockPrioritySize-1000) && txFee < minFee {
		str := fmt.Sprintf("transaction %v has %d fees which is under "+
			"the required amount of %d", txHash, txFee,
			minFee)
		return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
	}
	if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee {
		currentPriority := mining.CalcPriority(tx.MsgTx(), utxoView,
			nextBlockHeight)
		if currentPriority <= mining.MinHighPriority {
			str := fmt.Sprintf("transaction %v has insufficient "+
				"priority (%g <= %g)", txHash,
				currentPriority, mining.MinHighPriority)
			return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
		}
	}
	if rateLimit && txFee < minFee {
		nowUnix := time.Now().Unix()
		mp.pennyTotal *= math.Pow(1.0-1.0/600.0,
			float64(nowUnix-mp.lastPennyUnix))
		mp.lastPennyUnix = nowUnix
		if mp.pennyTotal >= mp.cfg.Policy.FreeTxRelayLimit*10*1000 {
			str := fmt.Sprintf("transaction %v has been rejected "+
				"by the rate limiter due to low fees", txHash)
			return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
		}
		oldTotal := mp.pennyTotal
		mp.pennyTotal += float64(serializedSize)
		log.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
			"limit %v", oldTotal, mp.pennyTotal,
			mp.cfg.Policy.FreeTxRelayLimit*10*1000)
	}
	err = blockchain.ValidateTransactionScripts(tx, utxoView, keyView,
		txscript.StandardVerifyFlags, mp.cfg.SigCache, mp.cfg.HashCache)
	if err != nil {
		if cerr, ok := err.(blockchain.RuleError); ok {
			return nil, nil, chainRuleError(cerr)
		}
		return nil, nil, err
	}
	txD := mp.addTransaction(utxoView, tx, bestHeight, txFee)
	log.Debugf("Accepted transaction %v (pool size: %v)", txHash,
		len(mp.pool))
	return nil, txD, nil
}
maybeAcceptTransaction is the internal function which implements the public
MaybeAcceptTransaction.  See the comment for MaybeAcceptTransaction for
more details.

This function MUST be called with the mempool lock held (for writes).
package mempool
import (
	"container/list"
	"fmt"
	"math"
	"sync"
	"sync/atomic"
	"time"
	"github.com/bitgo/prova/blockchain"
	"github.com/bitgo/prova/blockchain/indexers"
	"github.com/bitgo/prova/btcec"
	"github.com/bitgo/prova/btcjson"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/chaincfg/chainhash"
	"github.com/bitgo/prova/mining"
	"github.com/bitgo/prova/provautil"
	"github.com/bitgo/prova/txscript"
	"github.com/bitgo/prova/wire"
)
const (
	DefaultBlockPrioritySize = 50000
	orphanTTL = time.Minute * 15
	orphanExpireScanInterval = time.Minute * 5
)
type Tag uint64
type Config struct {
	Policy Policy
	ChainParams *chaincfg.Params
	FetchUtxoView func(*provautil.Tx) (*blockchain.UtxoViewpoint, error)
	ThreadTips func() map[provautil.ThreadID]*wire.OutPoint
	LastKeyID func() btcec.KeyID
	TotalSupply func() uint64
	GetKeyIDs func() btcec.KeyIdMap
	GetAdminKeySets func() map[btcec.KeySetType]btcec.PublicKeySet
	BestHeight func() uint32
	MedianTimePast func() time.Time
	CalcSequenceLock func(*provautil.Tx, *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error)
	SigCache *txscript.SigCache
	HashCache *txscript.HashCache
	TimeSource blockchain.MedianTimeSource
	AddrIndex *indexers.AddrIndex
}
type Policy struct {
	MaxTxVersion int32
	DisableRelayPriority bool
	AcceptNonStd bool
	FreeTxRelayLimit float64
	MaxOrphanTxs int
	MaxOrphanTxSize int
	MaxSigOpsPerTx int
	MinRelayTxFee provautil.Amount
}
type TxDesc struct {
	mining.TxDesc
	StartingPriority float64
}
type orphanTx struct {
	tx         *provautil.Tx
	tag        Tag
	expiration time.Time
}
type TxPool struct {
	lastUpdated int64  
	mtx           sync.RWMutex
	cfg           Config
	pool          map[chainhash.Hash]*TxDesc
	orphans       map[chainhash.Hash]*orphanTx
	orphansByPrev map[wire.OutPoint]map[chainhash.Hash]*provautil.Tx
	outpoints     map[wire.OutPoint]*provautil.Tx
	pennyTotal    float64  
	lastPennyUnix int64    
	nextExpireScan time.Time
}
var _ mining.TxSource = (*TxPool)(nil)
func (mp *TxPool) removeOrphan(tx *provautil.Tx, removeRedeemers bool) {
	txHash := tx.Hash()
	otx, exists := mp.orphans[*txHash]
	if !exists {
		return
	}
	for _, txIn := range otx.tx.MsgTx().TxIn {
		orphans, exists := mp.orphansByPrev[txIn.PreviousOutPoint]
		if exists {
			delete(orphans, *txHash)
			if len(orphans) == 0 {
				delete(mp.orphansByPrev, txIn.PreviousOutPoint)
			}
		}
	}
	if removeRedeemers {
		prevOut := wire.OutPoint{Hash: *txHash}
		for txOutIdx := range tx.MsgTx().TxOut {
			prevOut.Index = uint32(txOutIdx)
			for _, orphan := range mp.orphansByPrev[prevOut] {
				mp.removeOrphan(orphan, true)
			}
		}
	}
	delete(mp.orphans, *txHash)
}
func (mp *TxPool) RemoveOrphan(tx *provautil.Tx) {
	mp.mtx.Lock()
	mp.removeOrphan(tx, false)
	mp.mtx.Unlock()
}
func (mp *TxPool) RemoveOrphansByTag(tag Tag) uint64 {
	var numEvicted uint64
	mp.mtx.Lock()
	for _, otx := range mp.orphans {
		if otx.tag == tag {
			mp.removeOrphan(otx.tx, true)
			numEvicted++
		}
	}
	mp.mtx.Unlock()
	return numEvicted
}
func (mp *TxPool) limitNumOrphans() error {
	if now := time.Now(); now.After(mp.nextExpireScan) {
		origNumOrphans := len(mp.orphans)
		for _, otx := range mp.orphans {
			if now.After(otx.expiration) {
				mp.removeOrphan(otx.tx, true)
			}
		}
		mp.nextExpireScan = now.Add(orphanExpireScanInterval)
		numOrphans := len(mp.orphans)
		if numExpired := origNumOrphans - numOrphans; numExpired > 0 {
			log.Debugf("Expired %d %s (remaining: %d)", numExpired,
				pickNoun(numExpired, "orphan", "orphans"),
				numOrphans)
		}
	}
	if len(mp.orphans)+1 <= mp.cfg.Policy.MaxOrphanTxs {
		return nil
	}
	for _, otx := range mp.orphans {
		mp.removeOrphan(otx.tx, false)
		break
	}
	return nil
}
func (mp *TxPool) addOrphan(tx *provautil.Tx, tag Tag) {
	if mp.cfg.Policy.MaxOrphanTxs <= 0 {
		return
	}
	mp.limitNumOrphans()
	mp.orphans[*tx.Hash()] = &orphanTx{
		tx:         tx,
		tag:        tag,
		expiration: time.Now().Add(orphanTTL),
	}
	for _, txIn := range tx.MsgTx().TxIn {
		if _, exists := mp.orphansByPrev[txIn.PreviousOutPoint]; !exists {
			mp.orphansByPrev[txIn.PreviousOutPoint] =
				make(map[chainhash.Hash]*provautil.Tx)
		}
		mp.orphansByPrev[txIn.PreviousOutPoint][*tx.Hash()] = tx
	}
	log.Debugf("Stored orphan transaction %v (total: %d)", tx.Hash(),
		len(mp.orphans))
}
func (mp *TxPool) maybeAddOrphan(tx *provautil.Tx, tag Tag) error {
	serializedLen := tx.MsgTx().SerializeSize()
	if serializedLen > mp.cfg.Policy.MaxOrphanTxSize {
		str := fmt.Sprintf("orphan transaction size of %d bytes is "+
			"larger than max allowed size of %d bytes",
			serializedLen, mp.cfg.Policy.MaxOrphanTxSize)
		return txRuleError(wire.RejectNonstandard, str)
	}
	mp.addOrphan(tx, tag)
	return nil
}
func (mp *TxPool) removeOrphanDoubleSpends(tx *provautil.Tx) {
	msgTx := tx.MsgTx()
	for _, txIn := range msgTx.TxIn {
		for _, orphan := range mp.orphansByPrev[txIn.PreviousOutPoint] {
			mp.removeOrphan(orphan, true)
		}
	}
}
func (mp *TxPool) isTransactionInPool(hash *chainhash.Hash) bool {
	if _, exists := mp.pool[*hash]; exists {
		return true
	}
	return false
}
func (mp *TxPool) IsTransactionInPool(hash *chainhash.Hash) bool {
	mp.mtx.RLock()
	inPool := mp.isTransactionInPool(hash)
	mp.mtx.RUnlock()
	return inPool
}
func (mp *TxPool) isOrphanInPool(hash *chainhash.Hash) bool {
	if _, exists := mp.orphans[*hash]; exists {
		return true
	}
	return false
}
func (mp *TxPool) IsOrphanInPool(hash *chainhash.Hash) bool {
	mp.mtx.RLock()
	inPool := mp.isOrphanInPool(hash)
	mp.mtx.RUnlock()
	return inPool
}
func (mp *TxPool) haveTransaction(hash *chainhash.Hash) bool {
	return mp.isTransactionInPool(hash) || mp.isOrphanInPool(hash)
}
func (mp *TxPool) HaveTransaction(hash *chainhash.Hash) bool {
	mp.mtx.RLock()
	haveTx := mp.haveTransaction(hash)
	mp.mtx.RUnlock()
	return haveTx
}
func (mp *TxPool) removeTransaction(tx *provautil.Tx, removeRedeemers bool) {
	txHash := tx.Hash()
	if removeRedeemers {
		for i := uint32(0); i < uint32(len(tx.MsgTx().TxOut)); i++ {
			prevOut := wire.OutPoint{Hash: *txHash, Index: i}
			if txRedeemer, exists := mp.outpoints[prevOut]; exists {
				mp.removeTransaction(txRedeemer, true)
			}
		}
	}
	if txDesc, exists := mp.pool[*txHash]; exists {
		if mp.cfg.AddrIndex != nil {
			mp.cfg.AddrIndex.RemoveUnconfirmedTx(txHash)
		}
		for _, txIn := range txDesc.Tx.MsgTx().TxIn {
			delete(mp.outpoints, txIn.PreviousOutPoint)
		}
		delete(mp.pool, *txHash)
		atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix())
	}
}
func (mp *TxPool) RemoveTransaction(tx *provautil.Tx, removeRedeemers bool) {
	mp.mtx.Lock()
	mp.removeTransaction(tx, removeRedeemers)
	mp.mtx.Unlock()
}
func (mp *TxPool) RemoveDoubleSpends(tx *provautil.Tx) {
	mp.mtx.Lock()
	for _, txIn := range tx.MsgTx().TxIn {
		if txRedeemer, ok := mp.outpoints[txIn.PreviousOutPoint]; ok {
			if !txRedeemer.Hash().IsEqual(tx.Hash()) {
				mp.removeTransaction(txRedeemer, true)
			}
		}
	}
	mp.mtx.Unlock()
}
func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *provautil.Tx, height uint32, fee int64) *TxDesc {
	txD := &TxDesc{
		TxDesc: mining.TxDesc{
			Tx:       tx,
			Added:    time.Now(),
			Height:   height,
			Fee:      fee,
			FeePerKB: fee * 1000 / int64(tx.MsgTx().SerializeSize()),
		},
		StartingPriority: mining.CalcPriority(tx.MsgTx(), utxoView, height),
	}
	mp.pool[*tx.Hash()] = txD
	for _, txIn := range tx.MsgTx().TxIn {
		mp.outpoints[txIn.PreviousOutPoint] = tx
	}
	atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix())
	if mp.cfg.AddrIndex != nil {
		mp.cfg.AddrIndex.AddUnconfirmedTx(tx, utxoView)
	}
	return txD
}
func (mp *TxPool) checkPoolDoubleSpend(tx *provautil.Tx) error {
	for _, txIn := range tx.MsgTx().TxIn {
		if txR, exists := mp.outpoints[txIn.PreviousOutPoint]; exists {
			str := fmt.Sprintf("output %v already spent by "+
				"transaction %v in the memory pool",
				txIn.PreviousOutPoint, txR.Hash())
			return txRuleError(wire.RejectDuplicate, str)
		}
	}
	return nil
}
func (mp *TxPool) fetchInputUtxos(tx *provautil.Tx) (*blockchain.UtxoViewpoint, error) {
	utxoView, err := mp.cfg.FetchUtxoView(tx)
	if err != nil {
		return nil, err
	}
	for originHash, entry := range utxoView.Entries() {
		if entry != nil && !entry.IsFullySpent() {
			continue
		}
		if poolTxDesc, exists := mp.pool[originHash]; exists {
			utxoView.AddTxOuts(poolTxDesc.Tx, mining.UnminedHeight)
		}
	}
	return utxoView, nil
}
func (mp *TxPool) FetchTransaction(txHash *chainhash.Hash) (*provautil.Tx, error) {
	mp.mtx.RLock()
	txDesc, exists := mp.pool[*txHash]
	mp.mtx.RUnlock()
	if exists {
		return txDesc.Tx, nil
	}
	return nil, fmt.Errorf("transaction is not in the pool")
}