ZIP: 400 Title: Wallet.dat format Owners: Alfredo Garcia <oxarbitrage@gmail.com> Status: Draft Category: Wallet Created: 2020-05-26 License: MIT
The key words "MUST" and "MAY" in this document are to be interpreted as described in RFC 2119. [1]
This proposal defines the current format used in zcashd for wallet persistent storage, commonly known as wallet.dat
.
The process of saving wallet data into disk is currently unspecified. The key-values used in the current implementation are undocumented, and their structure and functionality are unknown without a deep analysis of the involved source code. This document details the schema and the mechanics of the wallet database. This is an informational document, no changes will be done to the source code.
Zcash stores wallet information in a Berkeley database (BDB) [2] commonly known as wallet.dat
.
The main purpose of this is the persistence of public and private keys the user created and the ability to recover the wallet state after a node restart. This file also allows the migration of user information from one node to another by moving the database to the corresponding new data directory, assuming both zcashd instances are running the same or similar version. A re-index may be necessary after this operation.
The current database is a key-value store where keys and values can have multiple data types in binary format. The first data found in a database key is always the property name, the rest of the key is generally used to identify a record, for example:
<------ KEY ----+- VALUE -> --------------------------- | zkey | pubkey | privkey | ---------------------------
Here zkey
is the property name located at the first position of the database key; the public key is also part of the database key, and it is located in the second position; the private key is saved in the database value column at the first position.
According to Zcash v3.0.0-rc1 the following key-values can be found: the property names in bold mean only one instance of this type can exist in the entire database, while the others, suffixed by '*' can have multiple instances. Keys and Values columns of the table contain the types that the stored data is representing. Included also there are the variable names hoping it will add some clarity to what the stored data is representing.
Name | Description | Keys | Values |
---|---|---|---|
acc* | Account data |
|
|
acentry* | Track internal transfers between accounts in the same wallet |
|
|
bestblock | The current best block of the blockchain. |
|
|
chdseed | Encrypted HD seed |
|
|
ckey* | Encrypted transparent pubkey and private key. |
|
|
csapzkey* | Encrypted Sapling pubkey and private key. |
|
|
cscript | Serialized script, used inside transaction inputs and outputs |
|
|
czkey* | Encrypted Sprout pubkey and private key. |
|
|
defaultkey | Default Transparent key. |
|
|
destdata* | Adds a destination data tuple to the store. |
|
|
hdchain | Hierarchical Deterministic chain code, derived from seed. |
|
|
hdseed* | Hierarchical Deterministic seed. [4] |
|
|
key* | Transparent pubkey and privkey. |
|
|
keymeta* | Transparent key metadata. |
|
|
minversion | Wallet required minimal version. | ||
mkey | Master key, used to encrypt public and private keys of the database. |
|
|
name* | Name of an address to insert in the address book. |
|
|
orderposnext | Index of next tx. |
|
|
pool* |
|
|
|
purpose* | Short description or identifier of an address. |
|
|
sapzaddr* | Sapling z-addr Incoming Viewing key and address. |
|
|
sapextfvk* | Sapling Extended Full Viewing Key | ||
sapzkey* | Sapling Incoming Viewing Key and Extended Spending Key |
|
|
tx* | Store all transactions that are related to wallet. |
|
|
version | The CLIENT_VERSION from clientversion.h . |
|
|
vkey* | Sprout Viewing Keys. |
|
|
watchs* | Watch-only t-addresses. |
|
|
witnesscachesize | Shielded Note Witness cache size. |
|
|
wkey* | Wallet key. | ||
zkey* | Sprout Payment Address and Spending Key. |
|
|
zkeymeta* | Sprout Payment Address and key metadata. |
|
|
When a zcashd node built with wallet support is started for the first time, a new wallet database is created. By default the node will automatically execute wallet actions that will be saved in the database at the first flush time.
The following flow will happen when a node with wallet support is started for the first time:
DEFAULT_KEYPOOL_SIZE
(100 by default) keys will be added to the pool, creating 100 records withpool
as property name (first value of database key).- Also 100
key
properties will be added. - 100
keymeta
. - Wallet will create a default transparent key to receive, this will be also added as
key
,pool
andkeymeta
properties. - This default key is also added as a
defaultkey
property. - The last action created an entry in the address book that is reflected in the database by the
name
andpurpose
properties. - If the wallet is created with HD support, it will have additional properties
hdseed
andhdchain
that will be saved. version
,minversion
,witnesscachesize
andbestblock
properties are added. These are settings and state information: thebestblock
property is a good example of the database being populated that is happening without any user interaction, but it will just update as the best block of the current chain changes.
At any time after the database is created, new properties can be added as the wallet users perform actions. For example, if the user creates a new Sapling address with the RPC command z_getnewaddress
then new records with properties sapzkey and sapzkeymeta will be added to the database.
In zcashd, database changes do not happen immediately but they are flushed in its own thread by ThreadFlushWalletDB()
function periodically to avoid overhead. The internal counter nWalletDBUpdated
is increased each time a new write operation to the database is done, this is compared with the last flush in order to commit new stuff.
When the node goes down for whatever reason the information in the wallet database SHOULD persist in the disk; the next time the node starts, the software will detect the database file, read from there and add the values into memory structures that will guarantee wallet functionality.
The wallet database will not save all the transactions that are happening in the blockchain however it will save all transactions where wallet keys are involved. This is needed for example to get balances. Therefore the wallet must have all the transactions related to a key to compute the final value of coin available in the derived address.
The tx
property will hold the transaction-related data with the transaction hash as the key and the full transaction as the value.
Transactions are saved in the database tx
key as they arrive, this means transactions have a sequence. The set of all transactions from the begging to a specified timestamp is the wallet state at that instant. Wallet state is important among other things to get current balance for a wallet or address.
In the blockchain, transactions can be invalidated by rollbacks; wallet code will handle this by updating the transactions in the memory database. New state needs to be reflected in the disk database, this is done in zcashd by the flag fAnyUnordered
where if true at start time, will launch a rescan over all transactions again.
The wallet database file may become corrupted. There are utilities in the zcutil/bin directory that may help with recovering it if this happens. Please ask for help on the Zcash forum or Community Discord.
Encryption will not be discussed in this document in detail as it is expected for the algorithm to change in the future according to the Wallet format ZIP issue: [3].
For a deeper understanding of the current encryption mechanism please refer to [5]
[1] | RFC 2119: Key words for use in RFCs to Indicate Requirement Levels |
[2] | Oracle Berkeley Database |
[3] | ZIP 400 issue |
[4] | ZIP 32: Shielded Hierarchical Deterministic Wallets |
[5] | Database key encryption implementation |