lnd icon indicating copy to clipboard operation
lnd copied to clipboard

fail recovery channel from SCB

Open nayuta-ueno opened this issue 2 years ago • 14 comments

Background

Recovery from SCB returned an error.

  • channels
    • 68cbece3cbd24e5c5203492ec0a7e8c1b4d6b15112c2614b44e57ed865fce2a4:0 (zero-conf)
    • d59aae26e80b71851093c2c103b4a171f3d40e1b3d0a42f36e72518e194f4aa7:1 (zero-conf)
[ERR] RPCS: [/lnrpc.Lightning/RestoreChannelBackups]: unable to unpack chan backup: could not derive private key for legacy channel revocation root format: unable to derive private key
[INF] CHBU: Restoring ChannelPoint(68cbece3cbd24e5c5203492ec0a7e8c1b4d6b15112c2614b44e57ed865fce2a4:0) to disk: 
[DBG] LTND: Using legacy revocation producer format for channel point 68cbece3cbd24e5c5203492ec0a7e8c1b4d6b15112c2614b44e57ed865fce2a4:0

Your environment

  • LND v0.15.2-beta
  • Android(Lndmobile.aar)
  • Neutrino mainnet
  • zero-conf channels

Steps to reproduce

  1. create two zero-conf channels
  2. (fundee) channel backup REST API /v1/channels/backup
  3. recovery wallet by passphrase
  4. restore REST API /v1/channels/backup/restore

Expected behaviour

Both channels are force closed from funder.

Actual behaviour

An error occurred and only one channel was closed. (It has only occurred once.)

nayuta-ueno avatar Oct 28 '22 09:10 nayuta-ueno

@nayuta-ueno were both channels already confirmed?

positiveblue avatar Oct 28 '22 09:10 positiveblue

I deleted the wallet so I don't have much data left, but I think it was these two channels. I think they were confirmed because it was a few hours ago.

https://mempool.space/ja/tx/68cbece3cbd24e5c5203492ec0a7e8c1b4d6b15112c2614b44e57ed865fce2a4:0 https://mempool.space/ja/tx/0:d59aae26e80b71851093c2c103b4a171f3d40e1b3d0a42f36e72518e194f4aa7

nayuta-ueno avatar Oct 28 '22 09:10 nayuta-ueno

I do not know if it was confirmed when the channel backup was done.

nayuta-ueno avatar Oct 28 '22 09:10 nayuta-ueno

When you say It has only occurred once. Does that mean that you were able to successfully force close the channel in a later retry?

If you had more channels (ZC or not) did they all close successfully but this one?

positiveblue avatar Oct 28 '22 10:10 positiveblue

I just tried to reproduce this and wasn't able to with 0.15.3. Could you please dump the raw bytes you sent as multi_chan_backup in /v1/channels/backup/restore into a file and use chantools dumpbackup --multi_file <the_file> to dump the backup and then post the content here? I'm mostly interested in the ShaChanRootDesc which should look similar to this:

   ShaChainRootDesc: (dump.KeyDescriptor) {
    Path: (string) (len=17) "m/1017'/1'/5'/0/7",
    PubKey: (string) (len=5) "<nil>"
   }

guggero avatar Oct 28 '22 10:10 guggero

When you say It has only occurred once. Does that mean that you were able to successfully force close the channel in a later retry?

I have tried several times to recovery with the same channel backup data and the same error was returned. Errors always occur with this backup data. The same procedure (create an another zero-conf channel, backup and recover) succeeded.

nayuta-ueno avatar Oct 31 '22 07:10 nayuta-ueno

use chantools dumpbackup

chantools returns error.

Error: could not extract multi file: chacha20poly1305: message authentication failed

nayuta-ueno avatar Oct 31 '22 07:10 nayuta-ueno

Error: could not extract multi file: chacha20poly1305: message authentication failed

That's usually the message you get when the seed doesn't match the channel backup.

guggero avatar Oct 31 '22 07:10 guggero

That's usually the message you get when the seed doesn't match the channel backup.

Sorry, I was using chantool incorrectly. I deleted all the LND data and then recovered, so I don't have the channel.backup from before the recovery. (I only have 24 words and multi_chan_backup base64 string.)

nayuta-ueno avatar Oct 31 '22 08:10 nayuta-ueno

