Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

starius
Copy link
Contributor

@starius starius commented Nov 16, 2023

This is part of lightningnetwork/lnd#7658 (comment) implementation.

This PR depends on lightningnetwork/lnd#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 #107 is fixing this.)

TODOs

Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the two PRs!
I think both need some extra work but are on the right track.

cmd/chantools/forceclose.go Outdated Show resolved Hide resolved
cmd/chantools/forceclose.go Outdated Show resolved Hide resolved
cmd/chantools/forceclose.go Outdated Show resolved Hide resolved
@starius starius force-pushed the close-tx-in-static-backup branch 3 times, most recently from 74d418b to 3e8ea11 Compare December 31, 2023 19:13
@starius starius changed the title chantools forceclose: extract close tx from SCB chantools scbforceclose: extract close tx from SCB and sign it Dec 31, 2023
@starius
Copy link
Contributor Author

starius commented Dec 31, 2023

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

@guggero Please take another look!

@starius starius requested a review from guggero December 31, 2023 20:29
@starius starius force-pushed the close-tx-in-static-backup branch from 3e8ea11 to dcc9b3b Compare January 2, 2024 02:39
@guggero guggero removed their request for review April 22, 2024 12:36
@starius starius marked this pull request as draft June 3, 2024 01:56
@starius starius force-pushed the close-tx-in-static-backup branch 2 times, most recently from b78f22b to 4b79ab4 Compare June 18, 2024 23:01
@starius
Copy link
Contributor Author

starius commented Jun 18, 2024

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

@starius starius marked this pull request as ready for review June 18, 2024 23:44
@starius starius requested a review from guggero June 18, 2024 23:45
Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR, looks very good.

Will test once lnd side has been updated.


if s.CloseTxInputs == nil {
return nil, errors.New("channel backup does not have data " +
"needed to sign force sloe tx")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/sloe/close/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, moved to LND PR (commit chanbackup: add function SignCloseTx).

}

func createTaprootNonceProducer(
s chanbackup.Single,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: formatting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, moved to LND PR (commit chanbackup: add function SignCloseTx).

@starius starius force-pushed the close-tx-in-static-backup branch from 4b79ab4 to 521d4c8 Compare July 7, 2024 04:10
@starius
Copy link
Contributor Author

starius commented Jul 7, 2024

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 starius requested a review from guggero July 7, 2024 04:13
Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love how minimal this now turned out 💯

LGTM pending some final manual tests (will attempt to do later today).

Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK, LGTM 🎉

To make things easier to debug, can you please apply the following diff as another commit? That will add the new data to the output of chantools dumpbackup.

dumpchanbackup.diff.txt

Will merge once the lnd PR is in.

return errors.New("failed to read user input")
}
if strings.TrimSpace(userInput) != "YES" {
return errors.New("cancelled by user")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: improve error message slightly, perhaps something like "canceled by user, must type uppercase 'YES'"? Same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

s.FundingOutpoint, err)
}
txHex := hex.EncodeToString(buf.Bytes())
fmt.Println(s.FundingOutpoint)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should prefix these values, otherwise it looks a bit misleading on the command line.
Now:

Type YES to proceed: YES
8821015bb99036728b00f4b1f68ab4e41d8573947f3f94fe0d4ec2f38c1f92d4:0
02000000000101d4921f8cf3c24e0dfe943f7f9473851de4b48af6b1f4008b723690b95b01218800000000000508b280044a010000000000002......

Suggestion:

Type YES to proceed: YES

Channel point: 8821015bb99036728b00f4b1f68ab4e41d8573947f3f94fe0d4ec2f38c1f92d4:0
Raw transaction hex: 02000000000101d4921f8cf3c24e0dfe943f7f9473851de4b48af6b1f4008b723690b95b01218800000000000508b280044a01000000000.....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented!

@starius starius force-pushed the close-tx-in-static-backup branch from 521d4c8 to 1049f12 Compare July 17, 2024 14:17
@starius
Copy link
Contributor Author

starius commented Jul 17, 2024

I rebased the PR, updated go replace to the latest lightningnetwork/lnd#8183 version.

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

@starius starius force-pushed the close-tx-in-static-backup branch 2 times, most recently from 9474fe1 to 2f7fc42 Compare September 20, 2024 19:14
@starius
Copy link
Contributor Author

starius commented Sep 20, 2024

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

@starius starius force-pushed the close-tx-in-static-backup branch from 2f7fc42 to 25f7bb7 Compare September 20, 2024 19:18
Remove MockSigner from Signer. MockSigner has MusigSessionManager is an embedded
field, and it is the only part of MockSigner needed in Signer.
It can now be used as keychain.ECDHRing.
It will be reused in scbforceclose.
@starius starius force-pushed the close-tx-in-static-backup branch from 25f7bb7 to 42b2824 Compare October 1, 2024 19:22
@ziggie1984
Copy link
Contributor

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
Copy link
Contributor Author

starius commented Oct 2, 2024

@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
Copy link
Contributor

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

starius and others added 3 commits October 2, 2024 10:44
It provides function SignCloseTx which produces a signed force close
transaction from a channel backup and private key material.
Include fields closetx, commit sig, commit height, tapscript root.

That will add the new data to the output of chantools dumpbackup.
@starius starius force-pushed the close-tx-in-static-backup branch from 42b2824 to a386bde Compare October 2, 2024 13:44
@starius
Copy link
Contributor Author

starius commented Oct 2, 2024

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

@guggero guggero self-requested a review October 6, 2024 07:41
@guggero
Copy link
Member

guggero commented Oct 30, 2024

I'm going to remove my request for review until lnd v0.19.0 (non-RC) is out.

@guggero guggero removed their request for review October 30, 2024 10:09
@lightninglabs-deploy
Copy link

@starius, remember to re-request review from reviewers when ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants