Migrating from zcashd
zcashd was a single process that acted as both a Zcash full node and a wallet. Its
replacement is a stack of separate components: zebrad provides the full node, and
Zallet provides the wallet. Migrating therefore has two halves: replacing the node, and
migrating the wallet. This page covers the wallet half, and links out to the node parts
you need.
⚠️ Keep your
zcashddata. Do not deletewallet.dat(or thezcashddatadir) after migrating. The migration reports anything it cannot represent in a Zallet wallet rather than migrating it, and that key material then exists only inwallet.dat.
Migration steps
-
Run a
zebradnode, and let it sync to the chain tip. Zallet reads chain data fromzebradvia one of its two chain backends; the backend you choose determines howzebradneeds to be built and configured.Do not run the wallet migration (step 5) until
zebradhas caught up: the migration sets each account’s birthday from the earliest activity it can place on the chain, and for transactions with no expiry height (pre-NU5 coinbase, or anything from before Overwinter) it can only do that through blocks the node has already synced. An unsynced node can therefore silently produce a birthday later than your wallet’s history, and the scan then never finds the earlier funds. See How the wallet birthday is chosen. -
Install Zallet. See Installation.
-
Create a Zallet config from your
zcash.conf:$ zallet migrate-zcash-conf --zcashd-datadir /path/to/zcashd/datadir -o /path/to/zallet/datadir/zallet.tomlWallet-relevant options are translated to their
zallet.tomlequivalents; options that only affect the node are ignored, and wallet options that cannot be migrated produce warnings. Note thatrpcuser/rpcpasswordare not migrated: Zallet’s JSON-RPC interface uses cookie authentication by default, and you can add password credentials withzallet add-rpc-user. -
Initialize wallet encryption. Zallet encrypts key material with an age identity that you create before importing any keys; see Wallet setup.
-
Migrate your
wallet.dat:$ zallet migrate-zcashd-wallet --zcashd-datadir /path/to/zcashd/datadirThis imports the wallet’s key material and creates corresponding Zallet accounts. If you have several
wallet.datfiles, run it once per file (subsequent runs need--allow-multiple-wallet-imports); each wallet becomes a distinct set of accounts. If the wallet’s key material is encrypted, the command prompts for thezcashdwallet passphrase; see Encrypted wallets.Decide before the first run whether to pass
--allow-partial-import. Without it, the migration fails if any account or transparent spending key could not be imported, listing what was left behind. That failure happens after the importable accounts have been written, so a second run against the same wallet database is refused because the database already holds an importedzcashdwallet. Do not get past that refusal with--allow-multiple-wallet-imports, even though its message suggests the flag: that flag is for importing a differentwallet.dat. Its duplicate check compares only mnemonic fingerprints, and a wallet with no HD seed gets a freshly generated mnemonic on every run, so re-running such a wallet would import it a second time on top of the first. Recovering from a partial-import failure means starting over instead: deletewallet.db, re-runzallet init-wallet-encryption(the identity file can stay), and migrate again with the flag. Anything the flag lets the migration skip remains accessible only through the originalwallet.dat. -
Confirm the backup of each migrated mnemonic. Zallet will not derive new accounts or addresses from a mnemonic until you have confirmed that you hold a copy of it: while
keystore.require_backupis in effect (the default on mainnet and testnet),z_getnewaccount,z_getaddressforaccountandz_recoveraccountsreturn error-18for an unconfirmed mnemonic. A mnemonic brought in by the migration always starts out unconfirmed, whateverzcashdhad recorded, so a migrated wallet cannot hand out new deposit addresses until this step is done. For each mnemonic (the migration printed its fingerprint):$ zallet export-mnemonic --seedfp <fingerprint> >mnemonic.age $ zallet confirm-backup --seedfp <fingerprint>Decrypt the export with your age identity, write the phrase down, and answer the three words
confirm-backupasks for from that written copy. If you deliberately do not want this gate, setkeystore.require_backup = falseinzallet.tomlinstead. -
Decide whether to enable the legacy transparent pool. The migration printed the seed fingerprint of the account holding your
zcashdwallet’s legacy material (the account at the special ZIP 32 indexzcashdused forgetnewaddressandz_getnewaddress). If your tooling relied onzcashd’s single-pool transparent spending (z_sendmanyfromANY_TADDR), or if you will usezallet import-addresswithout naming an account, set that fingerprint inzallet.tomlbefore starting. (Wallet-wide legacy transparent balances are not reported yet, whether or not the option is set; see #384.)[features] legacy_pool_seed_fingerprint = "<fingerprint>"Only one migrated wallet can be the legacy pool, and what it re-enables is a migration bridge rather than a destination; see The legacy transparent pool for what changes and why to move off it. Leave the option unset otherwise.
-
Start Zallet and let it sync:
$ zallet startYour transaction history is already present: the migration imports every transaction from
wallet.datdirectly, including sends that were never mined and transactions that ended up on a non-main-chain block, which a chain scan alone could not recover. What the scan adds is the note commitment tree positions of your notes (and the mined heights of any transactions the migration could not place), and balances are not spendable until it has done so. Usezallet rpc getwalletstatusto observe sync progress, then verify your balances againstzcashdbefore decommissioning it. -
Update your RPC clients. Zallet implements a subset of the
zcashdwallet JSON-RPC methods, some with altered semantics, and somezcashdmethods are intentionally omitted. Check every method you use against the method status matrix. Thezallet rpccommand replaceszcash-cli.
What is migrated
- Mnemonic seeds and the keys derived from them. Accounts are re-created following the
structure of the
zcashdwallet. - Standalone (imported) Sapling spending keys and transparent keys.
- Transparent watch-only entries that include their public key or redeem script.
- Account birthdays, so that chain scanning starts from the right height.
What is not migrated
The migration logs each of these with a count instead of importing it, except the last, which is dropped silently:
- Sprout spending keys and funds. Zallet does not support the Sprout pool. Move any
Sprout funds (e.g. to Sapling, using
zcashd’s migration or a Sprout-capable tool) before retiringzcashd. - Address book entries. Zallet has no store for them yet (#774 tracks preserving
them); the labels exist only in
wallet.dat. - Watch-only entries recorded without their public key or redeem script, and watch-only public keys that are uncompressed or malformed.
- Watch-only redeem scripts the wallet cannot represent. Only multisig scripts within the P2SH size limit are imported; any other watched script is dropped.
- Standalone copies of unified spending keys. The unified accounts themselves are re-derived from the mnemonic, so this loses nothing.
- Transactions that carry no raw transaction data. If such a transaction was mined, the post-migration scan recovers it.
- Records of a kind the wallet parser does not recognise. These are discarded with
no warning at all, so a
wallet.datwritten by an unusualzcashdbuild may lose data without any sign of it in the log.
Not every skip is merely reported. A transparent spending key that cannot be imported
(one whose public key is uncompressed, or whose address appears under none of the
imported accounts) fails the migration unless --allow-partial-import is passed; see
step 5 above for why that decision has to be made before the first run.
Back up the migrated wallet
After migration, a mnemonic backup alone is not sufficient: imported keys exist only
in the wallet database. Keep secure copies of the wallet database (wallet.db), the age
encryption identity file, and your mnemonic phrase(s) — and keep the original
wallet.dat. See the warning in the
migrate-zcashd-wallet reference for details.
Two kinds of wallet.dat also leave you holding a mnemonic phrase you have never seen.
Only the first is announced in the migration’s output; recognise the second by the
zcashd version that last used the wallet:
- A minted mnemonic. If the wallet had no HD seed at all (one created before
zcashdhad HD key derivation, holding only standalone keys and watch-only addresses), the migration generates a fresh 24-word BIP 39 mnemonic and makes it the derivation root of the account that holds those keys. It prints a warning saying so, with the mnemonic’s fingerprint. This phrase never existed inzcashd: no backup ofwallet.datcovers it, nor any address Zallet later derives from it. Export it (zallet export-mnemonic --seedfp <fingerprint>), decrypt the export with your age identity, and record the phrase as you would any other. - A reconstructed mnemonic. If the wallet was last used with a
zcashdolder than 4.7.0, it has a legacy HD seed but no mnemonic. The migration derives the mnemonic thatzcashditself would have created on upgrading, exactly aszcashddid, so this one is recoverable fromwallet.dat. The migration prints only the usual seed fingerprint line for it, the same as for a wallet that already had a mnemonic. You have still never held the phrase, though, so export and record it now rather than relying on thezcashdfile.
In both cases the phrase must also be confirmed with zallet confirm-backup (step 6)
before Zallet will derive new accounts or addresses from it.