Merge pull request #497 from bhandras/taproot-musig2

multi: upgrade to using P2TR htlcs and added support for MuSig2 loopout sweep
pull/508/head
András Bánki-Horváth 2 years ago committed by GitHub
commit 11ab596080
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,6 +9,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/aperture/lsat" "github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
@ -158,6 +160,18 @@ func NewClient(dbDir string, cfg *ClientConfig) (*Client, func(), error) {
totalPaymentTimeout: cfg.TotalPaymentTimeout, totalPaymentTimeout: cfg.TotalPaymentTimeout,
maxPaymentRetries: cfg.MaxPaymentRetries, maxPaymentRetries: cfg.MaxPaymentRetries,
cancelSwap: swapServerClient.CancelLoopOutSwap, cancelSwap: swapServerClient.CancelLoopOutSwap,
verifySchnorrSig: func(pubKey *btcec.PublicKey, hash, sig []byte) error {
schnorrSig, err := schnorr.ParseSignature(sig)
if err != nil {
return err
}
if !schnorrSig.Verify(hash, pubKey) {
return fmt.Errorf("invalid signature")
}
return nil
},
}) })
client := &Client{ client := &Client{
@ -192,56 +206,92 @@ func (s *Client) FetchSwaps() ([]*SwapInfo, error) {
swaps := make([]*SwapInfo, 0, len(loopInSwaps)+len(loopOutSwaps)) swaps := make([]*SwapInfo, 0, len(loopInSwaps)+len(loopOutSwaps))
for _, swp := range loopOutSwaps { for _, swp := range loopOutSwaps {
swapInfo := &SwapInfo{
SwapType: swap.TypeOut,
SwapContract: swp.Contract.SwapContract,
SwapStateData: swp.State(),
SwapHash: swp.Hash,
LastUpdate: swp.LastUpdateTime(),
}
scriptVersion := GetHtlcScriptVersion(
swp.Contract.ProtocolVersion,
)
outputType := swap.HtlcP2WSH
if scriptVersion == swap.HtlcV3 {
outputType = swap.HtlcP2TR
}
htlc, err := swap.NewHtlc( htlc, err := swap.NewHtlc(
GetHtlcScriptVersion(swp.Contract.ProtocolVersion), scriptVersion,
swp.Contract.CltvExpiry, swp.Contract.SenderKey, swp.Contract.CltvExpiry, swp.Contract.SenderKey,
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcP2WSH, swp.Contract.ReceiverKey, swp.Hash,
s.lndServices.ChainParams, outputType, s.lndServices.ChainParams,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
swaps = append(swaps, &SwapInfo{ if outputType == swap.HtlcP2TR {
SwapType: swap.TypeOut, swapInfo.HtlcAddressP2TR = htlc.Address
SwapContract: swp.Contract.SwapContract, } else {
SwapStateData: swp.State(), swapInfo.HtlcAddressP2WSH = htlc.Address
SwapHash: swp.Hash, }
LastUpdate: swp.LastUpdateTime(),
HtlcAddressP2WSH: htlc.Address, swaps = append(swaps, swapInfo)
})
} }
for _, swp := range loopInSwaps { for _, swp := range loopInSwaps {
htlcNP2WSH, err := swap.NewHtlc( swapInfo := &SwapInfo{
GetHtlcScriptVersion(swp.Contract.ProtocolVersion), SwapType: swap.TypeIn,
swp.Contract.CltvExpiry, swp.Contract.SenderKey, SwapContract: swp.Contract.SwapContract,
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcNP2WSH, SwapStateData: swp.State(),
s.lndServices.ChainParams, SwapHash: swp.Hash,
) LastUpdate: swp.LastUpdateTime(),
if err != nil {
return nil, err
} }
htlcP2WSH, err := swap.NewHtlc( scriptVersion := GetHtlcScriptVersion(
GetHtlcScriptVersion(swp.Contract.ProtocolVersion), swp.Contract.SwapContract.ProtocolVersion,
swp.Contract.CltvExpiry, swp.Contract.SenderKey,
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcP2WSH,
s.lndServices.ChainParams,
) )
if err != nil {
return nil, err if scriptVersion == swap.HtlcV3 {
htlcP2TR, err := swap.NewHtlc(
swap.HtlcV3, swp.Contract.CltvExpiry,
swp.Contract.SenderKey, swp.Contract.ReceiverKey,
swp.Hash, swap.HtlcP2TR,
s.lndServices.ChainParams,
)
if err != nil {
return nil, err
}
swapInfo.HtlcAddressP2TR = htlcP2TR.Address
} else {
htlcNP2WSH, err := swap.NewHtlc(
swap.HtlcV1, swp.Contract.CltvExpiry,
swp.Contract.SenderKey, swp.Contract.ReceiverKey,
swp.Hash, swap.HtlcNP2WSH,
s.lndServices.ChainParams,
)
if err != nil {
return nil, err
}
htlcP2WSH, err := swap.NewHtlc(
swap.HtlcV2, swp.Contract.CltvExpiry,
swp.Contract.SenderKey, swp.Contract.ReceiverKey,
swp.Hash, swap.HtlcP2WSH,
s.lndServices.ChainParams,
)
if err != nil {
return nil, err
}
swapInfo.HtlcAddressP2WSH = htlcP2WSH.Address
swapInfo.HtlcAddressNP2WSH = htlcNP2WSH.Address
} }
swaps = append(swaps, &SwapInfo{ swaps = append(swaps, swapInfo)
SwapType: swap.TypeIn,
SwapContract: swp.Contract.SwapContract,
SwapStateData: swp.State(),
SwapHash: swp.Hash,
LastUpdate: swp.LastUpdateTime(),
HtlcAddressP2WSH: htlcP2WSH.Address,
HtlcAddressNP2WSH: htlcNP2WSH.Address,
})
} }
return swaps, nil return swaps, nil
@ -405,9 +455,9 @@ func (s *Client) LoopOut(globalCtx context.Context,
// Return hash so that the caller can identify this swap in the updates // Return hash so that the caller can identify this swap in the updates
// stream. // stream.
return &LoopOutSwapInfo{ return &LoopOutSwapInfo{
SwapHash: swap.hash, SwapHash: swap.hash,
HtlcAddressP2WSH: swap.htlc.Address, HtlcAddress: swap.htlc.Address,
ServerMessage: initResult.serverMessage, ServerMessage: initResult.serverMessage,
}, nil }, nil
} }
@ -463,7 +513,23 @@ func (s *Client) LoopOutQuote(ctx context.Context,
log.Infof("Offchain swap destination: %x", quote.SwapPaymentDest) log.Infof("Offchain swap destination: %x", quote.SwapPaymentDest)
swapFee := quote.SwapFee minerFee, err := s.getLoopOutSweepFee(ctx, request.SweepConfTarget)
if err != nil {
return nil, err
}
return &LoopOutQuote{
SwapFee: quote.SwapFee,
MinerFee: minerFee,
PrepayAmount: quote.PrepayAmount,
SwapPaymentDest: quote.SwapPaymentDest,
}, nil
}
// getLoopOutSweepFee is a helper method to estimate the loop out htlc sweep
// fee to a p2wsh address.
func (s *Client) getLoopOutSweepFee(ctx context.Context, confTarget int32) (
btcutil.Amount, error) {
// Generate dummy p2wsh address for fee estimation. The p2wsh address // Generate dummy p2wsh address for fee estimation. The p2wsh address
// type is chosen because it adds the most weight of all output types // type is chosen because it adds the most weight of all output types
@ -473,23 +539,21 @@ func (s *Client) LoopOutQuote(ctx context.Context,
wsh[:], s.lndServices.ChainParams, wsh[:], s.lndServices.ChainParams,
) )
if err != nil { if err != nil {
return nil, err return 0, err
} }
minerFee, err := s.sweeper.GetSweepFee( scriptVersion := GetHtlcScriptVersion(
ctx, swap.QuoteHtlc.AddSuccessToEstimator, loopdb.CurrentProtocolVersion(),
p2wshAddress, request.SweepConfTarget,
) )
if err != nil {
return nil, err htlc := swap.QuoteHtlcP2TR
if scriptVersion != swap.HtlcV3 {
htlc = swap.QuoteHtlcP2WSH
} }
return &LoopOutQuote{ return s.sweeper.GetSweepFee(
SwapFee: swapFee, ctx, htlc.AddSuccessToEstimator, p2wshAddress, confTarget,
MinerFee: minerFee, )
PrepayAmount: quote.PrepayAmount,
SwapPaymentDest: quote.SwapPaymentDest,
}, nil
} }
// LoopOutTerms returns the terms on which the server executes swaps. // LoopOutTerms returns the terms on which the server executes swaps.
@ -546,11 +610,17 @@ func (s *Client) LoopIn(globalCtx context.Context,
// Return hash so that the caller can identify this swap in the updates // Return hash so that the caller can identify this swap in the updates
// stream. // stream.
swapInfo := &LoopInSwapInfo{ swapInfo := &LoopInSwapInfo{
SwapHash: swap.hash, SwapHash: swap.hash,
HtlcAddressP2WSH: swap.htlcP2WSH.Address, ServerMessage: initResult.serverMessage,
HtlcAddressNP2WSH: swap.htlcNP2WSH.Address,
ServerMessage: initResult.serverMessage,
} }
if loopdb.CurrentProtocolVersion() < loopdb.ProtocolVersionHtlcV3 {
swapInfo.HtlcAddressNP2WSH = swap.htlcNP2WSH.Address
swapInfo.HtlcAddressP2WSH = swap.htlcP2WSH.Address
} else {
swapInfo.HtlcAddressP2TR = swap.htlcP2TR.Address
}
return swapInfo, nil return swapInfo, nil
} }
@ -626,7 +696,7 @@ func (s *Client) LoopInQuote(ctx context.Context,
// //
// TODO(guggero): Thread through error code from lnd to avoid string // TODO(guggero): Thread through error code from lnd to avoid string
// matching. // matching.
minerFee, err := s.lndServices.Client.EstimateFeeToP2WSH( minerFee, err := s.estimateFee(
ctx, request.Amount, request.HtlcConfTarget, ctx, request.Amount, request.HtlcConfTarget,
) )
if err != nil && strings.Contains(err.Error(), "insufficient funds") { if err != nil && strings.Contains(err.Error(), "insufficient funds") {
@ -647,6 +717,39 @@ func (s *Client) LoopInQuote(ctx context.Context,
}, nil }, nil
} }
// estimateFee is a helper method to estimate the total fee for paying the
// passed amount with the given conf target. It'll assume taproot destination
// if the protocol version indicates that we're using taproot htlcs.
func (s *Client) estimateFee(ctx context.Context, amt btcutil.Amount,
confTarget int32) (btcutil.Amount, error) {
var (
address btcutil.Address
err error
)
// Generate a dummy address for fee estimation.
witnessProg := [32]byte{}
scriptVersion := GetHtlcScriptVersion(
loopdb.CurrentProtocolVersion(),
)
if scriptVersion != swap.HtlcV3 {
address, err = btcutil.NewAddressWitnessScriptHash(
witnessProg[:], s.lndServices.ChainParams,
)
} else {
address, err = btcutil.NewAddressTaproot(
witnessProg[:], s.lndServices.ChainParams,
)
}
if err != nil {
return 0, err
}
return s.lndServices.Client.EstimateFee(ctx, address, amt, confTarget)
}
// LoopInTerms returns the terms on which the server executes swaps. // LoopInTerms returns the terms on which the server executes swaps.
func (s *Client) LoopInTerms(ctx context.Context) ( func (s *Client) LoopInTerms(ctx context.Context) (
*LoopInTerms, error) { *LoopInTerms, error) {

@ -45,9 +45,9 @@ var (
defaultConfirmations = int32(loopdb.DefaultLoopOutHtlcConfirmations) defaultConfirmations = int32(loopdb.DefaultLoopOutHtlcConfirmations)
) )
// TestSuccess tests the loop out happy flow, using a custom htlc confirmation // TestLoopOutSuccess tests the loop out happy flow, using a custom htlc
// target. // confirmation target.
func TestSuccess(t *testing.T) { func TestLoopOutSuccess(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
ctx := createClientTestContext(t, nil) ctx := createClientTestContext(t, nil)
@ -70,15 +70,15 @@ func TestSuccess(t *testing.T) {
// Expect client to register for conf. // Expect client to register for conf.
confIntent := ctx.AssertRegisterConf(false, req.HtlcConfirmations) confIntent := ctx.AssertRegisterConf(false, req.HtlcConfirmations)
testSuccess(ctx, testRequest.Amount, info.SwapHash, testLoopOutSuccess(ctx, testRequest.Amount, info.SwapHash,
signalPrepaymentResult, signalSwapPaymentResult, false, signalPrepaymentResult, signalSwapPaymentResult, false,
confIntent, swap.HtlcV2, confIntent, swap.HtlcV2,
) )
} }
// TestFailOffchain tests the handling of swap for which the server failed the // TestLoopOutFailOffchain tests the handling of swap for which the server
// payments. // failed the payments.
func TestFailOffchain(t *testing.T) { func TestLoopOutFailOffchain(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
ctx := createClientTestContext(t, nil) ctx := createClientTestContext(t, nil)
@ -110,8 +110,9 @@ func TestFailOffchain(t *testing.T) {
ctx.finish() ctx.finish()
} }
// TestWrongAmount asserts that the client checks the server invoice amounts. // TestLoopOutWrongAmount asserts that the client checks the server invoice
func TestFailWrongAmount(t *testing.T) { // amounts.
func TestLoopOutFailWrongAmount(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
test := func(t *testing.T, modifier func(*serverMock), test := func(t *testing.T, modifier func(*serverMock),
@ -148,9 +149,9 @@ func TestFailWrongAmount(t *testing.T) {
} }
// TestResume tests that swaps in various states are properly resumed after a // TestLoopOutResume tests that swaps in various states are properly resumed
// restart. // after a restart.
func TestResume(t *testing.T) { func TestLoopOutResume(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
defaultConfs := loopdb.DefaultLoopOutHtlcConfirmations defaultConfs := loopdb.DefaultLoopOutHtlcConfirmations
@ -158,6 +159,7 @@ func TestResume(t *testing.T) {
storedVersion := []loopdb.ProtocolVersion{ storedVersion := []loopdb.ProtocolVersion{
loopdb.ProtocolVersionUnrecorded, loopdb.ProtocolVersionUnrecorded,
loopdb.ProtocolVersionHtlcV2, loopdb.ProtocolVersionHtlcV2,
loopdb.ProtocolVersionHtlcV3,
} }
for _, version := range storedVersion { for _, version := range storedVersion {
@ -165,26 +167,26 @@ func TestResume(t *testing.T) {
t.Run(version.String(), func(t *testing.T) { t.Run(version.String(), func(t *testing.T) {
t.Run("not expired", func(t *testing.T) { t.Run("not expired", func(t *testing.T) {
testResume( testLoopOutResume(
t, defaultConfs, false, false, true, t, defaultConfs, false, false, true,
version, version,
) )
}) })
t.Run("not expired, custom confirmations", t.Run("not expired, custom confirmations",
func(t *testing.T) { func(t *testing.T) {
testResume( testLoopOutResume(
t, 3, false, false, true, t, 3, false, false, true,
version, version,
) )
}) })
t.Run("expired not revealed", func(t *testing.T) { t.Run("expired not revealed", func(t *testing.T) {
testResume( testLoopOutResume(
t, defaultConfs, true, false, false, t, defaultConfs, true, false, false,
version, version,
) )
}) })
t.Run("expired revealed", func(t *testing.T) { t.Run("expired revealed", func(t *testing.T) {
testResume( testLoopOutResume(
t, defaultConfs, true, true, true, t, defaultConfs, true, true, true,
version, version,
) )
@ -193,7 +195,7 @@ func TestResume(t *testing.T) {
} }
} }
func testResume(t *testing.T, confs uint32, expired, preimageRevealed, func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed,
expectSuccess bool, protocolVersion loopdb.ProtocolVersion) { expectSuccess bool, protocolVersion loopdb.ProtocolVersion) {
defer test.Guard(t)() defer test.Guard(t)()
@ -282,9 +284,15 @@ func testResume(t *testing.T, confs uint32, expired, preimageRevealed,
// Assert that the loopout htlc equals to the expected one. // Assert that the loopout htlc equals to the expected one.
scriptVersion := GetHtlcScriptVersion(protocolVersion) scriptVersion := GetHtlcScriptVersion(protocolVersion)
outputType := swap.HtlcP2TR
if scriptVersion != swap.HtlcV3 {
outputType = swap.HtlcP2WSH
}
htlc, err := swap.NewHtlc( htlc, err := swap.NewHtlc(
scriptVersion, pendingSwap.Contract.CltvExpiry, senderKey, scriptVersion, pendingSwap.Contract.CltvExpiry, senderKey,
receiverKey, hash, swap.HtlcP2WSH, &chaincfg.TestNet3Params, receiverKey, hash, outputType, &chaincfg.TestNet3Params,
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, htlc.PkScript, confIntent.PkScript) require.Equal(t, htlc.PkScript, confIntent.PkScript)
@ -301,8 +309,7 @@ func testResume(t *testing.T, confs uint32, expired, preimageRevealed,
// Because there is no reliable payment yet, an invoice is assumed to be // Because there is no reliable payment yet, an invoice is assumed to be
// paid after resume. // paid after resume.
testLoopOutSuccess(ctx, amt, hash,
testSuccess(ctx, amt, hash,
func(r error) {}, func(r error) {},
func(r error) {}, func(r error) {},
preimageRevealed, preimageRevealed,
@ -310,7 +317,7 @@ func testResume(t *testing.T, confs uint32, expired, preimageRevealed,
) )
} }
func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash, func testLoopOutSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
signalPrepaymentResult, signalSwapPaymentResult func(error), signalPrepaymentResult, signalSwapPaymentResult func(error),
preimageRevealed bool, confIntent *test.ConfRegistration, preimageRevealed bool, confIntent *test.ConfRegistration,
scriptVersion swap.ScriptVersion) { scriptVersion swap.ScriptVersion) {
@ -328,14 +335,31 @@ func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
// Publish tick. // Publish tick.
ctx.expiryChan <- testTime ctx.expiryChan <- testTime
// Expect a signing request. // Expect a signing request in the non taproot case.
<-ctx.Lnd.SignOutputRawChannel if scriptVersion != swap.HtlcV3 {
<-ctx.Lnd.SignOutputRawChannel
}
if !preimageRevealed { if !preimageRevealed {
ctx.assertStatus(loopdb.StatePreimageRevealed) ctx.assertStatus(loopdb.StatePreimageRevealed)
ctx.assertStorePreimageReveal() ctx.assertStorePreimageReveal()
} }
// When using taproot htlcs the flow is different as we do reveal the
// preimage before sweeping in order for the server to trust us with
// our MuSig2 signing attempts.
if scriptVersion == swap.HtlcV3 {
ctx.assertPreimagePush(testPreimage)
// Try MuSig2 signing first and fail it so that we go for a
// normal sweep.
for i := 0; i < maxMusigSweepRetries; i++ {
ctx.expiryChan <- testTime
ctx.assertPreimagePush(testPreimage)
}
<-ctx.Lnd.SignOutputRawChannel
}
// Expect client on-chain sweep of HTLC. // Expect client on-chain sweep of HTLC.
sweepTx := ctx.ReceiveTx() sweepTx := ctx.ReceiveTx()
@ -344,8 +368,15 @@ func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
ctx.T.Fatalf("client not sweeping from htlc tx") ctx.T.Fatalf("client not sweeping from htlc tx")
} }
preImageIndex := 1 var preImageIndex int
if scriptVersion == swap.HtlcV2 { switch scriptVersion {
case swap.HtlcV1:
preImageIndex = 1
case swap.HtlcV2:
preImageIndex = 0
case swap.HtlcV3:
preImageIndex = 0 preImageIndex = 0
} }
@ -361,10 +392,11 @@ func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
preimage, err := lntypes.MakePreimage(clientPreImage) preimage, err := lntypes.MakePreimage(clientPreImage)
require.NoError(ctx.T, err) require.NoError(ctx.T, err)
ctx.assertPreimagePush(preimage) if scriptVersion != swap.HtlcV3 {
ctx.assertPreimagePush(preimage)
// Simulate server pulling payment. // Simulate server pulling payment.
signalSwapPaymentResult(nil) signalSwapPaymentResult(nil)
}
ctx.NotifySpend(sweepTx, 0) ctx.NotifySpend(sweepTx, 0)

@ -205,13 +205,20 @@ func loopIn(ctx *cli.Context) error {
fmt.Printf("Swap initiated\n") fmt.Printf("Swap initiated\n")
fmt.Printf("ID: %v\n", resp.Id) fmt.Printf("ID: %v\n", resp.Id)
if external { if resp.HtlcAddressP2Tr != "" {
fmt.Printf("HTLC address (NP2WSH): %v\n", resp.HtlcAddressNp2Wsh) fmt.Printf("HTLC address (P2TR): %v\n", resp.HtlcAddressP2Tr)
} else {
if external {
fmt.Printf("HTLC address (NP2WSH): %v\n",
resp.HtlcAddressNp2Wsh)
}
fmt.Printf("HTLC address (P2WSH): %v\n", resp.HtlcAddressP2Wsh)
} }
fmt.Printf("HTLC address (P2WSH): %v\n", resp.HtlcAddressP2Wsh)
if resp.ServerMessage != "" { if resp.ServerMessage != "" {
fmt.Printf("Server message: %v\n", resp.ServerMessage) fmt.Printf("Server message: %v\n", resp.ServerMessage)
} }
fmt.Println() fmt.Println()
fmt.Printf("Run `loop monitor` to monitor progress.\n") fmt.Printf("Run `loop monitor` to monitor progress.\n")

@ -345,6 +345,7 @@ func logSwap(swap *looprpc.SwapStatus) {
fmt.Printf("%v %v %v %v -", fmt.Printf("%v %v %v %v -",
time.Unix(0, swap.LastUpdateTime).Format(time.RFC3339), time.Unix(0, swap.LastUpdateTime).Format(time.RFC3339),
swap.Type, swapState, btcutil.Amount(swap.Amt)) swap.Type, swapState, btcutil.Amount(swap.Amt))
if swap.HtlcAddressP2Wsh != "" { if swap.HtlcAddressP2Wsh != "" {
fmt.Printf(" P2WSH: %v", swap.HtlcAddressP2Wsh) fmt.Printf(" P2WSH: %v", swap.HtlcAddressP2Wsh)
} }
@ -352,6 +353,10 @@ func logSwap(swap *looprpc.SwapStatus) {
if swap.HtlcAddressNp2Wsh != "" { if swap.HtlcAddressNp2Wsh != "" {
fmt.Printf(" NP2WSH: %v", swap.HtlcAddressNp2Wsh) fmt.Printf(" NP2WSH: %v", swap.HtlcAddressNp2Wsh)
} }
if swap.HtlcAddressP2Tr != "" {
fmt.Printf(" P2TR: %v", swap.HtlcAddressP2Tr)
}
} }
if swap.State != looprpc.SwapState_INITIATED && if swap.State != looprpc.SwapState_INITIATED &&

@ -8,6 +8,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/sweep" "github.com/lightninglabs/loop/sweep"
@ -31,6 +32,8 @@ type executorConfig struct {
maxPaymentRetries int maxPaymentRetries int
cancelSwap func(ctx context.Context, details *outCancelDetails) error cancelSwap func(ctx context.Context, details *outCancelDetails) error
verifySchnorrSig func(pubKey *btcec.PublicKey, hash, sig []byte) error
} }
// executor is responsible for executing swaps. // executor is responsible for executing swaps.
@ -153,6 +156,7 @@ func (s *executor) run(mainCtx context.Context,
totalPaymentTimout: s.executorConfig.totalPaymentTimeout, totalPaymentTimout: s.executorConfig.totalPaymentTimeout,
maxPaymentRetries: s.executorConfig.maxPaymentRetries, maxPaymentRetries: s.executorConfig.maxPaymentRetries,
cancelSwap: s.executorConfig.cancelSwap, cancelSwap: s.executorConfig.cancelSwap,
verifySchnorrSig: s.executorConfig.verifySchnorrSig,
}, height) }, height)
if err != nil && err != context.Canceled { if err != nil && err != context.Canceled {
log.Errorf("Execute error: %v", err) log.Errorf("Execute error: %v", err)

@ -1,7 +1,7 @@
module github.com/lightninglabs/loop module github.com/lightninglabs/loop
require ( require (
github.com/btcsuite/btcd v0.22.0-beta.0.20220413172512-bf64c8bdbbbf github.com/btcsuite/btcd v0.23.1
github.com/btcsuite/btcd/btcec/v2 v2.2.0 github.com/btcsuite/btcd/btcec/v2 v2.2.0
github.com/btcsuite/btcd/btcutil v1.1.1 github.com/btcsuite/btcd/btcutil v1.1.1
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
@ -32,4 +32,7 @@ require (
gopkg.in/macaroon.v2 v2.1.0 gopkg.in/macaroon.v2 v2.1.0
) )
// TODO(bhandras): remove when the next swapserverrpc is tagged.
replace github.com/lightninglabs/loop/swapserverrpc => ./swapserverrpc
go 1.16 go 1.16

@ -75,8 +75,9 @@ github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tj
github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g=
github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08=
github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923/go.mod h1:taIcYprAW2g6Z9S0gGUxyR+zDwimyDMK5ePOX+iJ2ds= github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923/go.mod h1:taIcYprAW2g6Z9S0gGUxyR+zDwimyDMK5ePOX+iJ2ds=
github.com/btcsuite/btcd v0.22.0-beta.0.20220413172512-bf64c8bdbbbf h1:8lTakMpAAOKTe60bNhiEjUDtcZAlrst2MF49ekuA9yI=
github.com/btcsuite/btcd v0.22.0-beta.0.20220413172512-bf64c8bdbbbf/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.22.0-beta.0.20220413172512-bf64c8bdbbbf/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
github.com/btcsuite/btcd v0.23.1 h1:IB8cVQcC2X5mHbnfirLG5IZnkWYNTPlLZVrxUYSotbE=
github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
@ -484,8 +485,6 @@ github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:ant
github.com/lightninglabs/lndclient v0.15.0-0/go.mod h1:ORS/YFe9hAXlzN/Uj+gvTmrnXEml6yD6dWwzCjpTJyQ= github.com/lightninglabs/lndclient v0.15.0-0/go.mod h1:ORS/YFe9hAXlzN/Uj+gvTmrnXEml6yD6dWwzCjpTJyQ=
github.com/lightninglabs/lndclient v0.15.0-6 h1:wFNmHtZSV54FzPZsPOX3/rwRIRUlLJLvWB760hzPCag= github.com/lightninglabs/lndclient v0.15.0-6 h1:wFNmHtZSV54FzPZsPOX3/rwRIRUlLJLvWB760hzPCag=
github.com/lightninglabs/lndclient v0.15.0-6/go.mod h1:bQrzrVGpBuaM2c6mapsAdbnhsXJydOU6gFAKvevEzgo= github.com/lightninglabs/lndclient v0.15.0-6/go.mod h1:bQrzrVGpBuaM2c6mapsAdbnhsXJydOU6gFAKvevEzgo=
github.com/lightninglabs/loop/swapserverrpc v1.0.1 h1:z99vrpRgQ834FC/C0Qx8HLtE0RX0WBDa1gAu7yOGPsg=
github.com/lightninglabs/loop/swapserverrpc v1.0.1/go.mod h1:imy1/sqnb70EEyBKMo4pHwwLBPW8uYahWZ8s+1Xcq1o=
github.com/lightninglabs/neutrino v0.13.2/go.mod h1:Cv/v8oHiPhuGiGvGgO+rIMhwCwEdsQFu6as840i2afw= github.com/lightninglabs/neutrino v0.13.2/go.mod h1:Cv/v8oHiPhuGiGvGgO+rIMhwCwEdsQFu6as840i2afw=
github.com/lightninglabs/neutrino v0.14.1 h1:ALFckeS3CPmWZmX75vxZaWvz2TUebuASH+CR4cqVo18= github.com/lightninglabs/neutrino v0.14.1 h1:ALFckeS3CPmWZmX75vxZaWvz2TUebuASH+CR4cqVo18=
github.com/lightninglabs/neutrino v0.14.1/go.mod h1:SV9ccrw2m6t6UvJX8xB//W0Dv+LEwMTbjg4V/Fb5KwU= github.com/lightninglabs/neutrino v0.14.1/go.mod h1:SV9ccrw2m6t6UvJX8xB//W0Dv+LEwMTbjg4V/Fb5KwU=

@ -298,6 +298,9 @@ type LoopInSwapInfo struct { // nolint
// where the loop-in funds may be paid. // where the loop-in funds may be paid.
HtlcAddressNP2WSH btcutil.Address HtlcAddressNP2WSH btcutil.Address
// HtlcAddresP2TR contains the v3 (pay to taproot) htlc address.
HtlcAddressP2TR btcutil.Address
// ServerMessages is the human-readable message received from the loop // ServerMessages is the human-readable message received from the loop
// server. // server.
ServerMessage string ServerMessage string
@ -309,9 +312,9 @@ type LoopOutSwapInfo struct { // nolint:revive
// SwapHash contains the sha256 hash of the swap preimage. // SwapHash contains the sha256 hash of the swap preimage.
SwapHash lntypes.Hash SwapHash lntypes.Hash
// HtlcAddressP2WSH contains the native segwit swap htlc address that // HtlcAddress contains the swap htlc address that the server will
// the server will publish to. // publish to.
HtlcAddressP2WSH btcutil.Address HtlcAddress btcutil.Address
// ServerMessages is the human-readable message received from the loop // ServerMessages is the human-readable message received from the loop
// server. // server.
@ -351,6 +354,10 @@ type SwapInfo struct {
// swap htlc. This is only used for external loop-in. // swap htlc. This is only used for external loop-in.
HtlcAddressNP2WSH btcutil.Address HtlcAddressNP2WSH btcutil.Address
// HtlcAddressP2TR stores the address of the P2TR (taproot) swap htlc.
// This is used for both internal and external loop-in and loop out.
HtlcAddressP2TR btcutil.Address
// ExternalHtlc is set to true for external loop-in swaps. // ExternalHtlc is set to true for external loop-in swaps.
ExternalHtlc bool ExternalHtlc bool

@ -386,8 +386,7 @@ func (m *Manager) autoloop(ctx context.Context) error {
} }
log.Infof("loop out automatically dispatched: hash: %v, "+ log.Infof("loop out automatically dispatched: hash: %v, "+
"address: %v", loopOut.SwapHash, "address: %v", loopOut.SwapHash, loopOut.HtlcAddress)
loopOut.HtlcAddressP2WSH)
} }
for _, in := range suggestion.InSwaps { for _, in := range suggestion.InSwaps {
@ -407,8 +406,9 @@ func (m *Manager) autoloop(ctx context.Context) error {
} }
log.Infof("loop in automatically dispatched: hash: %v, "+ log.Infof("loop in automatically dispatched: hash: %v, "+
"address: %v", loopIn.SwapHash, "address: np2wsh(%v), p2wsh(%v), p2tr(%v)",
loopIn.HtlcAddressNP2WSH) loopIn.SwapHash, loopIn.HtlcAddressNP2WSH,
loopIn.HtlcAddressP2WSH, loopIn.HtlcAddressP2TR)
} }
return nil return nil

@ -149,6 +149,8 @@ type Config struct {
TotalPaymentTimeout time.Duration `long:"totalpaymenttimeout" description:"The timeout to use for off-chain payments."` TotalPaymentTimeout time.Duration `long:"totalpaymenttimeout" description:"The timeout to use for off-chain payments."`
MaxPaymentRetries int `long:"maxpaymentretries" description:"The maximum number of times an off-chain payment may be retried."` MaxPaymentRetries int `long:"maxpaymentretries" description:"The maximum number of times an off-chain payment may be retried."`
EnableExperimental bool `long:"experimental" description:"Enable experimental features: taproot HTLCs and MuSig2 loop out sweeps."`
Lnd *lndConfig `group:"lnd" namespace:"lnd"` Lnd *lndConfig `group:"lnd" namespace:"lnd"`
Server *loopServerConfig `group:"server" namespace:"server"` Server *loopServerConfig `group:"server" namespace:"server"`
@ -185,6 +187,7 @@ func DefaultConfig() Config {
LoopOutMaxParts: defaultLoopOutMaxParts, LoopOutMaxParts: defaultLoopOutMaxParts,
TotalPaymentTimeout: defaultTotalPaymentTimeout, TotalPaymentTimeout: defaultTotalPaymentTimeout,
MaxPaymentRetries: defaultMaxPaymentRetries, MaxPaymentRetries: defaultMaxPaymentRetries,
EnableExperimental: false,
Lnd: &lndConfig{ Lnd: &lndConfig{
Host: "localhost:10009", Host: "localhost:10009",
MacaroonPath: DefaultLndMacaroonPath, MacaroonPath: DefaultLndMacaroonPath,

@ -349,6 +349,12 @@ func (d *Daemon) startWebServers() error {
// this method fails with an error then no goroutine was started yet and no // this method fails with an error then no goroutine was started yet and no
// cleanup is necessary. If it succeeds, then goroutines have been spawned. // cleanup is necessary. If it succeeds, then goroutines have been spawned.
func (d *Daemon) initialize(withMacaroonService bool) error { func (d *Daemon) initialize(withMacaroonService bool) error {
if d.cfg.EnableExperimental {
loopdb.EnableExperimentalProtocol()
}
log.Infof("Protocol version: %v", loopdb.CurrentProtocolVersion())
// If no swap server is specified, use the default addresses for mainnet // If no swap server is specified, use the default addresses for mainnet
// and testnet. // and testnet.
if d.cfg.Server.Host == "" { if d.cfg.Server.Host == "" {

@ -26,8 +26,8 @@ var (
// listed build tags/subservers need to be enabled. // listed build tags/subservers need to be enabled.
LoopMinRequiredLndVersion = &verrpc.Version{ LoopMinRequiredLndVersion = &verrpc.Version{
AppMajor: 0, AppMajor: 0,
AppMinor: 14, AppMinor: 15,
AppPatch: 2, AppPatch: 0,
BuildTags: []string{ BuildTags: []string{
"signrpc", "walletrpc", "chainrpc", "invoicesrpc", "signrpc", "walletrpc", "chainrpc", "invoicesrpc",
}, },

@ -150,13 +150,21 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
return nil, err return nil, err
} }
return &clientrpc.SwapResponse{ htlcAddress := info.HtlcAddress.String()
Id: info.SwapHash.String(), resp := &clientrpc.SwapResponse{
IdBytes: info.SwapHash[:], Id: info.SwapHash.String(),
HtlcAddress: info.HtlcAddressP2WSH.String(), IdBytes: info.SwapHash[:],
HtlcAddressP2Wsh: info.HtlcAddressP2WSH.String(), HtlcAddress: htlcAddress,
ServerMessage: info.ServerMessage, ServerMessage: info.ServerMessage,
}, nil }
if loopdb.CurrentProtocolVersion() < loopdb.ProtocolVersionHtlcV3 {
resp.HtlcAddressP2Wsh = htlcAddress
} else {
resp.HtlcAddressP2Tr = htlcAddress
}
return resp, nil
} }
func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) ( func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
@ -217,20 +225,33 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
} }
var swapType clientrpc.SwapType var swapType clientrpc.SwapType
var htlcAddress, htlcAddressP2WSH, htlcAddressNP2WSH string var (
htlcAddress string
htlcAddressP2TR string
htlcAddressP2WSH string
htlcAddressNP2WSH string
)
var outGoingChanSet []uint64 var outGoingChanSet []uint64
var lastHop []byte var lastHop []byte
switch loopSwap.SwapType { switch loopSwap.SwapType {
case swap.TypeIn: case swap.TypeIn:
swapType = clientrpc.SwapType_LOOP_IN swapType = clientrpc.SwapType_LOOP_IN
htlcAddressP2WSH = loopSwap.HtlcAddressP2WSH.EncodeAddress()
if loopSwap.ExternalHtlc { if loopSwap.HtlcAddressP2TR != nil {
htlcAddressNP2WSH = loopSwap.HtlcAddressNP2WSH.EncodeAddress() htlcAddressP2TR = loopSwap.HtlcAddressP2TR.EncodeAddress()
htlcAddress = htlcAddressNP2WSH htlcAddress = htlcAddressP2TR
} else { } else {
htlcAddress = htlcAddressP2WSH htlcAddressP2WSH =
loopSwap.HtlcAddressP2WSH.EncodeAddress()
if loopSwap.ExternalHtlc {
htlcAddressNP2WSH =
loopSwap.HtlcAddressNP2WSH.EncodeAddress()
htlcAddress = htlcAddressNP2WSH
} else {
htlcAddress = htlcAddressP2WSH
}
} }
if loopSwap.LastHop != nil { if loopSwap.LastHop != nil {
@ -239,8 +260,13 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
case swap.TypeOut: case swap.TypeOut:
swapType = clientrpc.SwapType_LOOP_OUT swapType = clientrpc.SwapType_LOOP_OUT
htlcAddressP2WSH = loopSwap.HtlcAddressP2WSH.EncodeAddress() if loopSwap.HtlcAddressP2WSH != nil {
htlcAddress = htlcAddressP2WSH htlcAddressP2WSH = loopSwap.HtlcAddressP2WSH.EncodeAddress()
htlcAddress = htlcAddressP2WSH
} else {
htlcAddressP2TR = loopSwap.HtlcAddressP2TR.EncodeAddress()
htlcAddress = htlcAddressP2TR
}
outGoingChanSet = loopSwap.OutgoingChanSet outGoingChanSet = loopSwap.OutgoingChanSet
@ -257,6 +283,7 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
InitiationTime: loopSwap.InitiationTime.UnixNano(), InitiationTime: loopSwap.InitiationTime.UnixNano(),
LastUpdateTime: loopSwap.LastUpdate.UnixNano(), LastUpdateTime: loopSwap.LastUpdate.UnixNano(),
HtlcAddress: htlcAddress, HtlcAddress: htlcAddress,
HtlcAddressP2Tr: htlcAddressP2TR,
HtlcAddressP2Wsh: htlcAddressP2WSH, HtlcAddressP2Wsh: htlcAddressP2WSH,
HtlcAddressNp2Wsh: htlcAddressNP2WSH, HtlcAddressNp2Wsh: htlcAddressNP2WSH,
Type: swapType, Type: swapType,
@ -618,8 +645,7 @@ func (s *swapClientServer) Probe(ctx context.Context,
} }
func (s *swapClientServer) LoopIn(ctx context.Context, func (s *swapClientServer) LoopIn(ctx context.Context,
in *clientrpc.LoopInRequest) ( in *clientrpc.LoopInRequest) (*clientrpc.SwapResponse, error) {
*clientrpc.SwapResponse, error) {
log.Infof("Loop in request received") log.Infof("Loop in request received")
@ -665,17 +691,25 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
} }
response := &clientrpc.SwapResponse{ response := &clientrpc.SwapResponse{
Id: swapInfo.SwapHash.String(), Id: swapInfo.SwapHash.String(),
IdBytes: swapInfo.SwapHash[:], IdBytes: swapInfo.SwapHash[:],
HtlcAddressP2Wsh: swapInfo.HtlcAddressP2WSH.String(), ServerMessage: swapInfo.ServerMessage,
ServerMessage: swapInfo.ServerMessage,
} }
if req.ExternalHtlc { if loopdb.CurrentProtocolVersion() < loopdb.ProtocolVersionHtlcV3 {
response.HtlcAddressNp2Wsh = swapInfo.HtlcAddressNP2WSH.String() if req.ExternalHtlc {
response.HtlcAddress = response.HtlcAddressNp2Wsh // nolint:staticcheck np2wshAddr := swapInfo.HtlcAddressNP2WSH.String()
response.HtlcAddress = np2wshAddr
response.HtlcAddressNp2Wsh = np2wshAddr
} else {
p2wshAddr := swapInfo.HtlcAddressP2WSH.String()
response.HtlcAddress = p2wshAddr
response.HtlcAddressP2Wsh = p2wshAddr
}
} else { } else {
response.HtlcAddress = response.HtlcAddressP2Wsh // nolint:staticcheck p2trAddr := swapInfo.HtlcAddressP2TR.String()
response.HtlcAddress = p2trAddr
response.HtlcAddressP2Tr = p2trAddr
} }
return response, nil return response, nil

@ -1,7 +1,11 @@
package loopdb package loopdb
import ( import (
"bytes"
"fmt" "fmt"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
) )
// itob returns an 8-byte big endian representation of v. // itob returns an 8-byte big endian representation of v.
@ -40,3 +44,39 @@ func MarshalProtocolVersion(version ProtocolVersion) []byte {
return versionBytes[:] return versionBytes[:]
} }
// MarshalKeyLocator marshals a keychain.KeyLocator to a byte slice.
func MarshalKeyLocator(keyLocator keychain.KeyLocator) ([]byte, error) {
var (
scratch [8]byte
buf bytes.Buffer
)
err := channeldb.EKeyLocator(&buf, &keyLocator, &scratch)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// UnmarshalKeyLocator unmarshals a keychain.KeyLocator from a byte slice.
func UnmarshalKeyLocator(data []byte) (keychain.KeyLocator, error) {
if data == nil {
return keychain.KeyLocator{}, nil
}
var (
scratch [8]byte
keyLocator keychain.KeyLocator
)
err := channeldb.DKeyLocator(
bytes.NewReader(data), &keyLocator, &scratch, 8,
)
if err != nil {
return keychain.KeyLocator{}, err
}
return keyLocator, nil
}

@ -1,8 +1,10 @@
package loopdb package loopdb
import ( import (
"math"
"testing" "testing"
"github.com/lightningnetwork/lnd/keychain"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -51,3 +53,46 @@ func TestProtocolVersionMarshalUnMarshal(t *testing.T) {
require.Equal(t, ProtocolVersionUnrecorded, version) require.Equal(t, ProtocolVersionUnrecorded, version)
} }
} }
// TestKeyLocatorMarshalUnMarshal tests that marshalling and unmarshalling
// keychain.KeyLocator works correctly.
func TestKeyLocatorMarshalUnMarshal(t *testing.T) {
t.Parallel()
tests := []struct {
keyLoc keychain.KeyLocator
}{
{
// Test that an empty keylocator is serialized and
// deserialized correctly.
keyLoc: keychain.KeyLocator{},
},
{
// Test that the max value keylocator is serialized and
// deserialized correctly.
keyLoc: keychain.KeyLocator{
Family: keychain.KeyFamily(math.MaxUint32),
Index: math.MaxUint32,
},
},
{
// Test that an arbitrary keylocator is serialized and
// deserialized correctly.
keyLoc: keychain.KeyLocator{
Family: keychain.KeyFamily(5),
Index: 7,
},
},
}
for _, test := range tests {
test := test
buf, err := MarshalKeyLocator(test.keyLoc)
require.NoError(t, err)
keyLoc, err := UnmarshalKeyLocator(buf)
require.NoError(t, err)
require.Equal(t, test.keyLoc, keyLoc)
}
}

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
) )
@ -26,6 +27,11 @@ type SwapContract struct {
// HTLC. // HTLC.
ReceiverKey [33]byte 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

@ -51,22 +51,58 @@ const (
// in order to enhance off-chain payments corresponding to a swap. // in order to enhance off-chain payments corresponding to a swap.
ProtocolVersionRoutingPlugin = 9 ProtocolVersionRoutingPlugin = 9
// ProtocolVersionHtlcV3 indicates that the client will now use the new
// HTLC v3 (P2TR) script for swaps.
ProtocolVersionHtlcV3 = 10
// 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
// CurrentRPCProtocolVersion defines the version of the RPC protocol // stableRPCProtocolVersion defines the current stable RPC protocol
// that is currently supported by the loop client. // version.
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_ROUTING_PLUGIN stableRPCProtocolVersion = looprpc.ProtocolVersion_ROUTING_PLUGIN
// experimentalRPCProtocolVersion defines the RPC protocol version that
// includes all currently experimentally released features.
experimentalRPCProtocolVersion = looprpc.ProtocolVersion_HTLC_V3
)
// CurrentInternalProtocolVersion defines the RPC current protocol in var (
// the internal representation. // currentRPCProtocolVersion holds the version of the RPC protocol
CurrentInternalProtocolVersion = ProtocolVersion(CurrentRPCProtocolVersion) // that the client selected to use for new swaps. Shouldn't be lower
// than the previous protocol version.
currentRPCProtocolVersion = stableRPCProtocolVersion
) )
// CurrentRPCProtocolVersion returns the RPC protocol version selected to be
// used for new swaps.
func CurrentRPCProtocolVersion() looprpc.ProtocolVersion {
return currentRPCProtocolVersion
}
// CurrentProtocolVersion returns the internal protocol version selected to be
// used for new swaps.
func CurrentProtocolVersion() ProtocolVersion {
return ProtocolVersion(currentRPCProtocolVersion)
}
// EnableExperimentalProtocol sets the current protocol version to include all
// experimental features. Do not call this function directly: used in loopd and
// unit tests only.
func EnableExperimentalProtocol() {
currentRPCProtocolVersion = experimentalRPCProtocolVersion
}
// ResetCurrentProtocolVersion resets the current protocol version to the stable
// protocol. Note: used in integration tests only!
func ResetCurrentProtocolVersion() {
currentRPCProtocolVersion = stableRPCProtocolVersion
}
// Valid returns true if the value of the ProtocolVersion is valid. // Valid returns true if the value of the ProtocolVersion is valid.
func (p ProtocolVersion) Valid() bool { func (p ProtocolVersion) Valid() bool {
return p <= CurrentInternalProtocolVersion return p <= ProtocolVersion(experimentalRPCProtocolVersion)
} }
// String returns the string representation of a protocol version. // String returns the string representation of a protocol version.
@ -102,6 +138,9 @@ func (p ProtocolVersion) String() string {
case ProtocolVersionRoutingPlugin: case ProtocolVersionRoutingPlugin:
return "Routing Plugin" return "Routing Plugin"
case ProtocolVersionHtlcV3:
return "HTLC V3"
default: default:
return "Unknown" return "Unknown"
} }

@ -24,6 +24,7 @@ func TestProtocolVersionSanity(t *testing.T) {
ProtocolVersionLoopOutCancel, ProtocolVersionLoopOutCancel,
ProtocolVersionProbe, ProtocolVersionProbe,
ProtocolVersionRoutingPlugin, ProtocolVersionRoutingPlugin,
ProtocolVersionHtlcV3,
} }
rpcVersions := [...]looprpc.ProtocolVersion{ rpcVersions := [...]looprpc.ProtocolVersion{
@ -37,6 +38,7 @@ func TestProtocolVersionSanity(t *testing.T) {
looprpc.ProtocolVersion_LOOP_OUT_CANCEL, looprpc.ProtocolVersion_LOOP_OUT_CANCEL,
looprpc.ProtocolVersion_PROBE, looprpc.ProtocolVersion_PROBE,
looprpc.ProtocolVersion_ROUTING_PLUGIN, looprpc.ProtocolVersion_ROUTING_PLUGIN,
looprpc.ProtocolVersion_HTLC_V3,
} }
require.Equal(t, len(versions), len(rpcVersions)) require.Equal(t, len(versions), len(rpcVersions))
@ -46,12 +48,19 @@ func TestProtocolVersionSanity(t *testing.T) {
// Finally test that the current version contants are up to date // Finally test that the current version contants are up to date
require.Equal(t, require.Equal(t,
CurrentInternalProtocolVersion, CurrentProtocolVersion(),
versions[len(versions)-1], versions[len(versions)-2],
) )
require.Equal(t, require.Equal(t,
uint32(CurrentInternalProtocolVersion), uint32(CurrentProtocolVersion()),
uint32(CurrentRPCProtocolVersion), uint32(CurrentRPCProtocolVersion()),
)
EnableExperimentalProtocol()
require.Equal(t,
CurrentProtocolVersion(),
ProtocolVersion(experimentalRPCProtocolVersion),
) )
} }

@ -102,6 +102,16 @@ var (
// parameters. // parameters.
liquidtyParamsKey = []byte("params") liquidtyParamsKey = []byte("params")
// keyLocatorKey is the key that stores the receiver key's locator info
// for loop outs or the sender key's locator info for loop ins. This is
// required for MuSig2 swaps. Only serialized/deserialized for swaps
// that have protocol version >= ProtocolVersionHtlcV3.
//
// path: loopInBucket/loopOutBucket -> swapBucket[hash] -> keyLocatorKey
//
// value: concatenation of uint32 values [family, index].
keyLocatorKey = []byte("keylocator")
byteOrder = binary.BigEndian byteOrder = binary.BigEndian
keyLength = 33 keyLength = 33
@ -327,6 +337,16 @@ func (s *boltSwapStore) FetchLoopOutSwaps() ([]*LoopOut, error) {
return err return err
} }
// Try to unmarshal the key locator.
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 {
contract.ClientKeyLocator, err = UnmarshalKeyLocator(
swapBucket.Get(keyLocatorKey),
)
if err != nil {
return err
}
}
loop := LoopOut{ loop := LoopOut{
Loop: Loop{ Loop: Loop{
Events: updates, Events: updates,
@ -464,6 +484,16 @@ func (s *boltSwapStore) FetchLoopInSwaps() ([]*LoopIn, error) {
return err return err
} }
// Try to unmarshal the key locator.
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 {
contract.ClientKeyLocator, err = UnmarshalKeyLocator(
swapBucket.Get(keyLocatorKey),
)
if err != nil {
return err
}
}
loop := LoopIn{ loop := LoopIn{
Loop: Loop{ Loop: Loop{
Events: updates, Events: updates,
@ -583,6 +613,21 @@ func (s *boltSwapStore) CreateLoopOut(hash lntypes.Hash,
return err 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
}
}
// Finally, we'll create an empty updates bucket for this swap // Finally, we'll create an empty updates bucket for this swap
// to track any future updates to the swap itself. // to track any future updates to the swap itself.
_, err = swapBucket.CreateBucket(updatesBucketKey) _, err = swapBucket.CreateBucket(updatesBucketKey)
@ -634,6 +679,21 @@ func (s *boltSwapStore) CreateLoopIn(hash lntypes.Hash,
return err 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
}
}
// Finally, we'll create an empty updates bucket for this swap // Finally, we'll create an empty updates bucket for this swap
// to track any future updates to the swap itself. // to track any future updates to the swap itself.
_, err = swapBucket.CreateBucket(updatesBucketKey) _, err = swapBucket.CreateBucket(updatesBucketKey)

@ -62,6 +62,8 @@ type loopInSwap struct {
htlcNP2WSH *swap.Htlc htlcNP2WSH *swap.Htlc
htlcP2TR *swap.Htlc
// htlcTxHash is the confirmed htlc tx id. // htlcTxHash is the confirmed htlc tx id.
htlcTxHash *chainhash.Hash htlcTxHash *chainhash.Hash
@ -238,13 +240,14 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
InitiationTime: initiationTime, InitiationTime: initiationTime,
ReceiverKey: swapResp.receiverKey, ReceiverKey: swapResp.receiverKey,
SenderKey: senderKey, SenderKey: senderKey,
ClientKeyLocator: keyDesc.KeyLocator,
Preimage: swapPreimage, Preimage: swapPreimage,
AmountRequested: request.Amount, AmountRequested: request.Amount,
CltvExpiry: swapResp.expiry, CltvExpiry: swapResp.expiry,
MaxMinerFee: request.MaxMinerFee, MaxMinerFee: request.MaxMinerFee,
MaxSwapFee: request.MaxSwapFee, MaxSwapFee: request.MaxSwapFee,
Label: request.Label, Label: request.Label,
ProtocolVersion: loopdb.CurrentInternalProtocolVersion, ProtocolVersion: loopdb.CurrentProtocolVersion(),
}, },
} }
@ -402,6 +405,18 @@ func validateLoopInContract(lnd *lndclient.LndServices,
// initHtlcs creates and updates the native and nested segwit htlcs // initHtlcs creates and updates the native and nested segwit htlcs
// of the loopInSwap. // of the loopInSwap.
func (s *loopInSwap) initHtlcs() error { func (s *loopInSwap) initHtlcs() error {
if IsTaprootSwap(&s.SwapContract) {
htlcP2TR, err := s.swapKit.getHtlc(swap.HtlcP2TR)
if err != nil {
return err
}
s.swapKit.log.Infof("Htlc address (P2TR): %v", htlcP2TR.Address)
s.htlcP2TR = htlcP2TR
return nil
}
htlcP2WSH, err := s.swapKit.getHtlc(swap.HtlcP2WSH) htlcP2WSH, err := s.swapKit.getHtlc(swap.HtlcP2WSH)
if err != nil { if err != nil {
return err return err
@ -427,8 +442,13 @@ func (s *loopInSwap) sendUpdate(ctx context.Context) error {
info := s.swapInfo() info := s.swapInfo()
s.log.Infof("Loop in swap state: %v", info.State) s.log.Infof("Loop in swap state: %v", info.State)
info.HtlcAddressP2WSH = s.htlcP2WSH.Address if IsTaprootSwap(&s.SwapContract) {
info.HtlcAddressNP2WSH = s.htlcNP2WSH.Address info.HtlcAddressP2TR = s.htlcP2TR.Address
} else {
info.HtlcAddressP2WSH = s.htlcP2WSH.Address
info.HtlcAddressNP2WSH = s.htlcNP2WSH.Address
}
info.ExternalHtlc = s.ExternalHtlc info.ExternalHtlc = s.ExternalHtlc
// In order to avoid potentially dangerous ownership sharing // In order to avoid potentially dangerous ownership sharing
@ -605,16 +625,29 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
notifier := s.lnd.ChainNotifier notifier := s.lnd.ChainNotifier
confChanP2WSH, confErrP2WSH, err := notifier.RegisterConfirmationsNtfn( notifyConfirmation := func(htlc *swap.Htlc) (
ctx, s.htlcTxHash, s.htlcP2WSH.PkScript, 1, s.InitiationHeight, chan *chainntnfs.TxConfirmation, chan error, error) {
)
if htlc == nil {
return nil, nil, nil
}
return notifier.RegisterConfirmationsNtfn(
ctx, s.htlcTxHash, htlc.PkScript, 1, s.InitiationHeight,
)
}
confChanP2WSH, confErrP2WSH, err := notifyConfirmation(s.htlcP2WSH)
if err != nil { if err != nil {
return nil, err return nil, err
} }
confChanNP2WSH, confErrNP2WSH, err := notifier.RegisterConfirmationsNtfn( confChanNP2WSH, confErrNP2WSH, err := notifyConfirmation(s.htlcNP2WSH)
ctx, s.htlcTxHash, s.htlcNP2WSH.PkScript, 1, s.InitiationHeight, if err != nil {
) return nil, err
}
confChanP2TR, confErrP2TR, err := notifyConfirmation(s.htlcP2TR)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -633,6 +666,11 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
s.htlc = s.htlcNP2WSH s.htlc = s.htlcNP2WSH
s.log.Infof("NP2WSH htlc confirmed") s.log.Infof("NP2WSH htlc confirmed")
// P2TR htlc confirmed.
case conf = <-confChanP2TR:
s.htlc = s.htlcP2TR
s.log.Infof("P2TR htlc confirmed")
// Conf ntfn error. // Conf ntfn error.
case err := <-confErrP2WSH: case err := <-confErrP2WSH:
return nil, err return nil, err
@ -641,6 +679,10 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
case err := <-confErrNP2WSH: case err := <-confErrNP2WSH:
return nil, err return nil, err
// Conf ntfn error.
case err := <-confErrP2TR:
return nil, err
// Keep up with block height. // Keep up with block height.
case notification := <-s.blockEpochChan: case notification := <-s.blockEpochChan:
s.height = notification.(int32) s.height = notification.(int32)
@ -697,10 +739,16 @@ func (s *loopInSwap) publishOnChainHtlc(ctx context.Context) (bool, error) {
s.log.Infof("Publishing on chain HTLC with fee rate %v", feeRate) s.log.Infof("Publishing on chain HTLC with fee rate %v", feeRate)
// Internal loop-in is always P2WSH. var pkScript []byte
if IsTaprootSwap(&s.SwapContract) {
pkScript = s.htlcP2TR.PkScript
} else {
pkScript = s.htlcP2WSH.PkScript
}
tx, err := s.lnd.WalletKit.SendOutputs( tx, err := s.lnd.WalletKit.SendOutputs(
ctx, []*wire.TxOut{{ ctx, []*wire.TxOut{{
PkScript: s.htlcP2WSH.PkScript, PkScript: pkScript,
Value: int64(s.LoopInContract.AmountRequested), Value: int64(s.LoopInContract.AmountRequested),
}}, feeRate, labels.LoopInHtlcLabel(swap.ShortHash(&s.hash)), }}, feeRate, labels.LoopInHtlcLabel(swap.ShortHash(&s.hash)),
) )

@ -2,6 +2,7 @@ package loop
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
@ -27,6 +28,19 @@ var (
// TestLoopInSuccess tests the success scenario where the swap completes the // TestLoopInSuccess tests the success scenario where the swap completes the
// happy flow. // happy flow.
func TestLoopInSuccess(t *testing.T) { func TestLoopInSuccess(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testLoopInSuccess(t)
})
t.Run("experimental protocol", func(t *testing.T) {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
testLoopInSuccess(t)
})
}
func testLoopInSuccess(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
ctx := newLoopInTestContext(t) ctx := newLoopInTestContext(t)
@ -47,13 +61,13 @@ func TestLoopInSuccess(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
swap := initResult.swap inSwap := initResult.swap
ctx.store.assertLoopInStored() ctx.store.assertLoopInStored()
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := swap.execute(context.Background(), ctx.cfg, height) err := inSwap.execute(context.Background(), ctx.cfg, height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
@ -87,10 +101,15 @@ func TestLoopInSuccess(t *testing.T) {
require.NotNil(t, state.HtlcTxHash) require.NotNil(t, state.HtlcTxHash)
require.Equal(t, cost, state.Cost) require.Equal(t, cost, state.Cost)
// Expect register for htlc conf. // Expect register for htlc conf (only one, since the htlc is p2tr).
<-ctx.lnd.RegisterConfChannel
<-ctx.lnd.RegisterConfChannel <-ctx.lnd.RegisterConfChannel
// If the swap is legacy, then we'll register two confirmation
// notifications.
if !IsTaprootSwap(&inSwap.SwapContract) {
<-ctx.lnd.RegisterConfChannel
}
// Confirm htlc. // Confirm htlc.
ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{ ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
Tx: &htlcTx, Tx: &htlcTx,
@ -112,8 +131,13 @@ func TestLoopInSuccess(t *testing.T) {
// Server spends htlc. // Server spends htlc.
successTx := wire.MsgTx{} successTx := wire.MsgTx{}
witness, err := inSwap.htlc.GenSuccessWitness(
[]byte{}, inSwap.contract.Preimage,
)
require.NoError(t, err)
successTx.AddTxIn(&wire.TxIn{ successTx.AddTxIn(&wire.TxIn{
Witness: [][]byte{{}, {}, {}}, Witness: witness,
}) })
ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{ ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
@ -134,33 +158,52 @@ func TestLoopInSuccess(t *testing.T) {
// and the client is forced to reclaim the funds using the timeout tx. // and the client is forced to reclaim the funds using the timeout tx.
func TestLoopInTimeout(t *testing.T) { func TestLoopInTimeout(t *testing.T) {
testAmt := int64(testLoopInRequest.Amount) testAmt := int64(testLoopInRequest.Amount)
t.Run("internal htlc", func(t *testing.T) { testCases := []struct {
testLoopInTimeout(t, swap.HtlcP2WSH, 0) name string
}) externalValue int64
}{
{
name: "internal htlc",
externalValue: 0,
},
{
name: "external htlc",
externalValue: testAmt,
},
{
name: "external htlc amount too high",
externalValue: testAmt + 1,
},
{
name: "external htlc amount too low",
externalValue: testAmt - 1,
},
}
outputTypes := []swap.HtlcOutputType{swap.HtlcP2WSH, swap.HtlcNP2WSH} for _, next := range []bool{false, true} {
next := next
for _, outputType := range outputTypes { for _, testCase := range testCases {
outputType := outputType testCase := testCase
t.Run(outputType.String(), func(t *testing.T) {
t.Run("external htlc", func(t *testing.T) {
testLoopInTimeout(t, outputType, testAmt)
})
t.Run("external amount too high", func(t *testing.T) { name := testCase.name
testLoopInTimeout(t, outputType, testAmt+1) if next {
}) name += " experimental protocol"
}
t.Run(name, func(t *testing.T) {
if next {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
}
t.Run("external amount too low", func(t *testing.T) { testLoopInTimeout(t, testCase.externalValue)
testLoopInTimeout(t, outputType, testAmt-1)
}) })
}) }
} }
} }
func testLoopInTimeout(t *testing.T, func testLoopInTimeout(t *testing.T, externalValue int64) {
outputType swap.HtlcOutputType, externalValue int64) {
defer test.Guard(t)() defer test.Guard(t)()
ctx := newLoopInTestContext(t) ctx := newLoopInTestContext(t)
@ -181,13 +224,13 @@ func testLoopInTimeout(t *testing.T,
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
s := initResult.swap inSwap := initResult.swap
ctx.store.assertLoopInStored() ctx.store.assertLoopInStored()
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := s.execute(context.Background(), ctx.cfg, height) err := inSwap.execute(context.Background(), ctx.cfg, height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
@ -203,9 +246,11 @@ func testLoopInTimeout(t *testing.T,
htlcTx wire.MsgTx htlcTx wire.MsgTx
cost loopdb.SwapCost cost loopdb.SwapCost
) )
if externalValue == 0 { if externalValue == 0 {
// Expect htlc to be published. // Expect htlc to be published.
htlcTx = <-ctx.lnd.SendOutputsChannel htlcTx = <-ctx.lnd.SendOutputsChannel
cost = loopdb.SwapCost{ cost = loopdb.SwapCost{
Onchain: getTxFee( Onchain: getTxFee(
&htlcTx, test.DefaultMockFee.FeePerKVByte(), &htlcTx, test.DefaultMockFee.FeePerKVByte(),
@ -220,11 +265,16 @@ func testLoopInTimeout(t *testing.T,
} else { } else {
// Create an external htlc publish tx. // Create an external htlc publish tx.
var pkScript []byte var pkScript []byte
if outputType == swap.HtlcNP2WSH { if !IsTaprootSwap(&inSwap.SwapContract) {
pkScript = s.htlcNP2WSH.PkScript if req.ExternalHtlc {
pkScript = inSwap.htlcNP2WSH.PkScript
} else {
pkScript = inSwap.htlcP2WSH.PkScript
}
} else { } else {
pkScript = s.htlcP2WSH.PkScript pkScript = inSwap.htlcP2TR.PkScript
} }
htlcTx = wire.MsgTx{ htlcTx = wire.MsgTx{
TxOut: []*wire.TxOut{ TxOut: []*wire.TxOut{
{ {
@ -237,7 +287,12 @@ func testLoopInTimeout(t *testing.T,
// Expect register for htlc conf. // Expect register for htlc conf.
<-ctx.lnd.RegisterConfChannel <-ctx.lnd.RegisterConfChannel
<-ctx.lnd.RegisterConfChannel
// If the swap is legacy, then we'll register two confirmation
// notifications.
if !IsTaprootSwap(&inSwap.SwapContract) {
<-ctx.lnd.RegisterConfChannel
}
// Confirm htlc. // Confirm htlc.
ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{ ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
@ -265,7 +320,7 @@ func testLoopInTimeout(t *testing.T,
ctx.assertSubscribeInvoice(ctx.server.swapHash) ctx.assertSubscribeInvoice(ctx.server.swapHash)
// Let htlc expire. // Let htlc expire.
ctx.blockEpochChan <- s.LoopInContract.CltvExpiry ctx.blockEpochChan <- inSwap.LoopInContract.CltvExpiry
// Expect a signing request for the htlc tx output value. // Expect a signing request for the htlc tx output value.
signReq := <-ctx.lnd.SignOutputRawChannel signReq := <-ctx.lnd.SignOutputRawChannel
@ -278,9 +333,9 @@ func testLoopInTimeout(t *testing.T,
// We can just get our sweep fee as we would in the swap code because // We can just get our sweep fee as we would in the swap code because
// our estimate is static. // our estimate is static.
fee, err := s.sweeper.GetSweepFee( fee, err := inSwap.sweeper.GetSweepFee(
context.Background(), s.htlc.AddTimeoutToEstimator, context.Background(), inSwap.htlc.AddTimeoutToEstimator,
s.timeoutAddr, TimeoutTxConfTarget, inSwap.timeoutAddr, TimeoutTxConfTarget,
) )
require.NoError(t, err) require.NoError(t, err)
cost.Onchain += fee cost.Onchain += fee
@ -313,52 +368,71 @@ func TestLoopInResume(t *testing.T) {
storedVersion := []loopdb.ProtocolVersion{ storedVersion := []loopdb.ProtocolVersion{
loopdb.ProtocolVersionUnrecorded, loopdb.ProtocolVersionUnrecorded,
loopdb.ProtocolVersionHtlcV2, loopdb.ProtocolVersionHtlcV2,
loopdb.ProtocolVersionHtlcV3,
} }
htlcVersion := []swap.ScriptVersion{ testCases := []struct {
swap.HtlcV1, name string
swap.HtlcV2, state loopdb.SwapState
expired bool
}{
{
name: "initiated",
state: loopdb.StateInitiated,
expired: false,
},
{
name: "initiated expired",
state: loopdb.StateInitiated,
expired: true,
},
{
name: "htlc published",
state: loopdb.StateHtlcPublished,
expired: false,
},
} }
for i, version := range storedVersion { for _, next := range []bool{false, true} {
version := version for _, version := range storedVersion {
scriptVersion := htlcVersion[i] version := version
for _, testCase := range testCases {
t.Run(version.String(), func(t *testing.T) { testCase := testCase
t.Run("initiated", func(t *testing.T) {
testLoopInResume(
t, loopdb.StateInitiated, false,
version, scriptVersion,
)
})
t.Run("initiated expired", func(t *testing.T) {
testLoopInResume(
t, loopdb.StateInitiated, true,
version, scriptVersion,
)
})
t.Run("htlc published", func(t *testing.T) { name := fmt.Sprintf(
testLoopInResume( "%v %v", testCase, version.String(),
t, loopdb.StateHtlcPublished, false,
version, scriptVersion,
) )
}) if next {
}) name += " next protocol"
}
t.Run(name, func(t *testing.T) {
testLoopInResume(
t, testCase.state,
testCase.expired,
version,
)
})
}
}
} }
} }
func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
storedVersion loopdb.ProtocolVersion, scriptVersion swap.ScriptVersion) { storedVersion loopdb.ProtocolVersion) {
defer test.Guard(t)() defer test.Guard(t)()
ctx := newLoopInTestContext(t) ctx := newLoopInTestContext(t)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server) cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
senderKey := [33]byte{4} // Create sender and receiver keys.
receiverKey := [33]byte{5} _, senderPubKey := test.CreateKey(1)
_, receiverPubKey := test.CreateKey(2)
var senderKey, receiverKey [33]byte
copy(receiverKey[:], receiverPubKey.SerializeCompressed())
copy(senderKey[:], senderPubKey.SerializeCompressed())
contract := &loopdb.LoopInContract{ contract := &loopdb.LoopInContract{
HtlcConfTarget: 2, HtlcConfTarget: 2,
@ -397,9 +471,16 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
pendSwap.Loop.Events[0].Cost = cost pendSwap.Loop.Events[0].Cost = cost
} }
scriptVersion := GetHtlcScriptVersion(storedVersion)
outputType := swap.HtlcNP2WSH
if scriptVersion == swap.HtlcV3 {
outputType = swap.HtlcP2TR
}
htlc, err := swap.NewHtlc( htlc, err := swap.NewHtlc(
scriptVersion, contract.CltvExpiry, contract.SenderKey, scriptVersion, contract.CltvExpiry, contract.SenderKey,
contract.ReceiverKey, testPreimage.Hash(), swap.HtlcNP2WSH, contract.ReceiverKey, testPreimage.Hash(), outputType,
cfg.lnd.ChainParams, cfg.lnd.ChainParams,
) )
if err != nil { if err != nil {
@ -411,7 +492,7 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
t.Fatal(err) t.Fatal(err)
} }
swap, err := resumeLoopInSwap( inSwap, err := resumeLoopInSwap(
context.Background(), cfg, context.Background(), cfg,
pendSwap, pendSwap,
) )
@ -428,7 +509,7 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := swap.execute(context.Background(), ctx.cfg, height) err := inSwap.execute(context.Background(), ctx.cfg, height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
@ -489,7 +570,12 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
// Expect register for htlc conf. // Expect register for htlc conf.
<-ctx.lnd.RegisterConfChannel <-ctx.lnd.RegisterConfChannel
<-ctx.lnd.RegisterConfChannel
// If the swap is legacy, then we'll register two confirmation
// notifications.
if !IsTaprootSwap(&inSwap.SwapContract) {
<-ctx.lnd.RegisterConfChannel
}
// Confirm htlc. // Confirm htlc.
ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{ ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
@ -513,8 +599,11 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
// Server spends htlc. // Server spends htlc.
successTx := wire.MsgTx{} successTx := wire.MsgTx{}
witness, err := htlc.GenSuccessWitness([]byte{}, testPreimage)
require.NoError(t, err)
successTx.AddTxIn(&wire.TxIn{ successTx.AddTxIn(&wire.TxIn{
Witness: [][]byte{{}, {}, {}}, Witness: witness,
}) })
successTxHash := successTx.TxHash() successTxHash := successTx.TxHash()

@ -10,8 +10,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"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/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/labels" "github.com/lightninglabs/loop/labels"
@ -20,18 +23,25 @@ import (
"github.com/lightninglabs/loop/sweep" "github.com/lightninglabs/loop/sweep"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"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/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
) )
// loopInternalHops indicate the number of hops that a loop out swap makes in const (
// the server's off-chain infrastructure. We are ok reporting failure distances // loopInternalHops indicate the number of hops that a loop out swap
// from the server up until this point, because every swap takes these two // makes in the server's off-chain infrastructure. We are ok reporting
// hops, so surfacing this information does not identify the client in any way. // failure distances from the server up until this point, because every
// After this point, the client does not report failure distances, so that // swap takes these two hops, so surfacing this information does not
// sender-privacy is preserved. // identify the client in any way. After this point, the client does not
const loopInternalHops = 2 // report failure distances, so that sender-privacy is preserved.
loopInternalHops = 2
// We'll try to sweep with MuSig2 at most 10 times. If that fails we'll
// fail back to using standard scriptspend sweep.
maxMusigSweepRetries = 10
)
var ( var (
// MinLoopOutPreimageRevealDelta configures the minimum number of // MinLoopOutPreimageRevealDelta configures the minimum number of
@ -86,6 +96,7 @@ type executeConfig struct {
totalPaymentTimout time.Duration totalPaymentTimout time.Duration
maxPaymentRetries int maxPaymentRetries int
cancelSwap func(context.Context, *outCancelDetails) error cancelSwap func(context.Context, *outCancelDetails) error
verifySchnorrSig func(pubKey *btcec.PublicKey, hash, sig []byte) error
} }
// loopOutInitResult contains information about a just-initiated loop out swap. // loopOutInitResult contains information about a just-initiated loop out swap.
@ -169,28 +180,35 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
SwapContract: loopdb.SwapContract{ SwapContract: loopdb.SwapContract{
InitiationHeight: currentHeight, InitiationHeight: currentHeight,
InitiationTime: initiationTime, InitiationTime: initiationTime,
ReceiverKey: receiverKey,
SenderKey: swapResp.senderKey, SenderKey: swapResp.senderKey,
ReceiverKey: receiverKey,
ClientKeyLocator: keyDesc.KeyLocator,
Preimage: swapPreimage, Preimage: swapPreimage,
AmountRequested: request.Amount, AmountRequested: request.Amount,
CltvExpiry: request.Expiry, CltvExpiry: request.Expiry,
MaxMinerFee: request.MaxMinerFee, MaxMinerFee: request.MaxMinerFee,
MaxSwapFee: request.MaxSwapFee, MaxSwapFee: request.MaxSwapFee,
Label: request.Label, Label: request.Label,
ProtocolVersion: loopdb.CurrentInternalProtocolVersion, ProtocolVersion: loopdb.CurrentProtocolVersion(),
}, },
OutgoingChanSet: chanSet, OutgoingChanSet: chanSet,
} }
swapKit := newSwapKit( swapKit := newSwapKit(
swapHash, swap.TypeOut, swapHash, swap.TypeOut, cfg, &contract.SwapContract,
cfg, &contract.SwapContract,
) )
swapKit.lastUpdateTime = initiationTime swapKit.lastUpdateTime = initiationTime
scriptVersion := GetHtlcScriptVersion(loopdb.CurrentProtocolVersion())
outputType := swap.HtlcP2TR
if scriptVersion != swap.HtlcV3 {
// Default to using P2WSH for legacy htlcs.
outputType = swap.HtlcP2WSH
}
// Create the htlc. // Create the htlc.
htlc, err := swapKit.getHtlc(swap.HtlcP2WSH) htlc, err := swapKit.getHtlc(outputType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -239,12 +257,18 @@ func resumeLoopOutSwap(reqContext context.Context, cfg *swapConfig,
log.Infof("Resuming loop out swap %v", hash) log.Infof("Resuming loop out swap %v", hash)
swapKit := newSwapKit( swapKit := newSwapKit(
hash, swap.TypeOut, cfg, hash, swap.TypeOut, cfg, &pend.Contract.SwapContract,
&pend.Contract.SwapContract,
) )
scriptVersion := GetHtlcScriptVersion(pend.Contract.ProtocolVersion)
outputType := swap.HtlcP2TR
if scriptVersion != swap.HtlcV3 {
// Default to using P2WSH for legacy htlcs.
outputType = swap.HtlcP2WSH
}
// Create the htlc. // Create the htlc.
htlc, err := swapKit.getHtlc(swap.HtlcP2WSH) htlc, err := swapKit.getHtlc(outputType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -513,11 +537,8 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
} }
// Try to spend htlc and continue (rbf) until a spend has confirmed. // Try to spend htlc and continue (rbf) until a spend has confirmed.
spendDetails, err := s.waitForHtlcSpendConfirmed(globalCtx, spendDetails, err := s.waitForHtlcSpendConfirmed(
*htlcOutpoint, globalCtx, *htlcOutpoint, htlcValue,
func() error {
return s.sweep(globalCtx, *htlcOutpoint, htlcValue)
},
) )
if err != nil { if err != nil {
return err return err
@ -1012,14 +1033,14 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
// sweep offchain. So we must make sure we sweep successfully before on-chain // sweep offchain. So we must make sure we sweep successfully before on-chain
// timeout. // timeout.
func (s *loopOutSwap) waitForHtlcSpendConfirmed(globalCtx context.Context, func (s *loopOutSwap) waitForHtlcSpendConfirmed(globalCtx context.Context,
htlc wire.OutPoint, spendFunc func() error) (*chainntnfs.SpendDetail, htlcOutpoint wire.OutPoint, htlcValue btcutil.Amount) (
error) { *chainntnfs.SpendDetail, error) {
// Register the htlc spend notification. // Register the htlc spend notification.
ctx, cancel := context.WithCancel(globalCtx) ctx, cancel := context.WithCancel(globalCtx)
defer cancel() defer cancel()
spendChan, spendErr, err := s.lnd.ChainNotifier.RegisterSpendNtfn( spendChan, spendErr, err := s.lnd.ChainNotifier.RegisterSpendNtfn(
ctx, &htlc, s.htlc.PkScript, s.InitiationHeight, ctx, &htlcOutpoint, s.htlc.PkScript, s.InitiationHeight,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("register spend ntfn: %v", err) return nil, fmt.Errorf("register spend ntfn: %v", err)
@ -1035,16 +1056,26 @@ func (s *loopOutSwap) waitForHtlcSpendConfirmed(globalCtx context.Context,
return nil, fmt.Errorf("track payment: %v", err) return nil, fmt.Errorf("track payment: %v", err)
} }
// paymentComplete tracks whether our payment is complete, and is used var (
// to decide whether we need to push our preimage to the server. // paymentComplete tracks whether our payment is complete, and
var paymentComplete bool // is used to decide whether we need to push our preimage to
// the server.
paymentComplete bool
// musigSweepTryCount tracts the number of cooperative, MuSig2
// sweep attempts.
musigSweepTryCount int
// musigSweepSuccess tracks whether at least one MuSig2 sweep
// txn was successfully published to the mempool.
musigSweepSuccess bool
)
timerChan := s.timerFactory(republishDelay) timerChan := s.timerFactory(republishDelay)
for { for {
select { select {
// Htlc spend, break loop. // Htlc spend, break loop.
case spendDetails := <-spendChan: case spendDetails := <-spendChan:
s.log.Infof("Htlc spend by tx: %v", spendDetails.SpenderTxHash) s.log.Infof("Htlc spend by tx: %v",
spendDetails.SpenderTxHash)
return spendDetails, nil return spendDetails, nil
@ -1100,23 +1131,112 @@ func (s *loopOutSwap) waitForHtlcSpendConfirmed(globalCtx context.Context,
// Some time after start or after arrival of a new block, try // Some time after start or after arrival of a new block, try
// to spend again. // to spend again.
case <-timerChan: case <-timerChan:
err := spendFunc() if IsTaprootSwap(&s.SwapContract) {
if err != nil { // sweepConfTarget will return false if the
return nil, err // preimage is not revealed yet but the conf
} // target is closer than 20 blocks. In this case
// to be sure we won't attempt to sweep at all
// and we won't reveal the preimage either.
_, canSweep := s.sweepConfTarget()
if !canSweep {
s.log.Infof("Aborting swap, timed " +
"out on-chain")
s.state = loopdb.StateFailTimeout
err := s.persistState(ctx)
if err != nil {
log.Warnf("unable to persist " +
"state")
}
// If the result of our spend func was that the swap return nil, nil
// has reached a final state, then we return nil spend }
// details, because there is no further action required
// for this swap. // When using taproot HTLCs we're pushing the
if s.state.Type() != loopdb.StateTypePending { // preimage before attempting to sweep. This
return nil, nil // way the server will know that the swap will
} // go through and we'll be able to MuSig2
// cosign our sweep transaction. In the worst
// case if the server is uncooperative for any
// reason we can still sweep using scriptpath
// spend.
err = s.setStatePreimageRevealed(ctx)
if err != nil {
return nil, err
}
if !paymentComplete {
// Push the preimage for as long as the
// server is able to settle the swap
// invoice. So that we can continue
// with the MuSig2 sweep afterwards.
s.pushPreimage(ctx)
}
// If our off chain payment is not yet complete, we // Now attempt to publish a MuSig2 sweep txn.
// try to push our preimage to the server. // Only attempt at most maxMusigSweepRetires
if !paymentComplete { // times to still leave time for an emergency
s.pushPreimage(ctx) // script path sweep.
if musigSweepTryCount < maxMusigSweepRetries {
success := s.sweepMuSig2(
ctx, htlcOutpoint, htlcValue,
)
if !success {
musigSweepTryCount++
} else {
// Mark that we had a sweep
// that was successful. There's
// no need for the script spend
// now we can just keep pushing
// new sweeps to bump the fee.
musigSweepSuccess = true
}
} else if !musigSweepSuccess {
// Attempt to script path sweep. If the
// sweep fails, we can't do any better
// than go on and try again later as
// the preimage is alredy revealed and
// the server settled the swap payment.
// From the server's point of view the
// swap is succeeded at this point so
// we are free to retry as long as we
// want.
err := s.sweep(
ctx, htlcOutpoint, htlcValue,
)
if err != nil {
log.Warnf("Failed to publish "+
"non-cooperative "+
"sweep: %v", err)
}
}
// If the result of our spend func was that the
// swap has reached a final state, then we
// return nil spend details, because there is
// no further action required for this swap.
if s.state.Type() != loopdb.StateTypePending {
return nil, nil
}
} else {
err := s.sweep(ctx, htlcOutpoint, htlcValue)
if err != nil {
return nil, err
}
// If the result of our spend func was that the
// swap has reached a final state, then we
// return nil spend details, because there is no
// further action required for this swap.
if s.state.Type() != loopdb.StateTypePending {
return nil, nil
}
// If our off chain payment is not yet complete,
// we try to push our preimage to the server.
if !paymentComplete {
s.pushPreimage(ctx)
}
} }
// Context canceled. // Context canceled.
@ -1225,24 +1345,118 @@ func (s *loopOutSwap) failOffChain(ctx context.Context, paymentType paymentType,
} }
} }
// sweep tries to sweep the given htlc to a destination address. It takes into // createMuSig2SweepTxn creates a taproot keyspend sweep transaction and
// account the max miner fee and marks the preimage as revealed when it // attempts to cooperate with the server to create a MuSig2 signature witness.
// published the tx. If the preimage has not yet been revealed, and the time func (s *loopOutSwap) createMuSig2SweepTxn(
// during which we can safely reveal it has passed, the swap will be marked ctx context.Context, htlcOutpoint wire.OutPoint,
// as failed, and the function will return. htlcValue btcutil.Amount, fee btcutil.Amount) (*wire.MsgTx, error) {
//
// TODO: Use lnd sweeper?
func (s *loopOutSwap) sweep(ctx context.Context,
htlcOutpoint wire.OutPoint,
htlcValue btcutil.Amount) error {
witnessFunc := func(sig []byte) (wire.TxWitness, error) { // First assemble our taproot keyspend sweep transaction and get the
return s.htlc.GenSuccessWitness(sig, s.Preimage) // sig hash.
sweepTx, sigHash, err := s.sweeper.CreateUnsignedTaprootKeySpendSweepTx(
ctx, uint32(s.height), s.htlc, htlcOutpoint, htlcValue, fee,
s.DestAddr,
)
if err != nil {
return nil, err
} }
// Retrieve the full script required to unlock the output. var schnorrSenderKey, schnorrReceiverKey [32]byte
redeemScript := s.htlc.SuccessScript() copy(schnorrSenderKey[:], s.SenderKey[1:])
copy(schnorrReceiverKey[:], s.ReceiverKey[1:])
htlc, ok := s.htlc.HtlcScript.(*swap.HtlcScriptV3)
if !ok {
return nil, fmt.Errorf("non taproot htlc")
}
// 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, &s.ClientKeyLocator,
[][32]byte{schnorrSenderKey, schnorrReceiverKey},
lndclient.MuSig2TaprootTweakOpt(htlc.RootHash[:], false),
)
if err != nil {
return nil, err
}
// With the session active, we can now send the server our public nonce
// and the sig hash, so that it can create it's own MuSig2 session and
// return the server side nonce and partial signature.
serverNonce, serverSig, err := s.swapKit.server.MuSig2SignSweep(
ctx, s.SwapContract.ProtocolVersion, s.hash,
s.swapInvoicePaymentAddr, musig2SessionInfo.PublicNonce[:],
sigHash,
)
if err != nil {
return nil, err
}
var serverPublicNonce [musig2.PubNonceSize]byte
copy(serverPublicNonce[:], serverNonce)
// Register the server's nonce before attempting to create our partial
// signature.
haveAllNonces, err := s.lnd.Signer.MuSig2RegisterNonces(
ctx, musig2SessionInfo.SessionID,
[][musig2.PubNonceSize]byte{serverPublicNonce},
)
if err != nil {
return nil, err
}
// Sanity check that we have all the nonces.
if !haveAllNonces {
return nil, fmt.Errorf("invalid MuSig2 session: nonces missing")
}
var digest [32]byte
copy(digest[:], sigHash)
// Since our MuSig2 session has all nonces, we can now create the local
// partial signature by signing the sig hash.
_, err = s.lnd.Signer.MuSig2Sign(
ctx, musig2SessionInfo.SessionID, digest, false,
)
if err != nil {
return nil, err
}
// Now combine the partial signatures to use the final combined
// signature in the sweep transaction's witness.
haveAllSigs, finalSig, err := s.lnd.Signer.MuSig2CombineSig(
ctx, musig2SessionInfo.SessionID, [][]byte{serverSig},
)
if err != nil {
return nil, err
}
if !haveAllSigs {
return nil, fmt.Errorf("failed to combine signatures")
}
// 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,
)
if err != nil {
return nil, err
}
// Now that we know the signature is correct, we can fill it in to our
// witness.
sweepTx.TxIn[0].Witness = wire.TxWitness{
finalSig,
}
return sweepTx, nil
}
// sweepConfTarget returns the confirmation target for the htlc sweep or false
// if we're too late.
func (s *loopOutSwap) sweepConfTarget() (int32, bool) {
remainingBlocks := s.CltvExpiry - s.height remainingBlocks := s.CltvExpiry - s.height
blocksToLastReveal := remainingBlocks - MinLoopOutPreimageRevealDelta blocksToLastReveal := remainingBlocks - MinLoopOutPreimageRevealDelta
preimageRevealed := s.state == loopdb.StatePreimageRevealed preimageRevealed := s.state == loopdb.StatePreimageRevealed
@ -1258,7 +1472,7 @@ func (s *loopOutSwap) sweep(ctx context.Context,
s.height) s.height)
s.state = loopdb.StateFailTimeout s.state = loopdb.StateFailTimeout
return nil return 0, false
} }
// Calculate the transaction fee based on the confirmation target // Calculate the transaction fee based on the confirmation target
@ -1273,42 +1487,84 @@ func (s *loopOutSwap) sweep(ctx context.Context,
confTarget = DefaultSweepConfTarget confTarget = DefaultSweepConfTarget
} }
fee, err := s.sweeper.GetSweepFee( return confTarget, true
ctx, s.htlc.AddSuccessToEstimator, s.DestAddr, confTarget, }
)
if err != nil {
return err
}
// clampSweepFee will clamp the passed in sweep fee to the maximum configured
// miner fee. Returns false if sweeping should not continue. Note that in the
// MuSig2 case we always continue as the preimage is revealed to the server
// before cooperatively signing the sweep transaction.
func (s *loopOutSwap) clampSweepFee(fee btcutil.Amount) (btcutil.Amount, bool) {
// Ensure it doesn't exceed our maximum fee allowed. // Ensure it doesn't exceed our maximum fee allowed.
if fee > s.MaxMinerFee { if fee > s.MaxMinerFee {
s.log.Warnf("Required fee %v exceeds max miner fee of %v", s.log.Warnf("Required fee %v exceeds max miner fee of %v",
fee, s.MaxMinerFee) fee, s.MaxMinerFee)
if preimageRevealed { if s.state == loopdb.StatePreimageRevealed {
// The currently required fee exceeds the max, but we // The currently required fee exceeds the max, but we
// already revealed the preimage. The best we can do now // already revealed the preimage. The best we can do now
// is to republish with the max fee. // is to republish with the max fee.
fee = s.MaxMinerFee fee = s.MaxMinerFee
} else { } else {
s.log.Warnf("Not revealing preimage") s.log.Warnf("Not revealing preimage")
return nil return 0, false
} }
} }
// Create sweep tx. return fee, true
sweepTx, err := s.sweeper.CreateSweepTx( }
ctx, s.height, s.htlc.SuccessSequence(), s.htlc, htlcOutpoint,
s.ReceiverKey, redeemScript, witnessFunc, htlcValue, fee, // sweepMuSig2 attempts to sweep the on-chain HTLC using MuSig2. If anything
s.DestAddr, // fails, we'll log it but will simply return to allow further retries. Since
// the preimage is revealed by the time we attempt to MuSig2 sweep, we'll need
// to fall back to a script spend sweep if all MuSig2 sweep attempts fail (for
// example the server could be down due to maintenance or any other issue
// making the cooperative sweep fail).
func (s *loopOutSwap) sweepMuSig2(ctx context.Context,
htlcOutpoint wire.OutPoint, htlcValue btcutil.Amount) bool {
addInputToEstimator := func(e *input.TxWeightEstimator) error {
e.AddTaprootKeySpendInput(txscript.SigHashDefault)
return nil
}
confTarget, _ := s.sweepConfTarget()
fee, err := s.sweeper.GetSweepFee(
ctx, addInputToEstimator, s.DestAddr, confTarget,
) )
if err != nil { if err != nil {
return err s.log.Warnf("Failed to estimate fee MuSig2 sweep txn: %v", err)
return false
} }
// Before publishing the tx, already mark the preimage as revealed. This fee, _ = s.clampSweepFee(fee)
// is a precaution in case the publish call never returns and would
// leave us thinking we didn't reveal yet. // Now attempt the co-signing of the txn.
sweepTx, err := s.createMuSig2SweepTxn(
ctx, htlcOutpoint, htlcValue, fee,
)
if err != nil {
s.log.Warnf("Failed to create MuSig2 sweep txn: %v", err)
return false
}
// Finally, try publish the txn.
s.log.Infof("Sweep on chain HTLC using MuSig2 to address %v "+
"fee %v (tx %v)", s.DestAddr, fee, sweepTx.TxHash())
err = s.lnd.WalletKit.PublishTransaction(
ctx, sweepTx,
labels.LoopOutSweepSuccess(swap.ShortHash(&s.hash)),
)
if err != nil {
s.log.Warnf("Publish of MuSig2 sweep failed: %v", err)
return false
}
return true
}
func (s *loopOutSwap) setStatePreimageRevealed(ctx context.Context) error {
if s.state != loopdb.StatePreimageRevealed { if s.state != loopdb.StatePreimageRevealed {
s.state = loopdb.StatePreimageRevealed s.state = loopdb.StatePreimageRevealed
@ -1318,6 +1574,60 @@ func (s *loopOutSwap) sweep(ctx context.Context,
} }
} }
return nil
}
// sweep tries to sweep the given htlc to a destination address. It takes into
// account the max miner fee and unless the preimage is already revealed
// (MuSig2 case), marks the preimage as revealed when it published the tx. If
// the preimage has not yet been revealed, and the time during which we can
// safely reveal it has passed, the swap will be marked as failed, and the
// function will return.
func (s *loopOutSwap) sweep(ctx context.Context, htlcOutpoint wire.OutPoint,
htlcValue btcutil.Amount) error {
confTarget, canSweep := s.sweepConfTarget()
if !canSweep {
return nil
}
fee, err := s.sweeper.GetSweepFee(
ctx, s.htlc.AddSuccessToEstimator, s.DestAddr, confTarget,
)
if err != nil {
return err
}
fee, canSweep = s.clampSweepFee(fee)
if !canSweep {
return nil
}
witnessFunc := func(sig []byte) (wire.TxWitness, error) {
return s.htlc.GenSuccessWitness(sig, s.Preimage)
}
// Retrieve the full script required to unlock the output.
redeemScript := s.htlc.SuccessScript()
// 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,
)
if err != nil {
return err
}
// Before publishing the tx, already mark the preimage as revealed. This
// is a precaution in case the publish call never returns and would
// leave us thinking we didn't reveal yet.
err = s.setStatePreimageRevealed(ctx)
if err != nil {
return err
}
// Publish tx. // Publish tx.
s.log.Infof("Sweep on chain HTLC to address %v with fee %v (tx %v)", s.log.Infof("Sweep on chain HTLC to address %v with fee %v (tx %v)",
s.DestAddr, fee, sweepTx.TxHash()) s.DestAddr, fee, sweepTx.TxHash())
@ -1351,8 +1661,8 @@ func validateLoopOutContract(lnd *lndclient.LndServices,
if swapInvoiceHash != swapHash { if swapInvoiceHash != swapHash {
return fmt.Errorf( return fmt.Errorf(
"cannot initiate swap, swap invoice hash %v not equal generated swap hash %v", "cannot initiate swap, swap invoice hash %v not equal "+
swapInvoiceHash, swapHash) "generated swap hash %v", swapInvoiceHash, swapHash)
} }
_, _, _, prepayInvoiceAmt, err := swap.DecodeInvoice( _, _, _, prepayInvoiceAmt, err := swap.DecodeInvoice(

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
@ -24,6 +25,21 @@ import (
// TestLoopOutPaymentParameters tests the first part of the loop out process up // TestLoopOutPaymentParameters tests the first part of the loop out process up
// to the point where the off-chain payments are made. // to the point where the off-chain payments are made.
func TestLoopOutPaymentParameters(t *testing.T) { func TestLoopOutPaymentParameters(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testLoopOutPaymentParameters(t)
})
t.Run("experimental protocol", func(t *testing.T) {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
testLoopOutPaymentParameters(t)
})
}
// TestLoopOutPaymentParameters tests the first part of the loop out process up
// to the point where the off-chain payments are made.
func testLoopOutPaymentParameters(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
// Set up test context objects. // Set up test context objects.
@ -72,12 +88,13 @@ func TestLoopOutPaymentParameters(t *testing.T) {
go func() { go func() {
err := swap.execute(swapCtx, &executeConfig{ err := swap.execute(swapCtx, &executeConfig{
statusChan: statusChan, statusChan: statusChan,
sweeper: sweeper, sweeper: sweeper,
blockEpochChan: blockEpochChan, blockEpochChan: blockEpochChan,
timerFactory: timerFactory, timerFactory: timerFactory,
loopOutMaxParts: maxParts, loopOutMaxParts: maxParts,
cancelSwap: server.CancelLoopOutSwap, cancelSwap: server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigFail,
}, height) }, height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -144,6 +161,19 @@ func TestLoopOutPaymentParameters(t *testing.T) {
// TestLateHtlcPublish tests that the client is not revealing the preimage if // TestLateHtlcPublish tests that the client is not revealing the preimage if
// there are not enough blocks left. // there are not enough blocks left.
func TestLateHtlcPublish(t *testing.T) { func TestLateHtlcPublish(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testLateHtlcPublish(t)
})
t.Run("experimental protocol", func(t *testing.T) {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
testLateHtlcPublish(t)
})
}
func testLateHtlcPublish(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
lnd := test.NewMockLnd() lnd := test.NewMockLnd()
@ -181,11 +211,12 @@ func TestLateHtlcPublish(t *testing.T) {
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := swap.execute(context.Background(), &executeConfig{ err := swap.execute(context.Background(), &executeConfig{
statusChan: statusChan, statusChan: statusChan,
sweeper: sweeper, sweeper: sweeper,
blockEpochChan: blockEpochChan, blockEpochChan: blockEpochChan,
timerFactory: timerFactory, timerFactory: timerFactory,
cancelSwap: server.CancelLoopOutSwap, cancelSwap: server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigFail,
}, height) }, height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -232,6 +263,19 @@ func TestLateHtlcPublish(t *testing.T) {
// TestCustomSweepConfTarget ensures we are able to sweep a Loop Out HTLC with a // TestCustomSweepConfTarget ensures we are able to sweep a Loop Out HTLC with a
// custom confirmation target. // custom confirmation target.
func TestCustomSweepConfTarget(t *testing.T) { func TestCustomSweepConfTarget(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testCustomSweepConfTarget(t)
})
t.Run("experimental protocol", func(t *testing.T) {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
testCustomSweepConfTarget(t)
})
}
func testCustomSweepConfTarget(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
lnd := test.NewMockLnd() lnd := test.NewMockLnd()
@ -279,11 +323,12 @@ func TestCustomSweepConfTarget(t *testing.T) {
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := swap.execute(context.Background(), &executeConfig{ err := swap.execute(context.Background(), &executeConfig{
statusChan: statusChan, statusChan: statusChan,
blockEpochChan: blockEpochChan, blockEpochChan: blockEpochChan,
timerFactory: timerFactory, timerFactory: timerFactory,
sweeper: sweeper, sweeper: sweeper,
cancelSwap: server.CancelLoopOutSwap, cancelSwap: server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigFail,
}, ctx.Lnd.Height) }, ctx.Lnd.Height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -330,7 +375,9 @@ func TestCustomSweepConfTarget(t *testing.T) {
expiryChan <- time.Now() expiryChan <- time.Now()
// Expect a signing request for the HTLC success transaction. // Expect a signing request for the HTLC success transaction.
<-ctx.Lnd.SignOutputRawChannel if !IsTaprootSwap(&swap.SwapContract) {
<-ctx.Lnd.SignOutputRawChannel
}
cfg.store.(*storeMock).assertLoopOutState(loopdb.StatePreimageRevealed) cfg.store.(*storeMock).assertLoopOutState(loopdb.StatePreimageRevealed)
status := <-statusChan status := <-statusChan
@ -339,6 +386,24 @@ func TestCustomSweepConfTarget(t *testing.T) {
loopdb.StatePreimageRevealed, status.State) loopdb.StatePreimageRevealed, status.State)
} }
// When using taproot htlcs the flow is different as we do reveal the
// preimage before sweeping in order for the server to trust us with
// our MuSig2 signing attempts.
if IsTaprootSwap(&swap.SwapContract) {
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
// Try MuSig2 signing first and fail it so that we go for a
// normal sweep.
for i := 0; i < maxMusigSweepRetries; i++ {
expiryChan <- time.Now()
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
<-ctx.Lnd.SignOutputRawChannel
}
// assertSweepTx performs some sanity checks on a sweep transaction to // assertSweepTx performs some sanity checks on a sweep transaction to
// ensure it was constructed correctly. // ensure it was constructed correctly.
assertSweepTx := func(expConfTarget int32) *wire.MsgTx { assertSweepTx := func(expConfTarget int32) *wire.MsgTx {
@ -382,8 +447,10 @@ func TestCustomSweepConfTarget(t *testing.T) {
// Once we have published an on chain sweep, we expect a preimage to // Once we have published an on chain sweep, we expect a preimage to
// have been pushed to our server. // have been pushed to our server.
preimage := <-server.preimagePush if !IsTaprootSwap(&swap.SwapContract) {
require.Equal(t, swap.Preimage, preimage) preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
// Now that we have pushed our preimage to the sever, we send an update // Now that we have pushed our preimage to the sever, we send an update
// indicating that our off chain htlc is settled. We do this so that // indicating that our off chain htlc is settled. We do this so that
@ -433,6 +500,19 @@ func TestCustomSweepConfTarget(t *testing.T) {
// to start with a fee rate that will be too high, then progress to an // to start with a fee rate that will be too high, then progress to an
// acceptable one. // acceptable one.
func TestPreimagePush(t *testing.T) { func TestPreimagePush(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testPreimagePush(t)
})
t.Run("experimental protocol", func(t *testing.T) {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
testPreimagePush(t)
})
}
func testPreimagePush(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
lnd := test.NewMockLnd() lnd := test.NewMockLnd()
@ -474,11 +554,12 @@ func TestPreimagePush(t *testing.T) {
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := swap.execute(context.Background(), &executeConfig{ err := swap.execute(context.Background(), &executeConfig{
statusChan: statusChan, statusChan: statusChan,
blockEpochChan: blockEpochChan, blockEpochChan: blockEpochChan,
timerFactory: timerFactory, timerFactory: timerFactory,
sweeper: sweeper, sweeper: sweeper,
cancelSwap: server.CancelLoopOutSwap, cancelSwap: server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigFail,
}, ctx.Lnd.Height) }, ctx.Lnd.Height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -526,6 +607,41 @@ func TestPreimagePush(t *testing.T) {
// preimage is not revealed, we also do not expect a preimage push. // preimage is not revealed, we also do not expect a preimage push.
expiryChan <- testTime expiryChan <- testTime
// When using taproot htlcs the flow is different as we do reveal the
// preimage before sweeping in order for the server to trust us with
// our MuSig2 signing attempts.
if IsTaprootSwap(&swap.SwapContract) {
cfg.store.(*storeMock).assertLoopOutState(
loopdb.StatePreimageRevealed,
)
status := <-statusChan
require.Equal(
t, status.State, loopdb.StatePreimageRevealed,
)
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
// Try MuSig2 signing first and fail it so that we go for a
// normal sweep.
for i := 0; i < maxMusigSweepRetries; i++ {
expiryChan <- time.Now()
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
<-ctx.Lnd.SignOutputRawChannel
// We expect the sweep tx to have been published.
ctx.ReceiveTx()
}
// Since we don't have a reliable mechanism to non-intrusively avoid
// races by setting the fee estimate too soon, let's sleep here a bit
// to ensure the first sweep fails.
time.Sleep(500 * time.Millisecond)
// Now we decrease our fees for the swap's confirmation target to less // Now we decrease our fees for the swap's confirmation target to less
// than the maximum miner fee. // than the maximum miner fee.
ctx.Lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight( ctx.Lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight(
@ -537,30 +653,45 @@ func TestPreimagePush(t *testing.T) {
blockEpochChan <- ctx.Lnd.Height + 2 blockEpochChan <- ctx.Lnd.Height + 2
expiryChan <- testTime expiryChan <- testTime
if IsTaprootSwap(&swap.SwapContract) {
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
// Expect a signing request for the HTLC success transaction. // Expect a signing request for the HTLC success transaction.
<-ctx.Lnd.SignOutputRawChannel <-ctx.Lnd.SignOutputRawChannel
// This is the first time we have swept, so we expect our preimage if !IsTaprootSwap(&swap.SwapContract) {
// revealed state to be set. // This is the first time we have swept, so we expect our
cfg.store.(*storeMock).assertLoopOutState(loopdb.StatePreimageRevealed) // preimage revealed state to be set.
status := <-statusChan cfg.store.(*storeMock).assertLoopOutState(
require.Equal( loopdb.StatePreimageRevealed,
t, status.State, loopdb.StatePreimageRevealed, )
) status := <-statusChan
require.Equal(
t, status.State, loopdb.StatePreimageRevealed,
)
}
// We expect the sweep tx to have been published. // We expect the sweep tx to have been published.
ctx.ReceiveTx() ctx.ReceiveTx()
// Once we have published an on chain sweep, we expect a preimage to if !IsTaprootSwap(&swap.SwapContract) {
// have been pushed to the server after the sweep. // Once we have published an on chain sweep, we expect a
preimage := <-server.preimagePush // preimage to have been pushed to the server after the sweep.
require.Equal(t, swap.Preimage, preimage) preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
// To mock a server failure, we do not send a payment settled update // To mock a server failure, we do not send a payment settled update
// for our off chain payment yet. We also do not confirm our sweep on // for our off chain payment yet. We also do not confirm our sweep on
// chain yet so we can test our preimage push retry logic. Instead, we // chain yet so we can test our preimage push retry logic. Instead, we
// tick the expiry chan again to prompt another sweep. // tick the expiry chan again to prompt another sweep.
expiryChan <- testTime expiryChan <- testTime
if IsTaprootSwap(&swap.SwapContract) {
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
// We expect another signing request for out sweep, and publish of our // We expect another signing request for out sweep, and publish of our
// sweep transaction. // sweep transaction.
@ -570,8 +701,11 @@ func TestPreimagePush(t *testing.T) {
// Since we have not yet been notified of an off chain settle, and we // Since we have not yet been notified of an off chain settle, and we
// have attempted to sweep again, we expect another preimage push // have attempted to sweep again, we expect another preimage push
// attempt. // attempt.
preimage = <-server.preimagePush
require.Equal(t, swap.Preimage, preimage) if !IsTaprootSwap(&swap.SwapContract) {
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
}
// This time, we send a payment succeeded update into our payment stream // This time, we send a payment succeeded update into our payment stream
// to reflect that the server received our preimage push and settled off // to reflect that the server received our preimage push and settled off
@ -592,7 +726,7 @@ func TestPreimagePush(t *testing.T) {
ctx.NotifySpend(sweepTx, 0) ctx.NotifySpend(sweepTx, 0)
cfg.store.(*storeMock).assertLoopOutState(loopdb.StateSuccess) cfg.store.(*storeMock).assertLoopOutState(loopdb.StateSuccess)
status = <-statusChan status := <-statusChan
require.Equal( require.Equal(
t, status.State, loopdb.StateSuccess, t, status.State, loopdb.StateSuccess,
) )
@ -604,6 +738,18 @@ func TestPreimagePush(t *testing.T) {
// we have revealed our preimage, demonstrating that we do not reveal our // we have revealed our preimage, demonstrating that we do not reveal our
// preimage once we've reached our expiry height. // preimage once we've reached our expiry height.
func TestExpiryBeforeReveal(t *testing.T) { func TestExpiryBeforeReveal(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testExpiryBeforeReveal(t)
})
// Note that there's no point of testing this case with the new
// protocol where we use taproot htlc and attempt MuSig2 sweep. The
// reason is that the preimage is revealed to the server once the
// htlc is confirmed in order to facilitate the cooperative signing of
// the sweep transaction.
}
func testExpiryBeforeReveal(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
lnd := test.NewMockLnd() lnd := test.NewMockLnd()
@ -642,10 +788,11 @@ func TestExpiryBeforeReveal(t *testing.T) {
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
err := swap.execute(context.Background(), &executeConfig{ err := swap.execute(context.Background(), &executeConfig{
statusChan: statusChan, statusChan: statusChan,
blockEpochChan: blockEpochChan, blockEpochChan: blockEpochChan,
timerFactory: timerFactory, timerFactory: timerFactory,
sweeper: sweeper, sweeper: sweeper,
verifySchnorrSig: mockVerifySchnorrSigFail,
}, ctx.Lnd.Height) }, ctx.Lnd.Height)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -692,6 +839,11 @@ func TestExpiryBeforeReveal(t *testing.T) {
// won't attempt a sweep at this point. // won't attempt a sweep at this point.
expiryChan <- testTime expiryChan <- testTime
// Since we don't have a reliable mechanism to non-intrusively avoid
// races by setting the fee estimate too soon, let's sleep here a bit
// to ensure the first sweep fails.
time.Sleep(500 * time.Millisecond)
// Now we decrease our conf target to less than our max miner fee. // Now we decrease our conf target to less than our max miner fee.
lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight( lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight(
testReq.MaxMinerFee/2, testReq.MaxMinerFee/2,
@ -719,6 +871,19 @@ func TestExpiryBeforeReveal(t *testing.T) {
// TestFailedOffChainCancelation tests sending of a cancelation message to // TestFailedOffChainCancelation tests sending of a cancelation message to
// the server when a swap fails due to off-chain routing. // the server when a swap fails due to off-chain routing.
func TestFailedOffChainCancelation(t *testing.T) { func TestFailedOffChainCancelation(t *testing.T) {
t.Run("stable protocol", func(t *testing.T) {
testFailedOffChainCancelation(t)
})
t.Run("experimental protocol", func(t *testing.T) {
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
testFailedOffChainCancelation(t)
})
}
func testFailedOffChainCancelation(t *testing.T) {
defer test.Guard(t)() defer test.Guard(t)()
lnd := test.NewMockLnd() lnd := test.NewMockLnd()
@ -750,11 +915,12 @@ func TestFailedOffChainCancelation(t *testing.T) {
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
cfg := &executeConfig{ cfg := &executeConfig{
statusChan: statusChan, statusChan: statusChan,
sweeper: sweeper, sweeper: sweeper,
blockEpochChan: blockEpochChan, blockEpochChan: blockEpochChan,
timerFactory: timerFactory, timerFactory: timerFactory,
cancelSwap: server.CancelLoopOutSwap, cancelSwap: server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigFail,
} }
err := swap.execute(context.Background(), cfg, ctx.Lnd.Height) err := swap.execute(context.Background(), cfg, ctx.Lnd.Height)
@ -852,3 +1018,174 @@ func TestFailedOffChainCancelation(t *testing.T) {
require.Equal(t, state.State, loopdb.StateFailOffchainPayments) require.Equal(t, state.State, loopdb.StateFailOffchainPayments)
require.NoError(t, <-errChan) require.NoError(t, <-errChan)
} }
// TestLoopOutMuSig2Sweep tests the loop out sweep flow when the MuSig2 signing
// process is successful.
func TestLoopOutMuSig2Sweep(t *testing.T) {
defer test.Guard(t)()
// TODO(bhandras): remove when MuSig2 is default.
loopdb.EnableExperimentalProtocol()
defer loopdb.ResetCurrentProtocolVersion()
lnd := test.NewMockLnd()
ctx := test.NewContext(t, lnd)
server := newServerMock(lnd)
testReq := *testRequest
testReq.SweepConfTarget = 10
testReq.Expiry = ctx.Lnd.Height + testLoopOutMinOnChainCltvDelta
// We set our mock fee estimate for our target sweep confs to be our
// max miner fee * 2. With MuSig2 we still expect that the client will
// publish the sweep but with the fee clamped to the maximum allowed
// miner fee as the preimage is revealed before the sweep txn is
// published.
ctx.Lnd.SetFeeEstimate(
testReq.SweepConfTarget, chainfee.SatPerKWeight(
testReq.MaxMinerFee*2,
),
)
cfg := newSwapConfig(
&lnd.LndServices, newStoreMock(t), server,
)
initResult, err := newLoopOutSwap(
context.Background(), cfg, ctx.Lnd.Height, &testReq,
)
require.NoError(t, err)
swap := initResult.swap
// Set up the required dependencies to execute the swap.
sweeper := &sweep.Sweeper{Lnd: &lnd.LndServices}
blockEpochChan := make(chan interface{})
statusChan := make(chan SwapInfo)
expiryChan := make(chan time.Time)
timerFactory := func(_ time.Duration) <-chan time.Time {
return expiryChan
}
errChan := make(chan error)
// Mock a successful signature verify to make sure we don't fail
// creating the MuSig2 sweep.
mockVerifySchnorrSigSuccess := func(pubKey *btcec.PublicKey, hash,
sig []byte) error {
return nil
}
go func() {
err := swap.execute(context.Background(), &executeConfig{
statusChan: statusChan,
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
sweeper: sweeper,
cancelSwap: server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigSuccess,
}, ctx.Lnd.Height)
if err != nil {
log.Error(err)
}
errChan <- err
}()
// The swap should be found in its initial state.
cfg.store.(*storeMock).assertLoopOutStored()
state := <-statusChan
require.Equal(t, loopdb.StateInitiated, state.State)
// We'll then pay both the swap and prepay invoice, which should trigger
// the server to publish the on-chain HTLC.
signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
signalSwapPaymentResult(nil)
signalPrepaymentResult(nil)
// Notify the confirmation notification for the HTLC.
ctx.AssertRegisterConf(false, defaultConfirmations)
blockEpochChan <- ctx.Lnd.Height + 1
htlcTx := wire.NewMsgTx(2)
htlcTx.AddTxOut(&wire.TxOut{
Value: int64(swap.AmountRequested),
PkScript: swap.htlc.PkScript,
})
ctx.NotifyConf(htlcTx)
// The client should then register for a spend of the HTLC and attempt
// to sweep it using the custom confirmation target.
ctx.AssertRegisterSpendNtfn(swap.htlc.PkScript)
// Assert that we made a query to track our payment, as required for
// preimage push tracking.
trackPayment := ctx.AssertTrackPayment()
// Tick the expiry channel, we are still using our client confirmation
// target at this stage which has fees higher than our max acceptable
// fee. We do not expect a sweep attempt at this point. Since our
// preimage is not revealed, we also do not expect a preimage push.
expiryChan <- testTime
// When using taproot htlcs the flow is different as we do reveal the
// preimage before sweeping in order for the server to trust us with
// our MuSig2 signing attempts.
cfg.store.(*storeMock).assertLoopOutState(
loopdb.StatePreimageRevealed,
)
status := <-statusChan
require.Equal(
t, status.State, loopdb.StatePreimageRevealed,
)
preimage := <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
// We expect the sweep tx to have been published.
ctx.ReceiveTx()
// Since we don't have a reliable mechanism to non-intrusively avoid
// races by setting the fee estimate too soon, let's sleep here a bit
// to ensure the first sweep fails.
time.Sleep(500 * time.Millisecond)
// Now we decrease our fees for the swap's confirmation target to less
// than the maximum miner fee.
ctx.Lnd.SetFeeEstimate(testReq.SweepConfTarget, chainfee.SatPerKWeight(
testReq.MaxMinerFee/2,
))
// Now when we report a new block and tick our expiry fee timer, and
// fees are acceptably low so we expect our sweep to be published.
blockEpochChan <- ctx.Lnd.Height + 2
expiryChan <- testTime
preimage = <-server.preimagePush
require.Equal(t, swap.Preimage, preimage)
// We expect the sweep tx to have been published.
sweepTx := ctx.ReceiveTx()
// This time, we send a payment succeeded update into our payment stream
// to reflect that the server received our preimage push and settled off
// chain.
trackPayment.Updates <- lndclient.PaymentStatus{
State: lnrpc.Payment_SUCCEEDED,
}
// Make sure our sweep tx has a single witness indicating keyspend.
require.Len(t, sweepTx.TxIn[0].Witness, 1)
// Finally, we put this swap out of its misery and notify a successful
// spend our our sweepTx and assert that the swap succeeds.
ctx.NotifySpend(sweepTx, 0)
cfg.store.(*storeMock).assertLoopOutState(loopdb.StateSuccess)
status = <-statusChan
require.Equal(t, status.State, loopdb.StateSuccess)
require.NoError(t, <-errChan)
}

@ -811,6 +811,8 @@ type SwapResponse struct {
//The native segwit address of the on-chain htlc. //The native segwit address of the on-chain htlc.
//Used for both loop-in and loop-out. //Used for both loop-in and loop-out.
HtlcAddressP2Wsh string `protobuf:"bytes,5,opt,name=htlc_address_p2wsh,json=htlcAddressP2wsh,proto3" json:"htlc_address_p2wsh,omitempty"` HtlcAddressP2Wsh string `protobuf:"bytes,5,opt,name=htlc_address_p2wsh,json=htlcAddressP2wsh,proto3" json:"htlc_address_p2wsh,omitempty"`
// The address of the v3 (taproot) htlc. Used for both loop-in and loop-out.
HtlcAddressP2Tr string `protobuf:"bytes,7,opt,name=htlc_address_p2tr,json=htlcAddressP2tr,proto3" json:"htlc_address_p2tr,omitempty"`
// A human-readable message received from the loop server. // A human-readable message received from the loop server.
ServerMessage string `protobuf:"bytes,6,opt,name=server_message,json=serverMessage,proto3" json:"server_message,omitempty"` ServerMessage string `protobuf:"bytes,6,opt,name=server_message,json=serverMessage,proto3" json:"server_message,omitempty"`
} }
@ -884,6 +886,13 @@ func (x *SwapResponse) GetHtlcAddressP2Wsh() string {
return "" return ""
} }
func (x *SwapResponse) GetHtlcAddressP2Tr() string {
if x != nil {
return x.HtlcAddressP2Tr
}
return ""
}
func (x *SwapResponse) GetServerMessage() string { func (x *SwapResponse) GetServerMessage() string {
if x != nil { if x != nil {
return x.ServerMessage return x.ServerMessage
@ -980,6 +989,8 @@ type SwapStatus struct {
HtlcAddressP2Wsh string `protobuf:"bytes,12,opt,name=htlc_address_p2wsh,json=htlcAddressP2wsh,proto3" json:"htlc_address_p2wsh,omitempty"` HtlcAddressP2Wsh string `protobuf:"bytes,12,opt,name=htlc_address_p2wsh,json=htlcAddressP2wsh,proto3" json:"htlc_address_p2wsh,omitempty"`
// HTLC address (nested segwit), used in loop-in swaps only. // HTLC address (nested segwit), used in loop-in swaps only.
HtlcAddressNp2Wsh string `protobuf:"bytes,13,opt,name=htlc_address_np2wsh,json=htlcAddressNp2wsh,proto3" json:"htlc_address_np2wsh,omitempty"` HtlcAddressNp2Wsh string `protobuf:"bytes,13,opt,name=htlc_address_np2wsh,json=htlcAddressNp2wsh,proto3" json:"htlc_address_np2wsh,omitempty"`
// The address of the v3 (taproot) htlc. Used for both loop-in and loop-out.
HtlcAddressP2Tr string `protobuf:"bytes,18,opt,name=htlc_address_p2tr,json=htlcAddressP2tr,proto3" json:"htlc_address_p2tr,omitempty"`
// Swap server cost // Swap server cost
CostServer int64 `protobuf:"varint,8,opt,name=cost_server,json=costServer,proto3" json:"cost_server,omitempty"` CostServer int64 `protobuf:"varint,8,opt,name=cost_server,json=costServer,proto3" json:"cost_server,omitempty"`
// On-chain transaction cost // On-chain transaction cost
@ -1105,6 +1116,13 @@ func (x *SwapStatus) GetHtlcAddressNp2Wsh() string {
return "" return ""
} }
func (x *SwapStatus) GetHtlcAddressP2Tr() string {
if x != nil {
return x.HtlcAddressP2Tr
}
return ""
}
func (x *SwapStatus) GetCostServer() int64 { func (x *SwapStatus) GetCostServer() int64 {
if x != nil { if x != nil {
return x.CostServer return x.CostServer
@ -2787,7 +2805,7 @@ var file_client_proto_rawDesc = []byte{
0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73,
0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xe9, 0x01, 0x0a, 0x0c, 0x53, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x0c, 0x53,
0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x02, 0x69, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12,
0x19, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x19, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
@ -2800,370 +2818,376 @@ var file_client_proto_rawDesc = []byte{
0x68, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x68, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x5f, 0x70, 0x32, 0x77, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x68, 0x73, 0x5f, 0x70, 0x32, 0x77, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x68,
0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, 0x68, 0x12, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, 0x68, 0x12,
0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x70, 0x32, 0x74, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x74, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x73,
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xfb, 0x04, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20,
0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x12, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x67, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71,
0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa7, 0x05, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61,
0x08, 0x69, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
0x07, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x12, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x64, 0x5f,
0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x69, 0x64, 0x42,
0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61,
0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x73,
0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f,
0x0e, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05,
0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e,
0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52,
0x03, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65,
0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x69,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x69,
0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0c, 0x68, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x28, 0x0a,
0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64,
0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0c, 0x68, 0x74, 0x6c, 0x63, 0x5f,
0x73, 0x73, 0x5f, 0x70, 0x32, 0x77, 0x73, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18,
0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, 0x68, 0x01, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c,
0x12, 0x2e, 0x0a, 0x13, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70,
0x5f, 0x6e, 0x70, 0x32, 0x77, 0x73, 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x68, 0x32, 0x77, 0x73, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x68, 0x74, 0x6c, 0x63,
0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4e, 0x70, 0x32, 0x77, 0x73, 0x68, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x13,
0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x70, 0x32,
0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x77, 0x73, 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x41,
0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4e, 0x70, 0x32, 0x77, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11,
0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x6e, 0x63, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x32, 0x74,
0x68, 0x61, 0x69, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64,
0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x73, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x74, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x73, 0x74,
0x74, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63,
0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x73,
0x74, 0x48, 0x6f, 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x74, 0x5f, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52,
0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x11, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x23, 0x0a, 0x0d,
0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x65, 0x74, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x0a, 0x20,
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69,
0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x10, 0x20,
0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3e, 0x0a, 0x11, 0x4c, 0x69, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x2a, 0x0a, 0x11,
0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x65,
0x29, 0x0a, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x74, 0x18, 0x11, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65,
0x74, 0x75, 0x73, 0x52, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73, 0x22, 0x21, 0x0a, 0x0f, 0x53, 0x77, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x12,
0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x73, 0x74, 0x22, 0x3e, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52,
0x0c, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7f, 0x0a, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73,
0x0f, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x73, 0x77, 0x61,
0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x70, 0x73, 0x22, 0x21, 0x0a, 0x0f, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65,
0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7f, 0x0a, 0x0f, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f,
0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0xcc, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
0x01, 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f,
0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77,
0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04,
0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05,
0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0xcc, 0x01, 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x54, 0x65,
0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d,
0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05,
0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f,
0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f,
0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61,
0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6d,
0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0xa8, 0x02, 0x69, 0x6e, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x08, 0x20,
0x0a, 0x0c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74,
0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65,
0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x6c, 0x74, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x43, 0x6c,
0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08,
0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a,
0x6c, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0xa8, 0x02, 0x0a, 0x0c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52,
0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x73, 0x77, 0x61, 0x70, 0x50, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74,
0x6e, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x6f, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x3a,
0x6f, 0x70, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x41, 0x0a, 0x13, 0x6c, 0x0a, 0x19, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x04, 0x52, 0x17, 0x73, 0x77, 0x61, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x6c, 0x6f, 0x6f, 0x6e, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6c, 0x6f,
0x6f, 0x70, 0x49, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x05,
0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74,
0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x51, 0x48, 0x6f, 0x70, 0x12, 0x41, 0x0a, 0x13, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x72,
0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x28, 0x03, 0x52, 0x0a, 0x73, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2f, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x6f, 0x75, 0x74,
0x0a, 0x14, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x66, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x68, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
0x6c, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x22, 0xb0, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65,
0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1f, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x77, 0x61, 0x70,
0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2f, 0x0a, 0x14, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x70,
0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4a, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03,
0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0xf3, 0x01, 0x0a, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x68, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f,
0x12, 0x20, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74,
0x61, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x61, 0x6d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e,
0x5f, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x70, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08,
0x61, 0x79, 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x04, 0x10, 0x05, 0x22, 0xf3, 0x01, 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65,
0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70,
0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x53, 0x77, 0x65, 0x65, 0x70, 0x46, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72,
0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01,
0x52, 0x0f, 0x73, 0x77, 0x61, 0x70, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74,
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x12, 0x2b, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66,
0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x68, 0x74,
0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x6c, 0x63, 0x53, 0x77, 0x65, 0x65, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2a, 0x0a,
0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x11, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65,
0x74, 0x22, 0x70, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x77, 0x61, 0x70, 0x50, 0x61,
0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74,
0x61, 0x6d, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x33, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66,
0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63,
0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x70, 0x0a, 0x0c, 0x50, 0x72, 0x6f,
0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74,
0x6e, 0x74, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6c,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3c, 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x33, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52,
0x63, 0x2e, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x50,
0x65, 0x6e, 0x73, 0x22, 0xbb, 0x02, 0x0a, 0x09, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d,
0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3c, 0x0a,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x62, 0x61, 0x73, 0x65, 0x4d, 0x61, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f,
0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0xbb, 0x02, 0x0a, 0x09,
0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73,
0x01, 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x69, 0x65, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x6d, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x52, 0x0c, 0x62, 0x61, 0x73, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x21,
0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02,
0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73,
0x0a, 0x15, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x61, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65,
0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x72, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x61, 0x79,
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10,
0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74,
0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61,
0x61, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e,
0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x12, 0x21, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18,
0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d,
0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52,
0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07,
0x06, 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65,
0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74,
0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65, 0x74,
0x75, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52,
0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x3d, 0x0a, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8a, 0x06, 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75, 0x69,
0x1c, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c,
0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e,
0x01, 0x28, 0x04, 0x52, 0x17, 0x73, 0x77, 0x65, 0x65, 0x70, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74,
0x65, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07,
0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66,
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x3d, 0x0a, 0x1c, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66,
0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x75, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f,
0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x73, 0x77, 0x65,
0x28, 0x04, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x70, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56,
0x65, 0x50, 0x70, 0x6d, 0x12, 0x3a, 0x0a, 0x1a, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x62, 0x79, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70,
0x61, 0x79, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d,
0x70, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x2d, 0x0a,
0x70, 0x61, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65,
0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x73, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52,
0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x3a, 0x0a, 0x1a,
0x70, 0x61, 0x79, 0x53, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x69, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69,
0x6e, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04,
0x04, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x65, 0x65, 0x53, 0x61, 0x52, 0x16, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x69,
0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x73, 0x77, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04,
0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x53, 0x61, 0x74, 0x12, 0x29,
0x13, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x5f,
0x5f, 0x73, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x4d, 0x69,
0x75, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x53, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x6e, 0x65, 0x72, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x77, 0x65,
0x08, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x65, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x08,
0x08, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x54,
0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01,
0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, 0x61, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x61, 0x75, 0x74, 0x28, 0x04, 0x52, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x6f,
0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x66, 0x66, 0x53, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f,
0x72, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x61, 0x75, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f,
0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75,
0x74, 0x53, 0x65, 0x63, 0x12, 0x2b, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x78, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11,
0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, 0x61,
0x52, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x4d, 0x61, 0x78, 0x49, 0x6e, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75,
0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x0c,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75,
0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x64, 0x67, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x63, 0x12, 0x2b, 0x0a, 0x12,
0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67,
0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x68, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x4d, 0x61,
0x74, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x78, 0x49, 0x6e, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e,
0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x74, 0x6c, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01,
0x63, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x84, 0x02, 0x0a, 0x0d, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e,
0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d,
0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53,
0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x09, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x74, 0x6c,
0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x11, 0x20,
0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72,
0x70, 0x65, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x74, 0x22, 0x84, 0x02, 0x0a, 0x0d, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74,
0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x62, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e,
0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, 0x79, 0x70,
0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70,
0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05,
0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04,
0x6f, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f,
0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75,
0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x12,
0x6c, 0x64, 0x22, 0x59, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f,
0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69,
0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6f,
0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c,
0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e,
0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x1c, 0x0a, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x59, 0x0a, 0x19, 0x53, 0x65,
0x1a, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73,
0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d,
0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f,
0x73, 0x74, 0x22, 0x72, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50,
0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d,
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75,
0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77,
0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x72, 0x0a, 0x0c, 0x44, 0x69,
0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68,
0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xb6, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x67, 0x67, 0x65, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62,
0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65,
0x79, 0x12, 0x2b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x74, 0x6f,
0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xb6,
0x01, 0x0a, 0x14, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x6f, 0x70, 0x5f,
0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x52, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6c,
0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x52, 0x06, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x39, 0x0a, 0x0c,
0x64, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73,
0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x71, 0x75,
0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2a, 0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x54,
0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10,
0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73,
0x0a, 0x09, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49,
0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50, 0x52,
0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44, 0x10,
0x01, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53,
0x48, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53,
0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13,
0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45,
0x44, 0x10, 0x05, 0x2a, 0xed, 0x01, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b,
0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46,
0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49,
0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55,
0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f,
0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49,
0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55,
0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04,
0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53,
0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23,
0x0a, 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e,
0x54, 0x10, 0x06, 0x2a, 0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79,
0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e,
0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f,
0x4c, 0x44, 0x10, 0x01, 0x2a, 0xa6, 0x03, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53,
0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47,
0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01,
0x12, 0x1a, 0x0a, 0x16, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47,
0x45, 0x54, 0x5f, 0x45, 0x4c, 0x41, 0x50, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46,
0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10,
0x05, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x4d, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50,
0x41, 0x59, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b,
0x4f, 0x46, 0x46, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09, 0x12,
0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c,
0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54,
0x59, 0x5f, 0x4f, 0x4b, 0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53,
0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49,
0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0d, 0x32, 0xc2, 0x07,
0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07,
0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49,
0x6e, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70,
0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x39, 0x0a, 0x07, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c,
0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x39, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f,
0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x4f, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x18, 0x03, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f,
0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x06, 0x6c, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x6f, 0x70, 0x49, 0x6e, 0x12, 0x39, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54,
0x66, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c,
0x64, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2a, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75,
0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41,
0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73, 0x0a, 0x09, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75,
0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75,
0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f,
0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x2a, 0xed, 0x01, 0x0a, 0x0d, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e,
0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71,
0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50,
0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d,
0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12,
0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10,
0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54,
0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c,
0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f,
0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52,
0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45,
0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, 0x2a, 0x2f, 0x0a, 0x11, 0x4c,
0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a,
0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x01, 0x2a, 0xa6, 0x03, 0x0a,
0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x53,
0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x46, 0x45,
0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x45, 0x4c, 0x41, 0x50, 0x53,
0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x04, 0x12,
0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53,
0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54,
0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x46,
0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c,
0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x4f, 0x46, 0x46, 0x10, 0x08, 0x12, 0x18, 0x0a,
0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f,
0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x0a,
0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x4f, 0x4b, 0x10, 0x0b, 0x12, 0x23,
0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55,
0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e,
0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53,
0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49,
0x45, 0x4e, 0x54, 0x10, 0x0d, 0x32, 0xc2, 0x07, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x43, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12,
0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x4d, 0x6f, 0x6e, 0x69,
0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f,
0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73,
0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53,
0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x49,
0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77,
0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72,
0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72,
0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51,
0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51,
0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f,
0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74,
0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51,
0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05,
0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56,
0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61,
0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47,
0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61,
0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71,
0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64,
0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69,
0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5d, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73,
0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74,
0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75,
0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61,
0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69,
0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

@ -317,6 +317,9 @@ message SwapResponse {
*/ */
string htlc_address_p2wsh = 5; string htlc_address_p2wsh = 5;
// The address of the v3 (taproot) htlc. Used for both loop-in and loop-out.
string htlc_address_p2tr = 7;
// A human-readable message received from the loop server. // A human-readable message received from the loop server.
string server_message = 6; string server_message = 6;
} }
@ -387,6 +390,9 @@ message SwapStatus {
// HTLC address (nested segwit), used in loop-in swaps only. // HTLC address (nested segwit), used in loop-in swaps only.
string htlc_address_np2wsh = 13; string htlc_address_np2wsh = 13;
// The address of the v3 (taproot) htlc. Used for both loop-in and loop-out.
string htlc_address_p2tr = 18;
// Swap server cost // Swap server cost
int64 cost_server = 8; int64 cost_server = 8;

@ -1044,6 +1044,10 @@
"type": "string", "type": "string",
"description": "The native segwit address of the on-chain htlc.\nUsed for both loop-in and loop-out." "description": "The native segwit address of the on-chain htlc.\nUsed for both loop-in and loop-out."
}, },
"htlc_address_p2tr": {
"type": "string",
"description": "The address of the v3 (taproot) htlc. Used for both loop-in and loop-out."
},
"server_message": { "server_message": {
"type": "string", "type": "string",
"description": "A human-readable message received from the loop server." "description": "A human-readable message received from the loop server."
@ -1114,6 +1118,10 @@
"type": "string", "type": "string",
"description": "HTLC address (nested segwit), used in loop-in swaps only." "description": "HTLC address (nested segwit), used in loop-in swaps only."
}, },
"htlc_address_p2tr": {
"type": "string",
"description": "The address of the v3 (taproot) htlc. Used for both loop-in and loop-out."
},
"cost_server": { "cost_server": {
"type": "string", "type": "string",
"format": "int64", "format": "int64",

@ -16,6 +16,8 @@ This file tracks release notes for the loop client.
#### New Features #### New Features
* P2TR HTLCs and privacy preserving and cheaper MuSig2 loopout sweeps are now supported as an experimental feature when running `loopd` with the`--experimental` flag.
#### Breaking Changes #### Breaking Changes
#### Bug Fixes #### Bug Fixes

@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/test" "github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
@ -253,3 +254,9 @@ func (s *serverMock) ReportRoutingResult(_ context.Context, _ lntypes.Hash,
return nil return nil
} }
func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion,
_ lntypes.Hash, _ [32]byte, _ []byte, _ []byte) ([]byte, []byte, error) {
return nil, nil, nil
}

@ -53,18 +53,30 @@ func newSwapKit(hash lntypes.Hash, swapType swap.Type, cfg *swapConfig,
func GetHtlcScriptVersion( func GetHtlcScriptVersion(
protocolVersion loopdb.ProtocolVersion) swap.ScriptVersion { protocolVersion loopdb.ProtocolVersion) swap.ScriptVersion {
if protocolVersion != loopdb.ProtocolVersionUnrecorded && // Unrecorded protocol version implies that there was no protocol
protocolVersion >= loopdb.ProtocolVersionHtlcV2 { // version stored along side a serialized swap that we're resuming in
// which case the swap was initiated with HTLC v1 script.
// Use HTLC v2 script only if we know the swap was initiated if protocolVersion == loopdb.ProtocolVersionUnrecorded {
// with a client that supports HTLC v2. Unrecorded protocol return swap.HtlcV1
// version implies that there was no protocol version stored }
// along side a serialized swap that we're resuming in which
// case the swap was initiated with HTLC v1 script. // If the swap was initiated before we had our v2 script, use v1.
if protocolVersion < loopdb.ProtocolVersionHtlcV2 {
return swap.HtlcV1
}
// If the swap was initiated before we had our v3 script, use v2.
if protocolVersion < loopdb.ProtocolVersionHtlcV3 {
return swap.HtlcV2 return swap.HtlcV2
} }
return swap.HtlcV1 return swap.HtlcV3
}
// IsTaproot returns true if the swap referenced by the passed swap contract
// uses the v3 (taproot) htlc.
func IsTaprootSwap(swapContract *loopdb.SwapContract) bool {
return GetHtlcScriptVersion(swapContract.ProtocolVersion) == swap.HtlcV3
} }
// getHtlc composes and returns the on-chain swap script. // getHtlc composes and returns the on-chain swap script.

@ -108,17 +108,30 @@ type Htlc struct {
} }
var ( var (
quoteKey [33]byte // dummyPubKey is a valid public key use for the quote htlc
// construction.
dummyPubKey = [33]byte{
0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 0x09, 0xfb,
0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, 0x25, 0x21, 0x88,
0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 0x7f, 0x5b, 0x2a, 0x4b,
0x7d, 0x44, 0x8e,
}
// quoteHash is an empty hash used for the quote htlc construction.
quoteHash lntypes.Hash quoteHash lntypes.Hash
// QuoteHtlc is a template script just used for fee estimation. It uses // QuoteHtlcP2WSH is a template script just used for sweep fee
// the maximum value for cltv expiry to get the maximum (worst case) // estimation.
// script size. QuoteHtlcP2WSH, _ = NewHtlc(
QuoteHtlc, _ = NewHtlc( HtlcV2, ^int32(0), dummyPubKey, dummyPubKey, quoteHash,
HtlcV2, HtlcP2WSH, &chaincfg.MainNetParams,
^int32(0), quoteKey, quoteKey, quoteHash, HtlcP2WSH, )
&chaincfg.MainNetParams,
// QuoteHtlcP2TR is a template script just used for sweep fee
// estimation.
QuoteHtlcP2TR, _ = NewHtlc(
HtlcV3, ^int32(0), dummyPubKey, dummyPubKey, quoteHash,
HtlcP2TR, &chaincfg.MainNetParams,
) )
// ErrInvalidScriptVersion is returned when an unknown htlc version // ErrInvalidScriptVersion is returned when an unknown htlc version
@ -853,7 +866,9 @@ func (h *HtlcScriptV3) GenTimeoutWitness(
// IsSuccessWitness checks whether the given stack is valid for // IsSuccessWitness checks whether the given stack is valid for
// redeeming the htlc. // redeeming the htlc.
func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool { func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool {
return len(witness) == 4 // The witness has four elements if this is a script spend or one
// element if this is a keyspend.
return len(witness) == 4 || len(witness) == 1
} }
// TimeoutScript returns the redeem script required to unlock the htlc after // TimeoutScript returns the redeem script required to unlock the htlc after

@ -121,6 +121,13 @@ type swapServerClient interface {
swapHash lntypes.Hash, paymentAddr [32]byte, swapHash lntypes.Hash, paymentAddr [32]byte,
plugin RoutingPluginType, success bool, attempts int32, plugin RoutingPluginType, success bool, attempts int32,
totalTime int64) error totalTime int64) error
// MuSig2SignSweep calls the server to cooperatively sign the MuSig2
// htlc spend. Returns the server's nonce and partial signature.
MuSig2SignSweep(ctx context.Context,
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
paymentAddr [32]byte, nonce []byte, sigHash []byte) (
[]byte, []byte, error)
} }
type grpcSwapServerClient struct { type grpcSwapServerClient struct {
@ -174,7 +181,7 @@ func (s *grpcSwapServerClient) GetLoopOutTerms(ctx context.Context) (
defer rpcCancel() defer rpcCancel()
terms, err := s.server.LoopOutTerms(rpcCtx, terms, err := s.server.LoopOutTerms(rpcCtx,
&looprpc.ServerLoopOutTermsRequest{ &looprpc.ServerLoopOutTermsRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
}, },
) )
if err != nil { if err != nil {
@ -199,7 +206,7 @@ func (s *grpcSwapServerClient) GetLoopOutQuote(ctx context.Context,
&looprpc.ServerLoopOutQuoteRequest{ &looprpc.ServerLoopOutQuoteRequest{
Amt: uint64(amt), Amt: uint64(amt),
SwapPublicationDeadline: swapPublicationDeadline.Unix(), SwapPublicationDeadline: swapPublicationDeadline.Unix(),
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
Expiry: expiry, Expiry: expiry,
}, },
) )
@ -231,7 +238,7 @@ func (s *grpcSwapServerClient) GetLoopInTerms(ctx context.Context) (
defer rpcCancel() defer rpcCancel()
terms, err := s.server.LoopInTerms(rpcCtx, terms, err := s.server.LoopInTerms(rpcCtx,
&looprpc.ServerLoopInTermsRequest{ &looprpc.ServerLoopInTermsRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
}, },
) )
if err != nil { if err != nil {
@ -258,7 +265,7 @@ func (s *grpcSwapServerClient) GetLoopInQuote(ctx context.Context,
req := &looprpc.ServerLoopInQuoteRequest{ req := &looprpc.ServerLoopInQuoteRequest{
Amt: uint64(amt), Amt: uint64(amt),
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
Pubkey: pubKey[:], Pubkey: pubKey[:],
} }
@ -343,7 +350,7 @@ func (s *grpcSwapServerClient) Probe(ctx context.Context, amt btcutil.Amount,
req := &looprpc.ServerProbeRequest{ req := &looprpc.ServerProbeRequest{
Amt: uint64(amt), Amt: uint64(amt),
Target: target[:], Target: target[:],
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
RouteHints: rpcRouteHints, RouteHints: rpcRouteHints,
} }
@ -368,7 +375,7 @@ func (s *grpcSwapServerClient) NewLoopOutSwap(ctx context.Context,
Amt: uint64(amount), Amt: uint64(amount),
ReceiverKey: receiverKey[:], ReceiverKey: receiverKey[:],
SwapPublicationDeadline: swapPublicationDeadline.Unix(), SwapPublicationDeadline: swapPublicationDeadline.Unix(),
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
Expiry: expiry, Expiry: expiry,
UserAgent: UserAgent(initiator), UserAgent: UserAgent(initiator),
}, },
@ -403,7 +410,7 @@ func (s *grpcSwapServerClient) PushLoopOutPreimage(ctx context.Context,
_, err := s.server.LoopOutPushPreimage(rpcCtx, _, err := s.server.LoopOutPushPreimage(rpcCtx,
&looprpc.ServerLoopOutPushPreimageRequest{ &looprpc.ServerLoopOutPushPreimageRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
Preimage: preimage[:], Preimage: preimage[:],
}, },
) )
@ -424,7 +431,7 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
Amt: uint64(amount), Amt: uint64(amount),
SenderKey: senderKey[:], SenderKey: senderKey[:],
SwapInvoice: swapInvoice, SwapInvoice: swapInvoice,
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
ProbeInvoice: probeInvoice, ProbeInvoice: probeInvoice,
UserAgent: UserAgent(initiator), UserAgent: UserAgent(initiator),
} }
@ -469,7 +476,7 @@ func (s *grpcSwapServerClient) SubscribeLoopInUpdates(ctx context.Context,
resp, err := s.server.SubscribeLoopInUpdates( resp, err := s.server.SubscribeLoopInUpdates(
ctx, &looprpc.SubscribeUpdatesRequest{ ctx, &looprpc.SubscribeUpdatesRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
SwapHash: hash[:], SwapHash: hash[:],
}, },
) )
@ -500,7 +507,7 @@ func (s *grpcSwapServerClient) SubscribeLoopOutUpdates(ctx context.Context,
resp, err := s.server.SubscribeLoopOutUpdates( resp, err := s.server.SubscribeLoopOutUpdates(
ctx, &looprpc.SubscribeUpdatesRequest{ ctx, &looprpc.SubscribeUpdatesRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
SwapHash: hash[:], SwapHash: hash[:],
}, },
) )
@ -639,7 +646,7 @@ func (s *grpcSwapServerClient) CancelLoopOutSwap(ctx context.Context,
details *outCancelDetails) error { details *outCancelDetails) error {
req := &looprpc.CancelLoopOutSwapRequest{ req := &looprpc.CancelLoopOutSwapRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
SwapHash: details.hash[:], SwapHash: details.hash[:],
PaymentAddress: details.paymentAddr[:], PaymentAddress: details.paymentAddr[:],
} }
@ -660,7 +667,7 @@ func (s *grpcSwapServerClient) RecommendRoutingPlugin(ctx context.Context,
swapHash lntypes.Hash, paymentAddr [32]byte) (RoutingPluginType, error) { swapHash lntypes.Hash, paymentAddr [32]byte) (RoutingPluginType, error) {
req := &looprpc.RecommendRoutingPluginReq{ req := &looprpc.RecommendRoutingPluginReq{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
SwapHash: swapHash[:], SwapHash: swapHash[:],
PaymentAddress: paymentAddr[:], PaymentAddress: paymentAddr[:],
} }
@ -704,7 +711,7 @@ func (s *grpcSwapServerClient) ReportRoutingResult(ctx context.Context,
} }
req := &looprpc.ReportRoutingResultReq{ req := &looprpc.ReportRoutingResultReq{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion, ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
SwapHash: swapHash[:], SwapHash: swapHash[:],
PaymentAddress: paymentAddr[:], PaymentAddress: paymentAddr[:],
Plugin: rpcRoutingPlugin, Plugin: rpcRoutingPlugin,
@ -720,6 +727,32 @@ func (s *grpcSwapServerClient) ReportRoutingResult(ctx context.Context,
return err return err
} }
// MuSig2SignSweep calls the server to cooperatively sign the MuSig2 htlc
// spend. Returns the server's nonce and partial signature.
func (s *grpcSwapServerClient) MuSig2SignSweep(ctx context.Context,
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
paymentAddr [32]byte, nonce []byte, sigHash []byte) (
[]byte, []byte, error) {
req := &looprpc.MuSig2SignSweepReq{
ProtocolVersion: looprpc.ProtocolVersion(protocolVersion),
SwapHash: swapHash[:],
PaymentAddress: paymentAddr[:],
Nonce: nonce,
SigHash: sigHash,
}
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
defer rpcCancel()
res, err := s.server.MuSig2SignSweep(rpcCtx, req)
if err != nil {
return nil, nil, err
}
return res.Nonce, res.PartialSignature, nil
}
func rpcRouteCancel(details *outCancelDetails) ( func rpcRouteCancel(details *outCancelDetails) (
*looprpc.CancelLoopOutSwapRequest_RouteCancel, error) { *looprpc.CancelLoopOutSwapRequest_RouteCancel, error) {

@ -9,6 +9,7 @@ function generate() {
do do
protoc -I/usr/local/include -I. -I.. \ protoc -I/usr/local/include -I. -I.. \
--go_out . --go_opt paths=source_relative \ --go_out . --go_opt paths=source_relative \
--go-grpc_out . --go-grpc_opt paths=source_relative \
"${file}" "${file}"
done done
} }

@ -62,21 +62,24 @@ const (
// The client may ask the server to use a custom routing helper plugin in // The client may ask the server to use a custom routing helper plugin in
// order to enhance off-chain payments corresponding to a swap. // order to enhance off-chain payments corresponding to a swap.
ProtocolVersion_ROUTING_PLUGIN ProtocolVersion = 9 ProtocolVersion_ROUTING_PLUGIN ProtocolVersion = 9
// The client will use the new v3 (taproot) HTLC scripts.
ProtocolVersion_HTLC_V3 ProtocolVersion = 10
) )
// Enum value maps for ProtocolVersion. // Enum value maps for ProtocolVersion.
var ( var (
ProtocolVersion_name = map[int32]string{ ProtocolVersion_name = map[int32]string{
0: "LEGACY", 0: "LEGACY",
1: "MULTI_LOOP_OUT", 1: "MULTI_LOOP_OUT",
2: "NATIVE_SEGWIT_LOOP_IN", 2: "NATIVE_SEGWIT_LOOP_IN",
3: "PREIMAGE_PUSH_LOOP_OUT", 3: "PREIMAGE_PUSH_LOOP_OUT",
4: "USER_EXPIRY_LOOP_OUT", 4: "USER_EXPIRY_LOOP_OUT",
5: "HTLC_V2", 5: "HTLC_V2",
6: "MULTI_LOOP_IN", 6: "MULTI_LOOP_IN",
7: "LOOP_OUT_CANCEL", 7: "LOOP_OUT_CANCEL",
8: "PROBE", 8: "PROBE",
9: "ROUTING_PLUGIN", 9: "ROUTING_PLUGIN",
10: "HTLC_V3",
} }
ProtocolVersion_value = map[string]int32{ ProtocolVersion_value = map[string]int32{
"LEGACY": 0, "LEGACY": 0,
@ -89,6 +92,7 @@ var (
"LOOP_OUT_CANCEL": 7, "LOOP_OUT_CANCEL": 7,
"PROBE": 8, "PROBE": 8,
"ROUTING_PLUGIN": 9, "ROUTING_PLUGIN": 9,
"HTLC_V3": 10,
} }
) )
@ -2259,6 +2263,147 @@ func (*ReportRoutingResultRes) Descriptor() ([]byte, []int) {
return file_server_proto_rawDescGZIP(), []int{26} return file_server_proto_rawDescGZIP(), []int{26}
} }
type MuSig2SignSweepReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ProtocolVersion ProtocolVersion `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The swap hash.
SwapHash []byte `protobuf:"bytes,2,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"`
// The payment address for the swap invoice, used to ensure that only the
// swap owner can obtain the partial signature.
PaymentAddress []byte `protobuf:"bytes,3,opt,name=payment_address,json=paymentAddress,proto3" json:"payment_address,omitempty"`
// The local public nonce.
Nonce []byte `protobuf:"bytes,4,opt,name=nonce,proto3" json:"nonce,omitempty"`
// The sighash of the htlc sweep.
SigHash []byte `protobuf:"bytes,5,opt,name=sig_hash,json=sigHash,proto3" json:"sig_hash,omitempty"`
}
func (x *MuSig2SignSweepReq) Reset() {
*x = MuSig2SignSweepReq{}
if protoimpl.UnsafeEnabled {
mi := &file_server_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MuSig2SignSweepReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MuSig2SignSweepReq) ProtoMessage() {}
func (x *MuSig2SignSweepReq) ProtoReflect() protoreflect.Message {
mi := &file_server_proto_msgTypes[27]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MuSig2SignSweepReq.ProtoReflect.Descriptor instead.
func (*MuSig2SignSweepReq) Descriptor() ([]byte, []int) {
return file_server_proto_rawDescGZIP(), []int{27}
}
func (x *MuSig2SignSweepReq) GetProtocolVersion() ProtocolVersion {
if x != nil {
return x.ProtocolVersion
}
return ProtocolVersion_LEGACY
}
func (x *MuSig2SignSweepReq) GetSwapHash() []byte {
if x != nil {
return x.SwapHash
}
return nil
}
func (x *MuSig2SignSweepReq) GetPaymentAddress() []byte {
if x != nil {
return x.PaymentAddress
}
return nil
}
func (x *MuSig2SignSweepReq) GetNonce() []byte {
if x != nil {
return x.Nonce
}
return nil
}
func (x *MuSig2SignSweepReq) GetSigHash() []byte {
if x != nil {
return x.SigHash
}
return nil
}
type MuSig2SignSweepRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The server side public nonce.
Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"`
// The partial signature of the server for the requested sighash.
PartialSignature []byte `protobuf:"bytes,2,opt,name=partial_signature,json=partialSignature,proto3" json:"partial_signature,omitempty"`
}
func (x *MuSig2SignSweepRes) Reset() {
*x = MuSig2SignSweepRes{}
if protoimpl.UnsafeEnabled {
mi := &file_server_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MuSig2SignSweepRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MuSig2SignSweepRes) ProtoMessage() {}
func (x *MuSig2SignSweepRes) ProtoReflect() protoreflect.Message {
mi := &file_server_proto_msgTypes[28]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MuSig2SignSweepRes.ProtoReflect.Descriptor instead.
func (*MuSig2SignSweepRes) Descriptor() ([]byte, []int) {
return file_server_proto_rawDescGZIP(), []int{28}
}
func (x *MuSig2SignSweepRes) GetNonce() []byte {
if x != nil {
return x.Nonce
}
return nil
}
func (x *MuSig2SignSweepRes) GetPartialSignature() []byte {
if x != nil {
return x.PartialSignature
}
return nil
}
var File_server_proto protoreflect.FileDescriptor var File_server_proto protoreflect.FileDescriptor
var file_server_proto_rawDesc = []byte{ var file_server_proto_rawDesc = []byte{
@ -2524,153 +2669,177 @@ var file_server_proto_rawDesc = []byte{
0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74,
0x6f, 0x74, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x70, 0x6f, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x70, 0x6f,
0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
0x65, 0x73, 0x2a, 0xd6, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, 0x65, 0x71, 0x12, 0x43, 0x0a, 0x10, 0x70, 0x72, 0x6f,
0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
0x5f, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x41, 0x54, 0x49, 0x56, 0x45, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72,
0x5f, 0x53, 0x45, 0x47, 0x57, 0x49, 0x54, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x70,
0x02, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x55, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b,
0x53, 0x48, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
0x14, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x4c, 0x4f, 0x4f, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x70,
0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03,
0x56, 0x32, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64,
0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20,
0x4f, 0x55, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69,
0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x08, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x4f, 0x55, 0x54, 0x49, 0x67, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69,
0x4e, 0x47, 0x5f, 0x50, 0x4c, 0x55, 0x47, 0x49, 0x4e, 0x10, 0x09, 0x2a, 0x9e, 0x04, 0x0a, 0x0f, 0x67, 0x48, 0x61, 0x73, 0x68, 0x22, 0x57, 0x0a, 0x12, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e,
0x14, 0x0a, 0x10, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63,
0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x67,
0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x61,
0x12, 0x12, 0x0a, 0x0e, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2a, 0xe3,
0x53, 0x53, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69,
0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x12,
0x0a, 0x0e, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54,
0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x41, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x53, 0x45, 0x47,
0x57, 0x49, 0x54, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x1a, 0x0a,
0x16, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4c,
0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x53, 0x45,
0x52, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55,
0x54, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x56, 0x32, 0x10, 0x05,
0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49,
0x4e, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x5f,
0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x42,
0x45, 0x10, 0x08, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x4f, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x50,
0x4c, 0x55, 0x47, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x54, 0x4c, 0x43, 0x5f,
0x56, 0x33, 0x10, 0x0a, 0x2a, 0x9e, 0x04, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53,
0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x52, 0x56,
0x45, 0x52, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19,
0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55,
0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x45, 0x52,
0x56, 0x45, 0x52, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x19, 0x0a,
0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x55,
0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56,
0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x48, 0x54, 0x4c,
0x43, 0x10, 0x04, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41,
0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x48, 0x54, 0x4c,
0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x53, 0x45,
0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4f, 0x46, 0x46, 0x5f,
0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x06, 0x12,
0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44,
0x5f, 0x4e, 0x4f, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x04, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45,
0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x53, 0x57, 0x41, 0x50,
0x4c, 0x49, 0x44, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x5f, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53,
0x05, 0x12, 0x23, 0x0a, 0x1f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x48, 0x54, 0x4c,
0x45, 0x44, 0x5f, 0x4f, 0x46, 0x46, 0x5f, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x09, 0x12,
0x45, 0x4f, 0x55, 0x54, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55,
0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x1d, 0x0a,
0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x19, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54,
0x45, 0x44, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x45, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15,
0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x43, 0x4f, 0x4e, 0x46,
0x4c, 0x45, 0x44, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x41, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x10, 0x0c, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45, 0x52, 0x56, 0x45,
0x54, 0x49, 0x4f, 0x4e, 0x10, 0x09, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f,
0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0d, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56,
0x45, 0x44, 0x10, 0x0a, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x55, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43,
0x4e, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0e, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x45,
0x45, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4d, 0x55, 0x4c, 0x54,
0x4c, 0x43, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x10, 0x0c, 0x12, 0x1f, 0x49, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54,
0x0a, 0x1b, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x10, 0x0f, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41,
0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0d, 0x12, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54,
0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x10, 0x2a, 0x4a, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x50, 0x61,
0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55,
0x0e, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c,
0x45, 0x44, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x11,
0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x53, 0x10, 0x0f, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10,
0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x02, 0x2a, 0xf1, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69,
0x49, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x10, 0x2a, 0x4a, 0x0a, 0x10, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x4e,
0x52, 0x6f, 0x75, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x12, 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x4e, 0x44, 0x5f, 0x46,
0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, 0x52, 0x4f, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49,
0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4c, 0x4e, 0x44, 0x5f, 0x46,
0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x2a, 0xf1, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f,
0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x4e, 0x44, 0x5f,
0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1e, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41,
0x0a, 0x1a, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44,
0x0a, 0x1b, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x2b, 0x0a, 0x27, 0x4c, 0x4e, 0x44, 0x5f,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49,
0x1c, 0x0a, 0x18, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x2a, 0x27, 0x0a, 0x0d, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67,
0x2c, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00,
0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x57, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x01, 0x32, 0xd7,
0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x4f, 0x0a,
0x2b, 0x0a, 0x27, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x22, 0x2e,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45,
0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x2a, 0x27, 0x0a, 0x0d,
0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x08, 0x0a,
0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x57, 0x5f, 0x48,
0x49, 0x47, 0x48, 0x10, 0x01, 0x32, 0x8a, 0x09, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54,
0x65, 0x72, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74,
0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x4f, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70,
0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75,
0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x29, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f,
0x6f, 0x70, 0x4f, 0x75, 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76,
0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x4f,
0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x12, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65,
0x75, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x6c, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72,
0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x65, 0x72, 0x6d, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x50, 0x75,
0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76,
0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65,
0x72, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x0d, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a,
0x53, 0x77, 0x61, 0x70, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x2e,
0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f,
0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76,
0x65, 0x12, 0x54, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x4c,
0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x21, 0x2e,
0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f,
0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x0d,
0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1c, 0x2e,
0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f,
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x55, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x4c, 0x6f,
0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e,
0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f,
0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x67, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f,
0x70, 0x4f, 0x75, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x16, 0x53, 0x75, 0x62,
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75,
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01,
0x12, 0x65, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75,
0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, 0x70, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61,
0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6c, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74,
0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72,
0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x60, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75,
0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x22,
0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74,
0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e,
0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f,
0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69,
0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x4d,
0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x12, 0x1b,
0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53,
0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f,
0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e,
0x71, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, 0x65, 0x73, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68,
0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67,
0x65, 0x73, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61, 0x70, 0x73, 0x65,
0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61, 0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70,
0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -2686,7 +2855,7 @@ func file_server_proto_rawDescGZIP() []byte {
} }
var file_server_proto_enumTypes = make([]protoimpl.EnumInfo, 5) var file_server_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
var file_server_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_server_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
var file_server_proto_goTypes = []interface{}{ var file_server_proto_goTypes = []interface{}{
(ProtocolVersion)(0), // 0: looprpc.ProtocolVersion (ProtocolVersion)(0), // 0: looprpc.ProtocolVersion
(ServerSwapState)(0), // 1: looprpc.ServerSwapState (ServerSwapState)(0), // 1: looprpc.ServerSwapState
@ -2720,14 +2889,16 @@ var file_server_proto_goTypes = []interface{}{
(*RecommendRoutingPluginRes)(nil), // 29: looprpc.RecommendRoutingPluginRes (*RecommendRoutingPluginRes)(nil), // 29: looprpc.RecommendRoutingPluginRes
(*ReportRoutingResultReq)(nil), // 30: looprpc.ReportRoutingResultReq (*ReportRoutingResultReq)(nil), // 30: looprpc.ReportRoutingResultReq
(*ReportRoutingResultRes)(nil), // 31: looprpc.ReportRoutingResultRes (*ReportRoutingResultRes)(nil), // 31: looprpc.ReportRoutingResultRes
(*RouteHint)(nil), // 32: looprpc.RouteHint (*MuSig2SignSweepReq)(nil), // 32: looprpc.MuSig2SignSweepReq
(*MuSig2SignSweepRes)(nil), // 33: looprpc.MuSig2SignSweepRes
(*RouteHint)(nil), // 34: looprpc.RouteHint
} }
var file_server_proto_depIdxs = []int32{ var file_server_proto_depIdxs = []int32{
0, // 0: looprpc.ServerLoopOutRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 0: looprpc.ServerLoopOutRequest.protocol_version:type_name -> looprpc.ProtocolVersion
0, // 1: looprpc.ServerLoopOutQuoteRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 1: looprpc.ServerLoopOutQuoteRequest.protocol_version:type_name -> looprpc.ProtocolVersion
0, // 2: looprpc.ServerLoopOutTermsRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 2: looprpc.ServerLoopOutTermsRequest.protocol_version:type_name -> looprpc.ProtocolVersion
0, // 3: looprpc.ServerLoopInRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 3: looprpc.ServerLoopInRequest.protocol_version:type_name -> looprpc.ProtocolVersion
32, // 4: looprpc.ServerLoopInQuoteRequest.route_hints:type_name -> looprpc.RouteHint 34, // 4: looprpc.ServerLoopInQuoteRequest.route_hints:type_name -> looprpc.RouteHint
0, // 5: looprpc.ServerLoopInQuoteRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 5: looprpc.ServerLoopInQuoteRequest.protocol_version:type_name -> looprpc.ProtocolVersion
0, // 6: looprpc.ServerLoopInTermsRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 6: looprpc.ServerLoopInTermsRequest.protocol_version:type_name -> looprpc.ProtocolVersion
0, // 7: looprpc.ServerLoopOutPushPreimageRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 7: looprpc.ServerLoopOutPushPreimageRequest.protocol_version:type_name -> looprpc.ProtocolVersion
@ -2740,42 +2911,45 @@ var file_server_proto_depIdxs = []int32{
0, // 14: looprpc.CancelLoopOutSwapRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 14: looprpc.CancelLoopOutSwapRequest.protocol_version:type_name -> looprpc.ProtocolVersion
22, // 15: looprpc.CancelLoopOutSwapRequest.route_cancel:type_name -> looprpc.RouteCancel 22, // 15: looprpc.CancelLoopOutSwapRequest.route_cancel:type_name -> looprpc.RouteCancel
0, // 16: looprpc.ServerProbeRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 16: looprpc.ServerProbeRequest.protocol_version:type_name -> looprpc.ProtocolVersion
32, // 17: looprpc.ServerProbeRequest.route_hints:type_name -> looprpc.RouteHint 34, // 17: looprpc.ServerProbeRequest.route_hints:type_name -> looprpc.RouteHint
0, // 18: looprpc.RecommendRoutingPluginReq.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 18: looprpc.RecommendRoutingPluginReq.protocol_version:type_name -> looprpc.ProtocolVersion
4, // 19: looprpc.RecommendRoutingPluginRes.plugin:type_name -> looprpc.RoutingPlugin 4, // 19: looprpc.RecommendRoutingPluginRes.plugin:type_name -> looprpc.RoutingPlugin
0, // 20: looprpc.ReportRoutingResultReq.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 20: looprpc.ReportRoutingResultReq.protocol_version:type_name -> looprpc.ProtocolVersion
4, // 21: looprpc.ReportRoutingResultReq.plugin:type_name -> looprpc.RoutingPlugin 4, // 21: looprpc.ReportRoutingResultReq.plugin:type_name -> looprpc.RoutingPlugin
9, // 22: looprpc.SwapServer.LoopOutTerms:input_type -> looprpc.ServerLoopOutTermsRequest 0, // 22: looprpc.MuSig2SignSweepReq.protocol_version:type_name -> looprpc.ProtocolVersion
5, // 23: looprpc.SwapServer.NewLoopOutSwap:input_type -> looprpc.ServerLoopOutRequest 9, // 23: looprpc.SwapServer.LoopOutTerms:input_type -> looprpc.ServerLoopOutTermsRequest
17, // 24: looprpc.SwapServer.LoopOutPushPreimage:input_type -> looprpc.ServerLoopOutPushPreimageRequest 5, // 24: looprpc.SwapServer.NewLoopOutSwap:input_type -> looprpc.ServerLoopOutRequest
7, // 25: looprpc.SwapServer.LoopOutQuote:input_type -> looprpc.ServerLoopOutQuoteRequest 17, // 25: looprpc.SwapServer.LoopOutPushPreimage:input_type -> looprpc.ServerLoopOutPushPreimageRequest
15, // 26: looprpc.SwapServer.LoopInTerms:input_type -> looprpc.ServerLoopInTermsRequest 7, // 26: looprpc.SwapServer.LoopOutQuote:input_type -> looprpc.ServerLoopOutQuoteRequest
11, // 27: looprpc.SwapServer.NewLoopInSwap:input_type -> looprpc.ServerLoopInRequest 15, // 27: looprpc.SwapServer.LoopInTerms:input_type -> looprpc.ServerLoopInTermsRequest
13, // 28: looprpc.SwapServer.LoopInQuote:input_type -> looprpc.ServerLoopInQuoteRequest 11, // 28: looprpc.SwapServer.NewLoopInSwap:input_type -> looprpc.ServerLoopInRequest
19, // 29: looprpc.SwapServer.SubscribeLoopOutUpdates:input_type -> looprpc.SubscribeUpdatesRequest 13, // 29: looprpc.SwapServer.LoopInQuote:input_type -> looprpc.ServerLoopInQuoteRequest
19, // 30: looprpc.SwapServer.SubscribeLoopInUpdates:input_type -> looprpc.SubscribeUpdatesRequest 19, // 30: looprpc.SwapServer.SubscribeLoopOutUpdates:input_type -> looprpc.SubscribeUpdatesRequest
24, // 31: looprpc.SwapServer.CancelLoopOutSwap:input_type -> looprpc.CancelLoopOutSwapRequest 19, // 31: looprpc.SwapServer.SubscribeLoopInUpdates:input_type -> looprpc.SubscribeUpdatesRequest
26, // 32: looprpc.SwapServer.Probe:input_type -> looprpc.ServerProbeRequest 24, // 32: looprpc.SwapServer.CancelLoopOutSwap:input_type -> looprpc.CancelLoopOutSwapRequest
28, // 33: looprpc.SwapServer.RecommendRoutingPlugin:input_type -> looprpc.RecommendRoutingPluginReq 26, // 33: looprpc.SwapServer.Probe:input_type -> looprpc.ServerProbeRequest
30, // 34: looprpc.SwapServer.ReportRoutingResult:input_type -> looprpc.ReportRoutingResultReq 28, // 34: looprpc.SwapServer.RecommendRoutingPlugin:input_type -> looprpc.RecommendRoutingPluginReq
10, // 35: looprpc.SwapServer.LoopOutTerms:output_type -> looprpc.ServerLoopOutTerms 30, // 35: looprpc.SwapServer.ReportRoutingResult:input_type -> looprpc.ReportRoutingResultReq
6, // 36: looprpc.SwapServer.NewLoopOutSwap:output_type -> looprpc.ServerLoopOutResponse 32, // 36: looprpc.SwapServer.MuSig2SignSweep:input_type -> looprpc.MuSig2SignSweepReq
18, // 37: looprpc.SwapServer.LoopOutPushPreimage:output_type -> looprpc.ServerLoopOutPushPreimageResponse 10, // 37: looprpc.SwapServer.LoopOutTerms:output_type -> looprpc.ServerLoopOutTerms
8, // 38: looprpc.SwapServer.LoopOutQuote:output_type -> looprpc.ServerLoopOutQuote 6, // 38: looprpc.SwapServer.NewLoopOutSwap:output_type -> looprpc.ServerLoopOutResponse
16, // 39: looprpc.SwapServer.LoopInTerms:output_type -> looprpc.ServerLoopInTerms 18, // 39: looprpc.SwapServer.LoopOutPushPreimage:output_type -> looprpc.ServerLoopOutPushPreimageResponse
12, // 40: looprpc.SwapServer.NewLoopInSwap:output_type -> looprpc.ServerLoopInResponse 8, // 40: looprpc.SwapServer.LoopOutQuote:output_type -> looprpc.ServerLoopOutQuote
14, // 41: looprpc.SwapServer.LoopInQuote:output_type -> looprpc.ServerLoopInQuoteResponse 16, // 41: looprpc.SwapServer.LoopInTerms:output_type -> looprpc.ServerLoopInTerms
20, // 42: looprpc.SwapServer.SubscribeLoopOutUpdates:output_type -> looprpc.SubscribeLoopOutUpdatesResponse 12, // 42: looprpc.SwapServer.NewLoopInSwap:output_type -> looprpc.ServerLoopInResponse
21, // 43: looprpc.SwapServer.SubscribeLoopInUpdates:output_type -> looprpc.SubscribeLoopInUpdatesResponse 14, // 43: looprpc.SwapServer.LoopInQuote:output_type -> looprpc.ServerLoopInQuoteResponse
25, // 44: looprpc.SwapServer.CancelLoopOutSwap:output_type -> looprpc.CancelLoopOutSwapResponse 20, // 44: looprpc.SwapServer.SubscribeLoopOutUpdates:output_type -> looprpc.SubscribeLoopOutUpdatesResponse
27, // 45: looprpc.SwapServer.Probe:output_type -> looprpc.ServerProbeResponse 21, // 45: looprpc.SwapServer.SubscribeLoopInUpdates:output_type -> looprpc.SubscribeLoopInUpdatesResponse
29, // 46: looprpc.SwapServer.RecommendRoutingPlugin:output_type -> looprpc.RecommendRoutingPluginRes 25, // 46: looprpc.SwapServer.CancelLoopOutSwap:output_type -> looprpc.CancelLoopOutSwapResponse
31, // 47: looprpc.SwapServer.ReportRoutingResult:output_type -> looprpc.ReportRoutingResultRes 27, // 47: looprpc.SwapServer.Probe:output_type -> looprpc.ServerProbeResponse
35, // [35:48] is the sub-list for method output_type 29, // 48: looprpc.SwapServer.RecommendRoutingPlugin:output_type -> looprpc.RecommendRoutingPluginRes
22, // [22:35] is the sub-list for method input_type 31, // 49: looprpc.SwapServer.ReportRoutingResult:output_type -> looprpc.ReportRoutingResultRes
22, // [22:22] is the sub-list for extension type_name 33, // 50: looprpc.SwapServer.MuSig2SignSweep:output_type -> looprpc.MuSig2SignSweepRes
22, // [22:22] is the sub-list for extension extendee 37, // [37:51] is the sub-list for method output_type
0, // [0:22] is the sub-list for field type_name 23, // [23:37] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
} }
func init() { file_server_proto_init() } func init() { file_server_proto_init() }
@ -3109,6 +3283,30 @@ func file_server_proto_init() {
return nil return nil
} }
} }
file_server_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MuSig2SignSweepReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_server_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MuSig2SignSweepRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
file_server_proto_msgTypes[19].OneofWrappers = []interface{}{ file_server_proto_msgTypes[19].OneofWrappers = []interface{}{
(*CancelLoopOutSwapRequest_RouteCancel)(nil), (*CancelLoopOutSwapRequest_RouteCancel)(nil),
@ -3119,7 +3317,7 @@ func file_server_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_server_proto_rawDesc, RawDescriptor: file_server_proto_rawDesc,
NumEnums: 5, NumEnums: 5,
NumMessages: 27, NumMessages: 29,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

@ -43,6 +43,8 @@ service SwapServer {
rpc ReportRoutingResult (ReportRoutingResultReq) rpc ReportRoutingResult (ReportRoutingResultReq)
returns (ReportRoutingResultRes); returns (ReportRoutingResultRes);
rpc MuSig2SignSweep (MuSig2SignSweepReq) returns (MuSig2SignSweepRes);
} }
/** /**
@ -92,6 +94,9 @@ enum ProtocolVersion {
// The client may ask the server to use a custom routing helper plugin in // The client may ask the server to use a custom routing helper plugin in
// order to enhance off-chain payments corresponding to a swap. // order to enhance off-chain payments corresponding to a swap.
ROUTING_PLUGIN = 9; ROUTING_PLUGIN = 9;
// The client will use the new v3 (taproot) HTLC scripts.
HTLC_V3 = 10;
} }
message ServerLoopOutRequest { message ServerLoopOutRequest {
@ -523,3 +528,28 @@ message ReportRoutingResultReq {
message ReportRoutingResultRes { message ReportRoutingResultRes {
} }
message MuSig2SignSweepReq {
ProtocolVersion protocol_version = 1;
// The swap hash.
bytes swap_hash = 2;
// The payment address for the swap invoice, used to ensure that only the
// swap owner can obtain the partial signature.
bytes payment_address = 3;
// The local public nonce.
bytes nonce = 4;
// The sighash of the htlc sweep.
bytes sig_hash = 5;
}
message MuSig2SignSweepRes {
// The server side public nonce.
bytes nonce = 1;
// The partial signature of the server for the requested sighash.
bytes partial_signature = 2;
}

@ -31,6 +31,7 @@ type SwapServerClient interface {
Probe(ctx context.Context, in *ServerProbeRequest, opts ...grpc.CallOption) (*ServerProbeResponse, error) Probe(ctx context.Context, in *ServerProbeRequest, opts ...grpc.CallOption) (*ServerProbeResponse, error)
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)
} }
type swapServerClient struct { type swapServerClient struct {
@ -204,6 +205,15 @@ func (c *swapServerClient) ReportRoutingResult(ctx context.Context, in *ReportRo
return out, nil return out, nil
} }
func (c *swapServerClient) MuSig2SignSweep(ctx context.Context, in *MuSig2SignSweepReq, opts ...grpc.CallOption) (*MuSig2SignSweepRes, error) {
out := new(MuSig2SignSweepRes)
err := c.cc.Invoke(ctx, "/looprpc.SwapServer/MuSig2SignSweep", 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
@ -221,6 +231,7 @@ type SwapServerServer interface {
Probe(context.Context, *ServerProbeRequest) (*ServerProbeResponse, error) Probe(context.Context, *ServerProbeRequest) (*ServerProbeResponse, error)
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)
mustEmbedUnimplementedSwapServerServer() mustEmbedUnimplementedSwapServerServer()
} }
@ -267,6 +278,9 @@ func (UnimplementedSwapServerServer) RecommendRoutingPlugin(context.Context, *Re
func (UnimplementedSwapServerServer) ReportRoutingResult(context.Context, *ReportRoutingResultReq) (*ReportRoutingResultRes, error) { func (UnimplementedSwapServerServer) ReportRoutingResult(context.Context, *ReportRoutingResultReq) (*ReportRoutingResultRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReportRoutingResult not implemented") return nil, status.Errorf(codes.Unimplemented, "method ReportRoutingResult not implemented")
} }
func (UnimplementedSwapServerServer) MuSig2SignSweep(context.Context, *MuSig2SignSweepReq) (*MuSig2SignSweepRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method MuSig2SignSweep 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.
@ -520,6 +534,24 @@ func _SwapServer_ReportRoutingResult_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _SwapServer_MuSig2SignSweep_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MuSig2SignSweepReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SwapServerServer).MuSig2SignSweep(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.SwapServer/MuSig2SignSweep",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SwapServerServer).MuSig2SignSweep(ctx, req.(*MuSig2SignSweepReq))
}
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)
@ -571,6 +603,10 @@ var SwapServer_ServiceDesc = grpc.ServiceDesc{
MethodName: "ReportRoutingResult", MethodName: "ReportRoutingResult",
Handler: _SwapServer_ReportRoutingResult_Handler, Handler: _SwapServer_ReportRoutingResult_Handler,
}, },
{
MethodName: "MuSig2SignSweep",
Handler: _SwapServer_MuSig2SignSweep_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {

@ -19,6 +19,66 @@ type Sweeper struct {
Lnd *lndclient.LndServices Lnd *lndclient.LndServices
} }
// CreateUnsignedTaprootKeySpendSweepTx creates a taproot htlc sweep tx using
// keyspend. Returns the raw unsigned txn and the sighash or an error.
func (s *Sweeper) CreateUnsignedTaprootKeySpendSweepTx(
ctx context.Context, lockTime uint32,
htlc *swap.Htlc, htlcOutpoint wire.OutPoint,
amount, fee btcutil.Amount, destAddr btcutil.Address) (
*wire.MsgTx, []byte, error) {
if htlc.Version != swap.HtlcV3 {
return nil, nil, fmt.Errorf("invalid htlc version")
}
// Compose tx.
sweepTx := wire.NewMsgTx(2)
sweepTx.LockTime = lockTime
// Add HTLC input.
sweepTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: htlcOutpoint,
SignatureScript: htlc.SigScript,
})
// Add output for the destination address.
sweepPkScript, err := txscript.PayToAddrScript(destAddr)
if err != nil {
return nil, nil, err
}
sweepTx.AddTxOut(&wire.TxOut{
PkScript: sweepPkScript,
Value: int64(amount - fee),
})
// We need our previous outputs for taproot spends, and there's no
// harm including them for segwit v0, so we always include our prevOut.
prevOut := []*wire.TxOut{
{
Value: int64(amount),
PkScript: htlc.PkScript,
},
}
// We now need to create the raw sighash of the transaction, as that
// will be the message we're signing collaboratively.
prevOutputFetcher := txscript.NewCannedPrevOutputFetcher(
prevOut[0].PkScript, prevOut[0].Value,
)
sigHashes := txscript.NewTxSigHashes(sweepTx, prevOutputFetcher)
taprootSigHash, err := txscript.CalcTaprootSignatureHash(
sigHashes, txscript.SigHashDefault, sweepTx, 0,
prevOutputFetcher,
)
if err != nil {
return nil, nil, err
}
return sweepTx, taprootSigHash, nil
}
// CreateSweepTx creates an htlc sweep tx. // CreateSweepTx creates an htlc sweep tx.
func (s *Sweeper) CreateSweepTx( func (s *Sweeper) CreateSweepTx(
globalCtx context.Context, height int32, sequence uint32, globalCtx context.Context, height int32, sequence uint32,

@ -3,9 +3,11 @@ package test
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/hex"
"fmt" "fmt"
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
@ -68,7 +70,21 @@ func (s *mockSigner) MuSig2CreateSession(context.Context, *keychain.KeyLocator,
[][32]byte, ...lndclient.MuSig2SessionOpts) (*input.MuSig2SessionInfo, [][32]byte, ...lndclient.MuSig2SessionOpts) (*input.MuSig2SessionInfo,
error) { error) {
return nil, nil const testPubKey = "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"
pubKeyBytes, err := hex.DecodeString(testPubKey)
if err != nil {
return nil, err
}
combinedKey, err := schnorr.ParsePubKey(pubKeyBytes)
if err != nil {
return nil, err
}
return &input.MuSig2SessionInfo{
CombinedKey: combinedKey,
HaveAllNonces: true,
}, nil
} }
// MuSig2RegisterNonces registers one or more public nonces of other signing // MuSig2RegisterNonces registers one or more public nonces of other signing
@ -77,7 +93,7 @@ func (s *mockSigner) MuSig2CreateSession(context.Context, *keychain.KeyLocator,
func (s *mockSigner) MuSig2RegisterNonces(context.Context, [32]byte, func (s *mockSigner) MuSig2RegisterNonces(context.Context, [32]byte,
[][66]byte) (bool, error) { [][66]byte) (bool, error) {
return false, nil return true, nil
} }
// MuSig2Sign creates a partial signature using the local signing key // MuSig2Sign creates a partial signature using the local signing key
@ -100,7 +116,7 @@ func (s *mockSigner) MuSig2Sign(context.Context, [32]byte, [32]byte,
func (s *mockSigner) MuSig2CombineSig(context.Context, [32]byte, func (s *mockSigner) MuSig2CombineSig(context.Context, [32]byte,
[][]byte) (bool, []byte, error) { [][]byte) (bool, []byte, error) {
return false, nil, nil return true, nil, nil
} }
// MuSig2Cleanup removes a session from memory to free up resources. // MuSig2Cleanup removes a session from memory to free up resources.

@ -2,9 +2,11 @@ package loop
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"time" "time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient" "github.com/lightninglabs/lndclient"
@ -40,6 +42,15 @@ type testContext struct {
stop func() stop func()
} }
// mockVerifySchnorrSigFail is used to simulate failed taproot keyspend
// signature verification. If passed to the executeConfig we'll test an
// uncooperative server and will fall back to scriptspend sweep.
func mockVerifySchnorrSigFail(pubKey *btcec.PublicKey, hash,
sig []byte) error {
return fmt.Errorf("invalid sig")
}
func newSwapClient(config *clientConfig) *Client { func newSwapClient(config *clientConfig) *Client {
sweeper := &sweep.Sweeper{ sweeper := &sweep.Sweeper{
Lnd: config.LndServices, Lnd: config.LndServices,
@ -53,6 +64,7 @@ func newSwapClient(config *clientConfig) *Client {
sweeper: sweeper, sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer, createExpiryTimer: config.CreateExpiryTimer,
cancelSwap: config.Server.CancelLoopOutSwap, cancelSwap: config.Server.CancelLoopOutSwap,
verifySchnorrSig: mockVerifySchnorrSigFail,
}) })
return &Client{ return &Client{

Loading…
Cancel
Save