mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge pull request #547 from bhandras/musig-keyreveal
loop: Loop In MuSig2 support
This commit is contained in:
commit
7f19c43047
16 changed files with 1075 additions and 574 deletions
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -240,8 +239,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,
|
||||
|
|
@ -273,30 +276,13 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed,
|
|||
// Expect client to register for our expected number of confirmations.
|
||||
confIntent := ctx.AssertRegisterConf(preimageRevealed, int32(confs))
|
||||
|
||||
// Assert that the loopout htlc equals to the expected one.
|
||||
scriptVersion := GetHtlcScriptVersion(protocolVersion)
|
||||
var htlc *swap.Htlc
|
||||
|
||||
switch scriptVersion {
|
||||
case swap.HtlcV2:
|
||||
htlc, err = swap.NewHtlcV2(
|
||||
pendingSwap.Contract.CltvExpiry, senderKey,
|
||||
receiverKey, hash, &chaincfg.TestNet3Params,
|
||||
)
|
||||
|
||||
case swap.HtlcV3:
|
||||
htlc, err = swap.NewHtlcV3(
|
||||
input.MuSig2Version040,
|
||||
pendingSwap.Contract.CltvExpiry, senderKey,
|
||||
receiverKey, senderKey, receiverKey, hash,
|
||||
&chaincfg.TestNet3Params,
|
||||
)
|
||||
|
||||
default:
|
||||
t.Fatalf(swap.ErrInvalidScriptVersion.Error())
|
||||
}
|
||||
|
||||
htlc, err := GetHtlc(
|
||||
hash, &pendingSwap.Contract.SwapContract,
|
||||
&chaincfg.TestNet3Params,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Assert that the loopout htlc equals to the expected one.
|
||||
require.Equal(t, htlc.PkScript, confIntent.PkScript)
|
||||
|
||||
signalSwapPaymentResult(nil)
|
||||
|
|
@ -315,7 +301,7 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed,
|
|||
func(r error) {},
|
||||
func(r error) {},
|
||||
preimageRevealed,
|
||||
confIntent, scriptVersion,
|
||||
confIntent, GetHtlcScriptVersion(protocolVersion),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ const (
|
|||
// HTLC v3 (P2TR) script for swaps.
|
||||
ProtocolVersionHtlcV3 = 10
|
||||
|
||||
// ProtocolVersionMuSig2 will enable MuSig2 signature scheme for loops.
|
||||
ProtocolVersionMuSig2 ProtocolVersion = 11
|
||||
|
||||
// ProtocolVersionUnrecorded is set for swaps were created before we
|
||||
// started saving protocol version with swaps.
|
||||
ProtocolVersionUnrecorded ProtocolVersion = math.MaxUint32
|
||||
|
|
@ -65,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 (
|
||||
|
|
@ -141,6 +144,9 @@ func (p ProtocolVersion) String() string {
|
|||
case ProtocolVersionHtlcV3:
|
||||
return "HTLC V3"
|
||||
|
||||
case ProtocolVersionMuSig2:
|
||||
return "MuSig2"
|
||||
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
|
|
|||
172
loopdb/store.go
172
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{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
110
loopin.go
110
loopin.go
|
|
@ -9,6 +9,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/mempool"
|
||||
|
|
@ -19,6 +20,7 @@ import (
|
|||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
invpkg "github.com/lightningnetwork/lnd/invoices"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
|
|
@ -189,6 +191,23 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Default the HTLC internal key to our sender key.
|
||||
senderInternalPubKey := senderKey
|
||||
|
||||
// If this is a MuSig2 swap then we'll generate a brand new key pair
|
||||
// and will use that as the internal key for the HTLC.
|
||||
if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 {
|
||||
secret, err := sharedSecretFromHash(
|
||||
globalCtx, cfg.lnd.Signer, swapHash,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, pubKey := btcec.PrivKeyFromBytes(secret[:])
|
||||
copy(senderInternalPubKey[:], pubKey.SerializeCompressed())
|
||||
}
|
||||
|
||||
// Create a cancellable context that is used for monitoring the probe.
|
||||
probeWaitCtx, probeWaitCancel := context.WithCancel(globalCtx)
|
||||
|
||||
|
|
@ -204,8 +223,8 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
// htlc.
|
||||
log.Infof("Initiating swap request at height %v", currentHeight)
|
||||
swapResp, err := cfg.server.NewLoopInSwap(globalCtx, swapHash,
|
||||
request.Amount, senderKey, swapInvoice, probeInvoice,
|
||||
request.LastHop, request.Initiator,
|
||||
request.Amount, senderKey, senderInternalPubKey, swapInvoice,
|
||||
probeInvoice, request.LastHop, request.Initiator,
|
||||
)
|
||||
probeWaitCancel()
|
||||
if err != nil {
|
||||
|
|
@ -237,19 +256,30 @@ 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(),
|
||||
},
|
||||
}
|
||||
|
||||
// For MuSig2 swaps we store the proper internal keys that we generated
|
||||
// and received from the server.
|
||||
if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 {
|
||||
contract.HtlcKeys.SenderInternalPubKey = senderInternalPubKey
|
||||
contract.HtlcKeys.ReceiverInternalPubKey = swapResp.receiverInternalKey
|
||||
}
|
||||
|
||||
swapKit := newSwapKit(
|
||||
swapHash, swap.TypeIn,
|
||||
cfg, &contract.SwapContract,
|
||||
|
|
@ -809,6 +839,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
|
|||
|
||||
htlcSpend := false
|
||||
invoiceFinalized := false
|
||||
htlcKeyRevealed := false
|
||||
for !htlcSpend || !invoiceFinalized {
|
||||
select {
|
||||
// Spend notification error.
|
||||
|
|
@ -825,6 +856,10 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
|
|||
return err
|
||||
}
|
||||
|
||||
if invoiceFinalized && !htlcKeyRevealed {
|
||||
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
|
||||
}
|
||||
|
||||
// The htlc spend is confirmed. Inspect the spending tx to
|
||||
// determine the final swap state.
|
||||
case spendDetails := <-spendChan:
|
||||
|
|
@ -888,6 +923,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
|
|||
}
|
||||
|
||||
invoiceFinalized = true
|
||||
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
|
||||
|
||||
// Canceled invoice has no effect on server cost
|
||||
// balance.
|
||||
|
|
@ -903,6 +939,36 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
|
|||
return nil
|
||||
}
|
||||
|
||||
// tryPushHtlcKey attempts to push the htlc key to the server. If the server
|
||||
// returns an error of any kind we'll log it as a warning but won't act as
|
||||
// the swap execution can just go on without the server gaining knowledge of
|
||||
// our internal key.
|
||||
func (s *loopInSwap) tryPushHtlcKey(ctx context.Context) bool {
|
||||
if s.ProtocolVersion < loopdb.ProtocolVersionMuSig2 {
|
||||
return false
|
||||
}
|
||||
|
||||
log.Infof("Attempting to reveal internal HTLC key to the server")
|
||||
|
||||
internalPrivKey, err := sharedSecretFromHash(
|
||||
ctx, s.swapConfig.lnd.Signer, s.hash,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Warnf("Unable to derive HTLC internal private key: %v",
|
||||
err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
err = s.server.PushKey(ctx, s.ProtocolVersion, s.hash, internalPrivKey)
|
||||
if err != nil {
|
||||
s.log.Warnf("Internal HTLC key reveal failed: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *loopInSwap) processHtlcSpend(ctx context.Context,
|
||||
spend *chainntnfs.SpendDetail, htlcValue,
|
||||
sweepFee btcutil.Amount) error {
|
||||
|
|
@ -975,8 +1041,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
|
||||
|
|
@ -1026,3 +1093,18 @@ func (s *loopInSwap) setState(state loopdb.SwapState) {
|
|||
s.lastUpdateTime = time.Now()
|
||||
s.state = state
|
||||
}
|
||||
|
||||
// sharedSecretFromHash derives the shared secret from the swap hash using the
|
||||
// swap.KeyFamily family and zero as index.
|
||||
func sharedSecretFromHash(ctx context.Context, signer lndclient.SignerClient,
|
||||
hash lntypes.Hash) ([32]byte, error) {
|
||||
|
||||
_, hashPubKey := btcec.PrivKeyFromBytes(hash[:])
|
||||
|
||||
return signer.DeriveSharedKey(
|
||||
ctx, hashPubKey, &keychain.KeyLocator{
|
||||
Family: keychain.KeyFamily(swap.KeyFamily),
|
||||
Index: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ import (
|
|||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
invpkg "github.com/lightningnetwork/lnd/invoices"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -343,6 +341,7 @@ func TestLoopInResume(t *testing.T) {
|
|||
loopdb.ProtocolVersionUnrecorded,
|
||||
loopdb.ProtocolVersionHtlcV2,
|
||||
loopdb.ProtocolVersionHtlcV3,
|
||||
loopdb.ProtocolVersionMuSig2,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
|
|
@ -414,8 +413,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,
|
||||
|
|
@ -445,32 +448,10 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
|
|||
pendSwap.Loop.Events[0].Cost = cost
|
||||
}
|
||||
|
||||
var (
|
||||
htlc *swap.Htlc
|
||||
err error
|
||||
htlc, err := GetHtlc(
|
||||
testPreimage.Hash(), &contract.SwapContract,
|
||||
cfg.lnd.ChainParams,
|
||||
)
|
||||
|
||||
switch GetHtlcScriptVersion(storedVersion) {
|
||||
case swap.HtlcV2:
|
||||
htlc, err = swap.NewHtlcV2(
|
||||
contract.CltvExpiry, contract.SenderKey,
|
||||
contract.ReceiverKey, 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(),
|
||||
cfg.lnd.ChainParams,
|
||||
)
|
||||
|
||||
default:
|
||||
t.Fatalf("unknown HTLC script version")
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ctx.store.CreateLoopIn(testPreimage.Hash(), contract)
|
||||
|
|
|
|||
57
loopout.go
57
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,
|
||||
}
|
||||
|
|
@ -1351,11 +1355,28 @@ func (s *loopOutSwap) createMuSig2SweepTxn(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
signers := [][]byte{
|
||||
s.SenderKey[1:], s.ReceiverKey[1:],
|
||||
var (
|
||||
signers [][]byte
|
||||
muSig2Verion input.MuSig2Version
|
||||
)
|
||||
|
||||
// Depending on the MuSig2 version we either pass 32 byte Schnorr
|
||||
// public keys or normal 33 byte public keys.
|
||||
if s.ProtocolVersion >= loopdb.ProtocolVersionMuSig2 {
|
||||
muSig2Verion = input.MuSig2Version100RC2
|
||||
signers = [][]byte{
|
||||
s.HtlcKeys.SenderInternalPubKey[:],
|
||||
s.HtlcKeys.ReceiverInternalPubKey[:],
|
||||
}
|
||||
} else {
|
||||
muSig2Verion = input.MuSig2Version040
|
||||
signers = [][]byte{
|
||||
s.HtlcKeys.SenderInternalPubKey[1:],
|
||||
s.HtlcKeys.ReceiverInternalPubKey[1:],
|
||||
}
|
||||
}
|
||||
|
||||
htlc, ok := s.htlc.HtlcScript.(*swap.HtlcScriptV3)
|
||||
htlcScript, ok := s.htlc.HtlcScript.(*swap.HtlcScriptV3)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("non taproot htlc")
|
||||
}
|
||||
|
|
@ -1363,8 +1384,8 @@ 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, muSig2Verion, &s.HtlcKeys.ClientScriptKeyLocator, signers,
|
||||
lndclient.MuSig2TaprootTweakOpt(htlcScript.RootHash[:], false),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1428,7 +1449,7 @@ func (s *loopOutSwap) createMuSig2SweepTxn(
|
|||
// To be sure that we're good, parse and validate that the combined
|
||||
// signature is indeed valid for the sig hash and the internal pubkey.
|
||||
err = s.executeConfig.verifySchnorrSig(
|
||||
htlc.TaprootKey, sigHash, finalSig,
|
||||
htlcScript.TaprootKey, sigHash, finalSig,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1609,8 +1630,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
|
||||
|
|
|
|||
|
|
@ -151,17 +151,22 @@ func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, err
|
|||
}
|
||||
|
||||
func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
|
||||
amount btcutil.Amount, _ [33]byte, swapInvoice, _ string,
|
||||
amount btcutil.Amount, _, _ [33]byte, swapInvoice, _ string,
|
||||
_ *route.Vertex, _ string) (*newLoopInResponse, error) {
|
||||
|
||||
_, receiverKey := test.CreateKey(101)
|
||||
_, receiverInternalKey := test.CreateKey(102)
|
||||
|
||||
if amount != s.expectedSwapAmt {
|
||||
return nil, errors.New("unexpected test swap amount")
|
||||
}
|
||||
|
||||
var receiverKeyArray [33]byte
|
||||
var receiverKeyArray, receiverInternalKeyArray [33]byte
|
||||
copy(receiverKeyArray[:], receiverKey.SerializeCompressed())
|
||||
copy(
|
||||
receiverInternalKeyArray[:],
|
||||
receiverInternalKey.SerializeCompressed(),
|
||||
)
|
||||
|
||||
s.swapInvoice = swapInvoice
|
||||
s.swapHash = swapHash
|
||||
|
|
@ -175,8 +180,9 @@ func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
|
|||
<-s.lnd.FailInvoiceChannel
|
||||
|
||||
resp := &newLoopInResponse{
|
||||
expiry: s.height + testChargeOnChainCltvDelta,
|
||||
receiverKey: receiverKeyArray,
|
||||
expiry: s.height + testChargeOnChainCltvDelta,
|
||||
receiverKey: receiverKeyArray,
|
||||
receiverInternalKey: receiverInternalKeyArray,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
|
@ -261,3 +267,9 @@ func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion
|
|||
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (s *serverMock) PushKey(_ context.Context, _ loopdb.ProtocolVersion,
|
||||
_ lntypes.Hash, _ [32]byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
23
swap.go
23
swap.go
|
|
@ -76,18 +76,27 @@ 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:
|
||||
// Swaps that implement the new MuSig2 protocol will be expected
|
||||
// to use the 1.0RC2 MuSig2 key derivation scheme.
|
||||
muSig2Version := input.MuSig2Version040
|
||||
if contract.ProtocolVersion >= loopdb.ProtocolVersionMuSig2 {
|
||||
muSig2Version = input.MuSig2Version100RC2
|
||||
}
|
||||
|
||||
return swap.NewHtlcV3(
|
||||
input.MuSig2Version040,
|
||||
contract.CltvExpiry, contract.SenderKey,
|
||||
contract.ReceiverKey, contract.SenderKey,
|
||||
contract.ReceiverKey, hash,
|
||||
chainParams,
|
||||
muSig2Version,
|
||||
contract.CltvExpiry,
|
||||
contract.HtlcKeys.SenderInternalPubKey,
|
||||
contract.HtlcKeys.ReceiverInternalPubKey,
|
||||
contract.HtlcKeys.SenderScriptKey,
|
||||
contract.HtlcKeys.ReceiverScriptKey,
|
||||
hash, chainParams,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,10 +94,10 @@ type swapServerClient interface {
|
|||
preimage lntypes.Preimage) error
|
||||
|
||||
NewLoopInSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount,
|
||||
senderKey [33]byte, swapInvoice, probeInvoice string,
|
||||
lastHop *route.Vertex, initiator string) (*newLoopInResponse,
|
||||
error)
|
||||
swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey,
|
||||
senderInternalKey [33]byte, swapInvoice, probeInvoice string,
|
||||
lastHop *route.Vertex, initiator string) (
|
||||
*newLoopInResponse, error)
|
||||
|
||||
// SubscribeLoopOutUpdates subscribes to loop out server state.
|
||||
SubscribeLoopOutUpdates(ctx context.Context,
|
||||
|
|
@ -128,6 +128,12 @@ type swapServerClient interface {
|
|||
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
|
||||
paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) (
|
||||
[]byte, []byte, error)
|
||||
|
||||
// PushKey sends the client's HTLC internal key associated with the
|
||||
// swap to the server.
|
||||
PushKey(ctx context.Context,
|
||||
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
|
||||
clientInternalPrivateKey [32]byte) error
|
||||
}
|
||||
|
||||
type grpcSwapServerClient struct {
|
||||
|
|
@ -419,9 +425,9 @@ func (s *grpcSwapServerClient) PushLoopOutPreimage(ctx context.Context,
|
|||
}
|
||||
|
||||
func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount, senderKey [33]byte,
|
||||
swapInvoice, probeInvoice string, lastHop *route.Vertex,
|
||||
initiator string) (*newLoopInResponse, error) {
|
||||
swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey,
|
||||
senderInternalKey [33]byte, swapInvoice, probeInvoice string,
|
||||
lastHop *route.Vertex, initiator string) (*newLoopInResponse, error) {
|
||||
|
||||
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
|
||||
defer rpcCancel()
|
||||
|
|
@ -429,7 +435,7 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
|
|||
req := &looprpc.ServerLoopInRequest{
|
||||
SwapHash: swapHash[:],
|
||||
Amt: uint64(amount),
|
||||
SenderKey: senderKey[:],
|
||||
SenderKey: senderScriptKey[:],
|
||||
SwapInvoice: swapInvoice,
|
||||
ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
|
||||
ProbeInvoice: probeInvoice,
|
||||
|
|
@ -439,13 +445,19 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
|
|||
req.LastHop = lastHop[:]
|
||||
}
|
||||
|
||||
// Set the client's internal key if this is a MuSig2 swap.
|
||||
if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 {
|
||||
req.SenderInternalPubkey = senderInternalKey[:]
|
||||
}
|
||||
|
||||
swapResp, err := s.server.NewLoopInSwap(rpcCtx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var receiverKey [33]byte
|
||||
var receiverKey, receiverInternalKey [33]byte
|
||||
copy(receiverKey[:], swapResp.ReceiverKey)
|
||||
copy(receiverInternalKey[:], swapResp.ReceiverInternalPubkey)
|
||||
|
||||
// Validate receiver key.
|
||||
_, err = btcec.ParsePubKey(receiverKey[:])
|
||||
|
|
@ -454,9 +466,10 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
|
|||
}
|
||||
|
||||
return &newLoopInResponse{
|
||||
receiverKey: receiverKey,
|
||||
expiry: swapResp.Expiry,
|
||||
serverMessage: swapResp.ServerMessage,
|
||||
receiverKey: receiverKey,
|
||||
receiverInternalKey: receiverInternalKey,
|
||||
expiry: swapResp.Expiry,
|
||||
serverMessage: swapResp.ServerMessage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -753,6 +766,25 @@ func (s *grpcSwapServerClient) MuSig2SignSweep(ctx context.Context,
|
|||
return res.Nonce, res.PartialSignature, nil
|
||||
}
|
||||
|
||||
// PushKey sends the client's HTLC internal key associated with the swap to
|
||||
// the server.
|
||||
func (s *grpcSwapServerClient) PushKey(ctx context.Context,
|
||||
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
|
||||
clientInternalPrivateKey [32]byte) error {
|
||||
|
||||
req := &looprpc.ServerPushKeyReq{
|
||||
ProtocolVersion: looprpc.ProtocolVersion(protocolVersion),
|
||||
SwapHash: swapHash[:],
|
||||
InternalPrivkey: clientInternalPrivateKey[:],
|
||||
}
|
||||
|
||||
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
|
||||
defer rpcCancel()
|
||||
|
||||
_, err := s.server.PushKey(rpcCtx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func rpcRouteCancel(details *outCancelDetails) (
|
||||
*looprpc.CancelLoopOutSwapRequest_RouteCancel, error) {
|
||||
|
||||
|
|
@ -873,7 +905,8 @@ type newLoopOutResponse struct {
|
|||
}
|
||||
|
||||
type newLoopInResponse struct {
|
||||
receiverKey [33]byte
|
||||
expiry int32
|
||||
serverMessage string
|
||||
receiverKey [33]byte
|
||||
receiverInternalKey [33]byte
|
||||
expiry int32
|
||||
serverMessage string
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -45,6 +45,8 @@ service SwapServer {
|
|||
returns (ReportRoutingResultRes);
|
||||
|
||||
rpc MuSig2SignSweep (MuSig2SignSweepReq) returns (MuSig2SignSweepRes);
|
||||
|
||||
rpc PushKey (ServerPushKeyReq) returns (ServerPushKeyRes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,6 +99,9 @@ enum ProtocolVersion {
|
|||
|
||||
// The client will use the new v3 (taproot) HTLC scripts.
|
||||
HTLC_V3 = 10;
|
||||
|
||||
// Enables MuSig2 signature scheme for swaps.
|
||||
MUSIG2 = 11;
|
||||
}
|
||||
|
||||
message ServerLoopOutRequest {
|
||||
|
|
@ -196,14 +201,21 @@ message ServerLoopOutTerms {
|
|||
|
||||
message ServerLoopInRequest {
|
||||
bytes sender_key = 1;
|
||||
|
||||
bytes sender_internal_pubkey = 9;
|
||||
|
||||
bytes swap_hash = 2;
|
||||
|
||||
uint64 amt = 3;
|
||||
|
||||
string swap_invoice = 4;
|
||||
|
||||
bytes last_hop = 5;
|
||||
|
||||
/// The protocol version that the client adheres to.
|
||||
ProtocolVersion protocol_version = 6;
|
||||
|
||||
// An invoice that can be used for the purpose of probing.
|
||||
string probe_invoice = 7;
|
||||
|
||||
// The user agent string that identifies the software running on the user's
|
||||
|
|
@ -218,6 +230,9 @@ message ServerLoopInRequest {
|
|||
|
||||
message ServerLoopInResponse {
|
||||
bytes receiver_key = 1;
|
||||
|
||||
bytes receiver_internal_pubkey = 9;
|
||||
|
||||
int32 expiry = 2;
|
||||
|
||||
// A human-readable message from the loop server.
|
||||
|
|
@ -458,7 +473,7 @@ message CancelLoopOutSwapResponse {
|
|||
}
|
||||
|
||||
message ServerProbeRequest {
|
||||
/// The protocol version that the client adheres to.
|
||||
// The protocol version that the client adheres to.
|
||||
ProtocolVersion protocol_version = 1;
|
||||
|
||||
// The probe amount.
|
||||
|
|
@ -470,9 +485,7 @@ message ServerProbeRequest {
|
|||
// Optional last hop to use when probing the client.
|
||||
bytes last_hop = 4;
|
||||
|
||||
/*
|
||||
Optional route hints to reach the destination through private channels.
|
||||
*/
|
||||
// Optional route hints to reach the destination through private channels.
|
||||
repeated RouteHint route_hints = 5;
|
||||
}
|
||||
|
||||
|
|
@ -555,3 +568,17 @@ message MuSig2SignSweepRes {
|
|||
// The partial signature of the server for the requested sighash.
|
||||
bytes partial_signature = 2;
|
||||
}
|
||||
|
||||
message ServerPushKeyReq {
|
||||
// The protocol version that the client adheres to.
|
||||
ProtocolVersion protocol_version = 1;
|
||||
|
||||
// The swap hash.
|
||||
bytes swap_hash = 2;
|
||||
|
||||
// The client’s private key used in the HTLC’s aggregated internal key.
|
||||
bytes internal_privkey = 3;
|
||||
}
|
||||
|
||||
message ServerPushKeyRes {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ type SwapServerClient interface {
|
|||
RecommendRoutingPlugin(ctx context.Context, in *RecommendRoutingPluginReq, opts ...grpc.CallOption) (*RecommendRoutingPluginRes, error)
|
||||
ReportRoutingResult(ctx context.Context, in *ReportRoutingResultReq, opts ...grpc.CallOption) (*ReportRoutingResultRes, error)
|
||||
MuSig2SignSweep(ctx context.Context, in *MuSig2SignSweepReq, opts ...grpc.CallOption) (*MuSig2SignSweepRes, error)
|
||||
PushKey(ctx context.Context, in *ServerPushKeyReq, opts ...grpc.CallOption) (*ServerPushKeyRes, error)
|
||||
}
|
||||
|
||||
type swapServerClient struct {
|
||||
|
|
@ -214,6 +215,15 @@ func (c *swapServerClient) MuSig2SignSweep(ctx context.Context, in *MuSig2SignSw
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *swapServerClient) PushKey(ctx context.Context, in *ServerPushKeyReq, opts ...grpc.CallOption) (*ServerPushKeyRes, error) {
|
||||
out := new(ServerPushKeyRes)
|
||||
err := c.cc.Invoke(ctx, "/looprpc.SwapServer/PushKey", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SwapServerServer is the server API for SwapServer service.
|
||||
// All implementations must embed UnimplementedSwapServerServer
|
||||
// for forward compatibility
|
||||
|
|
@ -232,6 +242,7 @@ type SwapServerServer interface {
|
|||
RecommendRoutingPlugin(context.Context, *RecommendRoutingPluginReq) (*RecommendRoutingPluginRes, error)
|
||||
ReportRoutingResult(context.Context, *ReportRoutingResultReq) (*ReportRoutingResultRes, error)
|
||||
MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error)
|
||||
PushKey(context.Context, *ServerPushKeyReq) (*ServerPushKeyRes, error)
|
||||
mustEmbedUnimplementedSwapServerServer()
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +292,9 @@ func (UnimplementedSwapServerServer) ReportRoutingResult(context.Context, *Repor
|
|||
func (UnimplementedSwapServerServer) MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MuSig2SignSweep not implemented")
|
||||
}
|
||||
func (UnimplementedSwapServerServer) PushKey(context.Context, *ServerPushKeyReq) (*ServerPushKeyRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PushKey not implemented")
|
||||
}
|
||||
func (UnimplementedSwapServerServer) mustEmbedUnimplementedSwapServerServer() {}
|
||||
|
||||
// UnsafeSwapServerServer may be embedded to opt out of forward compatibility for this service.
|
||||
|
|
@ -552,6 +566,24 @@ func _SwapServer_MuSig2SignSweep_Handler(srv interface{}, ctx context.Context, d
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SwapServer_PushKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ServerPushKeyReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwapServerServer).PushKey(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/looprpc.SwapServer/PushKey",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwapServerServer).PushKey(ctx, req.(*ServerPushKeyReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// SwapServer_ServiceDesc is the grpc.ServiceDesc for SwapServer service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
|
|
@ -607,6 +639,10 @@ var SwapServer_ServiceDesc = grpc.ServiceDesc{
|
|||
MethodName: "MuSig2SignSweep",
|
||||
Handler: _SwapServer_MuSig2SignSweep_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "PushKey",
|
||||
Handler: _SwapServer_PushKey_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue