diff --git a/cmd/loop/instantout.go b/cmd/loop/instantout.go new file mode 100644 index 0000000..b1d7ce5 --- /dev/null +++ b/cmd/loop/instantout.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightninglabs/loop/looprpc" + "github.com/urfave/cli" +) + +var instantOutCommand = cli.Command{ + Name: "instantout", + Usage: "perform an instant off-chain to on-chain swap (looping out)", + Description: ` + Attempts to instantly loop out into the backing lnd's wallet. The amount + will be chosen via the cli. + `, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "channel", + Usage: "the comma-separated list of short " + + "channel IDs of the channels to loop out", + }, + }, + Action: instantOut, +} + +func instantOut(ctx *cli.Context) error { + // Parse outgoing channel set. Don't string split if the flag is empty. + // Otherwise, strings.Split returns a slice of length one with an empty + // element. + var outgoingChanSet []uint64 + if ctx.IsSet("channel") { + chanStrings := strings.Split(ctx.String("channel"), ",") + for _, chanString := range chanStrings { + chanID, err := strconv.ParseUint(chanString, 10, 64) + if err != nil { + return fmt.Errorf("error parsing channel id "+ + "\"%v\"", chanString) + } + outgoingChanSet = append(outgoingChanSet, chanID) + } + } + + // First set up the swap client itself. + client, cleanup, err := getClient(ctx) + if err != nil { + return err + } + defer cleanup() + + // Now we fetch all the confirmed reservations. + reservations, err := client.ListReservations( + context.Background(), &looprpc.ListReservationsRequest{}, + ) + if err != nil { + return err + } + + var ( + confirmedReservations []*looprpc.ClientReservation + totalAmt int64 + idx int + ) + + for _, res := range reservations.Reservations { + if res.State != string(reservation.Confirmed) { + continue + } + + confirmedReservations = append(confirmedReservations, res) + } + + if len(confirmedReservations) == 0 { + fmt.Printf("No confirmed reservations found \n") + return nil + } + + fmt.Printf("Available reservations: \n\n") + for _, res := range confirmedReservations { + idx++ + fmt.Printf("Reservation %v: %v \n", idx, res.Amount) + totalAmt += int64(res.Amount) + } + + fmt.Println() + fmt.Printf("Max amount to instant out: %v\n", totalAmt) + fmt.Println() + + fmt.Println("Select reservations for instantout (e.g. '1,2,3')") + fmt.Println("Type 'ALL' to use all available reservations.") + + var answer string + fmt.Scanln(&answer) + + // Parse + var selectedReservations [][]byte + switch answer { + case "ALL": + for _, res := range confirmedReservations { + selectedReservations = append( + selectedReservations, + res.ReservationId, + ) + } + + case "": + return fmt.Errorf("no reservations selected") + + default: + selectedIndexes := strings.Split(answer, ",") + selectedIndexMap := make(map[int]struct{}) + for _, idxStr := range selectedIndexes { + idx, err := strconv.Atoi(idxStr) + if err != nil { + return err + } + if idx < 0 { + return fmt.Errorf("invalid index %v", idx) + } + + if idx > len(confirmedReservations) { + return fmt.Errorf("invalid index %v", idx) + } + if _, ok := selectedIndexMap[idx]; ok { + return fmt.Errorf("duplicate index %v", idx) + } + + selectedReservations = append( + selectedReservations, + confirmedReservations[idx-1].ReservationId, + ) + + selectedIndexMap[idx] = struct{}{} + } + } + + fmt.Println("Starting instant swap out") + + // Now we can request the instant out swap. + instantOutRes, err := client.InstantOut( + context.Background(), + &looprpc.InstantOutRequest{ + ReservationIds: selectedReservations, + OutgoingChanSet: outgoingChanSet, + }, + ) + + if err != nil { + return err + } + + fmt.Printf("Instant out swap initiated with ID: %x, State: %v \n", + instantOutRes.InstantOutHash, instantOutRes.State) + + if instantOutRes.SweepTxId != "" { + fmt.Printf("Sweepless sweep tx id: %v \n", + instantOutRes.SweepTxId) + } + + return nil +} diff --git a/cmd/loop/main.go b/cmd/loop/main.go index 3ac4cea..0e970c2 100644 --- a/cmd/loop/main.go +++ b/cmd/loop/main.go @@ -148,6 +148,7 @@ func main() { listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand, setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand, getInfoCommand, abandonSwapCommand, reservationsCommands, + instantOutCommand, } err := app.Run(os.Args) diff --git a/fsm/example_fsm.md b/fsm/example_fsm.md index 7de0644..882d340 100644 --- a/fsm/example_fsm.md +++ b/fsm/example_fsm.md @@ -2,11 +2,11 @@ stateDiagram-v2 [*] --> InitFSM: OnRequestStuff InitFSM -InitFSM --> StuffFailed: OnError InitFSM --> StuffSentOut: OnStuffSentOut +InitFSM --> StuffFailed: OnError StuffFailed StuffSentOut -StuffSentOut --> StuffFailed: OnError StuffSentOut --> StuffSuccess: OnStuffSuccess +StuffSentOut --> StuffFailed: OnError StuffSuccess ``` \ No newline at end of file diff --git a/fsm/stateparser/stateparser.go b/fsm/stateparser/stateparser.go index d065833..d706452 100644 --- a/fsm/stateparser/stateparser.go +++ b/fsm/stateparser/stateparser.go @@ -10,6 +10,7 @@ import ( "sort" "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/instantout" "github.com/lightninglabs/loop/instantout/reservation" ) @@ -49,6 +50,13 @@ func run() error { return err } + case "instantout": + instantout := &instantout.FSM{} + err = writeMermaidFile(fp, instantout.GetV1ReservationStates()) + if err != nil { + return err + } + default: fmt.Println("Missing or wrong argument: fsm must be one of:") fmt.Println("\treservations") diff --git a/instantout/actions.go b/instantout/actions.go new file mode 100644 index 0000000..46e53af --- /dev/null +++ b/instantout/actions.go @@ -0,0 +1,615 @@ +package instantout + +import ( + "context" + "crypto/rand" + "errors" + "fmt" + "time" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/txscript" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightninglabs/loop/loopdb" + "github.com/lightninglabs/loop/swap" + loop_rpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + "github.com/lightningnetwork/lnd/lntypes" +) + +var ( + // Define route independent max routing fees. We have currently no way + // to get a reliable estimate of the routing fees. Best we can do is + // the minimum routing fees, which is not very indicative. + maxRoutingFeeBase = btcutil.Amount(10) + + maxRoutingFeeRate = int64(20000) + + // urgentConfTarget is the target number of blocks for the htlc to be + // confirmed quickly. + urgentConfTarget = int32(3) + + // normalConfTarget is the target number of blocks for the sweepless + // sweep to be confirmed. + normalConfTarget = int32(6) + + // defaultMaxParts is the default maximum number of parts for the swap. + defaultMaxParts = uint32(5) + + // defaultSendpaymentTimeout is the default timeout for the swap invoice. + defaultSendpaymentTimeout = time.Minute * 5 + + // defaultPollPaymentTime is the default time to poll the server for the + // payment status. + defaultPollPaymentTime = time.Second * 15 +) + +// InitInstantOutCtx contains the context for the InitInstantOutAction. +type InitInstantOutCtx struct { + cltvExpiry int32 + reservations []reservation.ID + initationHeight int32 + outgoingChanSet loopdb.ChannelSet + protocolVersion ProtocolVersion +} + +// InitInstantOutAction is the first action that is executed when the instant +// out FSM is started. It will send the instant out request to the server. +func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { + initCtx, ok := eventCtx.(*InitInstantOutCtx) + if !ok { + return f.HandleError(fsm.ErrInvalidContextType) + } + + if len(initCtx.reservations) == 0 { + return f.HandleError(fmt.Errorf("no reservations provided")) + } + + var ( + reservationAmt uint64 + reservationIds = make([][]byte, 0, len(initCtx.reservations)) + reservations = make( + []*reservation.Reservation, 0, len(initCtx.reservations), + ) + ) + + // The requested amount needs to be full reservation amounts. + for _, reservationId := range initCtx.reservations { + resId := reservationId + res, err := f.cfg.ReservationManager.GetReservation( + f.ctx, resId, + ) + if err != nil { + return f.HandleError(err) + } + + // Check if the reservation is locked. + if res.State == reservation.Locked { + return f.HandleError(fmt.Errorf("reservation %v is "+ + "locked", reservationId)) + } + + reservationAmt += uint64(res.Value) + reservationIds = append(reservationIds, resId[:]) + reservations = append(reservations, res) + } + + // Create the preimage for the swap. + var preimage lntypes.Preimage + if _, err := rand.Read(preimage[:]); err != nil { + return f.HandleError(err) + } + + // Create the keys for the swap. + keyRes, err := f.cfg.Wallet.DeriveNextKey(f.ctx, KeyFamily) + if err != nil { + return f.HandleError(err) + } + + swapHash := preimage.Hash() + + // Create a high fee rate so that the htlc will be confirmed quickly. + feeRate, err := f.cfg.Wallet.EstimateFeeRate(f.ctx, urgentConfTarget) + if err != nil { + f.Infof("error estimating fee rate: %v", err) + return f.HandleError(err) + } + + // Send the instantout request to the server. + instantOutResponse, err := f.cfg.InstantOutClient.RequestInstantLoopOut( + f.ctx, + &loop_rpc.InstantLoopOutRequest{ + ReceiverKey: keyRes.PubKey.SerializeCompressed(), + SwapHash: swapHash[:], + Expiry: initCtx.cltvExpiry, + HtlcFeeRate: uint64(feeRate), + ReservationIds: reservationIds, + ProtocolVersion: CurrentRpcProtocolVersion(), + }, + ) + if err != nil { + return f.HandleError(err) + } + // Decode the invoice to check if the hash is valid. + payReq, err := f.cfg.LndClient.DecodePaymentRequest( + f.ctx, instantOutResponse.SwapInvoice, + ) + if err != nil { + return f.HandleError(err) + } + + if swapHash != payReq.Hash { + return f.HandleError(fmt.Errorf("invalid swap invoice hash: "+ + "expected %x got %x", preimage.Hash(), payReq.Hash)) + } + serverPubkey, err := btcec.ParsePubKey(instantOutResponse.SenderKey) + if err != nil { + return f.HandleError(err) + } + + // Create the address that we'll send the funds to. + sweepAddress, err := f.cfg.Wallet.NextAddr( + f.ctx, "", walletrpc.AddressType_TAPROOT_PUBKEY, false, + ) + if err != nil { + return f.HandleError(err) + } + + // Now we can create the instant out. + instantOut := &InstantOut{ + SwapHash: swapHash, + swapPreimage: preimage, + protocolVersion: ProtocolVersionFullReservation, + initiationHeight: initCtx.initationHeight, + outgoingChanSet: initCtx.outgoingChanSet, + cltvExpiry: initCtx.cltvExpiry, + clientPubkey: keyRes.PubKey, + serverPubkey: serverPubkey, + value: btcutil.Amount(reservationAmt), + htlcFeeRate: feeRate, + swapInvoice: instantOutResponse.SwapInvoice, + reservations: reservations, + keyLocator: keyRes.KeyLocator, + sweepAddress: sweepAddress, + } + + err = f.cfg.Store.CreateInstantLoopOut(f.ctx, instantOut) + if err != nil { + return f.HandleError(err) + } + + f.InstantOut = instantOut + + return OnInit +} + +// PollPaymentAcceptedAction locks the reservations, sends the payment to the +// server and polls the server for the payment status. +func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType { + // Now that we're doing the swap, we first lock the reservations + // so that they can't be used for other swaps. + for _, reservation := range f.InstantOut.reservations { + err := f.cfg.ReservationManager.LockReservation( + f.ctx, reservation.ID, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + } + + // Now we send the payment to the server. + payChan, paymentErrChan, err := f.cfg.RouterClient.SendPayment( + f.ctx, + lndclient.SendPaymentRequest{ + Invoice: f.InstantOut.swapInvoice, + Timeout: defaultSendpaymentTimeout, + MaxParts: defaultMaxParts, + MaxFee: getMaxRoutingFee(f.InstantOut.value), + }, + ) + if err != nil { + f.Errorf("error sending payment: %v", err) + return f.handleErrorAndUnlockReservations(err) + } + + // We'll continuously poll the server for the payment status. + pollPaymentTries := 0 + + // We want to poll quickly the first time. + timer := time.NewTimer(time.Second) + for { + select { + case payRes := <-payChan: + f.Debugf("payment result: %v", payRes) + if payRes.State == lnrpc.Payment_FAILED { + return f.handleErrorAndUnlockReservations( + fmt.Errorf("payment failed: %v", + payRes.FailureReason), + ) + } + case err := <-paymentErrChan: + f.Errorf("error sending payment: %v", err) + return f.handleErrorAndUnlockReservations(err) + + case <-f.ctx.Done(): + return f.handleErrorAndUnlockReservations(nil) + + case <-timer.C: + res, err := f.cfg.InstantOutClient.PollPaymentAccepted( + f.ctx, &loop_rpc.PollPaymentAcceptedRequest{ + SwapHash: f.InstantOut.SwapHash[:], + }, + ) + if err != nil { + pollPaymentTries++ + if pollPaymentTries > 20 { + return f.handleErrorAndUnlockReservations(err) + } + } + if res != nil && res.Accepted { + return OnPaymentAccepted + } + timer.Reset(defaultPollPaymentTime) + } + } +} + +// BuildHTLCAction creates the htlc transaction, exchanges nonces with +// the server and sends the htlc signatures to the server. +func (f *FSM) BuildHTLCAction(eventCtx fsm.EventContext) fsm.EventType { + htlcSessions, htlcClientNonces, err := f.InstantOut.createMusig2Session( + f.ctx, f.cfg.Signer, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + f.htlcMusig2Sessions = htlcSessions + + // Send the server the client nonces. + htlcInitRes, err := f.cfg.InstantOutClient.InitHtlcSig( + f.ctx, + &loop_rpc.InitHtlcSigRequest{ + SwapHash: f.InstantOut.SwapHash[:], + HtlcClientNonces: htlcClientNonces, + }, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + if len(htlcInitRes.HtlcServerNonces) != len(f.InstantOut.reservations) { + return f.handleErrorAndUnlockReservations( + errors.New("invalid number of server nonces"), + ) + } + + htlcServerNonces, err := toNonces(htlcInitRes.HtlcServerNonces) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + // Now that our nonces are set, we can create and sign the htlc + // transaction. + htlcTx, err := f.InstantOut.createHtlcTransaction(f.cfg.Network) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + // Next we'll get our sweep tx signatures. + htlcSigs, err := f.InstantOut.signMusig2Tx( + f.ctx, f.cfg.Signer, htlcTx, f.htlcMusig2Sessions, + htlcServerNonces, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + // Send the server the htlc signatures. + htlcRes, err := f.cfg.InstantOutClient.PushHtlcSig( + f.ctx, + &loop_rpc.PushHtlcSigRequest{ + SwapHash: f.InstantOut.SwapHash[:], + ClientSigs: htlcSigs, + }, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + // We can now finalize the htlc transaction. + htlcTx, err = f.InstantOut.finalizeMusig2Transaction( + f.ctx, f.cfg.Signer, f.htlcMusig2Sessions, htlcTx, + htlcRes.ServerSigs, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + f.InstantOut.finalizedHtlcTx = htlcTx + + return OnHtlcSigReceived +} + +// PushPreimageAction pushes the preimage to the server. It also creates the +// sweepless sweep transaction and sends the signatures to the server. Finally, +// it publishes the sweepless sweep transaction. If any of the steps after +// pushing the preimage fail, the htlc timeout transaction will be published. +func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType { + // First we'll create the musig2 context. + coopSessions, coopClientNonces, err := f.InstantOut.createMusig2Session( + f.ctx, f.cfg.Signer, + ) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + f.sweeplessSweepSessions = coopSessions + + // Get the feerate for the coop sweep. + feeRate, err := f.cfg.Wallet.EstimateFeeRate(f.ctx, normalConfTarget) + if err != nil { + return f.handleErrorAndUnlockReservations(err) + } + + pushPreImageRes, err := f.cfg.InstantOutClient.PushPreimage( + f.ctx, + &loop_rpc.PushPreimageRequest{ + Preimage: f.InstantOut.swapPreimage[:], + ClientNonces: coopClientNonces, + ClientSweepAddr: f.InstantOut.sweepAddress.String(), + MusigTxFeeRate: uint64(feeRate), + }, + ) + // Now that we have revealed the preimage, if any following step fail, + // we'll need to publish the htlc tx. + if err != nil { + f.LastActionError = err + return OnErrorPublishHtlc + } + + // Now that we have the sweepless sweep signatures we can build and + // publish the sweepless sweep transaction. + sweepTx, err := f.InstantOut.createSweeplessSweepTx(feeRate) + if err != nil { + f.LastActionError = err + return OnErrorPublishHtlc + } + + coopServerNonces, err := toNonces(pushPreImageRes.ServerNonces) + if err != nil { + f.LastActionError = err + return OnErrorPublishHtlc + } + + // Next we'll get our sweep tx signatures. + _, err = f.InstantOut.signMusig2Tx( + f.ctx, f.cfg.Signer, sweepTx, f.sweeplessSweepSessions, + coopServerNonces, + ) + if err != nil { + f.LastActionError = err + return OnErrorPublishHtlc + } + + // Now we'll finalize the sweepless sweep transaction. + sweepTx, err = f.InstantOut.finalizeMusig2Transaction( + f.ctx, f.cfg.Signer, f.sweeplessSweepSessions, sweepTx, + pushPreImageRes.Musig2SweepSigs, + ) + if err != nil { + f.LastActionError = err + return OnErrorPublishHtlc + } + + txLabel := fmt.Sprintf("sweepless-sweep-%v", + f.InstantOut.swapPreimage.Hash()) + + // Publish the sweepless sweep transaction. + err = f.cfg.Wallet.PublishTransaction(f.ctx, sweepTx, txLabel) + if err != nil { + f.LastActionError = err + return OnErrorPublishHtlc + } + + f.InstantOut.finalizedSweeplessSweepTx = sweepTx + txHash := f.InstantOut.finalizedSweeplessSweepTx.TxHash() + + f.InstantOut.SweepTxHash = &txHash + + return OnSweeplessSweepPublished +} + +// WaitForSweeplessSweepConfirmedAction waits for the sweepless sweep +// transaction to be confirmed. +func (f *FSM) WaitForSweeplessSweepConfirmedAction( + eventCtx fsm.EventContext) fsm.EventType { + + pkscript, err := txscript.PayToAddrScript(f.InstantOut.sweepAddress) + if err != nil { + return f.HandleError(err) + } + + confChan, confErrChan, err := f.cfg.ChainNotifier. + RegisterConfirmationsNtfn( + f.ctx, f.InstantOut.SweepTxHash, pkscript, + 1, f.InstantOut.initiationHeight, + ) + if err != nil { + return f.HandleError(err) + } + + for { + select { + case spendErr := <-confErrChan: + f.LastActionError = spendErr + f.Errorf("error listening for sweepless sweep "+ + "confirmation: %v", spendErr) + + return OnErrorPublishHtlc + + case conf := <-confChan: + f.InstantOut. + sweepConfirmationHeight = conf.BlockHeight + + return OnSweeplessSweepConfirmed + } + } +} + +// PublishHtlcAction publishes the htlc transaction and the htlc sweep +// transaction. +func (f *FSM) PublishHtlcAction(eventCtx fsm.EventContext) fsm.EventType { + // Publish the htlc transaction. + err := f.cfg.Wallet.PublishTransaction( + f.ctx, f.InstantOut.finalizedHtlcTx, + fmt.Sprintf("htlc-%v", f.InstantOut.swapPreimage.Hash()), + ) + if err != nil { + return f.HandleError(err) + } + + txHash := f.InstantOut.finalizedHtlcTx.TxHash() + f.Debugf("published htlc tx: %v", txHash) + + // We'll now wait for the htlc to be confirmed. + confChan, confErrChan, err := f.cfg.ChainNotifier. + RegisterConfirmationsNtfn( + f.ctx, &txHash, + f.InstantOut.finalizedHtlcTx.TxOut[0].PkScript, + 1, f.InstantOut.initiationHeight, + ) + if err != nil { + return f.HandleError(err) + } + for { + select { + case spendErr := <-confErrChan: + return f.HandleError(spendErr) + + case <-confChan: + return OnHtlcPublished + } + } +} + +// PublishHtlcSweepAction publishes the htlc sweep transaction. +func (f *FSM) PublishHtlcSweepAction(eventCtx fsm.EventContext) fsm.EventType { + // Create a feerate that will confirm the htlc quickly. + feeRate, err := f.cfg.Wallet.EstimateFeeRate(f.ctx, urgentConfTarget) + if err != nil { + return f.HandleError(err) + } + + getInfo, err := f.cfg.LndClient.GetInfo(f.ctx) + if err != nil { + return f.HandleError(err) + } + + // We can immediately publish the htlc sweep transaction. + htlcSweepTx, err := f.InstantOut.generateHtlcSweepTx( + f.ctx, f.cfg.Signer, feeRate, f.cfg.Network, getInfo.BlockHeight, + ) + if err != nil { + return f.HandleError(err) + } + + label := fmt.Sprintf("htlc-sweep-%v", f.InstantOut.swapPreimage.Hash()) + + err = f.cfg.Wallet.PublishTransaction(f.ctx, htlcSweepTx, label) + if err != nil { + log.Errorf("error publishing htlc sweep tx: %v", err) + return f.HandleError(err) + } + + sweepTxHash := htlcSweepTx.TxHash() + + f.InstantOut.SweepTxHash = &sweepTxHash + + return OnHtlcSweepPublished +} + +// WaitForHtlcSweepConfirmedAction waits for the htlc sweep transaction to be +// confirmed. +func (f *FSM) WaitForHtlcSweepConfirmedAction( + eventCtx fsm.EventContext) fsm.EventType { + + sweepPkScript, err := txscript.PayToAddrScript( + f.InstantOut.sweepAddress, + ) + if err != nil { + return f.HandleError(err) + } + + confChan, confErrChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( + f.ctx, f.InstantOut.SweepTxHash, sweepPkScript, + 1, f.InstantOut.initiationHeight, + ) + if err != nil { + return f.HandleError(err) + } + + f.Debugf("waiting for htlc sweep tx %v to be confirmed", + f.InstantOut.SweepTxHash) + + for { + select { + case spendErr := <-confErrChan: + return f.HandleError(spendErr) + + case conf := <-confChan: + f.InstantOut. + sweepConfirmationHeight = conf.BlockHeight + + return OnHtlcSwept + } + } +} + +// handleErrorAndUnlockReservations handles an error and unlocks the +// reservations. +func (f *FSM) handleErrorAndUnlockReservations(err error) fsm.EventType { + // We might get here from a canceled context, we create a new context + // with a timeout to unlock the reservations. + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + // Unlock the reservations. + for _, reservation := range f.InstantOut.reservations { + err := f.cfg.ReservationManager.UnlockReservation( + ctx, reservation.ID, + ) + if err != nil { + f.Errorf("error unlocking reservation: %v", err) + return f.HandleError(err) + } + } + + // We're also sending the server a cancel message so that it can + // release the reservations. This can be done in a goroutine as we + // wan't to fail the fsm early. + go func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + _, cancelErr := f.cfg.InstantOutClient.CancelInstantSwap( + ctx, &loop_rpc.CancelInstantSwapRequest{ + SwapHash: f.InstantOut.SwapHash[:], + }, + ) + if cancelErr != nil { + // We'll log the error but not return it as we want to return the + // original error. + f.Debugf("error sending cancel message: %v", cancelErr) + } + }() + + return f.HandleError(err) +} + +func getMaxRoutingFee(amt btcutil.Amount) btcutil.Amount { + return swap.CalcFee(amt, maxRoutingFeeBase, maxRoutingFeeRate) +} diff --git a/instantout/fsm.go b/instantout/fsm.go new file mode 100644 index 0000000..21559df --- /dev/null +++ b/instantout/fsm.go @@ -0,0 +1,401 @@ +package instantout + +import ( + "context" + "errors" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/fsm" + loop_rpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/input" +) + +type ProtocolVersion uint32 + +const ( + // ProtocolVersionUndefined is the undefined protocol version. + ProtocolVersionUndefined ProtocolVersion = 0 + + // ProtocolVersionFullReservation is the protocol version that uses + // the full reservation amount without change. + ProtocolVersionFullReservation ProtocolVersion = 1 +) + +// CurrentProtocolVersion returns the current protocol version. +func CurrentProtocolVersion() ProtocolVersion { + return ProtocolVersionFullReservation +} + +// CurrentRpcProtocolVersion returns the current rpc protocol version. +func CurrentRpcProtocolVersion() loop_rpc.InstantOutProtocolVersion { + return loop_rpc.InstantOutProtocolVersion(CurrentProtocolVersion()) +} + +const ( + // defaultObserverSize is the size of the fsm observer channel. + defaultObserverSize = 15 +) + +var ( + ErrProtocolVersionNotSupported = errors.New( + "protocol version not supported", + ) +) + +// States. +var ( + // Init is the initial state of the instant out FSM. + Init = fsm.StateType("Init") + + // SendPaymentAndPollAccepted is the state where the payment is sent + // and the server is polled for the accepted state. + SendPaymentAndPollAccepted = fsm.StateType("SendPaymentAndPollAccepted") + + // BuildHtlc is the state where the htlc transaction is built. + BuildHtlc = fsm.StateType("BuildHtlc") + + // PushPreimage is the state where the preimage is pushed to the server. + PushPreimage = fsm.StateType("PushPreimage") + + // WaitForSweeplessSweepConfirmed is the state where we wait for the + // sweepless sweep to be confirmed. + WaitForSweeplessSweepConfirmed = fsm.StateType( + "WaitForSweeplessSweepConfirmed") + + // FinishedSweeplessSweep is the state where the swap is finished by + // publishing the sweepless sweep. + FinishedSweeplessSweep = fsm.StateType("FinishedSweeplessSweep") + + // PublishHtlc is the state where the htlc transaction is published. + PublishHtlc = fsm.StateType("PublishHtlc") + + // PublishHtlcSweep is the state where the htlc sweep transaction is + // published. + PublishHtlcSweep = fsm.StateType("PublishHtlcSweep") + + // FinishedHtlcPreimageSweep is the state where the swap is finished by + // publishing the htlc preimage sweep. + FinishedHtlcPreimageSweep = fsm.StateType("FinishedHtlcPreimageSweep") + + // WaitForHtlcSweepConfirmed is the state where we wait for the htlc + // sweep to be confirmed. + WaitForHtlcSweepConfirmed = fsm.StateType("WaitForHtlcSweepConfirmed") + + // FailedHtlcSweep is the state where the htlc sweep failed. + FailedHtlcSweep = fsm.StateType("FailedHtlcSweep") + + // Failed is the state where the swap failed. + Failed = fsm.StateType("InstantOutFailed") +) + +// Events. +var ( + // OnStart is the event that is sent when the FSM is started. + OnStart = fsm.EventType("OnStart") + + // OnInit is the event that is triggered when the FSM is initialized. + OnInit = fsm.EventType("OnInit") + + // OnPaymentAccepted is the event that is triggered when the payment + // is accepted by the server. + OnPaymentAccepted = fsm.EventType("OnPaymentAccepted") + + // OnHtlcSigReceived is the event that is triggered when the htlc sig + // is received. + OnHtlcSigReceived = fsm.EventType("OnHtlcSigReceived") + + // OnPreimagePushed is the event that is triggered when the preimage + // is pushed to the server. + OnPreimagePushed = fsm.EventType("OnPreimagePushed") + + // OnSweeplessSweepPublished is the event that is triggered when the + // sweepless sweep is published. + OnSweeplessSweepPublished = fsm.EventType("OnSweeplessSweepPublished") + + // OnSweeplessSweepConfirmed is the event that is triggered when the + // sweepless sweep is confirmed. + OnSweeplessSweepConfirmed = fsm.EventType("OnSweeplessSweepConfirmed") + + // OnErrorPublishHtlc is the event that is triggered when the htlc + // sweep is published after an error. + OnErrorPublishHtlc = fsm.EventType("OnErrorPublishHtlc") + + // OnInvalidCoopSweep is the event that is triggered when the coop + // sweep is invalid. + OnInvalidCoopSweep = fsm.EventType("OnInvalidCoopSweep") + + // OnHtlcPublished is the event that is triggered when the htlc + // transaction is published. + OnHtlcPublished = fsm.EventType("OnHtlcPublished") + + // OnHtlcSweepPublished is the event that is triggered when the htlc + // sweep is published. + OnHtlcSweepPublished = fsm.EventType("OnHtlcSweepPublished") + + // OnHtlcSwept is the event that is triggered when the htlc sweep is + // confirmed. + OnHtlcSwept = fsm.EventType("OnHtlcSwept") + + // OnRecover is the event that is triggered when the FSM recovers from + // a restart. + OnRecover = fsm.EventType("OnRecover") +) + +// Config contains the services required for the instant out FSM. +type Config struct { + // Store is used to store the instant out. + Store InstantLoopOutStore + + // LndClient is used to decode the swap invoice. + LndClient lndclient.LightningClient + + // RouterClient is used to send the offchain payment to the server. + RouterClient lndclient.RouterClient + + // ChainNotifier is used to be notified of on-chain events. + ChainNotifier lndclient.ChainNotifierClient + + // Signer is used to sign transactions. + Signer lndclient.SignerClient + + // Wallet is used to derive keys. + Wallet lndclient.WalletKitClient + + // InstantOutClient is used to communicate with the swap server. + InstantOutClient loop_rpc.InstantSwapServerClient + + // ReservationManager is used to get the reservations and lock them. + ReservationManager ReservationManager + + // Network is the network that is used for the swap. + Network *chaincfg.Params +} + +// FSM is the state machine that handles the instant out. +type FSM struct { + *fsm.StateMachine + + ctx context.Context + + // cfg contains all the services that the reservation manager needs to + // operate. + cfg *Config + + // InstantOut contains all the information about the instant out. + InstantOut *InstantOut + + // htlcMusig2Sessions contains all the reservations input musig2 + // sessions that will be used for the htlc transaction. + htlcMusig2Sessions []*input.MuSig2SessionInfo + + // sweeplessSweepSessions contains all the reservations input musig2 + // sessions that will be used for the sweepless sweep transaction. + sweeplessSweepSessions []*input.MuSig2SessionInfo +} + +// NewFSM creates a new instant out FSM. +func NewFSM(ctx context.Context, cfg *Config, + protocolVersion ProtocolVersion) (*FSM, error) { + + instantOut := &InstantOut{ + State: fsm.EmptyState, + protocolVersion: protocolVersion, + } + + return NewFSMFromInstantOut(ctx, cfg, instantOut) +} + +// NewFSMFromInstantOut creates a new instantout FSM from an existing instantout +// recovered from the database. +func NewFSMFromInstantOut(ctx context.Context, cfg *Config, + instantOut *InstantOut) (*FSM, error) { + + instantOutFSM := &FSM{ + ctx: ctx, + cfg: cfg, + InstantOut: instantOut, + } + switch instantOut.protocolVersion { + case ProtocolVersionFullReservation: + instantOutFSM.StateMachine = fsm.NewStateMachineWithState( + instantOutFSM.GetV1ReservationStates(), + instantOut.State, defaultObserverSize, + ) + + default: + return nil, ErrProtocolVersionNotSupported + } + + instantOutFSM.ActionEntryFunc = instantOutFSM.updateInstantOut + + return instantOutFSM, nil +} + +// GetV1ReservationStates returns the states for the v1 reservation. +func (f *FSM) GetV1ReservationStates() fsm.States { + return fsm.States{ + fsm.EmptyState: fsm.State{ + Transitions: fsm.Transitions{ + OnStart: Init, + }, + Action: nil, + }, + Init: fsm.State{ + Transitions: fsm.Transitions{ + OnInit: SendPaymentAndPollAccepted, + fsm.OnError: Failed, + OnRecover: Failed, + }, + Action: f.InitInstantOutAction, + }, + SendPaymentAndPollAccepted: fsm.State{ + Transitions: fsm.Transitions{ + OnPaymentAccepted: BuildHtlc, + fsm.OnError: Failed, + OnRecover: Failed, + }, + Action: f.PollPaymentAcceptedAction, + }, + BuildHtlc: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcSigReceived: PushPreimage, + fsm.OnError: Failed, + OnRecover: Failed, + }, + Action: f.BuildHTLCAction, + }, + PushPreimage: fsm.State{ + Transitions: fsm.Transitions{ + OnSweeplessSweepPublished: WaitForSweeplessSweepConfirmed, + fsm.OnError: Failed, + OnErrorPublishHtlc: PublishHtlc, + OnRecover: PushPreimage, + }, + Action: f.PushPreimageAction, + }, + WaitForSweeplessSweepConfirmed: fsm.State{ + Transitions: fsm.Transitions{ + OnSweeplessSweepConfirmed: FinishedSweeplessSweep, + OnRecover: WaitForSweeplessSweepConfirmed, + fsm.OnError: PublishHtlc, + }, + Action: f.WaitForSweeplessSweepConfirmedAction, + }, + FinishedSweeplessSweep: fsm.State{ + Transitions: fsm.Transitions{}, + Action: fsm.NoOpAction, + }, + PublishHtlc: fsm.State{ + Transitions: fsm.Transitions{ + fsm.OnError: FailedHtlcSweep, + OnRecover: PublishHtlc, + OnHtlcPublished: PublishHtlcSweep, + }, + Action: f.PublishHtlcAction, + }, + PublishHtlcSweep: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcSweepPublished: WaitForHtlcSweepConfirmed, + OnRecover: PublishHtlcSweep, + fsm.OnError: FailedHtlcSweep, + }, + Action: f.PublishHtlcSweepAction, + }, + WaitForHtlcSweepConfirmed: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcSwept: FinishedHtlcPreimageSweep, + OnRecover: WaitForHtlcSweepConfirmed, + fsm.OnError: FailedHtlcSweep, + }, + Action: f.WaitForHtlcSweepConfirmedAction, + }, + FinishedHtlcPreimageSweep: fsm.State{ + Transitions: fsm.Transitions{}, + Action: fsm.NoOpAction, + }, + FailedHtlcSweep: fsm.State{ + Action: fsm.NoOpAction, + Transitions: fsm.Transitions{ + OnRecover: PublishHtlcSweep, + }, + }, + Failed: fsm.State{ + Action: fsm.NoOpAction, + }, + } +} + +// updateInstantOut is called after every action and updates the reservation +// in the db. +func (f *FSM) updateInstantOut(notification fsm.Notification) { + f.Infof("Previous: %v, Event: %v, Next: %v", notification.PreviousState, + notification.Event, notification.NextState) + + // Skip the update if the reservation is not yet initialized. + if f.InstantOut == nil { + return + } + + f.InstantOut.State = notification.NextState + + // If we're in the early stages we don't have created the reservation + // in the store yet and won't need to update it. + if f.InstantOut.State == Init || + f.InstantOut.State == fsm.EmptyState || + (notification.PreviousState == Init && + f.InstantOut.State == Failed) { + + return + } + + err := f.cfg.Store.UpdateInstantLoopOut(f.ctx, f.InstantOut) + if err != nil { + log.Errorf("Error updating instant out: %v", err) + return + } +} + +// Infof logs an info message with the reservation hash as prefix. +func (f *FSM) Infof(format string, args ...interface{}) { + log.Infof( + "InstantOut %v: "+format, + append( + []interface{}{f.InstantOut.swapPreimage.Hash()}, + args..., + )..., + ) +} + +// Debugf logs a debug message with the reservation hash as prefix. +func (f *FSM) Debugf(format string, args ...interface{}) { + log.Debugf( + "InstantOut %v: "+format, + append( + []interface{}{f.InstantOut.swapPreimage.Hash()}, + args..., + )..., + ) +} + +// Errorf logs an error message with the reservation hash as prefix. +func (f *FSM) Errorf(format string, args ...interface{}) { + log.Errorf( + "InstantOut %v: "+format, + append( + []interface{}{f.InstantOut.swapPreimage.Hash()}, + args..., + )..., + ) +} + +// isFinalState returns true if the state is a final state. +func isFinalState(state fsm.StateType) bool { + switch state { + case Failed, FinishedHtlcPreimageSweep, + FinishedSweeplessSweep: + + return true + } + return false +} diff --git a/instantout/fsm.md b/instantout/fsm.md new file mode 100644 index 0000000..4b90fb2 --- /dev/null +++ b/instantout/fsm.md @@ -0,0 +1,36 @@ +```mermaid +stateDiagram-v2 +[*] --> Init: OnStart +BuildHtlc +BuildHtlc --> PushPreimage: OnHtlcSigReceived +BuildHtlc --> InstantFailedOutFailed: OnError +BuildHtlc --> InstantFailedOutFailed: OnRecover +FailedHtlcSweep +FinishedSweeplessSweep +Init +Init --> SendPaymentAndPollAccepted: OnInit +Init --> InstantFailedOutFailed: OnError +Init --> InstantFailedOutFailed: OnRecover +InstantFailedOutFailed +PublishHtlc +PublishHtlc --> FailedHtlcSweep: OnError +PublishHtlc --> PublishHtlc: OnRecover +PublishHtlc --> WaitForHtlcSweepConfirmed: OnHtlcBroadcasted +PushPreimage +PushPreimage --> PushPreimage: OnRecover +PushPreimage --> WaitForSweeplessSweepConfirmed: OnSweeplessSweepPublished +PushPreimage --> InstantFailedOutFailed: OnError +PushPreimage --> PublishHtlc: OnErrorPublishHtlc +SendPaymentAndPollAccepted +SendPaymentAndPollAccepted --> BuildHtlc: OnPaymentAccepted +SendPaymentAndPollAccepted --> InstantFailedOutFailed: OnError +SendPaymentAndPollAccepted --> InstantFailedOutFailed: OnRecover +WaitForHtlcSweepConfirmed +WaitForHtlcSweepConfirmed --> FinishedHtlcPreimageSweep: OnHtlcSwept +WaitForHtlcSweepConfirmed --> WaitForHtlcSweepConfirmed: OnRecover +WaitForHtlcSweepConfirmed --> FailedHtlcSweep: OnError +WaitForSweeplessSweepConfirmed +WaitForSweeplessSweepConfirmed --> FinishedSweeplessSweep: OnSweeplessSweepConfirmed +WaitForSweeplessSweepConfirmed --> WaitForSweeplessSweepConfirmed: OnRecover +WaitForSweeplessSweepConfirmed --> PublishHtlc: OnError +``` \ No newline at end of file diff --git a/instantout/instantout.go b/instantout/instantout.go new file mode 100644 index 0000000..162fef2 --- /dev/null +++ b/instantout/instantout.go @@ -0,0 +1,488 @@ +package instantout + +import ( + "context" + "errors" + "fmt" + "reflect" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightninglabs/loop/loopdb" + "github.com/lightninglabs/loop/swap" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" +) + +// InstantOut holds the necessary information to execute an instant out swap. +type InstantOut struct { + // SwapHash is the hash of the swap. + SwapHash lntypes.Hash + + // swapPreimage is the preimage that is used for the swap. + swapPreimage lntypes.Preimage + + // State is the current state of the swap. + State fsm.StateType + + // cltvExpiry is the expiry of the swap. + cltvExpiry int32 + + // outgoingChanSet optionally specifies the short channel ids of the + // channels that may be used to loop out. + outgoingChanSet loopdb.ChannelSet + + // reservations are the reservations that are used in as inputs for the + // instant out swap. + reservations []*reservation.Reservation + + // protocolVersion is the version of the protocol that is used for the + // swap. + protocolVersion ProtocolVersion + + // initiationHeight is the height at which the swap was initiated. + initiationHeight int32 + + // value is the amount that is swapped. + value btcutil.Amount + + // keyLocator is the key locator that is used for the swap. + keyLocator keychain.KeyLocator + + // clientPubkey is the pubkey of the client that is used for the swap. + clientPubkey *btcec.PublicKey + + // serverPubkey is the pubkey of the server that is used for the swap. + serverPubkey *btcec.PublicKey + + // swapInvoice is the invoice that is used for the swap. + swapInvoice string + + // htlcFeeRate is the fee rate that is used for the htlc transaction. + htlcFeeRate chainfee.SatPerKWeight + + // sweepAddress is the address that is used to sweep the funds to. + sweepAddress btcutil.Address + + // finalizedHtlcTx is the finalized htlc transaction that is used in the + // non-cooperative path for the instant out swap. + finalizedHtlcTx *wire.MsgTx + + // SweepTxHash is the hash of the sweep transaction. + SweepTxHash *chainhash.Hash + + // finalizedSweeplessSweepTx is the transaction that is used to sweep + // the funds in the cooperative path. + finalizedSweeplessSweepTx *wire.MsgTx + + // sweepConfirmationHeight is the height at which the sweep + // transaction was confirmed. + sweepConfirmationHeight uint32 +} + +// getHtlc returns the swap.htlc for the instant out. +func (i *InstantOut) getHtlc(chainParams *chaincfg.Params) (*swap.Htlc, error) { + return swap.NewHtlcV2( + i.cltvExpiry, pubkeyTo33ByteSlice(i.serverPubkey), + pubkeyTo33ByteSlice(i.clientPubkey), i.SwapHash, chainParams, + ) +} + +// createMusig2Session creates a musig2 session for the instant out. +func (i *InstantOut) createMusig2Session(ctx context.Context, + signer lndclient.SignerClient) ([]*input.MuSig2SessionInfo, + [][]byte, error) { + + // Create the htlc musig2 context. + musig2Sessions := make([]*input.MuSig2SessionInfo, len(i.reservations)) + clientNonces := make([][]byte, len(i.reservations)) + + // Create the sessions and nonces from the reservations. + for idx, reservation := range i.reservations { + session, err := reservation.Musig2CreateSession(ctx, signer) + if err != nil { + return nil, nil, err + } + + musig2Sessions[idx] = session + clientNonces[idx] = session.PublicNonce[:] + } + + return musig2Sessions, clientNonces, nil +} + +// getInputReservation returns the input reservation for the instant out. +func (i *InstantOut) getInputReservations() (InputReservations, error) { + if len(i.reservations) == 0 { + return nil, errors.New("no reservations") + } + + inputs := make(InputReservations, len(i.reservations)) + for idx, reservation := range i.reservations { + pkScript, err := reservation.GetPkScript() + if err != nil { + return nil, err + } + + inputs[idx] = InputReservation{ + Outpoint: *reservation.Outpoint, + Value: reservation.Value, + PkScript: pkScript, + } + } + + return inputs, nil +} + +// createHtlcTransaction creates the htlc transaction for the instant out. +func (i *InstantOut) createHtlcTransaction(network *chaincfg.Params) ( + *wire.MsgTx, error) { + + if network == nil { + return nil, errors.New("no network provided") + } + + inputReservations, err := i.getInputReservations() + if err != nil { + return nil, err + } + + // First Create the tx. + msgTx := wire.NewMsgTx(2) + + // add the reservation inputs + for _, reservation := range inputReservations { + msgTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: reservation.Outpoint, + }) + } + + // Estimate the fee + weight := htlcWeight(len(inputReservations)) + fee := i.htlcFeeRate.FeeForWeight(weight) + if fee > i.value/5 { + return nil, errors.New("fee is higher than 20% of " + + "sweep value") + } + + htlc, err := i.getHtlc(network) + if err != nil { + return nil, err + } + + // Create the sweep output + sweepOutput := &wire.TxOut{ + Value: int64(i.value) - int64(fee), + PkScript: htlc.PkScript, + } + + msgTx.AddTxOut(sweepOutput) + + return msgTx, nil +} + +// createSweeplessSweepTx creates the sweepless sweep transaction for the +// instant out. +func (i *InstantOut) createSweeplessSweepTx(feerate chainfee.SatPerKWeight) ( + *wire.MsgTx, error) { + + inputReservations, err := i.getInputReservations() + if err != nil { + return nil, err + } + + // First Create the tx. + msgTx := wire.NewMsgTx(2) + + // add the reservation inputs + for _, reservation := range inputReservations { + msgTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: reservation.Outpoint, + }) + } + + // Estimate the fee + weight := sweeplessSweepWeight(len(inputReservations)) + fee := feerate.FeeForWeight(weight) + if fee > i.value/5 { + return nil, errors.New("fee is higher than 20% of " + + "sweep value") + } + + pkscript, err := txscript.PayToAddrScript(i.sweepAddress) + if err != nil { + return nil, err + } + + // Create the sweep output + sweepOutput := &wire.TxOut{ + Value: int64(i.value) - int64(fee), + PkScript: pkscript, + } + + msgTx.AddTxOut(sweepOutput) + + return msgTx, nil +} + +// signMusig2Tx adds the server nonces to the musig2 sessions and signs the +// transaction. +func (i *InstantOut) signMusig2Tx(ctx context.Context, + signer lndclient.SignerClient, tx *wire.MsgTx, + musig2sessions []*input.MuSig2SessionInfo, + counterPartyNonces [][66]byte) ([][]byte, error) { + + inputs, err := i.getInputReservations() + if err != nil { + return nil, err + } + + prevOutFetcher := inputs.GetPrevoutFetcher() + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + sigs := make([][]byte, len(inputs)) + + for idx, reservation := range inputs { + if !reflect.DeepEqual(tx.TxIn[idx].PreviousOutPoint, + reservation.Outpoint) { + + return nil, fmt.Errorf("tx input does not match " + + "reservation") + } + + taprootSigHash, err := txscript.CalcTaprootSignatureHash( + sigHashes, txscript.SigHashDefault, + tx, idx, prevOutFetcher, + ) + if err != nil { + return nil, err + } + + var digest [32]byte + copy(digest[:], taprootSigHash) + + // Register the server's nonce before attempting to create our + // partial signature. + haveAllNonces, err := signer.MuSig2RegisterNonces( + ctx, musig2sessions[idx].SessionID, + [][musig2.PubNonceSize]byte{counterPartyNonces[idx]}, + ) + 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") + } + + // Since our MuSig2 session has all nonces, we can now create + // the local partial signature by signing the sig hash. + sig, err := signer.MuSig2Sign( + ctx, musig2sessions[idx].SessionID, digest, false, + ) + if err != nil { + return nil, err + } + + sigs[idx] = sig + } + + return sigs, nil +} + +// finalizeMusig2Transaction creates the finalized transactions for either +// the htlc or the cooperative close. +func (i *InstantOut) finalizeMusig2Transaction(ctx context.Context, + signer lndclient.SignerClient, + musig2Sessions []*input.MuSig2SessionInfo, + tx *wire.MsgTx, serverSigs [][]byte) (*wire.MsgTx, error) { + + inputs, err := i.getInputReservations() + if err != nil { + return nil, err + } + + for idx := range inputs { + haveAllSigs, finalSig, err := signer.MuSig2CombineSig( + ctx, musig2Sessions[idx].SessionID, + [][]byte{serverSigs[idx]}, + ) + if err != nil { + return nil, err + } + + if !haveAllSigs { + return nil, fmt.Errorf("missing sigs") + } + + tx.TxIn[idx].Witness = wire.TxWitness{finalSig} + } + + return tx, nil +} + +// generateHtlcSweepTx creates the htlc sweep transaction for the instant out. +func (i *InstantOut) generateHtlcSweepTx(ctx context.Context, + signer lndclient.SignerClient, feeRate chainfee.SatPerKWeight, + network *chaincfg.Params, blockheight uint32) ( + *wire.MsgTx, error) { + + if network == nil { + return nil, errors.New("no network provided") + } + + if i.finalizedHtlcTx == nil { + return nil, errors.New("no finalized htlc tx") + } + + htlc, err := i.getHtlc(network) + if err != nil { + return nil, err + } + + // Create the sweep transaction. + sweepTx := wire.NewMsgTx(2) + sweepTx.LockTime = blockheight + + var weightEstimator input.TxWeightEstimator + weightEstimator.AddP2TROutput() + + err = htlc.AddSuccessToEstimator(&weightEstimator) + if err != nil { + return nil, err + } + + htlcHash := i.finalizedHtlcTx.TxHash() + + // Add the htlc input. + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: htlcHash, + Index: 0, + }, + SignatureScript: htlc.SigScript, + Sequence: htlc.SuccessSequence(), + }) + + // Add the sweep output. + sweepPkScript, err := txscript.PayToAddrScript(i.sweepAddress) + if err != nil { + return nil, err + } + + fee := feeRate.FeeForWeight(int64(weightEstimator.Weight())) + + htlcOutValue := i.finalizedHtlcTx.TxOut[0].Value + output := &wire.TxOut{ + Value: htlcOutValue - int64(fee), + PkScript: sweepPkScript, + } + + sweepTx.AddTxOut(output) + + signDesc := lndclient.SignDescriptor{ + WitnessScript: htlc.SuccessScript(), + Output: &wire.TxOut{ + Value: htlcOutValue, + PkScript: htlc.PkScript, + }, + HashType: htlc.SigHash(), + InputIndex: 0, + KeyDesc: keychain.KeyDescriptor{ + KeyLocator: i.keyLocator, + }, + } + + rawSigs, err := signer.SignOutputRaw( + ctx, sweepTx, []*lndclient.SignDescriptor{&signDesc}, nil, + ) + if err != nil { + return nil, fmt.Errorf("sign output error: %v", err) + } + sig := rawSigs[0] + + // Add witness stack to the tx input. + sweepTx.TxIn[0].Witness, err = htlc.GenSuccessWitness( + sig, i.swapPreimage, + ) + if err != nil { + return nil, err + } + + return sweepTx, nil +} + +// htlcWeight returns the weight for the htlc transaction. +func htlcWeight(numInputs int) int64 { + var weightEstimator input.TxWeightEstimator + for i := 0; i < numInputs; i++ { + weightEstimator.AddTaprootKeySpendInput( + txscript.SigHashDefault, + ) + } + + weightEstimator.AddP2WSHOutput() + + return int64(weightEstimator.Weight()) +} + +// sweeplessSweepWeight returns the weight for the sweepless sweep transaction. +func sweeplessSweepWeight(numInputs int) int64 { + var weightEstimator input.TxWeightEstimator + for i := 0; i < numInputs; i++ { + weightEstimator.AddTaprootKeySpendInput( + txscript.SigHashDefault, + ) + } + + weightEstimator.AddP2TROutput() + + return int64(weightEstimator.Weight()) +} + +// pubkeyTo33ByteSlice converts a pubkey to a 33 byte slice. +func pubkeyTo33ByteSlice(pubkey *btcec.PublicKey) [33]byte { + var pubkeyBytes [33]byte + copy(pubkeyBytes[:], pubkey.SerializeCompressed()) + + return pubkeyBytes +} + +// toNonces converts a byte slice to a 66 byte slice. +func toNonces(nonces [][]byte) ([][66]byte, error) { + res := make([][66]byte, 0, len(nonces)) + for _, n := range nonces { + n := n + nonce, err := byteSliceTo66ByteSlice(n) + if err != nil { + return nil, err + } + + res = append(res, nonce) + } + + return res, nil +} + +// byteSliceTo66ByteSlice converts a byte slice to a 66 byte slice. +func byteSliceTo66ByteSlice(b []byte) ([66]byte, error) { + if len(b) != 66 { + return [66]byte{}, fmt.Errorf("invalid byte slice length") + } + + var res [66]byte + copy(res[:], b) + + return res, nil +} diff --git a/instantout/interfaces.go b/instantout/interfaces.go new file mode 100644 index 0000000..2f9018e --- /dev/null +++ b/instantout/interfaces.go @@ -0,0 +1,73 @@ +package instantout + +import ( + "context" + + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/instantout/reservation" +) + +const ( + KeyFamily = int32(42069) +) + +// InstantLoopOutStore is the interface that needs to be implemented by a +// store that wants to be used by the instant loop out manager. +type InstantLoopOutStore interface { + // CreateInstantLoopOut adds a new instant loop out to the store. + CreateInstantLoopOut(ctx context.Context, instantOut *InstantOut) error + + // UpdateInstantLoopOut updates an existing instant loop out in the + // store. + UpdateInstantLoopOut(ctx context.Context, instantOut *InstantOut) error + + // GetInstantLoopOut returns the instant loop out for the given swap + // hash. + GetInstantLoopOut(ctx context.Context, swapHash []byte) (*InstantOut, error) + + // ListInstantLoopOuts returns all instant loop outs that are in the + // store. + ListInstantLoopOuts(ctx context.Context) ([]*InstantOut, error) +} + +// ReservationManager handles fetching and locking of reservations. +type ReservationManager interface { + // GetReservation returns the reservation for the given id. + GetReservation(ctx context.Context, id reservation.ID) ( + *reservation.Reservation, error) + + // LockReservation locks the reservation for the given id. + LockReservation(ctx context.Context, id reservation.ID) error + + // UnlockReservation unlocks the reservation for the given id. + UnlockReservation(ctx context.Context, id reservation.ID) error +} + +// InputReservations is a helper struct for the input reservations. +type InputReservations []InputReservation + +// InputReservation is a helper struct for the input reservation. +type InputReservation struct { + Outpoint wire.OutPoint + Value btcutil.Amount + PkScript []byte +} + +// Output returns the output for the input reservation. +func (r InputReservation) Output() *wire.TxOut { + return wire.NewTxOut(int64(r.Value), r.PkScript) +} + +// GetPrevoutFetcher returns a prevout fetcher for the input reservations. +func (i InputReservations) GetPrevoutFetcher() txscript.PrevOutputFetcher { + prevOuts := make(map[wire.OutPoint]*wire.TxOut) + + // add the reservation inputs + for _, reservation := range i { + prevOuts[reservation.Outpoint] = reservation.Output() + } + + return txscript.NewMultiPrevOutFetcher(prevOuts) +} diff --git a/instantout/log.go b/instantout/log.go new file mode 100644 index 0000000..75764f5 --- /dev/null +++ b/instantout/log.go @@ -0,0 +1,26 @@ +package instantout + +import ( + "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/build" +) + +// Subsystem defines the sub system name of this package. +const Subsystem = "INST" + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger(Subsystem, nil)) +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/instantout/manager.go b/instantout/manager.go new file mode 100644 index 0000000..ab4aad4 --- /dev/null +++ b/instantout/manager.go @@ -0,0 +1,201 @@ +package instantout + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightningnetwork/lnd/lntypes" +) + +var ( + defaultStateWaitTime = 30 * time.Second + defaultCltv = 100 + ErrSwapDoesNotExist = errors.New("swap does not exist") +) + +// Manager manages the instantout state machines. +type Manager struct { + // cfg contains all the services that the reservation manager needs to + // operate. + cfg *Config + + // activeInstantOuts contains all the active instantouts. + activeInstantOuts map[lntypes.Hash]*FSM + + // currentHeight stores the currently best known block height. + currentHeight int32 + + // blockEpochChan receives new block heights. + blockEpochChan chan int32 + + runCtx context.Context + + sync.Mutex +} + +// NewInstantOutManager creates a new instantout manager. +func NewInstantOutManager(cfg *Config) *Manager { + return &Manager{ + cfg: cfg, + activeInstantOuts: make(map[lntypes.Hash]*FSM), + blockEpochChan: make(chan int32), + } +} + +// Run runs the instantout manager. +func (m *Manager) Run(ctx context.Context, initChan chan struct{}, + height int32) error { + + log.Debugf("Starting instantout manager") + defer func() { + log.Debugf("Stopping instantout manager") + }() + + runCtx, cancel := context.WithCancel(ctx) + defer cancel() + + m.runCtx = runCtx + m.currentHeight = height + + err := m.recoverInstantOuts(runCtx) + if err != nil { + close(initChan) + return err + } + + newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier. + RegisterBlockEpochNtfn(ctx) + if err != nil { + close(initChan) + return err + } + + close(initChan) + + for { + select { + case <-runCtx.Done(): + return nil + + case height := <-newBlockChan: + m.Lock() + m.currentHeight = height + m.Unlock() + + case err := <-newBlockErrChan: + return err + } + } +} + +// recoverInstantOuts recovers all the active instantouts from the database. +func (m *Manager) recoverInstantOuts(ctx context.Context) error { + // Fetch all the active instantouts from the database. + activeInstantOuts, err := m.cfg.Store.ListInstantLoopOuts(ctx) + if err != nil { + return err + } + + for _, instantOut := range activeInstantOuts { + if isFinalState(instantOut.State) { + continue + } + + log.Debugf("Recovering instantout %v", instantOut.SwapHash) + + instantOutFSM, err := NewFSMFromInstantOut( + ctx, m.cfg, instantOut, + ) + if err != nil { + return err + } + + m.activeInstantOuts[instantOut.SwapHash] = instantOutFSM + + // As SendEvent can block, we'll start a goroutine to process + // the event. + go func() { + err := instantOutFSM.SendEvent(OnRecover, nil) + if err != nil { + log.Errorf("FSM %v Error sending recover "+ + "event %v, state: %v", + instantOutFSM.InstantOut.SwapHash, err, + instantOutFSM.InstantOut.State) + } + }() + } + + return nil +} + +// NewInstantOut creates a new instantout. +func (m *Manager) NewInstantOut(ctx context.Context, + reservations []reservation.ID) (*FSM, error) { + + m.Lock() + // Create the instantout request. + request := &InitInstantOutCtx{ + cltvExpiry: m.currentHeight + int32(defaultCltv), + reservations: reservations, + initationHeight: m.currentHeight, + protocolVersion: CurrentProtocolVersion(), + } + + instantOut, err := NewFSM( + m.runCtx, m.cfg, ProtocolVersionFullReservation, + ) + if err != nil { + m.Unlock() + return nil, err + } + m.activeInstantOuts[instantOut.InstantOut.SwapHash] = instantOut + m.Unlock() + + // Start the instantout FSM. + go func() { + err := instantOut.SendEvent(OnStart, request) + if err != nil { + log.Errorf("Error sending event: %v", err) + } + }() + + // If everything went well, we'll wait for the instant out to be + // waiting for sweepless sweep to be confirmed. + err = instantOut.DefaultObserver.WaitForState( + ctx, defaultStateWaitTime, WaitForSweeplessSweepConfirmed, + ) + if err != nil { + if instantOut.LastActionError != nil { + return instantOut, fmt.Errorf( + "error waiting for sweepless sweep "+ + "confirmed: %w", instantOut.LastActionError, + ) + } + return instantOut, nil + } + + return instantOut, nil +} + +// GetActiveInstantOut returns an active instant out. +func (m *Manager) GetActiveInstantOut(swapHash lntypes.Hash) (*FSM, error) { + m.Lock() + defer m.Unlock() + + fsm, ok := m.activeInstantOuts[swapHash] + if !ok { + return nil, ErrSwapDoesNotExist + } + + // If the instant out is in a final state, we'll remove it from the + // active instant outs. + if isFinalState(fsm.InstantOut.State) { + delete(m.activeInstantOuts, swapHash) + } + + return fsm, nil +} diff --git a/instantout/reservation/actions.go b/instantout/reservation/actions.go index 3d0965b..8ea1e98 100644 --- a/instantout/reservation/actions.go +++ b/instantout/reservation/actions.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightninglabs/loop/fsm" looprpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/chainntnfs" ) // InitReservationContext contains the request parameters for a reservation. @@ -21,18 +22,18 @@ type InitReservationContext struct { // InitAction is the action that is executed when the reservation state machine // is initialized. It creates the reservation in the database and dispatches the // payment to the server. -func (r *FSM) InitAction(eventCtx fsm.EventContext) fsm.EventType { +func (f *FSM) InitAction(eventCtx fsm.EventContext) fsm.EventType { // Check if the context is of the correct type. reservationRequest, ok := eventCtx.(*InitReservationContext) if !ok { - return r.HandleError(fsm.ErrInvalidContextType) + return f.HandleError(fsm.ErrInvalidContextType) } - keyRes, err := r.cfg.Wallet.DeriveNextKey( - r.ctx, KeyFamily, + keyRes, err := f.cfg.Wallet.DeriveNextKey( + f.ctx, KeyFamily, ) if err != nil { - return r.HandleError(err) + return f.HandleError(err) } // Send the client reservation details to the server. @@ -44,9 +45,9 @@ func (r *FSM) InitAction(eventCtx fsm.EventContext) fsm.EventType { ClientKey: keyRes.PubKey.SerializeCompressed(), } - _, err = r.cfg.ReservationClient.OpenReservation(r.ctx, request) + _, err = f.cfg.ReservationClient.OpenReservation(f.ctx, request) if err != nil { - return r.HandleError(err) + return f.HandleError(err) } reservation, err := NewReservation( @@ -59,15 +60,15 @@ func (r *FSM) InitAction(eventCtx fsm.EventContext) fsm.EventType { keyRes.KeyLocator, ) if err != nil { - return r.HandleError(err) + return f.HandleError(err) } - r.reservation = reservation + f.reservation = reservation // Create the reservation in the database. - err = r.cfg.Store.CreateReservation(r.ctx, reservation) + err = f.cfg.Store.CreateReservation(f.ctx, reservation) if err != nil { - return r.HandleError(err) + return f.HandleError(err) } return OnBroadcast @@ -76,101 +77,163 @@ func (r *FSM) InitAction(eventCtx fsm.EventContext) fsm.EventType { // SubscribeToConfirmationAction is the action that is executed when the // reservation is waiting for confirmation. It subscribes to the confirmation // of the reservation transaction. -func (r *FSM) SubscribeToConfirmationAction(_ fsm.EventContext) fsm.EventType { - pkscript, err := r.reservation.GetPkScript() +func (f *FSM) SubscribeToConfirmationAction(_ fsm.EventContext) fsm.EventType { + pkscript, err := f.reservation.GetPkScript() if err != nil { - return r.HandleError(err) + return f.HandleError(err) } - callCtx, cancel := context.WithCancel(r.ctx) + callCtx, cancel := context.WithCancel(f.ctx) defer cancel() // Subscribe to the confirmation of the reservation transaction. log.Debugf("Subscribing to conf for reservation: %x pkscript: %x, "+ - "initiation height: %v", r.reservation.ID, pkscript, - r.reservation.InitiationHeight) + "initiation height: %v", f.reservation.ID, pkscript, + f.reservation.InitiationHeight) - confChan, errConfChan, err := r.cfg.ChainNotifier.RegisterConfirmationsNtfn( + confChan, errConfChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( callCtx, nil, pkscript, DefaultConfTarget, - r.reservation.InitiationHeight, + f.reservation.InitiationHeight, ) if err != nil { - r.Errorf("unable to subscribe to conf notification: %v", err) - return r.HandleError(err) + f.Errorf("unable to subscribe to conf notification: %v", err) + return f.HandleError(err) } - blockChan, errBlockChan, err := r.cfg.ChainNotifier.RegisterBlockEpochNtfn( + blockChan, errBlockChan, err := f.cfg.ChainNotifier.RegisterBlockEpochNtfn( callCtx, ) if err != nil { - r.Errorf("unable to subscribe to block notifications: %v", err) - return r.HandleError(err) + f.Errorf("unable to subscribe to block notifications: %v", err) + return f.HandleError(err) } // We'll now wait for the confirmation of the reservation transaction. for { select { case err := <-errConfChan: - r.Errorf("conf subscription error: %v", err) - return r.HandleError(err) + f.Errorf("conf subscription error: %v", err) + return f.HandleError(err) case err := <-errBlockChan: - r.Errorf("block subscription error: %v", err) - return r.HandleError(err) + f.Errorf("block subscription error: %v", err) + return f.HandleError(err) case confInfo := <-confChan: - r.Debugf("reservation confirmed: %v", confInfo) - outpoint, err := r.reservation.findReservationOutput( + f.Debugf("confirmed in tx: %v", confInfo.Tx) + outpoint, err := f.reservation.findReservationOutput( confInfo.Tx, ) if err != nil { - return r.HandleError(err) + return f.HandleError(err) } - r.reservation.ConfirmationHeight = confInfo.BlockHeight - r.reservation.Outpoint = outpoint + f.reservation.ConfirmationHeight = confInfo.BlockHeight + f.reservation.Outpoint = outpoint return OnConfirmed case block := <-blockChan: - r.Debugf("block received: %v expiry: %v", block, - r.reservation.Expiry) + f.Debugf("block received: %v expiry: %v", block, + f.reservation.Expiry) - if uint32(block) >= r.reservation.Expiry { + if uint32(block) >= f.reservation.Expiry { return OnTimedOut } - case <-r.ctx.Done(): + case <-f.ctx.Done(): return fsm.NoOp } } } -// ReservationConfirmedAction waits for the reservation to be either expired or -// waits for other actions to happen. -func (r *FSM) ReservationConfirmedAction(_ fsm.EventContext) fsm.EventType { - blockHeightChan, errEpochChan, err := r.cfg.ChainNotifier. - RegisterBlockEpochNtfn(r.ctx) +// AsyncWaitForExpiredOrSweptAction waits for the reservation to be either +// expired or swept. This is non-blocking and can be used to wait for the +// reservation to expire while expecting other events. +func (f *FSM) AsyncWaitForExpiredOrSweptAction(_ fsm.EventContext, +) fsm.EventType { + + notifCtx, cancel := context.WithCancel(f.ctx) + + blockHeightChan, errEpochChan, err := f.cfg.ChainNotifier. + RegisterBlockEpochNtfn(notifCtx) if err != nil { - return r.HandleError(err) + cancel() + return f.HandleError(err) } + pkScript, err := f.reservation.GetPkScript() + if err != nil { + cancel() + return f.HandleError(err) + } + + spendChan, errSpendChan, err := f.cfg.ChainNotifier.RegisterSpendNtfn( + notifCtx, f.reservation.Outpoint, pkScript, + f.reservation.InitiationHeight, + ) + if err != nil { + cancel() + return f.HandleError(err) + } + + go func() { + defer cancel() + op, err := f.handleSubcriptions( + notifCtx, blockHeightChan, spendChan, errEpochChan, + errSpendChan, + ) + if err != nil { + f.handleAsyncError(err) + return + } + if op == fsm.NoOp { + return + } + err = f.SendEvent(op, nil) + if err != nil { + f.Errorf("Error sending %s event: %v", op, err) + } + }() + + return fsm.NoOp +} + +func (f *FSM) handleSubcriptions(ctx context.Context, + blockHeightChan <-chan int32, spendChan <-chan *chainntnfs.SpendDetail, + errEpochChan <-chan error, errSpendChan <-chan error, +) (fsm.EventType, error) { + for { select { case err := <-errEpochChan: - return r.HandleError(err) + return fsm.OnError, err + + case err := <-errSpendChan: + return fsm.OnError, err case blockHeight := <-blockHeightChan: - expired := blockHeight >= int32(r.reservation.Expiry) - if expired { - r.Debugf("Reservation %v expired", - r.reservation.ID) + expired := blockHeight >= int32(f.reservation.Expiry) - return OnTimedOut + if expired { + f.Debugf("Reservation expired") + return OnTimedOut, nil } - case <-r.ctx.Done(): - return fsm.NoOp + case <-spendChan: + return OnSpent, nil + + case <-ctx.Done(): + return fsm.NoOp, nil } } } + +func (f *FSM) handleAsyncError(err error) { + f.LastActionError = err + f.Errorf("Error on async action: %v", err) + err2 := f.SendEvent(fsm.OnError, err) + if err2 != nil { + f.Errorf("Error sending event: %v", err2) + } +} diff --git a/instantout/reservation/actions_test.go b/instantout/reservation/actions_test.go index 13559f1..2f989d9 100644 --- a/instantout/reservation/actions_test.go +++ b/instantout/reservation/actions_test.go @@ -129,6 +129,7 @@ func TestInitReservationAction(t *testing.T) { } for _, tc := range tests { + tc := tc ctxb := context.Background() mockLnd := test.NewMockLnd() mockReservationClient := new(mockReservationClient) @@ -223,6 +224,7 @@ func TestSubscribeToConfirmationAction(t *testing.T) { } for _, tc := range tests { + tc := tc t.Run(tc.name, func(t *testing.T) { chainNotifier := new(MockChainNotifier) @@ -304,14 +306,83 @@ func TestSubscribeToConfirmationAction(t *testing.T) { } } -// TestReservationConfirmedAction tests the ReservationConfirmedAction of the +// AsyncWaitForExpiredOrSweptAction tests the AsyncWaitForExpiredOrSweptAction +// of the reservation state machine. +func TestAsyncWaitForExpiredOrSweptAction(t *testing.T) { + tests := []struct { + name string + blockErr error + spendErr error + expectedEvent fsm.EventType + }{ + { + name: "noop", + expectedEvent: fsm.NoOp, + }, + { + name: "block error", + blockErr: errors.New("block error"), + expectedEvent: fsm.OnError, + }, + { + name: "spend error", + spendErr: errors.New("spend error"), + expectedEvent: fsm.OnError, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { // Create a mock ChainNotifier and Reservation + chainNotifier := new(MockChainNotifier) + + // Define your FSM + r := NewFSMFromReservation( + context.Background(), &Config{ + ChainNotifier: chainNotifier, + }, + &Reservation{ + ServerPubkey: defaultPubkey, + ClientPubkey: defaultPubkey, + Expiry: defaultExpiry, + }, + ) + + // Define the expected return values for your mocks + chainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return( + make(chan int32), make(chan error), tc.blockErr, + ) + + chainNotifier.On( + "RegisterSpendNtfn", mock.Anything, + mock.Anything, mock.Anything, + ).Return( + make(chan *chainntnfs.SpendDetail), + make(chan error), tc.spendErr, + ) + + eventType := r.AsyncWaitForExpiredOrSweptAction(nil) + // Assert that the return value is as expected + require.Equal(t, tc.expectedEvent, eventType) + }) + } +} + +// TesthandleSubcriptions tests the handleSubcriptions function of the // reservation state machine. -func TestReservationConfirmedAction(t *testing.T) { +func TestHandleSubcriptions(t *testing.T) { + var ( + blockErr = errors.New("block error") + spendErr = errors.New("spend error") + ) tests := []struct { name string blockHeight int32 blockErr error + spendDetail *chainntnfs.SpendDetail + spendErr error expectedEvent fsm.EventType + expectedErr error }{ { name: "expired", @@ -320,13 +391,25 @@ func TestReservationConfirmedAction(t *testing.T) { }, { name: "block error", - blockHeight: 0, - blockErr: errors.New("block error"), + blockErr: blockErr, + expectedEvent: fsm.OnError, + expectedErr: blockErr, + }, + { + name: "spent", + spendDetail: &chainntnfs.SpendDetail{}, + expectedEvent: OnSpent, + }, + { + name: "spend error", + spendErr: spendErr, expectedEvent: fsm.OnError, + expectedErr: spendErr, }, } for _, tc := range tests { + tc := tc t.Run(tc.name, func(t *testing.T) { chainNotifier := new(MockChainNotifier) @@ -336,36 +419,41 @@ func TestReservationConfirmedAction(t *testing.T) { ChainNotifier: chainNotifier, }, &Reservation{ - Expiry: defaultExpiry, + ServerPubkey: defaultPubkey, + ClientPubkey: defaultPubkey, + Expiry: defaultExpiry, }, ) blockChan := make(chan int32) blockErrChan := make(chan error) - // Define our expected return values for the mocks. - chainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return( - blockChan, blockErrChan, nil, - ) + spendChan := make(chan *chainntnfs.SpendDetail) + spendErrChan := make(chan error) + go func() { - // Send the block notification. if tc.blockHeight != 0 { blockChan <- tc.blockHeight } - }() - go func() { - // Send the block notification error. if tc.blockErr != nil { blockErrChan <- tc.blockErr } + + if tc.spendDetail != nil { + spendChan <- tc.spendDetail + } + if tc.spendErr != nil { + spendErrChan <- tc.spendErr + } }() - eventType := r.ReservationConfirmedAction(nil) + eventType, err := r.handleSubcriptions( + context.Background(), blockChan, spendChan, + blockErrChan, spendErrChan, + ) + require.Equal(t, tc.expectedErr, err) require.Equal(t, tc.expectedEvent, eventType) - - // Assert that the expected functions were called on the mocks - chainNotifier.AssertExpectations(t) }) } } diff --git a/instantout/reservation/fsm.go b/instantout/reservation/fsm.go index 3bd0820..af2698a 100644 --- a/instantout/reservation/fsm.go +++ b/instantout/reservation/fsm.go @@ -123,6 +123,18 @@ var ( // OnRecover is the event that is triggered when the reservation FSM // recovers from a restart. OnRecover = fsm.EventType("OnRecover") + + // OnSpent is the event that is triggered when the reservation has been + // spent. + OnSpent = fsm.EventType("OnSpent") + + // OnLocked is the event that is triggered when the reservation has + // been locked. + OnLocked = fsm.EventType("OnLocked") + + // OnUnlocked is the event that is triggered when the reservation has + // been unlocked. + OnUnlocked = fsm.EventType("OnUnlocked") ) // GetReservationStates returns the statemap that defines the reservation @@ -153,14 +165,38 @@ func (f *FSM) GetReservationStates() fsm.States { }, Confirmed: fsm.State{ Transitions: fsm.Transitions{ - OnTimedOut: TimedOut, - OnRecover: Confirmed, + OnSpent: Spent, + OnTimedOut: TimedOut, + OnRecover: Confirmed, + OnLocked: Locked, + fsm.OnError: Confirmed, + }, + Action: f.AsyncWaitForExpiredOrSweptAction, + }, + Locked: fsm.State{ + Transitions: fsm.Transitions{ + OnUnlocked: Confirmed, + OnTimedOut: TimedOut, + OnRecover: Locked, + OnSpent: Spent, + fsm.OnError: Locked, }, - Action: f.ReservationConfirmedAction, + Action: f.AsyncWaitForExpiredOrSweptAction, }, TimedOut: fsm.State{ + Transitions: fsm.Transitions{ + OnTimedOut: TimedOut, + }, Action: fsm.NoOpAction, }, + + Spent: fsm.State{ + Transitions: fsm.Transitions{ + OnSpent: Spent, + }, + Action: fsm.NoOpAction, + }, + Failed: fsm.State{ Action: fsm.NoOpAction, }, diff --git a/instantout/reservation/manager.go b/instantout/reservation/manager.go index 58baeae..58cec31 100644 --- a/instantout/reservation/manager.go +++ b/instantout/reservation/manager.go @@ -3,6 +3,7 @@ package reservation import ( "context" "fmt" + "strings" "sync" "time" @@ -22,6 +23,8 @@ type Manager struct { // activeReservations contains all the active reservationsFSMs. activeReservations map[ID]*FSM + runCtx context.Context + sync.Mutex } @@ -35,12 +38,12 @@ func NewManager(cfg *Config) *Manager { // Run runs the reservation manager. func (m *Manager) Run(ctx context.Context, height int32) error { - // todo(sputn1ck): recover swaps on startup log.Debugf("Starting reservation manager") runCtx, cancel := context.WithCancel(ctx) defer cancel() + m.runCtx = runCtx currentHeight := height err := m.RecoverReservations(runCtx) @@ -58,7 +61,7 @@ func (m *Manager) Run(ctx context.Context, height int32) error { chan *reservationrpc.ServerReservationNotification, ) - err = m.RegisterReservationNotifications(runCtx, reservationResChan) + err = m.RegisterReservationNotifications(reservationResChan) if err != nil { return err } @@ -155,25 +158,29 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, // RegisterReservationNotifications registers a new reservation notification // stream. func (m *Manager) RegisterReservationNotifications( - ctx context.Context, reservationChan chan *reservationrpc. - ServerReservationNotification) error { + reservationChan chan *reservationrpc.ServerReservationNotification) error { // In order to create a valid lsat we first are going to call // the FetchL402 method. - err := m.cfg.FetchL402(ctx) + err := m.cfg.FetchL402(m.runCtx) if err != nil { return err } + ctx, cancel := context.WithCancel(m.runCtx) + // We'll now subscribe to the reservation notifications. reservationStream, err := m.cfg.ReservationClient. ReservationNotificationStream( ctx, &reservationrpc.ReservationNotificationRequest{}, ) if err != nil { + cancel() return err } + log.Debugf("Successfully subscribed to reservation notifications") + // We'll now start a goroutine that will forward all the reservation // notifications to the reservationChan. go func() { @@ -188,36 +195,30 @@ func (m *Manager) RegisterReservationNotifications( log.Errorf("Error receiving "+ "reservation: %v", err) - reconnectTimer := time.NewTimer(time.Second * 10) + cancel() // If we encounter an error, we'll // try to reconnect. for { select { - case <-ctx.Done(): + case <-m.runCtx.Done(): return - case <-reconnectTimer.C: + + case <-time.After(time.Second * 10): + log.Debugf("Reconnecting to " + + "reservation notifications") err = m.RegisterReservationNotifications( - ctx, reservationChan, + reservationChan, ) - if err == nil { - log.Debugf( - "Successfully " + - "reconnected", - ) - reconnectTimer.Stop() - // If we were able to - // reconnect, we'll - // return. - return + if err != nil { + log.Errorf("Error "+ + "reconnecting: %v", err) + continue } - log.Errorf("Error "+ - "reconnecting: %v", - err) - reconnectTimer.Reset( - time.Second * 10, - ) + // If we were able to reconnect, we'll + // return. + return } } } @@ -269,3 +270,54 @@ func (m *Manager) RecoverReservations(ctx context.Context) error { func (m *Manager) GetReservations(ctx context.Context) ([]*Reservation, error) { return m.cfg.Store.ListReservations(ctx) } + +// GetReservation returns the reservation for the given id. +func (m *Manager) GetReservation(ctx context.Context, id ID) (*Reservation, + error) { + + return m.cfg.Store.GetReservation(ctx, id) +} + +// LockReservation locks the reservation with the given ID. +func (m *Manager) LockReservation(ctx context.Context, id ID) error { + // Try getting the reservation from the active reservations map. + m.Lock() + reservation, ok := m.activeReservations[id] + m.Unlock() + + if !ok { + return fmt.Errorf("reservation not found") + } + + // Try to send the lock event to the reservation. + err := reservation.SendEvent(OnLocked, nil) + if err != nil { + return err + } + + return nil +} + +// UnlockReservation unlocks the reservation with the given ID. +func (m *Manager) UnlockReservation(ctx context.Context, id ID) error { + // Try getting the reservation from the active reservations map. + m.Lock() + reservation, ok := m.activeReservations[id] + m.Unlock() + + if !ok { + return fmt.Errorf("reservation not found") + } + + // Try to send the unlock event to the reservation. + err := reservation.SendEvent(OnUnlocked, nil) + if err != nil && strings.Contains(err.Error(), "config error") { + // If the error is a config error, we can ignore it, as the + // reservation is already unlocked. + return nil + } else if err != nil { + return err + } + + return nil +} diff --git a/instantout/reservation/manager_test.go b/instantout/reservation/manager_test.go index 1a4fe05..6b9239c 100644 --- a/instantout/reservation/manager_test.go +++ b/instantout/reservation/manager_test.go @@ -33,7 +33,7 @@ func TestManager(t *testing.T) { }() // Create a new reservation. - fsm, err := testContext.manager.newReservation( + reservationFSM, err := testContext.manager.newReservation( ctxb, uint32(testContext.mockLnd.Height), &swapserverrpc.ServerReservationNotification{ ReservationId: defaultReservationId[:], @@ -45,11 +45,11 @@ func TestManager(t *testing.T) { require.NoError(t, err) // We'll expect the spendConfirmation to be sent to the server. - pkScript, err := fsm.reservation.GetPkScript() + pkScript, err := reservationFSM.reservation.GetPkScript() require.NoError(t, err) - conf := <-testContext.mockLnd.RegisterConfChannel - require.Equal(t, conf.PkScript, pkScript) + confReg := <-testContext.mockLnd.RegisterConfChannel + require.Equal(t, confReg.PkScript, pkScript) confTx := &wire.MsgTx{ TxOut: []*wire.TxOut{ @@ -59,23 +59,39 @@ func TestManager(t *testing.T) { }, } // We'll now confirm the spend. - conf.ConfChan <- &chainntnfs.TxConfirmation{ + confReg.ConfChan <- &chainntnfs.TxConfirmation{ BlockHeight: uint32(testContext.mockLnd.Height), Tx: confTx, } // We'll now expect the reservation to be confirmed. - err = fsm.DefaultObserver.WaitForState(ctxb, 5*time.Second, Confirmed) + err = reservationFSM.DefaultObserver.WaitForState(ctxb, 5*time.Second, Confirmed) require.NoError(t, err) - // We'll now expire the reservation. - err = testContext.mockLnd.NotifyHeight( - testContext.mockLnd.Height + int32(defaultExpiry), - ) + // We'll now expect a spend registration. + spendReg := <-testContext.mockLnd.RegisterSpendChannel + require.Equal(t, spendReg.PkScript, pkScript) + + go func() { + // We'll expect a second spend registration. + spendReg = <-testContext.mockLnd.RegisterSpendChannel + require.Equal(t, spendReg.PkScript, pkScript) + }() + + // We'll now try to lock the reservation. + err = testContext.manager.LockReservation(ctxb, defaultReservationId) require.NoError(t, err) + // We'll try to lock the reservation again, which should fail. + err = testContext.manager.LockReservation(ctxb, defaultReservationId) + require.Error(t, err) + + testContext.mockLnd.SpendChannel <- &chainntnfs.SpendDetail{ + SpentOutPoint: spendReg.Outpoint, + } + // We'll now expect the reservation to be expired. - err = fsm.DefaultObserver.WaitForState(ctxb, 5*time.Second, TimedOut) + err = reservationFSM.DefaultObserver.WaitForState(ctxb, 5*time.Second, Spent) require.NoError(t, err) } diff --git a/instantout/reservation/reservation.go b/instantout/reservation/reservation.go index f220866..f9d5b66 100644 --- a/instantout/reservation/reservation.go +++ b/instantout/reservation/reservation.go @@ -2,14 +2,17 @@ package reservation import ( "bytes" + "context" "errors" "fmt" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/fsm" reservation_script "github.com/lightninglabs/loop/instantout/reservation/script" + "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" ) @@ -116,6 +119,16 @@ func (r *Reservation) GetPkScript() ([]byte, error) { return pkScript, nil } +// Output returns the reservation output. +func (r *Reservation) Output() (*wire.TxOut, error) { + pkscript, err := r.GetPkScript() + if err != nil { + return nil, err + } + + return wire.NewTxOut(int64(r.Value), pkscript), nil +} + func (r *Reservation) findReservationOutput(tx *wire.MsgTx) (*wire.OutPoint, error) { @@ -135,3 +148,32 @@ func (r *Reservation) findReservationOutput(tx *wire.MsgTx) (*wire.OutPoint, return nil, errors.New("reservation output not found") } + +// Musig2CreateSession creates a musig2 session for the reservation. +func (r *Reservation) Musig2CreateSession(ctx context.Context, + signer lndclient.SignerClient) (*input.MuSig2SessionInfo, error) { + + signers := [][]byte{ + r.ClientPubkey.SerializeCompressed(), + r.ServerPubkey.SerializeCompressed(), + } + + expiryLeaf, err := reservation_script.TaprootExpiryScript( + r.Expiry, r.ServerPubkey, + ) + if err != nil { + return nil, err + } + + rootHash := expiryLeaf.TapHash() + + musig2SessionInfo, err := signer.MuSig2CreateSession( + ctx, input.MuSig2Version100RC2, &r.KeyLocator, signers, + lndclient.MuSig2TaprootTweakOpt(rootHash[:], false), + ) + if err != nil { + return nil, err + } + + return musig2SessionInfo, nil +} diff --git a/instantout/reservation/reservation_fsm.md b/instantout/reservation/reservation_fsm.md index 712a1ea..1eefb5c 100644 --- a/instantout/reservation/reservation_fsm.md +++ b/instantout/reservation/reservation_fsm.md @@ -2,13 +2,17 @@ stateDiagram-v2 [*] --> Init: OnServerRequest Confirmed +Confirmed --> SpendBroadcasted: OnSpendBroadcasted Confirmed --> TimedOut: OnTimedOut Confirmed --> Confirmed: OnRecover Failed Init -Init --> Failed: OnError Init --> WaitForConfirmation: OnBroadcast Init --> Failed: OnRecover +Init --> Failed: OnError +SpendBroadcasted +SpendBroadcasted --> SpendConfirmed: OnSpendConfirmed +SpendConfirmed TimedOut WaitForConfirmation WaitForConfirmation --> WaitForConfirmation: OnRecover diff --git a/instantout/store.go b/instantout/store.go new file mode 100644 index 0000000..39b16d3 --- /dev/null +++ b/instantout/store.go @@ -0,0 +1,432 @@ +package instantout + +import ( + "bytes" + "context" + "database/sql" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightninglabs/loop/loopdb" + "github.com/lightninglabs/loop/loopdb/sqlc" + "github.com/lightningnetwork/lnd/clock" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" +) + +// InstantOutBaseDB is the interface that contains all the queries generated +// by sqlc for the instantout table. +type InstantOutBaseDB interface { + // InsertSwap inserts a new base swap. + InsertSwap(ctx context.Context, arg sqlc.InsertSwapParams) error + + // InsertHtlcKeys inserts the htlc keys for a swap. + InsertHtlcKeys(ctx context.Context, arg sqlc.InsertHtlcKeysParams) error + + // InsertInstantOut inserts a new instant out swap. + InsertInstantOut(ctx context.Context, + arg sqlc.InsertInstantOutParams) error + + // InsertInstantOutUpdate inserts a new instant out update. + InsertInstantOutUpdate(ctx context.Context, + arg sqlc.InsertInstantOutUpdateParams) error + + // UpdateInstantOut updates an instant out swap. + UpdateInstantOut(ctx context.Context, + arg sqlc.UpdateInstantOutParams) error + + // GetInstantOutSwap retrieves an instant out swap. + GetInstantOutSwap(ctx context.Context, + swapHash []byte) (sqlc.GetInstantOutSwapRow, error) + + // GetInstantOutSwapUpdates retrieves all instant out swap updates. + GetInstantOutSwapUpdates(ctx context.Context, + swapHash []byte) ([]sqlc.InstantoutUpdate, error) + + // GetInstantOutSwaps retrieves all instant out swaps. + GetInstantOutSwaps(ctx context.Context) ([]sqlc.GetInstantOutSwapsRow, + error) + + // ExecTx allows for executing a function in the context of a database + // transaction. + ExecTx(ctx context.Context, txOptions loopdb.TxOptions, + txBody func(*sqlc.Queries) error) error +} + +// ReservationStore is the interface that is required to load the reservations +// based on the stored reservation ids. +type ReservationStore interface { + // GetReservation returns the reservation for the given id. + GetReservation(ctx context.Context, id reservation.ID) ( + *reservation.Reservation, error) +} + +type SQLStore struct { + baseDb InstantOutBaseDB + reservationStore ReservationStore + clock clock.Clock + network *chaincfg.Params +} + +// NewSQLStore creates a new SQLStore. +func NewSQLStore(db InstantOutBaseDB, clock clock.Clock, + reservationStore ReservationStore, network *chaincfg.Params) *SQLStore { + + return &SQLStore{ + baseDb: db, + clock: clock, + reservationStore: reservationStore, + } +} + +// CreateInstantLoopOut adds a new instant loop out to the store. +func (s *SQLStore) CreateInstantLoopOut(ctx context.Context, + instantOut *InstantOut) error { + + swapArgs := sqlc.InsertSwapParams{ + SwapHash: instantOut.SwapHash[:], + Preimage: instantOut.swapPreimage[:], + InitiationTime: s.clock.Now(), + AmountRequested: int64(instantOut.value), + CltvExpiry: instantOut.cltvExpiry, + MaxMinerFee: 0, + MaxSwapFee: 0, + InitiationHeight: instantOut.initiationHeight, + ProtocolVersion: int32(instantOut.protocolVersion), + Label: "", + } + + htlcKeyArgs := sqlc.InsertHtlcKeysParams{ + SwapHash: instantOut.SwapHash[:], + SenderScriptPubkey: instantOut.serverPubkey. + SerializeCompressed(), + ReceiverScriptPubkey: instantOut.clientPubkey. + SerializeCompressed(), + ClientKeyFamily: int32(instantOut.keyLocator.Family), + ClientKeyIndex: int32(instantOut.keyLocator.Index), + } + + reservationIdByteSlice := reservationIdsToByteSlice( + instantOut.reservations, + ) + instantOutArgs := sqlc.InsertInstantOutParams{ + SwapHash: instantOut.SwapHash[:], + Preimage: instantOut.swapPreimage[:], + SweepAddress: instantOut.sweepAddress.String(), + OutgoingChanSet: instantOut.outgoingChanSet.String(), + HtlcFeeRate: int64(instantOut.htlcFeeRate), + ReservationIds: reservationIdByteSlice, + SwapInvoice: instantOut.swapInvoice, + } + + updateArgs := sqlc.InsertInstantOutUpdateParams{ + SwapHash: instantOut.SwapHash[:], + UpdateTimestamp: s.clock.Now(), + UpdateState: string(instantOut.State), + } + + return s.baseDb.ExecTx(ctx, &loopdb.SqliteTxOptions{}, + func(q *sqlc.Queries) error { + err := q.InsertSwap(ctx, swapArgs) + if err != nil { + return err + } + + err = q.InsertHtlcKeys(ctx, htlcKeyArgs) + if err != nil { + return err + } + + err = q.InsertInstantOut(ctx, instantOutArgs) + if err != nil { + return err + } + + return q.InsertInstantOutUpdate(ctx, updateArgs) + }) +} + +// UpdateInstantLoopOut updates an existing instant loop out in the +// store. +func (s *SQLStore) UpdateInstantLoopOut(ctx context.Context, + instantOut *InstantOut) error { + + // Serialize the FinalHtlcTx. + var finalHtlcTx []byte + if instantOut.finalizedHtlcTx != nil { + var buffer bytes.Buffer + err := instantOut.finalizedHtlcTx.Serialize( + &buffer, + ) + if err != nil { + return err + } + finalHtlcTx = buffer.Bytes() + } + + var finalSweeplessSweepTx []byte + if instantOut.finalizedSweeplessSweepTx != nil { + var buffer bytes.Buffer + err := instantOut.finalizedSweeplessSweepTx.Serialize( + &buffer, + ) + if err != nil { + return err + } + finalSweeplessSweepTx = buffer.Bytes() + } + + var sweepTxHash []byte + if instantOut.SweepTxHash != nil { + sweepTxHash = instantOut.SweepTxHash[:] + } + + updateParams := sqlc.UpdateInstantOutParams{ + SwapHash: instantOut.SwapHash[:], + FinalizedHtlcTx: finalHtlcTx, + SweepTxHash: sweepTxHash, + FinalizedSweeplessSweepTx: finalSweeplessSweepTx, + SweepConfirmationHeight: serializeNullInt32( + int32(instantOut.sweepConfirmationHeight), + ), + } + + updateArgs := sqlc.InsertInstantOutUpdateParams{ + SwapHash: instantOut.SwapHash[:], + UpdateTimestamp: s.clock.Now(), + UpdateState: string(instantOut.State), + } + + return s.baseDb.ExecTx(ctx, &loopdb.SqliteTxOptions{}, + func(q *sqlc.Queries) error { + err := q.UpdateInstantOut(ctx, updateParams) + if err != nil { + return err + } + + return q.InsertInstantOutUpdate(ctx, updateArgs) + }, + ) +} + +// GetInstantLoopOut returns the instant loop out for the given swap +// hash. +func (s *SQLStore) GetInstantLoopOut(ctx context.Context, swapHash []byte) ( + *InstantOut, error) { + + row, err := s.baseDb.GetInstantOutSwap(ctx, swapHash) + if err != nil { + return nil, err + } + + updates, err := s.baseDb.GetInstantOutSwapUpdates(ctx, swapHash) + if err != nil { + return nil, err + } + + return s.sqlInstantOutToInstantOut(ctx, row, updates) +} + +// ListInstantLoopOuts returns all instant loop outs that are in the +// store. +func (s *SQLStore) ListInstantLoopOuts(ctx context.Context) ([]*InstantOut, + error) { + + rows, err := s.baseDb.GetInstantOutSwaps(ctx) + if err != nil { + return nil, err + } + + var instantOuts []*InstantOut + for _, row := range rows { + updates, err := s.baseDb.GetInstantOutSwapUpdates( + ctx, row.SwapHash, + ) + if err != nil { + return nil, err + } + + instantOut, err := s.sqlInstantOutToInstantOut( + ctx, sqlc.GetInstantOutSwapRow(row), updates, + ) + if err != nil { + return nil, err + } + + instantOuts = append(instantOuts, instantOut) + } + + return instantOuts, nil +} + +// sqlInstantOutToInstantOut converts sql rows to an instant out struct. +func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context, + row sqlc.GetInstantOutSwapRow, updates []sqlc.InstantoutUpdate) ( + *InstantOut, error) { + + swapHash, err := lntypes.MakeHash(row.SwapHash) + if err != nil { + return nil, err + } + + swapPreImage, err := lntypes.MakePreimage(row.Preimage) + if err != nil { + return nil, err + } + + serverKey, err := btcec.ParsePubKey(row.SenderScriptPubkey) + if err != nil { + return nil, err + } + + clientKey, err := btcec.ParsePubKey(row.ReceiverScriptPubkey) + if err != nil { + return nil, err + } + + var finalizedHtlcTx *wire.MsgTx + if row.FinalizedHtlcTx != nil { + finalizedHtlcTx = &wire.MsgTx{} + err := finalizedHtlcTx.Deserialize(bytes.NewReader( + row.FinalizedHtlcTx, + )) + if err != nil { + return nil, err + } + } + + var finalizedSweepLessSweepTx *wire.MsgTx + if row.FinalizedSweeplessSweepTx != nil { + finalizedSweepLessSweepTx = &wire.MsgTx{} + err := finalizedSweepLessSweepTx.Deserialize(bytes.NewReader( + row.FinalizedSweeplessSweepTx, + )) + if err != nil { + return nil, err + } + } + + var sweepTxHash *chainhash.Hash + if row.SweepTxHash != nil { + sweepTxHash, err = chainhash.NewHash(row.SweepTxHash) + if err != nil { + return nil, err + } + } + + var outgoingChanSet loopdb.ChannelSet + if row.OutgoingChanSet != "" { + outgoingChanSet, err = loopdb.ConvertOutgoingChanSet( + row.OutgoingChanSet, + ) + if err != nil { + return nil, err + } + } + reservationIds, err := byteSliceToReservationIds(row.ReservationIds) + if err != nil { + return nil, err + } + + reservations := make([]*reservation.Reservation, 0, len(reservationIds)) + for _, id := range reservationIds { + reservation, err := s.reservationStore.GetReservation( + ctx, id, + ) + if err != nil { + return nil, err + } + + reservations = append(reservations, reservation) + } + + sweepAddress, err := btcutil.DecodeAddress(row.SweepAddress, s.network) + if err != nil { + return nil, err + } + + instantOut := &InstantOut{ + SwapHash: swapHash, + swapPreimage: swapPreImage, + cltvExpiry: row.CltvExpiry, + outgoingChanSet: outgoingChanSet, + reservations: reservations, + protocolVersion: ProtocolVersion(row.ProtocolVersion), + initiationHeight: row.InitiationHeight, + value: btcutil.Amount(row.AmountRequested), + keyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(row.ClientKeyFamily), + Index: uint32(row.ClientKeyIndex), + }, + clientPubkey: clientKey, + serverPubkey: serverKey, + swapInvoice: row.SwapInvoice, + htlcFeeRate: chainfee.SatPerKWeight(row.HtlcFeeRate), + sweepAddress: sweepAddress, + finalizedHtlcTx: finalizedHtlcTx, + SweepTxHash: sweepTxHash, + finalizedSweeplessSweepTx: finalizedSweepLessSweepTx, + sweepConfirmationHeight: uint32(deserializeNullInt32( + row.SweepConfirmationHeight, + )), + } + + if len(updates) > 0 { + lastUpdate := updates[len(updates)-1] + instantOut.State = fsm.StateType(lastUpdate.UpdateState) + } + + return instantOut, nil +} + +// reservationIdsToByteSlice converts a slice of reservation ids to a byte +// slice. +func reservationIdsToByteSlice(reservations []*reservation.Reservation) []byte { + var reservationIds []byte + for _, reservation := range reservations { + reservationIds = append(reservationIds, reservation.ID[:]...) + } + + return reservationIds +} + +// byteSliceToReservationIds converts a byte slice to a slice of reservation +// ids. +func byteSliceToReservationIds(byteSlice []byte) ([]reservation.ID, error) { + if len(byteSlice)%32 != 0 { + return nil, fmt.Errorf("invalid byte slice length") + } + + var reservationIds []reservation.ID + for i := 0; i < len(byteSlice); i += 32 { + var id reservation.ID + copy(id[:], byteSlice[i:i+32]) + reservationIds = append(reservationIds, id) + } + + return reservationIds, nil +} + +// serializeNullInt32 serializes an int32 to a sql.NullInt32. +func serializeNullInt32(i int32) sql.NullInt32 { + return sql.NullInt32{ + Int32: i, + Valid: true, + } +} + +// deserializeNullInt32 deserializes an int32 from a sql.NullInt32. +func deserializeNullInt32(i sql.NullInt32) int32 { + if i.Valid { + return i.Int32 + } + + return 0 +} diff --git a/instantout/store_test.go b/instantout/store_test.go new file mode 100644 index 0000000..c8e6c33 --- /dev/null +++ b/instantout/store_test.go @@ -0,0 +1,36 @@ +package instantout + +import ( + "crypto/rand" + "testing" + + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/stretchr/testify/require" +) + +func TestConvertingReservations(t *testing.T) { + var resId1, resId2 reservation.ID + + // fill the ids with random values. + if _, err := rand.Read(resId1[:]); err != nil { + t.Fatal(err) + } + + if _, err := rand.Read(resId2[:]); err != nil { + t.Fatal(err) + } + + reservations := []*reservation.Reservation{ + {ID: resId1}, {ID: resId2}, + } + + byteSlice := reservationIdsToByteSlice(reservations) + require.Len(t, byteSlice, 64) + + reservationIds, err := byteSliceToReservationIds(byteSlice) + require.NoError(t, err) + + require.Len(t, reservationIds, 2) + require.Equal(t, resId1, reservationIds[0]) + require.Equal(t, resId2, reservationIds[1]) +} diff --git a/loopd/daemon.go b/loopd/daemon.go index 747780d..fa30b11 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -16,6 +16,7 @@ import ( proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/instantout" "github.com/lightninglabs/loop/loopd/perms" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/sweepbatcher" @@ -24,6 +25,7 @@ import ( loop_looprpc "github.com/lightninglabs/loop/looprpc" loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/macaroons" "google.golang.org/grpc" @@ -67,10 +69,6 @@ type Daemon struct { // same process. swapClientServer - // reservationManager is the manager that handles all reservation state - // machines. - reservationManager *reservation.Manager - // ErrChan is an error channel that users of the Daemon struct must use // to detect runtime errors and also whether a shutdown is fully // completed. @@ -429,6 +427,11 @@ func (d *Daemon) initialize(withMacaroonService bool) error { swapClient.Conn, ) + // Create an instantout server client. + instantOutClient := loop_swaprpc.NewInstantSwapServerClient( + swapClient.Conn, + ) + // Both the client RPC server and the swap server client should stop // on main context cancel. So we create it early and pass it down. d.mainCtx, d.mainCtxCancel = context.WithCancel(context.Background()) @@ -486,7 +489,11 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } } - // Create the reservation rpc server. + var ( + reservationManager *reservation.Manager + instantOutManager *instantout.Manager + ) + // Create the reservation and instantout managers. if d.cfg.EnableExperimental { reservationStore := reservation.NewSQLStore(baseDb) reservationConfig := &reservation.Config{ @@ -497,9 +504,30 @@ func (d *Daemon) initialize(withMacaroonService bool) error { FetchL402: swapClient.Server.FetchL402, } - d.reservationManager = reservation.NewManager( + reservationManager = reservation.NewManager( reservationConfig, ) + + // Create the instantout services. + instantOutStore := instantout.NewSQLStore( + baseDb, clock.NewDefaultClock(), reservationStore, + d.lnd.ChainParams, + ) + instantOutConfig := &instantout.Config{ + Store: instantOutStore, + LndClient: d.lnd.Client, + RouterClient: d.lnd.Router, + ChainNotifier: d.lnd.ChainNotifier, + Signer: d.lnd.Signer, + Wallet: d.lnd.WalletKit, + ReservationManager: reservationManager, + InstantOutClient: instantOutClient, + Network: d.lnd.ChainParams, + } + + instantOutManager = instantout.NewInstantOutManager( + instantOutConfig, + ) } // Now finally fully initialize the swap client RPC server instance. @@ -513,7 +541,8 @@ func (d *Daemon) initialize(withMacaroonService bool) error { subscribers: make(map[int]chan<- interface{}), statusChan: make(chan loop.SwapInfo), mainCtx: d.mainCtx, - reservationManager: d.reservationManager, + reservationManager: reservationManager, + instantOutManager: instantOutManager, } // Retrieve all currently existing swaps from the database. @@ -606,6 +635,43 @@ func (d *Daemon) initialize(withMacaroonService bool) error { }() } + // Start the instant out manager. + if d.instantOutManager != nil { + d.wg.Add(1) + initChan := make(chan struct{}) + go func() { + defer d.wg.Done() + + getInfo, err := d.lnd.Client.GetInfo(d.mainCtx) + if err != nil { + d.internalErrChan <- err + return + } + + log.Info("Starting instantout manager") + defer log.Info("Instantout manager stopped") + + err = d.instantOutManager.Run( + d.mainCtx, initChan, int32(getInfo.BlockHeight), + ) + if err != nil && !errors.Is(err, context.Canceled) { + d.internalErrChan <- err + } + }() + + // Wait for the instantout server to be ready before starting the + // grpc server. + timeOutCtx, cancel := context.WithTimeout(d.mainCtx, 10*time.Second) + select { + case <-timeOutCtx.Done(): + cancel() + return fmt.Errorf("reservation server not ready: %v", + timeOutCtx.Err()) + case <-initChan: + cancel() + } + } + // Last, start our internal error handler. This will return exactly one // error or nil on the main error channel to inform the caller that // something went wrong or that shutdown is complete. We don't add to diff --git a/loopd/log.go b/loopd/log.go index 873a385..489fa98 100644 --- a/loopd/log.go +++ b/loopd/log.go @@ -6,6 +6,7 @@ import ( "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/instantout" "github.com/lightninglabs/loop/instantout/reservation" "github.com/lightninglabs/loop/liquidity" "github.com/lightninglabs/loop/loopdb" @@ -44,6 +45,9 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) { lnd.AddSubLogger( root, reservation.Subsystem, intercept, reservation.UseLogger, ) + lnd.AddSubLogger( + root, instantout.Subsystem, intercept, instantout.UseLogger, + ) } // genSubLogger creates a logger for a subsystem. We provide an instance of diff --git a/loopd/perms/perms.go b/loopd/perms/perms.go index 6e2d078..7ac85a2 100644 --- a/loopd/perms/perms.go +++ b/loopd/perms/perms.go @@ -97,7 +97,11 @@ var RequiredPermissions = map[string][]bakery.Op{ Action: "in", }}, "/looprpc.SwapClient/ListReservations": {{ - Entity: "reservation", + Entity: "swap", Action: "read", }}, + "/looprpc.SwapClient/InstantOut": {{ + Entity: "swap", + Action: "execute", + }}, } diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 05f9156..7cec2ad 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -18,6 +18,7 @@ import ( "github.com/lightninglabs/aperture/lsat" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/instantout" "github.com/lightninglabs/loop/instantout/reservation" "github.com/lightninglabs/loop/labels" "github.com/lightninglabs/loop/liquidity" @@ -81,6 +82,7 @@ type swapClientServer struct { liquidityMgr *liquidity.Manager lnd *lndclient.LndServices reservationManager *reservation.Manager + instantOutManager *instantout.Manager swaps map[lntypes.Hash]loop.SwapInfo subscribers map[int]chan<- interface{} statusChan chan loop.SwapInfo @@ -1169,6 +1171,44 @@ func (s *swapClientServer) ListReservations(ctx context.Context, }, nil } +// InstantOut initiates an instant out swap. +func (s *swapClientServer) InstantOut(ctx context.Context, + req *clientrpc.InstantOutRequest) (*clientrpc.InstantOutResponse, + error) { + + reservationIds := make([]reservation.ID, len(req.ReservationIds)) + for i, id := range req.ReservationIds { + if len(id) != reservation.IdLength { + return nil, fmt.Errorf("invalid reservation id: "+ + "expected %v bytes, got %d", + reservation.IdLength, len(id)) + } + + var resId reservation.ID + copy(resId[:], id) + + reservationIds[i] = resId + } + + instantOutFsm, err := s.instantOutManager.NewInstantOut( + ctx, reservationIds, + ) + if err != nil { + return nil, err + } + + res := &clientrpc.InstantOutResponse{ + InstantOutHash: instantOutFsm.InstantOut.SwapHash[:], + State: string(instantOutFsm.InstantOut.State), + } + + if instantOutFsm.InstantOut.SweepTxHash != nil { + res.SweepTxId = instantOutFsm.InstantOut.SweepTxHash.String() + } + + return res, nil +} + func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) { switch reason { case liquidity.ReasonNone: @@ -1448,11 +1488,11 @@ func toClientReservation( res *reservation.Reservation) *clientrpc.ClientReservation { var ( - txid []byte + txid string vout uint32 ) if res.Outpoint != nil { - txid = res.Outpoint.Hash[:] + txid = res.Outpoint.Hash.String() vout = res.Outpoint.Index } diff --git a/loopd/utils.go b/loopd/utils.go index ef763e2..acb22f4 100644 --- a/loopd/utils.go +++ b/loopd/utils.go @@ -59,6 +59,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore, db, err = loopdb.NewSqliteStore( cfg.Sqlite, chainParams, ) + if err != nil { + return nil, nil, err + } baseDb = *db.(*loopdb.SqliteSwapStore).BaseDB case DatabaseBackendPostgres: @@ -67,6 +70,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore, db, err = loopdb.NewPostgresStore( cfg.Postgres, chainParams, ) + if err != nil { + return nil, nil, err + } baseDb = *db.(*loopdb.PostgresStore).BaseDB default: diff --git a/loopdb/sql_store.go b/loopdb/sql_store.go index 83de95c..293d57d 100644 --- a/loopdb/sql_store.go +++ b/loopdb/sql_store.go @@ -543,7 +543,7 @@ func ConvertLoopOutRow(network *chaincfg.Params, row sqlc.GetLoopOutSwapRow, } if row.OutgoingChanSet != "" { - chanSet, err := convertOutgoingChanSet(row.OutgoingChanSet) + chanSet, err := ConvertOutgoingChanSet(row.OutgoingChanSet) if err != nil { return nil, err } @@ -666,9 +666,9 @@ func getSwapEvents(updates []sqlc.SwapUpdate) ([]*LoopEvent, error) { return events, nil } -// convertOutgoingChanSet converts a comma separated string of channel IDs into +// ConvertOutgoingChanSet converts a comma separated string of channel IDs into // a ChannelSet. -func convertOutgoingChanSet(outgoingChanSet string) (ChannelSet, error) { +func ConvertOutgoingChanSet(outgoingChanSet string) (ChannelSet, error) { // Split the string into a slice of strings chanStrings := strings.Split(outgoingChanSet, ",") channels := make([]uint64, len(chanStrings)) diff --git a/loopdb/sqlc/instantout.sql.go b/loopdb/sqlc/instantout.sql.go new file mode 100644 index 0000000..88ce11e --- /dev/null +++ b/loopdb/sqlc/instantout.sql.go @@ -0,0 +1,329 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.17.2 +// source: instantout.sql + +package sqlc + +import ( + "context" + "database/sql" + "time" +) + +const getInstantOutSwap = `-- name: GetInstantOutSwap :one +SELECT + swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label, + instantout_swaps.swap_hash, instantout_swaps.preimage, instantout_swaps.sweep_address, instantout_swaps.outgoing_chan_set, instantout_swaps.htlc_fee_rate, instantout_swaps.reservation_ids, instantout_swaps.swap_invoice, instantout_swaps.finalized_htlc_tx, instantout_swaps.sweep_tx_hash, instantout_swaps.finalized_sweepless_sweep_tx, instantout_swaps.sweep_confirmation_height, + htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index +FROM + swaps +JOIN + instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash +JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash +WHERE + swaps.swap_hash = $1 +` + +type GetInstantOutSwapRow struct { + ID int32 + SwapHash []byte + Preimage []byte + InitiationTime time.Time + AmountRequested int64 + CltvExpiry int32 + MaxMinerFee int64 + MaxSwapFee int64 + InitiationHeight int32 + ProtocolVersion int32 + Label string + SwapHash_2 []byte + Preimage_2 []byte + SweepAddress string + OutgoingChanSet string + HtlcFeeRate int64 + ReservationIds []byte + SwapInvoice string + FinalizedHtlcTx []byte + SweepTxHash []byte + FinalizedSweeplessSweepTx []byte + SweepConfirmationHeight sql.NullInt32 + SwapHash_3 []byte + SenderScriptPubkey []byte + ReceiverScriptPubkey []byte + SenderInternalPubkey []byte + ReceiverInternalPubkey []byte + ClientKeyFamily int32 + ClientKeyIndex int32 +} + +func (q *Queries) GetInstantOutSwap(ctx context.Context, swapHash []byte) (GetInstantOutSwapRow, error) { + row := q.db.QueryRowContext(ctx, getInstantOutSwap, swapHash) + var i GetInstantOutSwapRow + err := row.Scan( + &i.ID, + &i.SwapHash, + &i.Preimage, + &i.InitiationTime, + &i.AmountRequested, + &i.CltvExpiry, + &i.MaxMinerFee, + &i.MaxSwapFee, + &i.InitiationHeight, + &i.ProtocolVersion, + &i.Label, + &i.SwapHash_2, + &i.Preimage_2, + &i.SweepAddress, + &i.OutgoingChanSet, + &i.HtlcFeeRate, + &i.ReservationIds, + &i.SwapInvoice, + &i.FinalizedHtlcTx, + &i.SweepTxHash, + &i.FinalizedSweeplessSweepTx, + &i.SweepConfirmationHeight, + &i.SwapHash_3, + &i.SenderScriptPubkey, + &i.ReceiverScriptPubkey, + &i.SenderInternalPubkey, + &i.ReceiverInternalPubkey, + &i.ClientKeyFamily, + &i.ClientKeyIndex, + ) + return i, err +} + +const getInstantOutSwapUpdates = `-- name: GetInstantOutSwapUpdates :many +SELECT + instantout_updates.id, instantout_updates.swap_hash, instantout_updates.update_state, instantout_updates.update_timestamp +FROM + instantout_updates +WHERE + instantout_updates.swap_hash = $1 +` + +func (q *Queries) GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte) ([]InstantoutUpdate, error) { + rows, err := q.db.QueryContext(ctx, getInstantOutSwapUpdates, swapHash) + if err != nil { + return nil, err + } + defer rows.Close() + var items []InstantoutUpdate + for rows.Next() { + var i InstantoutUpdate + if err := rows.Scan( + &i.ID, + &i.SwapHash, + &i.UpdateState, + &i.UpdateTimestamp, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getInstantOutSwaps = `-- name: GetInstantOutSwaps :many +SELECT + swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label, + instantout_swaps.swap_hash, instantout_swaps.preimage, instantout_swaps.sweep_address, instantout_swaps.outgoing_chan_set, instantout_swaps.htlc_fee_rate, instantout_swaps.reservation_ids, instantout_swaps.swap_invoice, instantout_swaps.finalized_htlc_tx, instantout_swaps.sweep_tx_hash, instantout_swaps.finalized_sweepless_sweep_tx, instantout_swaps.sweep_confirmation_height, + htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index +FROM + swaps +JOIN + instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash +JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash +ORDER BY + swaps.id +` + +type GetInstantOutSwapsRow struct { + ID int32 + SwapHash []byte + Preimage []byte + InitiationTime time.Time + AmountRequested int64 + CltvExpiry int32 + MaxMinerFee int64 + MaxSwapFee int64 + InitiationHeight int32 + ProtocolVersion int32 + Label string + SwapHash_2 []byte + Preimage_2 []byte + SweepAddress string + OutgoingChanSet string + HtlcFeeRate int64 + ReservationIds []byte + SwapInvoice string + FinalizedHtlcTx []byte + SweepTxHash []byte + FinalizedSweeplessSweepTx []byte + SweepConfirmationHeight sql.NullInt32 + SwapHash_3 []byte + SenderScriptPubkey []byte + ReceiverScriptPubkey []byte + SenderInternalPubkey []byte + ReceiverInternalPubkey []byte + ClientKeyFamily int32 + ClientKeyIndex int32 +} + +func (q *Queries) GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsRow, error) { + rows, err := q.db.QueryContext(ctx, getInstantOutSwaps) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetInstantOutSwapsRow + for rows.Next() { + var i GetInstantOutSwapsRow + if err := rows.Scan( + &i.ID, + &i.SwapHash, + &i.Preimage, + &i.InitiationTime, + &i.AmountRequested, + &i.CltvExpiry, + &i.MaxMinerFee, + &i.MaxSwapFee, + &i.InitiationHeight, + &i.ProtocolVersion, + &i.Label, + &i.SwapHash_2, + &i.Preimage_2, + &i.SweepAddress, + &i.OutgoingChanSet, + &i.HtlcFeeRate, + &i.ReservationIds, + &i.SwapInvoice, + &i.FinalizedHtlcTx, + &i.SweepTxHash, + &i.FinalizedSweeplessSweepTx, + &i.SweepConfirmationHeight, + &i.SwapHash_3, + &i.SenderScriptPubkey, + &i.ReceiverScriptPubkey, + &i.SenderInternalPubkey, + &i.ReceiverInternalPubkey, + &i.ClientKeyFamily, + &i.ClientKeyIndex, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertInstantOut = `-- name: InsertInstantOut :exec +INSERT INTO instantout_swaps ( + swap_hash, + preimage, + sweep_address, + outgoing_chan_set, + htlc_fee_rate, + reservation_ids, + swap_invoice +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7 +) +` + +type InsertInstantOutParams struct { + SwapHash []byte + Preimage []byte + SweepAddress string + OutgoingChanSet string + HtlcFeeRate int64 + ReservationIds []byte + SwapInvoice string +} + +func (q *Queries) InsertInstantOut(ctx context.Context, arg InsertInstantOutParams) error { + _, err := q.db.ExecContext(ctx, insertInstantOut, + arg.SwapHash, + arg.Preimage, + arg.SweepAddress, + arg.OutgoingChanSet, + arg.HtlcFeeRate, + arg.ReservationIds, + arg.SwapInvoice, + ) + return err +} + +const insertInstantOutUpdate = `-- name: InsertInstantOutUpdate :exec +INSERT INTO instantout_updates ( + swap_hash, + update_state, + update_timestamp +) VALUES ( + $1, + $2, + $3 +) +` + +type InsertInstantOutUpdateParams struct { + SwapHash []byte + UpdateState string + UpdateTimestamp time.Time +} + +func (q *Queries) InsertInstantOutUpdate(ctx context.Context, arg InsertInstantOutUpdateParams) error { + _, err := q.db.ExecContext(ctx, insertInstantOutUpdate, arg.SwapHash, arg.UpdateState, arg.UpdateTimestamp) + return err +} + +const updateInstantOut = `-- name: UpdateInstantOut :exec +UPDATE instantout_swaps +SET + finalized_htlc_tx = $2, + sweep_tx_hash = $3, + finalized_sweepless_sweep_tx = $4, + sweep_confirmation_height = $5 +WHERE + instantout_swaps.swap_hash = $1 +` + +type UpdateInstantOutParams struct { + SwapHash []byte + FinalizedHtlcTx []byte + SweepTxHash []byte + FinalizedSweeplessSweepTx []byte + SweepConfirmationHeight sql.NullInt32 +} + +func (q *Queries) UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error { + _, err := q.db.ExecContext(ctx, updateInstantOut, + arg.SwapHash, + arg.FinalizedHtlcTx, + arg.SweepTxHash, + arg.FinalizedSweeplessSweepTx, + arg.SweepConfirmationHeight, + ) + return err +} diff --git a/loopdb/sqlc/migrations/000006_instantout.down.sql b/loopdb/sqlc/migrations/000006_instantout.down.sql new file mode 100644 index 0000000..8829e9a --- /dev/null +++ b/loopdb/sqlc/migrations/000006_instantout.down.sql @@ -0,0 +1,4 @@ +DROP INDEX IF EXISTS instantout_updates_swap_hash_idx; +DROP INDEX IF EXISTS instantout_swap_hash_idx; +DROP TABLE IF EXISTS instantout_updates; +DROP TABLE IF EXISTS instantout_swaps; \ No newline at end of file diff --git a/loopdb/sqlc/migrations/000006_instantout.up.sql b/loopdb/sqlc/migrations/000006_instantout.up.sql new file mode 100644 index 0000000..a7ccd29 --- /dev/null +++ b/loopdb/sqlc/migrations/000006_instantout.up.sql @@ -0,0 +1,52 @@ +CREATE TABLE IF NOT EXISTS instantout_swaps ( + -- swap_hash points to the parent swap hash. + swap_hash BLOB PRIMARY KEY, + + -- preimage is the preimage of the swap. + preimage BLOB NOT NULL, + + -- sweep_address is the address that the server should sweep the funds to. + sweep_address TEXT NOT NULL, + + -- outgoing_chan_set is the set of short ids of channels that may be used. + -- If empty, any channel may be used. + outgoing_chan_set TEXT NOT NULL, + + -- htlc_fee_rate is the fee rate in sat/kw that is used for the htlc transaction. + htlc_fee_rate BIGINT NOT NULL, + + -- reservation_ids is a list of ids of the reservations that are used for this swap. + reservation_ids BLOB NOT NULL, + + -- swap_invoice is the invoice that is to be paid by the client to + -- initiate the loop out swap. + swap_invoice TEXT NOT NULL, + + -- finalized_htlc_tx contains the fully signed htlc transaction. + finalized_htlc_tx BLOB, + + -- sweep_tx_hash is the hash of the transaction that sweeps the htlc. + sweep_tx_hash BLOB, + + -- finalized_sweepless_sweep_tx contains the fully signed sweepless sweep transaction. + finalized_sweepless_sweep_tx BLOB, + + -- sweep_confirmation_height is the block height at which the sweep transaction is confirmed. + sweep_confirmation_height INTEGER +); + +CREATE TABLE IF NOT EXISTS instantout_updates ( + -- id is auto incremented for each update. + id INTEGER PRIMARY KEY, + + -- swap_hash is the hash of the swap that this update is for. + swap_hash BLOB NOT NULL REFERENCES instantout_swaps(swap_hash), + + -- update_state is the state of the swap at the time of the update. + update_state TEXT NOT NULL, + + -- update_timestamp is the time at which the update was created. + update_timestamp TIMESTAMP NOT NULL +); + +CREATE INDEX IF NOT EXISTS instantout_updates_swap_hash_idx ON instantout_updates(swap_hash); diff --git a/loopdb/sqlc/models.go b/loopdb/sqlc/models.go index d10b67d..869bf62 100644 --- a/loopdb/sqlc/models.go +++ b/loopdb/sqlc/models.go @@ -19,6 +19,27 @@ type HtlcKey struct { ClientKeyIndex int32 } +type InstantoutSwap struct { + SwapHash []byte + Preimage []byte + SweepAddress string + OutgoingChanSet string + HtlcFeeRate int64 + ReservationIds []byte + SwapInvoice string + FinalizedHtlcTx []byte + SweepTxHash []byte + FinalizedSweeplessSweepTx []byte + SweepConfirmationHeight sql.NullInt32 +} + +type InstantoutUpdate struct { + ID int32 + SwapHash []byte + UpdateState string + UpdateTimestamp time.Time +} + type LiquidityParam struct { ID int32 Params []byte diff --git a/loopdb/sqlc/querier.go b/loopdb/sqlc/querier.go index a5578ba..bf9f71e 100644 --- a/loopdb/sqlc/querier.go +++ b/loopdb/sqlc/querier.go @@ -13,6 +13,9 @@ type Querier interface { CreateReservation(ctx context.Context, arg CreateReservationParams) error FetchLiquidityParams(ctx context.Context) ([]byte, error) GetBatchSweeps(ctx context.Context, batchID int32) ([]GetBatchSweepsRow, error) + GetInstantOutSwap(ctx context.Context, swapHash []byte) (GetInstantOutSwapRow, error) + GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte) ([]InstantoutUpdate, error) + GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsRow, error) GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopInSwapRow, error) GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, error) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopOutSwapRow, error) @@ -25,12 +28,15 @@ type Querier interface { GetUnconfirmedBatches(ctx context.Context) ([]SweepBatch, error) InsertBatch(ctx context.Context, arg InsertBatchParams) (int32, error) InsertHtlcKeys(ctx context.Context, arg InsertHtlcKeysParams) error + InsertInstantOut(ctx context.Context, arg InsertInstantOutParams) error + InsertInstantOutUpdate(ctx context.Context, arg InsertInstantOutUpdateParams) error InsertLoopIn(ctx context.Context, arg InsertLoopInParams) error InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) error InsertReservationUpdate(ctx context.Context, arg InsertReservationUpdateParams) error InsertSwap(ctx context.Context, arg InsertSwapParams) error InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdateParams) error UpdateBatch(ctx context.Context, arg UpdateBatchParams) error + UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error UpdateReservation(ctx context.Context, arg UpdateReservationParams) error UpsertLiquidityParams(ctx context.Context, params []byte) error UpsertSweep(ctx context.Context, arg UpsertSweepParams) error diff --git a/loopdb/sqlc/queries/instantout.sql b/loopdb/sqlc/queries/instantout.sql new file mode 100644 index 0000000..56f9052 --- /dev/null +++ b/loopdb/sqlc/queries/instantout.sql @@ -0,0 +1,75 @@ +-- name: InsertInstantOut :exec +INSERT INTO instantout_swaps ( + swap_hash, + preimage, + sweep_address, + outgoing_chan_set, + htlc_fee_rate, + reservation_ids, + swap_invoice +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7 +); + +-- name: UpdateInstantOut :exec +UPDATE instantout_swaps +SET + finalized_htlc_tx = $2, + sweep_tx_hash = $3, + finalized_sweepless_sweep_tx = $4, + sweep_confirmation_height = $5 +WHERE + instantout_swaps.swap_hash = $1; + +-- name: InsertInstantOutUpdate :exec +INSERT INTO instantout_updates ( + swap_hash, + update_state, + update_timestamp +) VALUES ( + $1, + $2, + $3 +); + +-- name: GetInstantOutSwap :one +SELECT + swaps.*, + instantout_swaps.*, + htlc_keys.* +FROM + swaps +JOIN + instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash +JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash +WHERE + swaps.swap_hash = $1; + +-- name: GetInstantOutSwaps :many +SELECT + swaps.*, + instantout_swaps.*, + htlc_keys.* +FROM + swaps +JOIN + instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash +JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash +ORDER BY + swaps.id; + +-- name: GetInstantOutSwapUpdates :many +SELECT + instantout_updates.* +FROM + instantout_updates +WHERE + instantout_updates.swap_hash = $1; diff --git a/looprpc/client.pb.go b/looprpc/client.pb.go index b049f8c..c1430be 100644 --- a/looprpc/client.pb.go +++ b/looprpc/client.pb.go @@ -613,6 +613,12 @@ type LoopOutRequest struct { //the wallet. This is used to flag whether this loop out swap could have its //associated sweep batched. IsExternalAddr bool `protobuf:"varint,17,opt,name=is_external_addr,json=isExternalAddr,proto3" json:"is_external_addr,omitempty"` + // + //The reservations to use for the swap. If this field is set, loop will try + //to use the instant out flow using the given reservations. If the + //reservations are not sufficient, the swap will fail. The swap amount must + //be equal to the sum of the amounts of the reservations. + ReservationIds [][]byte `protobuf:"bytes,18,rep,name=reservation_ids,json=reservationIds,proto3" json:"reservation_ids,omitempty"` } func (x *LoopOutRequest) Reset() { @@ -767,6 +773,13 @@ func (x *LoopOutRequest) GetIsExternalAddr() bool { return false } +func (x *LoopOutRequest) GetReservationIds() [][]byte { + if x != nil { + return x.ReservationIds + } + return nil +} + type LoopInRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3505,7 +3518,7 @@ type ClientReservation struct { Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // //The transaction id of the reservation. - TxId []byte `protobuf:"bytes,4,opt,name=tx_id,json=txId,proto3" json:"tx_id,omitempty"` + TxId string `protobuf:"bytes,4,opt,name=tx_id,json=txId,proto3" json:"tx_id,omitempty"` // //The vout of the reservation. Vout uint32 `protobuf:"varint,5,opt,name=vout,proto3" json:"vout,omitempty"` @@ -3567,11 +3580,11 @@ func (x *ClientReservation) GetAmount() uint64 { return 0 } -func (x *ClientReservation) GetTxId() []byte { +func (x *ClientReservation) GetTxId() string { if x != nil { return x.TxId } - return nil + return "" } func (x *ClientReservation) GetVout() uint32 { @@ -3588,13 +3601,143 @@ func (x *ClientReservation) GetExpiry() uint32 { return 0 } +type InstantOutRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // + //The reservations to use for the swap. + ReservationIds [][]byte `protobuf:"bytes,1,rep,name=reservation_ids,json=reservationIds,proto3" json:"reservation_ids,omitempty"` + // + //A restriction on the channel set that may be used to loop out. The actual + //channel(s) that will be used are selected based on the lowest routing fee + //for the swap payment to the server. + OutgoingChanSet []uint64 `protobuf:"varint,11,rep,packed,name=outgoing_chan_set,json=outgoingChanSet,proto3" json:"outgoing_chan_set,omitempty"` +} + +func (x *InstantOutRequest) Reset() { + *x = InstantOutRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstantOutRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstantOutRequest) ProtoMessage() {} + +func (x *InstantOutRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[36] + 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 InstantOutRequest.ProtoReflect.Descriptor instead. +func (*InstantOutRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{36} +} + +func (x *InstantOutRequest) GetReservationIds() [][]byte { + if x != nil { + return x.ReservationIds + } + return nil +} + +func (x *InstantOutRequest) GetOutgoingChanSet() []uint64 { + if x != nil { + return x.OutgoingChanSet + } + return nil +} + +type InstantOutResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // + //The hash of the swap preimage. + InstantOutHash []byte `protobuf:"bytes,1,opt,name=instant_out_hash,json=instantOutHash,proto3" json:"instant_out_hash,omitempty"` + // + //The transaction id of the sweep transaction. + SweepTxId string `protobuf:"bytes,2,opt,name=sweep_tx_id,json=sweepTxId,proto3" json:"sweep_tx_id,omitempty"` + // + //The state of the swap. + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *InstantOutResponse) Reset() { + *x = InstantOutResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstantOutResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstantOutResponse) ProtoMessage() {} + +func (x *InstantOutResponse) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[37] + 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 InstantOutResponse.ProtoReflect.Descriptor instead. +func (*InstantOutResponse) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{37} +} + +func (x *InstantOutResponse) GetInstantOutHash() []byte { + if x != nil { + return x.InstantOutHash + } + return nil +} + +func (x *InstantOutResponse) GetSweepTxId() string { + if x != nil { + return x.SweepTxId + } + return "" +} + +func (x *InstantOutResponse) GetState() string { + if x != nil { + return x.State + } + return "" +} + var File_client_proto protoreflect.FileDescriptor var file_client_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x1a, 0x1a, 0x73, 0x77, 0x61, 0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x05, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, + 0x6f, 0x74, 0x6f, 0x22, 0xdc, 0x05, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x14, @@ -3637,531 +3780,552 @@ var file_client_proto_rawDesc = []byte{ 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x22, 0xd4, 0x02, 0x0a, 0x0d, 0x4c, 0x6f, - 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x20, 0x0a, - 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x12, - 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4d, 0x69, 0x6e, 0x65, 0x72, - 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x23, - 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, - 0x74, 0x6c, 0x63, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, - 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, - 0x72, 0x12, 0x33, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, - 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 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, 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, 0xeb, 0x01, 0x0a, 0x0c, 0x53, 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, 0x19, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, - 0x12, 0x25, 0x0a, 0x0c, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x50, 0x32, 0x77, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x32, 0x74, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x74, - 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x10, - 0x0a, 0x0e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0xf7, 0x04, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, - 0x74, 0x12, 0x12, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 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, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, - 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0c, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x68, - 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x74, - 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x32, 0x77, 0x73, 0x68, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x32, 0x74, 0x72, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x50, 0x32, 0x74, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6f, 0x73, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x6e, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x73, - 0x74, 0x4f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x73, 0x74, - 0x5f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, - 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, - 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x11, 0x20, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x12, 0x20, 0x03, + 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x64, 0x73, 0x22, 0xd4, 0x02, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, + 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, + 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, + 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0b, 0x6d, 0x61, 0x78, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x28, 0x0a, 0x10, + 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x66, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x09, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x0b, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 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, 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, 0xeb, 0x01, 0x0a, 0x0c, 0x53, 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, 0x19, + 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0c, 0x68, 0x74, 0x6c, + 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 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, 0x74, + 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, 0x68, 0x12, 0x2a, + 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, + 0x32, 0x74, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x74, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x10, 0x0a, 0x0e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xf7, 0x04, 0x0a, 0x0a, 0x53, 0x77, + 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x12, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x02, 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, 0x0b, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x66, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6c, 0x61, + 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0c, + 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x5f, 0x70, 0x32, 0x77, 0x73, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x68, 0x74, 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x77, 0x73, + 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x5f, 0x70, 0x32, 0x74, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x74, + 0x6c, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x32, 0x74, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x21, + 0x0a, 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x6e, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x66, + 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, + 0x6f, 0x70, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, + 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x11, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x75, + 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x65, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x22, 0x56, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x10, 0x6c, 0x69, 0x73, 0x74, 0x5f, + 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x77, 0x61, 0x70, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x6c, 0x69, 0x73, + 0x74, 0x53, 0x77, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x9b, 0x02, 0x0a, 0x0f, + 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x44, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x77, 0x61, + 0x70, 0x54, 0x79, 0x70, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x08, 0x73, 0x77, 0x61, + 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, + 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, - 0x6e, 0x53, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x56, 0x0a, 0x10, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, - 0x0a, 0x10, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x22, 0x9b, 0x02, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, - 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x75, 0x74, 0x67, - 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, - 0x6c, 0x12, 0x27, 0x0a, 0x10, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, + 0x6e, 0x53, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x27, 0x0a, 0x10, 0x6c, 0x6f, + 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, + 0x48, 0x6f, 0x70, 0x22, 0x34, 0x0a, 0x0e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x0c, + 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, + 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x02, 0x22, 0x3e, 0x0a, 0x11, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, + 0x0a, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73, 0x22, 0x21, 0x0a, 0x0f, 0x53, 0x77, 0x61, + 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x0c, + 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7f, 0x0a, 0x0f, + 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, + 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, + 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4a, + 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, + 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0xcc, 0x01, + 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, + 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, + 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, + 0x65, 0x6c, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x43, + 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, + 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, + 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0xa8, 0x02, 0x0a, + 0x0c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, + 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x73, 0x77, 0x61, 0x70, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, - 0x70, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x22, 0x34, 0x0a, 0x0e, 0x53, 0x77, - 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x07, 0x0a, 0x03, - 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, - 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x02, - 0x22, 0x3e, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x73, 0x77, 0x61, 0x70, 0x73, - 0x22, 0x21, 0x0a, 0x0f, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x7f, 0x0a, 0x0f, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, - 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, - 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, - 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, - 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, - 0x08, 0x07, 0x10, 0x08, 0x22, 0xcc, 0x01, 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, - 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, - 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, - 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, - 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, - 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x43, 0x6c, 0x74, 0x76, - 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, - 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, - 0x07, 0x10, 0x08, 0x22, 0xa8, 0x02, 0x0a, 0x0c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x3a, 0x0a, 0x19, - 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x17, 0x73, 0x77, 0x61, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6c, 0x6f, 0x6f, 0x70, - 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x48, 0x6f, - 0x70, 0x12, 0x41, 0x0a, 0x13, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, - 0x6e, 0x74, 0x52, 0x10, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, - 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xb0, - 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, - 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x77, 0x61, 0x70, 0x46, 0x65, - 0x65, 0x53, 0x61, 0x74, 0x12, 0x2f, 0x0a, 0x14, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x46, - 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, - 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74, 0x76, 0x44, - 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, - 0x05, 0x22, 0xf3, 0x01, 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, - 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x77, - 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x70, - 0x61, 0x79, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0c, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x2b, - 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x65, 0x65, - 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, - 0x53, 0x77, 0x65, 0x65, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, - 0x77, 0x61, 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x77, 0x61, 0x70, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f, - 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74, - 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x70, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x62, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, - 0x74, 0x48, 0x6f, 0x70, 0x12, 0x33, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, - 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 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, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x72, 0x6f, - 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3c, 0x0a, 0x0e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, - 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0xcb, 0x02, 0x0a, 0x09, 0x4c, 0x73, - 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, - 0x62, 0x61, 0x73, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, - 0x29, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, - 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x69, 0x64, - 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x66, 0x65, 0x65, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, - 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, - 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xc8, 0x01, 0x0a, 0x09, 0x4c, 0x6f, 0x6f, 0x70, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, - 0x0a, 0x0f, 0x73, 0x75, 0x6d, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x75, 0x6d, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x75, 0x6d, 0x5f, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0f, 0x73, 0x75, 0x6d, 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x41, - 0x6d, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xc0, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x70, 0x63, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x70, 0x63, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, - 0x65, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x72, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x61, 0x74, - 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, - 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x38, 0x0a, 0x0e, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x6f, 0x75, - 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x52, 0x0c, 0x6c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, - 0x36, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0b, 0x6c, 0x6f, 0x6f, 0x70, - 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0x1b, 0x0a, 0x19, 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, 0x22, 0x94, 0x09, 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, - 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x05, - 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6f, - 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, - 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x65, - 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x65, 0x65, - 0x50, 0x70, 0x6d, 0x12, 0x3d, 0x0a, 0x1c, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x65, 0x65, - 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, - 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x73, 0x77, 0x65, 0x65, 0x70, - 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, - 0x74, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, - 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, - 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x2d, 0x0a, 0x13, 0x6d, - 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, - 0x70, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x75, - 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x3a, 0x0a, 0x1a, 0x6d, 0x61, - 0x78, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, - 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, - 0x65, 0x70, 0x61, 0x79, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, - 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x53, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x11, - 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x4d, 0x69, 0x6e, 0x65, - 0x72, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x77, 0x65, 0x65, 0x70, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x62, - 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, - 0x53, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x12, - 0x2e, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, - 0x65, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x61, 0x75, - 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, 0x61, 0x74, 0x12, - 0x3d, 0x0a, 0x19, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, - 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x16, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, - 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x63, 0x12, 0x2b, - 0x0a, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x6c, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x6f, - 0x4d, 0x61, 0x78, 0x49, 0x6e, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, - 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, - 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x68, - 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, - 0x70, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x44, 0x65, - 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4a, 0x0a, 0x22, 0x61, 0x75, 0x74, - 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, - 0x72, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1e, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, - 0x75, 0x64, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x50, 0x65, 0x72, 0x69, - 0x6f, 0x64, 0x53, 0x65, 0x63, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, - 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, - 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x61, 0x75, 0x74, - 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x52, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x61, 0x73, 0x79, 0x5f, 0x61, - 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, - 0x61, 0x73, 0x79, 0x41, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x12, 0x42, 0x0a, 0x1e, 0x65, - 0x61, 0x73, 0x79, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x1a, 0x65, 0x61, 0x73, 0x79, 0x41, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, - 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x61, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x11, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x18, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, 0x84, 0x02, 0x0a, 0x0d, - 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x68, 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, 0x2e, 0x0a, 0x09, - 0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, - 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, - 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, - 0x6f, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, - 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, - 0x6c, 0x64, 0x22, 0x59, 0x0a, 0x19, 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, 0x12, - 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 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, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x1c, 0x0a, - 0x1a, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, - 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x72, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 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, + 0x70, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x41, 0x0a, 0x13, 0x6c, 0x6f, + 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x6c, 0x6f, 0x6f, + 0x70, 0x49, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x51, 0x75, + 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, + 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0a, 0x73, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2f, 0x0a, + 0x14, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x66, 0x65, + 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x68, 0x74, 0x6c, + 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4a, 0x04, + 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0xf3, 0x01, 0x0a, 0x10, 0x4f, + 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x20, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, + 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x61, 0x6d, 0x74, 0x5f, + 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x70, 0x61, + 0x79, 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x53, 0x77, 0x65, 0x65, 0x70, 0x46, 0x65, + 0x65, 0x53, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0f, 0x73, 0x77, 0x61, 0x70, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, + 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x22, 0x70, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, + 0x6d, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x33, 0x0a, + 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 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, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x3c, 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x73, 0x22, 0xcb, 0x02, 0x0a, 0x09, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x62, 0x61, 0x73, 0x65, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, + 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, + 0x15, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x61, 0x69, + 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x72, 0x6f, + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0xc8, 0x01, 0x0a, 0x09, 0x4c, 0x6f, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x61, 0x69, 0x6c, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x61, + 0x69, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x75, 0x6d, 0x5f, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x73, 0x75, 0x6d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x74, 0x12, + 0x2a, 0x0a, 0x11, 0x73, 0x75, 0x6d, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, + 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x75, 0x6d, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x41, 0x6d, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc0, 0x02, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x70, 0x63, 0x5f, 0x6c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x70, 0x63, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x74, 0x4c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x6c, + 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x38, + 0x0a, 0x0e, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0c, 0x6c, 0x6f, 0x6f, 0x70, + 0x4f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x70, + 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x52, 0x0b, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x73, + 0x22, 0x1b, 0x0a, 0x19, 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, 0x22, 0x94, 0x09, + 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 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, 0x22, - 0x57, 0x0a, 0x12, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x5f, - 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, - 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x15, 0x0a, 0x13, 0x41, 0x62, 0x61, 0x6e, - 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x19, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x18, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x11, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, - 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x6f, 0x75, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x76, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x79, 0x2a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x54, - 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x01, 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, 0xbe, 0x02, 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, 0x12, 0x1c, 0x0a, 0x18, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x41, 0x42, - 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, 0x12, 0x31, 0x0a, 0x2d, 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, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, - 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x08, 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, 0xa3, 0x09, 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, + 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, + 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x3d, 0x0a, 0x1c, + 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, + 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x17, 0x73, 0x77, 0x65, 0x65, 0x70, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, + 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6d, + 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, + 0x65, 0x50, 0x70, 0x6d, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, + 0x50, 0x70, 0x6d, 0x12, 0x3a, 0x0a, 0x1a, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, + 0x79, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x70, + 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x70, + 0x61, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x50, 0x70, 0x6d, 0x12, + 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x5f, 0x73, 0x61, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x70, + 0x61, 0x79, 0x53, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, + 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x73, 0x77, 0x65, + 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x13, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, + 0x73, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x53, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, + 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, + 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, + 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, 0x61, 0x74, 0x12, 0x3d, 0x0a, 0x19, 0x61, 0x75, 0x74, 0x6f, + 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x16, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x63, 0x12, 0x2b, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x5f, + 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x4d, 0x61, 0x78, 0x49, 0x6e, 0x46, 0x6c, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x77, 0x61, 0x70, + 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, + 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, + 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, + 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x32, + 0x0a, 0x15, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, + 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x44, 0x65, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x4a, 0x0a, 0x22, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, + 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1e, + 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x12, 0x3f, + 0x0a, 0x1c, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, + 0x74, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x14, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x42, 0x75, + 0x64, 0x67, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, + 0x23, 0x0a, 0x0d, 0x65, 0x61, 0x73, 0x79, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x61, 0x73, 0x79, 0x41, 0x75, 0x74, 0x6f, + 0x6c, 0x6f, 0x6f, 0x70, 0x12, 0x42, 0x0a, 0x1e, 0x65, 0x61, 0x73, 0x79, 0x5f, 0x61, 0x75, 0x74, + 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x65, 0x61, + 0x73, 0x79, 0x41, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x6f, 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x54, 0x79, 0x70, 0x65, 0x22, 0x84, 0x02, 0x0a, 0x0d, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, + 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 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, 0x2e, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x73, 0x77, 0x61, + 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, + 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, + 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, + 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, + 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x12, + 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, + 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, + 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x59, 0x0a, 0x19, 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, 0x12, 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, + 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, + 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x72, 0x0a, 0x0c, 0x44, + 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x68, 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, 0x22, 0x57, 0x0a, 0x12, 0x41, 0x62, 0x61, 0x6e, + 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, + 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, + 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, + 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, + 0x67, 0x22, 0x15, 0x0a, 0x13, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3e, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0xa9, 0x01, 0x0a, 0x11, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, + 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x76, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x76, + 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x68, 0x0a, 0x11, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, + 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x0b, + 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, + 0x61, 0x6e, 0x53, 0x65, 0x74, 0x22, 0x74, 0x0a, 0x12, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, + 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x74, + 0x78, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x77, 0x65, 0x65, + 0x70, 0x54, 0x78, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, 0x3b, 0x0a, 0x0b, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x44, + 0x44, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, + 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, + 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x01, 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, 0xbe, 0x02, 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, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, + 0x10, 0x07, 0x12, 0x31, 0x0a, 0x2d, 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, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, + 0x4e, 0x43, 0x45, 0x10, 0x08, 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, + 0xea, 0x09, 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, 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, 0x48, 0x0a, 0x0b, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, - 0x61, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, - 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, - 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, + 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, 0x48, 0x0a, 0x0b, + 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 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, 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, 0x1a, 0x23, 0x2e, 0x6c, 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, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, - 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 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, 0x12, 0x57, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, - 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, + 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 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, 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, 0x1a, 0x23, 0x2e, 0x6c, 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, + 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, + 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x6c, 0x6f, 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, 0x12, 0x57, 0x0a, 0x10, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4f, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x4f, 0x75, 0x74, 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, @@ -4180,7 +4344,7 @@ func file_client_proto_rawDescGZIP() []byte { } var file_client_proto_enumTypes = make([]protoimpl.EnumInfo, 7) -var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 36) +var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 38) var file_client_proto_goTypes = []interface{}{ (AddressType)(0), // 0: looprpc.AddressType (SwapType)(0), // 1: looprpc.SwapType @@ -4225,19 +4389,21 @@ var file_client_proto_goTypes = []interface{}{ (*ListReservationsRequest)(nil), // 40: looprpc.ListReservationsRequest (*ListReservationsResponse)(nil), // 41: looprpc.ListReservationsResponse (*ClientReservation)(nil), // 42: looprpc.ClientReservation - (*swapserverrpc.RouteHint)(nil), // 43: looprpc.RouteHint + (*InstantOutRequest)(nil), // 43: looprpc.InstantOutRequest + (*InstantOutResponse)(nil), // 44: looprpc.InstantOutResponse + (*swapserverrpc.RouteHint)(nil), // 45: looprpc.RouteHint } var file_client_proto_depIdxs = []int32{ 0, // 0: looprpc.LoopOutRequest.account_addr_type:type_name -> looprpc.AddressType - 43, // 1: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint + 45, // 1: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint 1, // 2: looprpc.SwapStatus.type:type_name -> looprpc.SwapType 2, // 3: looprpc.SwapStatus.state:type_name -> looprpc.SwapState 3, // 4: looprpc.SwapStatus.failure_reason:type_name -> looprpc.FailureReason 13, // 5: looprpc.ListSwapsRequest.list_swap_filter:type_name -> looprpc.ListSwapsFilter 6, // 6: looprpc.ListSwapsFilter.swap_type:type_name -> looprpc.ListSwapsFilter.SwapTypeFilter 11, // 7: looprpc.ListSwapsResponse.swaps:type_name -> looprpc.SwapStatus - 43, // 8: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint - 43, // 9: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint + 45, // 8: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint + 45, // 9: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint 26, // 10: looprpc.TokensResponse.tokens:type_name -> looprpc.LsatToken 27, // 11: looprpc.GetInfoResponse.loop_out_stats:type_name -> looprpc.LoopStats 27, // 12: looprpc.GetInfoResponse.loop_in_stats:type_name -> looprpc.LoopStats @@ -4268,25 +4434,27 @@ var file_client_proto_depIdxs = []int32{ 33, // 37: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest 35, // 38: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest 40, // 39: looprpc.SwapClient.ListReservations:input_type -> looprpc.ListReservationsRequest - 9, // 40: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse - 9, // 41: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse - 11, // 42: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus - 14, // 43: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse - 11, // 44: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus - 39, // 45: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse - 18, // 46: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse - 21, // 47: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse - 17, // 48: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse - 20, // 49: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse - 23, // 50: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse - 25, // 51: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse - 29, // 52: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse - 31, // 53: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters - 34, // 54: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse - 37, // 55: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse - 41, // 56: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse - 40, // [40:57] is the sub-list for method output_type - 23, // [23:40] is the sub-list for method input_type + 43, // 40: looprpc.SwapClient.InstantOut:input_type -> looprpc.InstantOutRequest + 9, // 41: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse + 9, // 42: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse + 11, // 43: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus + 14, // 44: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse + 11, // 45: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus + 39, // 46: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse + 18, // 47: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse + 21, // 48: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse + 17, // 49: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse + 20, // 50: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse + 23, // 51: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse + 25, // 52: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse + 29, // 53: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse + 31, // 54: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters + 34, // 55: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse + 37, // 56: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse + 41, // 57: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse + 44, // 58: looprpc.SwapClient.InstantOut:output_type -> looprpc.InstantOutResponse + 41, // [41:59] is the sub-list for method output_type + 23, // [23:41] 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 @@ -4730,6 +4898,30 @@ func file_client_proto_init() { return nil } } + file_client_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstantOutRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstantOutResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -4737,7 +4929,7 @@ func file_client_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_client_proto_rawDesc, NumEnums: 7, - NumMessages: 36, + NumMessages: 38, NumExtensions: 0, NumServices: 1, }, diff --git a/looprpc/client.proto b/looprpc/client.proto index 7343dd7..890c90e 100644 --- a/looprpc/client.proto +++ b/looprpc/client.proto @@ -115,6 +115,11 @@ service SwapClient { */ rpc ListReservations (ListReservationsRequest) returns (ListReservationsResponse); + + /* loop: `instantout` + InstantOut initiates an instant out swap with the given parameters. + */ + rpc InstantOut (InstantOutRequest) returns (InstantOutResponse); } message LoopOutRequest { @@ -242,6 +247,14 @@ message LoopOutRequest { associated sweep batched. */ bool is_external_addr = 17; + + /* + The reservations to use for the swap. If this field is set, loop will try + to use the instant out flow using the given reservations. If the + reservations are not sufficient, the swap will fail. The swap amount must + be equal to the sum of the amounts of the reservations. + */ + repeated bytes reservation_ids = 18; } /* @@ -1284,7 +1297,7 @@ message ClientReservation { /* The transaction id of the reservation. */ - bytes tx_id = 4; + string tx_id = 4; /* The vout of the reservation. */ @@ -1293,4 +1306,33 @@ message ClientReservation { The expiry of the reservation. */ uint32 expiry = 6; +} + +message InstantOutRequest { + /* + The reservations to use for the swap. + */ + repeated bytes reservation_ids = 1; + + /* + A restriction on the channel set that may be used to loop out. The actual + channel(s) that will be used are selected based on the lowest routing fee + for the swap payment to the server. + */ + repeated uint64 outgoing_chan_set = 11; +} + +message InstantOutResponse { + /* + The hash of the swap preimage. + */ + bytes instant_out_hash = 1; + /* + The transaction id of the sweep transaction. + */ + string sweep_tx_id = 2; + /* + The state of the swap. + */ + string state = 3; } \ No newline at end of file diff --git a/looprpc/client.swagger.json b/looprpc/client.swagger.json index ab0120a..75a9e49 100644 --- a/looprpc/client.swagger.json +++ b/looprpc/client.swagger.json @@ -604,7 +604,6 @@ }, "tx_id": { "type": "string", - "format": "byte", "description": "The transaction id of the reservation." }, "vout": { @@ -760,6 +759,24 @@ } } }, + "looprpcInstantOutResponse": { + "type": "object", + "properties": { + "instant_out_hash": { + "type": "string", + "format": "byte", + "description": "The hash of the swap preimage." + }, + "sweep_tx_id": { + "type": "string", + "description": "The transaction id of the sweep transaction." + }, + "state": { + "type": "string", + "description": "The state of the swap." + } + } + }, "looprpcLiquidityParameters": { "type": "object", "properties": { @@ -1114,6 +1131,14 @@ "is_external_addr": { "type": "boolean", "description": "A flag indicating whether the defined destination address does not belong to\nthe wallet. This is used to flag whether this loop out swap could have its\nassociated sweep batched." + }, + "reservation_ids": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The reservations to use for the swap. If this field is set, loop will try\nto use the instant out flow using the given reservations. If the\nreservations are not sufficient, the swap will fail. The swap amount must\nbe equal to the sum of the amounts of the reservations." } } }, diff --git a/looprpc/client_grpc.pb.go b/looprpc/client_grpc.pb.go index f902806..fd6d54e 100644 --- a/looprpc/client_grpc.pb.go +++ b/looprpc/client_grpc.pb.go @@ -86,6 +86,9 @@ type SwapClientClient interface { // loop: `listreservations` //ListReservations returns a list of all reservations the server opened to us. ListReservations(ctx context.Context, in *ListReservationsRequest, opts ...grpc.CallOption) (*ListReservationsResponse, error) + // loop: `instantout` + //InstantOut initiates an instant out swap with the given parameters. + InstantOut(ctx context.Context, in *InstantOutRequest, opts ...grpc.CallOption) (*InstantOutResponse, error) } type swapClientClient struct { @@ -272,6 +275,15 @@ func (c *swapClientClient) ListReservations(ctx context.Context, in *ListReserva return out, nil } +func (c *swapClientClient) InstantOut(ctx context.Context, in *InstantOutRequest, opts ...grpc.CallOption) (*InstantOutResponse, error) { + out := new(InstantOutResponse) + err := c.cc.Invoke(ctx, "/looprpc.SwapClient/InstantOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SwapClientServer is the server API for SwapClient service. // All implementations must embed UnimplementedSwapClientServer // for forward compatibility @@ -344,6 +356,9 @@ type SwapClientServer interface { // loop: `listreservations` //ListReservations returns a list of all reservations the server opened to us. ListReservations(context.Context, *ListReservationsRequest) (*ListReservationsResponse, error) + // loop: `instantout` + //InstantOut initiates an instant out swap with the given parameters. + InstantOut(context.Context, *InstantOutRequest) (*InstantOutResponse, error) mustEmbedUnimplementedSwapClientServer() } @@ -402,6 +417,9 @@ func (UnimplementedSwapClientServer) SuggestSwaps(context.Context, *SuggestSwaps func (UnimplementedSwapClientServer) ListReservations(context.Context, *ListReservationsRequest) (*ListReservationsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListReservations not implemented") } +func (UnimplementedSwapClientServer) InstantOut(context.Context, *InstantOutRequest) (*InstantOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method InstantOut not implemented") +} func (UnimplementedSwapClientServer) mustEmbedUnimplementedSwapClientServer() {} // UnsafeSwapClientServer may be embedded to opt out of forward compatibility for this service. @@ -724,6 +742,24 @@ func _SwapClient_ListReservations_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _SwapClient_InstantOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InstantOutRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SwapClientServer).InstantOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.SwapClient/InstantOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SwapClientServer).InstantOut(ctx, req.(*InstantOutRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SwapClient_ServiceDesc is the grpc.ServiceDesc for SwapClient service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -795,6 +831,10 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListReservations", Handler: _SwapClient_ListReservations_Handler, }, + { + MethodName: "InstantOut", + Handler: _SwapClient_InstantOut_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/looprpc/swapclient.pb.json.go b/looprpc/swapclient.pb.json.go index 516b433..ecec5eb 100644 --- a/looprpc/swapclient.pb.json.go +++ b/looprpc/swapclient.pb.json.go @@ -462,4 +462,29 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex } callback(string(respBytes), nil) } + + registry["looprpc.SwapClient.InstantOut"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &InstantOutRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewSwapClientClient(conn) + resp, err := client.InstantOut(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } } diff --git a/scripts/fsm-generate.sh b/scripts/fsm-generate.sh index 60e6803..4ab2d74 100755 --- a/scripts/fsm-generate.sh +++ b/scripts/fsm-generate.sh @@ -1,3 +1,4 @@ #!/usr/bin/env bash go run ./fsm/stateparser/stateparser.go --out ./fsm/example_fsm.md --fsm example -go run ./fsm/stateparser/stateparser.go --out ./reservation/reservation_fsm.md --fsm reservation \ No newline at end of file +go run ./fsm/stateparser/stateparser.go --out ./reservation/reservation_fsm.md --fsm reservation +go run ./fsm/stateparser/stateparser.go --out ./instantout/fsm.md --fsm instantout \ No newline at end of file diff --git a/swapserverrpc/instantout.pb.go b/swapserverrpc/instantout.pb.go new file mode 100644 index 0000000..ac809b7 --- /dev/null +++ b/swapserverrpc/instantout.pb.go @@ -0,0 +1,1090 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v3.6.1 +// source: instantout.proto + +// We can't change this to swapserverrpc, it would be a breaking change because +// the package name is also contained in the HTTP URIs and old clients would +// call the wrong endpoints. Luckily with the go_package option we can have +// different golang and RPC package names to fix protobuf namespace conflicts. + +package swapserverrpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type InstantOutProtocolVersion int32 + +const ( + InstantOutProtocolVersion_INSTANTOUT_NONE InstantOutProtocolVersion = 0 + InstantOutProtocolVersion_INSTANTOUT_FULL_RESERVATION InstantOutProtocolVersion = 1 +) + +// Enum value maps for InstantOutProtocolVersion. +var ( + InstantOutProtocolVersion_name = map[int32]string{ + 0: "INSTANTOUT_NONE", + 1: "INSTANTOUT_FULL_RESERVATION", + } + InstantOutProtocolVersion_value = map[string]int32{ + "INSTANTOUT_NONE": 0, + "INSTANTOUT_FULL_RESERVATION": 1, + } +) + +func (x InstantOutProtocolVersion) Enum() *InstantOutProtocolVersion { + p := new(InstantOutProtocolVersion) + *p = x + return p +} + +func (x InstantOutProtocolVersion) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (InstantOutProtocolVersion) Descriptor() protoreflect.EnumDescriptor { + return file_instantout_proto_enumTypes[0].Descriptor() +} + +func (InstantOutProtocolVersion) Type() protoreflect.EnumType { + return &file_instantout_proto_enumTypes[0] +} + +func (x InstantOutProtocolVersion) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use InstantOutProtocolVersion.Descriptor instead. +func (InstantOutProtocolVersion) EnumDescriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{0} +} + +type InstantLoopOutRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Htlc related fields: + // The key for the htlc preimage spending path. + ReceiverKey []byte `protobuf:"bytes,1,opt,name=receiver_key,json=receiverKey,proto3" json:"receiver_key,omitempty"` + // The hash of the preimage that will be used to settle the htlc. + SwapHash []byte `protobuf:"bytes,2,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The requested absolute block height of the on-chain htlc. + Expiry int32 `protobuf:"varint,3,opt,name=expiry,proto3" json:"expiry,omitempty"` + // The fee rate in sat/kw that should be used for the htlc. + HtlcFeeRate uint64 `protobuf:"varint,4,opt,name=htlc_fee_rate,json=htlcFeeRate,proto3" json:"htlc_fee_rate,omitempty"` + // The reservations used as the inputs. + ReservationIds [][]byte `protobuf:"bytes,5,rep,name=reservation_ids,json=reservationIds,proto3" json:"reservation_ids,omitempty"` + // The protocol version to use for the swap. + ProtocolVersion InstantOutProtocolVersion `protobuf:"varint,6,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.InstantOutProtocolVersion" json:"protocol_version,omitempty"` +} + +func (x *InstantLoopOutRequest) Reset() { + *x = InstantLoopOutRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstantLoopOutRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstantLoopOutRequest) ProtoMessage() {} + +func (x *InstantLoopOutRequest) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[0] + 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 InstantLoopOutRequest.ProtoReflect.Descriptor instead. +func (*InstantLoopOutRequest) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{0} +} + +func (x *InstantLoopOutRequest) GetReceiverKey() []byte { + if x != nil { + return x.ReceiverKey + } + return nil +} + +func (x *InstantLoopOutRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *InstantLoopOutRequest) GetExpiry() int32 { + if x != nil { + return x.Expiry + } + return 0 +} + +func (x *InstantLoopOutRequest) GetHtlcFeeRate() uint64 { + if x != nil { + return x.HtlcFeeRate + } + return 0 +} + +func (x *InstantLoopOutRequest) GetReservationIds() [][]byte { + if x != nil { + return x.ReservationIds + } + return nil +} + +func (x *InstantLoopOutRequest) GetProtocolVersion() InstantOutProtocolVersion { + if x != nil { + return x.ProtocolVersion + } + return InstantOutProtocolVersion_INSTANTOUT_NONE +} + +type InstantLoopOutResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The swap invoice that the client should pay. + SwapInvoice string `protobuf:"bytes,1,opt,name=swap_invoice,json=swapInvoice,proto3" json:"swap_invoice,omitempty"` + // the key for the htlc expiry path. + SenderKey []byte `protobuf:"bytes,2,opt,name=sender_key,json=senderKey,proto3" json:"sender_key,omitempty"` +} + +func (x *InstantLoopOutResponse) Reset() { + *x = InstantLoopOutResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstantLoopOutResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstantLoopOutResponse) ProtoMessage() {} + +func (x *InstantLoopOutResponse) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[1] + 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 InstantLoopOutResponse.ProtoReflect.Descriptor instead. +func (*InstantLoopOutResponse) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{1} +} + +func (x *InstantLoopOutResponse) GetSwapInvoice() string { + if x != nil { + return x.SwapInvoice + } + return "" +} + +func (x *InstantLoopOutResponse) GetSenderKey() []byte { + if x != nil { + return x.SenderKey + } + return nil +} + +type PollPaymentAcceptedRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the swap invoice. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` +} + +func (x *PollPaymentAcceptedRequest) Reset() { + *x = PollPaymentAcceptedRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PollPaymentAcceptedRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollPaymentAcceptedRequest) ProtoMessage() {} + +func (x *PollPaymentAcceptedRequest) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[2] + 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 PollPaymentAcceptedRequest.ProtoReflect.Descriptor instead. +func (*PollPaymentAcceptedRequest) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{2} +} + +func (x *PollPaymentAcceptedRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +type PollPaymentAcceptedResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Whether the payment has been accepted. + Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"` +} + +func (x *PollPaymentAcceptedResponse) Reset() { + *x = PollPaymentAcceptedResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PollPaymentAcceptedResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollPaymentAcceptedResponse) ProtoMessage() {} + +func (x *PollPaymentAcceptedResponse) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[3] + 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 PollPaymentAcceptedResponse.ProtoReflect.Descriptor instead. +func (*PollPaymentAcceptedResponse) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{3} +} + +func (x *PollPaymentAcceptedResponse) GetAccepted() bool { + if x != nil { + return x.Accepted + } + return false +} + +type InitHtlcSigRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the swap invoice. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The nonces that the client will use to generate the htlc sigs. + HtlcClientNonces [][]byte `protobuf:"bytes,2,rep,name=htlc_client_nonces,json=htlcClientNonces,proto3" json:"htlc_client_nonces,omitempty"` +} + +func (x *InitHtlcSigRequest) Reset() { + *x = InitHtlcSigRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitHtlcSigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitHtlcSigRequest) ProtoMessage() {} + +func (x *InitHtlcSigRequest) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[4] + 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 InitHtlcSigRequest.ProtoReflect.Descriptor instead. +func (*InitHtlcSigRequest) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{4} +} + +func (x *InitHtlcSigRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *InitHtlcSigRequest) GetHtlcClientNonces() [][]byte { + if x != nil { + return x.HtlcClientNonces + } + return nil +} + +type InitHtlcSigResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The nonces that the server will use to generate the htlc sigs. + HtlcServerNonces [][]byte `protobuf:"bytes,2,rep,name=htlc_server_nonces,json=htlcServerNonces,proto3" json:"htlc_server_nonces,omitempty"` +} + +func (x *InitHtlcSigResponse) Reset() { + *x = InitHtlcSigResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitHtlcSigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitHtlcSigResponse) ProtoMessage() {} + +func (x *InitHtlcSigResponse) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[5] + 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 InitHtlcSigResponse.ProtoReflect.Descriptor instead. +func (*InitHtlcSigResponse) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{5} +} + +func (x *InitHtlcSigResponse) GetHtlcServerNonces() [][]byte { + if x != nil { + return x.HtlcServerNonces + } + return nil +} + +type PushHtlcSigRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the swap invoice. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The sigs that the client generated for the reservation inputs. + ClientSigs [][]byte `protobuf:"bytes,2,rep,name=client_sigs,json=clientSigs,proto3" json:"client_sigs,omitempty"` +} + +func (x *PushHtlcSigRequest) Reset() { + *x = PushHtlcSigRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushHtlcSigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushHtlcSigRequest) ProtoMessage() {} + +func (x *PushHtlcSigRequest) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[6] + 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 PushHtlcSigRequest.ProtoReflect.Descriptor instead. +func (*PushHtlcSigRequest) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{6} +} + +func (x *PushHtlcSigRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *PushHtlcSigRequest) GetClientSigs() [][]byte { + if x != nil { + return x.ClientSigs + } + return nil +} + +type PushHtlcSigResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The sigs that the server generated for the reservation inputs. + ServerSigs [][]byte `protobuf:"bytes,1,rep,name=server_sigs,json=serverSigs,proto3" json:"server_sigs,omitempty"` +} + +func (x *PushHtlcSigResponse) Reset() { + *x = PushHtlcSigResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushHtlcSigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushHtlcSigResponse) ProtoMessage() {} + +func (x *PushHtlcSigResponse) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[7] + 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 PushHtlcSigResponse.ProtoReflect.Descriptor instead. +func (*PushHtlcSigResponse) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{7} +} + +func (x *PushHtlcSigResponse) GetServerSigs() [][]byte { + if x != nil { + return x.ServerSigs + } + return nil +} + +type PushPreimageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The preimage that the client generated for the swap. + Preimage []byte `protobuf:"bytes,1,opt,name=preimage,proto3" json:"preimage,omitempty"` + // The nonces that the client used to generate the sweepless sweep sigs. + ClientNonces [][]byte `protobuf:"bytes,2,rep,name=client_nonces,json=clientNonces,proto3" json:"client_nonces,omitempty"` + // The address that the client wants to sweep the htlc to. + ClientSweepAddr string `protobuf:"bytes,3,opt,name=client_sweep_addr,json=clientSweepAddr,proto3" json:"client_sweep_addr,omitempty"` + // The fee rate in sat/kw that the client wants to use for the sweep. + MusigTxFeeRate uint64 `protobuf:"varint,4,opt,name=musig_tx_fee_rate,json=musigTxFeeRate,proto3" json:"musig_tx_fee_rate,omitempty"` +} + +func (x *PushPreimageRequest) Reset() { + *x = PushPreimageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushPreimageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushPreimageRequest) ProtoMessage() {} + +func (x *PushPreimageRequest) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[8] + 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 PushPreimageRequest.ProtoReflect.Descriptor instead. +func (*PushPreimageRequest) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{8} +} + +func (x *PushPreimageRequest) GetPreimage() []byte { + if x != nil { + return x.Preimage + } + return nil +} + +func (x *PushPreimageRequest) GetClientNonces() [][]byte { + if x != nil { + return x.ClientNonces + } + return nil +} + +func (x *PushPreimageRequest) GetClientSweepAddr() string { + if x != nil { + return x.ClientSweepAddr + } + return "" +} + +func (x *PushPreimageRequest) GetMusigTxFeeRate() uint64 { + if x != nil { + return x.MusigTxFeeRate + } + return 0 +} + +type PushPreimageResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The sweep sigs that the server generated for the htlc. + Musig2SweepSigs [][]byte `protobuf:"bytes,1,rep,name=musig2_sweep_sigs,json=musig2SweepSigs,proto3" json:"musig2_sweep_sigs,omitempty"` + // The nonces that the server used to generate the sweepless sweep sigs. + ServerNonces [][]byte `protobuf:"bytes,2,rep,name=server_nonces,json=serverNonces,proto3" json:"server_nonces,omitempty"` +} + +func (x *PushPreimageResponse) Reset() { + *x = PushPreimageResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushPreimageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushPreimageResponse) ProtoMessage() {} + +func (x *PushPreimageResponse) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[9] + 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 PushPreimageResponse.ProtoReflect.Descriptor instead. +func (*PushPreimageResponse) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{9} +} + +func (x *PushPreimageResponse) GetMusig2SweepSigs() [][]byte { + if x != nil { + return x.Musig2SweepSigs + } + return nil +} + +func (x *PushPreimageResponse) GetServerNonces() [][]byte { + if x != nil { + return x.ServerNonces + } + return nil +} + +type CancelInstantSwapRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the swap invoice. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` +} + +func (x *CancelInstantSwapRequest) Reset() { + *x = CancelInstantSwapRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CancelInstantSwapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelInstantSwapRequest) ProtoMessage() {} + +func (x *CancelInstantSwapRequest) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[10] + 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 CancelInstantSwapRequest.ProtoReflect.Descriptor instead. +func (*CancelInstantSwapRequest) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{10} +} + +func (x *CancelInstantSwapRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +type CancelInstantSwapResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *CancelInstantSwapResponse) Reset() { + *x = CancelInstantSwapResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_instantout_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CancelInstantSwapResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelInstantSwapResponse) ProtoMessage() {} + +func (x *CancelInstantSwapResponse) ProtoReflect() protoreflect.Message { + mi := &file_instantout_proto_msgTypes[11] + 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 CancelInstantSwapResponse.ProtoReflect.Descriptor instead. +func (*CancelInstantSwapResponse) Descriptor() ([]byte, []int) { + return file_instantout_proto_rawDescGZIP(), []int{11} +} + +var File_instantout_proto protoreflect.FileDescriptor + +var file_instantout_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x6f, 0x75, 0x74, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x22, 0x8b, 0x02, 0x0a, 0x15, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, + 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x22, 0x0a, + 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x4d, 0x0a, 0x10, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5a, 0x0a, 0x16, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x77, 0x61, 0x70, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x4b, 0x65, 0x79, 0x22, 0x39, 0x0a, 0x1a, 0x50, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, + 0x22, 0x39, 0x0a, 0x1b, 0x50, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x22, 0x5f, 0x0a, 0x12, 0x49, + 0x6e, 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2c, + 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, + 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x68, 0x74, 0x6c, 0x63, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x13, + 0x49, 0x6e, 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x10, 0x68, 0x74, 0x6c, 0x63, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, + 0x73, 0x22, 0x52, 0x0a, 0x12, 0x50, 0x75, 0x73, 0x68, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, + 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x53, 0x69, 0x67, 0x73, 0x22, 0x36, 0x0a, 0x13, 0x50, 0x75, 0x73, 0x68, 0x48, 0x74, 0x6c, + 0x63, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x69, 0x67, 0x73, 0x22, 0xad, 0x01, + 0x0a, 0x13, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x75, 0x73, 0x69, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, + 0x75, 0x73, 0x69, 0x67, 0x54, 0x78, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x67, 0x0a, + 0x14, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x73, 0x69, 0x67, 0x32, 0x5f, + 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, + 0x52, 0x0f, 0x6d, 0x75, 0x73, 0x69, 0x67, 0x32, 0x53, 0x77, 0x65, 0x65, 0x70, 0x53, 0x69, 0x67, + 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x37, 0x0a, 0x18, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x22, + 0x1b, 0x0a, 0x19, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x51, 0x0a, 0x19, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x53, + 0x54, 0x41, 0x4e, 0x54, 0x4f, 0x55, 0x54, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1f, + 0x0a, 0x1b, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x4f, 0x55, 0x54, 0x5f, 0x46, 0x55, 0x4c, + 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x32, + 0x8c, 0x04, 0x0a, 0x11, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x53, 0x77, 0x61, 0x70, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x15, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x1e, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x60, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, + 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x48, + 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, + 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x50, + 0x75, 0x73, 0x68, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, + 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x53, + 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, + 0x5a, 0x2b, 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, + 0x73, 0x77, 0x61, 0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_instantout_proto_rawDescOnce sync.Once + file_instantout_proto_rawDescData = file_instantout_proto_rawDesc +) + +func file_instantout_proto_rawDescGZIP() []byte { + file_instantout_proto_rawDescOnce.Do(func() { + file_instantout_proto_rawDescData = protoimpl.X.CompressGZIP(file_instantout_proto_rawDescData) + }) + return file_instantout_proto_rawDescData +} + +var file_instantout_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_instantout_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_instantout_proto_goTypes = []interface{}{ + (InstantOutProtocolVersion)(0), // 0: looprpc.InstantOutProtocolVersion + (*InstantLoopOutRequest)(nil), // 1: looprpc.InstantLoopOutRequest + (*InstantLoopOutResponse)(nil), // 2: looprpc.InstantLoopOutResponse + (*PollPaymentAcceptedRequest)(nil), // 3: looprpc.PollPaymentAcceptedRequest + (*PollPaymentAcceptedResponse)(nil), // 4: looprpc.PollPaymentAcceptedResponse + (*InitHtlcSigRequest)(nil), // 5: looprpc.InitHtlcSigRequest + (*InitHtlcSigResponse)(nil), // 6: looprpc.InitHtlcSigResponse + (*PushHtlcSigRequest)(nil), // 7: looprpc.PushHtlcSigRequest + (*PushHtlcSigResponse)(nil), // 8: looprpc.PushHtlcSigResponse + (*PushPreimageRequest)(nil), // 9: looprpc.PushPreimageRequest + (*PushPreimageResponse)(nil), // 10: looprpc.PushPreimageResponse + (*CancelInstantSwapRequest)(nil), // 11: looprpc.CancelInstantSwapRequest + (*CancelInstantSwapResponse)(nil), // 12: looprpc.CancelInstantSwapResponse +} +var file_instantout_proto_depIdxs = []int32{ + 0, // 0: looprpc.InstantLoopOutRequest.protocol_version:type_name -> looprpc.InstantOutProtocolVersion + 1, // 1: looprpc.InstantSwapServer.RequestInstantLoopOut:input_type -> looprpc.InstantLoopOutRequest + 3, // 2: looprpc.InstantSwapServer.PollPaymentAccepted:input_type -> looprpc.PollPaymentAcceptedRequest + 5, // 3: looprpc.InstantSwapServer.InitHtlcSig:input_type -> looprpc.InitHtlcSigRequest + 7, // 4: looprpc.InstantSwapServer.PushHtlcSig:input_type -> looprpc.PushHtlcSigRequest + 9, // 5: looprpc.InstantSwapServer.PushPreimage:input_type -> looprpc.PushPreimageRequest + 11, // 6: looprpc.InstantSwapServer.CancelInstantSwap:input_type -> looprpc.CancelInstantSwapRequest + 2, // 7: looprpc.InstantSwapServer.RequestInstantLoopOut:output_type -> looprpc.InstantLoopOutResponse + 4, // 8: looprpc.InstantSwapServer.PollPaymentAccepted:output_type -> looprpc.PollPaymentAcceptedResponse + 6, // 9: looprpc.InstantSwapServer.InitHtlcSig:output_type -> looprpc.InitHtlcSigResponse + 8, // 10: looprpc.InstantSwapServer.PushHtlcSig:output_type -> looprpc.PushHtlcSigResponse + 10, // 11: looprpc.InstantSwapServer.PushPreimage:output_type -> looprpc.PushPreimageResponse + 12, // 12: looprpc.InstantSwapServer.CancelInstantSwap:output_type -> looprpc.CancelInstantSwapResponse + 7, // [7:13] is the sub-list for method output_type + 1, // [1:7] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_instantout_proto_init() } +func file_instantout_proto_init() { + if File_instantout_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_instantout_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstantLoopOutRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstantLoopOutResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PollPaymentAcceptedRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PollPaymentAcceptedResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitHtlcSigRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitHtlcSigResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushHtlcSigRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushHtlcSigResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushPreimageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushPreimageResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelInstantSwapRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_instantout_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelInstantSwapResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_instantout_proto_rawDesc, + NumEnums: 1, + NumMessages: 12, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_instantout_proto_goTypes, + DependencyIndexes: file_instantout_proto_depIdxs, + EnumInfos: file_instantout_proto_enumTypes, + MessageInfos: file_instantout_proto_msgTypes, + }.Build() + File_instantout_proto = out.File + file_instantout_proto_rawDesc = nil + file_instantout_proto_goTypes = nil + file_instantout_proto_depIdxs = nil +} diff --git a/swapserverrpc/instantout.proto b/swapserverrpc/instantout.proto new file mode 100644 index 0000000..e8e95a2 --- /dev/null +++ b/swapserverrpc/instantout.proto @@ -0,0 +1,136 @@ +syntax = "proto3"; + +// We can't change this to swapserverrpc, it would be a breaking change because +// the package name is also contained in the HTTP URIs and old clients would +// call the wrong endpoints. Luckily with the go_package option we can have +// different golang and RPC package names to fix protobuf namespace conflicts. +package looprpc; + +option go_package = "github.com/lightninglabs/loop/swapserverrpc"; + +service InstantSwapServer { + // RequestInstantLoopOut initiates an instant loop out swap. + rpc RequestInstantLoopOut (InstantLoopOutRequest) + returns (InstantLoopOutResponse); + + // PollPaymentAccepted polls the server to see if the payment has been + // accepted. + rpc PollPaymentAccepted (PollPaymentAcceptedRequest) + returns (PollPaymentAcceptedResponse); + + // InitHtlcSig is called by the client to initiate the htlc sig exchange. + rpc InitHtlcSig (InitHtlcSigRequest) returns (InitHtlcSigResponse); + + // PushHtlcSig is called by the client to push the htlc sigs to the server. + rpc PushHtlcSig (PushHtlcSigRequest) returns (PushHtlcSigResponse); + + // PushPreimage is called by the client to push the preimage to the server. + // This returns the musig2 signatures that the client needs to sweep the + // htlc. + rpc PushPreimage (PushPreimageRequest) returns (PushPreimageResponse); + + // CancelInstantSwap tries to cancel the instant swap. This can only be + // called if the swap has not been accepted yet. + rpc CancelInstantSwap (CancelInstantSwapRequest) + returns (CancelInstantSwapResponse); +} + +message InstantLoopOutRequest { + // Htlc related fields: + // The key for the htlc preimage spending path. + bytes receiver_key = 1; + + // The hash of the preimage that will be used to settle the htlc. + bytes swap_hash = 2; + + // The requested absolute block height of the on-chain htlc. + int32 expiry = 3; + + // The fee rate in sat/kw that should be used for the htlc. + uint64 htlc_fee_rate = 4; + + // The reservations used as the inputs. + repeated bytes reservation_ids = 5; + + // The protocol version to use for the swap. + InstantOutProtocolVersion protocol_version = 6; +} + +message InstantLoopOutResponse { + // The swap invoice that the client should pay. + string swap_invoice = 1; + + // the key for the htlc expiry path. + bytes sender_key = 2; +}; + +message PollPaymentAcceptedRequest { + // The hash of the swap invoice. + bytes swap_hash = 1; +} + +message PollPaymentAcceptedResponse { + // Whether the payment has been accepted. + bool accepted = 1; +} + +message InitHtlcSigRequest { + // The hash of the swap invoice. + bytes swap_hash = 1; + + // The nonces that the client will use to generate the htlc sigs. + repeated bytes htlc_client_nonces = 2; +} + +message InitHtlcSigResponse { + // The nonces that the server will use to generate the htlc sigs. + repeated bytes htlc_server_nonces = 2; +} + +message PushHtlcSigRequest { + // The hash of the swap invoice. + bytes swap_hash = 1; + + // The sigs that the client generated for the reservation inputs. + repeated bytes client_sigs = 2; +} + +message PushHtlcSigResponse { + // The sigs that the server generated for the reservation inputs. + repeated bytes server_sigs = 1; +} + +message PushPreimageRequest { + // The preimage that the client generated for the swap. + bytes preimage = 1; + + // The nonces that the client used to generate the sweepless sweep sigs. + repeated bytes client_nonces = 2; + + // The address that the client wants to sweep the htlc to. + string client_sweep_addr = 3; + + // The fee rate in sat/kw that the client wants to use for the sweep. + uint64 musig_tx_fee_rate = 4; +} + +message PushPreimageResponse { + // The sweep sigs that the server generated for the htlc. + repeated bytes musig2_sweep_sigs = 1; + + // The nonces that the server used to generate the sweepless sweep sigs. + repeated bytes server_nonces = 2; +} + +message CancelInstantSwapRequest { + // The hash of the swap invoice. + bytes swap_hash = 1; +} + +message CancelInstantSwapResponse { +} + +enum InstantOutProtocolVersion { + INSTANTOUT_NONE = 0; + INSTANTOUT_FULL_RESERVATION = 1; +}; diff --git a/swapserverrpc/instantout_grpc.pb.go b/swapserverrpc/instantout_grpc.pb.go new file mode 100644 index 0000000..e9d105a --- /dev/null +++ b/swapserverrpc/instantout_grpc.pb.go @@ -0,0 +1,301 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package swapserverrpc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// InstantSwapServerClient is the client API for InstantSwapServer service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type InstantSwapServerClient interface { + // RequestInstantLoopOut initiates an instant loop out swap. + RequestInstantLoopOut(ctx context.Context, in *InstantLoopOutRequest, opts ...grpc.CallOption) (*InstantLoopOutResponse, error) + // PollPaymentAccepted polls the server to see if the payment has been + // accepted. + PollPaymentAccepted(ctx context.Context, in *PollPaymentAcceptedRequest, opts ...grpc.CallOption) (*PollPaymentAcceptedResponse, error) + // InitHtlcSig is called by the client to initiate the htlc sig exchange. + InitHtlcSig(ctx context.Context, in *InitHtlcSigRequest, opts ...grpc.CallOption) (*InitHtlcSigResponse, error) + // PushHtlcSig is called by the client to push the htlc sigs to the server. + PushHtlcSig(ctx context.Context, in *PushHtlcSigRequest, opts ...grpc.CallOption) (*PushHtlcSigResponse, error) + // PushPreimage is called by the client to push the preimage to the server. + // This returns the musig2 signatures that the client needs to sweep the + // htlc. + PushPreimage(ctx context.Context, in *PushPreimageRequest, opts ...grpc.CallOption) (*PushPreimageResponse, error) + // CancelInstantSwap tries to cancel the instant swap. This can only be + // called if the swap has not been accepted yet. + CancelInstantSwap(ctx context.Context, in *CancelInstantSwapRequest, opts ...grpc.CallOption) (*CancelInstantSwapResponse, error) +} + +type instantSwapServerClient struct { + cc grpc.ClientConnInterface +} + +func NewInstantSwapServerClient(cc grpc.ClientConnInterface) InstantSwapServerClient { + return &instantSwapServerClient{cc} +} + +func (c *instantSwapServerClient) RequestInstantLoopOut(ctx context.Context, in *InstantLoopOutRequest, opts ...grpc.CallOption) (*InstantLoopOutResponse, error) { + out := new(InstantLoopOutResponse) + err := c.cc.Invoke(ctx, "/looprpc.InstantSwapServer/RequestInstantLoopOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *instantSwapServerClient) PollPaymentAccepted(ctx context.Context, in *PollPaymentAcceptedRequest, opts ...grpc.CallOption) (*PollPaymentAcceptedResponse, error) { + out := new(PollPaymentAcceptedResponse) + err := c.cc.Invoke(ctx, "/looprpc.InstantSwapServer/PollPaymentAccepted", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *instantSwapServerClient) InitHtlcSig(ctx context.Context, in *InitHtlcSigRequest, opts ...grpc.CallOption) (*InitHtlcSigResponse, error) { + out := new(InitHtlcSigResponse) + err := c.cc.Invoke(ctx, "/looprpc.InstantSwapServer/InitHtlcSig", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *instantSwapServerClient) PushHtlcSig(ctx context.Context, in *PushHtlcSigRequest, opts ...grpc.CallOption) (*PushHtlcSigResponse, error) { + out := new(PushHtlcSigResponse) + err := c.cc.Invoke(ctx, "/looprpc.InstantSwapServer/PushHtlcSig", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *instantSwapServerClient) PushPreimage(ctx context.Context, in *PushPreimageRequest, opts ...grpc.CallOption) (*PushPreimageResponse, error) { + out := new(PushPreimageResponse) + err := c.cc.Invoke(ctx, "/looprpc.InstantSwapServer/PushPreimage", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *instantSwapServerClient) CancelInstantSwap(ctx context.Context, in *CancelInstantSwapRequest, opts ...grpc.CallOption) (*CancelInstantSwapResponse, error) { + out := new(CancelInstantSwapResponse) + err := c.cc.Invoke(ctx, "/looprpc.InstantSwapServer/CancelInstantSwap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// InstantSwapServerServer is the server API for InstantSwapServer service. +// All implementations must embed UnimplementedInstantSwapServerServer +// for forward compatibility +type InstantSwapServerServer interface { + // RequestInstantLoopOut initiates an instant loop out swap. + RequestInstantLoopOut(context.Context, *InstantLoopOutRequest) (*InstantLoopOutResponse, error) + // PollPaymentAccepted polls the server to see if the payment has been + // accepted. + PollPaymentAccepted(context.Context, *PollPaymentAcceptedRequest) (*PollPaymentAcceptedResponse, error) + // InitHtlcSig is called by the client to initiate the htlc sig exchange. + InitHtlcSig(context.Context, *InitHtlcSigRequest) (*InitHtlcSigResponse, error) + // PushHtlcSig is called by the client to push the htlc sigs to the server. + PushHtlcSig(context.Context, *PushHtlcSigRequest) (*PushHtlcSigResponse, error) + // PushPreimage is called by the client to push the preimage to the server. + // This returns the musig2 signatures that the client needs to sweep the + // htlc. + PushPreimage(context.Context, *PushPreimageRequest) (*PushPreimageResponse, error) + // CancelInstantSwap tries to cancel the instant swap. This can only be + // called if the swap has not been accepted yet. + CancelInstantSwap(context.Context, *CancelInstantSwapRequest) (*CancelInstantSwapResponse, error) + mustEmbedUnimplementedInstantSwapServerServer() +} + +// UnimplementedInstantSwapServerServer must be embedded to have forward compatible implementations. +type UnimplementedInstantSwapServerServer struct { +} + +func (UnimplementedInstantSwapServerServer) RequestInstantLoopOut(context.Context, *InstantLoopOutRequest) (*InstantLoopOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestInstantLoopOut not implemented") +} +func (UnimplementedInstantSwapServerServer) PollPaymentAccepted(context.Context, *PollPaymentAcceptedRequest) (*PollPaymentAcceptedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PollPaymentAccepted not implemented") +} +func (UnimplementedInstantSwapServerServer) InitHtlcSig(context.Context, *InitHtlcSigRequest) (*InitHtlcSigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method InitHtlcSig not implemented") +} +func (UnimplementedInstantSwapServerServer) PushHtlcSig(context.Context, *PushHtlcSigRequest) (*PushHtlcSigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushHtlcSig not implemented") +} +func (UnimplementedInstantSwapServerServer) PushPreimage(context.Context, *PushPreimageRequest) (*PushPreimageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushPreimage not implemented") +} +func (UnimplementedInstantSwapServerServer) CancelInstantSwap(context.Context, *CancelInstantSwapRequest) (*CancelInstantSwapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelInstantSwap not implemented") +} +func (UnimplementedInstantSwapServerServer) mustEmbedUnimplementedInstantSwapServerServer() {} + +// UnsafeInstantSwapServerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to InstantSwapServerServer will +// result in compilation errors. +type UnsafeInstantSwapServerServer interface { + mustEmbedUnimplementedInstantSwapServerServer() +} + +func RegisterInstantSwapServerServer(s grpc.ServiceRegistrar, srv InstantSwapServerServer) { + s.RegisterService(&InstantSwapServer_ServiceDesc, srv) +} + +func _InstantSwapServer_RequestInstantLoopOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InstantLoopOutRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InstantSwapServerServer).RequestInstantLoopOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.InstantSwapServer/RequestInstantLoopOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InstantSwapServerServer).RequestInstantLoopOut(ctx, req.(*InstantLoopOutRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InstantSwapServer_PollPaymentAccepted_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PollPaymentAcceptedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InstantSwapServerServer).PollPaymentAccepted(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.InstantSwapServer/PollPaymentAccepted", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InstantSwapServerServer).PollPaymentAccepted(ctx, req.(*PollPaymentAcceptedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InstantSwapServer_InitHtlcSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitHtlcSigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InstantSwapServerServer).InitHtlcSig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.InstantSwapServer/InitHtlcSig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InstantSwapServerServer).InitHtlcSig(ctx, req.(*InitHtlcSigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InstantSwapServer_PushHtlcSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PushHtlcSigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InstantSwapServerServer).PushHtlcSig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.InstantSwapServer/PushHtlcSig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InstantSwapServerServer).PushHtlcSig(ctx, req.(*PushHtlcSigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InstantSwapServer_PushPreimage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PushPreimageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InstantSwapServerServer).PushPreimage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.InstantSwapServer/PushPreimage", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InstantSwapServerServer).PushPreimage(ctx, req.(*PushPreimageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InstantSwapServer_CancelInstantSwap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CancelInstantSwapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InstantSwapServerServer).CancelInstantSwap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.InstantSwapServer/CancelInstantSwap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InstantSwapServerServer).CancelInstantSwap(ctx, req.(*CancelInstantSwapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// InstantSwapServer_ServiceDesc is the grpc.ServiceDesc for InstantSwapServer service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var InstantSwapServer_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "looprpc.InstantSwapServer", + HandlerType: (*InstantSwapServerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RequestInstantLoopOut", + Handler: _InstantSwapServer_RequestInstantLoopOut_Handler, + }, + { + MethodName: "PollPaymentAccepted", + Handler: _InstantSwapServer_PollPaymentAccepted_Handler, + }, + { + MethodName: "InitHtlcSig", + Handler: _InstantSwapServer_InitHtlcSig_Handler, + }, + { + MethodName: "PushHtlcSig", + Handler: _InstantSwapServer_PushHtlcSig_Handler, + }, + { + MethodName: "PushPreimage", + Handler: _InstantSwapServer_PushPreimage_Handler, + }, + { + MethodName: "CancelInstantSwap", + Handler: _InstantSwapServer_CancelInstantSwap_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "instantout.proto", +}