This tool provides helper functions that can be used to rescue funds locked in
lnd channels in case
lnd itself cannot run properly anymore.
WARNING: This tool was specifically built for a certain rescue operation and might not be well-suited for your use case. Or not all edge cases for your needs are coded properly. Please look at the code to understand what it does before you use it for anything serious.
WARNING 2: This tool will query public block explorer APIs for some
commands, your privacy might not be preserved. Use at your own risk or supply
a private API URL with
The easiest way to install
chantools is to download a pre-built binary for
your operating system and architecture.
Example (make sure you always use the latest version!):
$ cd /tmp $ wget -O chantools.tar.gz https://github.com/guggero/chantools/releases/download/v0.7.1/chantools-linux-amd64-v0.7.1.tar.gz $ tar -zxvf chantools.tar.gz $ sudo mv chantools-*/chantools /usr/local/bin/
If there isn't a pre-built binary for your operating system or architecture
available or you want to build
chantools from source for another reason, you
need to make sure you have
go 1.13.x (or later) and
make installed and can
then run the following commands:
git clone https://github.com/guggero/chantools.git cd chantools make install
The following flow chart shows the main recovery scenario this tool was built
for. This scenario assumes that you do have access to the crashed node's seed,
channel.backup file and some state of a
channel.db file (perhaps from a
file based backup or the recovered file from the crashed node).
Following this guide will help you get your channel funds back! The channels themselves can't be restored to work normally unless step 1 is successful ( compacting the DB).
Node crashed: For some reason your
lnd node crashed and isn't starting
anymore. If you get errors similar to
this, it is possible
that a simple compaction (a full copy in safe mode) can solve your problem.
If that doesn't work and you need to continue the recovery, make sure you can at least extract the
channel.backup file and if somehow possible any version
channel.db from the node.
Whatever you do, do never, ever replace your
channel.db file with an old
version (from a file based backup) and start your node that way.
Read this explanation why that can lead to loss of funds.
Rescue on-chain balance: To start the recovery process, we are going to
re-create the node from scratch. To make sure we don't overwrite any old data
in the process, make sure the old data directory of your node (usually
in the user's home directory) is safely moved away (or the whole folder
renamed) before continuing.
To start the on-chain recovery, follow the sub step "Starting On-Chain Recovery" of this guide. Don't follow the whole guide, only this single chapter!
This step is completed once the
lncli getinfo command shows both
"synced_to_chain": true and
"synced_to_graph": true which can take several
hours depending on the speed of your hardware. Do not be alarmed that the
lncli getinfo command shows 0 channels. This is normal as we haven't started
the off-chain recovery yet.
Recover channels using SCB: Now that the node is fully synced, we can try
to recover the channels using the Static Channel Backups (SCB).
For this, you need a file called
channel.backup. Simply run the command
lncli restorechanbackup --multi_file <path-to-your-channel.backup>. This
will take a while!. The command itself can take several minutes to complete,
depending on the number of channels. The recovery can easily take a day or
two as a lot of chain rescanning needs to happen. It is recommended to wait at
least one full day. You can watch the progress with the
command. If the list is empty, congratulations, you've recovered all channels!
If the list stays un-changed for several hours, it means not all channels
could be restored using this method.
One explanation can be found here.
Install chantools: To try to recover the remaining channels, we are going
chantools. Simply follow the installation instructions.
The recovery can only be continued if you have access to some version of the
channel.db. This could be the latest state as recovered from
the crashed file system, or a version from a regular file based backup. If you
do not have any version of a channel DB,
chantools won't be able to help
with the recovery. See step 11 for some possible manual steps.
Create copy of channel DB: To make sure we can read the channel DB, we
are going to create a copy in safe mode (called compaction). Simply run
chantools compactdb --sourcedb <recovered-channel.db> --destdb ./results/compacted.db
We are going to assume that the compacted copy of the channel DB is located in
./results/compacted.db in the following commands.
chantools summary: First,
chantools needs to find out the state of each
channel on chain. For this, a blockchain API (by default blockstream.info)
is queried. The result will be written to a file called
./results/summary-yyyy-mm-dd.json. This result file will be needed for the
chantools --fromchanneldb ./results/compacted.db summary
chantools rescueclosed: It is possible that by now the remote peers have
force-closed some of the remaining channels. What we now do is try to find the
private keys to sweep our balance of those channels. For this we need a shared
secret which is called the
commit_point and is changed whenever a channel is
updated. We do have the latest known version of this point in the channel DB.
The following command tries to find all private keys for channels that have
been closed by the other party. The command needs to know what channels it is
operating on, so we have to supply the
summary-yyy-mm-dd.json created by the
chantools --fromsummary ./results/<summary-file-created-in-last-step>.json rescueclosed --channeldb ./results/compacted.db
This will create a new file called
which will contain any found private keys and will also be needed for the next
bitcoind or Electrum Wallet to sweep all of the private keys.
chantools forceclose: This command will now close all channels that
chantools thinks are still open. This is achieved by publishing the latest
known channel state of the
Please read the full warning text of the
forceclose command below as this command can put
your funds at risk if the state in the channel DB is not the most recent
one. This command should only be executed for channels where the remote peer
is not online anymore.
chantools --fromsummary ./results/<rescueclosed-file-created-in-last-step>.json forceclose --channeldb ./results/compacted.db --publish
This will create a new file called
which will be needed for the next command.
If you get the error
non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation), you might be affected by an old bug
lnd that was fixed in the meantime. But it means the signature in the
force-close transaction is invalid and needs to be fixed. There is a guide
on how to do exactly that here.
Wait for timelocks: The previous command closed the remaining open
channels by publishing your node's state of the channel. By design of the
Lightning Network, you now have to wait until the channel funds belonging to
you are not time locked any longer. Depending on the size of the channel, you
have to wait for somewhere between 144 and 2000 confirmations of the
force-close transactions. Only continue with the next step after the channel
with the highest
csv_delay has reached that many confirmations of its
closing transaction. You can check this by looking up each force closed
channel transaction on a block explorer (like
blockstream.info for example). Open the result
JSON file of the last command (
look up every TXID in
"force_close" -> "txid" on the explorer. If the number
of confirmations is equal to or greater to the value shown in
"force_close" -> "csv_delay" for each of the channels, you can proceed.
chantools sweeptimelock: Once all force-close transactions have reached
the number of transactions as the
csv_timeout in the JSON demands, these
time locked funds can now be swept. Use the following command to sweep all the
channel funds to an address of your wallet:
chantools --fromsummary ./results/<forceclose-file-created-in-last-step>.json sweeptimelock --publish --sweepaddr <bech32-address-from-your-wallet>
Manual intervention necessary: You got to this step because you either
don't have a
channel.db file or because
chantools couldn't rescue all your
node's channels. There are a few things you can try manually that have some
chance of working:
lncli connect <node-pubkey>@<updated-ip-address>:<port>in the recovered
lndnode from step 3 and wait a few hours to see if the channel is now being force closed by the remote node.
lnd v0.8.0and later), the funds can be swept by your node.
Use Zombie Channel Recovery Matcher: As a final, last resort, you can
go to node-recovery.com and register your
node's ID for being matched up against other nodes with the same problem.
Once you were contacted with a match, follow the instructions on the Zombie Channel Recovery Guide page.
All commands that require the seed (and, if set, the seed's passphrase) offer three distinct possibilities to specify it:
--noseedbackupflag and extracted the
xprvfrom the wallet database with the
walletinfocommand. Those users can specify the master root key by passing the
--rootkeycommand line flag to each command that requires the seed.
chantoolsby removing the need to type into the terminal. There are three environment variables that can be set to skip entering values through the terminal:
AEZEED_MNEMONIC: Specifies the 24 word
AEZEED_PASSPHRASE: Specifies the passphrase for the aezeed. If no passphrase was used during the creation of the seed, the special value
AEZEED_PASSPHRASE="-"needs to be passed to indicate no passphrase should be used or read from the terminal.
WALLET_PASSWORD: Specifies the encryption password that is needed to access a
wallet.dbfile. This is currently only used by the
Example using environment variables:
# We add a space in front of each command to tell bash we don't want this # command stored in the history. $ export AEZEED_MNEMONIC="abandon able ... ... ..." # We didn't set a passphrase for this example seed, we need to indicate this by # passing in a single dash character. $ export AEZEED_PASSPHRASE="-" $ chantools showrootkey 2020-10-29 20:22:42.329 [INF] CHAN: chantools version v0.6.0 commit v0.6.0-3 Your BIP32 HD root key is: xprv9s21ZrQH1...
This tool provides helper functions that can be used rescue funds locked in lnd channels in case lnd itself cannot run properly anymore. Complete documentation is available at https://github.com/guggero/chantools/. Usage: chantools [command] Available Commands: chanbackup Create a channel.backup file from a channel database compactdb Create a copy of a channel.db file in safe/read-only mode derivekey Derive a key with a specific derivation path dropchannelgraph Remove all graph related data from a channel DB dumpbackup Dump the content of a channel.backup file dumpchannels Dump all channel information from an lnd channel database filterbackup Filter an lnd channel.backup file and remove certain channels fixoldbackup Fixes an old channel.backup file that is affected by the lnd issue #3881 (unable to derive shachain root key) forceclose Force-close the last state that is in the channel.db provided genimportscript Generate a script containing the on-chain keys of an lnd wallet that can be imported into other software like bitcoind help Help about any command migratedb Apply all recent lnd channel database migrations removechannel Remove a single channel from the given channel DB rescueclosed Try finding the private keys for funds that are in outputs of remotely force-closed channels rescuefunding Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the initiator of the channel needs to run showrootkey Extract and show the BIP32 HD root key from the 24 word lnd aezeed signrescuefunding Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the remote node (the non-initiator) of the channel needs to run summary Compile a summary about the current state of channels sweeptimelock Sweep the force-closed state after the time lock has expired sweeptimelockmanual Sweep the force-closed state of a single channel manually if only a channel backup file is available vanitygen Generate a seed with a custom lnd node identity public key that starts with the given prefix walletinfo Shows info about an lnd wallet.db file and optionally extracts the BIP32 HD root key Flags: -h, --help help for chantools -r, --regtest Indicates if regtest parameters should be used -t, --testnet Indicates if testnet parameters should be used Use "chantools [command] --help" for more information about a command.
Detailed documentation for each sub command is available in the docs folder.