mempool: Return type TxDesc instead of type btcutil.Tx

This will provide callers more information on the accepted transaction.
This commit is contained in:
David Hill 2016-10-28 14:23:51 -04:00
parent 1a69eb0617
commit 2615fa0849
4 changed files with 61 additions and 57 deletions

View File

@ -1185,7 +1185,7 @@ func (b *blockManager) handleNotifyMsg(notification *blockchain.Notification) {
// Reinsert all of the transactions (except the coinbase) into
// the transaction pool.
for _, tx := range block.Transactions()[1:] {
_, err := b.server.txMemPool.MaybeAcceptTransaction(tx,
_, _, err := b.server.txMemPool.MaybeAcceptTransaction(tx,
false, false)
if err != nil {
// Remove the transaction and all transactions

View File

@ -481,10 +481,10 @@ func (mp *TxPool) RemoveDoubleSpends(tx *btcutil.Tx) {
// helper for maybeAcceptTransaction.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32, fee int64) {
func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32, fee int64) *TxDesc {
// Add the transaction to the pool and mark the referenced outpoints
// as spent by the pool.
mp.pool[*tx.Hash()] = &TxDesc{
txD := &TxDesc{
TxDesc: mining.TxDesc{
Tx: tx,
Added: time.Now(),
@ -493,6 +493,8 @@ func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil
},
StartingPriority: mining.CalcPriority(tx.MsgTx(), utxoView, height),
}
mp.pool[*tx.Hash()] = txD
for _, txIn := range tx.MsgTx().TxIn {
mp.outpoints[txIn.PreviousOutPoint] = tx
}
@ -503,6 +505,8 @@ func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil
if mp.cfg.AddrIndex != nil {
mp.cfg.AddrIndex.AddUnconfirmedTx(tx, utxoView)
}
return txD
}
// checkPoolDoubleSpend checks whether or not the passed transaction is
@ -572,7 +576,7 @@ func (mp *TxPool) FetchTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error)
// more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, error) {
func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) {
txHash := tx.Hash()
// Don't accept the transaction if it already exists in the pool. This
@ -583,7 +587,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
mp.isOrphanInPool(txHash)) {
str := fmt.Sprintf("already have transaction %v", txHash)
return nil, txRuleError(wire.RejectDuplicate, str)
return nil, nil, txRuleError(wire.RejectDuplicate, str)
}
// Perform preliminary sanity checks on the transaction. This makes
@ -592,16 +596,16 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
err := blockchain.CheckTransactionSanity(tx)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
return nil, nil, chainRuleError(cerr)
}
return nil, err
return nil, nil, err
}
// A standalone transaction must not be a coinbase transaction.
if blockchain.IsCoinBase(tx) {
str := fmt.Sprintf("transaction %v is an individual coinbase",
txHash)
return nil, txRuleError(wire.RejectInvalid, str)
return nil, nil, txRuleError(wire.RejectInvalid, str)
}
// Don't accept transactions with a lock time after the maximum int32
@ -611,7 +615,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
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, txRuleError(wire.RejectNonstandard, str)
return nil, nil, txRuleError(wire.RejectNonstandard, str)
}
// Get the current height of the main chain. A standalone transaction
@ -638,7 +642,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
}
str := fmt.Sprintf("transaction %v is not standard: %v",
txHash, err)
return nil, txRuleError(rejectCode, str)
return nil, nil, txRuleError(rejectCode, str)
}
}
@ -652,7 +656,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
// which examines the actual spend data and prevents double spends.
err = mp.checkPoolDoubleSpend(tx)
if err != nil {
return nil, err
return nil, nil, err
}
// Fetch all of the unspent transaction outputs referenced by the inputs
@ -662,16 +666,16 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
utxoView, err := mp.fetchInputUtxos(tx)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
return nil, nil, chainRuleError(cerr)
}
return nil, err
return nil, nil, err
}
// Don't allow the transaction if it exists in the main chain and is not
// not already fully spent.
txEntry := utxoView.LookupEntry(txHash)
if txEntry != nil && !txEntry.IsFullySpent() {
return nil, txRuleError(wire.RejectDuplicate,
return nil, nil, txRuleError(wire.RejectDuplicate,
"transaction already exists")
}
delete(utxoView.Entries(), *txHash)
@ -692,7 +696,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
}
}
if len(missingParents) > 0 {
return missingParents, nil
return missingParents, nil, nil
}
// Don't allow the transaction into the mempool unless its sequence
@ -701,13 +705,13 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
sequenceLock, err := mp.cfg.CalcSequenceLock(tx, utxoView)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
return nil, nil, chainRuleError(cerr)
}
return nil, err
return nil, nil, err
}
if !blockchain.SequenceLockActive(sequenceLock, nextBlockHeight,
medianTimePast) {
return nil, txRuleError(wire.RejectNonstandard,
return nil, nil, txRuleError(wire.RejectNonstandard,
"transaction's sequence locks on inputs not met")
}
@ -719,9 +723,9 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
utxoView, mp.cfg.ChainParams)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
return nil, nil, chainRuleError(cerr)
}
return nil, err
return nil, nil, err
}
// Don't allow transactions with non-standard inputs if the network
@ -738,7 +742,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
}
str := fmt.Sprintf("transaction %v has a non-standard "+
"input: %v", txHash, err)
return nil, txRuleError(rejectCode, str)
return nil, nil, txRuleError(rejectCode, str)
}
}
@ -754,15 +758,15 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
numSigOps, err := blockchain.CountP2SHSigOps(tx, false, utxoView)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
return nil, nil, chainRuleError(cerr)
}
return nil, err
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, txRuleError(wire.RejectNonstandard, str)
return nil, nil, txRuleError(wire.RejectNonstandard, str)
}
// Don't allow transactions with fees too low to get into a mined block.
@ -783,7 +787,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
str := fmt.Sprintf("transaction %v has %d fees which is under "+
"the required amount of %d", txHash, txFee,
minFee)
return nil, txRuleError(wire.RejectInsufficientFee, str)
return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
}
// Require that free transactions have sufficient priority to be mined
@ -797,7 +801,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
str := fmt.Sprintf("transaction %v has insufficient "+
"priority (%g <= %g)", txHash,
currentPriority, mining.MinHighPriority)
return nil, txRuleError(wire.RejectInsufficientFee, str)
return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
}
}
@ -815,7 +819,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
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, txRuleError(wire.RejectInsufficientFee, str)
return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
}
oldTotal := mp.pennyTotal
@ -831,18 +835,18 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
txscript.StandardVerifyFlags, mp.cfg.SigCache)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
return nil, nil, chainRuleError(cerr)
}
return nil, err
return nil, nil, err
}
// Add to transaction pool.
mp.addTransaction(utxoView, tx, bestHeight, txFee)
txD := mp.addTransaction(utxoView, tx, bestHeight, txFee)
log.Debugf("Accepted transaction %v (pool size: %v)", txHash,
len(mp.pool))
return nil, nil
return nil, txD, nil
}
// MaybeAcceptTransaction is the main workhorse for handling insertion of new
@ -856,21 +860,21 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
// be added to the orphan pool.
//
// This function is safe for concurrent access.
func (mp *TxPool) MaybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*chainhash.Hash, error) {
func (mp *TxPool) MaybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*chainhash.Hash, *TxDesc, error) {
// Protect concurrent access.
mp.mtx.Lock()
hashes, err := mp.maybeAcceptTransaction(tx, isNew, rateLimit, true)
hashes, txD, err := mp.maybeAcceptTransaction(tx, isNew, rateLimit, true)
mp.mtx.Unlock()
return hashes, err
return hashes, txD, err
}
// processOrphans is the internal function which implements the public
// ProcessOrphans. See the comment for ProcessOrphans for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *TxPool) processOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
var acceptedTxns []*btcutil.Tx
func (mp *TxPool) processOrphans(acceptedTx *btcutil.Tx) []*TxDesc {
var acceptedTxns []*TxDesc
// Start with processing at least the passed transaction.
processList := list.New()
@ -901,7 +905,7 @@ func (mp *TxPool) processOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
// Potentially accept an orphan into the tx pool.
for _, tx := range orphans {
missing, err := mp.maybeAcceptTransaction(
missing, txD, err := mp.maybeAcceptTransaction(
tx, true, true, false)
if err != nil {
// The orphan is now invalid, so there
@ -925,7 +929,7 @@ func (mp *TxPool) processOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
// the orphan pool, and add it to the list of
// transactions to process so any orphans that
// depend on it are handled too.
acceptedTxns = append(acceptedTxns, tx)
acceptedTxns = append(acceptedTxns, txD)
mp.removeOrphan(tx, false)
processList.PushBack(tx)
@ -941,8 +945,8 @@ func (mp *TxPool) processOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
// by the accepted transactions since those are now definitive double
// spends.
mp.removeOrphanDoubleSpends(acceptedTx)
for _, tx := range acceptedTxns {
mp.removeOrphanDoubleSpends(tx)
for _, txD := range acceptedTxns {
mp.removeOrphanDoubleSpends(txD.Tx)
}
return acceptedTxns
@ -958,7 +962,7 @@ func (mp *TxPool) processOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
// no transactions were moved from the orphan pool to the mempool.
//
// This function is safe for concurrent access.
func (mp *TxPool) ProcessOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
func (mp *TxPool) ProcessOrphans(acceptedTx *btcutil.Tx) []*TxDesc {
mp.mtx.Lock()
acceptedTxns := mp.processOrphans(acceptedTx)
mp.mtx.Unlock()
@ -977,7 +981,7 @@ func (mp *TxPool) ProcessOrphans(acceptedTx *btcutil.Tx) []*btcutil.Tx {
// the passed one being accepted.
//
// This function is safe for concurrent access.
func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) ([]*btcutil.Tx, error) {
func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) ([]*TxDesc, error) {
log.Tracef("Processing transaction %v", tx.Hash())
// Protect concurrent access.
@ -985,7 +989,7 @@ func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool
defer mp.mtx.Unlock()
// Potentially accept the transaction to the memory pool.
missingParents, err := mp.maybeAcceptTransaction(tx, true, rateLimit,
missingParents, txD, err := mp.maybeAcceptTransaction(tx, true, rateLimit,
true)
if err != nil {
return nil, err
@ -997,11 +1001,11 @@ func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool
// are now available) and repeat for those accepted
// transactions until there are no more.
newTxs := mp.processOrphans(tx)
acceptedTxs := make([]*btcutil.Tx, len(newTxs)+1)
acceptedTxs := make([]*TxDesc, len(newTxs)+1)
// Add the parent transaction first so remote nodes
// do not add orphans.
acceptedTxs[0] = tx
acceptedTxs[0] = txD
copy(acceptedTxs[1:], newTxs)
return acceptedTxs, nil

View File

@ -442,10 +442,10 @@ func TestSimpleOrphanChain(t *testing.T) {
"length does not match expected -- got %d, want %d",
len(acceptedTxns), len(chainedTxns))
}
for _, tx := range acceptedTxns {
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, tx, false, true)
testPoolMembership(tc, txD.Tx, false, true)
}
}
@ -784,10 +784,10 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) {
"length does not match expected -- got %d, want %d",
len(acceptedTxns), maxOrphans)
}
for _, tx := range acceptedTxns {
for _, txD := range acceptedTxns {
// Ensure the transaction is no longer in the orphan pool, is
// in the transaction pool, and is reported as available.
testPoolMembership(tc, tx, false, true)
testPoolMembership(tc, txD.Tx, false, true)
}
// Ensure the double spending orphan is no longer in the orphan pool and

View File

@ -972,18 +972,18 @@ func (s *server) RemoveRebroadcastInventory(iv *wire.InvVect) {
// both websocket and getblocktemplate long poll clients of the passed
// transactions. This function should be called whenever new transactions
// are added to the mempool.
func (s *server) AnnounceNewTransactions(newTxs []*btcutil.Tx) {
func (s *server) AnnounceNewTransactions(newTxs []*mempool.TxDesc) {
// Generate and relay inventory vectors for all newly accepted
// transactions into the memory pool due to the original being
// accepted.
for _, tx := range newTxs {
for _, txD := range newTxs {
// Generate the inventory vector and relay it.
iv := wire.NewInvVect(wire.InvTypeTx, tx.Hash())
s.RelayInventory(iv, tx)
iv := wire.NewInvVect(wire.InvTypeTx, txD.Tx.Hash())
s.RelayInventory(iv, txD)
if s.rpcServer != nil {
// Notify websocket clients about mempool transactions.
s.rpcServer.ntfnMgr.NotifyMempoolTx(tx, true)
s.rpcServer.ntfnMgr.NotifyMempoolTx(txD.Tx, true)
// Potentially notify any getblocktemplate long poll clients
// about stale block templates due to the new transaction.
@ -1319,14 +1319,14 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) {
// Don't relay the transaction if there is a bloom
// filter loaded and the transaction doesn't match it.
if sp.filter.IsLoaded() {
tx, ok := msg.data.(*btcutil.Tx)
txD, ok := msg.data.(*mempool.TxDesc)
if !ok {
peerLog.Warnf("Underlying data for tx" +
" inv relay is not a transaction")
" inv relay is not a *mempool.TxDesc")
return
}
if !sp.filter.MatchTxAndUpdate(tx) {
if !sp.filter.MatchTxAndUpdate(txD.Tx) {
return
}
}