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
zcashd
have 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 = true
andvalidator_cookie_path
(if using cookie auth)validator_user
andvalidator_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 start
zallet example-config
zallet migrate-zcash-conf
zallet migrate-zcashd-wallet
zallet init-wallet-encryption
zallet generate-mnemonic
zallet import-mnemonic
zallet export-mnemonic
zallet rpc
zallet repair
subcommands
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-import
only.
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 azcashd
configuration file.--zcashd-datadir
: A path to azcashd
datadir. If this is provided, then--path
can be relative (or omitted, in which case the default filenamezcash.conf
will 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-import
only.
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 azcashd
wallet file.--zcashd-datadir
: A path to azcashd
datadir. If this is provided, then--path
can be relative (or omitted, in which case the default filenamewallet.dat
will be used).
Additional CLI arguments:
--zcashd-install-dir
: A path to a localzcashd
installation directory, for source-based builds ofzcashd
. This is used to find the installed version of thedb_dump
utility, which is required for operation. If not specified, Zallet will attempt to finddb_dump
on the system path; however, it is recommended to use adb_dump
provided via localzcashd
installation to ensure version compatibility with thewallet.dat
file.--allow-multiple-wallet-imports
: An optional flag that must be set if a user wants to import keys and transactions from multiplewallet.dat
files (not required for the firstwallet.dat
import.)--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.dat
file. 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: For the first Zallet alpha release (0.1.0-alpha.1),
zallet
requires 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 rpc
for unlocking a Zallet wallet:zallet rpc walletpassphrase PASSPHRASE
will 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 rpc
command
Available on crate feature
rpc-cli
only.
zallet rpc
lets you communicate with a Zallet wallet's JSON-RPC interface from a
command-line shell.
zallet rpc help
will 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-cli
implements type-checking on method parameters, which means that it cannot be used with Zallet JSON-RPC methods where the parameters have changed.zallet rpc
currently lacks this, which means that:zallet rpc
will work against bothzcashd
andzallet
processes, 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_uuid
field.
z_getnewaccount
Changes to parameters:
- New
account_name
required parameter. - New
seedfp
optional parameter.- This is required if the wallet has more than one seed.
z_getaddressforaccount
Changes to parameters:
account
parameter can be a UUID.
Changes to response:
- New
account_uuid
field. account
field in response is not present if theaccount
parameter 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_watchonly
includes addresses derived from imported Unified Viewing Keys.- Transparent addresses for which we have BIP 44 derivation information are now
listed in a new
derived_transparent
field (an array of objects) instead of thetransparent
field.
getrawtransaction
Changes to parameters:
blockhash
must benull
if set; single-block lookups are not currently supported.
Changes to response:
vjoinsplit
,joinSplitPubKey
, andjoinSplitSig
fields are always omitted.
z_viewtransaction
Changes to response:
- Some top-level fields from
gettransaction
have been added:status
confirmations
blockhash
,blockindex
,blocktime
version
expiryheight
, 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_uuid
field on inputs and outputs (if relevant). - New
accounts
top-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:
pool
field on both inputs and outputs can be"transparent"
.- New fields
tIn
andtOutPrev
on inputs. - New field
tOut
on outputs. address
field 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. UsewalletInternal
if you need to identify change outputs.memo
field on outputs is omitted ifpool = "transparent"
.memoStr
field on outputs is no longer only omitted ifmemo
does not contain valid UTF-8.
z_listunspent
Changes to response:
- For each output in the response array:
- The
amount
field has been renamed tovalue
for consistency withz_viewtransaction
. Theamount
field may be reintroduced under a deprecation flag in the future if there is user demand. - A
valueZat
field has been added for consistency withz_viewtransaction
- An
account_uuid
field identifying the account that received the output has been added. - The
account
field has been removed and there is no plan to reintroduce it; use theaccount_uuid
field instead. - An
is_watch_only
field has been added. - The
spendable
field has been removed; useis_watch_only
instead. Thespendable
field may be reintroduced under a deprecation flag in the future if there is user demand. - The
change
field 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
walletInternal
field has been added. - Transparent outputs are now included in the response array. The
pool
field for such outputs is set to the string"transparent"
. - The
memo
field is now omitted for transparent outputs.
- The
z_sendmany
Changes to parameters:
fee
must benull
if set; ZIP 317 fees are always used.- If the
minconf
field is omitted, the default ZIP 315 confirmation policy (3 confirmations for trusted notes, 10 confirmations for untrusted notes) is used.
Changes to response:
- New
txids
array field in response. txid
field is omitted iftxids
has 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 |