Skip to content

Instantly share code, notes, and snippets.

@ishaanam
Last active June 20, 2023 15:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ishaanam/846adf3b453c3a85fe6e15c882c57ae0 to your computer and use it in GitHub Desktop.
Save ishaanam/846adf3b453c3a85fe6e15c882c57ae0 to your computer and use it in GitHub Desktop.

Bitcoin Core Transaction States

Types of Tx States

  • TxStateConfirmed: Contains corresponding block information
  • TxStateInMempool
  • TxStateConflicted: Contains corresponding conflicting block information
  • TxStateInactive: Can be abandoned or not
  • TxStateUnrecognized: Treated as inactive

The Effect of A Transaction's State on Balance Calculation and Tx Construction

Note: all of the conditions described must be true

  • An output is considered for spending by AvailableCoins when:
    • Not an immature coinbase
    • Not conflicted
    • Mined in a block OR in the mempool in a tx where all of the inputs come from us
    • Not replacing a transaction or being replaced by a transaction
    • Not spent
  • An output's value is added to the balance when:
    • The output has been mined in a block OR is in the mempool
    • Not an immature coinbase
    • Not spent
  • An output is considered spent:
    • There exists a transaction spending that output that has been mined in a block OR (is not conflicted AND has not been abandoned)

How Tx States are Initialized

Transactions and their respective transaction states are written to the database and read when a wallet is loaded. These transaction states are then checked after adding them to mapWallet just in case their state has changed while the wallet was unloaded.

  • It starts at WalletBatch::LoadWallet:
    • LoadWallet calls ReadKeyValue, which is used to load information from the database into the wallet.
    • Because the transaction state is serialized as part of the transaction record in the database, ReadKeyValue just unserializes the transaction into a CWalletTx.
    • Then, LoadToWallet is called, which then checks whether the transaction state is either confirmed or conflicted, and if it can't be found in the blockchain, the tx state is updated to TxStateInactive.
    • It is important to note here that a wallet's m_spk_managers map is not updated until after transactions are deserialized from the database and added to mapWallet. This is important to note because when a UTXO pool is being constructed, IsMine will always return as false, since it has no way of identifying outputs that belong to the wallet without SPKMans. This means that it is important to iterate through mapWallet and update a UTXO pool accordingly at the end of CWallet::LoadWallet.

Locations Where Tx States Are Updated

  • SubmitTxMemoryPoolAndRelay: if broadcast is successful, the state is changed to TxStateInMempool{}. This is called by:
    • CommitTransaction: Usually called after calling CreateTransaction
    • ResubmitWalletTransactions: resubmits wallet transactions to the mempool, primarily to ensure that our own mempool is aware of our transactions. called periodically to relay unconfirmed transactions.
  • MarkConflicted: Marks a transaction and its in-wallet descendants as conflicting with a particular block. The state is changed to TxStateConflicted.
  • AbandonTransaction: Just the RPC abandontransaction. Marks a transaction and its in-wallet descendants as abandoned. The state is changed to TxStateInactive.
  • AddToWallet: This only changes the state when the wallet already knows about the given transaction, but the state just needs to be updated. This is called by:
    • AddToWalletIfInvolvingMe: Only add if the tx is related
      • Called by SyncTransaction:
        • transactionAddedToMempool: changes state to TxStateInMempool if it is in the mempool, if not but it was marked as being in the mempool, change to TxStateInactive. (Learn more about this by looking at RefreshMempoolStatus)
        • transactionRemovedFromMempool: if the transaction is actually in the mempool, make it TxStateInMempool, if it was not in the mempool but marked TxStateInMempool, mark it as TxStateInactive. Additionally, if the reason why the transaction was removed is a conflict, then mark it as TxStateInactive. (as a side note: I think that there might be some redundancy here)
        • blockConnected: Marks all txs in block as TxStateConfirmed. also calls transactionRemovedFromMempool for reasons that are beyond me.
        • blockDisconnected: Marks all txs in block as TxStateInactive. If a previously conflicted transaction is no longer conflicted, then mark that tx as TxStateInactive instead of TxStateConflicted (see #27145)
        • ScanForWalletTransactions: Marks txs found in blocks as TxStateConfirmed
    • CommitTransaction: This just adds a transaction to the wallet as TxStateInactive as transaction history. The state is later changed when SubmitTxMemoryPoolAndRelay is called [see above]

Proposed Changes

  • #27307: wallet: track mempool conflicts with wallet transactions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment