mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
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.
This commit is contained in:
parent
fd811a8ded
commit
dec6dd7e70
11 changed files with 733 additions and 67 deletions
|
|
@ -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)
|
||||
|
|
|
|||
12
go.mod
12
go.mod
|
|
@ -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
|
||||
|
|
|
|||
36
go.sum
36
go.sum
|
|
@ -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 {
|
||||
|
|
|
|||
2
swap.go
2
swap.go
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
393
swap/htlc.go
393
swap/htlc.go
|
|
@ -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…
Add table
Add a link
Reference in a new issue