The Bitcoin node’s wallet’s code

Here are some notes on aspects of the wallet included with the Bitcoin node. It is very possible there are errors or mistaken interpretations of the source code here. I started the day spring cleaning but somehow have ended up on the couch writing this.

Edit 2020/10/06: Added detail on notifications.

Wallet access

Wallets are loaded in a locked mode where the wallet password has not been provided, and no encrypted data like private keys can be accessed. This means that the JSON-RPC API calls will error for wallet-related calls with errors relating to this. The walletpassphrase JSON-RPC call can be used to provide the wallet passphrase to the node and “unlock” it with a provided time period after which the passphrase will be forgotten and the wallet re-locked.

Wallets are accessed using wallet-related JSON-RPC calls, and the name of the desired wallet is specified in the URL following the leading /wallet/ suffix.

Wallet keys

The keypoolrefill JSON-RPC call can be used to forceably refill the keypool or if a new count is provided, allocate a higher amount. In general an unlocked wallet should automatically ensure that it’s keypools are refilled, but it will not do this if the wallet is locked.

HD wallets

In theory a wallet can derive keys from the master public key without the password and only needs to obtain the passphrase when it needs to access or derive keys from the master private key. In the case of the node wallet it stores the processed passphrase and uses that to decrypt master keys on demand and then to derive keys from them.

The key pool derives keys from the master key with a derivation path of m/<account>'/<is change>'/n’ where n is the index of the derived child key. <account> is always 0. It uses an <is change> value of 1 for internal keys, and 0 for external keys. This supports the assumption that it uses a separate keypool for each of what are conventionally referred to as receiving and change “addresses”.

Encryption

Watch-only wallets

In the “full node” world of Bitcoin Core where blocks are small and everyone receives blocks to process, the concept of a watch-only wallet exists. This works by knowing the public key usage to watch out for and looking for them in a block. In a blockchain that actually gets used where the blocks become larger and larger, this is impractical for many reasons and not just the one alluded to here.

The node allows the importing of addresses and will watch for them; the importaddress JSON-RPC call can be used for this. Interestingly it supports both importing actual P2PKH and P2SH addresses and literal output scripts provided in hex form. It will presumably obtain occurrences of these and include them in the wallet history and balances, and as new transactions are detected alter those accordingly.

While the wallet will provide public keys that are watch-only on request, the situations where this allows a locked wallet to do anything meaningful is at this time unknown.

Key allocation

When something needs a key, it requests either an external or internal one. This marks the key as reserved in memory, and prevents it being dispensed to another caller. It is assumed that whatever successfully employs that key results in the key being marked as used, and fully removed from the relevant key pool.

Node integration

  • TransactionAddedToMempool
  • BlockConnected
  • BlockDisconnected

These give a view of all the transactions related to the wallet. For the most part as long as the wallet is open and the node is connected to the P2P network, this should ensure that it has the full history of all key/address/script usage. The -rescan command-line argument can be used to prompt a rescan, but cannot guarantee success in the case of a pruned node.

When importing new sources of keys, like public keys, scripts or addresses, the JSON-RPC the user can specifies (although it is optional and defaults to true) if the wallet should rescan the blockchain for usage of these keys. This will refuse to do the import if they enable this, and the node is pruned. The importprunedfunds JSON-RPC call is provided so that the user can provide the supplementary data on spends and receipts that a pruned node cannot scan for.

Accounts

A default account was referred to by "” and all accounts with ”*". An account is persisted with the last address that was reserved for it if there is one, this is what the getaccountaddress JSON-RPC call returns.

Most if not all references to accounts are marked as deprecated. This is a concept that was going away in Bitcoin Core, and would possibly have been going away in Bitcoin SV if the node wallet itself wasn’t becoming unusable with increasing blockchain usage.

Accounting entries

These appear to have largely been for the benefit of user interface.

Notifications

Bitcoin Core has since the date of forking been updated with additional replacements:

  • %w: The wallet name (but not on windows due to lack of shell escaping support).
  • %b: The hex block hash if confirmed or unconfirmed otherwise
  • %h: The block height if confirmed or -1 otherwise.

The Bitcoin SV inherited notifications:

  • The transaction is added to the wallet.
  • The transaction has a valid block hash already and it is changing to something else.
  • The transaction was flagged as abandoned but has been added (was broadcast and entered the mempool?).
  • The transaction index value changes, -1 means it is conflicted and a non-zero block hash is the earliest block it is conflicted with. It also defaults to -1, so… When the merkle proof is set on the transaction, the index value is set to the index of the transaction in the block (used for merkle proof verification).

The Bitcoin Core updated notification cases:

  • The transaction is added to the wallet.
  • The transaction state “index”(?) changes from one value to another.
  • In mempool (State of transaction added to mempool).
  • Confirmed (State of transaction confirmed in a block).
  • Conflicted (State of rejected transaction that conflicts with a confirmed block).
  • Inactive (State of transaction not confirmed or conflicting with a known block and not in the mempool. May conflict with the mempool, or with an unknown block, or be abandoned, never broadcast, or rejected from the mempool for another reason).
  • Inactive (abandoned).

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store