lightninglabs / chantools

A loose collection of tools all somehow related to lnd and Lightning Network channels.
MIT License
222 stars 32 forks source link

chantools scbforceclose: extract close tx from SCB and sign it #95

Open starius opened 11 months ago

starius commented 11 months ago

This is part of https://github.com/lightningnetwork/lnd/issues/7658#issuecomment-1774983789 implementation.

This PR depends on https://github.com/lightningnetwork/lnd/pull/8183 (Field chanbackup.Single.CloseTxInputs is needed.)

I added chantools scbforceclose command, which extracts closing tx from SCB and signs it and optionally broadcasts.

New command:

chantools scbforceclose --help
$ chantools scbforceclose --help

If you are certain that a node is offline for good (AFTER you've tried SCB!)
and a channel is still open, you can use this method to force-close your
latest state that you have in your channel.db.

**!!! WARNING !!! DANGER !!! WARNING !!!**

If you do this and the state that you publish is *not* the latest state, then
the remote node *could* punish you by taking the whole channel amount *if* they
come online before you can sweep the funds from the time locked (144 - 2000
blocks) transaction *or* they have a watch tower looking out for them.

**This should absolutely be the last resort and you have been warned!**

Usage:
  chantools scbforceclose [flags]

Examples:
chantools scbforceclose --multi_file channel.backup

Flags:
      --apiurl string          API URL to use (must be esplora compatible) (default "https://blockstream.info/api")
      --bip39                  read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag
  -h, --help                   help for scbforceclose
      --multi_backup string    a hex encoded multi-channel backup obtained from exportchanbackup for force-closing channels
      --multi_file string      the path to a single-channel backup file (channel.backup)
      --publish                publish force-closing TX to the chain API instead of just printing the TX
      --rootkey string         BIP32 HD root key of the wallet to use for decrypting the backup and signing tx; leave empty to prompt for lnd 24 word aezeed
      --single_backup string   a hex encoded single channel backup obtained from exportchanbackup for force-closing channels
      --single_file string     the path to a single-channel backup file

Global Flags:
  -r, --regtest   Indicates if regtest parameters should be used
  -s, --signet    Indicates if the public signet parameters should be used
  -t, --testnet   Indicates if testnet parameters should be used

The command extracts closing transactions from SCB, signs and prints them.

Example

Here is an example of using chantools scbforceclose in testnet to sign a force closing transaction and to publish it.

chantools --testnet scbforceclose --single_backup xxx --publish --apiurl https://blockstream.info/testnet/api
$ chantools --testnet scbforceclose --single_backup xxx --publish --apiurl https://blockstream.info/testnet/api
2023-12-31 18:06:04.121 [INF] CHAN: chantools version v0.12.1 commit 
Input your 24-word mnemonic separated by spaces: ***

Input your cipher seed passphrase (press enter if your seed doesn't have a passphrase): 

Found 1 channel backups, 1 of them have close tx.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
If you are certain that a node is offline for good (AFTER you've tried SCB!)
and a channel is still open, you can use this method to force-close your
latest state that you have in your channel.db.

**!!! WARNING !!! DANGER !!! WARNING !!!**

If you do this and the state that you publish is *not* the latest state, then
the remote node *could* punish you by taking the whole channel amount *if* they
come online before you can sweep the funds from the time locked (144 - 2000
blocks) transaction *or* they have a watch tower looking out for them.

**This should absolutely be the last resort and you have been warned!**
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Type YES to proceed: YES
Signed transactions will be broadcasted automatically.
Type YES again to proceed: YES
xxx:0
xxxxxxxx

2023-12-31 18:06:28.796 [INF] CHAN: Published TX xxx, response: xxx

(To use --publish option in testnet, --apiurl should be adjusted. Hopefully newExplorerAPI from https://github.com/lightninglabs/chantools/pull/107 is fixing this.)

TODOs

starius commented 9 months ago

I updated both PRs. Signing now happens in chantools scbforceclose. I put an example into PR's description.

@guggero Please take another look!

starius commented 4 months ago

Rebased for LND 0.18, fixed lint warnings. Tested manually.

starius commented 3 months ago

Thanks for the review! I updated LND PR and this PR. The code producing signed transaction was moved to LND to use it in itest.

starius commented 3 months ago

I rebased the PR, updated go replace to the latest https://github.com/lightningnetwork/lnd/pull/8183 version.

Also included the suggested patch for dump.go. @guggero I put your name in Author field of the commit.

starius commented 4 weeks ago

I moved function SignCloseTx from LND's PR https://github.com/lightningnetwork/lnd/pull/8183 here. Added unit tests validating that the produced transaction has a valid signature.

ziggie1984 commented 2 weeks ago

testvector for custom channels

{
    "chan_point": "85619671cb1987c80723ed457fd829f6444269a4d228ab580cd8f2176f1d4994:0",
    "chan_backup": "cbb0bde3b8a7a1158fc2186290c980862a384027bc6d7c8e82b4ae48b9294abe200cef8f07a96703981e573aa6b5e78f9b07dd240592e281f8e4027654c47ae612863d3c52d8f7ae33f433ceef26093179d5cfce3d11495f92542ad935953a97bd0454e76276494c24bab9448985103db9fbc1dea3bb8408f2b7fd0987745c3c3343f51c1285288e0248bb9288a0f65067225640281c97b201c29f9a8ff55d81f727484adeb58e4985559b1790fe23257bcf50186cb69e3dcc21f33131232e6510b080cdbc934d0c5d6c7283a1666e319a5f1862f5e285f22e06d9f35750a35778bae69c790fa2332caac18a25086d9f0500071214606f1a350bddc083a34b4362e3e70fe0c4063d3230387495e88570564dfa242ccfceb1d607f64726a0545066c482b73d00d6ac3e5527bd0aba6c8f79d2268c1824bda5ac9f48a7e545057f41cda74b9bcc0823c3357b85feb219ebf3bbe4b1d2c936b1589ab86f9e88f54f4893a31c1bd6ef1f71a59403a5daf51ed922350be2885b3604dd689e75c24f9d7c028138d9428a23b8f2cb624fa0ac2f6f4cbd9b36121f8bd20130c166a419cace33ba17dc03547e8679a319cd2d7061653559939e74e5e848dc62e1c051b1efe2a26f8f7028a1bedf2ddebfa7b89e4bd5a09248be62ea0a636c144ca80c9531dda69ea4007931105fe48348422b8eaea139d98ed34e5c805cc672f58c534bf44e64a2716fe3755700b704faae45e37c164f8ac286f99a903fdf70c781b043df7607c3bd662a6ae5e31e99f0ebc517c966d5d0bbc045110150b5b75d64a2bf2c6442a8cb6d91f0b88188094e6bc3bbc619bbfe7513c970ea4d538903cd3c1cb03962d813dcbb1573a544f0f2b45e780222ea3b998fc4e9d1a60aa0ac6ac9874fa3f2fef76c03d5857ee225dbbdf167b24d838fd76d8dbcc04e98c7b7481ea4de79be2a793cce7a363c91210eccff0e047b038e98658c233d410aea99b71d"
}

RootKey Regtest:

tprv8ZgxMBicQKsPf1ujkCE39iYKKmdzD4MgdpKb3EtiJrNCnE2K4GFMtL11adjRsCwA6J1jVKndF6GXpMdSpBGev9cczcjyhrSDVVeEXmwtmCt

Dump:

(dump.BackupMulti) {
 Version: (chanbackup.MultiBackupVersion) 0,
 StaticBackups: ([]dump.BackupSingle) (len=1 cap=1) {
  (dump.BackupSingle) {
   Version: (chanbackup.SingleBackupVersion) 6,
   IsInitiator: (bool) false,
   ChainHash: (string) (len=64) "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
   FundingOutpoint: (string) (len=66) "85619671cb1987c80723ed457fd829f6444269a4d228ab580cd8f2176f1d4994:0",
   ShortChannelID: (lnwire.ShortChannelID) 402:1:0,
   RemoteNodePub: (string) (len=66) "0310150ae975211a226fc85160232e238bf072a071202e58ce5d0527cda5d7e4c9",
   Addresses: ([]net.Addr) (len=2 cap=2) {
    (*net.TCPAddr)(0x14000138a50)(127.0.0.1:9636),
    (*net.TCPAddr)(0x14000138f00)(172.18.0.6:9735)
   },
   Capacity: (btcutil.Amount) 0.00100000 BTC,
   LocalChanCfg: (dump.ChannelConfig) {
    ChannelStateBounds: (channeldb.ChannelStateBounds) {
     ChanReserve: (btcutil.Amount) 0 BTC,
     MaxPendingAmount: (lnwire.MilliSatoshi) 0 mSAT,
     MinHTLC: (lnwire.MilliSatoshi) 0 mSAT,
     MaxAcceptedHtlcs: (uint16) 0
    },
    CommitmentParams: (channeldb.CommitmentParams) {
     DustLimit: (btcutil.Amount) 0 BTC,
     CsvDelay: (uint16) 144
    },
    MultiSigKey: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/0'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    RevocationBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/1'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    PaymentBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/3'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    DelayBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/4'/0/1",
     PubKey: (string) (len=5) "<nil>"
    },
    HtlcBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/2'/0/1",
     PubKey: (string) (len=5) "<nil>"
    }
   },
   RemoteChanCfg: (dump.ChannelConfig) {
    ChannelStateBounds: (channeldb.ChannelStateBounds) {
     ChanReserve: (btcutil.Amount) 0 BTC,
     MaxPendingAmount: (lnwire.MilliSatoshi) 0 mSAT,
     MinHTLC: (lnwire.MilliSatoshi) 0 mSAT,
     MaxAcceptedHtlcs: (uint16) 0
    },
    CommitmentParams: (channeldb.CommitmentParams) {
     DustLimit: (btcutil.Amount) 0 BTC,
     CsvDelay: (uint16) 144
    },
    MultiSigKey: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/0'/0/0",
     PubKey: (string) (len=66) "03c1102d1b9e016d72a39226ab34fb92fe6194b2f177882f5b167926d76417e070"
    },
    RevocationBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/0'/0/0",
     PubKey: (string) (len=66) "034a07a00f89920b27d965b041084cef9619f952fd8e1bbb4699be9f7a5806c4ee"
    },
    PaymentBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/0'/0/0",
     PubKey: (string) (len=66) "02a1b59387e693774772894155f5338f1e816ea3a36c2792377738f376ba1fde53"
    },
    DelayBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/0'/0/0",
     PubKey: (string) (len=66) "02a560b80e736d1b11af80069579b0894c19b7611f53b923674e4966c43d542140"
    },
    HtlcBasePoint: (dump.KeyDescriptor) {
     Path: (string) (len=17) "m/1017'/1'/0'/0/0",
     PubKey: (string) (len=66) "02e682ecf3cc018bb1547b89a4be96e6e7df228da97078930d55dc039d8dea7d5d"
    }
   },
   ShaChainRootDesc: (dump.KeyDescriptor) {
    Path: (string) (len=17) "m/1017'/1'/5'/0/2",
    PubKey: (string) (len=5) "<nil>"
   },
   CloseTxInputs: (*dump.CloseTxInputs)(0x1400028d700)({
    CommitTx: (string) (len=274) "020000000194491d6f17f2d80c58ab28d2a4694244f629d87f45ed2307c88719cb719661850000000000a0007180024a01000000000000225120fbd9aedad03a11215960cf6de9f51e5c7f7e292e9263a3b2410c2e14e0c68229987a010000000000225120e8807a199fd42c953f63cf78a840f444d3dd65541c38afe675ac2bf49f7d975a9bf94a20",
    CommitSig: (string) (len=196) "b8343a3033969c0eafbff5d4cff2fc981d279625b6ffc14996701c2fc90d787003ada823a2beee4a33b6524b9d1c6ccf751aa26c422b7bcc19631dea5214cf71b603d4d0cfa9a508e144a6f397319f56faa64854b7829129055bbab3ecf0fd4a49a2",
    CommitHeight: (uint64) 0,
    TapscriptRoot: (string) (len=64) "28dc51f66b841a32c48f4308e212d622d7ae5af2ceccdf10f9b37eeaf3ffcae3"
   })
  }
 }
}

