Introduction
Zallet is a full-node Zcash wallet written in Rust. It is being built as a replacement for
the zcashd wallet.
Security Warnings
Zallet is currently under development and has not been fully reviewed.
Current phase: Alpha release
Zallet is currently in alpha. What this means is:
- Breaking changes may occur at any time, requiring you to delete and recreate your Zallet wallet.
- Many JSON-RPC methods that will be ported from
zcashdhave not yet been implemented. - We will be rapidly making changes as we release new alpha versions.
We encourage everyone to test out Zallet during the alpha period and provide feedback,
either by opening issues on GitHub or contacting us in the #wallet-dev channel of the
Zcash R&D Discord.
Future phase: Beta release
After alpha testing will come the beta phase. At this point, all of the JSON-RPC methods that we intend to support will exist. Users will be expected to migrate to the provided JSON-RPC methods; semantic differences will need to be taken into account.
Installation
There are multiple ways to install the zallet binary. The table below has a summary of
the simplest options:
| Environment | CLI command |
|---|---|
| Debian | Debian packages |
| Ubuntu | Debian packages |
Help from new packagers is very welcome. However, please note that Zallet is currently ALPHA software, and is rapidly changing. If you create a Zallet package before the 1.0.0 production release, please ensure you mark it as alpha software and regularly update it.
Pre-compiled binaries
WARNING: This approach does not have automatic updates.
Executable binaries are available for download on the GitHub Releases page.
Build from source using Rust
WARNING: This approach does not have automatic updates.
To build Zallet from source, you will first need to install Rust and Cargo. Follow the instructions on the Rust installation page. Zallet currently requires at least Rust version 1.85.
WARNING: The following does not yet work because Zallet cannot be published to crates.io while it has unpublished dependencies. This will be fixed during the alpha phase. In the meantime, follow the instructions to install the latest development version.
Once you have installed Rust, the following command can be used to build and install Zallet:
cargo install zallet
This will automatically download Zallet from crates.io, build it, and install it in
Cargo’s global binary directory (~/.cargo/bin/ by default).
To update, run cargo install zallet again. It will check if there is a newer version,
and re-install Zallet if a new version is found. You will need to shut down and restart
any running Zallet instances to apply the new version.
To uninstall, run the command cargo uninstall zallet. This will only uninstall the
binary, and will not alter any existing wallet datadir.
Installing the latest development version
If you want to run the latest unpublished changes, then you can instead install Zallet directly from the main branch of its code repository:
cargo install --git https://github.com/zcash/wallet.git
Debian binary packages setup
The Electric Coin Company operates a package repository for 64-bit Debian-based distributions. If you’d like to try out the binary packages, you can set it up on your system and install Zallet from there.
First install the following dependency so you can talk to our repository using HTTPS:
sudo apt-get update && sudo apt-get install apt-transport-https wget gnupg2
Next add the Zcash master signing key to apt’s trusted keyring:
wget -qO - https://apt.z.cash/zcash.asc | gpg --import
gpg --export B1C9095EAA1848DBB54D9DDA1D05FDC66B372CFE | sudo apt-key add -
Key fingerprint = B1C9 095E AA18 48DB B54D 9DDA 1D05 FDC6 6B37 2CFE
Add the repository to your Bullseye sources:
echo "deb [arch=amd64] https://apt.z.cash/ bullseye main" | sudo tee /etc/apt/sources.list.d/zcash.list
Or add the repository to your Bookworm sources:
echo "deb [arch=amd64] https://apt.z.cash/ bookworm main" | sudo tee /etc/apt/sources.list.d/zcash.list
Update the cache of sources and install Zcash:
sudo apt update && sudo apt install zallet
Troubleshooting
Missing Public Key Error
If you see:
The following signatures couldn't be verified because the public key is not available: NO_PUBKEY B1C9095EAA1848DB
Get the new key directly from the z.cash site:
wget -qO - https://apt.z.cash/zcash.asc | gpg --import
gpg --export B1C9095EAA1848DBB54D9DDA1D05FDC66B372CFE | sudo apt-key add -
to retrieve the new key and resolve this error.
Revoked Key error
If you see something similar to:
The following signatures were invalid: REVKEYSIG AEFD26F966E279CD
Remove the key marked as revoked:
sudo apt-key del AEFD26F966E279CD
Then retrieve the updated key:
wget -qO - https://apt.z.cash/zcash.asc | gpg --import
gpg --export B1C9095EAA1848DBB54D9DDA1D05FDC66B372CFE | sudo apt-key add -
Then update the list again:
sudo apt update
Expired Key error
If you see something similar to:
The following signatures were invalid: KEYEXPIRED 1539886450
Remove the old signing key:
sudo apt-key del 1539886450
Remove the list item from local apt:
sudo rm /etc/apt/sources.list.d/zcash.list
Update the repository list:
sudo apt update
Then start again at the beginning of this document.
Setting up a Zallet wallet
WARNING: This process is currently unstable, very manual, and subject to change as we make Zallet easier to use.
Create a config file
Zallet by default uses $HOME/.zallet as its data directory. You can override
this with the -d/--datadir flag.
Once you have picked a datadir for Zallet, create a zallet.toml file in it.
You currently need at least the following:
[builder.limits]
[consensus]
network = "main"
[database]
[external]
[features]
as_of_version = "0.0.0"
[features.deprecated]
[features.experimental]
[indexer]
validator_user = ".."
validator_password = ".."
[keystore]
[note_management]
[rpc]
bind = ["127.0.0.1:SOMEPORT"]
In particular, you currently need to configure the [indexer] section to point
at your full node’s JSON-RPC endpoint. The relevant config options in that
section are:
validator_address(if not running on localhost at the default port)validator_cookie_auth = trueandvalidator_cookie_path(if using cookie auth)validator_userandvalidator_password(if using basic auth)
If you have an existing zcash.conf, you can use it as a starting point:
$ zallet migrate-zcash-conf --datadir /path/to/zcashd/datadir -o /path/to/zallet/datadir/zallet.toml
Initialize the wallet encryption
Zallet uses age encryption to encrypt all key
material internally. Currently you can use two kinds of age identities, which
you can generate with rage:
-
A plain identity file directly on disk:
$ rage-keygen -o /path/to/zallet/datadir/encryption-identity.txt Public key: age1... -
A passphrase-encrypted identity file:
$ rage -p -o /path/to/zallet/datadir/encryption-identity.txt <(rage-keygen) Public key: age1... Using an autogenerated passphrase: drip-lecture-engine-wood-play-business-blame-kitchen-month-combine
(age plugins will also be supported but currently are tricky to set up.)
Once you have created your identity file, initialize your Zallet wallet:
$ zallet -d /path/to/zallet/datadir init-wallet-encryption
Generate a mnemonic phrase
$ zallet -d /path/to/zallet/datadir generate-mnemonic
Each time you run this, a new BIP 39 mnemonic will be added to the wallet. Be careful to only run it multiple times if you want multiple independent roots of spend authority!
Start Zallet
$ zallet -d /path/to/zallet/datadir start
Command-line tool
The zallet command-line tool is used to create and maintain wallet datadirs, as well as
run wallets themselves. After you have installed zallet, you
can run the zallet help command in your terminal to view the available commands.
The following sections provide in-depth information on the different commands available.
zallet startzallet example-configzallet migrate-zcash-confzallet migrate-zcashd-walletzallet init-wallet-encryptionzallet generate-mnemoniczallet import-mnemoniczallet export-mnemoniczallet add-rpc-userzallet rpczallet repairsubcommands
The start command
zallet start starts a Zallet wallet!
The command takes no arguments (beyond the top-level flags on zallet itself). When run,
Zallet will connect to the backing full node (which must be running), start syncing, and
begin listening for JSON-RPC connections.
You can shut down a running Zallet wallet with Ctrl+C if zallet is in the foreground,
or (on Unix systems) by sending it the signal SIGINT or SIGTERM.
The example-config command
zallet example-config generates an example configuration TOML file that can be used to
run Zallet.
The command takes one flag that is currently required: -o/--output PATH which specifies
where the generated config file should be written. The value - will write the config to
stdout.
For the Zallet alpha releases, the command also currently takes another required flag
--this-is-alpha-code-and-you-will-need-to-recreate-the-example-later.
The generated config file contains every available config option, along with their documentation:
$ zallet example-config -o -
# Default configuration for Zallet.
#
# This file is generated as an example using Zallet's current defaults. It can
# be used as a skeleton for custom configs.
#
# Fields that are required to be set are uncommented, and set to an example
# value. Every other field is commented out, and set to the current default
# value that Zallet will use for it (or `UNSET` if the field has no default).
#
# Leaving a field commented out means that Zallet will always use the latest
# default value, even if it changes in future. Uncommenting a field but keeping
# it set to the current default value means that Zallet will treat it as a
# user-configured value going forward.
#
# Settings that affect transactions created by Zallet.
#
[builder]
# Whether to spend unconfirmed transparent change when sending transactions.
#
# Does not affect unconfirmed shielded change, which cannot be spent.
#spend_zeroconf_change = true
...
The migrate-zcash-conf command
Available on crate feature
zcashd-importonly.
zallet migrate-zcash-conf migrates a zcashd configuration file (zcash.conf) to an
equivalent Zallet configuration file (zallet.toml).
The command requires at least one of the following two flag:
--path: A path to azcashdconfiguration file.--zcashd-datadir: A path to azcashddatadir. If this is provided, then--pathcan be relative (or omitted, in which case the default filenamezcash.confwill be used).
For the Zallet alpha releases, the command also currently takes another required flag
--this-is-alpha-code-and-you-will-need-to-redo-the-migration-later.
When run, Zallet will parse the zcashd config file, and migrate its various options to
equivalent Zallet config options. Non-wallet options will be ignored, and wallet options
that cannot be migrated will cause a warning to be printed to stdout.
The migrate-zcashd-wallet command
Available on crate feature
zcashd-importonly.
zallet migrate-zcashd-wallet migrates a zcashd wallet file (wallet.dat) to a Zallet
wallet (wallet.db).
zallet init-wallet-encryption must be run before this command. In addition,
the db_dump utility (provided either by global installation or a local
source-based zcashd installation) must be available. Note that you specifically
need the db_dump utility built for BDB version 6.2.23 for greatest reliability.
The command requires at least one of the following two flag:
--path: A path to azcashdwallet file.--zcashd-datadir: A path to azcashddatadir. If this is provided, then--pathcan be relative (or omitted, in which case the default filenamewallet.datwill be used).
Additional CLI arguments:
--zcashd-install-dir: A path to a localzcashdinstallation directory, for source-based builds ofzcashd. This is used to find the installed version of thedb_dumputility, which is required for operation. If not specified, Zallet will attempt to finddb_dumpon the system path; however, it is recommended to use adb_dumpprovided via localzcashdinstallation to ensure version compatibility with thewallet.datfile.--allow-multiple-wallet-imports: An optional flag that must be set if a user wants to import keys and transactions from multiplewallet.datfiles (not required for the firstwallet.datimport.)--buffer-wallet-transactions: If set, Zallet will eagerly fetch transaction data from the chain as part of wallet migration instead of via ordinary chain sync. This may speed up wallet recovery, but requires all wallet transactions to be buffered in-memory which may cause out-of-memory errors for large wallets.--allow-warnings: If set, Zallet will ignore errors in parsing transactions extracted from thewallet.datfile. This can enable the import of key data from wallets that have been used on consensus forks of the Zcash chain.
For the Zallet alpha releases, the command also currently takes another required flag
--this-is-alpha-code-and-you-will-need-to-redo-the-migration-later.
When run, Zallet will parse the zcashd wallet file, connect to the backing
full node (to obtain necessary chain information for setting up wallet
birthdays), create Zallet accounts corresponding to the structure of the
zcashd wallet, and store the key material in the Zallet wallet. Parsing is
performed using the db_dump command-line utility, which must either be
present in the zcutil/bin directory of a zcashd source installation (as
specified via the --zcashd-install-dir argument), or avaliable on the system
$PATH.
The init-wallet-encryption command
zallet init-wallet-encryption prepares a Zallet wallet for storing key material
securely.
The command currently takes no arguments (beyond the top-level flags on zallet itself).
When run, Zallet will use the age encryption identity stored in a wallet’s datadir to
initialize the wallet’s encryption keys. The encryption identity file name (or path) can
be set with the keystore.encryption_identity config option.
WARNING: As of the latest Zallet alpha release (0.1.0-alpha.2),
zalletrequires the encryption identity file to already exist. You can generate one withrage.
Identity kinds
Zallet supports several kinds of age identities, and how zallet init-wallet-encryption
interacts with the user depends on what kind is used:
Plain (unencrypted) age identity file
In this case, zallet init-wallet-encryption will run successfully without any user
interaction.
The ability to spend funds in Zallet is directly tied to the capability to read the age identity file on disk. If Zallet is running, funds can be spent at any time.
Passphrase-encrypted identity file
In this case, zallet init-wallet-encryption will ask the user for the passphrase,
decrypt the identity, and then use it to initialize the wallet’s encryption keys.
Starting Zallet requires the capability to read the identity file on disk, but spending
funds additionally requires the passphrase. Zallet can be temporarily unlocked using the
JSON-RPC method walletpassphrase, and locked with walletlock.
WARNING: it is currently difficult to use
zallet rpcfor unlocking a Zallet wallet:zallet rpc walletpassphrase PASSPHRASEwill leak your passphrase into your terminal’s history.
Plugin identity file
age plugins will eventually be supported by
zallet init-wallet-encryption, but currently are tricky to set up and require manual database editing.
Starting Zallet requires the capability to read the plugin identity file on disk. Then,
each time a JSON-RPC method is called that requires access to specific key material, the
plugin will be called to decrypt it, and Zallet will keep the key material in memory only
as long as required to perform the operation. This can be used to control spend authority
with an external device like a YubiKey (with age-plugin-yubikey) or a KMS.
The generate-mnemonic command
zallet generate-mnemonic generates a new BIP 39 mnemonic and stores it in a Zallet
wallet.
The command takes no arguments (beyond the top-level flags on zallet itself). When run,
Zallet will generate a mnemonic, add it to the wallet, and print out its ZIP 32 seed
fingerprint (which you will use to identify it in other Zallet commands and RPCs).
$ zallet generate-mnemonic
Seed fingerprint: zip32seedfp1qhrfsdsqlj7xuvw3ncu76u98c2pxfyq2c24zdm5jr3pr6ms6dswss6dvur
Each time you run zallet generate-mnemonic, a new mnemonic will be added to the wallet.
Be careful to only run it multiple times if you want multiple independent roots of spend
authority!
The import-mnemonic command
zallet import-mnemonic enables a BIP 39 mnemonic to be imported into a Zallet wallet.
The command takes no arguments (beyond the top-level flags on zallet itself). When run,
Zallet will ask you to enter the mnemonic. It is recommended to paste the mnemonic in from
e.g. a password manager, as what you type will not be printed to the screen and thus it is
possible to make mistakes.
$ zallet import-mnemonic
Enter mnemonic:
Once the mnemonic has been provided, press Enter. Zallet will import the mnemonic, and print out its ZIP 32 seed fingerprint (which you will use to identify it in other Zallet commands and RPCs).
$ zallet import-mnemonic
Enter mnemonic:
Seed fingerprint: zip32seedfp1qhrfsdsqlj7xuvw3ncu76u98c2pxfyq2c24zdm5jr3pr6ms6dswss6dvur
The export-mnemonic command
zallet export-mnemonic enables a BIP 39 mnemonic to be exported from a Zallet wallet.
The command takes the UUID of the account for which the mnemonic should be exported. You
can obtain this from a running Zallet wallet with zallet rpc z_listaccounts.
The mnemonic is encrypted to the same age identity that the wallet uses to internally
encrypt key material. You can then use a tool like [rage] to decrypt the resulting
file.
$ zallet export-mnemonic --armor 514ab5f4-62bd-4d8c-94b5-23fa8d8d38c2 >mnemonic.age
$ echo mnemonic.age
-----BEGIN AGE ENCRYPTED FILE-----
...
-----END AGE ENCRYPTED FILE-----
$ rage -d -i path/to/encrypted-identity.txt mnemonic.age
some seed phrase ...
The add-rpc-user command
zallet add-rpc-user produces a config entry that authorizes a user to access the
JSON-RPC interface.
The command takes the username as its only argument. When run, Zallet will ask you to enter the password. It is recommended to paste the password in from e.g. a password manager, as what you type will not be printed to the screen and thus it is possible to make mistakes.
$ zallet add-rpc-user foobar
Enter password:
Once the password has been provided, press Enter. Zallet will hash the password and print out the user entry that you need to add to your config file.
$ zallet add-rpc-user foobar
Enter password:
Add this to your zallet.toml file:
[[rpc.auth]]
user = "foobar"
pwhash = "9a7e65104358b82cdd88e39155a5c36f$5564cf1836aa589f99250d7ddc11826cbb66bf9a9ae2079d43c353b1feaec445"
The rpc command
Available on crate feature
rpc-clionly.
zallet rpc lets you communicate with a Zallet wallet’s JSON-RPC interface from a
command-line shell.
zallet rpc helpwill print a list of all JSON-RPC methods supported by Zallet.zallet rpc help <method>will print out a description of<method>.zallet rpc <method>will call that JSON-RPC method. Parameters can be provided via additional CLI arguments (zallet rpc <method> <param>).
Comparison to zcash-cli
The zcashd full node came bundled with a zcash-cli binary, which served an equivalent
purpose to zallet rpc. There are some differences between the two, which we summarise
below:
zcash-cli functionality | zallet rpc equivalent |
|---|---|
zcash-cli -conf=<file> | zallet --config <file> rpc |
zcash-cli -datadir=<dir> | zallet --datadir <dir> rpc |
zcash-cli -stdin | Not implemented |
zcash-cli -rpcconnect=<ip> | rpc.bind setting in config file |
zcash-cli -rpcport=<port> | rpc.bind setting in config file |
zcash-cli -rpcwait | Not implemented |
zcash-cli -rpcuser=<user> | Not implemented |
zcash-cli -rpcpassword=<pw> | Not implemented |
zcash-cli -rpcclienttimeout=<n> | zallet rpc --timeout <n> |
| Hostname, domain, or IP address | Only IP address |
zcash-cli <method> [<param> ..] | zallet rpc <method> [<param> ..] |
For parameter parsing, zallet rpc is (as of the alpha releases) both more and less
flexible than zcash-cli:
-
It is more flexible because
zcash-cliimplements type-checking on method parameters, which means that it cannot be used with Zallet JSON-RPC methods where the parameters have changed.zallet rpccurrently lacks this, which means that:zallet rpcwill work against bothzcashdandzalletprocesses, which can be useful during the migration phase.- As the alpha and beta phases of Zallet progress, we can easily make changes to RPC methods as necessary.
-
It is less flexible because parameters need to be valid JSON:
- Strings need to be quoted in order to parse as JSON strings.
- Parameters that contain strings need to be externally quoted.
zcash-cli parameter | zallet rpc parameter |
|---|---|
null | null |
true | true |
42 | 42 |
string | '"string"' |
[42] | [42] |
["string"] | '["string"]' |
{"key": <value>} | '{"key": <value>}' |
Command-line repair tools
The zallet command-line tool comes bundled with a few commands that are specifically for
investigating and repairing broken wallet states:
The repair truncate-wallet command
If a Zallet wallet gets into an inconsistent state due to a reorg that it cannot handle
automatically, zallet start will shut down. If you encounter this situation, you can use
zallet repair truncate-wallet to roll back the state of the wallet to before the reorg
point, and then start the wallet again to catch back up to the current chain tip.
The command takes one argument: the maximum height that the wallet should know about after
truncation. Due to how Zallet represents its state internally, there may be heights that
the wallet cannot roll back to, in which case a lower height may be used. The actual
height used by zallet repair truncate-wallet is printed to standard output:
$ zallet repair truncate-wallet 3000000
2999500
Migrating from zcashd
TODO: Document how to migrate from zcashd to Zallet.
JSON-RPC altered semantics
Zallet implements a subset of the zcashd JSON-RPC wallet methods. While we
have endeavoured to preserve semantics where possible, for some methods it was
necessary to make changes in order for the methods to be usable with Zallet’s
wallet architecture. This page documents the semantic differences between the
zcashd and Zallet wallet methods.
Changed RPC methods
z_listaccounts
Changes to response:
- New
account_uuidfield.
z_getnewaccount
Changes to parameters:
- New
account_namerequired parameter. - New
seedfpoptional parameter.- This is required if the wallet has more than one seed.
z_getaddressforaccount
Changes to parameters:
accountparameter can be a UUID.
Changes to response:
- New
account_uuidfield. accountfield in response is not present if theaccountparameter is a UUID.- The returned address is now time-based if no transparent receiver is present and no explicit index is requested.
- Returns an error if an empty list of receiver types is provided along with a previously-generated diversifier index, and the previously-generated address did not use the default set of receiver types.
listaddresses
Changes to response:
imported_watchonlyincludes addresses derived from imported Unified Viewing Keys.- Transparent addresses for which we have BIP 44 derivation information are now
listed in a new
derived_transparentfield (an array of objects) instead of thetransparentfield.
getrawtransaction
Changes to parameters:
blockhashmust benullif set; single-block lookups are not currently supported.
Changes to response:
vjoinsplit,joinSplitPubKey, andjoinSplitSigfields are always omitted.
z_viewtransaction
Changes to response:
- Some top-level fields from
gettransactionhave been added:statusconfirmationsblockhash,blockindex,blocktimeversionexpiryheight, which is now always included (instead of only when a transaction has been mined).fee, which is now included even if the transaction does not spend any value from any account in the wallet, but can also be omitted if the transparent inputs for a transaction cannot be found.generated
- New
account_uuidfield on inputs and outputs (if relevant). - New
accountstop-level field, containing a map from UUIDs of involved accounts to the effect the transaction has on them. - Information about all transparent inputs and outputs (which are always visible
to the wallet) are now included. This causes the following semantic changes:
poolfield on both inputs and outputs can be"transparent".- New fields
tInandtOutPrevon inputs. - New field
tOuton outputs. addressfield on outputs: inzcashd, this was omitted only if the output was received on an account-internal address; it is now also omitted if it is a transparent output to a script that doesn’t have an address encoding. UsewalletInternalif you need to identify change outputs.memofield on outputs is omitted ifpool = "transparent".memoStrfield on outputs is no longer only omitted ifmemodoes not contain valid UTF-8.
z_listunspent
Changes to response:
- For each output in the response array:
- The
amountfield has been renamed tovaluefor consistency withz_viewtransaction. Theamountfield may be reintroduced under a deprecation flag in the future if there is user demand. - A
valueZatfield has been added for consistency withz_viewtransaction - An
account_uuidfield identifying the account that received the output has been added. - The
accountfield has been removed and there is no plan to reintroduce it; use theaccount_uuidfield instead. - An
is_watch_onlyfield has been added. - The
spendablefield has been removed; useis_watch_onlyinstead. Thespendablefield may be reintroduced under a deprecation flag in the future if there is user demand. - The
changefield has been removed, as determining whether an output qualifies as change involves a bunch of annoying subtleties and the meaning of this field has varied between Sapling and Orchard. - A
walletInternalfield has been added. - Transparent outputs are now included in the response array. The
poolfield for such outputs is set to the string"transparent". - The
memofield is now omitted for transparent outputs.
- The
z_sendmany
Changes to parameters:
feemust benullif set; ZIP 317 fees are always used.- If the
minconffield is omitted, the default ZIP 315 confirmation policy (3 confirmations for trusted notes, 10 confirmations for untrusted notes) is used.
Changes to response:
- New
txidsarray field in response. txidfield is omitted iftxidshas length greater than 1.
Omitted RPC methods
The following RPC methods from zcashd have intentionally not been implemented
in Zallet, either due to being long-deprecated in zcashd, or because other RPC
methods have been updated to replace them.
| Omitted RPC method | Use this instead |
|---|---|
createrawtransaction | To-be-implemented methods for working with PCZTs |
fundrawtransaction | To-be-implemented methods for working with PCZTs |
getnewaddress | z_getnewaccount, z_getaddressforaccount |
getrawchangeaddress | |
keypoolrefill | |
importpubkey | |
importwallet | |
settxfee | |
signrawtransaction | To-be-implemented methods for working with PCZTs |
z_importwallet | |
z_getbalance | z_getbalanceforaccount, z_getbalanceforviewingkey, getbalance |
z_getmigrationstatus | |
z_getnewaddress | z_getnewaccount, z_getaddressforaccount |
z_listaddresses | listaddresses |