From 72c9582b85c827d0ab304f232999e903c69fe57f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:43 +0100 Subject: [PATCH 1/9] multi: bump btcd to taproot aware version --- contractcourt/breacharbiter.go | 2 +- contractcourt/breacharbiter_test.go | 2 +- go.mod | 15 +-- go.sum | 31 ++++-- input/input.go | 2 +- input/script_utils.go | 2 +- input/script_utils_test.go | 102 ++++++++++++------ input/taproot.go | 20 ++++ lnrpc/signrpc/signer_server.go | 4 +- lnwallet/btcwallet/psbt.go | 2 +- lnwallet/btcwallet/psbt_test.go | 3 + lnwallet/chanfunding/wallet_assembler.go | 2 +- lnwallet/channel.go | 30 +++--- lnwallet/channel_test.go | 44 ++++++-- lnwallet/chanvalidate/validate.go | 1 + lnwallet/chanvalidate/validate_test.go | 2 +- lnwallet/rpcwallet/rpcwallet.go | 4 +- lnwallet/test/test_interface.go | 20 ++-- lnwallet/transactions_test.go | 23 ++-- lnwallet/wallet.go | 13 ++- sweep/txgenerator.go | 3 +- watchtower/lookout/justice_descriptor.go | 3 + watchtower/lookout/justice_descriptor_test.go | 2 +- watchtower/wtclient/backup_task.go | 3 +- 24 files changed, 228 insertions(+), 107 deletions(-) create mode 100644 input/taproot.go diff --git a/contractcourt/breacharbiter.go b/contractcourt/breacharbiter.go index ec285005a90..10f49daa54b 100644 --- a/contractcourt/breacharbiter.go +++ b/contractcourt/breacharbiter.go @@ -1440,7 +1440,7 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64, // Create a sighash cache to improve the performance of hashing and // signing SigHashAll inputs. - hashCache := txscript.NewTxSigHashes(txn) + hashCache := input.NewTxSigHashesV0Only(txn) // Create a closure that encapsulates the process of initializing a // particular output's witness generation function, computing the diff --git a/contractcourt/breacharbiter_test.go b/contractcourt/breacharbiter_test.go index f714b5d66a6..7dae490ab76 100644 --- a/contractcourt/breacharbiter_test.go +++ b/contractcourt/breacharbiter_test.go @@ -1377,7 +1377,7 @@ func getSpendTransactions(signer input.Signer, chanPoint *wire.OutPoint, // sign and add the witness to the HTLC sweep. retInfo := newRetributionInfo(chanPoint, retribution) - hashCache := txscript.NewTxSigHashes(htlcSweep) + hashCache := input.NewTxSigHashesV0Only(htlcSweep) for i := range retInfo.breachedOutputs { inp := &retInfo.breachedOutputs[i] diff --git a/go.mod b/go.mod index 65ae2cab352..f7a85ce0607 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,16 @@ require ( github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 - github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4 - github.com/btcsuite/btcd/btcec/v2 v2.1.0 - github.com/btcsuite/btcd/btcutil v1.1.0 - github.com/btcsuite/btcd/btcutil/psbt v1.1.0 + github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923 + github.com/btcsuite/btcd/btcec/v2 v2.1.3 + github.com/btcsuite/btcd/btcutil v1.1.1 + github.com/btcsuite/btcd/btcutil/psbt v1.1.1 + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f - github.com/btcsuite/btcwallet v0.14.0 - github.com/btcsuite/btcwallet/wallet/txauthor v1.2.1 + github.com/btcsuite/btcwallet v0.14.1-0.20220322182735-b0001c262734 + github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3 github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 - github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec + github.com/btcsuite/btcwallet/walletdb v1.4.0 github.com/btcsuite/btcwallet/wtxmgr v1.5.0 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 6e7b5de558a..b7ccfad8920 100644 --- a/go.sum +++ b/go.sum @@ -73,39 +73,46 @@ github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcug github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= -github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4 h1:CEGr/598C/0LZQUoioaT6sdGGcJgu4+ck0PDeJ/QkKs= github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= -github.com/btcsuite/btcd/btcec/v2 v2.1.0 h1:Whmbo9yShKKG+WrUfYGFfgj77vYBiwhwBSJnM66TMKI= +github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923 h1:6H47xWODLXYDuzHapvx4dauPqFjegX4+rHgUkFQPvfw= +github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923/go.mod h1:taIcYprAW2g6Z9S0gGUxyR+zDwimyDMK5ePOX+iJ2ds= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= -github.com/btcsuite/btcd/btcutil v1.1.0 h1:MO4klnGY+EWJdoWF12Wkuf4AWDBPMpZNeN/jRLrklUU= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil/psbt v1.1.0 h1:1LxDjz2ar4L2mrviBdxrzxesMMcAtj4nuBlX4FdqjOA= -github.com/btcsuite/btcd/btcutil/psbt v1.1.0/go.mod h1:xMuACsIKDzcE3kWMxqK+aLrAWZ8bMdn7YjYEwNs5q8k= +github.com/btcsuite/btcd/btcutil v1.1.1 h1:hDcDaXiP0uEzR8Biqo2weECKqEw0uHDZ9ixIWevVQqY= +github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= +github.com/btcsuite/btcd/btcutil/psbt v1.1.1 h1:t8yNrashLB1ZLqOapxtWy/d8jMfMDgwPKwf70sgnSac= +github.com/btcsuite/btcd/btcutil/psbt v1.1.1/go.mod h1:KsGzRAzAdEimzgERpK9Xm+RhuCMvc4j2ctK0BEQ8JV0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.14.0 h1:+Nwf0GkRUwDjd/F3i9HgrRhDp8RHZFbBZ3kQaZr6zD0= -github.com/btcsuite/btcwallet v0.14.0/go.mod h1:KFR1x3ZH7c31i4qA34XIvcsnhrEBLK1SHli52lN8E54= -github.com/btcsuite/btcwallet/wallet/txauthor v1.2.1 h1:oxIaFiF8gmOLJh7wNkXYkyLWT7Pj5igSrn5HthPVDYg= +github.com/btcsuite/btcwallet v0.14.1-0.20220322182735-b0001c262734 h1:gG2UgzXLiMiT4sw74161AEf0LE/mxDM8Ia6TaoV0VBw= +github.com/btcsuite/btcwallet v0.14.1-0.20220322182735-b0001c262734/go.mod h1:QN2tl1ipATUQRo9RtgvMHLSspqx7QWsj30qL+7AXuAo= github.com/btcsuite/btcwallet/wallet/txauthor v1.2.1/go.mod h1:/74bubxX5Js48d76nf/TsNabpYp/gndUuJw4chzCmhU= +github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3 h1:M2yr5UlULvpqtxUqpMxTME/pA92Z9cpqeyvAFk9lAg0= +github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3/go.mod h1:T2xSiKGpUkSLCh68aF+FMXmKK9mFqNdHl9VaqOr+JjU= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 h1:wZnOolEAeNOHzHTnznw/wQv+j35ftCIokNrnOTOU5o8= github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec h1:zcAU3Ij8SmqaE+ITtS76fua2Niq7DRNp46sJRhi8PiI= github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= +github.com/btcsuite/btcwallet/walletdb v1.4.0 h1:/C5JRF+dTuE2CNMCO/or5N8epsrhmSM4710uBQoYPTQ= +github.com/btcsuite/btcwallet/walletdb v1.4.0/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/wtxmgr v1.5.0 h1:WO0KyN4l6H3JWnlFxfGR7r3gDnlGT7W2cL8vl6av4SU= github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= @@ -631,6 +638,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/input/input.go b/input/input.go index 642897a8db0..865a761a37f 100644 --- a/input/input.go +++ b/input/input.go @@ -338,7 +338,7 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx, txinIdx int) (wire.TxWitness, error) { desc := signDetails.SignDesc - desc.SigHashes = txscript.NewTxSigHashes(txn) + desc.SigHashes = NewTxSigHashesV0Only(txn) desc.InputIndex = txinIdx return SenderHtlcSpendTimeout( diff --git a/input/script_utils.go b/input/script_utils.go index 53d87587a63..8302a972a9c 100644 --- a/input/script_utils.go +++ b/input/script_utils.go @@ -864,7 +864,7 @@ func HtlcSpendSuccess(signer Signer, signDesc *SignDescriptor, // As we mutated the transaction, we'll re-calculate the sighashes for // this instance. - signDesc.SigHashes = txscript.NewTxSigHashes(sweepTx) + signDesc.SigHashes = NewTxSigHashesV0Only(sweepTx) // With the proper sequence and version set, we'll now sign the timeout // transaction using the passed signed descriptor. In order to generate diff --git a/input/script_utils_test.go b/input/script_utils_test.go index 67a4f7988c8..a99873af839 100644 --- a/input/script_utils_test.go +++ b/input/script_utils_test.go @@ -280,7 +280,7 @@ func TestHTLCSenderSpendValidation(t *testing.T) { }, ) - sweepTxSigHashes = txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes = NewTxSigHashesV0Only(sweepTx) bobSigHash = txscript.SigHashAll if confirmed { @@ -546,9 +546,14 @@ func TestHTLCSenderSpendValidation(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(htlcPkScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(paymentAmt)) + return txscript.NewEngine( + htlcPkScript, sweepTx, 0, + txscript.StandardVerifyFlags, nil, nil, + int64(paymentAmt), + txscript.NewCannedPrevOutputFetcher( + htlcPkScript, int64(paymentAmt), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) @@ -674,7 +679,7 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { Value: 1 * 10e8, }, ) - sweepTxSigHashes = txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes = NewTxSigHashesV0Only(sweepTx) aliceSigHash = txscript.SigHashAll if confirmed { @@ -962,9 +967,14 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(htlcPkScript, + return txscript.NewEngine( + htlcPkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(paymentAmt)) + nil, int64(paymentAmt), + txscript.NewCannedPrevOutputFetcher( + htlcPkScript, int64(paymentAmt), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) @@ -1014,7 +1024,7 @@ func TestSecondLevelHtlcSpends(t *testing.T) { Value: 1 * 10e8, }, ) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) // The delay key will be crafted using Bob's public key as the output // we created will be spending from Alice's commitment transaction. @@ -1165,9 +1175,14 @@ func TestSecondLevelHtlcSpends(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(htlcPkScript, + return txscript.NewEngine( + htlcPkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(htlcAmt)) + nil, int64(htlcAmt), + txscript.NewCannedPrevOutputFetcher( + htlcPkScript, int64(htlcAmt), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) @@ -1220,7 +1235,7 @@ func TestLeaseSecondLevelHtlcSpends(t *testing.T) { Value: 1 * 10e8, }, ) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) // The delay key will be crafted using Bob's public key as the output // we created will be spending from Alice's commitment transaction. @@ -1400,9 +1415,14 @@ func TestLeaseSecondLevelHtlcSpends(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(htlcPkScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(htlcAmt)) + return txscript.NewEngine( + htlcPkScript, sweepTx, 0, + txscript.StandardVerifyFlags, nil, nil, + int64(htlcAmt), + txscript.NewCannedPrevOutputFetcher( + htlcPkScript, int64(htlcAmt), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) @@ -1472,7 +1492,7 @@ func TestLeaseCommmitSpendToSelf(t *testing.T) { // Bob can spend with his revocation key, but not // without the proper tweak. makeWitnessTestCase(t, func() (wire.TxWitness, error) { - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ PubKey: bobKeyPub, @@ -1494,7 +1514,7 @@ func TestLeaseCommmitSpendToSelf(t *testing.T) { // Bob can spend with his revocation key with the proper // tweak. makeWitnessTestCase(t, func() (wire.TxWitness, error) { - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ PubKey: bobKeyPub, @@ -1521,7 +1541,7 @@ func TestLeaseCommmitSpendToSelf(t *testing.T) { sweepTx.TxIn[0].Sequence = LockTimeToSequence( false, csvDelay/2, ) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ PubKey: aliceKeyPub, @@ -1548,7 +1568,7 @@ func TestLeaseCommmitSpendToSelf(t *testing.T) { sweepTx.TxIn[0].Sequence = LockTimeToSequence( false, csvDelay, ) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ PubKey: aliceKeyPub, @@ -1575,7 +1595,7 @@ func TestLeaseCommmitSpendToSelf(t *testing.T) { sweepTx.TxIn[0].Sequence = LockTimeToSequence( false, csvDelay, ) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ PubKey: aliceKeyPub, @@ -1599,9 +1619,14 @@ func TestLeaseCommmitSpendToSelf(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(commitPkScript, + return txscript.NewEngine( + commitPkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(outputVal)) + nil, int64(outputVal), + txscript.NewCannedPrevOutputFetcher( + commitPkScript, int64(outputVal), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) @@ -1659,7 +1684,7 @@ func TestCommitSpendToRemoteConfirmed(t *testing.T) { // Alice can spend after the a CSV delay has passed. makeWitnessTestCase(t, func() (wire.TxWitness, error) { sweepTx.TxIn[0].Sequence = LockTimeToSequence(false, 1) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ @@ -1681,7 +1706,7 @@ func TestCommitSpendToRemoteConfirmed(t *testing.T) { // Alice cannot spend output without sequence set. makeWitnessTestCase(t, func() (wire.TxWitness, error) { sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ @@ -1705,9 +1730,14 @@ func TestCommitSpendToRemoteConfirmed(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(commitPkScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(outputVal)) + return txscript.NewEngine( + commitPkScript, sweepTx, 0, + txscript.StandardVerifyFlags, nil, nil, + int64(outputVal), + txscript.NewCannedPrevOutputFetcher( + commitPkScript, int64(outputVal), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) @@ -1770,7 +1800,7 @@ func TestLeaseCommitSpendToRemoteConfirmed(t *testing.T) { sweepTx.TxIn[0].Sequence = LockTimeToSequence( false, 1, ) - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ @@ -1795,7 +1825,7 @@ func TestLeaseCommitSpendToRemoteConfirmed(t *testing.T) { makeWitnessTestCase(t, func() (wire.TxWitness, error) { sweepTx.LockTime = leaseExpiry sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ @@ -1820,7 +1850,7 @@ func TestLeaseCommitSpendToRemoteConfirmed(t *testing.T) { makeWitnessTestCase(t, func() (wire.TxWitness, error) { sweepTx.LockTime = 0 sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ @@ -1849,6 +1879,9 @@ func TestLeaseCommitSpendToRemoteConfirmed(t *testing.T) { commitPkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(outputVal), + txscript.NewCannedPrevOutputFetcher( + commitPkScript, int64(outputVal), + ), ) } @@ -1913,7 +1946,7 @@ func TestSpendAnchor(t *testing.T) { // Alice can spend immediately. makeWitnessTestCase(t, func() (wire.TxWitness, error) { sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum - sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + sweepTxSigHashes := NewTxSigHashesV0Only(sweepTx) signDesc := &SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ @@ -1953,9 +1986,14 @@ func TestSpendAnchor(t *testing.T) { sweepTx.TxIn[0].Witness = testCase.witness() newEngine := func() (*txscript.Engine, error) { - return txscript.NewEngine(anchorPkScript, + return txscript.NewEngine( + anchorPkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(anchorSize)) + nil, int64(anchorSize), + txscript.NewCannedPrevOutputFetcher( + anchorPkScript, int64(anchorSize), + ), + ) } assertEngineExecution(t, i, testCase.valid, newEngine) diff --git a/input/taproot.go b/input/taproot.go new file mode 100644 index 00000000000..a80b51d29b5 --- /dev/null +++ b/input/taproot.go @@ -0,0 +1,20 @@ +package input + +import ( + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// NewTxSigHashesV0Only returns a new txscript.TxSigHashes instance that will +// only calculate the sighash midstate values for segwit v0 inputs and can +// therefore never be used for transactions that want to spend segwit v1 +// (taproot) inputs. +func NewTxSigHashesV0Only(tx *wire.MsgTx) *txscript.TxSigHashes { + // The canned output fetcher returns a wire.TxOut instance with the + // given pk script and amount. We can get away with nil since the first + // thing the TxSigHashes constructor checks is the length of the pk + // script and whether it matches taproot output script length. If the + // length doesn't match it assumes v0 inputs only. + nilFetcher := txscript.NewCannedPrevOutputFetcher(nil, 0) + return txscript.NewTxSigHashes(tx, nilFetcher) +} diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index 54ce3a37d2e..bf0e2471c87 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -266,7 +266,7 @@ func (s *Server) SignOutputRaw(ctx context.Context, in *SignReq) (*SignResp, return nil, fmt.Errorf("unable to decode tx: %v", err) } - sigHashCache := txscript.NewTxSigHashes(&txToSign) + sigHashCache := input.NewTxSigHashesV0Only(&txToSign) log.Debugf("Generating sigs for %v inputs: ", len(in.SignDescs)) @@ -405,7 +405,7 @@ func (s *Server) ComputeInputScript(ctx context.Context, return nil, fmt.Errorf("unable to decode tx: %v", err) } - sigHashCache := txscript.NewTxSigHashes(&txToSign) + sigHashCache := input.NewTxSigHashesV0Only(&txToSign) signDescs := make([]*input.SignDescriptor, 0, len(in.SignDescs)) for _, signDesc := range in.SignDescs { diff --git a/lnwallet/btcwallet/psbt.go b/lnwallet/btcwallet/psbt.go index 064555340b9..409277d22dd 100644 --- a/lnwallet/btcwallet/psbt.go +++ b/lnwallet/btcwallet/psbt.go @@ -107,7 +107,7 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error { // there are inputs that we don't know how to sign, we won't return any // error. So it's possible we're not the final signer. tx := packet.UnsignedTx - sigHashes := txscript.NewTxSigHashes(tx) + sigHashes := input.NewTxSigHashesV0Only(tx) for idx := range tx.TxIn { in := packet.Inputs[idx] diff --git a/lnwallet/btcwallet/psbt_test.go b/lnwallet/btcwallet/psbt_test.go index b37dbf1d174..d4fcfc4ece5 100644 --- a/lnwallet/btcwallet/psbt_test.go +++ b/lnwallet/btcwallet/psbt_test.go @@ -330,6 +330,9 @@ func TestSignPsbt(t *testing.T) { refTx.TxOut[0].PkScript, finalTx, 0, txscript.StandardVerifyFlags, nil, nil, refTx.TxOut[0].Value, + txscript.NewCannedPrevOutputFetcher( + refTx.TxOut[0].PkScript, refTx.TxOut[0].Value, + ), ) require.NoError(t, err) require.NoError(t, vm.Execute()) diff --git a/lnwallet/chanfunding/wallet_assembler.go b/lnwallet/chanfunding/wallet_assembler.go index 39454050664..72cf56ce263 100644 --- a/lnwallet/chanfunding/wallet_assembler.go +++ b/lnwallet/chanfunding/wallet_assembler.go @@ -109,7 +109,7 @@ func (f *FullIntent) CompileFundingTx(extraInputs []*wire.TxIn, // order of the inputs. signDesc := input.SignDescriptor{ HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(fundingTx), + SigHashes: input.NewTxSigHashesV0Only(fundingTx), } for i, txIn := range fundingTx.TxIn { // We can only sign this input if it's ours, so we'll ask the diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 22a53defcd0..df5b244d84a 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3044,7 +3044,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, WitnessScript: htlc.theirWitnessScript, Output: txOut, HashType: sigHashType, - SigHashes: txscript.NewTxSigHashes(sigJob.Tx), + SigHashes: input.NewTxSigHashesV0Only(sigJob.Tx), InputIndex: 0, } sigJob.OutputIndex = htlc.remoteOutputIndex @@ -3098,7 +3098,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, WitnessScript: htlc.theirWitnessScript, Output: txOut, HashType: sigHashType, - SigHashes: txscript.NewTxSigHashes(sigJob.Tx), + SigHashes: input.NewTxSigHashesV0Only(sigJob.Tx), InputIndex: 0, } sigJob.OutputIndex = htlc.remoteOutputIndex @@ -3610,7 +3610,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, // While the jobs are being carried out, we'll Sign their version of // the new commitment transaction while we're waiting for the rest of // the HTLC signatures to be processed. - lc.signDesc.SigHashes = txscript.NewTxSigHashes(newCommitView.txn) + lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(newCommitView.txn) rawSig, err := lc.Signer.SignOutputRaw(newCommitView.txn, lc.signDesc) if err != nil { close(cancelChan) @@ -4143,7 +4143,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, return nil, err } - hashCache := txscript.NewTxSigHashes(successTx) + hashCache := input.NewTxSigHashesV0Only(successTx) sigHash, err := txscript.CalcWitnessSigHash( htlc.ourWitnessScript, hashCache, sigHashType, successTx, 0, @@ -4198,7 +4198,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, return nil, err } - hashCache := txscript.NewTxSigHashes(timeoutTx) + hashCache := input.NewTxSigHashesV0Only(timeoutTx) sigHash, err := txscript.CalcWitnessSigHash( htlc.ourWitnessScript, hashCache, sigHashType, timeoutTx, 0, @@ -4391,7 +4391,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, // this newly proposed state update. localCommitTx := localCommitmentView.txn multiSigScript := lc.signDesc.WitnessScript - hashCache := txscript.NewTxSigHashes(localCommitTx) + hashCache := input.NewTxSigHashesV0Only(localCommitTx) sigHash, err := txscript.CalcWitnessSigHash( multiSigScript, hashCache, txscript.SigHashAll, localCommitTx, 0, int64(lc.channelState.Capacity), @@ -5493,7 +5493,7 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { // With this, we then generate the full witness so the caller can // broadcast a fully signed transaction. - lc.signDesc.SigHashes = txscript.NewTxSigHashes(commitTx) + lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(commitTx) ourSig, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc) if err != nil { return nil, err @@ -5901,7 +5901,7 @@ func newOutgoingHtlcResolution(signer input.Signer, WitnessScript: htlcScript, Output: txOut, HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(timeoutTx), + SigHashes: input.NewTxSigHashesV0Only(timeoutTx), InputIndex: 0, } @@ -6036,7 +6036,7 @@ func newIncomingHtlcResolution(signer input.Signer, WitnessScript: htlcScript, Output: txOut, HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(successTx), + SigHashes: input.NewTxSigHashesV0Only(successTx), InputIndex: 0, } @@ -6459,7 +6459,7 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, // initiator we'll simply send our signature over to the remote party, // using the generated txid to be notified once the closure transaction // has been confirmed. - lc.signDesc.SigHashes = txscript.NewTxSigHashes(closeTx) + lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(closeTx) sig, err := lc.Signer.SignOutputRaw(closeTx, lc.signDesc) if err != nil { return nil, nil, 0, err @@ -6519,7 +6519,7 @@ func (lc *LightningChannel) CompleteCooperativeClose( if err := blockchain.CheckTransactionSanity(tx); err != nil { return nil, 0, err } - hashCache := txscript.NewTxSigHashes(closeTx) + hashCache := input.NewTxSigHashesV0Only(closeTx) // Finally, construct the witness stack minding the order of the // pubkeys+sigs on the stack. @@ -6536,8 +6536,12 @@ func (lc *LightningChannel) CompleteCooperativeClose( // Validate the finalized transaction to ensure the output script is // properly met, and that the remote peer supplied a valid signature. prevOut := lc.signDesc.Output - vm, err := txscript.NewEngine(prevOut.PkScript, closeTx, 0, - txscript.StandardVerifyFlags, nil, hashCache, prevOut.Value) + vm, err := txscript.NewEngine( + prevOut.PkScript, closeTx, 0, txscript.StandardVerifyFlags, nil, + hashCache, prevOut.Value, txscript.NewCannedPrevOutputFetcher( + prevOut.PkScript, prevOut.Value, + ), + ) if err != nil { return nil, 0, err } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 0c6c456f788..4962c61d6ef 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -990,9 +990,14 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // the multi-sig clause within the output on the commitment transaction // that produces this HTLC. timeoutTx := htlcResolution.SignedTimeoutTx - vm, err := txscript.NewEngine(senderHtlcPkScript, + vm, err := txscript.NewEngine( + senderHtlcPkScript, timeoutTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(htlcAmount.ToSatoshis())) + nil, int64(htlcAmount.ToSatoshis()), + txscript.NewCannedPrevOutputFetcher( + senderHtlcPkScript, int64(htlcAmount.ToSatoshis()), + ), + ) if err != nil { t.Fatalf("unable to create engine: %v", err) } @@ -1028,6 +1033,10 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { htlcResolution.SweepSignDesc.Output.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, htlcResolution.SweepSignDesc.Output.Value, + txscript.NewCannedPrevOutputFetcher( + htlcResolution.SweepSignDesc.Output.PkScript, + htlcResolution.SweepSignDesc.Output.Value, + ), ) if err != nil { t.Fatalf("unable to create engine: %v", err) @@ -1057,9 +1066,14 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // before publication. successTx := inHtlcResolution.SignedSuccessTx successTx.TxIn[0].Witness[3] = preimageBob[:] - vm, err = txscript.NewEngine(receiverHtlcScript, + vm, err = txscript.NewEngine( + receiverHtlcScript, successTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(htlcAmount.ToSatoshis())) + nil, int64(htlcAmount.ToSatoshis()), + txscript.NewCannedPrevOutputFetcher( + receiverHtlcScript, int64(htlcAmount.ToSatoshis()), + ), + ) if err != nil { t.Fatalf("unable to create engine: %v", err) } @@ -1091,6 +1105,10 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { inHtlcResolution.SweepSignDesc.Output.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, inHtlcResolution.SweepSignDesc.Output.Value, + txscript.NewCannedPrevOutputFetcher( + inHtlcResolution.SweepSignDesc.Output.PkScript, + inHtlcResolution.SweepSignDesc.Output.Value, + ), ) if err != nil { t.Fatalf("unable to create engine: %v", err) @@ -5978,7 +5996,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { Value: outHtlcResolution.SweepSignDesc.Output.Value, }) outHtlcResolution.SweepSignDesc.InputIndex = 0 - outHtlcResolution.SweepSignDesc.SigHashes = txscript.NewTxSigHashes( + outHtlcResolution.SweepSignDesc.SigHashes = input.NewTxSigHashesV0Only( sweepTx, ) sweepTx.LockTime = outHtlcResolution.Expiry @@ -5996,6 +6014,10 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { outHtlcResolution.SweepSignDesc.Output.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, outHtlcResolution.SweepSignDesc.Output.Value, + txscript.NewCannedPrevOutputFetcher( + outHtlcResolution.SweepSignDesc.Output.PkScript, + outHtlcResolution.SweepSignDesc.Output.Value, + ), ) if err != nil { t.Fatalf("unable to create engine: %v", err) @@ -6016,7 +6038,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { Value: inHtlcResolution.SweepSignDesc.Output.Value, }) inHtlcResolution.SweepSignDesc.InputIndex = 0 - inHtlcResolution.SweepSignDesc.SigHashes = txscript.NewTxSigHashes( + inHtlcResolution.SweepSignDesc.SigHashes = input.NewTxSigHashesV0Only( sweepTx, ) sweepTx.TxIn[0].Witness, err = input.SenderHtlcSpendRedeem( @@ -6034,6 +6056,10 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { inHtlcResolution.SweepSignDesc.Output.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, inHtlcResolution.SweepSignDesc.Output.Value, + txscript.NewCannedPrevOutputFetcher( + inHtlcResolution.SweepSignDesc.Output.PkScript, + inHtlcResolution.SweepSignDesc.Output.Value, + ), ) if err != nil { t.Fatalf("unable to create engine: %v", err) @@ -6151,7 +6177,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { PkScript: testHdSeed[:], Value: aliceSignDesc.Output.Value, }) - aliceSignDesc.SigHashes = txscript.NewTxSigHashes(sweepTx) + aliceSignDesc.SigHashes = input.NewTxSigHashesV0Only(sweepTx) sweepTx.TxIn[0].Witness, err = input.CommitSpendNoDelay( aliceChannel.Signer, &aliceSignDesc, sweepTx, false, ) @@ -6165,6 +6191,10 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { aliceSignDesc.Output.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, aliceSignDesc.Output.Value, + txscript.NewCannedPrevOutputFetcher( + aliceSignDesc.Output.PkScript, + aliceSignDesc.Output.Value, + ), ) if err != nil { t.Fatalf("unable to create engine: %v", err) diff --git a/lnwallet/chanvalidate/validate.go b/lnwallet/chanvalidate/validate.go index e0b10590df8..c349ea974b5 100644 --- a/lnwallet/chanvalidate/validate.go +++ b/lnwallet/chanvalidate/validate.go @@ -189,6 +189,7 @@ func Validate(ctx *Context) (*wire.OutPoint, error) { vm, err := txscript.NewEngine( ctx.MultiSigPkScript, ctx.CommitCtx.FullySignedCommitTx, 0, txscript.StandardVerifyFlags, nil, nil, fundingValue, + txscript.NewCannedPrevOutputFetcher(ctx.MultiSigPkScript, 0), ) if err != nil { return nil, err diff --git a/lnwallet/chanvalidate/validate_test.go b/lnwallet/chanvalidate/validate_test.go index 13a192b2111..85d0e92a109 100644 --- a/lnwallet/chanvalidate/validate_test.go +++ b/lnwallet/chanvalidate/validate_test.go @@ -98,7 +98,7 @@ func newChannelTestCtx(chanSize int64) (*channelTestCtx, error) { }, } - sigHashes := txscript.NewTxSigHashes(commitTx) + sigHashes := input.NewTxSigHashesV0Only(commitTx) aliceSigRaw, err := txscript.RawTxInWitnessSignature( commitTx, sigHashes, 0, chanSize, multiSigScript, txscript.SigHashAll, alicePriv, diff --git a/lnwallet/rpcwallet/rpcwallet.go b/lnwallet/rpcwallet/rpcwallet.go index e4ff17ea60a..052b3d86c05 100644 --- a/lnwallet/rpcwallet/rpcwallet.go +++ b/lnwallet/rpcwallet/rpcwallet.go @@ -135,7 +135,7 @@ func (r *RPCKeyRing) SendOutputs(outputs []*wire.TxOut, // So we can just compute the input script using the remote signer. signDesc := input.SignDescriptor{ HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(tx), + SigHashes: input.NewTxSigHashesV0Only(tx), } for i, txIn := range tx.TxIn { // We can only sign this input if it's ours, so we'll ask the @@ -255,7 +255,7 @@ func (r *RPCKeyRing) FinalizePsbt(packet *psbt.Packet, _ string) error { // ones to sign. If there is any input without witness data that we // cannot sign because it's not our UTXO, this will be a hard failure. tx := packet.UnsignedTx - sigHashes := txscript.NewTxSigHashes(tx) + sigHashes := input.NewTxSigHashesV0Only(tx) for idx, txIn := range tx.TxIn { in := packet.Inputs[idx] diff --git a/lnwallet/test/test_interface.go b/lnwallet/test/test_interface.go index cba0eae7ba1..5f0f3fbabd4 100644 --- a/lnwallet/test/test_interface.go +++ b/lnwallet/test/test_interface.go @@ -1710,7 +1710,7 @@ func txFromOutput(tx *wire.MsgTx, signer input.Signer, fromPubKey, WitnessScript: keyScript, Output: tx.TxOut[outputIndex], HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(tx1), + SigHashes: input.NewTxSigHashesV0Only(tx1), InputIndex: 0, // Has only one input. } @@ -1730,7 +1730,9 @@ func txFromOutput(tx *wire.MsgTx, signer input.Signer, fromPubKey, // private key. vm, err := txscript.NewEngine( keyScript, tx1, 0, txscript.StandardVerifyFlags, nil, - nil, outputValue, + nil, outputValue, txscript.NewCannedPrevOutputFetcher( + keyScript, outputValue, + ), ) if err != nil { return nil, fmt.Errorf("unable to create engine: %v", err) @@ -2096,7 +2098,7 @@ func testSignOutputUsingTweaks(r *rpctest.Harness, WitnessScript: keyScript, Output: newOutput, HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(sweepTx), + SigHashes: input.NewTxSigHashesV0Only(sweepTx), InputIndex: 0, } @@ -2123,9 +2125,13 @@ func testSignOutputUsingTweaks(r *rpctest.Harness, // Finally, attempt to validate the completed transaction. This // should succeed if the wallet was able to properly generate // the proper private key. - vm, err := txscript.NewEngine(keyScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(btcutil.SatoshiPerBitcoin)) + vm, err := txscript.NewEngine( + keyScript, sweepTx, 0, txscript.StandardVerifyFlags, + nil, nil, int64(btcutil.SatoshiPerBitcoin), + txscript.NewCannedPrevOutputFetcher( + keyScript, int64(btcutil.SatoshiPerBitcoin), + ), + ) if err != nil { t.Fatalf("unable to create engine: %v", err) } @@ -2833,7 +2839,7 @@ func testSignOutputCreateAccount(r *rpctest.Harness, w *lnwallet.LightningWallet Value: 1000, }, HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(fakeTx), + SigHashes: input.NewTxSigHashesV0Only(fakeTx), InputIndex: 0, } diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 4e8d76f7ee9..e8446e0e4cf 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -612,7 +612,7 @@ func testSpendValidation(t *testing.T, tweakless bool) { PubKey: aliceKeyPub, }, SingleTweak: remoteCommitTweak, - SigHashes: txscript.NewTxSigHashes(sweepTx), + SigHashes: input.NewTxSigHashesV0Only(sweepTx), Output: &wire.TxOut{ Value: int64(channelBalance), }, @@ -626,9 +626,11 @@ func testSpendValidation(t *testing.T, tweakless bool) { t.Fatalf("unable to generate delay commit spend witness: %v", err) } sweepTx.TxIn[0].Witness = aliceWitnessSpend - vm, err := txscript.NewEngine(delayOutput.PkScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(channelBalance)) + vm, err := txscript.NewEngine( + delayOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, + nil, nil, int64(channelBalance), + txscript.NewCannedPrevOutputFetcher(nil, 0), + ) if err != nil { t.Fatalf("unable to create engine: %v", err) } @@ -647,7 +649,7 @@ func testSpendValidation(t *testing.T, tweakless bool) { }, DoubleTweak: commitSecret, WitnessScript: delayScript, - SigHashes: txscript.NewTxSigHashes(sweepTx), + SigHashes: input.NewTxSigHashesV0Only(sweepTx), Output: &wire.TxOut{ Value: int64(channelBalance), }, @@ -660,9 +662,11 @@ func testSpendValidation(t *testing.T, tweakless bool) { t.Fatalf("unable to generate revocation witness: %v", err) } sweepTx.TxIn[0].Witness = bobWitnessSpend - vm, err = txscript.NewEngine(delayOutput.PkScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(channelBalance)) + vm, err = txscript.NewEngine( + delayOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, + nil, nil, int64(channelBalance), + txscript.NewCannedPrevOutputFetcher(nil, 0), + ) if err != nil { t.Fatalf("unable to create engine: %v", err) } @@ -691,7 +695,7 @@ func testSpendValidation(t *testing.T, tweakless bool) { PubKey: bobKeyPub, }, WitnessScript: bobScriptP2WKH, - SigHashes: txscript.NewTxSigHashes(sweepTx), + SigHashes: input.NewTxSigHashesV0Only(sweepTx), Output: &wire.TxOut{ Value: int64(channelBalance), PkScript: bobScriptP2WKH, @@ -713,6 +717,7 @@ func testSpendValidation(t *testing.T, tweakless bool) { regularOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(channelBalance), + txscript.NewCannedPrevOutputFetcher(bobScriptP2WKH, 0), ) if err != nil { t.Fatalf("unable to create engine: %v", err) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index c3548b1dee2..e6145fae366 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -1615,7 +1615,7 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { KeyDesc: ourKey, Output: fundingOutput, HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(theirCommitTx), + SigHashes: input.NewTxSigHashesV0Only(theirCommitTx), InputIndex: 0, } sigTheirCommit, err := l.Cfg.Signer.SignOutputRaw(theirCommitTx, &signDesc) @@ -1690,7 +1690,7 @@ func (l *LightningWallet) verifyFundingInputs(fundingTx *wire.MsgTx, remoteInputScripts []*input.Script) error { sigIndex := 0 - fundingHashCache := txscript.NewTxSigHashes(fundingTx) + fundingHashCache := input.NewTxSigHashesV0Only(fundingTx) inputScripts := remoteInputScripts for i, txin := range fundingTx.TxIn { if len(inputScripts) != 0 && len(txin.Witness) == 0 { @@ -1727,6 +1727,9 @@ func (l *LightningWallet) verifyFundingInputs(fundingTx *wire.MsgTx, output.PkScript, fundingTx, i, txscript.StandardVerifyFlags, nil, fundingHashCache, output.Value, + txscript.NewCannedPrevOutputFetcher( + output.PkScript, output.Value, + ), ) if err != nil { return fmt.Errorf("cannot create script "+ @@ -1806,7 +1809,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Next, create the spending scriptSig, and then verify that the script // is complete, allowing us to spend from the funding transaction. channelValue := int64(res.partialState.Capacity) - hashCache := txscript.NewTxSigHashes(commitTx) + hashCache := input.NewTxSigHashesV0Only(commitTx) sigHash, err := txscript.CalcWitnessSigHash( witnessScript, hashCache, txscript.SigHashAll, commitTx, 0, channelValue, @@ -1957,7 +1960,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { req.fundingOutpoint, spew.Sdump(theirCommitTx)) channelValue := int64(pendingReservation.partialState.Capacity) - hashCache := txscript.NewTxSigHashes(ourCommitTx) + hashCache := input.NewTxSigHashesV0Only(ourCommitTx) theirKey := pendingReservation.theirContribution.MultiSigKey ourKey := pendingReservation.ourContribution.MultiSigKey witnessScript, _, err := input.GenFundingPkScript( @@ -2008,7 +2011,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { Value: channelValue, }, HashType: txscript.SigHashAll, - SigHashes: txscript.NewTxSigHashes(theirCommitTx), + SigHashes: input.NewTxSigHashesV0Only(theirCommitTx), InputIndex: 0, } sigTheirCommit, err := l.Cfg.Signer.SignOutputRaw(theirCommitTx, &signDesc) diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index 19934213d06..3cbdb7c7b88 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -7,7 +7,6 @@ import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" @@ -264,7 +263,7 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut, return nil, err } - hashCache := txscript.NewTxSigHashes(sweepTx) + hashCache := input.NewTxSigHashesV0Only(sweepTx) // With all the inputs in place, use each output's unique input script // function to generate the final witness required for spending. diff --git a/watchtower/lookout/justice_descriptor.go b/watchtower/lookout/justice_descriptor.go index 87eebbe484d..5e21cda6bb0 100644 --- a/watchtower/lookout/justice_descriptor.go +++ b/watchtower/lookout/justice_descriptor.go @@ -228,6 +228,9 @@ func (p *JusticeDescriptor) assembleJusticeTxn(txWeight int64, input.txOut.PkScript, justiceTxn, i, txscript.StandardVerifyFlags, nil, nil, input.txOut.Value, + txscript.NewCannedPrevOutputFetcher( + input.txOut.PkScript, input.txOut.Value, + ), ) if err != nil { return nil, err diff --git a/watchtower/lookout/justice_descriptor_test.go b/watchtower/lookout/justice_descriptor_test.go index 9ed26db3c57..aa1b10f7bf0 100644 --- a/watchtower/lookout/justice_descriptor_test.go +++ b/watchtower/lookout/justice_descriptor_test.go @@ -269,7 +269,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) { justiceTxn.TxOut = outputs txsort.InPlaceSort(justiceTxn) - hashCache := txscript.NewTxSigHashes(justiceTxn) + hashCache := input.NewTxSigHashesV0Only(justiceTxn) // Create the sign descriptor used to sign for the to-local input. toLocalSignDesc := &input.SignDescriptor{ diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index 34935655483..2ae836595e0 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -7,7 +7,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/txsort" - "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" @@ -285,7 +284,7 @@ func (t *backupTask) craftSessionPayload( } // Construct a sighash cache to improve signing performance. - hashCache := txscript.NewTxSigHashes(justiceTxn) + hashCache := input.NewTxSigHashesV0Only(justiceTxn) // Since the transaction inputs could have been reordered as a result of // the BIP69 sort, create an index mapping each prevout to it's new From f130eddb92e8667f3ce2e9c13f09934598a75853 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:44 +0100 Subject: [PATCH 2/9] multi: use prev output fetcher where possible --- contractcourt/breacharbiter.go | 32 +++++++----- contractcourt/breacharbiter_test.go | 5 +- input/input.go | 31 +++++++++--- input/signdescriptor.go | 5 ++ input/taproot.go | 24 +++++++++ lnwallet/btcwallet/psbt.go | 4 +- lnwallet/chanfunding/wallet_assembler.go | 62 +++++++++++++++++++++++- lnwallet/rpcwallet/rpcwallet.go | 32 +++++++++++- lnwallet/wallet.go | 33 +++++++++++++ sweep/sweeper_test.go | 4 +- sweep/txgenerator.go | 10 +++- watchtower/lookout/justice_descriptor.go | 44 ++++++++++++----- watchtower/wtclient/backup_task.go | 13 +++-- 13 files changed, 255 insertions(+), 44 deletions(-) diff --git a/contractcourt/breacharbiter.go b/contractcourt/breacharbiter.go index 10f49daa54b..4ff90be819d 100644 --- a/contractcourt/breacharbiter.go +++ b/contractcourt/breacharbiter.go @@ -1096,11 +1096,15 @@ func (bo *breachedOutput) SignDesc() *input.SignDescriptor { // sign descriptor. The method then returns the witness computed by invoking // this function on the first and subsequent calls. func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) (*input.Script, error) { + hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, + txinIdx int) (*input.Script, error) { // First, we ensure that the witness generation function has been // initialized for this breached output. - bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, bo.SignDesc()) + signDesc := bo.SignDesc() + signDesc.PrevOutputFetcher = prevOutputFetcher + bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, signDesc) // Now that we have ensured that the witness generation function has // been initialized, we can proceed to execute it and generate the @@ -1397,8 +1401,8 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64, // Compute the total amount contained in the inputs. var totalAmt btcutil.Amount - for _, input := range inputs { - totalAmt += btcutil.Amount(input.SignDesc().Output.Value) + for _, inp := range inputs { + totalAmt += btcutil.Amount(inp.SignDesc().Output.Value) } // We'll actually attempt to target inclusion within the next two @@ -1424,10 +1428,10 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64, // Next, we add all of the spendable outputs as inputs to the // transaction. - for _, input := range inputs { + for _, inp := range inputs { txn.AddTxIn(&wire.TxIn{ - PreviousOutPoint: *input.OutPoint(), - Sequence: input.BlocksToMaturity(), + PreviousOutPoint: *inp.OutPoint(), + Sequence: inp.BlocksToMaturity(), }) } @@ -1440,7 +1444,11 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64, // Create a sighash cache to improve the performance of hashing and // signing SigHashAll inputs. - hashCache := input.NewTxSigHashesV0Only(txn) + prevOutputFetcher, err := input.MultiPrevOutFetcher(inputs) + if err != nil { + return nil, err + } + hashCache := txscript.NewTxSigHashes(txn, prevOutputFetcher) // Create a closure that encapsulates the process of initializing a // particular output's witness generation function, computing the @@ -1452,7 +1460,7 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64, // transaction using the SpendableOutput's witness generation // function. inputScript, err := so.CraftInputScript( - b.cfg.Signer, txn, hashCache, idx, + b.cfg.Signer, txn, hashCache, prevOutputFetcher, idx, ) if err != nil { return err @@ -1467,8 +1475,8 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64, // Finally, generate a witness for each output and attach it to the // transaction. - for i, input := range inputs { - if err := addWitness(i, input); err != nil { + for i, inp := range inputs { + if err := addWitness(i, inp); err != nil { return nil, err } } @@ -1648,7 +1656,7 @@ func (ret *retributionInfo) Encode(w io.Writer) error { return nil } -// Dencode deserializes a retribution from the passed byte stream. +// Decode deserializes a retribution from the passed byte stream. func (ret *retributionInfo) Decode(r io.Reader) error { var scratch [32]byte diff --git a/contractcourt/breacharbiter_test.go b/contractcourt/breacharbiter_test.go index 7dae490ab76..3cb16c084ee 100644 --- a/contractcourt/breacharbiter_test.go +++ b/contractcourt/breacharbiter_test.go @@ -1386,8 +1386,11 @@ func getSpendTransactions(signer input.Signer, chanPoint *wire.OutPoint, case input.HtlcAcceptedRevoke: fallthrough case input.HtlcOfferedRevoke: + cannedFetcher := txscript.NewCannedPrevOutputFetcher( + nil, 0, + ) inputScript, err := inp.CraftInputScript( - signer, htlcSweep, hashCache, 0, + signer, htlcSweep, hashCache, cannedFetcher, 0, ) if err != nil { return nil, err diff --git a/input/input.go b/input/input.go index 865a761a37f..98ae2c056c3 100644 --- a/input/input.go +++ b/input/input.go @@ -42,6 +42,7 @@ type Input interface { // also nested p2sh outputs. CraftInputScript(signer Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script, error) // BlocksToMaturity returns the relative timelock, as a number of @@ -221,9 +222,13 @@ func NewCsvInputWithCltv(outpoint *wire.OutPoint, witnessType WitnessType, // txIndex within the passed transaction. The input scripts generated by this // method support spending p2wkh, p2wsh, and also nested p2sh outputs. func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) { + hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script, + error) { - witnessFunc := bi.witnessType.WitnessGenerator(signer, bi.SignDesc()) + signDesc := bi.SignDesc() + signDesc.PrevOutputFetcher = prevOutputFetcher + witnessFunc := bi.witnessType.WitnessGenerator(signer, signDesc) return witnessFunc(txn, hashCache, txinIdx) } @@ -260,11 +265,14 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint, // txIndex within the passed transaction. The input scripts generated by this // method support spending p2wkh, p2wsh, and also nested p2sh outputs. func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) { + hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script, + error) { desc := h.signDesc desc.SigHashes = hashCache desc.InputIndex = txinIdx + desc.PrevOutputFetcher = prevOutputFetcher witness, err := SenderHtlcSpendRedeem( signer, &desc, txn, h.preimage, @@ -291,7 +299,9 @@ type HtlcSecondLevelAnchorInput struct { // createWitness creates a witness allowing the passed transaction to // spend the input. createWitness func(signer Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) (wire.TxWitness, error) + hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, + txinIdx int) (wire.TxWitness, error) } // RequiredTxOut returns the tx out needed to be present on the sweep tx for @@ -313,9 +323,12 @@ func (i *HtlcSecondLevelAnchorInput) RequiredLockTime() (uint32, bool) { // method support spending p2wkh, p2wsh, and also nested p2sh outputs. func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, - txinIdx int) (*Script, error) { + prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script, + error) { - witness, err := i.createWitness(signer, txn, hashCache, txinIdx) + witness, err := i.createWitness( + signer, txn, hashCache, prevOutputFetcher, txinIdx, + ) if err != nil { return nil, err } @@ -335,11 +348,13 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx, // 2nd timeout transaction. createWitness := func(signer Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (wire.TxWitness, error) { desc := signDetails.SignDesc - desc.SigHashes = NewTxSigHashesV0Only(txn) + desc.SigHashes = txscript.NewTxSigHashes(txn, prevOutputFetcher) desc.InputIndex = txinIdx + desc.PrevOutputFetcher = prevOutputFetcher return SenderHtlcSpendTimeout( signDetails.PeerSig, signDetails.SigHashType, signer, @@ -373,11 +388,13 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx, // success transaction. createWitness := func(signer Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (wire.TxWitness, error) { desc := signDetails.SignDesc desc.SigHashes = hashCache desc.InputIndex = txinIdx + desc.PrevOutputFetcher = prevOutputFetcher return ReceiverHtlcSpendRedeem( signDetails.PeerSig, signDetails.SigHashType, diff --git a/input/signdescriptor.go b/input/signdescriptor.go index bfcd50fe25e..b97fbb5de06 100644 --- a/input/signdescriptor.go +++ b/input/signdescriptor.go @@ -74,6 +74,11 @@ type SignDescriptor struct { // generating the final sighash for signing. SigHashes *txscript.TxSigHashes + // PrevOutputFetcher is an interface that can return the output + // information on all UTXOs that are being spent in this transaction. + // This MUST be set when spending Taproot outputs. + PrevOutputFetcher txscript.PrevOutputFetcher + // InputIndex is the target input within the transaction that should be // signed. InputIndex int diff --git a/input/taproot.go b/input/taproot.go index a80b51d29b5..e54628f7ac4 100644 --- a/input/taproot.go +++ b/input/taproot.go @@ -1,6 +1,8 @@ package input import ( + "fmt" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" ) @@ -18,3 +20,25 @@ func NewTxSigHashesV0Only(tx *wire.MsgTx) *txscript.TxSigHashes { nilFetcher := txscript.NewCannedPrevOutputFetcher(nil, 0) return txscript.NewTxSigHashes(tx, nilFetcher) } + +// MultiPrevOutFetcher returns a txscript.MultiPrevOutFetcher for the given set +// of inputs. +func MultiPrevOutFetcher(inputs []Input) (*txscript.MultiPrevOutFetcher, error) { + fetcher := txscript.NewMultiPrevOutFetcher(nil) + for _, inp := range inputs { + op := inp.OutPoint() + desc := inp.SignDesc() + + if op == nil { + return nil, fmt.Errorf("missing input outpoint") + } + + if desc == nil || desc.Output == nil { + return nil, fmt.Errorf("missing input utxo information") + } + + fetcher.AddPrevOut(*op, desc.Output) + } + + return fetcher, nil +} diff --git a/lnwallet/btcwallet/psbt.go b/lnwallet/btcwallet/psbt.go index 409277d22dd..ca19e132509 100644 --- a/lnwallet/btcwallet/psbt.go +++ b/lnwallet/btcwallet/psbt.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/btcsuite/btcwallet/wallet" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -107,7 +108,8 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error { // there are inputs that we don't know how to sign, we won't return any // error. So it's possible we're not the final signer. tx := packet.UnsignedTx - sigHashes := input.NewTxSigHashesV0Only(tx) + prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet) + sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher) for idx := range tx.TxIn { in := packet.Inputs[idx] diff --git a/lnwallet/chanfunding/wallet_assembler.go b/lnwallet/chanfunding/wallet_assembler.go index 72cf56ce263..cc14dcac07c 100644 --- a/lnwallet/chanfunding/wallet_assembler.go +++ b/lnwallet/chanfunding/wallet_assembler.go @@ -107,9 +107,15 @@ func (f *FullIntent) CompileFundingTx(extraInputs []*wire.TxIn, // Next, sign all inputs that are ours, collecting the signatures in // order of the inputs. + prevOutFetcher := NewSegWitV0DualFundingPrevOutputFetcher( + f.coinSource, extraInputs, + ) signDesc := input.SignDescriptor{ - HashType: txscript.SigHashAll, - SigHashes: input.NewTxSigHashesV0Only(fundingTx), + HashType: txscript.SigHashAll, + SigHashes: txscript.NewTxSigHashes( + fundingTx, prevOutFetcher, + ), + PrevOutputFetcher: prevOutFetcher, } for i, txIn := range fundingTx.TxIn { // We can only sign this input if it's ours, so we'll ask the @@ -374,3 +380,55 @@ func (w *WalletAssembler) FundingTxAvailable() {} // A compile-time assertion to ensure the WalletAssembler meets the // FundingTxAssembler interface. var _ FundingTxAssembler = (*WalletAssembler)(nil) + +// SegWitV0DualFundingPrevOutputFetcher is a txscript.PrevOutputFetcher that +// knows about local and remote funding inputs. +// +// TODO(guggero): Support dual funding with p2tr inputs, currently only segwit +// v0 inputs are supported. +type SegWitV0DualFundingPrevOutputFetcher struct { + local CoinSource + remote *txscript.MultiPrevOutFetcher +} + +var _ txscript.PrevOutputFetcher = (*SegWitV0DualFundingPrevOutputFetcher)(nil) + +// NewSegWitV0DualFundingPrevOutputFetcher creates a new +// txscript.PrevOutputFetcher from the given local and remote inputs. +// +// NOTE: Since the actual pkScript and amounts aren't passed in, this will just +// make sure that nothing will panic when creating a SegWit v0 sighash. But this +// code will NOT WORK for transactions that spend any Taproot inputs! +func NewSegWitV0DualFundingPrevOutputFetcher(localSource CoinSource, + remoteInputs []*wire.TxIn) txscript.PrevOutputFetcher { + + remote := txscript.NewMultiPrevOutFetcher(nil) + for _, inp := range remoteInputs { + // We add an empty output to prevent the sighash calculation + // from panicking. But this will always detect the inputs as + // SegWig v0! + remote.AddPrevOut(inp.PreviousOutPoint, &wire.TxOut{}) + } + return &SegWitV0DualFundingPrevOutputFetcher{ + local: localSource, + remote: remote, + } +} + +// FetchPrevOutput attempts to fetch the previous output referenced by the +// passed outpoint. +// +// NOTE: This is a part of the txscript.PrevOutputFetcher interface. +func (d *SegWitV0DualFundingPrevOutputFetcher) FetchPrevOutput( + op wire.OutPoint) *wire.TxOut { + + // Try the local source first. This will return nil if our internal + // wallet doesn't know the outpoint. + coin, err := d.local.CoinFromOutPoint(op) + if err == nil && coin != nil { + return &coin.TxOut + } + + // Fall back to the remote + return d.remote.FetchPrevOutput(op) +} diff --git a/lnwallet/rpcwallet/rpcwallet.go b/lnwallet/rpcwallet/rpcwallet.go index 052b3d86c05..4871744e084 100644 --- a/lnwallet/rpcwallet/rpcwallet.go +++ b/lnwallet/rpcwallet/rpcwallet.go @@ -133,9 +133,11 @@ func (r *RPCKeyRing) SendOutputs(outputs []*wire.TxOut, // We know at this point that we only have inputs from our own wallet. // So we can just compute the input script using the remote signer. + outputFetcher := lnwallet.NewWalletPrevOutputFetcher(r.WalletController) signDesc := input.SignDescriptor{ - HashType: txscript.SigHashAll, - SigHashes: input.NewTxSigHashesV0Only(tx), + HashType: txscript.SigHashAll, + SigHashes: txscript.NewTxSigHashes(tx, outputFetcher), + PrevOutputFetcher: outputFetcher, } for i, txIn := range tx.TxIn { // We can only sign this input if it's ours, so we'll ask the @@ -579,6 +581,32 @@ func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor, return nil, fmt.Errorf("error converting TX into PSBT: %v", err) } + // We need to add witness information for all inputs! Otherwise, we'll + // have a problem when attempting to sign a taproot input! + for idx := range packet.Inputs { + // Skip the input we're signing for, that will get a special + // treatment later on. + if idx == signDesc.InputIndex { + continue + } + + txIn := tx.TxIn[idx] + info, err := r.WalletController.FetchInputInfo( + &txIn.PreviousOutPoint, + ) + if err != nil { + log.Warnf("No UTXO info found for index %d "+ + "(prev_outpoint=%v), won't be able to sign "+ + "for taproot output!", idx, + txIn.PreviousOutPoint) + continue + } + packet.Inputs[idx].WitnessUtxo = &wire.TxOut{ + Value: int64(info.Value), + PkScript: info.PkScript, + } + } + // Catch incorrect signing input index, just in case. if signDesc.InputIndex < 0 || signDesc.InputIndex >= len(packet.Inputs) { return nil, fmt.Errorf("invalid input index in sign descriptor") diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index e6145fae366..86eb1a92051 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -2266,3 +2266,36 @@ func validateUpfrontShutdown(shutdown lnwire.DeliveryAddress, return false } } + +// WalletPrevOutputFetcher is a txscript.PrevOutputFetcher that can fetch +// outputs from a given wallet controller. +type WalletPrevOutputFetcher struct { + wc WalletController +} + +// A compile time assertion that WalletPrevOutputFetcher implements the +// txscript.PrevOutputFetcher interface. +var _ txscript.PrevOutputFetcher = (*WalletPrevOutputFetcher)(nil) + +// NewWalletPrevOutputFetcher creates a new WalletPrevOutputFetcher that fetches +// previous outputs from the given wallet controller. +func NewWalletPrevOutputFetcher(wc WalletController) *WalletPrevOutputFetcher { + return &WalletPrevOutputFetcher{ + wc: wc, + } +} + +// FetchPrevOutput attempts to fetch the previous output referenced by the +// passed outpoint. A nil value will be returned if the passed outpoint doesn't +// exist. +func (w *WalletPrevOutputFetcher) FetchPrevOutput(op wire.OutPoint) *wire.TxOut { + utxo, err := w.wc.FetchInputInfo(&op) + if err != nil { + return nil + } + + return &wire.TxOut{ + Value: int64(utxo.Value), + PkScript: utxo.PkScript, + } +} diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index 5ae22748d38..a541ae6f542 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -1623,7 +1623,9 @@ func (i *testInput) RequiredTxOut() *wire.TxOut { // encode the spending outpoint and the tx input index as part of the returned // witness. func (i *testInput) CraftInputScript(_ input.Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) (*input.Script, error) { + hashCache *txscript.TxSigHashes, + prevOutputFetcher txscript.PrevOutputFetcher, + txinIdx int) (*input.Script, error) { // We'll encode the outpoint in the witness, so we can assert that the // expected input was signed at the correct index. diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index 3cbdb7c7b88..76e959380ae 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" @@ -263,13 +264,18 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut, return nil, err } - hashCache := input.NewTxSigHashesV0Only(sweepTx) + prevInputFetcher, err := input.MultiPrevOutFetcher(inputs) + if err != nil { + return nil, fmt.Errorf("error creating prev input fetcher "+ + "for hash cache: %v", err) + } + hashCache := txscript.NewTxSigHashes(sweepTx, prevInputFetcher) // With all the inputs in place, use each output's unique input script // function to generate the final witness required for spending. addInputScript := func(idx int, tso input.Input) error { inputScript, err := tso.CraftInputScript( - signer, sweepTx, hashCache, idx, + signer, sweepTx, hashCache, prevInputFetcher, idx, ) if err != nil { return err diff --git a/watchtower/lookout/justice_descriptor.go b/watchtower/lookout/justice_descriptor.go index 5e21cda6bb0..dac18b048d1 100644 --- a/watchtower/lookout/justice_descriptor.go +++ b/watchtower/lookout/justice_descriptor.go @@ -2,6 +2,7 @@ package lookout import ( "errors" + "fmt" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec/v2" @@ -177,11 +178,11 @@ func (p *JusticeDescriptor) assembleJusticeTxn(txWeight int64, // First, construct add the breached inputs to our justice transaction // and compute the total amount that will be swept. var totalAmt btcutil.Amount - for _, input := range inputs { - totalAmt += btcutil.Amount(input.txOut.Value) + for _, inp := range inputs { + totalAmt += btcutil.Amount(inp.txOut.Value) justiceTxn.AddTxIn(&wire.TxIn{ - PreviousOutPoint: input.outPoint, - Sequence: input.sequence, + PreviousOutPoint: inp.outPoint, + Sequence: inp.sequence, }) } @@ -217,20 +218,22 @@ func (p *JusticeDescriptor) assembleJusticeTxn(txWeight int64, } // Attach each of the provided witnesses to the transaction. - for _, input := range inputs { + prevOutFetcher, err := prevOutFetcher(inputs) + if err != nil { + return nil, fmt.Errorf("error creating previous output "+ + "fetcher: %v", err) + } + for _, inp := range inputs { // Lookup the input's new post-sort position. - i := inputIndex[input.outPoint] - justiceTxn.TxIn[i].Witness = input.witness + i := inputIndex[inp.outPoint] + justiceTxn.TxIn[i].Witness = inp.witness // Validate the reconstructed witnesses to ensure they are valid // for the breached inputs. vm, err := txscript.NewEngine( - input.txOut.PkScript, justiceTxn, i, + inp.txOut.PkScript, justiceTxn, i, txscript.StandardVerifyFlags, - nil, nil, input.txOut.Value, - txscript.NewCannedPrevOutputFetcher( - input.txOut.PkScript, input.txOut.Value, - ), + nil, nil, inp.txOut.Value, prevOutFetcher, ) if err != nil { return nil, err @@ -345,3 +348,20 @@ func buildWitness(witnessStack [][]byte, witnessScript []byte) [][]byte { return witness } + +// prevOutFetcher returns a txscript.MultiPrevOutFetcher for the given set +// of inputs. +func prevOutFetcher(inputs []*breachedInput) (*txscript.MultiPrevOutFetcher, + error) { + + fetcher := txscript.NewMultiPrevOutFetcher(nil) + for _, inp := range inputs { + if inp.txOut == nil { + return nil, fmt.Errorf("missing input utxo information") + } + + fetcher.AddPrevOut(inp.outPoint, inp.txOut) + } + + return fetcher, nil +} diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index 2ae836595e0..6f9d2082a81 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/txsort" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" @@ -262,10 +263,14 @@ func (t *backupTask) craftSessionPayload( // information. This will either be contain both the to-local and // to-remote outputs, or only be the to-local output. inputs := t.inputs() - for prevOutPoint, input := range inputs { + prevOutputFetcher := txscript.NewMultiPrevOutFetcher(nil) + for prevOutPoint, inp := range inputs { + prevOutputFetcher.AddPrevOut( + prevOutPoint, inp.SignDesc().Output, + ) justiceTxn.AddTxIn(&wire.TxIn{ PreviousOutPoint: prevOutPoint, - Sequence: input.BlocksToMaturity(), + Sequence: inp.BlocksToMaturity(), }) } @@ -284,7 +289,7 @@ func (t *backupTask) craftSessionPayload( } // Construct a sighash cache to improve signing performance. - hashCache := input.NewTxSigHashesV0Only(justiceTxn) + hashCache := txscript.NewTxSigHashes(justiceTxn, prevOutputFetcher) // Since the transaction inputs could have been reordered as a result of // the BIP69 sort, create an index mapping each prevout to it's new @@ -303,7 +308,7 @@ func (t *backupTask) craftSessionPayload( // Construct the full witness required to spend this input. inputScript, err := inp.CraftInputScript( - signer, justiceTxn, hashCache, i, + signer, justiceTxn, hashCache, prevOutputFetcher, i, ) if err != nil { return hint, nil, err From c73cf03a558b17a1bc9dbf552a15a77b0c9f0de4 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:46 +0100 Subject: [PATCH 3/9] multi: add p2tr keyspend wallet support --- cmd/lncli/commands.go | 5 +- config_builder.go | 9 +- input/size.go | 34 ++++ input/witnessgen.go | 13 ++ lnrpc/lightning.pb.go | 28 ++- lnrpc/lightning.proto | 3 + lnrpc/lightning.swagger.json | 10 +- lnrpc/marshall_utils.go | 3 + lnrpc/walletrpc/walletkit.pb.go | 267 +++++++++++++------------ lnrpc/walletrpc/walletkit.proto | 1 + lnrpc/walletrpc/walletkit.swagger.json | 12 +- lnrpc/walletrpc/walletkit_server.go | 11 + lntest/itest/lnd_remote_signer_test.go | 13 +- lntest/itest/lnd_taproot_test.go | 83 ++++++++ lntest/itest/lnd_test_list_on_test.go | 4 + lnwallet/btcwallet/btcwallet.go | 26 ++- lnwallet/btcwallet/signer.go | 3 +- lnwallet/interface.go | 3 + rpcserver.go | 16 ++ sweep/sweeper_test.go | 2 +- sweep/txgenerator.go | 21 +- sweep/txgenerator_test.go | 2 +- sweep/walletsweep.go | 4 + sweep/weight_estimator.go | 6 + 24 files changed, 416 insertions(+), 163 deletions(-) create mode 100644 lntest/itest/lnd_taproot_test.go diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index bf54c7650eb..80601d4c64c 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -139,7 +139,8 @@ var newAddressCommand = cli.Command{ Description: ` Generate a wallet new address. Address-types has to be one of: - p2wkh: Pay to witness key hash - - np2wkh: Pay to nested witness key hash`, + - np2wkh: Pay to nested witness key hash + - p2tr: Pay to taproot pubkey`, Action: actionDecorator(newAddress), } @@ -161,6 +162,8 @@ func newAddress(ctx *cli.Context) error { addrType = lnrpc.AddressType_WITNESS_PUBKEY_HASH case "np2wkh": addrType = lnrpc.AddressType_NESTED_PUBKEY_HASH + case "p2tr": + addrType = lnrpc.AddressType_TAPROOT_PUBKEY default: return fmt.Errorf("invalid address type %v, support address type "+ "are: p2wkh and np2wkh", stringAddrType) diff --git a/config_builder.go b/config_builder.go index 0e1bc5560fc..48498173ddd 100644 --- a/config_builder.go +++ b/config_builder.go @@ -1112,7 +1112,14 @@ func importWatchOnlyAccounts(wallet *wallet.Wallet, for _, scope := range scopes { addrSchema := waddrmgr.ScopeAddrMap[waddrmgr.KeyScopeBIP0084] - if scope.Scope.Purpose == waddrmgr.KeyScopeBIP0049Plus.Purpose { + + // We want witness pubkey hash by default, except for BIP49 + // where we want mixed and BIP86 where we want taproot address + // formats. + switch scope.Scope.Purpose { + case waddrmgr.KeyScopeBIP0049Plus.Purpose, + waddrmgr.KeyScopeBIP0086.Purpose: + addrSchema = waddrmgr.ScopeAddrMap[scope.Scope] } diff --git a/input/size.go b/input/size.go index ed6d4cb719a..1b9e8d991ff 100644 --- a/input/size.go +++ b/input/size.go @@ -77,6 +77,18 @@ const ( // - pkscript (p2sh): 23 bytes P2SHOutputSize = 8 + 1 + P2SHSize + // P2TRSize 34 bytes + // - OP_0: 1 byte + // - OP_DATA: 1 byte (x-only public key length) + // - x-only public key length: 32 bytes + P2TRSize = 34 + + // P2TROutputSize 43 bytes + // - value: 8 bytes + // - var_int: 1 byte (pkscript_length) + // - pkscript (p2tr): 34 bytes + P2TROutputSize = 8 + 1 + P2TRSize + // P2PKHScriptSigSize 108 bytes // - OP_DATA: 1 byte (signature length) // - signature @@ -519,6 +531,19 @@ const ( // - witness_script_length: 1 byte // - witness_script (anchor_script) AnchorWitnessSize = 1 + 1 + 73 + 1 + AnchorScriptSize + + // TaprootKeyPathWitnessSize 66 bytes + // - NumberOfWitnessElements: 1 byte + // - sigLength: 1 byte + // - sig: 64 bytes + TaprootKeyPathWitnessSize = 1 + 1 + 64 + + // TaprootKeyPathCustomSighashWitnessSize 67 bytes + // - NumberOfWitnessElements: 1 byte + // - sigLength: 1 byte + // - sig: 64 bytes + // - sighashFlag: 1 byte + TaprootKeyPathCustomSighashWitnessSize = TaprootKeyPathWitnessSize + 1 ) // EstimateCommitTxWeight estimate commitment transaction weight depending on @@ -639,6 +664,15 @@ func (twe *TxWeightEstimator) AddP2WSHOutput() *TxWeightEstimator { return twe } +// AddP2TROutput updates the weight estimate to account for an additional native +// SegWit v1 P2TR output. +func (twe *TxWeightEstimator) AddP2TROutput() *TxWeightEstimator { + twe.outputSize += P2TROutputSize + twe.outputCount++ + + return twe +} + // AddP2SHOutput updates the weight estimate to account for an additional P2SH // output. func (twe *TxWeightEstimator) AddP2SHOutput() *TxWeightEstimator { diff --git a/input/witnessgen.go b/input/witnessgen.go index 9fea9179934..7df388c7c33 100644 --- a/input/witnessgen.go +++ b/input/witnessgen.go @@ -175,6 +175,11 @@ const ( // and CLTV locktime as part of the script enforced lease commitment // type. LeaseHtlcAcceptedSuccessSecondLevel StandardWitnessType = 20 + + // TaprootPubKeySpend is a witness type that allows us to spend a + // regular p2tr output that's sent to an output which is under complete + // control of the backing wallet. + TaprootPubKeySpend StandardWitnessType = 21 ) // String returns a human readable version of the target WitnessType. @@ -245,6 +250,9 @@ func (wt StandardWitnessType) String() string { case LeaseHtlcAcceptedSuccessSecondLevel: return "LeaseHtlcAcceptedSuccessSecondLevel" + case TaprootPubKeySpend: + return "TaprootPubKeySpend" + default: return fmt.Sprintf("Unknown WitnessType: %v", uint32(wt)) } @@ -388,6 +396,8 @@ func (wt StandardWitnessType) WitnessGenerator(signer Signer, case WitnessKeyHash: fallthrough + case TaprootPubKeySpend: + fallthrough case NestedWitnessKeyHash: return signer.ComputeInputScript(tx, desc) @@ -494,6 +504,9 @@ func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) { // The revocation output of a second level output of an HTLC. case HtlcSecondLevelRevoke: return ToLocalPenaltyWitnessSize, false, nil + + case TaprootPubKeySpend: + return TaprootKeyPathCustomSighashWitnessSize, false, nil } return 0, false, fmt.Errorf("unexpected witness type: %v", wt) diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index bd6c0ab9339..2fb6649a9a7 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -92,6 +92,7 @@ func (OutputScriptType) EnumDescriptor() ([]byte, []int) { // //- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0) //- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1) +//- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 4) type AddressType int32 const ( @@ -99,6 +100,8 @@ const ( AddressType_NESTED_PUBKEY_HASH AddressType = 1 AddressType_UNUSED_WITNESS_PUBKEY_HASH AddressType = 2 AddressType_UNUSED_NESTED_PUBKEY_HASH AddressType = 3 + AddressType_TAPROOT_PUBKEY AddressType = 4 + AddressType_UNUSED_TAPROOT_PUBKEY AddressType = 5 ) // Enum value maps for AddressType. @@ -108,12 +111,16 @@ var ( 1: "NESTED_PUBKEY_HASH", 2: "UNUSED_WITNESS_PUBKEY_HASH", 3: "UNUSED_NESTED_PUBKEY_HASH", + 4: "TAPROOT_PUBKEY", + 5: "UNUSED_TAPROOT_PUBKEY", } AddressType_value = map[string]int32{ "WITNESS_PUBKEY_HASH": 0, "NESTED_PUBKEY_HASH": 1, "UNUSED_WITNESS_PUBKEY_HASH": 2, "UNUSED_NESTED_PUBKEY_HASH": 3, + "TAPROOT_PUBKEY": 4, + "UNUSED_TAPROOT_PUBKEY": 5, } ) @@ -18964,15 +18971,18 @@ var file_lightning_proto_rawDesc = []byte{ 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, - 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x08, 0x2a, 0x7d, 0x0a, 0x0b, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x49, - 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, - 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, - 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x55, - 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, - 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x55, - 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, 0x42, - 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x2a, 0x78, 0x0a, 0x0e, 0x43, 0x6f, + 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x08, 0x2a, 0xac, 0x01, 0x0a, 0x0b, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x57, + 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, + 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, + 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, + 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, + 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, + 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, + 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, + 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, + 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, + 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x78, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index 6cf88b6bcfc..088e8f7431b 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -1138,12 +1138,15 @@ message ListUnspentResponse { - `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0) - `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1) +- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 4) */ enum AddressType { WITNESS_PUBKEY_HASH = 0; NESTED_PUBKEY_HASH = 1; UNUSED_WITNESS_PUBKEY_HASH = 2; UNUSED_NESTED_PUBKEY_HASH = 3; + TAPROOT_PUBKEY = 4; + UNUSED_TAPROOT_PUBKEY = 5; } message NewAddressRequest { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index 85f004bc488..cdc66dad343 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -1801,7 +1801,9 @@ "WITNESS_PUBKEY_HASH", "NESTED_PUBKEY_HASH", "UNUSED_WITNESS_PUBKEY_HASH", - "UNUSED_NESTED_PUBKEY_HASH" + "UNUSED_NESTED_PUBKEY_HASH", + "TAPROOT_PUBKEY", + "UNUSED_TAPROOT_PUBKEY" ], "default": "WITNESS_PUBKEY_HASH" }, @@ -2923,10 +2925,12 @@ "WITNESS_PUBKEY_HASH", "NESTED_PUBKEY_HASH", "UNUSED_WITNESS_PUBKEY_HASH", - "UNUSED_NESTED_PUBKEY_HASH" + "UNUSED_NESTED_PUBKEY_HASH", + "TAPROOT_PUBKEY", + "UNUSED_TAPROOT_PUBKEY" ], "default": "WITNESS_PUBKEY_HASH", - "description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)", + "description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)\n- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 4)", "title": "`AddressType` has to be one of:" }, "lnrpcAmount": { diff --git a/lnrpc/marshall_utils.go b/lnrpc/marshall_utils.go index f2567c8f824..4e79f7dea8f 100644 --- a/lnrpc/marshall_utils.go +++ b/lnrpc/marshall_utils.go @@ -94,6 +94,9 @@ func MarshalUtxos(utxos []*lnwallet.Utxo, activeNetParams *chaincfg.Params) ( case lnwallet.NestedWitnessPubKey: addrType = AddressType_NESTED_PUBKEY_HASH + case lnwallet.TaprootPubkey: + addrType = AddressType_TAPROOT_PUBKEY + case lnwallet.UnknownAddressType: continue diff --git a/lnrpc/walletrpc/walletkit.pb.go b/lnrpc/walletrpc/walletkit.pb.go index baddd9d1174..9e3daf086a5 100644 --- a/lnrpc/walletrpc/walletkit.pb.go +++ b/lnrpc/walletrpc/walletkit.pb.go @@ -29,6 +29,7 @@ const ( AddressType_WITNESS_PUBKEY_HASH AddressType = 1 AddressType_NESTED_WITNESS_PUBKEY_HASH AddressType = 2 AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH AddressType = 3 + AddressType_TAPROOT_PUBKEY AddressType = 4 ) // Enum value maps for AddressType. @@ -38,12 +39,14 @@ var ( 1: "WITNESS_PUBKEY_HASH", 2: "NESTED_WITNESS_PUBKEY_HASH", 3: "HYBRID_NESTED_WITNESS_PUBKEY_HASH", + 4: "TAPROOT_PUBKEY", } AddressType_value = map[string]int32{ "UNKNOWN": 0, "WITNESS_PUBKEY_HASH": 1, "NESTED_WITNESS_PUBKEY_HASH": 2, "HYBRID_NESTED_WITNESS_PUBKEY_HASH": 3, + "TAPROOT_PUBKEY": 4, } ) @@ -3207,139 +3210,141 @@ var file_walletrpc_walletkit_proto_rawDesc = []byte{ 0x65, 0x64, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x55, 0x74, 0x78, 0x6f, - 0x73, 0x2a, 0x7a, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x17, 0x0a, - 0x13, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, - 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, - 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, - 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x48, 0x59, 0x42, 0x52, 0x49, 0x44, - 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x2a, 0x99, 0x03, - 0x0a, 0x0b, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, - 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, - 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, - 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x5f, 0x44, 0x45, - 0x4c, 0x41, 0x59, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, - 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x46, 0x46, 0x45, 0x52, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x56, - 0x4f, 0x4b, 0x45, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x43, - 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x10, 0x05, 0x12, - 0x25, 0x0a, 0x21, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x46, 0x46, 0x45, 0x52, 0x45, 0x44, 0x5f, - 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x5f, 0x4c, - 0x45, 0x56, 0x45, 0x4c, 0x10, 0x06, 0x12, 0x26, 0x0a, 0x22, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, - 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, - 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x10, 0x07, 0x12, 0x1f, - 0x0a, 0x1b, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x46, 0x46, 0x45, 0x52, 0x45, 0x44, 0x5f, 0x52, - 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x08, 0x12, - 0x20, 0x0a, 0x1c, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, - 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, - 0x09, 0x12, 0x1c, 0x0a, 0x18, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, - 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x10, 0x0a, 0x12, - 0x14, 0x0a, 0x10, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x48, - 0x41, 0x53, 0x48, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, - 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, - 0x10, 0x0c, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x0d, 0x32, 0xf7, 0x0b, 0x0a, 0x09, 0x57, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x4b, 0x69, 0x74, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x55, - 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x12, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x12, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x4c, - 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x4e, 0x65, 0x78, 0x74, - 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x38, - 0x0a, 0x09, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x13, 0x2e, 0x73, 0x69, - 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, - 0x1a, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x78, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x12, 0x16, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, + 0x73, 0x2a, 0x8e, 0x01, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x17, + 0x0a, 0x13, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, + 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x45, 0x53, 0x54, 0x45, + 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, + 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x48, 0x59, 0x42, 0x52, 0x49, + 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, + 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, + 0x10, 0x04, 0x2a, 0x99, 0x03, 0x0a, 0x0b, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x57, 0x49, + 0x54, 0x4e, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x4d, 0x49, + 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x4b, 0x10, + 0x01, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, + 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x4c, 0x41, 0x59, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, + 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x10, + 0x03, 0x12, 0x17, 0x0a, 0x13, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x46, 0x46, 0x45, 0x52, 0x45, + 0x44, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x48, 0x54, + 0x4c, 0x43, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x56, 0x4f, + 0x4b, 0x45, 0x10, 0x05, 0x12, 0x25, 0x0a, 0x21, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x46, 0x46, + 0x45, 0x52, 0x45, 0x44, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x45, 0x43, + 0x4f, 0x4e, 0x44, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x10, 0x06, 0x12, 0x26, 0x0a, 0x22, 0x48, + 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x55, 0x43, + 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x5f, 0x4c, 0x45, 0x56, 0x45, + 0x4c, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x46, 0x46, 0x45, + 0x52, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, + 0x55, 0x54, 0x10, 0x08, 0x12, 0x20, 0x0a, 0x1c, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x43, 0x43, + 0x45, 0x50, 0x54, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x53, 0x55, 0x43, + 0x43, 0x45, 0x53, 0x53, 0x10, 0x09, 0x12, 0x1c, 0x0a, 0x18, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, + 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x52, 0x45, 0x56, 0x4f, + 0x4b, 0x45, 0x10, 0x0a, 0x12, 0x14, 0x0a, 0x10, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, + 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x45, + 0x53, 0x54, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x4b, 0x45, 0x59, + 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x0c, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4d, 0x4d, 0x49, + 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x0d, 0x32, 0xf7, + 0x0b, 0x0a, 0x09, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x4b, 0x69, 0x74, 0x12, 0x4c, 0x0a, 0x0b, + 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x65, + 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, + 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x44, 0x65, 0x72, 0x69, 0x76, + 0x65, 0x4e, 0x65, 0x78, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x73, 0x69, + 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x12, 0x38, 0x0a, 0x09, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x4b, 0x65, 0x79, + 0x12, 0x13, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x6f, 0x72, 0x1a, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x3b, 0x0a, + 0x08, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x16, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x22, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x12, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, - 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x1d, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, - 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1d, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, - 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, - 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, - 0x0a, 0x07, 0x42, 0x75, 0x6d, 0x70, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x6d, 0x70, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x42, 0x75, 0x6d, 0x70, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x12, 0x1c, - 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x65, - 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x22, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x46, 0x75, 0x6e, 0x64, - 0x50, 0x73, 0x62, 0x74, 0x12, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, - 0x64, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, - 0x08, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x73, 0x62, 0x74, 0x12, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x58, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x21, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x12, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x16, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x73, 0x12, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, + 0x12, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, + 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, + 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x52, 0x0a, 0x0d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, + 0x12, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x07, 0x42, 0x75, 0x6d, 0x70, 0x46, 0x65, 0x65, 0x12, 0x19, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x6d, 0x70, 0x46, + 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x6d, 0x70, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x65, + 0x65, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x5b, 0x0a, 0x10, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, + 0x08, 0x46, 0x75, 0x6e, 0x64, 0x50, 0x73, 0x62, 0x74, 0x12, 0x1a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x73, - 0x62, 0x74, 0x12, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x73, 0x62, 0x74, 0x12, 0x1a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x50, + 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x73, 0x62, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x50, 0x73, 0x62, 0x74, 0x12, 0x1e, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x73, 0x62, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x73, 0x62, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/lnrpc/walletrpc/walletkit.proto b/lnrpc/walletrpc/walletkit.proto index ccabbacf338..d9e7c10650f 100644 --- a/lnrpc/walletrpc/walletkit.proto +++ b/lnrpc/walletrpc/walletkit.proto @@ -334,6 +334,7 @@ enum AddressType { WITNESS_PUBKEY_HASH = 1; NESTED_WITNESS_PUBKEY_HASH = 2; HYBRID_NESTED_WITNESS_PUBKEY_HASH = 3; + TAPROOT_PUBKEY = 4; } message Account { // The name used to identify the account. diff --git a/lnrpc/walletrpc/walletkit.swagger.json b/lnrpc/walletrpc/walletkit.swagger.json index 483e767a25f..fb91e39c25d 100644 --- a/lnrpc/walletrpc/walletkit.swagger.json +++ b/lnrpc/walletrpc/walletkit.swagger.json @@ -52,7 +52,8 @@ "UNKNOWN", "WITNESS_PUBKEY_HASH", "NESTED_WITNESS_PUBKEY_HASH", - "HYBRID_NESTED_WITNESS_PUBKEY_HASH" + "HYBRID_NESTED_WITNESS_PUBKEY_HASH", + "TAPROOT_PUBKEY" ], "default": "UNKNOWN" } @@ -695,10 +696,12 @@ "WITNESS_PUBKEY_HASH", "NESTED_PUBKEY_HASH", "UNUSED_WITNESS_PUBKEY_HASH", - "UNUSED_NESTED_PUBKEY_HASH" + "UNUSED_NESTED_PUBKEY_HASH", + "TAPROOT_PUBKEY", + "UNUSED_TAPROOT_PUBKEY" ], "default": "WITNESS_PUBKEY_HASH", - "description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)", + "description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)\n- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 4)", "title": "`AddressType` has to be one of:" }, "lnrpcOutPoint": { @@ -1015,7 +1018,8 @@ "UNKNOWN", "WITNESS_PUBKEY_HASH", "NESTED_WITNESS_PUBKEY_HASH", - "HYBRID_NESTED_WITNESS_PUBKEY_HASH" + "HYBRID_NESTED_WITNESS_PUBKEY_HASH", + "TAPROOT_PUBKEY" ], "default": "UNKNOWN" }, diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index 1ee37276f3b..946f1298ff7 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -1347,6 +1347,9 @@ func marshalWalletAccount(internalScope waddrmgr.KeyScope, case waddrmgr.KeyScopeBIP0084: addrType = AddressType_WITNESS_PUBKEY_HASH + case waddrmgr.KeyScopeBIP0086: + addrType = AddressType_TAPROOT_PUBKEY + case internalScope: addrType = AddressType_WITNESS_PUBKEY_HASH @@ -1405,6 +1408,10 @@ func (w *WalletKit) ListAccounts(ctx context.Context, keyScope := waddrmgr.KeyScopeBIP0049Plus keyScopeFilter = &keyScope + case AddressType_TAPROOT_PUBKEY: + keyScope := waddrmgr.KeyScopeBIP0086 + keyScopeFilter = &keyScope + default: return nil, fmt.Errorf("unhandled address type %v", req.AddressType) } @@ -1460,6 +1467,10 @@ func parseAddrType(addrType AddressType, addrTyp := waddrmgr.WitnessPubKey return &addrTyp, nil + case AddressType_TAPROOT_PUBKEY: + addrTyp := waddrmgr.TaprootPubKey + return &addrTyp, nil + default: return nil, fmt.Errorf("unhandled address type %v", addrType) } diff --git a/lntest/itest/lnd_remote_signer_test.go b/lntest/itest/lnd_remote_signer_test.go index 18027f5c2c0..ecd564519db 100644 --- a/lntest/itest/lnd_remote_signer_test.go +++ b/lntest/itest/lnd_remote_signer_test.go @@ -24,7 +24,7 @@ var ( accounts = []*lnrpc.WatchOnlyAccount{{ Purpose: waddrmgr.KeyScopeBIP0049Plus.Purpose, - // We always use the mainnet coin type for our BIP49/84 + // We always use the mainnet coin type for our BIP49/84/86 // addresses! CoinType: 0, Account: 0, @@ -33,13 +33,22 @@ var ( "FPhTq", }, { Purpose: waddrmgr.KeyScopeBIP0084.Purpose, - // We always use the mainnet coin type for our BIP49/84 + // We always use the mainnet coin type for our BIP49/84/86 // addresses! CoinType: 0, Account: 0, Xpub: "tpubDDWAWrSLRSFrG1KdqXMQQyTKYGSKLKaY7gxpvK7RdV3e3Dkhvu" + "W2GgsFvsPN4RGmuoYtUgZ1LHZE8oftz7T4mzc1BxGt5rt8zJcVQi" + "KTPPV", + }, { + Purpose: waddrmgr.KeyScopeBIP0086.Purpose, + // We always use the mainnet coin type for our BIP49/84/86 + // addresses! + CoinType: 0, + Account: 0, + Xpub: "tpubDDtdXpdJFU2zFKWHJwe5M2WtYtcV7qSWtKohT9VP9zarNSwKnm" + + "kwDQawsu1vUf9xwXhUDYXbdUqpcrRTn9bLyW4BAVRimZ4K7r5o1J" + + "S924u", }} ) diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go new file mode 100644 index 00000000000..89f24de8641 --- /dev/null +++ b/lntest/itest/lnd_taproot_test.go @@ -0,0 +1,83 @@ +package itest + +import ( + "context" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/stretchr/testify/require" +) + +// testTaproot ensures that the daemon can send to and spend from taproot (p2tr) +// outputs. +func testTaproot(net *lntest.NetworkHarness, t *harnessTest) { + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + + // We'll start the test by sending Alice some coins, which she'll use to + // send to herself on a p2tr output. + net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, net.Alice) + + // Let's create a p2tr address now. + p2trResp, err := net.Alice.NewAddress(ctxt, &lnrpc.NewAddressRequest{ + Type: lnrpc.AddressType_TAPROOT_PUBKEY, + }) + require.NoError(t.t, err) + + // Assert this is a segwit v1 address that starts with bcrt1p. + require.Contains( + t.t, p2trResp.Address, net.Miner.ActiveNet.Bech32HRPSegwit+"1p", + ) + + // Send the coins from Alice's wallet to her own, but to the new p2tr + // address. + _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ + Addr: p2trResp.Address, + Amount: 0.5 * btcutil.SatoshiPerBitcoin, + }) + require.NoError(t.t, err) + + txid, err := waitForTxInMempool(net.Miner.Client, defaultTimeout) + require.NoError(t.t, err) + + // Wait until bob has seen the tx and considers it as owned. + p2trOutputIndex := getOutputIndex(t, net.Miner, txid, p2trResp.Address) + op := &lnrpc.OutPoint{ + TxidBytes: txid[:], + OutputIndex: uint32(p2trOutputIndex), + } + assertWalletUnspent(t, net.Alice, op) + + // Mine a block to clean up the mempool. + mineBlocks(t, net, 1, 1) + + // Let's sweep the whole wallet to a new p2tr address, making sure we + // can sign transactions with v0 and v1 inputs. + p2trResp, err = net.Alice.NewAddress(ctxt, &lnrpc.NewAddressRequest{ + Type: lnrpc.AddressType_TAPROOT_PUBKEY, + }) + require.NoError(t.t, err) + + _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ + Addr: p2trResp.Address, + SendAll: true, + }) + require.NoError(t.t, err) + + // Wait until the wallet cleaning sweep tx is found. + txid, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout) + require.NoError(t.t, err) + + // Wait until bob has seen the tx and considers it as owned. + p2trOutputIndex = getOutputIndex(t, net.Miner, txid, p2trResp.Address) + op = &lnrpc.OutPoint{ + TxidBytes: txid[:], + OutputIndex: uint32(p2trOutputIndex), + } + assertWalletUnspent(t, net.Alice, op) + + // Mine another block to clean up the mempool. + mineBlocks(t, net, 1, 1) +} diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index 72c2ed0d032..28a3a9b6a70 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -387,4 +387,8 @@ var allTestCases = []*testCase{ name: "3rd party anchor spend", test: testAnchorThirdPartySpend, }, + { + name: "taproot", + test: testTaproot, + }, } diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index d6a63b0b896..5a19f5cfbf3 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -461,6 +461,8 @@ func (b *BtcWallet) keyScopeForAccountAddr(accountName string, addrKeyScope = waddrmgr.KeyScopeBIP0084 case lnwallet.NestedWitnessPubKey: addrKeyScope = waddrmgr.KeyScopeBIP0049Plus + case lnwallet.TaprootPubkey: + addrKeyScope = waddrmgr.KeyScopeBIP0086 default: return waddrmgr.KeyScope{}, 0, fmt.Errorf("unknown address type") @@ -575,7 +577,7 @@ func (b *BtcWallet) ListAccounts(name string, // Only the name filter was provided. case name != "" && keyScope == nil: // If the name corresponds to the default or imported accounts, - // we'll return them for both of our supported key scopes. + // we'll return them for all our supported key scopes. if name == lnwallet.DefaultAccountName || name == waddrmgr.ImportedAddrAccountName { @@ -594,6 +596,14 @@ func (b *BtcWallet) ListAccounts(name string, return nil, err } res = append(res, a2) + + a3, err := b.wallet.AccountPropertiesByName( + waddrmgr.KeyScopeBIP0086, name, + ) + if err != nil { + return nil, err + } + res = append(res, a3) break } @@ -642,6 +652,15 @@ func (b *BtcWallet) ListAccounts(name string, res = append(res, &account.AccountProperties) } + accounts, err = b.wallet.Accounts(waddrmgr.KeyScopeBIP0086) + if err != nil { + return nil, err + } + for _, account := range accounts.Accounts { + account := account + res = append(res, &account.AccountProperties) + } + accounts, err = b.wallet.Accounts(waddrmgr.KeyScope{ Purpose: keychain.BIP0043Purpose, Coin: b.cfg.CoinType, @@ -912,10 +931,13 @@ func (b *BtcWallet) ListUnspentWitness(minConfs, maxConfs int32, // wallet are nested p2pkh. We can't check the redeem script because // the btcwallet service does not include it. addressType = lnwallet.NestedWitnessPubKey + } else if txscript.IsPayToTaproot(pkScript) { + addressType = lnwallet.TaprootPubkey } if addressType == lnwallet.WitnessPubKey || - addressType == lnwallet.NestedWitnessPubKey { + addressType == lnwallet.NestedWitnessPubKey || + addressType == lnwallet.TaprootPubkey { txid, err := chainhash.NewHashFromStr(output.TxID) if err != nil { diff --git a/lnwallet/btcwallet/signer.go b/lnwallet/btcwallet/signer.go index c9c3c6c44f6..cbbb7283a9a 100644 --- a/lnwallet/btcwallet/signer.go +++ b/lnwallet/btcwallet/signer.go @@ -129,7 +129,8 @@ func (b *BtcWallet) deriveKeyByBIP32Path(path []uint32) (*btcec.PrivateKey, // Is it a standard, BIP defined purpose that the wallet understands? case waddrmgr.KeyScopeBIP0044.Purpose, waddrmgr.KeyScopeBIP0049Plus.Purpose, - waddrmgr.KeyScopeBIP0084.Purpose: + waddrmgr.KeyScopeBIP0084.Purpose, + waddrmgr.KeyScopeBIP0086.Purpose: // We're going to continue below the switch statement to avoid // unnecessary indentation for this default case. diff --git a/lnwallet/interface.go b/lnwallet/interface.go index f91b1ae8192..e9c06b5e2ca 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -42,6 +42,9 @@ const ( // NestedWitnessPubKey represents a p2sh output which is itself a // nested p2wkh output. NestedWitnessPubKey + + // TaprootPubkey represents a p2tr key path spending address. + TaprootPubkey ) var ( diff --git a/rpcserver.go b/rpcserver.go index b3696167f8a..6a08b342d08 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1496,6 +1496,14 @@ func (r *rpcServer) NewAddress(ctx context.Context, return nil, err } + case lnrpc.AddressType_TAPROOT_PUBKEY: + addr, err = r.server.cc.Wallet.NewAddress( + lnwallet.TaprootPubkey, false, account, + ) + if err != nil { + return nil, err + } + case lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH: addr, err = r.server.cc.Wallet.LastUnusedAddress( lnwallet.WitnessPubKey, account, @@ -1511,6 +1519,14 @@ func (r *rpcServer) NewAddress(ctx context.Context, if err != nil { return nil, err } + + case lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY: + addr, err = r.server.cc.Wallet.LastUnusedAddress( + lnwallet.TaprootPubkey, account, + ) + if err != nil { + return nil, err + } } rpcsLog.Debugf("[newaddress] account=%v type=%v addr=%v", account, diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index a541ae6f542..5f4830711e7 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -355,7 +355,7 @@ func assertTxFeeRate(t *testing.T, tx *wire.MsgTx, outputAmt := tx.TxOut[0].Value fee := btcutil.Amount(inputAmt - outputAmt) - _, estimator := getWeightEstimate(inputs, nil, 0) + _, estimator := getWeightEstimate(inputs, nil, 0, nil) txWeight := estimator.weight() expectedFee := expectedFeeRate.FeeForWeight(int64(txWeight)) diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index 76e959380ae..95da8a7647d 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -139,7 +139,9 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut, feePerKw chainfee.SatPerKWeight, signer input.Signer) (*wire.MsgTx, error) { - inputs, estimator := getWeightEstimate(inputs, outputs, feePerKw) + inputs, estimator := getWeightEstimate( + inputs, outputs, feePerKw, changePkScript, + ) txFee := estimator.fee() var ( @@ -312,7 +314,8 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut, // getWeightEstimate returns a weight estimate for the given inputs. // Additionally, it returns counts for the number of csv and cltv inputs. func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut, - feeRate chainfee.SatPerKWeight) ([]input.Input, *weightEstimator) { + feeRate chainfee.SatPerKWeight, outputPkScript []byte) ([]input.Input, + *weightEstimator) { // We initialize a weight estimator so we can accurately asses the // amount of fees we need to pay for this sweep transaction. @@ -327,14 +330,18 @@ func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut, } // If there is any leftover change after paying to the given outputs - // and required outputs, it will go to a single segwit p2wkh address. - // This will be our change address, so ensure it contributes to our - // weight estimate. Note that if we have other outputs, we might end up - // creating a sweep tx without a change output. It is okay to add the + // and required outputs, it will go to a single segwit p2wkh or p2tr + // address. This will be our change address, so ensure it contributes to + // our weight estimate. Note that if we have other outputs, we might end + // up creating a sweep tx without a change output. It is okay to add the // change output to the weight estimate regardless, since the estimated // fee will just be subtracted from this already dust output, and // trimmed. - weightEstimate.addP2WKHOutput() + if txscript.IsPayToTaproot(outputPkScript) { + weightEstimate.addP2TROutput() + } else { + weightEstimate.addP2WKHOutput() + } // For each output, use its witness type to determine the estimate // weight of its witness, and add it to the proper set of spendable diff --git a/sweep/txgenerator_test.go b/sweep/txgenerator_test.go index f8a6b8ac4f0..960da63d31e 100644 --- a/sweep/txgenerator_test.go +++ b/sweep/txgenerator_test.go @@ -39,7 +39,7 @@ func TestWeightEstimate(t *testing.T) { )) } - _, estimator := getWeightEstimate(inputs, nil, 0) + _, estimator := getWeightEstimate(inputs, nil, 0, nil) weight := int64(estimator.weight()) if weight != expectedWeight { t.Fatalf("unexpected weight. expected %d but got %d.", diff --git a/sweep/walletsweep.go b/sweep/walletsweep.go index e541c190e4e..6044b85f895 100644 --- a/sweep/walletsweep.go +++ b/sweep/walletsweep.go @@ -255,6 +255,10 @@ func CraftSweepAllTx(feeRate chainfee.SatPerKWeight, blockHeight uint32, case lnwallet.NestedWitnessPubKey: witnessType = input.NestedWitnessKeyHash + case lnwallet.TaprootPubkey: + witnessType = input.TaprootPubKeySpend + signDesc.HashType = txscript.SigHashDefault + // All other output types we count as unknown and will fail to // sweep. default: diff --git a/sweep/weight_estimator.go b/sweep/weight_estimator.go index ad8c978ba3d..aba61cd92cb 100644 --- a/sweep/weight_estimator.go +++ b/sweep/weight_estimator.go @@ -77,6 +77,12 @@ func (w *weightEstimator) addP2WKHOutput() { w.estimator.AddP2WKHOutput() } +// addP2TROutput updates the weight estimate to account for an additional native +// SegWit v1 P2TR output. +func (w *weightEstimator) addP2TROutput() { + w.estimator.AddP2TROutput() +} + // addOutput updates the weight estimate to account for the known // output given. func (w *weightEstimator) addOutput(txOut *wire.TxOut) { From 108f49f23bd0995ef80f647d6fef9da301e36e56 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:47 +0100 Subject: [PATCH 4/9] input+signer: test wallet script import --- input/taproot.go | 53 ++++++++++++ lnwallet/btcwallet/btcwallet.go | 20 +++++ lnwallet/btcwallet/psbt_test.go | 2 +- lnwallet/btcwallet/signer_test.go | 129 ++++++++++++++++++++++++++++-- 4 files changed, 197 insertions(+), 7 deletions(-) diff --git a/input/taproot.go b/input/taproot.go index e54628f7ac4..56a2c1a83a7 100644 --- a/input/taproot.go +++ b/input/taproot.go @@ -3,8 +3,11 @@ package input import ( "fmt" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcwallet/waddrmgr" + secp "github.com/decred/dcrd/dcrec/secp256k1/v4" ) // NewTxSigHashesV0Only returns a new txscript.TxSigHashes instance that will @@ -42,3 +45,53 @@ func MultiPrevOutFetcher(inputs []Input) (*txscript.MultiPrevOutFetcher, error) return fetcher, nil } + +// TapscriptFullTree creates a waddrmgr.Tapscript for the given internal key and +// tree leaves. +func TapscriptFullTree(internalKey *btcec.PublicKey, + allTreeLeaves ...txscript.TapLeaf) *waddrmgr.Tapscript { + + tree := txscript.AssembleTaprootScriptTree(allTreeLeaves...) + rootHash := tree.RootNode.TapHash() + tapKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash[:]) + + var outputKeyYIsOdd bool + if tapKey.SerializeCompressed()[0] == secp.PubKeyFormatCompressedOdd { + outputKeyYIsOdd = true + } + + return &waddrmgr.Tapscript{ + Type: waddrmgr.TapscriptTypeFullTree, + ControlBlock: &txscript.ControlBlock{ + InternalKey: internalKey, + OutputKeyYIsOdd: outputKeyYIsOdd, + LeafVersion: txscript.BaseLeafVersion, + }, + Leaves: allTreeLeaves, + } +} + +// TapscriptPartialReveal creates a waddrmgr.Tapscript for the given internal +// key and revealed script. +func TapscriptPartialReveal(internalKey *btcec.PublicKey, + revealedLeaf txscript.TapLeaf, + inclusionProof [32]byte) *waddrmgr.Tapscript { + + controlBlock := &txscript.ControlBlock{ + InternalKey: internalKey, + LeafVersion: txscript.BaseLeafVersion, + InclusionProof: inclusionProof[:], + } + rootHash := controlBlock.RootHash(revealedLeaf.Script) + tapKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash) + + if tapKey.SerializeCompressed()[0] == secp.PubKeyFormatCompressedOdd { + controlBlock.OutputKeyYIsOdd = true + } + + return &waddrmgr.Tapscript{ + Type: waddrmgr.TapscriptTypePartialReveal, + ControlBlock: controlBlock, + RevealedScript: revealedLeaf.Script, + } +} diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 5a19f5cfbf3..0cb6b5ad3ae 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -750,6 +750,26 @@ func (b *BtcWallet) ImportPublicKey(pubKey *btcec.PublicKey, return b.wallet.ImportPublicKey(pubKey, addrType) } +// ImportTaprootScript imports a user-provided taproot script into the address +// manager. The imported script will act as a pay-to-taproot address. +func (b *BtcWallet) ImportTaprootScript(scope waddrmgr.KeyScope, + tapscript *waddrmgr.Tapscript) (waddrmgr.ManagedAddress, error) { + + // We want to be able to import script addresses into a watch-only + // wallet, which is only possible if we don't encrypt the script with + // the private key encryption key. By specifying the script as being + // "not secret", we can also decrypt the script in a watch-only wallet. + const isSecretScript = false + + // Currently, only v1 (Taproot) scripts are supported. We don't even + // know what a v2 witness version would look like at this point. + const witnessVersionTaproot byte = 1 + + return b.wallet.ImportTaprootScript( + scope, tapscript, nil, witnessVersionTaproot, isSecretScript, + ) +} + // SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to // the specified outputs. In the case the wallet has insufficient funds, or the // outputs are non-standard, a non-nil error will be returned. diff --git a/lnwallet/btcwallet/psbt_test.go b/lnwallet/btcwallet/psbt_test.go index d4fcfc4ece5..467d59b5d11 100644 --- a/lnwallet/btcwallet/psbt_test.go +++ b/lnwallet/btcwallet/psbt_test.go @@ -250,7 +250,7 @@ func serializeTxWitness(txWitness wire.TxWitness) ([]byte, error) { // TestSignPsbt tests the PSBT signing functionality. func TestSignPsbt(t *testing.T) { - w, cleanup := newTestWallet(t, netParams, seedBytes) + w, _, cleanup := newTestWallet(t, netParams, seedBytes) defer cleanup() testCases := []struct { diff --git a/lnwallet/btcwallet/signer_test.go b/lnwallet/btcwallet/signer_test.go index b946f3acfa7..949977c82d8 100644 --- a/lnwallet/btcwallet/signer_test.go +++ b/lnwallet/btcwallet/signer_test.go @@ -2,17 +2,25 @@ package btcwallet import ( "encoding/hex" + "fmt" "io/ioutil" + "math" "os" "testing" "time" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/integration/rpctest" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/chain" + "github.com/btcsuite/btcwallet/waddrmgr" "github.com/lightningnetwork/lnd/blockcache" + "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/stretchr/testify/require" ) @@ -32,6 +40,29 @@ var ( // which is a special case for the BIP49/84 addresses in btcwallet). firstAddress = "bcrt1qgdlgjc5ede7fjv350wcjqat80m0zsmfaswsj9p" + // firstAddressTaproot is the first address that we should get from the + // wallet when deriving a taproot address. + firstAddressTaproot = "bcrt1ps8c222fgysvnsj2m8hxk8khy6wthcrhv9va9z3t4" + + "h3qeyz65sh4qqwvdgc" + + testPubKeyBytes, _ = hex.DecodeString( + "037a67771635344641d4b56aac33cd5f7a265b59678dce3aec31b89125e3" + + "b8b9b2", + ) + testPubKey, _ = btcec.ParsePubKey(testPubKeyBytes) + testTaprootKeyBytes, _ = hex.DecodeString( + "03f068684c9141027318eed958dccbf4f7f748700e1da53315630d82a362" + + "d6a887", + ) + testTaprootKey, _ = btcec.ParsePubKey(testTaprootKeyBytes) + + testTapscriptAddr = "bcrt1p7p5xsny3gyp8xx8wm9vdejl57lm5suqwrkjnx9trpk" + + "p2xckk4zrs4xehl8" + testTapscriptPkScript = append( + []byte{txscript.OP_1, txscript.OP_DATA_32}, + schnorr.SerializePubKey(testTaprootKey)..., + ) + testCases = []struct { name string path []uint32 @@ -133,7 +164,7 @@ var ( // BIP32 key path correctly. func TestBip32KeyDerivation(t *testing.T) { netParams := &chaincfg.RegressionNetParams - w, cleanup := newTestWallet(t, netParams, seedBytes) + w, _, cleanup := newTestWallet(t, netParams, seedBytes) defer cleanup() // This is just a sanity check that the wallet was initialized @@ -167,8 +198,94 @@ func TestBip32KeyDerivation(t *testing.T) { } } +// TestScriptImport tests the btcwallet's tapscript import capabilities by +// importing both a full taproot script tree and a partially revealed branch +// with a proof to make sure the resulting addresses match up. +func TestScriptImport(t *testing.T) { + netParams := &chaincfg.RegressionNetParams + w, miner, cleanup := newTestWallet(t, netParams, seedBytes) + defer cleanup() + + firstDerivedAddr, err := w.NewAddress( + lnwallet.TaprootPubkey, false, lnwallet.DefaultAccountName, + ) + require.NoError(t, err) + require.Equal(t, firstAddressTaproot, firstDerivedAddr.String()) + + scope := waddrmgr.KeyScopeBIP0086 + _, err = w.InternalWallet().Manager.FetchScopedKeyManager(scope) + require.NoError(t, err) + + // Let's create a taproot script output now. This is a hash lock with a + // simple preimage of "foobar". + builder := txscript.NewScriptBuilder() + builder.AddOp(txscript.OP_DUP) + builder.AddOp(txscript.OP_HASH160) + builder.AddData(btcutil.Hash160([]byte("foobar"))) + builder.AddOp(txscript.OP_EQUALVERIFY) + script1, err := builder.Script() + require.NoError(t, err) + leaf1 := txscript.NewBaseTapLeaf(script1) + + // Let's add a second script output as well to test the partial reveal. + builder = txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(testPubKey)) + builder.AddOp(txscript.OP_CHECKSIG) + script2, err := builder.Script() + require.NoError(t, err) + leaf2 := txscript.NewBaseTapLeaf(script2) + + // Our first test case is storing the script with all its leaves. + tapscript1 := input.TapscriptFullTree(testPubKey, leaf1, leaf2) + + taprootKey1, err := tapscript1.TaprootKey() + require.NoError(t, err) + require.Equal( + t, testTaprootKey.SerializeCompressed(), + taprootKey1.SerializeCompressed(), + ) + + addr1, err := w.ImportTaprootScript(scope, tapscript1) + require.NoError(t, err) + + require.Equal(t, testTapscriptAddr, addr1.Address().String()) + pkScript, err := txscript.PayToAddrScript(addr1.Address()) + require.NoError(t, err) + require.Equal(t, testTapscriptPkScript, pkScript) + + // Send some coins to the taproot address now and wait until they are + // seen as unconfirmed. + _, err = miner.SendOutputs([]*wire.TxOut{{ + Value: btcutil.SatoshiPerBitcoin, + PkScript: pkScript, + }}, 1) + require.NoError(t, err) + + var utxos []*lnwallet.Utxo + require.Eventually(t, func() bool { + utxos, err = w.ListUnspentWitness(0, math.MaxInt32, "") + require.NoError(t, err) + + return len(utxos) == 1 + }, time.Minute, 50*time.Millisecond) + require.Equal(t, testTapscriptPkScript, utxos[0].PkScript) + + // Now, as a last test, make sure that when we try adding an address + // with partial script reveal, we get an error that the address already + // exists. + tapscript2 := input.TapscriptPartialReveal( + testPubKey, leaf1, leaf2.TapHash(), + ) + _, err = w.ImportTaprootScript(scope, tapscript2) + require.Error(t, err) + require.Contains(t, err.Error(), fmt.Sprintf( + "address for script hash/key %x already exists", + schnorr.SerializePubKey(testTaprootKey), + )) +} + func newTestWallet(t *testing.T, netParams *chaincfg.Params, - seedBytes []byte) (*BtcWallet, func()) { + seedBytes []byte) (*BtcWallet, *rpctest.Harness, func()) { tempDir, err := ioutil.TempDir("", "lnwallet") if err != nil { @@ -176,7 +293,7 @@ func newTestWallet(t *testing.T, netParams *chaincfg.Params, t.Fatalf("creating temp dir failed: %v", err) } - chainBackend, backendCleanup := getChainBackend(t, netParams) + chainBackend, miner, backendCleanup := getChainBackend(t, netParams) cleanup := func() { _ = os.RemoveAll(tempDir) backendCleanup() @@ -206,12 +323,12 @@ func newTestWallet(t *testing.T, netParams *chaincfg.Params, t.Fatalf("starting wallet failed: %v", err) } - return w, cleanup + return w, miner, cleanup } // getChainBackend returns a simple btcd based chain backend to back the wallet. func getChainBackend(t *testing.T, netParams *chaincfg.Params) (chain.Interface, - func()) { + *rpctest.Harness, func()) { miningNode, err := rpctest.New(netParams, nil, nil, "") require.NoError(t, err) @@ -230,7 +347,7 @@ func getChainBackend(t *testing.T, netParams *chaincfg.Params) (chain.Interface, ) require.NoError(t, err) - return chainClient, func() { + return chainClient, miningNode, func() { _ = miningNode.TearDown() } } From 5109c5572125c4751ce176bb7a3f80246c886461 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:48 +0100 Subject: [PATCH 5/9] itest: assert P2TR on-chain fund recovery --- lntest/harness_net.go | 11 +++++++ lntest/itest/lnd_recovery_test.go | 53 ++++++++++++++++++++----------- lntest/itest/test_harness.go | 1 + 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/lntest/harness_net.go b/lntest/harness_net.go index 4bdc8dbe85a..fc88e28fbd7 100644 --- a/lntest/harness_net.go +++ b/lntest/harness_net.go @@ -1452,6 +1452,17 @@ func (n *NetworkHarness) SendCoinsNP2WKH(t *testing.T, amt btcutil.Amount, ) } +// SendCoinsP2TR attempts to send amt satoshis from the internal mining node +// to the targeted lightning node using a P2TR address. +func (n *NetworkHarness) SendCoinsP2TR(t *testing.T, amt btcutil.Amount, + target *HarnessNode) { + + err := n.sendCoins(amt, target, lnrpc.AddressType_TAPROOT_PUBKEY, true) + require.NoErrorf( + t, err, "unable to send P2TR coins for %s", target.Cfg.Name, + ) +} + // sendCoins attempts to send amt satoshis from the internal mining node to the // targeted lightning node. The confirmed boolean indicates whether the // transaction that pays to the target should confirm. diff --git a/lntest/itest/lnd_recovery_test.go b/lntest/itest/lnd_recovery_test.go index 101c3dbba71..ce9cc799116 100644 --- a/lntest/itest/lnd_recovery_test.go +++ b/lntest/itest/lnd_recovery_test.go @@ -220,15 +220,24 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) { Type: AddrTypeNestedPubkeyHash, } + newP2TRAddrReq := &lnrpc.NewAddressRequest{ + Type: AddrTypeTaprootPubkey, + } + // Generate and skip the number of addresses requested. + ctxt, cancel := context.WithTimeout( + ctxb, defaultTimeout, + ) + defer cancel() for i := 0; i < nskip; i++ { - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) _, err = node.NewAddress(ctxt, newP2WKHAddrReq) require.NoError(t.t, err) - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) _, err = node.NewAddress(ctxt, newNP2WKHAddrReq) require.NoError(t.t, err) + + _, err = node.NewAddress(ctxt, newP2TRAddrReq) + require.NoError(t.t, err) } // Send one BTC to the next P2WKH address. @@ -238,6 +247,11 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) { net.SendCoinsNP2WKH( t.t, btcutil.SatoshiPerBitcoin, node, ) + + // Add another whole coin to the P2TR address. + net.SendCoinsP2TR( + t.t, btcutil.SatoshiPerBitcoin, node, + ) } } @@ -256,26 +270,26 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) { // the two transactions above. We should also now have 2 UTXOs in the // wallet at the end of the recovery attempt. // - // After, we will generate and skip 9 P2WKH and NP2WKH addresses, and - // send another BTC to the subsequent 10th address in each derivation - // path. - restoreCheckBalance(2*btcutil.SatoshiPerBitcoin, 2, 1, skipAndSend(9)) + // After, we will generate and skip 9 P2WKH, NP2WKH and P2TR addresses, + // and send another BTC to the subsequent 10th address in each + // derivation path. + restoreCheckBalance(3*btcutil.SatoshiPerBitcoin, 3, 1, skipAndSend(9)) // Check that using a recovery window of 9 does not find the two most // recent txns. - restoreCheckBalance(2*btcutil.SatoshiPerBitcoin, 2, 9, nil) + restoreCheckBalance(3*btcutil.SatoshiPerBitcoin, 3, 9, nil) // Extending our recovery window to 10 should find the most recent - // transactions, leaving the wallet with 4 BTC total. We should also + // transactions, leaving the wallet with 6 BTC total. We should also // learn of the two additional UTXOs created above. // // After, we will skip 19 more addrs, sending to the 20th address past // our last found address, and repeat the same checks. - restoreCheckBalance(4*btcutil.SatoshiPerBitcoin, 4, 10, skipAndSend(19)) + restoreCheckBalance(6*btcutil.SatoshiPerBitcoin, 6, 10, skipAndSend(19)) // Check that recovering with a recovery window of 19 fails to find the // most recent transactions. - restoreCheckBalance(4*btcutil.SatoshiPerBitcoin, 4, 19, nil) + restoreCheckBalance(6*btcutil.SatoshiPerBitcoin, 6, 19, nil) // Ensure that using a recovery window of 20 succeeds with all UTXOs // found and the final balance reflected. @@ -285,11 +299,11 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) { // fixed bug in the wallet in which change addresses could at times be // created outside of the default key scopes. Recovery only used to be // performed on the default key scopes, so ideally this test case - // would've caught the bug earlier. Carol has received 6 BTC so far from - // the miner, we'll send 5 back to ensure all of her UTXOs get spent to + // would've caught the bug earlier. Carol has received 9 BTC so far from + // the miner, we'll send 8 back to ensure all of her UTXOs get spent to // avoid fee discrepancies and a change output is formed. - const minerAmt = 5 * btcutil.SatoshiPerBitcoin - const finalBalance = 6 * btcutil.SatoshiPerBitcoin + const minerAmt = 8 * btcutil.SatoshiPerBitcoin + const finalBalance = 9 * btcutil.SatoshiPerBitcoin promptChangeAddr := func(node *lntest.HarnessNode) { t.t.Helper() @@ -310,12 +324,13 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) { block := mineBlocks(t, net, 1, 1)[0] assertTxInBlock(t, block, txid) } - restoreCheckBalance(finalBalance, 6, 20, promptChangeAddr) + restoreCheckBalance(finalBalance, 9, 20, promptChangeAddr) - // We should expect a static fee of 27750 satoshis for spending 6 inputs - // (3 P2WPKH, 3 NP2WPKH) to two P2WPKH outputs. Carol should therefore - // only have one UTXO present (the change output) of 6 - 5 - fee BTC. - const fee = 27750 + // We should expect a static fee of 50100 satoshis for spending 9 inputs + // (3 P2WPKH, 3 NP2WPKH, 3 P2TR) to two P2WPKH outputs. Carol should + // therefore only have one UTXO present (the change output) of + // 9 - 8 - fee BTC. + const fee = 50100 restoreCheckBalance(finalBalance-minerAmt-fee, 1, 21, nil) // Last of all, make sure we can also restore a node from the extended diff --git a/lntest/itest/test_harness.go b/lntest/itest/test_harness.go index c6f16e4d40a..1aff55b0a37 100644 --- a/lntest/itest/test_harness.go +++ b/lntest/itest/test_harness.go @@ -46,6 +46,7 @@ const ( AddrTypeWitnessPubkeyHash = lnrpc.AddressType_WITNESS_PUBKEY_HASH AddrTypeNestedPubkeyHash = lnrpc.AddressType_NESTED_PUBKEY_HASH + AddrTypeTaprootPubkey = lnrpc.AddressType_TAPROOT_PUBKEY ) // harnessTest wraps a regular testing.T providing enhanced error detection From 78db46be7eceb567e042ad2b607ff4158b051d98 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:50 +0100 Subject: [PATCH 6/9] multi: add p2tr tapscript signing capabilities --- input/size.go | 38 ++++- lnrpc/signrpc/signer.pb.go | 211 +++++++++++++++------------- lnrpc/signrpc/signer.proto | 6 + lnrpc/signrpc/signer.swagger.json | 7 + lnrpc/signrpc/signer_server.go | 37 ++++- lntest/itest/lnd_taproot_test.go | 222 ++++++++++++++++++++++++++++++ lnwallet/btcwallet/signer.go | 28 ++++ 7 files changed, 445 insertions(+), 104 deletions(-) diff --git a/input/size.go b/input/size.go index 1b9e8d991ff..902c95573b9 100644 --- a/input/size.go +++ b/input/size.go @@ -3,6 +3,7 @@ package input import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcwallet/waddrmgr" ) const ( @@ -532,11 +533,16 @@ const ( // - witness_script (anchor_script) AnchorWitnessSize = 1 + 1 + 73 + 1 + AnchorScriptSize + // TaprootSignatureWitnessSize 65 bytes + // - sigLength: 1 byte + // - sig: 64 bytes + TaprootSignatureWitnessSize = 1 + 64 + // TaprootKeyPathWitnessSize 66 bytes // - NumberOfWitnessElements: 1 byte // - sigLength: 1 byte // - sig: 64 bytes - TaprootKeyPathWitnessSize = 1 + 1 + 64 + TaprootKeyPathWitnessSize = 1 + TaprootSignatureWitnessSize // TaprootKeyPathCustomSighashWitnessSize 67 bytes // - NumberOfWitnessElements: 1 byte @@ -544,6 +550,11 @@ const ( // - sig: 64 bytes // - sighashFlag: 1 byte TaprootKeyPathCustomSighashWitnessSize = TaprootKeyPathWitnessSize + 1 + + // TaprootBaseControlBlockWitnessSize 33 bytes + // - leafVersionAndParity: 1 byte + // - schnorrPubKey: 32 byte + TaprootBaseControlBlockWitnessSize = 33 ) // EstimateCommitTxWeight estimate commitment transaction weight depending on @@ -607,6 +618,31 @@ func (twe *TxWeightEstimator) AddWitnessInput(witnessSize int) *TxWeightEstimato return twe } +// AddTapscriptInput updates the weight estimate to account for an additional +// input spending a segwit v1 pay-to-taproot output using the script path. This +// accepts the total size of the witness for the script leaf that is executed +// and adds the size of the control block to the total witness size. +// +// NOTE: The leaf witness size must be calculated without the byte that accounts +// for the number of witness elements, only the total size of all elements on +// the stack that are consumed by the revealed script should be counted. +func (twe *TxWeightEstimator) AddTapscriptInput(leafWitnessSize int, + tapscript *waddrmgr.Tapscript) *TxWeightEstimator { + + // We add 1 byte for the total number of witness elements. + controlBlockWitnessSize := 1 + TaprootBaseControlBlockWitnessSize + + // 1 byte for the length of the element plus the element itself. + 1 + len(tapscript.RevealedScript) + + 1 + len(tapscript.ControlBlock.InclusionProof) + + twe.inputSize += InputSize + twe.inputWitnessSize += leafWitnessSize + controlBlockWitnessSize + twe.inputCount++ + twe.hasWitness = true + + return twe +} + // AddNestedP2WKHInput updates the weight estimate to account for an additional // input spending a P2SH output with a nested P2WKH redeem script. func (twe *TxWeightEstimator) AddNestedP2WKHInput() *TxWeightEstimator { diff --git a/lnrpc/signrpc/signer.pb.go b/lnrpc/signrpc/signer.pb.go index 531df6deefb..35d88217242 100644 --- a/lnrpc/signrpc/signer.pb.go +++ b/lnrpc/signrpc/signer.pb.go @@ -334,6 +334,10 @@ type SignReq struct { RawTxBytes []byte `protobuf:"bytes,1,opt,name=raw_tx_bytes,json=rawTxBytes,proto3" json:"raw_tx_bytes,omitempty"` // A set of sign descriptors, for each input to be signed. SignDescs []*SignDescriptor `protobuf:"bytes,2,rep,name=sign_descs,json=signDescs,proto3" json:"sign_descs,omitempty"` + // + //The full list of UTXO information for each of the inputs being spent. This + //is required when spending one or more taproot (SegWit v1) outputs. + PrevOutputs []*TxOut `protobuf:"bytes,3,rep,name=prev_outputs,json=prevOutputs,proto3" json:"prev_outputs,omitempty"` } func (x *SignReq) Reset() { @@ -382,6 +386,13 @@ func (x *SignReq) GetSignDescs() []*SignDescriptor { return nil } +func (x *SignReq) GetPrevOutputs() []*TxOut { + if x != nil { + return x.PrevOutputs + } + return nil +} + type SignResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -940,85 +951,88 @@ var file_signrpc_signer_proto_rawDesc = []byte{ 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x69, 0x67, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x63, - 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x61, 0x77, - 0x5f, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0a, 0x72, 0x61, 0x77, 0x54, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0a, 0x73, - 0x69, 0x67, 0x6e, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x44, 0x65, - 0x73, 0x63, 0x73, 0x22, 0x25, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x19, 0x0a, 0x08, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0c, 0x52, 0x07, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, 0x73, 0x22, 0x46, 0x0a, 0x0b, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x69, 0x74, - 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, 0x74, 0x6e, - 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x22, 0x4c, 0x0a, 0x0f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x0d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, - 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, - 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6b, 0x65, 0x79, - 0x4c, 0x6f, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, - 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x63, 0x74, 0x53, 0x69, 0x67, 0x22, 0x2f, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x5a, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, - 0x65, 0x79, 0x22, 0x29, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xa2, 0x01, - 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, - 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x70, - 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, - 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, - 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x12, - 0x31, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x44, 0x65, - 0x73, 0x63, 0x22, 0x32, 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x65, - 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x68, 0x61, - 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x32, 0xd4, 0x02, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, - 0x61, 0x77, 0x12, 0x10, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, 0x75, - 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x10, 0x2e, - 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x1a, - 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x0b, 0x53, 0x69, 0x67, - 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x17, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x46, 0x0a, 0x0d, 0x56, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x73, - 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x12, 0x48, 0x0a, 0x0f, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x53, 0x68, 0x61, - 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x61, 0x72, - 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2f, 0x5a, - 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, - 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, - 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x05, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x96, + 0x01, 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x61, + 0x77, 0x5f, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x72, 0x61, 0x77, 0x54, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0a, + 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x44, + 0x65, 0x73, 0x63, 0x73, 0x12, 0x31, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x69, 0x67, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x69, 0x67, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, 0x73, 0x22, 0x46, + 0x0a, 0x0b, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, + 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x5f, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x4c, 0x0a, 0x0f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x0d, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x07, 0x6b, 0x65, 0x79, + 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x69, 0x67, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, + 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, + 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x6f, + 0x75, 0x62, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x69, 0x67, 0x22, 0x2f, 0x0a, 0x0f, 0x53, 0x69, 0x67, + 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x5a, 0x0a, 0x10, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, + 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, + 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x29, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x22, 0xa2, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, + 0x72, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6b, 0x65, + 0x79, 0x12, 0x30, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, + 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6b, 0x65, 0x79, + 0x4c, 0x6f, 0x63, 0x12, 0x31, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x6b, + 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x22, 0x32, 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x32, 0xd4, 0x02, 0x0a, 0x06, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x52, 0x61, 0x77, 0x12, 0x10, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x12, 0x43, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x12, 0x10, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, + 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x17, 0x2e, 0x73, + 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x46, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x19, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x69, + 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x48, 0x0a, 0x0f, 0x44, 0x65, 0x72, 0x69, 0x76, + 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x73, 0x69, 0x67, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x72, + 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1055,25 +1069,26 @@ var file_signrpc_signer_proto_depIdxs = []int32{ 1, // 1: signrpc.SignDescriptor.key_desc:type_name -> signrpc.KeyDescriptor 2, // 2: signrpc.SignDescriptor.output:type_name -> signrpc.TxOut 3, // 3: signrpc.SignReq.sign_descs:type_name -> signrpc.SignDescriptor - 6, // 4: signrpc.InputScriptResp.input_scripts:type_name -> signrpc.InputScript - 0, // 5: signrpc.SignMessageReq.key_loc:type_name -> signrpc.KeyLocator - 0, // 6: signrpc.SharedKeyRequest.key_loc:type_name -> signrpc.KeyLocator - 1, // 7: signrpc.SharedKeyRequest.key_desc:type_name -> signrpc.KeyDescriptor - 4, // 8: signrpc.Signer.SignOutputRaw:input_type -> signrpc.SignReq - 4, // 9: signrpc.Signer.ComputeInputScript:input_type -> signrpc.SignReq - 8, // 10: signrpc.Signer.SignMessage:input_type -> signrpc.SignMessageReq - 10, // 11: signrpc.Signer.VerifyMessage:input_type -> signrpc.VerifyMessageReq - 12, // 12: signrpc.Signer.DeriveSharedKey:input_type -> signrpc.SharedKeyRequest - 5, // 13: signrpc.Signer.SignOutputRaw:output_type -> signrpc.SignResp - 7, // 14: signrpc.Signer.ComputeInputScript:output_type -> signrpc.InputScriptResp - 9, // 15: signrpc.Signer.SignMessage:output_type -> signrpc.SignMessageResp - 11, // 16: signrpc.Signer.VerifyMessage:output_type -> signrpc.VerifyMessageResp - 13, // 17: signrpc.Signer.DeriveSharedKey:output_type -> signrpc.SharedKeyResponse - 13, // [13:18] is the sub-list for method output_type - 8, // [8:13] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 2, // 4: signrpc.SignReq.prev_outputs:type_name -> signrpc.TxOut + 6, // 5: signrpc.InputScriptResp.input_scripts:type_name -> signrpc.InputScript + 0, // 6: signrpc.SignMessageReq.key_loc:type_name -> signrpc.KeyLocator + 0, // 7: signrpc.SharedKeyRequest.key_loc:type_name -> signrpc.KeyLocator + 1, // 8: signrpc.SharedKeyRequest.key_desc:type_name -> signrpc.KeyDescriptor + 4, // 9: signrpc.Signer.SignOutputRaw:input_type -> signrpc.SignReq + 4, // 10: signrpc.Signer.ComputeInputScript:input_type -> signrpc.SignReq + 8, // 11: signrpc.Signer.SignMessage:input_type -> signrpc.SignMessageReq + 10, // 12: signrpc.Signer.VerifyMessage:input_type -> signrpc.VerifyMessageReq + 12, // 13: signrpc.Signer.DeriveSharedKey:input_type -> signrpc.SharedKeyRequest + 5, // 14: signrpc.Signer.SignOutputRaw:output_type -> signrpc.SignResp + 7, // 15: signrpc.Signer.ComputeInputScript:output_type -> signrpc.InputScriptResp + 9, // 16: signrpc.Signer.SignMessage:output_type -> signrpc.SignMessageResp + 11, // 17: signrpc.Signer.VerifyMessage:output_type -> signrpc.VerifyMessageResp + 13, // 18: signrpc.Signer.DeriveSharedKey:output_type -> signrpc.SharedKeyResponse + 14, // [14:19] is the sub-list for method output_type + 9, // [9:14] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_signrpc_signer_proto_init() } diff --git a/lnrpc/signrpc/signer.proto b/lnrpc/signrpc/signer.proto index 608c71ae5f1..83a5c93dee5 100644 --- a/lnrpc/signrpc/signer.proto +++ b/lnrpc/signrpc/signer.proto @@ -158,6 +158,12 @@ message SignReq { // A set of sign descriptors, for each input to be signed. repeated SignDescriptor sign_descs = 2; + + /* + The full list of UTXO information for each of the inputs being spent. This + is required when spending one or more taproot (SegWit v1) outputs. + */ + repeated TxOut prev_outputs = 3; } message SignResp { diff --git a/lnrpc/signrpc/signer.swagger.json b/lnrpc/signrpc/signer.swagger.json index e4e0c24ca16..a87cf8853b8 100644 --- a/lnrpc/signrpc/signer.swagger.json +++ b/lnrpc/signrpc/signer.swagger.json @@ -389,6 +389,13 @@ "$ref": "#/definitions/signrpcSignDescriptor" }, "description": "A set of sign descriptors, for each input to be signed." + }, + "prev_outputs": { + "type": "array", + "items": { + "$ref": "#/definitions/signrpcTxOut" + }, + "description": "The full list of UTXO information for each of the inputs being spent. This\nis required when spending one or more taproot (SegWit v1) outputs." } } }, diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index bf0e2471c87..1d8e875239c 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -238,7 +238,7 @@ func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispat // provides an invalid transaction, then we'll return with an error. // // NOTE: The resulting signature should be void of a sighash byte. -func (s *Server) SignOutputRaw(ctx context.Context, in *SignReq) (*SignResp, +func (s *Server) SignOutputRaw(_ context.Context, in *SignReq) (*SignResp, error) { switch { @@ -266,7 +266,33 @@ func (s *Server) SignOutputRaw(ctx context.Context, in *SignReq) (*SignResp, return nil, fmt.Errorf("unable to decode tx: %v", err) } - sigHashCache := input.NewTxSigHashesV0Only(&txToSign) + var ( + sigHashCache = input.NewTxSigHashesV0Only(&txToSign) + prevOutputFetcher = txscript.NewMultiPrevOutFetcher(nil) + ) + + // If we're spending one or more SegWit v1 (Taproot) inputs, then we + // need the full UTXO information available. + if len(in.PrevOutputs) > 0 { + if len(in.PrevOutputs) != len(txToSign.TxIn) { + return nil, fmt.Errorf("provided previous outputs " + + "doesn't match number of transaction inputs") + } + + // Add all previous inputs to our sighash prev out fetcher so we + // can calculate the sighash correctly. + for idx, txIn := range txToSign.TxIn { + prevOutputFetcher.AddPrevOut( + txIn.PreviousOutPoint, &wire.TxOut{ + Value: in.PrevOutputs[idx].Value, + PkScript: in.PrevOutputs[idx].PkScript, + }, + ) + } + sigHashCache = txscript.NewTxSigHashes( + &txToSign, prevOutputFetcher, + ) + } log.Debugf("Generating sigs for %v inputs: ", len(in.SignDescs)) @@ -344,9 +370,10 @@ func (s *Server) SignOutputRaw(ctx context.Context, in *SignReq) (*SignResp, Value: signDesc.Output.Value, PkScript: signDesc.Output.PkScript, }, - HashType: txscript.SigHashType(signDesc.Sighash), - SigHashes: sigHashCache, - InputIndex: int(signDesc.InputIndex), + HashType: txscript.SigHashType(signDesc.Sighash), + SigHashes: sigHashCache, + InputIndex: int(signDesc.InputIndex), + PrevOutputFetcher: prevOutputFetcher, }) } diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go index 89f24de8641..de012ad57a7 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/lntest/itest/lnd_taproot_test.go @@ -1,11 +1,23 @@ package itest import ( + "bytes" "context" + "encoding/hex" + "testing" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/signrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" ) @@ -16,6 +28,15 @@ func testTaproot(net *lntest.NetworkHarness, t *harnessTest) { ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) defer cancel() + testTaprootKeySpend(ctxt, t, net) + testTaprootScriptSpend(ctxt, t, net) +} + +// testTaprootKeySpend tests sending to and spending from p2tr key spend only +// (BIP-0086) addresses. +func testTaprootKeySpend(ctxt context.Context, t *harnessTest, + net *lntest.NetworkHarness) { + // We'll start the test by sending Alice some coins, which she'll use to // send to herself on a p2tr output. net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, net.Alice) @@ -81,3 +102,204 @@ func testTaproot(net *lntest.NetworkHarness, t *harnessTest) { // Mine another block to clean up the mempool. mineBlocks(t, net, 1, 1) } + +// testTaprootScriptSpend tests sending to and spending from p2tr script +// addresses. +func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, + net *lntest.NetworkHarness) { + + // For the next step, we need a public key. Let's use a special family + // for this. + const taprootKeyFamily = 77 + keyDesc, err := net.Alice.WalletKitClient.DeriveNextKey( + ctxt, &walletrpc.KeyReq{ + KeyFamily: taprootKeyFamily, + }, + ) + require.NoError(t.t, err) + + leafSigningKey, err := btcec.ParsePubKey(keyDesc.RawKeyBytes) + require.NoError(t.t, err) + + // Let's create a taproot script output now. This is a hash lock with a + // simple preimage of "foobar". + leaf1 := testScriptHashLock(t.t, []byte("foobar")) + + // Let's add a second script output as well to test the partial reveal. + leaf2 := testScriptSchnorrSig(t.t, leafSigningKey) + + dummyInternalKeyBytes, _ := hex.DecodeString( + "03464805f5468e294d88cf15a3f06aef6c89d63ef1bd7b42db2e0c74c1ac" + + "eb90fe", + ) + dummyInternalKey, _ := btcec.ParsePubKey(dummyInternalKeyBytes) + + tapscript := input.TapscriptPartialReveal( + dummyInternalKey, leaf2, leaf1.TapHash(), + ) + taprootKey, err := tapscript.TaprootKey() + require.NoError(t.t, err) + + tapScriptAddr, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(taprootKey), harnessNetParams, + ) + require.NoError(t.t, err) + p2trPkScript, err := txscript.PayToAddrScript(tapScriptAddr) + require.NoError(t.t, err) + + // Send some coins to the generated tapscript address. + _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ + Addr: tapScriptAddr.String(), + Amount: 800_000, + }) + require.NoError(t.t, err) + + // Wait until the TX is found in the mempool. + txid, err := waitForTxInMempool(net.Miner.Client, minerMempoolTimeout) + require.NoError(t.t, err) + + p2trOutputIndex := getOutputIndex( + t, net.Miner, txid, tapScriptAddr.String(), + ) + + // Clear the mempool. + mineBlocks(t, net, 1, 1) + + // Spend the output again, this time back to a p2wkh address. + p2wkhAddr, p2wkhPkScript := newAddrWithScript( + ctxt, t.t, net.Alice, lnrpc.AddressType_WITNESS_PUBKEY_HASH, + ) + + // Create fee estimation for a p2tr input and p2wkh output. + feeRate := chainfee.SatPerKWeight(12500) + estimator := input.TxWeightEstimator{} + estimator.AddTapscriptInput( + input.TaprootSignatureWitnessSize, tapscript, + ) + estimator.AddP2WKHOutput() + estimatedWeight := int64(estimator.Weight()) + requiredFee := feeRate.FeeForWeight(estimatedWeight) + + tx := wire.NewMsgTx(2) + tx.TxIn = []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *txid, + Index: uint32(p2trOutputIndex), + }, + }} + value := int64(800_000 - requiredFee) + tx.TxOut = []*wire.TxOut{{ + PkScript: p2wkhPkScript, + Value: value, + }} + + var buf bytes.Buffer + require.NoError(t.t, tx.Serialize(&buf)) + + utxoInfo := []*signrpc.TxOut{{ + PkScript: p2trPkScript, + Value: 800_000, + }} + signResp, err := net.Alice.SignerClient.SignOutputRaw( + ctxt, &signrpc.SignReq{ + RawTxBytes: buf.Bytes(), + SignDescs: []*signrpc.SignDescriptor{{ + Output: utxoInfo[0], + InputIndex: 0, + KeyDesc: keyDesc, + Sighash: uint32(txscript.SigHashDefault), + WitnessScript: leaf2.Script, + }}, + PrevOutputs: utxoInfo, + }, + ) + require.NoError(t.t, err) + + // We can now assemble the witness stack. + controlBlockBytes, err := tapscript.ControlBlock.ToBytes() + require.NoError(t.t, err) + + tx.TxIn[0].Witness = wire.TxWitness{ + signResp.RawSigs[0], + leaf2.Script, + controlBlockBytes, + } + + buf.Reset() + require.NoError(t.t, tx.Serialize(&buf)) + + // Since Schnorr signatures are fixed size, we must be able to estimate + // the size of this transaction exactly. + txWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx)) + require.Equal(t.t, txWeight, estimatedWeight) + + _, err = net.Alice.WalletKitClient.PublishTransaction( + ctxt, &walletrpc.Transaction{ + TxHex: buf.Bytes(), + }, + ) + require.NoError(t.t, err) + + // Wait until the spending tx is found. + txid, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout) + require.NoError(t.t, err) + p2wpkhOutputIndex := getOutputIndex( + t, net.Miner, txid, p2wkhAddr.String(), + ) + op := &lnrpc.OutPoint{ + TxidBytes: txid[:], + OutputIndex: uint32(p2wpkhOutputIndex), + } + assertWalletUnspent(t, net.Alice, op) + + // Mine another block to clean up the mempool and to make sure the spend + // tx is actually included in a block. + mineBlocks(t, net, 1, 1) +} + +// testScriptHashLock returns a simple bitcoin script that locks the funds to +// a hash lock of the given preimage. +func testScriptHashLock(t *testing.T, preimage []byte) txscript.TapLeaf { + builder := txscript.NewScriptBuilder() + builder.AddOp(txscript.OP_DUP) + builder.AddOp(txscript.OP_HASH160) + builder.AddData(btcutil.Hash160(preimage)) + builder.AddOp(txscript.OP_EQUALVERIFY) + script1, err := builder.Script() + require.NoError(t, err) + return txscript.NewBaseTapLeaf(script1) +} + +// testScriptSchnorrSig returns a simple bitcoin script that locks the funds to +// a Schnorr signature of the given public key. +func testScriptSchnorrSig(t *testing.T, + pubKey *btcec.PublicKey) txscript.TapLeaf { + + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(pubKey)) + builder.AddOp(txscript.OP_CHECKSIG) + script2, err := builder.Script() + require.NoError(t, err) + return txscript.NewBaseTapLeaf(script2) +} + +// newAddrWithScript returns a new address and its pkScript. +func newAddrWithScript(ctx context.Context, t *testing.T, + node *lntest.HarnessNode, addrType lnrpc.AddressType) (btcutil.Address, + []byte) { + + p2wkhResp, err := node.NewAddress(ctx, &lnrpc.NewAddressRequest{ + Type: addrType, + }) + require.NoError(t, err) + + p2wkhAddr, err := btcutil.DecodeAddress( + p2wkhResp.Address, harnessNetParams, + ) + require.NoError(t, err) + + p2wkhPkScript, err := txscript.PayToAddrScript(p2wkhAddr) + require.NoError(t, err) + + return p2wkhAddr, p2wkhPkScript +} diff --git a/lnwallet/btcwallet/signer.go b/lnwallet/btcwallet/signer.go index cbbb7283a9a..1e475146e8c 100644 --- a/lnwallet/btcwallet/signer.go +++ b/lnwallet/btcwallet/signer.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -356,6 +357,33 @@ func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx, return nil, err } + // In case of a taproot output any signature is always a Schnorr + // signature, based on the new tapscript sighash algorithm. + if txscript.IsPayToTaproot(signDesc.Output.PkScript) { + sigHashes := txscript.NewTxSigHashes( + tx, signDesc.PrevOutputFetcher, + ) + leaf := txscript.TapLeaf{ + LeafVersion: txscript.BaseLeafVersion, + Script: witnessScript, + } + rawSig, err := txscript.RawTxInTapscriptSignature( + tx, sigHashes, signDesc.InputIndex, + signDesc.Output.Value, signDesc.Output.PkScript, + leaf, signDesc.HashType, privKey, + ) + if err != nil { + return nil, err + } + + sig, err := schnorr.ParseSignature(rawSig) + if err != nil { + return nil, err + } + + return sig, nil + } + // TODO(roasbeef): generate sighash midstate if not present? amt := signDesc.Output.Value From ef98f2df8a73adff1b6dd7fd247b5b50c9f9e23a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:51 +0100 Subject: [PATCH 7/9] multi: add p2tr tapscript key path signing capabilities --- input/signdescriptor.go | 5 + input/size.go | 22 ++++ lnrpc/signrpc/signer.pb.go | 189 ++++++++++++++++-------------- lnrpc/signrpc/signer.proto | 15 ++- lnrpc/signrpc/signer.swagger.json | 6 +- lnrpc/signrpc/signer_server.go | 11 +- lntest/itest/lnd_taproot_test.go | 143 ++++++++++++++++++++++ lnwallet/btcwallet/signer.go | 43 +++++-- 8 files changed, 330 insertions(+), 104 deletions(-) diff --git a/input/signdescriptor.go b/input/signdescriptor.go index b97fbb5de06..e3ffc5e24a1 100644 --- a/input/signdescriptor.go +++ b/input/signdescriptor.go @@ -61,6 +61,11 @@ type SignDescriptor struct { // script (PkScript). WitnessScript []byte + // TaprootKeySpend indicates that instead of a witness script being + // spent by the signature that results from this signing request, a + // taproot key spend is performed instead. + TaprootKeySpend bool + // Output is the target output which should be signed. The PkScript and // Value fields within the output should be properly populated, // otherwise an invalid signature may be generated. diff --git a/input/size.go b/input/size.go index 902c95573b9..9796314cbaa 100644 --- a/input/size.go +++ b/input/size.go @@ -2,6 +2,7 @@ package input import ( "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/waddrmgr" ) @@ -643,6 +644,27 @@ func (twe *TxWeightEstimator) AddTapscriptInput(leafWitnessSize int, return twe } +// AddTaprootKeySpendInput updates the weight estimate to account for an +// additional input spending a segwit v1 pay-to-taproot output using the key +// spend path. This accepts the sighash type being used since that has an +// influence on the total size of the signature. +func (twe *TxWeightEstimator) AddTaprootKeySpendInput( + hashType txscript.SigHashType) *TxWeightEstimator { + + twe.inputSize += InputSize + + if hashType == txscript.SigHashDefault { + twe.inputWitnessSize += TaprootKeyPathWitnessSize + } else { + twe.inputWitnessSize += TaprootKeyPathCustomSighashWitnessSize + } + + twe.inputCount++ + twe.hasWitness = true + + return twe +} + // AddNestedP2WKHInput updates the weight estimate to account for an additional // input spending a P2SH output with a nested P2WKH redeem script. func (twe *TxWeightEstimator) AddNestedP2WKHInput() *TxWeightEstimator { diff --git a/lnrpc/signrpc/signer.pb.go b/lnrpc/signrpc/signer.pb.go index 35d88217242..2d12035f45a 100644 --- a/lnrpc/signrpc/signer.pb.go +++ b/lnrpc/signrpc/signer.pb.go @@ -228,8 +228,11 @@ type SignDescriptor struct { //tweakPriv*sha256(tweakPub || pubKey)) mod N DoubleTweak []byte `protobuf:"bytes,3,opt,name=double_tweak,json=doubleTweak,proto3" json:"double_tweak,omitempty"` // - //The full script required to properly redeem the output. This field will - //only be populated if a p2wsh or a p2sh output is being signed. + //The full script required to properly redeem the output. This field will + //only be populated if a p2tr, p2wsh or a p2sh output is being signed. In case + //taproot_key_spend is set to true then this value must correspond to the + //taproot root hash (in case of a tapscript output) or the tap hashed internal + //public key (in case of a BIP-0086 output). WitnessScript []byte `protobuf:"bytes,4,opt,name=witness_script,json=witnessScript,proto3" json:"witness_script,omitempty"` // //A description of the output being spent. The value and script MUST be @@ -242,6 +245,12 @@ type SignDescriptor struct { // //The target input within the transaction that should be signed. InputIndex int32 `protobuf:"varint,8,opt,name=input_index,json=inputIndex,proto3" json:"input_index,omitempty"` + // + //Indicates that this should produce a signature that can be used for the key + //spend path of a taproot input. This requires the witness_script field to be + //set to the taproot root hash (in case of a tapscript output) or the tap + //hashed internal public key (in case of a BIP-0086 output). + TaprootKeySpend bool `protobuf:"varint,9,opt,name=taproot_key_spend,json=taprootKeySpend,proto3" json:"taproot_key_spend,omitempty"` } func (x *SignDescriptor) Reset() { @@ -325,6 +334,13 @@ func (x *SignDescriptor) GetInputIndex() int32 { return 0 } +func (x *SignDescriptor) GetTaprootKeySpend() bool { + if x != nil { + return x.TaprootKeySpend + } + return false +} + type SignReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -934,7 +950,7 @@ var file_signrpc_signer_proto_rawDesc = []byte{ 0x54, 0x78, 0x4f, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6b, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x93, 0x02, 0x0a, 0x0e, 0x53, 0x69, 0x67, + 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0xbf, 0x02, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, @@ -951,88 +967,91 @@ var file_signrpc_signer_proto_rawDesc = []byte{ 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x69, 0x67, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x96, - 0x01, 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x61, - 0x77, 0x5f, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0a, 0x72, 0x61, 0x77, 0x54, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0a, - 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x44, - 0x65, 0x73, 0x63, 0x73, 0x12, 0x31, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x69, 0x67, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x69, 0x67, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, 0x73, 0x22, 0x46, - 0x0a, 0x0b, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, - 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x5f, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x4c, 0x0a, 0x0f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x0d, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x07, 0x6b, 0x65, 0x79, - 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x69, 0x67, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, - 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, - 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x6f, - 0x75, 0x62, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x63, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x69, 0x67, 0x22, 0x2f, 0x0a, 0x0f, 0x53, 0x69, 0x67, - 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x5a, 0x0a, 0x10, 0x56, 0x65, - 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, - 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, - 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x29, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x22, 0xa2, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6b, 0x65, - 0x79, 0x12, 0x30, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, - 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6b, 0x65, 0x79, - 0x4c, 0x6f, 0x63, 0x12, 0x31, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x6b, - 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x22, 0x32, 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x32, 0xd4, 0x02, 0x0a, 0x06, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x52, 0x61, 0x77, 0x12, 0x10, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x12, 0x43, - 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x12, 0x10, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, - 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x17, 0x2e, 0x73, - 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x46, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x19, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x69, - 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x48, 0x0a, 0x0f, 0x44, 0x65, 0x72, 0x69, 0x76, - 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x73, 0x69, 0x67, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x72, - 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x05, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2a, + 0x0a, 0x11, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x70, + 0x65, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x74, 0x61, 0x70, 0x72, 0x6f, + 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x22, 0x96, 0x01, 0x0a, 0x07, 0x53, + 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x61, + 0x77, 0x54, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, + 0x5f, 0x64, 0x65, 0x73, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, + 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x73, + 0x12, 0x31, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x19, 0x0a, 0x08, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x07, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, 0x73, 0x22, 0x46, 0x0a, 0x0b, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x69, 0x74, + 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, 0x74, 0x6e, + 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x22, 0x4c, 0x0a, 0x0f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x0d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, + 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, + 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6b, 0x65, 0x79, + 0x4c, 0x6f, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, + 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x63, 0x74, 0x53, 0x69, 0x67, 0x22, 0x2f, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x5a, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, + 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, + 0x65, 0x79, 0x22, 0x29, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xa2, 0x01, + 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x70, + 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, + 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x12, + 0x31, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x44, 0x65, + 0x73, 0x63, 0x22, 0x32, 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x32, 0xd4, 0x02, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, + 0x61, 0x77, 0x12, 0x10, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, + 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x10, 0x2e, + 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x1a, + 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x0b, 0x53, 0x69, 0x67, + 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x17, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x46, 0x0a, 0x0d, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x73, + 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x48, 0x0a, 0x0f, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x53, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x61, 0x72, + 0x65, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2f, 0x5a, + 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, + 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/lnrpc/signrpc/signer.proto b/lnrpc/signrpc/signer.proto index 83a5c93dee5..c75b95a3406 100644 --- a/lnrpc/signrpc/signer.proto +++ b/lnrpc/signrpc/signer.proto @@ -129,8 +129,11 @@ message SignDescriptor { bytes double_tweak = 3; /* - The full script required to properly redeem the output. This field will - only be populated if a p2wsh or a p2sh output is being signed. + The full script required to properly redeem the output. This field will + only be populated if a p2tr, p2wsh or a p2sh output is being signed. In case + taproot_key_spend is set to true then this value must correspond to the + taproot root hash (in case of a tapscript output) or the tap hashed internal + public key (in case of a BIP-0086 output). */ bytes witness_script = 4; @@ -150,6 +153,14 @@ message SignDescriptor { The target input within the transaction that should be signed. */ int32 input_index = 8; + + /* + Indicates that this should produce a signature that can be used for the key + spend path of a taproot input. This requires the witness_script field to be + set to the taproot root hash (in case of a tapscript output) or the tap + hashed internal public key (in case of a BIP-0086 output). + */ + bool taproot_key_spend = 9; } message SignReq { diff --git a/lnrpc/signrpc/signer.swagger.json b/lnrpc/signrpc/signer.swagger.json index a87cf8853b8..a39922207c6 100644 --- a/lnrpc/signrpc/signer.swagger.json +++ b/lnrpc/signrpc/signer.swagger.json @@ -325,7 +325,7 @@ "witness_script": { "type": "string", "format": "byte", - "description": "The full script required to properly redeem the output. This field will\nonly be populated if a p2wsh or a p2sh output is being signed." + "description": "The full script required to properly redeem the output. This field will\nonly be populated if a p2tr, p2wsh or a p2sh output is being signed. In case\ntaproot_key_spend is set to true then this value must correspond to the\ntaproot root hash (in case of a tapscript output) or the tap hashed internal\npublic key (in case of a BIP-0086 output)." }, "output": { "$ref": "#/definitions/signrpcTxOut", @@ -340,6 +340,10 @@ "type": "integer", "format": "int32", "description": "The target input within the transaction that should be signed." + }, + "taproot_key_spend": { + "type": "boolean", + "description": "Indicates that this should produce a signature that can be used for the key\nspend path of a taproot input. This requires the witness_script field to be\nset to the taproot root hash (in case of a tapscript output) or the tap\nhashed internal public key (in case of a BIP-0086 output)." } } }, diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index 1d8e875239c..1474c0ec9ce 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -341,9 +341,9 @@ func (s *Server) SignOutputRaw(_ context.Context, in *SignReq) (*SignResp, // output. We'll send it in the WitnessScript field, the // SignOutputRaw RPC will know what to do with it when creating // the sighash. - if len(signDesc.WitnessScript) == 0 { + if len(signDesc.WitnessScript) == 0 && !signDesc.TaprootKeySpend { return nil, fmt.Errorf("witness script MUST be " + - "specified") + "specified for non-taproot-key-spends") } // If the users provided a double tweak, then we'll need to @@ -363,9 +363,10 @@ func (s *Server) SignOutputRaw(_ context.Context, in *SignReq) (*SignResp, KeyLocator: keyLoc, PubKey: targetPubKey, }, - SingleTweak: signDesc.SingleTweak, - DoubleTweak: tweakPrivKey, - WitnessScript: signDesc.WitnessScript, + SingleTweak: signDesc.SingleTweak, + DoubleTweak: tweakPrivKey, + WitnessScript: signDesc.WitnessScript, + TaprootKeySpend: signDesc.TaprootKeySpend, Output: &wire.TxOut{ Value: signDesc.Output.Value, PkScript: signDesc.Output.PkScript, diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go index de012ad57a7..434466af683 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/lntest/itest/lnd_taproot_test.go @@ -3,6 +3,7 @@ package itest import ( "bytes" "context" + "crypto/sha256" "encoding/hex" "testing" @@ -30,6 +31,7 @@ func testTaproot(net *lntest.NetworkHarness, t *harnessTest) { testTaprootKeySpend(ctxt, t, net) testTaprootScriptSpend(ctxt, t, net) + testTaprootKeySpendRPC(ctxt, t, net) } // testTaprootKeySpend tests sending to and spending from p2tr key spend only @@ -257,6 +259,147 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, mineBlocks(t, net, 1, 1) } +// testTaprootKeySpendRPC tests that a tapscript address can also be spent using +// the key spend path through the RPC. +func testTaprootKeySpendRPC(ctxt context.Context, t *harnessTest, + net *lntest.NetworkHarness) { + + // For the next step, we need a public key. Let's use a special family + // for this. + const taprootKeyFamily = 77 + keyDesc, err := net.Alice.WalletKitClient.DeriveNextKey( + ctxt, &walletrpc.KeyReq{ + KeyFamily: taprootKeyFamily, + }, + ) + require.NoError(t.t, err) + + internalKey, err := btcec.ParsePubKey(keyDesc.RawKeyBytes) + require.NoError(t.t, err) + + // We want to make sure we can still use a tweaked key, even if it ends + // up being essentially double tweaked because of the taproot root hash. + dummyKeyTweak := sha256.Sum256([]byte("this is a key tweak")) + internalKey = input.TweakPubKeyWithTweak(internalKey, dummyKeyTweak[:]) + + // Let's create a taproot script output now. This is a hash lock with a + // simple preimage of "foobar". + leaf1 := testScriptHashLock(t.t, []byte("foobar")) + + rootHash := leaf1.TapHash() + taprootKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash[:]) + + tapScriptAddr, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(taprootKey), harnessNetParams, + ) + require.NoError(t.t, err) + p2trPkScript, err := txscript.PayToAddrScript(tapScriptAddr) + require.NoError(t.t, err) + + // Send some coins to the generated tapscript address. + _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ + Addr: tapScriptAddr.String(), + Amount: 800_000, + }) + require.NoError(t.t, err) + + // Wait until the TX is found in the mempool. + txid, err := waitForTxInMempool(net.Miner.Client, minerMempoolTimeout) + require.NoError(t.t, err) + + p2trOutputIndex := getOutputIndex( + t, net.Miner, txid, tapScriptAddr.String(), + ) + + // Clear the mempool. + mineBlocks(t, net, 1, 1) + + // Spend the output again, this time back to a p2wkh address. + p2wkhAddr, p2wkhPkScript := newAddrWithScript( + ctxt, t.t, net.Alice, lnrpc.AddressType_WITNESS_PUBKEY_HASH, + ) + + // Create fee estimation for a p2tr input and p2wkh output. + feeRate := chainfee.SatPerKWeight(12500) + estimator := input.TxWeightEstimator{} + estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) + estimator.AddP2WKHOutput() + estimatedWeight := int64(estimator.Weight()) + requiredFee := feeRate.FeeForWeight(estimatedWeight) + + tx := wire.NewMsgTx(2) + tx.TxIn = []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *txid, + Index: uint32(p2trOutputIndex), + }, + }} + value := int64(800_000 - requiredFee) + tx.TxOut = []*wire.TxOut{{ + PkScript: p2wkhPkScript, + Value: value, + }} + + var buf bytes.Buffer + require.NoError(t.t, tx.Serialize(&buf)) + + utxoInfo := []*signrpc.TxOut{{ + PkScript: p2trPkScript, + Value: 800_000, + }} + signResp, err := net.Alice.SignerClient.SignOutputRaw( + ctxt, &signrpc.SignReq{ + RawTxBytes: buf.Bytes(), + SignDescs: []*signrpc.SignDescriptor{{ + Output: utxoInfo[0], + InputIndex: 0, + KeyDesc: keyDesc, + SingleTweak: dummyKeyTweak[:], + Sighash: uint32(txscript.SigHashDefault), + WitnessScript: rootHash[:], + TaprootKeySpend: true, + }}, + PrevOutputs: utxoInfo, + }, + ) + require.NoError(t.t, err) + + tx.TxIn[0].Witness = wire.TxWitness{ + signResp.RawSigs[0], + } + + buf.Reset() + require.NoError(t.t, tx.Serialize(&buf)) + + // Since Schnorr signatures are fixed size, we must be able to estimate + // the size of this transaction exactly. + txWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx)) + require.Equal(t.t, txWeight, estimatedWeight) + + _, err = net.Alice.WalletKitClient.PublishTransaction( + ctxt, &walletrpc.Transaction{ + TxHex: buf.Bytes(), + }, + ) + require.NoError(t.t, err) + + // Wait until the spending tx is found. + txid, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout) + require.NoError(t.t, err) + p2wpkhOutputIndex := getOutputIndex( + t, net.Miner, txid, p2wkhAddr.String(), + ) + op := &lnrpc.OutPoint{ + TxidBytes: txid[:], + OutputIndex: uint32(p2wpkhOutputIndex), + } + assertWalletUnspent(t, net.Alice, op) + + // Mine another block to clean up the mempool and to make sure the spend + // tx is actually included in a block. + mineBlocks(t, net, 1, 1) +} + // testScriptHashLock returns a simple bitcoin script that locks the funds to // a hash lock of the given preimage. func testScriptHashLock(t *testing.T, preimage []byte) txscript.TapLeaf { diff --git a/lnwallet/btcwallet/signer.go b/lnwallet/btcwallet/signer.go index 1e475146e8c..a205109b546 100644 --- a/lnwallet/btcwallet/signer.go +++ b/lnwallet/btcwallet/signer.go @@ -363,17 +363,38 @@ func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx, sigHashes := txscript.NewTxSigHashes( tx, signDesc.PrevOutputFetcher, ) - leaf := txscript.TapLeaf{ - LeafVersion: txscript.BaseLeafVersion, - Script: witnessScript, - } - rawSig, err := txscript.RawTxInTapscriptSignature( - tx, sigHashes, signDesc.InputIndex, - signDesc.Output.Value, signDesc.Output.PkScript, - leaf, signDesc.HashType, privKey, - ) - if err != nil { - return nil, err + + // Are we spending a script path or the key path? The API is + // slightly different, so we need to account for that to get the + // raw signature. + var rawSig []byte + if signDesc.TaprootKeySpend { + // This function tweaks the private key using the tap + // root key supplied as the tweak. So we pass in the + // original private key to avoid it being double + // tweaked! + rawSig, err = txscript.RawTxInTaprootSignature( + tx, sigHashes, signDesc.InputIndex, + signDesc.Output.Value, signDesc.Output.PkScript, + signDesc.WitnessScript, signDesc.HashType, + privKey, + ) + if err != nil { + return nil, err + } + } else { + leaf := txscript.TapLeaf{ + LeafVersion: txscript.BaseLeafVersion, + Script: witnessScript, + } + rawSig, err = txscript.RawTxInTapscriptSignature( + tx, sigHashes, signDesc.InputIndex, + signDesc.Output.Value, signDesc.Output.PkScript, + leaf, signDesc.HashType, privKey, + ) + if err != nil { + return nil, err + } } sig, err := schnorr.ParseSignature(rawSig) From 3b5585c12b7a4054dad371c01ab227cb76cb86b1 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 18 Mar 2022 18:37:52 +0100 Subject: [PATCH 8/9] multi: fix and test v1 output spend ntfns Because Taproot key spend only spends don't allow us to re-construct the spent pkScript from the witness alone, we cannot support registering spend notifications for v1 pkScripts only. We instead require the outpoint to be specified. This commit makes it possible to only match by outpoint and also adds an itest for it. --- chainntnfs/txnotifier.go | 44 ++++++++++++ lnrpc/chainrpc/chainnotifier.pb.go | 6 +- lnrpc/chainrpc/chainnotifier.proto | 6 +- lnrpc/chainrpc/chainnotifier.swagger.json | 2 +- lntest/harness_node.go | 5 ++ lntest/itest/lnd_taproot_test.go | 81 +++++++++++++++++++++-- 6 files changed, 137 insertions(+), 7 deletions(-) diff --git a/chainntnfs/txnotifier.go b/chainntnfs/txnotifier.go index acf8dee25b7..409002346fe 100644 --- a/chainntnfs/txnotifier.go +++ b/chainntnfs/txnotifier.go @@ -39,6 +39,19 @@ var ( // notifier to match _and_ dispatch upon detecting the spend of the // script on-chain, rather than the outpoint. ZeroOutPoint wire.OutPoint + + // zeroV1KeyPush is a pkScript that pushes an all-zero 32-byte Taproot + // SegWit v1 key to the stack. + zeroV1KeyPush = [34]byte{ + txscript.OP_1, txscript.OP_DATA_32, // 32 byte of zeroes here + } + + // ZeroTaprootPkScript is the parsed txscript.PkScript of an empty + // Taproot SegWit v1 key being pushed to the stack. This allows the + // notifier to match _and_ dispatch upon detecting the spend of the + // outpoint on-chain, rather than the pkScript (which cannot be derived + // from the witness alone in the SegWit v1 case). + ZeroTaprootPkScript, _ = txscript.ParsePkScript(zeroV1KeyPush[:]) ) var ( @@ -322,6 +335,24 @@ func NewSpendRequest(op *wire.OutPoint, pkScript []byte) (SpendRequest, error) { } r.PkScript = outputScript + // For Taproot spends we have the main problem that for the key spend + // path we cannot derive the pkScript from only looking at the input's + // witness. So we need to rely on the outpoint information alone. + // + // TODO(guggero): For script path spends we can derive the pkScript from + // the witness, since we have the full control block and the spent + // script available. + if outputScript.Class() == txscript.WitnessV1TaprootTy { + if op == nil { + return r, fmt.Errorf("cannot register witness v1 " + + "spend request without outpoint") + } + + // We have an outpoint, so we can set the pkScript to an all + // zero Taproot key that we'll compare this spend request to. + r.PkScript = ZeroTaprootPkScript + } + return r, nil } @@ -1488,6 +1519,19 @@ func (n *TxNotifier) filterTx(tx *btcutil.Tx, blockHash *chainhash.Hash, if _, ok := n.spendNotifications[spendRequest]; ok { notifyDetails(spendRequest, prevOut, uint32(i)) } + + // Now try with an empty taproot key pkScript, since we + // cannot derive the spent pkScript directly from the + // witness. But we have the outpoint, which should be + // enough. + spendRequest.PkScript = ZeroTaprootPkScript + if _, ok := n.spendNotifications[spendRequest]; ok { + notifyDetails(spendRequest, prevOut, uint32(i)) + } + + // Restore the pkScript but try with a zero outpoint + // instead (won't be possible for Taproot). + spendRequest.PkScript = pkScript spendRequest.OutPoint = ZeroOutPoint if _, ok := n.spendNotifications[spendRequest]; ok { notifyDetails(spendRequest, prevOut, uint32(i)) diff --git a/lnrpc/chainrpc/chainnotifier.pb.go b/lnrpc/chainrpc/chainnotifier.pb.go index 46416767e25..19317b87b09 100644 --- a/lnrpc/chainrpc/chainnotifier.pb.go +++ b/lnrpc/chainrpc/chainnotifier.pb.go @@ -372,7 +372,11 @@ type SpendRequest struct { // //The outpoint for which we should request a spend notification for. If set to //a zero outpoint, then the spend notification will be requested for the - //script instead. + //script instead. A zero or nil outpoint is not supported for Taproot spends + //because the output script cannot reliably be computed from the witness alone + //and the spent output script is not always available in the rescan context. + //So an outpoint must _always_ be specified when registering a spend + //notification for a Taproot output. Outpoint *Outpoint `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` // //The output script for the outpoint above. This will be used by light clients diff --git a/lnrpc/chainrpc/chainnotifier.proto b/lnrpc/chainrpc/chainnotifier.proto index 5c9202b2a5b..2c97ae561ba 100644 --- a/lnrpc/chainrpc/chainnotifier.proto +++ b/lnrpc/chainrpc/chainnotifier.proto @@ -119,7 +119,11 @@ message SpendRequest { /* The outpoint for which we should request a spend notification for. If set to a zero outpoint, then the spend notification will be requested for the - script instead. + script instead. A zero or nil outpoint is not supported for Taproot spends + because the output script cannot reliably be computed from the witness alone + and the spent output script is not always available in the rescan context. + So an outpoint must _always_ be specified when registering a spend + notification for a Taproot output. */ Outpoint outpoint = 1; diff --git a/lnrpc/chainrpc/chainnotifier.swagger.json b/lnrpc/chainrpc/chainnotifier.swagger.json index ee684f16920..ec224b6ba07 100644 --- a/lnrpc/chainrpc/chainnotifier.swagger.json +++ b/lnrpc/chainrpc/chainnotifier.swagger.json @@ -290,7 +290,7 @@ "properties": { "outpoint": { "$ref": "#/definitions/chainrpcOutpoint", - "description": "The outpoint for which we should request a spend notification for. If set to\na zero outpoint, then the spend notification will be requested for the\nscript instead." + "description": "The outpoint for which we should request a spend notification for. If set to\na zero outpoint, then the spend notification will be requested for the\nscript instead. A zero or nil outpoint is not supported for Taproot spends\nbecause the output script cannot reliably be computed from the witness alone\nand the spent output script is not always available in the rescan context.\nSo an outpoint must _always_ be specified when registering a spend\nnotification for a Taproot output." }, "script": { "type": "string", diff --git a/lntest/harness_node.go b/lntest/harness_node.go index ddaeb582daa..f3e7b349621 100644 --- a/lntest/harness_node.go +++ b/lntest/harness_node.go @@ -23,6 +23,7 @@ import ( "github.com/jackc/pgx/v4/pgxpool" "github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" @@ -361,6 +362,7 @@ type HarnessNode struct { Watchtower watchtowerrpc.WatchtowerClient WatchtowerClient wtclientrpc.WatchtowerClientClient StateClient lnrpc.StateClient + ChainClient chainrpc.ChainNotifierClient } // RPCClients wraps a list of RPC clients into a single struct for easier @@ -378,6 +380,7 @@ type RPCClients struct { Watchtower watchtowerrpc.WatchtowerClient WatchtowerClient wtclientrpc.WatchtowerClientClient State lnrpc.StateClient + ChainClient chainrpc.ChainNotifierClient } // Assert *HarnessNode implements the lnrpc.LightningClient interface. @@ -929,6 +932,7 @@ func (hn *HarnessNode) InitRPCClients(c *grpc.ClientConn) { WatchtowerClient: wtclientrpc.NewWatchtowerClientClient(c), Signer: signrpc.NewSignerClient(c), State: lnrpc.NewStateClient(c), + ChainClient: chainrpc.NewChainNotifierClient(c), } } @@ -949,6 +953,7 @@ func (hn *HarnessNode) initLightningClient() error { hn.WatchtowerClient = wtclientrpc.NewWatchtowerClientClient(conn) hn.SignerClient = signrpc.NewSignerClient(conn) hn.StateClient = lnrpc.NewStateClient(conn) + hn.ChainClient = chainrpc.NewChainNotifierClient(conn) // Wait until the server is fully started. if err := hn.WaitUntilServerActive(); err != nil { diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go index 434466af683..cb0510a49fd 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/lntest/itest/lnd_taproot_test.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" @@ -101,8 +102,36 @@ func testTaprootKeySpend(ctxt context.Context, t *harnessTest, } assertWalletUnspent(t, net.Alice, op) + // Before we confirm the transaction, let's register a confirmation + // listener for it, which we expect to fire after mining a block. + p2trAddr, err := btcutil.DecodeAddress( + p2trResp.Address, harnessNetParams, + ) + require.NoError(t.t, err) + p2trPkScript, err := txscript.PayToAddrScript(p2trAddr) + require.NoError(t.t, err) + + _, currentHeight, err := net.Miner.Client.GetBestBlock() + require.NoError(t.t, err) + confClient, err := net.Alice.ChainClient.RegisterConfirmationsNtfn( + ctxt, &chainrpc.ConfRequest{ + Script: p2trPkScript, + Txid: txid[:], + HeightHint: uint32(currentHeight), + NumConfs: 1, + }, + ) + require.NoError(t.t, err) + // Mine another block to clean up the mempool. mineBlocks(t, net, 1, 1) + + // We now expect our confirmation to go through. + confMsg, err := confClient.Recv() + require.NoError(t.t, err) + conf := confMsg.GetConf() + require.NotNil(t.t, conf) + require.Equal(t.t, conf.BlockHeight, uint32(currentHeight+1)) } // testTaprootScriptSpend tests sending to and spending from p2tr script @@ -163,6 +192,10 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, p2trOutputIndex := getOutputIndex( t, net.Miner, txid, tapScriptAddr.String(), ) + p2trOutpoint := wire.OutPoint{ + Hash: *txid, + Index: uint32(p2trOutputIndex), + } // Clear the mempool. mineBlocks(t, net, 1, 1) @@ -184,10 +217,7 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, tx := wire.NewMsgTx(2) tx.TxIn = []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: *txid, - Index: uint32(p2trOutputIndex), - }, + PreviousOutPoint: p2trOutpoint, }} value := int64(800_000 - requiredFee) tx.TxOut = []*wire.TxOut{{ @@ -235,6 +265,42 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, txWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx)) require.Equal(t.t, txWeight, estimatedWeight) + // Before we publish the tx that spends the p2tr transaction, we want to + // register a spend listener that we expect to fire after mining the + // block. + _, currentHeight, err := net.Miner.Client.GetBestBlock() + require.NoError(t.t, err) + + // For a Taproot output we cannot leave the outpoint empty. Let's make + // sure the API returns the correct error here. + spendClient, err := net.Alice.ChainClient.RegisterSpendNtfn( + ctxt, &chainrpc.SpendRequest{ + Script: p2trPkScript, + HeightHint: uint32(currentHeight), + }, + ) + require.NoError(t.t, err) + + // The error is only thrown when trying to read a message. + _, err = spendClient.Recv() + require.Contains( + t.t, err.Error(), + "cannot register witness v1 spend request without outpoint", + ) + + // Now try again, this time with the outpoint set. + spendClient, err = net.Alice.ChainClient.RegisterSpendNtfn( + ctxt, &chainrpc.SpendRequest{ + Outpoint: &chainrpc.Outpoint{ + Hash: p2trOutpoint.Hash[:], + Index: p2trOutpoint.Index, + }, + Script: p2trPkScript, + HeightHint: uint32(currentHeight), + }, + ) + require.NoError(t.t, err) + _, err = net.Alice.WalletKitClient.PublishTransaction( ctxt, &walletrpc.Transaction{ TxHex: buf.Bytes(), @@ -257,6 +323,13 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, // Mine another block to clean up the mempool and to make sure the spend // tx is actually included in a block. mineBlocks(t, net, 1, 1) + + // We now expect our spend event to go through. + spendMsg, err := spendClient.Recv() + require.NoError(t.t, err) + spend := spendMsg.GetSpend() + require.NotNil(t.t, spend) + require.Equal(t.t, spend.SpendingHeight, uint32(currentHeight+1)) } // testTaprootKeySpendRPC tests that a tapscript address can also be spent using From f500892681f1d5d7ed21b510a607e5eac70dcdef Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 24 Mar 2022 14:51:46 +0100 Subject: [PATCH 9/9] docs: add release notes --- docs/release-notes/release-notes-0.15.0.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/release-notes/release-notes-0.15.0.md b/docs/release-notes/release-notes-0.15.0.md index eb4cb3cd8ac..bab8a0a2be9 100644 --- a/docs/release-notes/release-notes-0.15.0.md +++ b/docs/release-notes/release-notes-0.15.0.md @@ -5,6 +5,15 @@ * [Misconfigured ZMQ setup now gets reported](https://github.com/lightningnetwork/lnd/pull/5710). +## Taproot + +The internal on-chain wallet of `lnd` is now able to create and spend from +[Taproot (SegWit v1) +addresses](https://github.com/lightningnetwork/lnd/pull/6263). Using +`lncli newaddress p2tr` will create a new BIP-0086 keyspend only address and +then watch it on chain. Taproot script spends are also supported through the +`signrpc.SignOutputRaw` RPC (`/v2/signer/signraw` in REST). + ## `lncli` * Add [auto-generated command-line completions](https://github.com/lightningnetwork/lnd/pull/4177)