11

I lost my Lnd channel backup in a boating accident, but I still have the mnemonic. I had one freshly opened channel to my own c-lightning node which has never been used. I force closed the channel from the c-lightning side.

The funds are now in a P2WPKH address which I've imported into Bitcoin Core. Now I need to figure out the private key in order to sweep it. This requires knowing how to derive it, and then how to dump it.

There is wallet utility pull request that I could tweak in order to dump the key I need once I know how to derive it.

I also know the funding transaction id, the channel id and all the other channel details c-lightning spits out with listfunds.

Key derivation is explained in derivation.go:

m/1017'/coinType'/keyFamily'/0/index

The coinType for Bitcoin is 0, and I believe the key family is 3: "used in scripts that pay directly to us without any delay"

My guess is that the index is 0 since it was the first channel.

BOLT 3 then explains how to do tweak this key for a commitment transaction. Because the other side closed this channel, I need the to_remote output, which requires the remotepubkey.

pubkey = basepoint + SHA256(per_commitment_point || basepoint) * G 
privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)

The spec says "the remotepubkey uses the remote node's payment_basepoint". Is that the key we just derived?

So that leaves the per_commitment_point to be figured out, derived from per_commitment_secret. I'm confused where that comes from.

What Go incantation should I use to dump the private key I'm looking for?

1
  • 2
    I cannot give you the answer out of the box but I wanted to chase down that logic since a long time. if noone finds the answer I would volunteer to have a hangout (or what ever service you prefer) to chase down the derrivation of the the per_commit secretes from the mnemonic seed Sep 3, 2019 at 18:51

2 Answers 2

5

In c-lightning the per commitment point is derived from the hsm_secret using per_commit_point from:

  1. The node id / public key (can be found with listfunds, until the channel is purged some time after closing)
  2. The channel database id (can be found with cat ~/.lightning/debug.log | grep $node_id), shown as chan #$node_id
  3. The commitment number (0 when opening the channel, increases during fee negotiations and other channel updates, | grep commit)

We care about the commitment point, not the secret, because the private key we need is derived as follows:

privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)

I created a tool for c-lightning to dump this point (with help from @cdecker): https://github.com/ElementsProject/lightning/pull/3115

Back to Lnd... One easy place to dump a private key is in the lnwallet walletinfo command. Add something like this to commands.go:

basePointSecret, err := keyRing.DerivePrivKey(keychain.KeyDescriptor{
  KeyLocator: keychain.KeyLocator{
    Family: keychain.KeyFamilyPaymentBase,
    Index:  0, // Same commitment index as used in c-lightning 
  },
})

// Parse per_commitment_point dumped by c-lightning:
bytes, err := hex.DecodeString(".........")
commitPoint, err := btcec.ParsePubKey(bytes, btcec.S256())

// Obtain SHA256(per_commitment_point || basepoint)
h := sha256.New()
h.Write(commitPoint.SerializeCompressed())
h.Write(basePointSecret.PubKey().SerializeCompressed())
commitTweak := h.Sum(nil)

// Obtain and dump private key
tweakedPrivkey := TweakPrivKey(basePointSecret, commitTweak)
commitmentWif, err := btcutil.NewWIF(tweakedPrivkey, &chaincfg.MainNetParams, true)

fmt.Printf("Dump commitment priv key: %s\n", commitmentWif)

Where TweakPrivKey() is lifted from script_utils.

Import the resulting private key into Bitcoin Core et voila!

0
0

So that leaves the per_commitment_point to be figured out, derived from per_commitment_secret. I'm confused where that comes from.

This comes from the open_channel/accept_channel messages in BOLT2. It is called first_per_commitment_point.

Unfortunately I can't find where you can access this on lnd documentation.

An idea: Look at the lnd backup documentation and try to figure out how that works. per_commitment_secret must be backed up to trustlessly redeem funds. Since it looks like they do not offer easy access to that, you may be able to find it in the backup information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.