you should be able to convert the multi_chan_backup thing using echo <multi_chan_backup_string> | base64 -d > channel.backup and then dump it with chantools. Was the backup string created with a vanilla/original lnd or a fork? Did you do anything with the backup that you extracted (e.g. decrypt/de-serialize it and then serialize it again)? Or just store the content in a file for recovery later on?

guggero avatar Oct 31 '22 08:10 guggero

chantools dumpbackup output:

(dump.BackupMulti) {
 Version: (chanbackup.MultiBackupVersion) 0,
 StaticBackups: ([]dump.BackupSingle) (len=2 cap=2) {
  (dump.BackupSingle) {
   Version: (chanbackup.SingleBackupVersion) 3,
   IsInitiator: (bool) false,
   ChainHash: (string) (len=64) "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
   FundingOutpoint: (string) (len=66) "68cbece3cbd24e5c5203492ec0a7e8c1b4d6b15112c2614b44e57ed865fce2a4:0",
   ShortChannelID: (lnwire.ShortChannelID) 760345:752:0,
   RemoteNodePub: (string) (len=66) "0381cb801ca3ed432b067b2f4db840ba6c1462782f043b86ecf60a3b1666ad13c5",
   Addresses: ([]net.Addr) (len=1 cap=1) {
    (*net.TCPAddr)(0xc000488630)(3.115.41.6:9735)
   },
   Capacity: (btcutil.Amount) 0.00024 BTC,
   LocalChanCfg: (dump.ChannelConfig) {
    ChannelConstraints: (channeldb.ChannelConstraints) {
     DustLimit: (btcutil.Amount) 0 BTC,
     ChanReserve: (btcutil.Amount) 0 BTC,
     MaxPendingAmount: (lnwire.MilliSatoshi) 0 mSAT,
     MinHTLC: (lnwire.MilliSatoshi) 0 mSAT,
     MaxAcceptedHtlcs: (uint16) 0,
     CsvDelay: (uint16) 144
    },
    MultiSigKey: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    RevocationBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/1'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    PaymentBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/3'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    DelayBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/4'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    HtlcBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/2'/0/1",
     PubKey: (string) (len=5) "<nil>"
    }
   },
   RemoteChanCfg: (dump.ChannelConfig) {
    ChannelConstraints: (channeldb.ChannelConstraints) {
     DustLimit: (btcutil.Amount) 0 BTC,
     ChanReserve: (btcutil.Amount) 0 BTC,
     MaxPendingAmount: (lnwire.MilliSatoshi) 0 mSAT,
     MinHTLC: (lnwire.MilliSatoshi) 0 mSAT,
     MaxAcceptedHtlcs: (uint16) 0,
     CsvDelay: (uint16) 4032
    },
    MultiSigKey: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "032b8ff881e22f8e18cb018176d048c665c71f3b365e99241fa071b98435ae0bf1"
    },
    RevocationBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "0239fd8f7d1cf08cf11c8064eae63654b86b2d3d6b8555359c2e8745452b89e58f"
    },
    PaymentBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "03f672a5d81bcbb3c989c07e48c99e625c42d4835c70b8a2cda6847e869b4f3dae"
    },
    DelayBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "0386075b368abd7c3e9205c32872b3f1c97117974c4080f4159a4dfd4cc370c87b"
    },
    HtlcBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "02822adbf2915d7af576d9f536ddf4208d6cd6912c610912548629775c069343de"
    }
   },
   ShaChainRootDesc: (dump.KeyDescriptor) {
    Path: (string) (len=17) "m/1017'/0'/5'/0/0",
    PubKey: (string) (len=66) "03c655dd1d8b6554295022530f29052468d1bda94ecd5768e42dee83195ef4c176"
   }
  },
  (dump.BackupSingle) {
   Version: (chanbackup.SingleBackupVersion) 3,
   IsInitiator: (bool) false,
   ChainHash: (string) (len=64) "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
   FundingOutpoint: (string) (len=66) "d59aae26e80b71851093c2c103b4a171f3d40e1b3d0a42f36e72518e194f4aa7:1",
   ShortChannelID: (lnwire.ShortChannelID) 760512:0:0,
   RemoteNodePub: (string) (len=66) "0381cb801ca3ed432b067b2f4db840ba6c1462782f043b86ecf60a3b1666ad13c5",
   Addresses: ([]net.Addr) (len=1 cap=1) {
    (*net.TCPAddr)(0xc000488690)(3.115.41.6:9735)
   },
   Capacity: (btcutil.Amount) 0.00025 BTC,
   LocalChanCfg: (dump.ChannelConfig) {
    ChannelConstraints: (channeldb.ChannelConstraints) {
     DustLimit: (btcutil.Amount) 0 BTC,
     ChanReserve: (btcutil.Amount) 0 BTC,
     MaxPendingAmount: (lnwire.MilliSatoshi) 0 mSAT,
     MinHTLC: (lnwire.MilliSatoshi) 0 mSAT,
     MaxAcceptedHtlcs: (uint16) 0,
     CsvDelay: (uint16) 144
    },
    MultiSigKey: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=5) "<nil>"
    },
    RevocationBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/1'/0/0",
     PubKey: (string) (len=5) "<nil>"
    },
    PaymentBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/3'/0/0",
     PubKey: (string) (len=5) "<nil>"
    },
    DelayBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/4'/0/0",
     PubKey: (string) (len=5) "<nil>"
    },
    HtlcBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/2'/0/0",
     PubKey: (string) (len=5) "<nil>"
    }
   },
   RemoteChanCfg: (dump.ChannelConfig) {
    ChannelConstraints: (channeldb.ChannelConstraints) {
     DustLimit: (btcutil.Amount) 0 BTC,
     ChanReserve: (btcutil.Amount) 0 BTC,
     MaxPendingAmount: (lnwire.MilliSatoshi) 0 mSAT,
     MinHTLC: (lnwire.MilliSatoshi) 0 mSAT,
     MaxAcceptedHtlcs: (uint16) 0,
     CsvDelay: (uint16) 4032
    },
    MultiSigKey: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "024cdae1b008449a8d5b7def640fe8070cd538087fa55734cbb371e3dce00b3c7a"
    },
    RevocationBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "03da4d28a0ee573eb81210462fec32eb4bb35afa26825dde993938deb025f490eb"
    },
    PaymentBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "03cb6cffcfd5e640e1654ce2aeb2fef53708ee4f9dab61845e4c05711cc2494d4d"
    },
    DelayBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "02c2e6becb4e28466bef9afd8dd0ad9f622f5a7de1a015a140569c2e19a2861e07"
    },
    HtlcBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/0'/0'/0/0",
     PubKey: (string) (len=66) "03a591dc56b071e67da587e8e864c483062be32b09f8df77dd718d64798d7291e6"
    }
   },
   ShaChainRootDesc: (dump.KeyDescriptor) {
    Path: (string) (len=17) "m/1017'/0'/5'/0/1",
    PubKey: (string) (len=5) "<nil>"
   }
  }
 }
}

