A command-line tool for converting Ethereum wallets from:
to:
Caution
It is highly recommended to refrain from any operations on the validation keys and use the provided script only in critical situations to avoid any potential risks of slashing.
Binaries for the latest version of dkc can be obtained from the releases page.
dkc handles validator private key material, so it is worth checking that the archive you downloaded is the one the release workflow built. Every release archive is signed and carries build provenance.
The signature is keyless: there is no dkc public key to distribute, the signature is bound to the identity of the workflow that produced it. Each archive ships with a .cosign.bundle beside it, verified with cosign:
$ cosign verify-blob \
--bundle dkc-<version>-linux-amd64.tar.gz.cosign.bundle \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github\.com/p2p-org/dkc/' \
dkc-<version>-linux-amd64.tar.gzBuild provenance — which workflow, commit and runner produced the archive — is attested to GitHub and checked with the gh CLI:
$ gh attestation verify dkc-<version>-linux-amd64.tar.gz --repo p2p-org/dkcReleases also carry a CycloneDX SBOM (dkc-<version>.cdx.json) listing every Go module linked into the binaries, signed the same way.
If you have nix flakes enabled you can run dkc using this command:
$ nix run github:p2p-org/dkcdkc can be built locally using the command
$ go build .Caution
Before you begin, make sure you backup keys and store recovered wallets and passwords securely.
dkc uses herumi/bls-eth-go-binary. You can test dkc on predefined inputs here.
An example config for each pair could be found here
The example below shows how to convert wallets from distributed type to non-deterministic type (note: hierarchical deterministic can only be used as input - HD keys are derived from a mnemonic and cannot be imported).
base-dir for distributed wallets is located in ./i_wallet see more info about distributed wallet file structure here
base-dir for non-deterministic wallets is located in ./o_wallet see more info about non-deterministic wallet file structure here
input: #Input section
store: #Store section
path: ./i_wallet #Location of input wallets
wallet: #Wallet section
type: distributed #Type for input wallet. Valid types are: distributed, non-deterministic, hierarchical deterministic, keystore
threshold: 2 #Threshold number. It must be len(peers)/2 < threshold < len(peers)
peers: #Peers section
10: #Peer ID is used for generating bls participants
name: old1:9091 #Peer name is used for generating bls participants. All wallets for this peer are located in ./i_wallet/old1
passphrases: #Passphrases section
path: ./peer1.txt #Path to passphrases file(you can use separate passphrases file for each peer)
index: 1 #Password index in passphrases file. If not provided will use all passwords for unlocking wallets and only first password for creating accounts (Default: Using all passwords provided in passphrases file)
20:
name: old2:9091
passphrases:
path: ./peer2.txt
index: 1
30:
name: old3:9091
passphrases:
path: ./peer3.txt
index: 1
output: #Output Wallet Section
store:
path: ./o_wallet
wallet:
type: non-deterministic #Valid output types are: distributed, non-deterministic, keystore
passphrases:
path: ./o_passphrases.txt
log-level: debug #Log-level (Default: INFO)This wallet type can be used as input or output wallet. More information about this wallet type is provided here
The following is an example file structure if one were to combine threshold keys. test1, test2, test3 are each the base-dir wallet directory from the dirk instance. test1, test2, test3 are each their own wallet directory.
ethdo wallet list --base-dir ./distributed-to-nd/distributed should return nothing.
The subdirectories of wallet folder are the actual ethdo wallets:
$ ethdo wallet list --base-dir distributed-to-nd/distributed/test1
Wallet2
Wallet3
Wallet1
$ ethdo wallet list --base-dir distributed-to-nd/distributed/test2
Wallet2
Wallet3
Wallet1
$ ethdo wallet list --base-dir distributed-to-nd/distributed/test3
Wallet1
Wallet2
Wallet3
Importantly, the names of each wallet folder must correspond with the values in *.wallet.peers defined in the config.
The keys within each wallet must also have the same name
All of the keys with corresponding names (ex: name = 1) should be the threshold keys corresponding to the same composite public key.
This wallet type can be used only as input wallet. More information about this wallet type is provided here
The following is an example file structure. The base-dir wallet directory is hd-to-distributed/hd
$ ethdo wallet --base-dir hd-to-distributed/hd list
Wallet1
Wallet3
Wallet2
This wallet type can be used as input or output wallet. More information about this wallet type is provided here
The following is an example file structure. The base-dir wallet directory is nd-to-distributed/nd
$ ethdo wallet --base-dir nd-to-distributed/nd list
Wallet1
Wallet3
Wallet2
This wallet type can be used as input or output wallet. More information about this wallet type is provided here
Keystore wallets store individual keys non-deterministically in keystore format. The file structure is similar to non-deterministic wallets:
$ ethdo wallet --base-dir keystore-to-distributed/keystore list
Wallet1
Wallet2
Wallet3
Configuration for keystore wallets:
input:
store:
path: ./keystore_wallet
wallet:
type: keystore
passphrases:
path: ./keystore_passwords.txt
index: 0 # Optional: use specific password indexAfter preparing config and backing up keys simply run:
./dkc convert --config=config.yamlUseful flags:
--log-level- overrides the config log level--pprof- enables a profiling server onlocalhost:6060(off by default)
Contributions welcome. Please check out the issues.