Addition to the mempool

Transactions are added to the mempool via addUnchecked() as part of the AcceptToMemoryPool() flow. See Transaction validation for more information on how this flow is entered.

The function name addUnchecked specifically refers to the fact that no checks are being performed, so this must not be called until policy checks have passed.

This function is called from within validation.cpp (MemPoolAccept::Finalize()) where the appropriate consensus and policy checks have already been performed on the transaction. The transaction is added to the primary index mapTx before any fee prioritisation ("delta") is applied to it.

Next any links to parent transactions are generated by looping through the inputs and mapping the COutPoint of the input to this transaction CTransaction in the mapNextTx map. Additionally the tx input is added to a set which is used to update parent transactions if they are still in the mempool.

After all inputs have been considered, UpdateAncestorsOf() is called which will add this transaction as a descendant to any ancestors in the mempool. This is followed by UpdateEntryForAncestors() which will re-calculate and apply descendant count, size, fee and sigOpCost of the ancestors with the new descendant being accounted for.

Finally update totalTxSize and totalFee (both sum totals of the mempool) to account for this new transaction.

Removal from the mempool

Transactions are removed from the mempool for a number of reasons:

  1. A new block has been connected removeForBlock()

  2. A re-org is taking place removeForReorg()

  3. The transaction has expired Expire()

  4. The transaction is being replaced by a higher-fee version MemPoolAccept::Finalize()

  5. The mempool must be trimmed back down below its maximum size TrimToSize()

mempool removal
Figure 1. Removal from the mempool

RemoveStaged() takes a set of transactions referenced by their txid along with their removal reason, and removes them sequentially. It does this by first updating the ancestors of the transaction, followed by the descendants. After calculating and updating related transaction information it calls removeUnchecked() which actions the removal from the mempool.

removeUnchecked() starts by notifying the validation interface that a transaction has been removed from the mempool for all reasons other than a new block arriving, as there is a different BlockConnected signal which can be used for that.

Next it loops over the txins of the transaction, and removes each prevout of each txin from the mapNextTx map.

mapNextTx is used to map a COutPoint to the unconfirmed transaction spending it. This way there is a quick lookup available to check that a new transaction being added to the mempool is not trying to double spend a UTXO.

You can see the map being created as new transactions are learned about in addUnchecked().

If the node has upgraded to SegWit the vTxHashes vector, which stores wtxids is then updated. As vTxHashes stores the wtxids in random order, first we move the transaction’s entry to the back, and then pop it off, resizing the vector if needed.

Finally, as with addUnchecked() we update the mempool sum totals for txSize and fee and erase the transaction from the primary mempool index mapTx.

Both adding and removing transactions increment the mempool_seqence counter. This is used by the getrawmempool RPC (via MempoolToJSON) in tracking the number of mempool database transaction operations.