diff --git a/client_test.go b/client_test.go index 8539a88..ba3ff6e 100644 --- a/client_test.go +++ b/client_test.go @@ -240,8 +240,12 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed, Preimage: preimage, AmountRequested: amt, CltvExpiry: 744, - ReceiverKey: receiverKey, - SenderKey: senderKey, + HtlcKeys: loopdb.HtlcKeys{ + SenderScriptKey: senderKey, + SenderInternalPubKey: senderKey, + ReceiverScriptKey: receiverKey, + ReceiverInternalPubKey: receiverKey, + }, MaxSwapFee: 60000, MaxMinerFee: 50000, ProtocolVersion: protocolVersion, diff --git a/loopdb/loop.go b/loopdb/loop.go index e98745f..867476b 100644 --- a/loopdb/loop.go +++ b/loopdb/loop.go @@ -10,6 +10,31 @@ import ( "github.com/lightningnetwork/lnd/lntypes" ) +// HtlcKeys is a holder of all keys used when constructing the swap HTLC. Since +// it's used for both loop in and loop out swaps it may hold partial information +// about the sender or receiver depending on the swap type. +type HtlcKeys struct { + // SenderScriptKey is the sender's public key that is used in the HTLC, + // specifically when constructing the script spend scripts. + SenderScriptKey [33]byte + + // SenderInternalPubKey is the sender's internal pubkey that is used in + // taproot HTLCs as part of the aggregate internal key. + SenderInternalPubKey [33]byte + + // ReceiverScriptKey is the receiver's public key that is used in the + // HTLC, specifically when constructing the script spend scripts. + ReceiverScriptKey [33]byte + + // ReceiverInternalPubKey is the sender's internal pubkey that is used + // in taproot HTLCs as part of the aggregate internal key. + ReceiverInternalPubKey [33]byte + + // ClientScriptKeyLocator is the client's key locator for the key used + // in the HTLC script spend scripts. + ClientScriptKeyLocator keychain.KeyLocator +} + // SwapContract contains the base data that is serialized to persistent storage // for pending swaps. type SwapContract struct { @@ -19,18 +44,8 @@ type SwapContract struct { // AmountRequested is the total amount of the swap. AmountRequested btcutil.Amount - // SenderKey is the key of the sender that will be used in the on-chain - // HTLC. - SenderKey [33]byte - - // ReceiverKey is the of the receiver that will be used in the on-chain - // HTLC. - ReceiverKey [33]byte - - // ClientKeyLocator is the key locator (family and index) for the client - // key. It is for the receiver key if this is a loop out contract, or - // the sender key if this is a loop in contract. - ClientKeyLocator keychain.KeyLocator + // HtlcKeys holds all keys used in the swap HTLC construction. + HtlcKeys HtlcKeys // CltvExpiry is the total absolute CLTV expiry of the swap. CltvExpiry int32 diff --git a/loopdb/loopin.go b/loopdb/loopin.go index 7a665e2..e10e898 100644 --- a/loopdb/loopin.go +++ b/loopdb/loopin.go @@ -63,7 +63,7 @@ func serializeLoopInContract(swap *LoopInContract) ( return nil, err } - n, err := b.Write(swap.SenderKey[:]) + n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:]) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func serializeLoopInContract(swap *LoopInContract) ( return nil, fmt.Errorf("sender key has invalid length") } - n, err = b.Write(swap.ReceiverKey[:]) + n, err = b.Write(swap.HtlcKeys.ReceiverScriptKey[:]) if err != nil { return nil, err } @@ -161,7 +161,7 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) { return nil, err } - n, err := r.Read(contract.SenderKey[:]) + n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:]) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) { return nil, fmt.Errorf("sender key has invalid length") } - n, err = r.Read(contract.ReceiverKey[:]) + n, err = r.Read(contract.HtlcKeys.ReceiverScriptKey[:]) if err != nil { return nil, err } diff --git a/loopdb/loopout.go b/loopdb/loopout.go index ad628a9..9ebb858 100644 --- a/loopdb/loopout.go +++ b/loopdb/loopout.go @@ -134,7 +134,7 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) ( return nil, err } - n, err := r.Read(contract.SenderKey[:]) + n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:]) if err != nil { return nil, err } @@ -142,7 +142,7 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) ( return nil, fmt.Errorf("sender key has invalid length") } - n, err = r.Read(contract.ReceiverKey[:]) + n, err = r.Read(contract.HtlcKeys.ReceiverScriptKey[:]) if err != nil { return nil, err } @@ -229,7 +229,7 @@ func serializeLoopOutContract(swap *LoopOutContract) ( return nil, err } - n, err := b.Write(swap.SenderKey[:]) + n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:]) if err != nil { return nil, err } @@ -237,7 +237,7 @@ func serializeLoopOutContract(swap *LoopOutContract) ( return nil, fmt.Errorf("sender key has invalid length") } - n, err = b.Write(swap.ReceiverKey[:]) + n, err = b.Write(swap.HtlcKeys.ReceiverScriptKey[:]) if err != nil { return nil, err } diff --git a/loopdb/protocol_version.go b/loopdb/protocol_version.go index 2e95eca..a9fdee8 100644 --- a/loopdb/protocol_version.go +++ b/loopdb/protocol_version.go @@ -68,7 +68,7 @@ const ( // experimentalRPCProtocolVersion defines the RPC protocol version that // includes all currently experimentally released features. - experimentalRPCProtocolVersion = looprpc.ProtocolVersion_HTLC_V3 + experimentalRPCProtocolVersion = looprpc.ProtocolVersion_MUSIG2 ) var ( diff --git a/loopdb/store.go b/loopdb/store.go index b86008d..63b4be4 100644 --- a/loopdb/store.go +++ b/loopdb/store.go @@ -112,9 +112,30 @@ var ( // value: concatenation of uint32 values [family, index]. keyLocatorKey = []byte("keylocator") + // senderInternalPubKeyKey is the key that stores the sender's internal + // public key which used when constructing the swap HTLC. + // + // path: loopInBucket/loopOutBucket -> swapBucket[hash] + // -> senderInternalPubKeyKey + // value: serialized public key. + senderInternalPubKeyKey = []byte("sender-internal-pubkey") + + // receiverInternalPubKeyKey is the key that stores the receiver's + // internal public key which is used when constructing the swap HTLC. + // + // path: loopInBucket/loopOutBucket -> swapBucket[hash] + // -> receiverInternalPubKeyKey + // value: serialized public key. + receiverInternalPubKeyKey = []byte("receiver-internal-pubkey") + byteOrder = binary.BigEndian + // keyLength is the length of a serialized public key. keyLength = 33 + + // errInvalidKey is returned when a serialized key is not the expected + // length. + errInvalidKey = fmt.Errorf("invalid serialized key") ) const ( @@ -233,6 +254,95 @@ func NewBoltSwapStore(dbPath string, chainParams *chaincfg.Params) ( }, nil } +// marshalHtlcKeys marshals the HTLC keys of the swap contract into the swap +// bucket. +func marshalHtlcKeys(swapBucket *bbolt.Bucket, contract *SwapContract) error { + var err error + + // Store the key locator for swaps that use taproot HTLCs. + if contract.ProtocolVersion >= ProtocolVersionHtlcV3 { + keyLocator, err := MarshalKeyLocator( + contract.HtlcKeys.ClientScriptKeyLocator, + ) + if err != nil { + return err + } + + err = swapBucket.Put(keyLocatorKey, keyLocator) + if err != nil { + return err + } + } + + // Store the internal keys for MuSig2 swaps. + if contract.ProtocolVersion >= ProtocolVersionMuSig2 { + // Internal pubkeys are always filled. + err = swapBucket.Put( + senderInternalPubKeyKey, + contract.HtlcKeys.SenderInternalPubKey[:], + ) + if err != nil { + return err + } + + err = swapBucket.Put( + receiverInternalPubKeyKey, + contract.HtlcKeys.ReceiverInternalPubKey[:], + ) + if err != nil { + return err + } + } + + return nil +} + +// unmarshalHtlcKeys deserializes the htlc keys from the swap bucket. +func unmarshalHtlcKeys(swapBucket *bbolt.Bucket, contract *SwapContract) error { + var err error + + // HTLC V3 contracts have the client script key locator stored. + if contract.ProtocolVersion >= ProtocolVersionHtlcV3 && + ProtocolVersionUnrecorded > contract.ProtocolVersion { + + contract.HtlcKeys.ClientScriptKeyLocator, err = + UnmarshalKeyLocator( + swapBucket.Get(keyLocatorKey), + ) + if err != nil { + return err + } + + // Default the internal scriptkeys to the sender and receiver + // keys. + contract.HtlcKeys.SenderInternalPubKey = + contract.HtlcKeys.SenderScriptKey + contract.HtlcKeys.ReceiverInternalPubKey = + contract.HtlcKeys.ReceiverScriptKey + } + + // MuSig2 contracts have the internal keys stored too. + if contract.ProtocolVersion >= ProtocolVersionMuSig2 && + ProtocolVersionUnrecorded > contract.ProtocolVersion { + + // The pubkeys used for the joint HTLC internal key are always + // present. + key := swapBucket.Get(senderInternalPubKeyKey) + if len(key) != keyLength { + return errInvalidKey + } + copy(contract.HtlcKeys.SenderInternalPubKey[:], key) + + key = swapBucket.Get(receiverInternalPubKeyKey) + if len(key) != keyLength { + return errInvalidKey + } + copy(contract.HtlcKeys.ReceiverInternalPubKey[:], key) + } + + return nil +} + // FetchLoopOutSwaps returns all loop out swaps currently in the store. // // NOTE: Part of the loopdb.SwapStore interface. @@ -435,19 +545,10 @@ func (s *boltSwapStore) CreateLoopOut(hash lntypes.Hash, return err } - // Store the key locator for swaps with taproot htlc. - if swap.ProtocolVersion >= ProtocolVersionHtlcV3 { - keyLocator, err := MarshalKeyLocator( - swap.ClientKeyLocator, - ) - if err != nil { - return err - } - - err = swapBucket.Put(keyLocatorKey, keyLocator) - if err != nil { - return err - } + // Store the htlc keys and server key locator. + err = marshalHtlcKeys(swapBucket, &swap.SwapContract) + if err != nil { + return err } // Finally, we'll create an empty updates bucket for this swap @@ -501,19 +602,10 @@ func (s *boltSwapStore) CreateLoopIn(hash lntypes.Hash, return err } - // Store the key locator for swaps with taproot htlc. - if swap.ProtocolVersion >= ProtocolVersionHtlcV3 { - keyLocator, err := MarshalKeyLocator( - swap.ClientKeyLocator, - ) - if err != nil { - return err - } - - err = swapBucket.Put(keyLocatorKey, keyLocator) - if err != nil { - return err - } + // Store the htlc keys and server key locator. + err = marshalHtlcKeys(swapBucket, &swap.SwapContract) + if err != nil { + return err } // Finally, we'll create an empty updates bucket for this swap @@ -788,14 +880,12 @@ func (s *boltSwapStore) fetchLoopOutSwap(rootBucket *bbolt.Bucket, return nil, err } - // Try to unmarshal the key locator. - if contract.ProtocolVersion >= ProtocolVersionHtlcV3 { - contract.ClientKeyLocator, err = UnmarshalKeyLocator( - swapBucket.Get(keyLocatorKey), - ) - if err != nil { - return nil, err - } + // Unmarshal HTLC keys if the contract is recent. + err = unmarshalHtlcKeys( + swapBucket, &contract.SwapContract, + ) + if err != nil { + return nil, err } loop := LoopOut{ @@ -865,14 +955,12 @@ func (s *boltSwapStore) fetchLoopInSwap(rootBucket *bbolt.Bucket, return nil, err } - // Try to unmarshal the key locator. - if contract.ProtocolVersion >= ProtocolVersionHtlcV3 { - contract.ClientKeyLocator, err = UnmarshalKeyLocator( - swapBucket.Get(keyLocatorKey), - ) - if err != nil { - return nil, err - } + // Unmarshal HTLC keys if the contract is recent. + err = unmarshalHtlcKeys( + swapBucket, &contract.SwapContract, + ) + if err != nil { + return nil, err } loop := LoopIn{ diff --git a/loopdb/store_test.go b/loopdb/store_test.go index 8d2602c..324e965 100644 --- a/loopdb/store_test.go +++ b/loopdb/store_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/coreos/bbolt" "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" @@ -28,6 +29,16 @@ var ( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, } + senderInternalKey = [33]byte{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + } + + receiverInternalKey = [33]byte{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, + } + testPreimage = lntypes.Preimage([32]byte{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, @@ -51,9 +62,16 @@ func TestLoopOutStore(t *testing.T) { AmountRequested: 100, Preimage: testPreimage, CltvExpiry: 144, - SenderKey: senderKey, - - ReceiverKey: receiverKey, + HtlcKeys: HtlcKeys{ + SenderScriptKey: senderKey, + ReceiverScriptKey: receiverKey, + SenderInternalPubKey: senderInternalKey, + ReceiverInternalPubKey: receiverInternalKey, + ClientScriptKeyLocator: keychain.KeyLocator{ + Family: 1, + Index: 2, + }, + }, MaxMinerFee: 10, MaxSwapFee: 20, @@ -61,7 +79,8 @@ func TestLoopOutStore(t *testing.T) { // Convert to/from unix to remove timezone, so that it // doesn't interfere with DeepEqual. - InitiationTime: time.Unix(0, initiationTime.UnixNano()), + InitiationTime: time.Unix(0, initiationTime.UnixNano()), + ProtocolVersion: ProtocolVersionMuSig2, }, MaxPrepayRoutingFee: 40, PrepayInvoice: "prepayinvoice", @@ -195,18 +214,27 @@ func TestLoopInStore(t *testing.T) { pendingSwap := LoopInContract{ SwapContract: SwapContract{ - AmountRequested: 100, - Preimage: testPreimage, - CltvExpiry: 144, - SenderKey: senderKey, - ReceiverKey: receiverKey, + AmountRequested: 100, + Preimage: testPreimage, + CltvExpiry: 144, + HtlcKeys: HtlcKeys{ + SenderScriptKey: senderKey, + ReceiverScriptKey: receiverKey, + SenderInternalPubKey: senderInternalKey, + ReceiverInternalPubKey: receiverInternalKey, + ClientScriptKeyLocator: keychain.KeyLocator{ + Family: 1, + Index: 2, + }, + }, MaxMinerFee: 10, MaxSwapFee: 20, InitiationHeight: 99, // Convert to/from unix to remove timezone, so that it // doesn't interfere with DeepEqual. - InitiationTime: time.Unix(0, initiationTime.UnixNano()), + InitiationTime: time.Unix(0, initiationTime.UnixNano()), + ProtocolVersion: ProtocolVersionMuSig2, }, HtlcConfTarget: 2, LastHop: &lastHop, diff --git a/loopin.go b/loopin.go index ae0c697..c329725 100644 --- a/loopin.go +++ b/loopin.go @@ -237,16 +237,20 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, SwapContract: loopdb.SwapContract{ InitiationHeight: currentHeight, InitiationTime: initiationTime, - ReceiverKey: swapResp.receiverKey, - SenderKey: senderKey, - ClientKeyLocator: keyDesc.KeyLocator, - Preimage: swapPreimage, - AmountRequested: request.Amount, - CltvExpiry: swapResp.expiry, - MaxMinerFee: request.MaxMinerFee, - MaxSwapFee: request.MaxSwapFee, - Label: request.Label, - ProtocolVersion: loopdb.CurrentProtocolVersion(), + HtlcKeys: loopdb.HtlcKeys{ + SenderScriptKey: senderKey, + SenderInternalPubKey: senderKey, + ReceiverScriptKey: swapResp.receiverKey, + ReceiverInternalPubKey: swapResp.receiverKey, + ClientScriptKeyLocator: keyDesc.KeyLocator, + }, + Preimage: swapPreimage, + AmountRequested: request.Amount, + CltvExpiry: swapResp.expiry, + MaxMinerFee: request.MaxMinerFee, + MaxSwapFee: request.MaxSwapFee, + Label: request.Label, + ProtocolVersion: loopdb.CurrentProtocolVersion(), }, } @@ -975,8 +979,9 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context, sequence := uint32(0) timeoutTx, err := s.sweeper.CreateSweepTx( - ctx, s.height, sequence, s.htlc, *htlcOutpoint, s.SenderKey, - redeemScript, witnessFunc, htlcValue, fee, s.timeoutAddr, + ctx, s.height, sequence, s.htlc, *htlcOutpoint, + s.HtlcKeys.SenderScriptKey, redeemScript, witnessFunc, + htlcValue, fee, s.timeoutAddr, ) if err != nil { return 0, err diff --git a/loopin_test.go b/loopin_test.go index 7bf3b8f..ca3b707 100644 --- a/loopin_test.go +++ b/loopin_test.go @@ -343,6 +343,7 @@ func TestLoopInResume(t *testing.T) { loopdb.ProtocolVersionUnrecorded, loopdb.ProtocolVersionHtlcV2, loopdb.ProtocolVersionHtlcV3, + loopdb.ProtocolVersionMuSig2, } testCases := []struct { @@ -414,8 +415,12 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, Preimage: testPreimage, AmountRequested: 100000, CltvExpiry: 744, - ReceiverKey: receiverKey, - SenderKey: senderKey, + HtlcKeys: loopdb.HtlcKeys{ + SenderScriptKey: senderKey, + SenderInternalPubKey: senderKey, + ReceiverScriptKey: receiverKey, + ReceiverInternalPubKey: receiverKey, + }, MaxSwapFee: 60000, MaxMinerFee: 50000, ProtocolVersion: storedVersion, @@ -453,17 +458,22 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, switch GetHtlcScriptVersion(storedVersion) { case swap.HtlcV2: htlc, err = swap.NewHtlcV2( - contract.CltvExpiry, contract.SenderKey, - contract.ReceiverKey, testPreimage.Hash(), + contract.CltvExpiry, + contract.HtlcKeys.SenderScriptKey, + contract.HtlcKeys.ReceiverScriptKey, + testPreimage.Hash(), cfg.lnd.ChainParams, ) case swap.HtlcV3: htlc, err = swap.NewHtlcV3( input.MuSig2Version040, - contract.CltvExpiry, contract.SenderKey, - contract.ReceiverKey, contract.SenderKey, - contract.ReceiverKey, testPreimage.Hash(), + contract.CltvExpiry, + contract.HtlcKeys.SenderInternalPubKey, + contract.HtlcKeys.ReceiverInternalPubKey, + contract.HtlcKeys.SenderScriptKey, + contract.HtlcKeys.ReceiverScriptKey, + testPreimage.Hash(), cfg.lnd.ChainParams, ) diff --git a/loopout.go b/loopout.go index 00654fe..ff9bd92 100644 --- a/loopout.go +++ b/loopout.go @@ -181,16 +181,20 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig, SwapContract: loopdb.SwapContract{ InitiationHeight: currentHeight, InitiationTime: initiationTime, - SenderKey: swapResp.senderKey, - ReceiverKey: receiverKey, - ClientKeyLocator: keyDesc.KeyLocator, - Preimage: swapPreimage, - AmountRequested: request.Amount, - CltvExpiry: request.Expiry, - MaxMinerFee: request.MaxMinerFee, - MaxSwapFee: request.MaxSwapFee, - Label: request.Label, - ProtocolVersion: loopdb.CurrentProtocolVersion(), + HtlcKeys: loopdb.HtlcKeys{ + SenderScriptKey: swapResp.senderKey, + SenderInternalPubKey: swapResp.senderKey, + ReceiverScriptKey: receiverKey, + ReceiverInternalPubKey: receiverKey, + ClientScriptKeyLocator: keyDesc.KeyLocator, + }, + Preimage: swapPreimage, + AmountRequested: request.Amount, + CltvExpiry: request.Expiry, + MaxMinerFee: request.MaxMinerFee, + MaxSwapFee: request.MaxSwapFee, + Label: request.Label, + ProtocolVersion: loopdb.CurrentProtocolVersion(), }, OutgoingChanSet: chanSet, } @@ -1352,7 +1356,8 @@ func (s *loopOutSwap) createMuSig2SweepTxn( } signers := [][]byte{ - s.SenderKey[1:], s.ReceiverKey[1:], + s.HtlcKeys.SenderInternalPubKey[1:], + s.HtlcKeys.ReceiverInternalPubKey[1:], } htlc, ok := s.htlc.HtlcScript.(*swap.HtlcScriptV3) @@ -1363,8 +1368,9 @@ func (s *loopOutSwap) createMuSig2SweepTxn( // Now we're creating a local MuSig2 session using the receiver key's // key locator and the htlc's root hash. musig2SessionInfo, err := s.lnd.Signer.MuSig2CreateSession( - ctx, input.MuSig2Version040, &s.ClientKeyLocator, - signers, lndclient.MuSig2TaprootTweakOpt(htlc.RootHash[:], false), + ctx, input.MuSig2Version040, + &s.HtlcKeys.ClientScriptKeyLocator, signers, + lndclient.MuSig2TaprootTweakOpt(htlc.RootHash[:], false), ) if err != nil { return nil, err @@ -1609,8 +1615,8 @@ func (s *loopOutSwap) sweep(ctx context.Context, htlcOutpoint wire.OutPoint, // Create sweep tx. sweepTx, err := s.sweeper.CreateSweepTx( ctx, s.height, s.htlc.SuccessSequence(), s.htlc, - htlcOutpoint, s.ReceiverKey, redeemScript, witnessFunc, - htlcValue, fee, s.DestAddr, + htlcOutpoint, s.contract.HtlcKeys.ReceiverScriptKey, + redeemScript, witnessFunc, htlcValue, fee, s.DestAddr, ) if err != nil { return err diff --git a/swap.go b/swap.go index 6e2d947..caa5d21 100644 --- a/swap.go +++ b/swap.go @@ -76,18 +76,20 @@ func GetHtlc(hash lntypes.Hash, contract *loopdb.SwapContract, switch GetHtlcScriptVersion(contract.ProtocolVersion) { case swap.HtlcV2: return swap.NewHtlcV2( - contract.CltvExpiry, contract.SenderKey, - contract.ReceiverKey, hash, + contract.CltvExpiry, contract.HtlcKeys.SenderScriptKey, + contract.HtlcKeys.ReceiverScriptKey, hash, chainParams, ) case swap.HtlcV3: return swap.NewHtlcV3( input.MuSig2Version040, - contract.CltvExpiry, contract.SenderKey, - contract.ReceiverKey, contract.SenderKey, - contract.ReceiverKey, hash, - chainParams, + contract.CltvExpiry, + contract.HtlcKeys.SenderInternalPubKey, + contract.HtlcKeys.ReceiverInternalPubKey, + contract.HtlcKeys.SenderScriptKey, + contract.HtlcKeys.ReceiverScriptKey, + hash, chainParams, ) }