Merge pull request #547 from bhandras/musig-keyreveal

loop: Loop In MuSig2 support
pull/560/head
András Bánki-Horváth 1 year ago committed by GitHub
commit 7f19c43047
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -13,7 +13,6 @@ import (
"github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test" "github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -240,8 +239,12 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed,
Preimage: preimage, Preimage: preimage,
AmountRequested: amt, AmountRequested: amt,
CltvExpiry: 744, CltvExpiry: 744,
ReceiverKey: receiverKey, HtlcKeys: loopdb.HtlcKeys{
SenderKey: senderKey, SenderScriptKey: senderKey,
SenderInternalPubKey: senderKey,
ReceiverScriptKey: receiverKey,
ReceiverInternalPubKey: receiverKey,
},
MaxSwapFee: 60000, MaxSwapFee: 60000,
MaxMinerFee: 50000, MaxMinerFee: 50000,
ProtocolVersion: protocolVersion, 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. // Expect client to register for our expected number of confirmations.
confIntent := ctx.AssertRegisterConf(preimageRevealed, int32(confs)) confIntent := ctx.AssertRegisterConf(preimageRevealed, int32(confs))
// Assert that the loopout htlc equals to the expected one. htlc, err := GetHtlc(
scriptVersion := GetHtlcScriptVersion(protocolVersion) hash, &pendingSwap.Contract.SwapContract,
var htlc *swap.Htlc &chaincfg.TestNet3Params,
)
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())
}
require.NoError(t, err) require.NoError(t, err)
// Assert that the loopout htlc equals to the expected one.
require.Equal(t, htlc.PkScript, confIntent.PkScript) require.Equal(t, htlc.PkScript, confIntent.PkScript)
signalSwapPaymentResult(nil) signalSwapPaymentResult(nil)
@ -315,7 +301,7 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed,
func(r error) {}, func(r error) {},
func(r error) {}, func(r error) {},
preimageRevealed, preimageRevealed,
confIntent, scriptVersion, confIntent, GetHtlcScriptVersion(protocolVersion),
) )
} }