scbclose:

If you do this and the state that you publish is *not* the latest state, then
the remote node *could* punish you by taking the whole channel amount *if* they
come online before you can sweep the funds from the time locked (144 - 2000
blocks) transaction *or* they have a watch tower looking out for them.

**This should absolutely be the last resort and you have been warned!**
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Type YES to proceed: YES
Channel point: 85619671cb1987c80723ed457fd829f6444269a4d228ab580cd8f2176f1d4994:0
Raw transaction hex: 0200000000010194491d6f17f2d80c58ab28d2a4694244f629d87f45ed2307c88719cb719661850000000000a0007180024a01000000000000225120fbd9aedad03a11215960cf6de9f51e5c7f7e292e9263a3b2410c2e14e0c68229987a010000000000225120e8807a199fd42c953f63cf78a840f444d3dd65541c38afe675ac2bf49f7d975a014093106f79bc27cee0105d3b77a11bf04109a33ec42e9d8c1813f48eac864f4413ef3b4551c6f22585044a4316408789e29be33aa82a2c362a4a988e02d8c1fcd99bf94a20
starius commented 2 weeks ago

@ziggie1984 Thanks for providing the test vector for custom channel! Could you also post pk_script for the channel UTXO, please? It is needed to verify the signature and can't be deduced from the data already posted.

ziggie1984 commented 2 weeks ago
 "vout": [
    {
      "value": 0.00100000,
      "n": 0,
      "scriptPubKey": {
        "asm": "1 559ed0f63cde7befccbd0f98fdfb1058a2a556eb33704dda981b90a5c205f742",
        "desc": "rawtr(559ed0f63cde7befccbd0f98fdfb1058a2a556eb33704dda981b90a5c205f742)#3t2jfx29",
        "hex": "5120559ed0f63cde7befccbd0f98fdfb1058a2a556eb33704dda981b90a5c205f742",
        "address": "bcrt1p2k0dpa3umea7ln9ap7v0m7cstz3224htxdcymk5crwg2tss97apqp5wtyy",
        "type": "witness_v1_taproot"
      }
    },
starius commented 2 weeks ago

@ziggie1984 Thank you! It works! I updated the testdata for scbforceclose package.

lightninglabs-deploy commented 2 days ago

@guggero: review reminder