nayuta-ueno avatar Oct 31 '22 09:10 nayuta-ueno

Was the backup string created with a vanilla/original lnd or a fork?

I have made some modifications to LND v0.15.2-beta, but not to the backup string. The backup string is a multi_chan_backup obtained with the REST API '/v1/channels/backup' and stored in JSON format.

nayuta-ueno avatar Oct 31 '22 09:10 nayuta-ueno

Can you describe the modifications you made?

Because the issue is (as I expected) that the pubkey here is not nil:

   ShaChainRootDesc: (dump.KeyDescriptor) {
    Path: (string) (len=17) "m/1017'/0'/5'/0/0",
    PubKey: (string) (len=66) "03c655dd1d8b6554295022530f29052468d1bda94ecd5768e42dee83195ef4c176"
   }

But that should only happen if the channel is created by an lnd version before 0.14. Which I don't think can be the case given the channel was only created a few hundred blocks ago? Or did you upgrade that node from an old software? Or did your modifications change anything in the key derivation logic?

guggero avatar Oct 31 '22 10:10 guggero

The channel-related modification is that ChanReserve can now be set to zero. The node was recently created in v0.15.2-beta, the key derivation logic has not been changed. I will check to see if any other modifications have been made.

nayuta-ueno avatar Oct 31 '22 23:10 nayuta-ueno

Converting this to a discussion, feel free to convert back to the issue if any specific action is required

saubyk avatar Dec 14 '22 16:12 saubyk