swap: HTLCV3 added

In this commit we add the version 3 htlc, which is implemented with
taproot script spending the two payment paths: the claim path case, and
the timeout case.
pull/482/head
Harsha Goli 2 years ago
parent fd811a8ded
commit dec6dd7e70
No known key found for this signature in database
GPG Key ID: 90E00CCB1C74C611

@ -195,7 +195,7 @@ func (s *Client) FetchSwaps() ([]*SwapInfo, error) {
htlc, err := swap.NewHtlc(
GetHtlcScriptVersion(swp.Contract.ProtocolVersion),
swp.Contract.CltvExpiry, swp.Contract.SenderKey,
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcP2WSH,
swp.Contract.ReceiverKey, nil, swp.Hash, swap.HtlcP2WSH,
s.lndServices.ChainParams,
)
if err != nil {
@ -216,7 +216,7 @@ func (s *Client) FetchSwaps() ([]*SwapInfo, error) {
htlcNP2WSH, err := swap.NewHtlc(
GetHtlcScriptVersion(swp.Contract.ProtocolVersion),
swp.Contract.CltvExpiry, swp.Contract.SenderKey,
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcNP2WSH,
swp.Contract.ReceiverKey, nil, swp.Hash, swap.HtlcNP2WSH,
s.lndServices.ChainParams,
)
if err != nil {
@ -226,7 +226,7 @@ func (s *Client) FetchSwaps() ([]*SwapInfo, error) {
htlcP2WSH, err := swap.NewHtlc(
GetHtlcScriptVersion(swp.Contract.ProtocolVersion),
swp.Contract.CltvExpiry, swp.Contract.SenderKey,
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcP2WSH,
swp.Contract.ReceiverKey, nil, swp.Hash, swap.HtlcP2WSH,
s.lndServices.ChainParams,
)
if err != nil {

@ -284,7 +284,7 @@ func testResume(t *testing.T, confs uint32, expired, preimageRevealed,
scriptVersion := GetHtlcScriptVersion(protocolVersion)
htlc, err := swap.NewHtlc(
scriptVersion, pendingSwap.Contract.CltvExpiry, senderKey,
receiverKey, hash, swap.HtlcP2WSH, &chaincfg.TestNet3Params,
receiverKey, nil, hash, swap.HtlcP2WSH, &chaincfg.TestNet3Params,
)
require.NoError(t, err)
require.Equal(t, htlc.PkScript, confIntent.PkScript)

@ -1,21 +1,23 @@
module github.com/lightninglabs/loop
require (
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 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/chaincfg/chainhash v1.0.1
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcwallet/wtxmgr v1.5.0
github.com/coreos/bbolt v1.3.3
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/fortytw2/leaktest v1.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
github.com/jessevdk/go-flags v1.4.0
github.com/lightninglabs/aperture v0.1.17-beta.0.20220325093943-42b9d4c1be7f
github.com/lightninglabs/aperture v0.1.17-beta.0.20220328072456-4a2632d0be38
github.com/lightninglabs/lndclient v0.15.0-0
github.com/lightninglabs/loop/swapserverrpc v1.0.0
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display
github.com/lightningnetwork/lnd v0.14.1-beta.0.20220324135938-0dcaa511a249
github.com/lightningnetwork/lnd v0.14.1-beta.0.20220328072343-6e2214fd0fc0
github.com/lightningnetwork/lnd/cert v1.1.1
github.com/lightningnetwork/lnd/clock v1.1.0
github.com/lightningnetwork/lnd/queue v1.1.0

@ -73,39 +73,48 @@ 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 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.0/go.mod h1:xMuACsIKDzcE3kWMxqK+aLrAWZ8bMdn7YjYEwNs5q8k=
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=
@ -464,8 +473,8 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightninglabs/aperture v0.1.17-beta.0.20220325093943-42b9d4c1be7f h1:wfyZ3sZXoGayB+V8icHl6uoqOX6wiyncKFK9pTleGRw=
github.com/lightninglabs/aperture v0.1.17-beta.0.20220325093943-42b9d4c1be7f/go.mod h1:lDjRKhndRH0CzZQ2m8dWODdqp/ejEW7esb2u2nlvrw4=
github.com/lightninglabs/aperture v0.1.17-beta.0.20220328072456-4a2632d0be38 h1:ht1wzuSmScXJEnW30Cp2vUBHYzJZvme5DHxNMyg+LT0=
github.com/lightninglabs/aperture v0.1.17-beta.0.20220328072456-4a2632d0be38/go.mod h1:lDjRKhndRH0CzZQ2m8dWODdqp/ejEW7esb2u2nlvrw4=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:antQGRDRJiuyQF6l+k6NECCSImgCpwaZapATth2Chv4=
@ -477,8 +486,9 @@ github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display h1:RZJ8H4ueU/aQ
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE=
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5 h1:TkKwqFcQTGYoI+VEqyxA8rxpCin8qDaYX0AfVRinT3k=
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5/go.mod h1:7dDx73ApjEZA0kcknI799m2O5kkpfg4/gr7N092ojNo=
github.com/lightningnetwork/lnd v0.14.1-beta.0.20220324135938-0dcaa511a249 h1:ELu8An/QgkPU0yQUvTlUE/ZCMBAf4hWacRd1/ayh4pc=
github.com/lightningnetwork/lnd v0.14.1-beta.0.20220324135938-0dcaa511a249/go.mod h1:Tp3ZxsfioUl6kQ30RrbMqWoZyZ4K+fv/o1lMEU8U7rA=
github.com/lightningnetwork/lnd v0.14.1-beta.0.20220328072343-6e2214fd0fc0 h1:pQ7SCPNcaUvPdSikqtCX1nvYn3cdWE8r44zWyZTCgCs=
github.com/lightningnetwork/lnd v0.14.1-beta.0.20220328072343-6e2214fd0fc0/go.mod h1:ocTs4P7UuJrTjl37IyxwSb7/HOzTqF5GX41zFL2guCk=
github.com/lightningnetwork/lnd/cert v1.1.1 h1:Nsav0RlIDRbOnzz2Yu69SQlK939IKya3Q2S0mDviIN8=
github.com/lightningnetwork/lnd/cert v1.1.1/go.mod h1:1P46svkkd73oSoeI4zjkVKgZNwGq8bkGuPR8z+5vQUs=
github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
@ -646,6 +656,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=

@ -54,7 +54,7 @@ func viewOut(swapClient *loop.Client, chainParams *chaincfg.Params) error {
s.Contract.CltvExpiry,
s.Contract.SenderKey,
s.Contract.ReceiverKey,
s.Hash, swap.HtlcP2WSH, chainParams,
nil, s.Hash, swap.HtlcP2WSH, chainParams,
)
if err != nil {
return err
@ -106,7 +106,7 @@ func viewIn(swapClient *loop.Client, chainParams *chaincfg.Params) error {
s.Contract.CltvExpiry,
s.Contract.SenderKey,
s.Contract.ReceiverKey,
s.Hash, swap.HtlcNP2WSH, chainParams,
nil, s.Hash, swap.HtlcNP2WSH, chainParams,
)
if err != nil {
return err

@ -938,7 +938,7 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context,
}
witnessFunc := func(sig []byte) (wire.TxWitness, error) {
return s.htlc.GenTimeoutWitness(sig), nil
return s.htlc.GenTimeoutWitness(sig)
}
sequence := uint32(0)

@ -387,7 +387,7 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
htlc, err := swap.NewHtlc(
scriptVersion, contract.CltvExpiry, contract.SenderKey,
contract.ReceiverKey, testPreimage.Hash(), swap.HtlcNP2WSH,
contract.ReceiverKey, nil, testPreimage.Hash(), swap.HtlcNP2WSH,
cfg.lnd.ChainParams,
)
if err != nil {

@ -72,7 +72,7 @@ func (s *swapKit) getHtlc(outputType swap.HtlcOutputType) (*swap.Htlc, error) {
return swap.NewHtlc(
GetHtlcScriptVersion(s.contract.ProtocolVersion),
s.contract.CltvExpiry, s.contract.SenderKey,
s.contract.ReceiverKey, s.hash, outputType,
s.contract.ReceiverKey, nil, s.hash, outputType,
s.swapConfig.lnd.ChainParams,
)
}

@ -6,10 +6,13 @@ import (
"errors"
"fmt"
btcec "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
)
@ -24,6 +27,9 @@ const (
// HtlcNP2WSH is a nested pay-to-witness-script-hash output that can be
// paid to be legacy wallets.
HtlcNP2WSH
// HtlcP2TR is a pay-to-taproot output with three separate spend paths.
HtlcP2TR
)
// ScriptVersion defines the HTLC script version.
@ -35,17 +41,22 @@ const (
// HtlcV2 refers to the improved version of the HTLC script.
HtlcV2
// HtlcV3 refers to an upgraded version of HtlcV2 implemented with
// tapscript.
HtlcV3
)
// htlcScript defines an interface for the different HTLC implementations.
type HtlcScript interface {
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
genSuccessWitness(receiverSig []byte, preimage lntypes.Preimage) wire.TxWitness
genSuccessWitness(receiverSig []byte,
preimage lntypes.Preimage) (wire.TxWitness, error)
// GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout.
GenTimeoutWitness(senderSig []byte) wire.TxWitness
GenTimeoutWitness(senderSig []byte) (wire.TxWitness, error)
// IsSuccessWitness checks whether the given stack is valid for
// redeeming the htlc.
@ -90,11 +101,30 @@ var (
// script size.
QuoteHtlc, _ = NewHtlc(
HtlcV2,
^int32(0), quoteKey, quoteKey, quoteHash, HtlcP2WSH,
^int32(0), quoteKey, quoteKey, nil, quoteHash, HtlcP2WSH,
&chaincfg.MainNetParams,
)
// ErrInvalidScriptVersion is returned when an unknown htlc version
// is provided to NewHtlc. The supported version are HtlcV1, HtlcV2,
// HtlcV3 as enums.
ErrInvalidScriptVersion = fmt.Errorf("invalid script version")
// ErrInvalidOutputSelected is returned when a taproot output is
// selected for a v1 or v2 script.
ErrInvalidOutputSelected = fmt.Errorf("taproot output selected for " +
"non taproot htlc")
// ErrSharedKeyNotNeeded is returned when a shared key is provided for
// either the v1 or v2 script. Shared key is only necessary for the v3
// script.
ErrSharedKeyNotNeeded = fmt.Errorf("shared key not supported for " +
"script version")
// ErrSharedKeyRequired is returned when a script version requires a
// shared key.
ErrSharedKeyRequired = fmt.Errorf("shared key required for script " +
"version")
)
// String returns the string value of HtlcOutputType.
@ -106,14 +136,18 @@ func (h HtlcOutputType) String() string {
case HtlcNP2WSH:
return "NP2WSH"
case HtlcP2TR:
return "P2TR"
default:
return "unknown"
}
}
// NewHtlc returns a new instance.
// NewHtlc returns a new instance. For v3 scripts, an internal pubkey generated
// by both participants must be provided.
func NewHtlc(version ScriptVersion, cltvExpiry int32,
senderKey, receiverKey [33]byte,
senderKey, receiverKey [33]byte, sharedKey *btcec.PublicKey,
hash lntypes.Hash, outputType HtlcOutputType,
chainParams *chaincfg.Params) (*Htlc, error) {
@ -124,15 +158,30 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
switch version {
case HtlcV1:
if sharedKey != nil {
return nil, ErrSharedKeyNotNeeded
}
htlc, err = newHTLCScriptV1(
cltvExpiry, senderKey, receiverKey, hash,
)
case HtlcV2:
if sharedKey != nil {
return nil, ErrSharedKeyNotNeeded
}
htlc, err = newHTLCScriptV2(
cltvExpiry, senderKey, receiverKey, hash,
)
case HtlcV3:
if sharedKey == nil {
return nil, ErrSharedKeyRequired
}
htlc, err = newHTLCScriptV3(
cltvExpiry, senderKey, receiverKey,
sharedKey, hash,
)
default:
return nil, ErrInvalidScriptVersion
}
@ -141,16 +190,16 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
return nil, err
}
p2wshPkScript, err := input.WitnessScriptHash(htlc.Script())
if err != nil {
return nil, err
}
var pkScript, sigScript []byte
var address btcutil.Address
switch outputType {
case HtlcNP2WSH:
p2wshPkScript, err := input.WitnessScriptHash(htlc.Script())
if err != nil {
return nil, err
}
// Generate p2sh script for p2wsh (nested).
p2wshPkScriptHash := sha256.Sum256(p2wshPkScript)
hash160 := input.Ripemd160H(p2wshPkScriptHash[:])
@ -185,15 +234,40 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
}
case HtlcP2WSH:
pkScript = p2wshPkScript
pkScript, err = input.WitnessScriptHash(htlc.Script())
if err != nil {
return nil, err
}
address, err = btcutil.NewAddressWitnessScriptHash(
p2wshPkScript[2:],
pkScript[2:],
chainParams,
)
if err != nil {
return nil, err
}
case HtlcP2TR:
// Confirm we have a v3 htlc.
trHtlc, ok := htlc.(*HtlcScriptV3)
if !ok {
return nil, ErrInvalidOutputSelected
}
// Generate a tapscript address from our HTLC's taptree.
address, err = btcutil.NewAddressTaproot(
schnorr.SerializePubKey(trHtlc.TaprootKey), chainParams,
)
if err != nil {
return nil, err
}
// Generate locking script.
pkScript, err = txscript.PayToAddrScript(address)
if err != nil {
return nil, err
}
default:
return nil, errors.New("unknown output type")
}
@ -219,33 +293,67 @@ func (h *Htlc) GenSuccessWitness(receiverSig []byte,
return nil, errors.New("preimage doesn't match hash")
}
return h.genSuccessWitness(receiverSig, preimage), nil
return h.genSuccessWitness(receiverSig, preimage)
}
// AddSuccessToEstimator adds a successful spend to a weight estimator.
func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) {
func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) error {
maxSuccessWitnessSize := h.MaxSuccessWitnessSize()
switch h.OutputType {
case HtlcP2TR:
// Generate tapscript.
trHtlc, ok := h.HtlcScript.(*HtlcScriptV3)
if !ok {
return ErrInvalidOutputSelected
}
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript)
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript)
tapscript := input.TapscriptPartialReveal(
trHtlc.InternalPubKey, successLeaf,
timeoutLeaf.TapHash(),
)
estimator.AddTapscriptInput(maxSuccessWitnessSize, tapscript)
case HtlcP2WSH:
estimator.AddWitnessInput(maxSuccessWitnessSize)
case HtlcNP2WSH:
estimator.AddNestedP2WSHInput(maxSuccessWitnessSize)
}
return nil
}
// AddTimeoutToEstimator adds a timeout spend to a weight estimator.
func (h *Htlc) AddTimeoutToEstimator(estimator *input.TxWeightEstimator) {
func (h *Htlc) AddTimeoutToEstimator(estimator *input.TxWeightEstimator) error {
maxTimeoutWitnessSize := h.MaxTimeoutWitnessSize()
switch h.OutputType {
case HtlcP2TR:
// Generate tapscript.
trHtlc, ok := h.HtlcScript.(*HtlcScriptV3)
if !ok {
return ErrInvalidOutputSelected
}
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript)
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript)
tapscript := input.TapscriptPartialReveal(
trHtlc.InternalPubKey, timeoutLeaf,
successLeaf.TapHash(),
)
estimator.AddTapscriptInput(maxTimeoutWitnessSize, tapscript)
case HtlcP2WSH:
estimator.AddWitnessInput(maxTimeoutWitnessSize)
case HtlcNP2WSH:
estimator.AddNestedP2WSHInput(maxTimeoutWitnessSize)
}
return nil
}
// HtlcScriptV1 encapsulates the htlc v1 script.
@ -309,26 +417,27 @@ func newHTLCScriptV1(cltvExpiry int32, senderHtlcKey,
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
func (h *HtlcScriptV1) genSuccessWitness(receiverSig []byte,
preimage lntypes.Preimage) wire.TxWitness {
preimage lntypes.Preimage) (wire.TxWitness, error) {
witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = append(receiverSig, byte(txscript.SigHashAll))
witnessStack[1] = preimage[:]
witnessStack[2] = h.script
return witnessStack
return witnessStack, nil
}
// GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout.
func (h *HtlcScriptV1) GenTimeoutWitness(senderSig []byte) wire.TxWitness {
func (h *HtlcScriptV1) GenTimeoutWitness(
senderSig []byte) (wire.TxWitness, error) {
witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = append(senderSig, byte(txscript.SigHashAll))
witnessStack[1] = []byte{0}
witnessStack[2] = h.script
return witnessStack
return witnessStack, nil
}
// IsSuccessWitness checks whether the given stack is valid for redeeming the
@ -443,14 +552,14 @@ func newHTLCScriptV2(cltvExpiry int32, senderHtlcKey,
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
func (h *HtlcScriptV2) genSuccessWitness(receiverSig []byte,
preimage lntypes.Preimage) wire.TxWitness {
preimage lntypes.Preimage) (wire.TxWitness, error) {
witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = preimage[:]
witnessStack[1] = append(receiverSig, byte(txscript.SigHashAll))
witnessStack[2] = h.script
return witnessStack
return witnessStack, nil
}
// IsSuccessWitness checks whether the given stack is valid for redeeming the
@ -463,7 +572,8 @@ func (h *HtlcScriptV2) IsSuccessWitness(witness wire.TxWitness) bool {
// GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout.
func (h *HtlcScriptV2) GenTimeoutWitness(senderSig []byte) wire.TxWitness {
func (h *HtlcScriptV2) GenTimeoutWitness(
senderSig []byte) (wire.TxWitness, error) {
witnessStack := make(wire.TxWitness, 4)
witnessStack[0] = append(senderSig, byte(txscript.SigHashAll))
@ -471,7 +581,7 @@ func (h *HtlcScriptV2) GenTimeoutWitness(senderSig []byte) wire.TxWitness {
witnessStack[2] = []byte{}
witnessStack[3] = h.script
return witnessStack
return witnessStack, nil
}
// Script returns the htlc script.
@ -512,3 +622,240 @@ func (h *HtlcScriptV2) MaxTimeoutWitnessSize() int {
func (h *HtlcScriptV2) SuccessSequence() uint32 {
return 1
}
// HtlcScriptV3 encapsulates the htlc v3 script.
type HtlcScriptV3 struct {
// The final locking script for the timeout path which is available to
// the sender after the set blockheight.
TimeoutScript []byte
// The final locking script for the success path in which the receiver
// reveals the preimage.
SuccessScript []byte
// The public key for the keyspend path which bypasses the above two
// locking scripts.
InternalPubKey *btcec.PublicKey
// The taproot public key which is created with the above 3 inputs.
TaprootKey *btcec.PublicKey
}
// newHTLCScriptV3 constructs a HtlcScipt with the HTLC V3 taproot script.
func newHTLCScriptV3(cltvExpiry int32, senderHtlcKey,
receiverHtlcKey [33]byte, sharedKey *btcec.PublicKey,
swapHash lntypes.Hash) (*HtlcScriptV3, error) {
receiverPubKey, err := btcec.ParsePubKey(
receiverHtlcKey[:],
)
if err != nil {
return nil, err
}
senderPubKey, err := btcec.ParsePubKey(
senderHtlcKey[:],
)
if err != nil {
return nil, err
}
var schnorrSenderKey, schnorrReceiverKey [32]byte
copy(schnorrSenderKey[:], schnorr.SerializePubKey(senderPubKey))
copy(schnorrReceiverKey[:], schnorr.SerializePubKey(receiverPubKey))
// Create our success path script, we'll use this separately
// to generate the success path leaf.
successPathScript, err := GenSuccessPathScript(
schnorrReceiverKey, swapHash,
)
if err != nil {
return nil, err
}
// Create our timeout path leaf, we'll use this separately
// to generate the timeout path leaf.
timeoutPathScript, err := GenTimeoutPathScript(
schnorrSenderKey, int64(cltvExpiry),
)
if err != nil {
return nil, err
}
// Assemble our taproot script tree from our leaves.
tree := txscript.AssembleTaprootScriptTree(
txscript.NewBaseTapLeaf(successPathScript),
txscript.NewBaseTapLeaf(timeoutPathScript),
)
rootHash := tree.RootNode.TapHash()
// Calculate top level taproot key.
taprootKey := txscript.ComputeTaprootOutputKey(
sharedKey, rootHash[:],
)
return &HtlcScriptV3{
TimeoutScript: timeoutPathScript,
SuccessScript: successPathScript,
InternalPubKey: sharedKey,
TaprootKey: taprootKey,
}, nil
}
// GenTimeoutPathScript constructs an HtlcScript for the timeout payment path.
// Largest possible bytesize of the script is 32 + 1 + 2 + 1 = 36.
//
// <senderHtlcKey> OP_CHECKSIGVERIFY <cltvExpiry> OP_CHECKLOCKTIMEVERIFY
func GenTimeoutPathScript(
senderHtlcKey [32]byte, cltvExpiry int64) ([]byte, error) {
builder := txscript.NewScriptBuilder()
builder.AddData(senderHtlcKey[:])
builder.AddOp(txscript.OP_CHECKSIGVERIFY)
builder.AddInt64(cltvExpiry)
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
return builder.Script()
}
// GenSuccessPathScript constructs an HtlcScript for the success payment path.
// Largest possible bytesize of the script is 32 + 5*1 + 20 + 3*1 = 60.
//
// <receiverHtlcKey> OP_CHECKSIGVERIFY
// OP_SIZE 32 OP_EQUALVERIFY
// OP_HASH160 <ripemd160h(swapHash)> OP_EQUALVERIFY
// 1 OP_CHECKSEQUENCEVERIFY
func GenSuccessPathScript(receiverHtlcKey [32]byte,
swapHash lntypes.Hash) ([]byte, error) {
builder := txscript.NewScriptBuilder()
builder.AddData(receiverHtlcKey[:])
builder.AddOp(txscript.OP_CHECKSIGVERIFY)
builder.AddOp(txscript.OP_SIZE)
builder.AddInt64(32)
builder.AddOp(txscript.OP_EQUALVERIFY)
builder.AddOp(txscript.OP_HASH160)
builder.AddData(input.Ripemd160H(swapHash[:]))
builder.AddOp(txscript.OP_EQUALVERIFY)
builder.AddInt64(1)
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
return builder.Script()
}
// genControlBlock constructs the control block with the depth 1 leaf of the
// unused path to compute the proof. For example if spending path a of (root ->
// a, root -> b), genControlBlock(b.Script) would be used to create the
// controlBlock for a.
func (h *HtlcScriptV3) genControlBlock(leafScript []byte) ([]byte, error) {
var outputKeyYIsOdd bool
// Check for odd bit.
if h.TaprootKey.SerializeCompressed()[0] == secp.PubKeyFormatCompressedOdd {
outputKeyYIsOdd = true
}
// Generate proof with unused script path.
leaf := txscript.NewBaseTapLeaf(leafScript)
proof := leaf.TapHash()
controlBlock := txscript.ControlBlock{
InternalKey: h.InternalPubKey,
OutputKeyYIsOdd: outputKeyYIsOdd,
LeafVersion: txscript.BaseLeafVersion,
InclusionProof: proof[:],
}
return controlBlock.ToBytes()
}
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
func (h *HtlcScriptV3) genSuccessWitness(
receiverSig []byte, preimage lntypes.Preimage) (wire.TxWitness, error) {
controlBlockBytes, err := h.genControlBlock(h.TimeoutScript)
if err != nil {
return nil, err
}
return wire.TxWitness{
preimage[:],
receiverSig,
h.SuccessScript,
controlBlockBytes,
}, nil
}
// GenTimeoutWitness returns the timeout script to spend this htlc after
// timeout.
func (h *HtlcScriptV3) GenTimeoutWitness(
senderSig []byte) (wire.TxWitness, error) {
controlBlockBytes, err := h.genControlBlock(h.SuccessScript)
if err != nil {
return nil, err
}
return wire.TxWitness{
senderSig,
h.TimeoutScript,
controlBlockBytes,
}, nil
}
// IsSuccessWitness checks whether the given stack is valid for
// redeeming the htlc.
func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool {
return len(witness) == 4
}
// Script is not implemented, but necessary to conform to interface.
func (h *HtlcScriptV3) Script() []byte {
return nil
}
// MaxSuccessWitnessSize returns the maximum witness size for the
// success case witness.
func (h *HtlcScriptV3) MaxSuccessWitnessSize() int {
// Calculate maximum success witness size
//
// - number_of_witness_elements: 1 byte
// - sigLength: 1 byte
// - sig: 64 bytes
// - preimage_length: 1 byte
// - preimage: 32 bytes
// - witness_script_length: 1 byte
// - witness_script: 60 bytes
// - control_block_length: 1 byte
// - control_block: 65 bytes
// - leafVersionAndParity: 1
// - internalPubkey: 32
// - proof: 32
return 1 + 1 + 64 + 1 + 32 + 1 + 60 + 1 + 65
}
// MaxTimeoutWitnessSize returns the maximum witness size for the
// timeout case witness.
func (h *HtlcScriptV3) MaxTimeoutWitnessSize() int {
// Calculate maximum timeout witness size
//
// - number_of_witness_elements: 1 byte
// - sigLength: 1 byte
// - sig: 64 bytes
// - witness_script_length: 1 byte
// - witness_script: 36 bytes
// - control_block_length: 1 byte
// - control_block: 65 bytes
// - leafVersionAndParity: 1
// - internalPubkey: 32
// - proof: 32
return 1 + 1 + 64 + 1 + 36 + 1 + 65
}
// SuccessSequence returns the sequence to spend this htlc in the
// success case.
func (h *HtlcScriptV3) SuccessSequence() uint32 {
return 1
}

@ -3,15 +3,18 @@ package swap
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
@ -101,6 +104,8 @@ func TestHtlcV2(t *testing.T) {
var (
testPreimage = lntypes.Preimage([32]byte{1, 2, 3})
err error
receiverKey [33]byte
senderKey [33]byte
)
// We generate a fake output, and the corresponding txin. This output
@ -125,19 +130,14 @@ func TestHtlcV2(t *testing.T) {
senderPrivKey, senderPubKey := test.CreateKey(1)
receiverPrivKey, receiverPubKey := test.CreateKey(2)
var (
senderKey [33]byte
receiverKey [33]byte
)
copy(senderKey[:], senderPubKey.SerializeCompressed())
copy(receiverKey[:], receiverPubKey.SerializeCompressed())
copy(senderKey[:], senderPubKey.SerializeCompressed())
hash := sha256.Sum256(testPreimage[:])
// Create the htlc.
htlc, err := NewHtlc(
HtlcV2, testCltvExpiry,
senderKey, receiverKey, hash,
HtlcV2, testCltvExpiry, senderKey, receiverKey, nil, hash,
HtlcP2WSH, &chaincfg.MainNetParams,
)
require.NoError(t, err)
@ -155,6 +155,9 @@ func TestHtlcV2(t *testing.T) {
receiverSigner := &input.MockSigner{
Privkeys: []*btcec.PrivateKey{receiverPrivKey},
}
prevOutFetcher := txscript.NewCannedPrevOutputFetcher(
htlc.PkScript, 800_000,
)
signTx := func(tx *wire.MsgTx, pubkey *btcec.PublicKey,
signer *input.MockSigner) (input.Signature, error) {
@ -167,8 +170,10 @@ func TestHtlcV2(t *testing.T) {
WitnessScript: htlc.Script(),
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(tx),
InputIndex: 0,
SigHashes: txscript.NewTxSigHashes(
tx, prevOutFetcher,
),
InputIndex: 0,
}
return signer.SignOutputRaw(tx, signDesc)
@ -227,9 +232,12 @@ func TestHtlcV2(t *testing.T) {
)
require.NoError(t, err)
return htlc.GenTimeoutWitness(
witness, err := htlc.GenTimeoutWitness(
sweepSig.Serialize(),
)
require.NoError(t, err)
return witness
}, false,
},
{
@ -242,13 +250,16 @@ func TestHtlcV2(t *testing.T) {
)
require.NoError(t, err)
return htlc.GenTimeoutWitness(
witness, err := htlc.GenTimeoutWitness(
sweepSig.Serialize(),
)
require.NoError(t, err)
return witness
}, true,
},
{
// Receiver can't spend after timeout.
// Receiver can't spend via timeout path.
"timeout case receiver cannot spend",
func(t *testing.T) wire.TxWitness {
sweepTx.LockTime = testCltvExpiry
@ -257,9 +268,12 @@ func TestHtlcV2(t *testing.T) {
)
require.NoError(t, err)
return htlc.GenTimeoutWitness(
witness, err := htlc.GenTimeoutWitness(
sweepSig.Serialize(),
)
require.NoError(t, err)
return witness
}, false,
},
{
@ -272,7 +286,7 @@ func TestHtlcV2(t *testing.T) {
// Create the htlc with the bogus key.
htlc, err = NewHtlc(
HtlcV2, testCltvExpiry,
bogusKey, receiverKey, hash,
bogusKey, receiverKey, nil, hash,
HtlcP2WSH, &chaincfg.MainNetParams,
)
require.NoError(t, err)
@ -289,9 +303,12 @@ func TestHtlcV2(t *testing.T) {
)
require.NoError(t, err)
return htlc.GenTimeoutWitness(
witness, err := htlc.GenTimeoutWitness(
sweepSig.Serialize(),
)
require.NoError(t, err)
return witness
}, false,
},
}
@ -306,7 +323,291 @@ func TestHtlcV2(t *testing.T) {
return txscript.NewEngine(
htlc.PkScript, sweepTx, 0,
txscript.StandardVerifyFlags, nil,
nil, int64(htlcValue),
nil, int64(htlcValue), prevOutFetcher,
)
}
assertEngineExecution(t, testCase.valid, newEngine)
})
}
}
// TestHtlcV3 tests the HTLC V3 script success and timeout spend cases.
func TestHtlcV3(t *testing.T) {
var (
receiverKey [33]byte
senderKey [33]byte
)
preimage := [32]byte{1, 2, 3}
p := lntypes.Preimage(preimage)
hashedPreimage := sha256.Sum256(p[:])
value := int64(800_000)
cltvExpiry := int32(10)
senderPrivKey, senderPubKey := test.CreateKey(1)
receiverPrivKey, receiverPubKey := test.CreateKey(2)
copy(receiverKey[:], receiverPubKey.SerializeCompressed())
copy(senderKey[:], senderPubKey.SerializeCompressed())
randomSharedKey, err := hex.DecodeString(
"03fcb7d1b502bd59f4dbc6cf503e5c280189e0e6dd2d10c4c14d97ed8611" +
"a99178",
)
require.NoError(t, err)
randomSharedPubKey, err := btcec.ParsePubKey(randomSharedKey)
require.NoError(t, err)
htlc, err := NewHtlc(
HtlcV3, cltvExpiry, senderKey, receiverKey, randomSharedPubKey,
hashedPreimage, HtlcP2TR, &chaincfg.MainNetParams,
)
require.NoError(t, err)
trAddress, ok := htlc.Address.(*btcutil.AddressTaproot)
require.True(t, ok)
p2trPkScript, err := txscript.PayToAddrScript(trAddress)
require.NoError(t, err)
tx := wire.NewMsgTx(2)
tx.TxIn = []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
Hash: chainhash.Hash(sha256.Sum256([]byte{1, 2, 3})),
Index: 50,
},
}}
tx.TxOut = []*wire.TxOut{{
PkScript: []byte{
0, 20, 2, 141, 221, 230, 144,
171, 89, 230, 219, 198, 90, 157,
110, 89, 89, 67, 128, 16, 150, 186,
},
Value: value,
}}
prevOutFetcher := txscript.NewCannedPrevOutputFetcher(
p2trPkScript, value,
)
hashCache := txscript.NewTxSigHashes(
tx, prevOutFetcher,
)
signTx := func(tx *wire.MsgTx, privateKey *secp.PrivateKey,
leaf txscript.TapLeaf) []byte {
sig, err := txscript.RawTxInTapscriptSignature(
tx, hashCache, 0, value, p2trPkScript, leaf,
txscript.SigHashDefault, privateKey,
)
require.NoError(t, err)
return sig
}
testCases := []struct {
name string
witness func(*testing.T) wire.TxWitness
valid bool
}{
{
// Receiver can spend with valid preimage.
"success case spend with valid preimage",
func(t *testing.T) wire.TxWitness {
tx.TxIn[0].Sequence = htlc.SuccessSequence()
tx.LockTime = uint32(cltvExpiry)
trHtlc, ok := htlc.HtlcScript.(*HtlcScriptV3)
require.True(t, ok)
sig := signTx(
tx, receiverPrivKey,
txscript.NewBaseTapLeaf(
trHtlc.SuccessScript,
),
)
witness, err := htlc.genSuccessWitness(
sig, preimage,
)
require.NoError(t, err)
return witness
}, true,
},
{
// Receiver can't spend with the valid preimage and with
// zero sequence.
"success case no spend with valid preimage and zero sequence",
func(t *testing.T) wire.TxWitness {
tx.TxIn[0].Sequence = 0
trHtlc, ok := htlc.HtlcScript.(*HtlcScriptV3)
require.True(t, ok)
sig := signTx(
tx, receiverPrivKey,
txscript.NewBaseTapLeaf(
trHtlc.SuccessScript,
),
)
witness, err := htlc.genSuccessWitness(
sig, preimage,
)
require.NoError(t, err)
return witness
}, false,
},
{
// Sender can't spend when haven't yet timed out.
"timeout case no spend before timeout",
func(t *testing.T) wire.TxWitness {
tx.TxIn[0].Sequence = htlc.SuccessSequence()
tx.LockTime = uint32(cltvExpiry) - 1
trHtlc, ok := htlc.HtlcScript.(*HtlcScriptV3)
require.True(t, ok)
sig := signTx(
tx, senderPrivKey,
txscript.NewBaseTapLeaf(
trHtlc.TimeoutScript,
),
)
witness, err := htlc.GenTimeoutWitness(sig)
require.NoError(t, err)
return witness
}, false,
},
{
// Sender can spend after timeout.
"timeout case spend after timeout",
func(t *testing.T) wire.TxWitness {
tx.TxIn[0].Sequence = htlc.SuccessSequence()
tx.LockTime = uint32(cltvExpiry)
trHtlc, ok := htlc.HtlcScript.(*HtlcScriptV3)
require.True(t, ok)
sig := signTx(
tx, senderPrivKey,
txscript.NewBaseTapLeaf(
trHtlc.TimeoutScript,
),
)
witness, err := htlc.GenTimeoutWitness(sig)
require.NoError(t, err)
return witness
}, true,
},
{
// Receiver can't spend via timeout path.
"timeout case receiver cannot spend",
func(t *testing.T) wire.TxWitness {
tx.TxIn[0].Sequence = htlc.SuccessSequence()
tx.LockTime = uint32(cltvExpiry)
trHtlc, ok := htlc.HtlcScript.(*HtlcScriptV3)
require.True(t, ok)
sig := signTx(
tx, receiverPrivKey,
txscript.NewBaseTapLeaf(
trHtlc.TimeoutScript,
),
)
witness, err := htlc.GenTimeoutWitness(sig)
require.NoError(t, err)
return witness
}, false,
},
{
// Sender can't spend after timeout with wrong sender
// key.
"timeout case cannot spend with wrong key",
func(t *testing.T) wire.TxWitness {
var bogusKeyBytes [33]byte
_, bogusKey := test.CreateKey(5)
copy(
bogusKeyBytes[:],
bogusKey.SerializeCompressed(),
)
var shnorrSenderKey [32]byte
copy(
shnorrSenderKey[:],
schnorr.SerializePubKey(
senderPubKey,
),
)
htlc, err := NewHtlc(
HtlcV3, cltvExpiry, bogusKeyBytes,
receiverKey, randomSharedPubKey,
hashedPreimage, HtlcP2TR,
&chaincfg.MainNetParams,
)
require.NoError(t, err)
trAddress, ok := htlc.Address.(*btcutil.AddressTaproot)
require.True(t, ok)
p2trPkScript, err := txscript.PayToAddrScript(
trAddress,
)
require.NoError(t, err)
prevOutFetcher := txscript.NewCannedPrevOutputFetcher(
p2trPkScript, 800_000,
)
hashCache = txscript.NewTxSigHashes(
tx, prevOutFetcher,
)
timeoutScript, err := GenTimeoutPathScript(
shnorrSenderKey, int64(cltvExpiry),
)
require.NoError(t, err)
sig := signTx(
tx, senderPrivKey,
txscript.NewBaseTapLeaf(
timeoutScript,
),
)
witness, err := htlc.genSuccessWitness(
sig, preimage,
)
require.NoError(t, err)
return witness
}, false,
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
tx.TxIn[0].Witness = testCase.witness(t)
newEngine := func() (*txscript.Engine, error) {
return txscript.NewEngine(
p2trPkScript, tx, 0,
txscript.StandardVerifyFlags, nil,
hashCache, value, prevOutFetcher,
)
}

@ -92,7 +92,7 @@ func (s *Sweeper) CreateSweepTx(
// function that is expected to add the weight of the input to the weight
// estimator.
func (s *Sweeper) GetSweepFee(ctx context.Context,
addInputEstimate func(*input.TxWeightEstimator),
addInputEstimate func(*input.TxWeightEstimator) error,
destAddr btcutil.Address, sweepConfTarget int32) (
btcutil.Amount, error) {
@ -117,7 +117,11 @@ func (s *Sweeper) GetSweepFee(ctx context.Context,
return 0, fmt.Errorf("unknown address type %T", destAddr)
}
addInputEstimate(&weightEstimate)
err = addInputEstimate(&weightEstimate)
if err != nil {
return 0, err
}
weight := weightEstimate.Weight()
return feeRate.FeeForWeight(int64(weight)), nil

Loading…
Cancel
Save