@ -10,6 +10,31 @@ import (
"github.com/lightningnetwork/lnd/lntypes" "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 // SwapContract contains the base data that is serialized to persistent storage
// for pending swaps. // for pending swaps.
type SwapContract struct { type SwapContract struct {
@ -19,18 +44,8 @@ type SwapContract struct {
// AmountRequested is the total amount of the swap. // AmountRequested is the total amount of the swap.
AmountRequested btcutil.Amount AmountRequested btcutil.Amount
// SenderKey is the key of the sender that will be used in the on-chain // HtlcKeys holds all keys used in the swap HTLC construction.
// HTLC. HtlcKeys HtlcKeys
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
// CltvExpiry is the total absolute CLTV expiry of the swap. // CltvExpiry is the total absolute CLTV expiry of the swap.
CltvExpiry int32 CltvExpiry int32

@ -63,7 +63,7 @@ func serializeLoopInContract(swap *LoopInContract) (
return nil, err return nil, err
} }
n, err := b.Write(swap.SenderKey[:]) n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -71,7 +71,7 @@ func serializeLoopInContract(swap *LoopInContract) (
return nil, fmt.Errorf("sender key has invalid length") 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 { if err != nil {
return nil, err return nil, err
} }
@ -161,7 +161,7 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) {
return nil, err return nil, err
} }
n, err := r.Read(contract.SenderKey[:]) n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -169,7 +169,7 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) {
return nil, fmt.Errorf("sender key has invalid length") 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 { if err != nil {
return nil, err return nil, err
} }

@ -134,7 +134,7 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) (
return nil, err return nil, err
} }
n, err := r.Read(contract.SenderKey[:]) n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -142,7 +142,7 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) (
return nil, fmt.Errorf("sender key has invalid length") 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 { if err != nil {
return nil, err return nil, err
} }
@ -229,7 +229,7 @@ func serializeLoopOutContract(swap *LoopOutContract) (
return nil, err return nil, err
} }
n, err := b.Write(swap.SenderKey[:]) n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -237,7 +237,7 @@ func serializeLoopOutContract(swap *LoopOutContract) (
return nil, fmt.Errorf("sender key has invalid length") 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 { if err != nil {
return nil, err return nil, err
} }

@ -55,6 +55,9 @@ const (
// HTLC v3 (P2TR) script for swaps. // HTLC v3 (P2TR) script for swaps.
ProtocolVersionHtlcV3 = 10 ProtocolVersionHtlcV3 = 10
// ProtocolVersionMuSig2 will enable MuSig2 signature scheme for loops.
ProtocolVersionMuSig2 ProtocolVersion = 11
// ProtocolVersionUnrecorded is set for swaps were created before we // ProtocolVersionUnrecorded is set for swaps were created before we
// started saving protocol version with swaps. // started saving protocol version with swaps.
ProtocolVersionUnrecorded ProtocolVersion = math.MaxUint32 ProtocolVersionUnrecorded ProtocolVersion = math.MaxUint32
@ -65,7 +68,7 @@ const (
// experimentalRPCProtocolVersion defines the RPC protocol version that // experimentalRPCProtocolVersion defines the RPC protocol version that
// includes all currently experimentally released features. // includes all currently experimentally released features.
experimentalRPCProtocolVersion = looprpc.ProtocolVersion_HTLC_V3 experimentalRPCProtocolVersion = looprpc.ProtocolVersion_MUSIG2
) )
var ( var (
@ -141,6 +144,9 @@ func (p ProtocolVersion) String() string {
case ProtocolVersionHtlcV3: case ProtocolVersionHtlcV3:
return "HTLC V3" return "HTLC V3"
case ProtocolVersionMuSig2:
return "MuSig2"
default: default:
return "Unknown" return "Unknown"
} }

@ -112,9 +112,30 @@ var (
// value: concatenation of uint32 values [family, index]. // value: concatenation of uint32 values [family, index].
keyLocatorKey = []byte("keylocator") 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 byteOrder = binary.BigEndian
// keyLength is the length of a serialized public key.
keyLength = 33 keyLength = 33
// errInvalidKey is returned when a serialized key is not the expected
// length.
errInvalidKey = fmt.Errorf("invalid serialized key")
) )
const ( const (
@ -233,6 +254,95 @@ func NewBoltSwapStore(dbPath string, chainParams *chaincfg.Params) (
}, nil }, 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. // FetchLoopOutSwaps returns all loop out swaps currently in the store.
// //
// NOTE: Part of the loopdb.SwapStore interface. // NOTE: Part of the loopdb.SwapStore interface.
@ -435,19 +545,10 @@ func (s *boltSwapStore) CreateLoopOut(hash lntypes.Hash,
return err return err
} }
// Store the key locator for swaps with taproot htlc. // Store the htlc keys and server key locator.
if swap.ProtocolVersion >= ProtocolVersionHtlcV3 { err = marshalHtlcKeys(swapBucket, &swap.SwapContract)
keyLocator, err := MarshalKeyLocator( if err != nil {
swap.ClientKeyLocator, return err
)
if err != nil {
return err
}
err = swapBucket.Put(keyLocatorKey, keyLocator)
if err != nil {
return err
}
} }
// Finally, we'll create an empty updates bucket for this swap // Finally, we'll create an empty updates bucket for this swap
@ -501,19 +602,10 @@ func (s *boltSwapStore) CreateLoopIn(hash lntypes.Hash,
return err return err
} }
// Store the key locator for swaps with taproot htlc. // Store the htlc keys and server key locator.
if swap.ProtocolVersion >= ProtocolVersionHtlcV3 { err = marshalHtlcKeys(swapBucket, &swap.SwapContract)
keyLocator, err := MarshalKeyLocator( if err != nil {
swap.ClientKeyLocator, return err
)
if err != nil {
return err
}
err = swapBucket.Put(keyLocatorKey, keyLocator)
if err != nil {
return err
}
} }
// Finally, we'll create an empty updates bucket for this swap // 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 return nil, err
} }
// Try to unmarshal the key locator. // Unmarshal HTLC keys if the contract is recent.
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 { err = unmarshalHtlcKeys(
contract.ClientKeyLocator, err = UnmarshalKeyLocator( swapBucket, &contract.SwapContract,
swapBucket.Get(keyLocatorKey), )
) if err != nil {
if err != nil { return nil, err
return nil, err
}
} }
loop := LoopOut{ loop := LoopOut{
@ -865,14 +955,12 @@ func (s *boltSwapStore) fetchLoopInSwap(rootBucket *bbolt.Bucket,
return nil, err return nil, err
} }
// Try to unmarshal the key locator. // Unmarshal HTLC keys if the contract is recent.
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 { err = unmarshalHtlcKeys(
contract.ClientKeyLocator, err = UnmarshalKeyLocator( swapBucket, &contract.SwapContract,
swapBucket.Get(keyLocatorKey), )
) if err != nil {
if err != nil { return nil, err
return nil, err
}
} }
loop := LoopIn{ loop := LoopIn{

@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/coreos/bbolt" "github.com/coreos/bbolt"
"github.com/lightninglabs/loop/test" "github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require" "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, 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{ testPreimage = lntypes.Preimage([32]byte{
1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4,
@ -51,9 +62,16 @@ func TestLoopOutStore(t *testing.T) {
AmountRequested: 100, AmountRequested: 100,
Preimage: testPreimage, Preimage: testPreimage,
CltvExpiry: 144, CltvExpiry: 144,
SenderKey: senderKey, HtlcKeys: HtlcKeys{
SenderScriptKey: senderKey,
ReceiverKey: receiverKey, ReceiverScriptKey: receiverKey,
SenderInternalPubKey: senderInternalKey,
ReceiverInternalPubKey: receiverInternalKey,
ClientScriptKeyLocator: keychain.KeyLocator{
Family: 1,
Index: 2,
},
},
MaxMinerFee: 10, MaxMinerFee: 10,
MaxSwapFee: 20, MaxSwapFee: 20,
@ -61,7 +79,8 @@ func TestLoopOutStore(t *testing.T) {
// Convert to/from unix to remove timezone, so that it // Convert to/from unix to remove timezone, so that it
// doesn't interfere with DeepEqual. // doesn't interfere with DeepEqual.
InitiationTime: time.Unix(0, initiationTime.UnixNano()), InitiationTime: time.Unix(0, initiationTime.UnixNano()),
ProtocolVersion: ProtocolVersionMuSig2,
}, },
MaxPrepayRoutingFee: 40, MaxPrepayRoutingFee: 40,
PrepayInvoice: "prepayinvoice", PrepayInvoice: "prepayinvoice",
@ -195,18 +214,27 @@ func TestLoopInStore(t *testing.T) {
pendingSwap := LoopInContract{ pendingSwap := LoopInContract{
SwapContract: SwapContract{ SwapContract: SwapContract{
AmountRequested: 100, AmountRequested: 100,
Preimage: testPreimage, Preimage: testPreimage,
CltvExpiry: 144, CltvExpiry: 144,
SenderKey: senderKey, HtlcKeys: HtlcKeys{
ReceiverKey: receiverKey, SenderScriptKey: senderKey,
ReceiverScriptKey: receiverKey,
SenderInternalPubKey: senderInternalKey,
ReceiverInternalPubKey: receiverInternalKey,
ClientScriptKeyLocator: keychain.KeyLocator{
Family: 1,
Index: 2,
},
},
MaxMinerFee: 10, MaxMinerFee: 10,
MaxSwapFee: 20, MaxSwapFee: 20,
InitiationHeight: 99, InitiationHeight: 99,
// Convert to/from unix to remove timezone, so that it // Convert to/from unix to remove timezone, so that it
// doesn't interfere with DeepEqual. // doesn't interfere with DeepEqual.
InitiationTime: time.Unix(0, initiationTime.UnixNano()), InitiationTime: time.Unix(0, initiationTime.UnixNano()),
ProtocolVersion: ProtocolVersionMuSig2,
}, },
HtlcConfTarget: 2, HtlcConfTarget: 2,
LastHop: &lastHop, LastHop: &lastHop,

@ -9,6 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/mempool" "github.com/btcsuite/btcd/mempool"
@ -19,6 +20,7 @@ import (
"github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
invpkg "github.com/lightningnetwork/lnd/invoices" invpkg "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
@ -189,6 +191,23 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
return nil, err 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. // Create a cancellable context that is used for monitoring the probe.
probeWaitCtx, probeWaitCancel := context.WithCancel(globalCtx) probeWaitCtx, probeWaitCancel := context.WithCancel(globalCtx)
@ -204,8 +223,8 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
// htlc. // htlc.
log.Infof("Initiating swap request at height %v", currentHeight) log.Infof("Initiating swap request at height %v", currentHeight)
swapResp, err := cfg.server.NewLoopInSwap(globalCtx, swapHash, swapResp, err := cfg.server.NewLoopInSwap(globalCtx, swapHash,
request.Amount, senderKey, swapInvoice, probeInvoice, request.Amount, senderKey, senderInternalPubKey, swapInvoice,
request.LastHop, request.Initiator, probeInvoice, request.LastHop, request.Initiator,
) )
probeWaitCancel() probeWaitCancel()
if err != nil { if err != nil {
@ -237,19 +256,30 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
SwapContract: loopdb.SwapContract{ SwapContract: loopdb.SwapContract{
InitiationHeight: currentHeight, InitiationHeight: currentHeight,
InitiationTime: initiationTime, InitiationTime: initiationTime,
ReceiverKey: swapResp.receiverKey, HtlcKeys: loopdb.HtlcKeys{
SenderKey: senderKey, SenderScriptKey: senderKey,
ClientKeyLocator: keyDesc.KeyLocator, SenderInternalPubKey: senderKey,
Preimage: swapPreimage, ReceiverScriptKey: swapResp.receiverKey,
AmountRequested: request.Amount, ReceiverInternalPubKey: swapResp.receiverKey,
CltvExpiry: swapResp.expiry, ClientScriptKeyLocator: keyDesc.KeyLocator,
MaxMinerFee: request.MaxMinerFee, },
MaxSwapFee: request.MaxSwapFee, Preimage: swapPreimage,
Label: request.Label, AmountRequested: request.Amount,
ProtocolVersion: loopdb.CurrentProtocolVersion(), 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( swapKit := newSwapKit(
swapHash, swap.TypeIn, swapHash, swap.TypeIn,
cfg, &contract.SwapContract, cfg, &contract.SwapContract,
@ -809,6 +839,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
htlcSpend := false htlcSpend := false
invoiceFinalized := false invoiceFinalized := false
htlcKeyRevealed := false
for !htlcSpend || !invoiceFinalized { for !htlcSpend || !invoiceFinalized {
select { select {
// Spend notification error. // Spend notification error.
@ -825,6 +856,10 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
return err return err
} }
if invoiceFinalized && !htlcKeyRevealed {
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
}
// The htlc spend is confirmed. Inspect the spending tx to // The htlc spend is confirmed. Inspect the spending tx to
// determine the final swap state. // determine the final swap state.
case spendDetails := <-spendChan: case spendDetails := <-spendChan:
@ -888,6 +923,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
} }
invoiceFinalized = true invoiceFinalized = true
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
// Canceled invoice has no effect on server cost // Canceled invoice has no effect on server cost
// balance. // balance.
@ -903,6 +939,36 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
return nil 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, func (s *loopInSwap) processHtlcSpend(ctx context.Context,
spend *chainntnfs.SpendDetail, htlcValue, spend *chainntnfs.SpendDetail, htlcValue,
sweepFee btcutil.Amount) error { sweepFee btcutil.Amount) error {
@ -975,8 +1041,9 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context,
sequence := uint32(0) sequence := uint32(0)
timeoutTx, err := s.sweeper.CreateSweepTx( timeoutTx, err := s.sweeper.CreateSweepTx(
ctx, s.height, sequence, s.htlc, *htlcOutpoint, s.SenderKey, ctx, s.height, sequence, s.htlc, *htlcOutpoint,
redeemScript, witnessFunc, htlcValue, fee, s.timeoutAddr, s.HtlcKeys.SenderScriptKey, redeemScript, witnessFunc,
htlcValue, fee, s.timeoutAddr,
) )
if err != nil { if err != nil {
return 0, err return 0, err
@ -1026,3 +1093,18 @@ func (s *loopInSwap) setState(state loopdb.SwapState) {
s.lastUpdateTime = time.Now() s.lastUpdateTime = time.Now()
s.state = state 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/btcutil"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test" "github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/input"
invpkg "github.com/lightningnetwork/lnd/invoices" invpkg "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -343,6 +341,7 @@ func TestLoopInResume(t *testing.T) {
loopdb.ProtocolVersionUnrecorded, loopdb.ProtocolVersionUnrecorded,
loopdb.ProtocolVersionHtlcV2, loopdb.ProtocolVersionHtlcV2,
loopdb.ProtocolVersionHtlcV3, loopdb.ProtocolVersionHtlcV3,
loopdb.ProtocolVersionMuSig2,
} }
testCases := []struct { testCases := []struct {
@ -414,8 +413,12 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
Preimage: testPreimage, Preimage: testPreimage,
AmountRequested: 100000, AmountRequested: 100000,
CltvExpiry: 744, CltvExpiry: 744,
ReceiverKey: receiverKey, HtlcKeys: loopdb.HtlcKeys{
SenderKey: senderKey, SenderScriptKey: senderKey,
SenderInternalPubKey: senderKey,
ReceiverScriptKey: receiverKey,
ReceiverInternalPubKey: receiverKey,
},
MaxSwapFee: 60000, MaxSwapFee: 60000,
MaxMinerFee: 50000, MaxMinerFee: 50000,
ProtocolVersion: storedVersion, ProtocolVersion: storedVersion,
@ -445,32 +448,10 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
pendSwap.Loop.Events[0].Cost = cost pendSwap.Loop.Events[0].Cost = cost
} }
var ( htlc, err := GetHtlc(
htlc *swap.Htlc testPreimage.Hash(), &contract.SwapContract,
err error 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) require.NoError(t, err)
err = ctx.store.CreateLoopIn(testPreimage.Hash(), contract) err = ctx.store.CreateLoopIn(testPreimage.Hash(), contract)

@ -181,16 +181,20 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
SwapContract: loopdb.SwapContract{ SwapContract: loopdb.SwapContract{
InitiationHeight: currentHeight, InitiationHeight: currentHeight,
InitiationTime: initiationTime, InitiationTime: initiationTime,
SenderKey: swapResp.senderKey, HtlcKeys: loopdb.HtlcKeys{
ReceiverKey: receiverKey, SenderScriptKey: swapResp.senderKey,
ClientKeyLocator: keyDesc.KeyLocator, SenderInternalPubKey: swapResp.senderKey,
Preimage: swapPreimage, ReceiverScriptKey: receiverKey,
AmountRequested: request.Amount, ReceiverInternalPubKey: receiverKey,
CltvExpiry: request.Expiry, ClientScriptKeyLocator: keyDesc.KeyLocator,
MaxMinerFee: request.MaxMinerFee, },
MaxSwapFee: request.MaxSwapFee, Preimage: swapPreimage,
Label: request.Label, AmountRequested: request.Amount,
ProtocolVersion: loopdb.CurrentProtocolVersion(), CltvExpiry: request.Expiry,
MaxMinerFee: request.MaxMinerFee,
MaxSwapFee: request.MaxSwapFee,
Label: request.Label,
ProtocolVersion: loopdb.CurrentProtocolVersion(),
}, },
OutgoingChanSet: chanSet, OutgoingChanSet: chanSet,
} }
@ -1351,11 +1355,28 @@ func (s *loopOutSwap) createMuSig2SweepTxn(
return nil, err return nil, err
} }
signers := [][]byte{ var (
s.SenderKey[1:], s.ReceiverKey[1:], 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 { if !ok {
return nil, fmt.Errorf("non taproot htlc") 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 // Now we're creating a local MuSig2 session using the receiver key's
// key locator and the htlc's root hash. // key locator and the htlc's root hash.
musig2SessionInfo, err := s.lnd.Signer.MuSig2CreateSession( musig2SessionInfo, err := s.lnd.Signer.MuSig2CreateSession(
ctx, input.MuSig2Version040, &s.ClientKeyLocator, ctx, muSig2Verion, &s.HtlcKeys.ClientScriptKeyLocator, signers,
signers, lndclient.MuSig2TaprootTweakOpt(htlc.RootHash[:], false), lndclient.MuSig2TaprootTweakOpt(htlcScript.RootHash[:], false),
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -1428,7 +1449,7 @@ func (s *loopOutSwap) createMuSig2SweepTxn(
// To be sure that we're good, parse and validate that the combined // 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. // signature is indeed valid for the sig hash and the internal pubkey.
err = s.executeConfig.verifySchnorrSig( err = s.executeConfig.verifySchnorrSig(
htlc.TaprootKey, sigHash, finalSig, htlcScript.TaprootKey, sigHash, finalSig,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -1609,8 +1630,8 @@ func (s *loopOutSwap) sweep(ctx context.Context, htlcOutpoint wire.OutPoint,
// Create sweep tx. // Create sweep tx.
sweepTx, err := s.sweeper.CreateSweepTx( sweepTx, err := s.sweeper.CreateSweepTx(
ctx, s.height, s.htlc.SuccessSequence(), s.htlc, ctx, s.height, s.htlc.SuccessSequence(), s.htlc,
htlcOutpoint, s.ReceiverKey, redeemScript, witnessFunc, htlcOutpoint, s.contract.HtlcKeys.ReceiverScriptKey,
htlcValue, fee, s.DestAddr, redeemScript, witnessFunc, htlcValue, fee, s.DestAddr,
) )
if err != nil { if err != nil {
return err 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, 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) { _ *route.Vertex, _ string) (*newLoopInResponse, error) {
_, receiverKey := test.CreateKey(101) _, receiverKey := test.CreateKey(101)
_, receiverInternalKey := test.CreateKey(102)
if amount != s.expectedSwapAmt { if amount != s.expectedSwapAmt {
return nil, errors.New("unexpected test swap amount") return nil, errors.New("unexpected test swap amount")
} }
var receiverKeyArray [33]byte var receiverKeyArray, receiverInternalKeyArray [33]byte
copy(receiverKeyArray[:], receiverKey.SerializeCompressed()) copy(receiverKeyArray[:], receiverKey.SerializeCompressed())
copy(
receiverInternalKeyArray[:],
receiverInternalKey.SerializeCompressed(),
)
s.swapInvoice = swapInvoice s.swapInvoice = swapInvoice
s.swapHash = swapHash s.swapHash = swapHash
@ -175,8 +180,9 @@ func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
<-s.lnd.FailInvoiceChannel <-s.lnd.FailInvoiceChannel
resp := &newLoopInResponse{ resp := &newLoopInResponse{
expiry: s.height + testChargeOnChainCltvDelta, expiry: s.height + testChargeOnChainCltvDelta,
receiverKey: receiverKeyArray, receiverKey: receiverKeyArray,
receiverInternalKey: receiverInternalKeyArray,
} }
return resp, nil return resp, nil
@ -261,3 +267,9 @@ func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion
return nil, nil, nil return nil, nil, nil
} }
func (s *serverMock) PushKey(_ context.Context, _ loopdb.ProtocolVersion,
_ lntypes.Hash, _ [32]byte) error {
return nil
}

@ -76,18 +76,27 @@ func GetHtlc(hash lntypes.Hash, contract *loopdb.SwapContract,
switch GetHtlcScriptVersion(contract.ProtocolVersion) { switch GetHtlcScriptVersion(contract.ProtocolVersion) {
case swap.HtlcV2: case swap.HtlcV2:
return swap.NewHtlcV2( return swap.NewHtlcV2(
contract.CltvExpiry, contract.SenderKey, contract.CltvExpiry, contract.HtlcKeys.SenderScriptKey,
contract.ReceiverKey, hash, contract.HtlcKeys.ReceiverScriptKey, hash,
chainParams, chainParams,
) )
case swap.HtlcV3: 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( return swap.NewHtlcV3(
input.MuSig2Version040, muSig2Version,
contract.CltvExpiry, contract.SenderKey, contract.CltvExpiry,
contract.ReceiverKey, contract.SenderKey, contract.HtlcKeys.SenderInternalPubKey,
contract.ReceiverKey, hash, contract.HtlcKeys.ReceiverInternalPubKey,
chainParams, contract.HtlcKeys.SenderScriptKey,
contract.HtlcKeys.ReceiverScriptKey,
hash, chainParams,
) )
} }

@ -94,10 +94,10 @@ type swapServerClient interface {
preimage lntypes.Preimage) error preimage lntypes.Preimage) error
NewLoopInSwap(ctx context.Context, NewLoopInSwap(ctx context.Context,
swapHash lntypes.Hash, amount btcutil.Amount, swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey,
senderKey [33]byte, swapInvoice, probeInvoice string, senderInternalKey [33]byte, swapInvoice, probeInvoice string,
lastHop *route.Vertex, initiator string) (*newLoopInResponse, lastHop *route.Vertex, initiator string) (
error) *newLoopInResponse, error)
// SubscribeLoopOutUpdates subscribes to loop out server state. // SubscribeLoopOutUpdates subscribes to loop out server state.
SubscribeLoopOutUpdates(ctx context.Context, SubscribeLoopOutUpdates(ctx context.Context,
@ -128,6 +128,12 @@ type swapServerClient interface {
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash, protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) ( paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) (
[]byte, []byte, error) []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 { type grpcSwapServerClient struct {
@ -419,9 +425,9 @@ func (s *grpcSwapServerClient) PushLoopOutPreimage(ctx context.Context,
} }
func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context, func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
swapHash lntypes.Hash, amount btcutil.Amount, senderKey [33]byte, swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey,
swapInvoice, probeInvoice string, lastHop *route.Vertex, senderInternalKey [33]byte, swapInvoice, probeInvoice string,
initiator string) (*newLoopInResponse, error) { lastHop *route.Vertex, initiator string) (*newLoopInResponse, error) {
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout) rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
defer rpcCancel() defer rpcCancel()
@ -429,7 +435,7 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
req := &looprpc.ServerLoopInRequest{ req := &looprpc.ServerLoopInRequest{
SwapHash: swapHash[:], SwapHash: swapHash[:],
Amt: uint64(amount), Amt: uint64(amount),
SenderKey: senderKey[:], SenderKey: senderScriptKey[:],
SwapInvoice: swapInvoice, SwapInvoice: swapInvoice,
ProtocolVersion: loopdb.CurrentRPCProtocolVersion(), ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
ProbeInvoice: probeInvoice, ProbeInvoice: probeInvoice,
@ -439,13 +445,19 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
req.LastHop = lastHop[:] 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) swapResp, err := s.server.NewLoopInSwap(rpcCtx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var receiverKey [33]byte var receiverKey, receiverInternalKey [33]byte
copy(receiverKey[:], swapResp.ReceiverKey) copy(receiverKey[:], swapResp.ReceiverKey)
copy(receiverInternalKey[:], swapResp.ReceiverInternalPubkey)
// Validate receiver key. // Validate receiver key.
_, err = btcec.ParsePubKey(receiverKey[:]) _, err = btcec.ParsePubKey(receiverKey[:])
@ -454,9 +466,10 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
} }
return &newLoopInResponse{ return &newLoopInResponse{
receiverKey: receiverKey, receiverKey: receiverKey,
expiry: swapResp.Expiry, receiverInternalKey: receiverInternalKey,
serverMessage: swapResp.ServerMessage, expiry: swapResp.Expiry,
serverMessage: swapResp.ServerMessage,
}, nil }, nil
} }
@ -753,6 +766,25 @@ func (s *grpcSwapServerClient) MuSig2SignSweep(ctx context.Context,
return res.Nonce, res.PartialSignature, nil 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) ( func rpcRouteCancel(details *outCancelDetails) (
*looprpc.CancelLoopOutSwapRequest_RouteCancel, error) { *looprpc.CancelLoopOutSwapRequest_RouteCancel, error) {
@ -873,7 +905,8 @@ type newLoopOutResponse struct {
} }
type newLoopInResponse struct { type newLoopInResponse struct {
receiverKey [33]byte receiverKey [33]byte
expiry int32 receiverInternalKey [33]byte
serverMessage string expiry int32
serverMessage string
} }

File diff suppressed because it is too large Load Diff

@ -45,6 +45,8 @@ service SwapServer {
returns (ReportRoutingResultRes); returns (ReportRoutingResultRes);
rpc MuSig2SignSweep (MuSig2SignSweepReq) returns (MuSig2SignSweepRes); 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. // The client will use the new v3 (taproot) HTLC scripts.
HTLC_V3 = 10; HTLC_V3 = 10;
// Enables MuSig2 signature scheme for swaps.
MUSIG2 = 11;
} }
message ServerLoopOutRequest { message ServerLoopOutRequest {
@ -196,14 +201,21 @@ message ServerLoopOutTerms {
message ServerLoopInRequest { message ServerLoopInRequest {
bytes sender_key = 1; bytes sender_key = 1;
bytes sender_internal_pubkey = 9;
bytes swap_hash = 2; bytes swap_hash = 2;
uint64 amt = 3; uint64 amt = 3;
string swap_invoice = 4; string swap_invoice = 4;
bytes last_hop = 5; bytes last_hop = 5;
/// The protocol version that the client adheres to. /// The protocol version that the client adheres to.
ProtocolVersion protocol_version = 6; ProtocolVersion protocol_version = 6;
// An invoice that can be used for the purpose of probing.
string probe_invoice = 7; string probe_invoice = 7;
// The user agent string that identifies the software running on the user's // The user agent string that identifies the software running on the user's
@ -218,6 +230,9 @@ message ServerLoopInRequest {
message ServerLoopInResponse { message ServerLoopInResponse {
bytes receiver_key = 1; bytes receiver_key = 1;
bytes receiver_internal_pubkey = 9;
int32 expiry = 2; int32 expiry = 2;
// A human-readable message from the loop server. // A human-readable message from the loop server.
@ -458,7 +473,7 @@ message CancelLoopOutSwapResponse {
} }
message ServerProbeRequest { message ServerProbeRequest {
/// The protocol version that the client adheres to. // The protocol version that the client adheres to.
ProtocolVersion protocol_version = 1; ProtocolVersion protocol_version = 1;
// The probe amount. // The probe amount.
@ -470,9 +485,7 @@ message ServerProbeRequest {
// Optional last hop to use when probing the client. // Optional last hop to use when probing the client.
bytes last_hop = 4; 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; repeated RouteHint route_hints = 5;
} }
@ -555,3 +568,17 @@ message MuSig2SignSweepRes {
// The partial signature of the server for the requested sighash. // The partial signature of the server for the requested sighash.
bytes partial_signature = 2; 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 clients private key used in the HTLCs 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) RecommendRoutingPlugin(ctx context.Context, in *RecommendRoutingPluginReq, opts ...grpc.CallOption) (*RecommendRoutingPluginRes, error)
ReportRoutingResult(ctx context.Context, in *ReportRoutingResultReq, opts ...grpc.CallOption) (*ReportRoutingResultRes, error) ReportRoutingResult(ctx context.Context, in *ReportRoutingResultReq, opts ...grpc.CallOption) (*ReportRoutingResultRes, error)
MuSig2SignSweep(ctx context.Context, in *MuSig2SignSweepReq, opts ...grpc.CallOption) (*MuSig2SignSweepRes, 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 { type swapServerClient struct {
@ -214,6 +215,15 @@ func (c *swapServerClient) MuSig2SignSweep(ctx context.Context, in *MuSig2SignSw
return out, nil 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. // SwapServerServer is the server API for SwapServer service.
// All implementations must embed UnimplementedSwapServerServer // All implementations must embed UnimplementedSwapServerServer
// for forward compatibility // for forward compatibility
@ -232,6 +242,7 @@ type SwapServerServer interface {
RecommendRoutingPlugin(context.Context, *RecommendRoutingPluginReq) (*RecommendRoutingPluginRes, error) RecommendRoutingPlugin(context.Context, *RecommendRoutingPluginReq) (*RecommendRoutingPluginRes, error)
ReportRoutingResult(context.Context, *ReportRoutingResultReq) (*ReportRoutingResultRes, error) ReportRoutingResult(context.Context, *ReportRoutingResultReq) (*ReportRoutingResultRes, error)
MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error) MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error)
PushKey(context.Context, *ServerPushKeyReq) (*ServerPushKeyRes, error)
mustEmbedUnimplementedSwapServerServer() mustEmbedUnimplementedSwapServerServer()
} }
@ -281,6 +292,9 @@ func (UnimplementedSwapServerServer) ReportRoutingResult(context.Context, *Repor
func (UnimplementedSwapServerServer) MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error) { func (UnimplementedSwapServerServer) MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method MuSig2SignSweep not implemented") 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() {} func (UnimplementedSwapServerServer) mustEmbedUnimplementedSwapServerServer() {}
// UnsafeSwapServerServer may be embedded to opt out of forward compatibility for this service. // 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) 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. // SwapServer_ServiceDesc is the grpc.ServiceDesc for SwapServer service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -607,6 +639,10 @@ var SwapServer_ServiceDesc = grpc.ServiceDesc{
MethodName: "MuSig2SignSweep", MethodName: "MuSig2SignSweep",
Handler: _SwapServer_MuSig2SignSweep_Handler, Handler: _SwapServer_MuSig2SignSweep_Handler,
}, },
{
MethodName: "PushKey",
Handler: _SwapServer_PushKey_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {

Loading…
Cancel
Save