diff --git a/cmd/loop/instantout.go b/cmd/loop/instantout.go index 9783f0054..4295222b1 100644 --- a/cmd/loop/instantout.go +++ b/cmd/loop/instantout.go @@ -190,6 +190,7 @@ func instantOut(ctx context.Context, cmd *cli.Command) error { ReservationIds: selectedReservations, OutgoingChanSet: outgoingChanSet, DestAddr: cmd.String("addr"), + MaxSwapFeeSat: quote.ServiceFeeSat, }, ) if err != nil { diff --git a/cmd/loop/main.go b/cmd/loop/main.go index 127634d2f..dc57ddc12 100644 --- a/cmd/loop/main.go +++ b/cmd/loop/main.go @@ -41,6 +41,8 @@ var ( defaultSwapWaitTime = 30 * time.Minute + defaultRpcTimeout = 30 * time.Second + // maxMsgRecvSize is the largest message our client will receive. We // set this to 200MiB atm. maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200) diff --git a/cmd/loop/reservations.go b/cmd/loop/reservations.go index 122eda12f..12563ac30 100644 --- a/cmd/loop/reservations.go +++ b/cmd/loop/reservations.go @@ -2,13 +2,27 @@ package main import ( "context" + "errors" + "fmt" + "strings" "github.com/lightninglabs/loop/looprpc" "github.com/urfave/cli/v3" ) -var reservationsCommands = &cli.Command{ +var ( + reservationAmountFlag = &cli.Uint64Flag{ + Name: "amt", + Usage: "the amount in satoshis for the reservation", + } + reservationExpiryFlag = &cli.UintFlag{ + Name: "expiry", + Usage: "the relative block height at which the reservation" + + " expires", + } +) +var reservationsCommands = &cli.Command{ Name: "reservations", Aliases: []string{"r"}, Usage: "manage reservations", @@ -20,6 +34,7 @@ var reservationsCommands = &cli.Command{ `, Commands: []*cli.Command{ listReservationsCommand, + newReservationCommand, }, } @@ -34,8 +49,76 @@ var ( `, Action: listReservations, } + + newReservationCommand = &cli.Command{ + Name: "new", + Aliases: []string{"n"}, + Usage: "create a new reservation", + Description: ` + Create a new reservation with the given value and expiry. + `, + Action: newReservation, + Flags: []cli.Flag{ + reservationAmountFlag, + reservationExpiryFlag, + }, + } ) +func newReservation(ctx context.Context, cmd *cli.Command) error { + client, cleanup, err := getClient(cmd) + if err != nil { + return err + } + defer cleanup() + + rpcCtx, cancel := context.WithTimeout(ctx, defaultRpcTimeout) + defer cancel() + + if !cmd.IsSet(reservationAmountFlag.Name) { + return errors.New("amt flag missing") + } + + if !cmd.IsSet(reservationExpiryFlag.Name) { + return errors.New("expiry flag missing") + } + + quoteReq, err := client.ReservationQuote( + rpcCtx, &looprpc.ReservationQuoteRequest{ + Amt: cmd.Uint64(reservationAmountFlag.Name), + Expiry: uint32(cmd.Uint(reservationExpiryFlag.Name)), + }, + ) + if err != nil { + return err + } + + fmt.Printf(satAmtFmt, "Reservation Cost: ", quoteReq.PrepayAmt) + + fmt.Printf("CONTINUE RESERVATION? (y/n): ") + + var answer string + if _, err := fmt.Scanln(&answer); err != nil || + !strings.EqualFold(answer, "y") { + + return nil + } + + reservationRes, err := client.ReservationRequest( + rpcCtx, &looprpc.ReservationRequestRequest{ + Amt: cmd.Uint64(reservationAmountFlag.Name), + Expiry: uint32(cmd.Uint(reservationExpiryFlag.Name)), + MaxPrepayAmt: quoteReq.PrepayAmt, + }, + ) + if err != nil { + return err + } + + printRespJSON(reservationRes) + return nil +} + func listReservations(ctx context.Context, cmd *cli.Command) error { client, cleanup, err := getClient(cmd) if err != nil { diff --git a/cmd/loop/testdata/sessions/instantout/02_loop-instantout.json b/cmd/loop/testdata/sessions/instantout/02_loop-instantout.json index 6dae20b15..257f27c7c 100644 --- a/cmd/loop/testdata/sessions/instantout/02_loop-instantout.json +++ b/cmd/loop/testdata/sessions/instantout/02_loop-instantout.json @@ -139,7 +139,8 @@ "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=" ], "outgoing_chan_set": [], - "dest_addr": "" + "dest_addr": "", + "max_swap_fee_sat": "4800" } } }, diff --git a/cmd/loop/testdata/sessions/instantout/07_loop-instantout-channel.json b/cmd/loop/testdata/sessions/instantout/07_loop-instantout-channel.json index 205ae43e8..07038f8e1 100644 --- a/cmd/loop/testdata/sessions/instantout/07_loop-instantout-channel.json +++ b/cmd/loop/testdata/sessions/instantout/07_loop-instantout-channel.json @@ -148,7 +148,8 @@ "outgoing_chan_set": [ "125344325763072" ], - "dest_addr": "" + "dest_addr": "", + "max_swap_fee_sat": "3200" } } }, diff --git a/cmd/loop/testdata/sessions/instantout/08_loop-instantout-select-index.json b/cmd/loop/testdata/sessions/instantout/08_loop-instantout-select-index.json index 9a6d103b3..89f13c318 100644 --- a/cmd/loop/testdata/sessions/instantout/08_loop-instantout-select-index.json +++ b/cmd/loop/testdata/sessions/instantout/08_loop-instantout-select-index.json @@ -162,7 +162,8 @@ "cSfKVONNmsK9+p4Uc5nc3ZtE+37uOODHeq1vprhh/x4=" ], "outgoing_chan_set": [], - "dest_addr": "" + "dest_addr": "", + "max_swap_fee_sat": "1600" } } }, diff --git a/docs/loop.1 b/docs/loop.1 index 1e21905b8..f8f2e729f 100644 --- a/docs/loop.1 +++ b/docs/loop.1 @@ -388,6 +388,18 @@ list all reservations .PP \fB--help, -h\fP: show help +.SS new, n +create a new reservation + +.PP +\fB--amt\fP="": the amount in satoshis for the reservation (default: 0) + +.PP +\fB--expiry\fP="": the relative block height at which the reservation expires (default: 0) + +.PP +\fB--help, -h\fP: show help + .SH instantout perform an instant off-chain to on-chain swap (looping out) diff --git a/docs/loop.md b/docs/loop.md index 316cebdce..ab17754d8 100644 --- a/docs/loop.md +++ b/docs/loop.md @@ -455,6 +455,26 @@ The following flags are supported: |-----------------|-------------|------|:-------------:| | `--help` (`-h`) | show help | bool | `false` | +### `reservations new` subcommand (aliases: `n`) + +create a new reservation. + +Create a new reservation with the given value and expiry. + +Usage: + +```bash +$ loop [GLOBAL FLAGS] reservations new [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Type | Default value | +|-----------------|------------------------------------------------------------|------|:-------------:| +| `--amt="…"` | the amount in satoshis for the reservation | uint | `0` | +| `--expiry="…"` | the relative block height at which the reservation expires | uint | `0` | +| `--help` (`-h`) | show help | bool | `false` | + ### `instantout` command perform an instant off-chain to on-chain swap (looping out). diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index f0ca7972d..f7f242698 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -6,6 +6,10 @@ #### Bug Fixes +* Hardened Instant Out and reservation handling against malformed server + responses, invalid signatures and reservation outputs, resource exhaustion, + unsafe recovery, excessive swap fees, and under-scoped macaroon permissions. + * Taproot Asset Loop Out handling now validates RFQ timeouts and asset rates, keeps cached asset-name lookups responsive during slow `tapd` queries, and closes `tapd` connections cleanly during shutdown and startup failures. diff --git a/instantout/actions.go b/instantout/actions.go index d1c405fd8..778209281 100644 --- a/instantout/actions.go +++ b/instantout/actions.go @@ -20,6 +20,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" ) const ( @@ -61,6 +62,13 @@ type InitInstantOutCtx struct { outgoingChanSet loopdb.ChannelSet protocolVersion ProtocolVersion sweepAddress btcutil.Address + maxSwapFee btcutil.Amount +} + +// RecoverInstantOutCtx contains the chain height at which an instant out is +// resumed after restart. +type RecoverInstantOutCtx struct { + currentHeight int32 } // InitInstantOutAction is the first action that is executed when the instant @@ -78,7 +86,7 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, } var ( - reservationAmt uint64 + reservationAmt btcutil.Amount reservationIds = make([][]byte, 0, len(initCtx.reservations)) reservations = make( []*reservation.Reservation, 0, len(initCtx.reservations), @@ -99,7 +107,7 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, "locked", reservationId)) } - reservationAmt += uint64(res.Value) + reservationAmt += res.Value reservationIds = append(reservationIds, resId[:]) reservations = append(reservations, res) @@ -161,6 +169,11 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, return f.HandleError(fmt.Errorf("invalid swap invoice hash: "+ "expected %x got %x", preimage.Hash(), payReq.Hash)) } + if err := validateInstantOutInvoiceAmount( + payReq.Value, reservationAmt, initCtx.maxSwapFee, + ); err != nil { + return f.HandleError(err) + } serverPubkey, err := btcec.ParsePubKey(instantOutResponse.SenderKey) if err != nil { return f.HandleError(err) @@ -188,7 +201,8 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, CltvExpiry: initCtx.cltvExpiry, clientPubkey: keyRes.PubKey, serverPubkey: serverPubkey, - Value: btcutil.Amount(reservationAmt), + Value: reservationAmt, + MaxSwapFee: initCtx.maxSwapFee, htlcFeeRate: feeRate, swapInvoice: instantOutResponse.SwapInvoice, Reservations: reservations, @@ -206,6 +220,31 @@ func (f *FSM) InitInstantOutAction(ctx context.Context, return OnInit } +// validateInstantOutInvoiceAmount verifies that the server invoice doesn't +// charge more than the client-approved swap fee. Sub-satoshi fees are rounded +// up so the cap cannot be bypassed with millisatoshi precision. +func validateInstantOutInvoiceAmount(invoiceAmount lnwire.MilliSatoshi, + swapAmount, maxSwapFee btcutil.Amount) error { + + if maxSwapFee < 0 { + return fmt.Errorf("maximum swap fee must not be negative") + } + + swapAmountMsat := lnwire.NewMSatFromSatoshis(swapAmount) + if invoiceAmount <= swapAmountMsat { + return nil + } + + swapFeeMsat := invoiceAmount - swapAmountMsat + swapFeeSat := btcutil.Amount((int64(swapFeeMsat)-1)/1000 + 1) + if swapFeeSat > maxSwapFee { + return fmt.Errorf("instant out swap fee %d exceeds maximum %d", + swapFeeSat, maxSwapFee) + } + + return nil +} + // PollPaymentAcceptedAction locks the reservations, sends the payment to the // server and polls the server for the payment status. func (f *FSM) PollPaymentAcceptedAction(ctx context.Context, @@ -293,6 +332,15 @@ func (f *FSM) BuildHTLCAction(ctx context.Context, } f.htlcMusig2Sessions = htlcSessions + defer func() { + err := cleanupMuSig2Sessions( + ctx, f.cfg.Signer, f.htlcMusig2Sessions, + ) + if err != nil { + f.Errorf("unable to clean up HTLC MuSig2 sessions: %v", err) + } + f.htlcMusig2Sessions = nil + }() // Send the server the client nonces. htlcInitRes, err := f.cfg.InstantOutClient.InitHtlcSig( @@ -373,6 +421,26 @@ func (f *FSM) BuildHTLCAction(ctx context.Context, func (f *FSM) PushPreimageAction(ctx context.Context, eventCtx fsm.EventContext) fsm.EventType { + // A recovered swap may have been offline long enough that the server's + // reservation timeout is now close. Fall back to the already finalized + // HTLC instead of revealing the preimage without enough time to publish + // that safety transaction. + if recoverCtx, ok := eventCtx.(*RecoverInstantOutCtx); ok { + minReservationExpiry := int64(recoverCtx.currentHeight) + + int64(htlcExpiryDelta) + for _, res := range f.InstantOut.Reservations { + if int64(res.Expiry) >= minReservationExpiry { + continue + } + + f.LastActionError = fmt.Errorf("reservation %x expires at "+ + "height %d, before recovery safety height %d", + res.ID, res.Expiry, minReservationExpiry) + + return OnErrorPublishHtlc + } + } + // First we'll create the musig2 context. coopSessions, coopClientNonces, err := f.InstantOut.createMusig2Session( ctx, f.cfg.Signer, @@ -382,6 +450,15 @@ func (f *FSM) PushPreimageAction(ctx context.Context, } f.sweeplessSweepSessions = coopSessions + defer func() { + err := cleanupMuSig2Sessions( + ctx, f.cfg.Signer, f.sweeplessSweepSessions, + ) + if err != nil { + f.Errorf("unable to clean up sweep MuSig2 sessions: %v", err) + } + f.sweeplessSweepSessions = nil + }() // Get the feerate for the coop sweep. feeRate, err := f.cfg.Wallet.EstimateFeeRate(ctx, normalConfTarget) @@ -614,13 +691,34 @@ func (f *FSM) WaitForHtlcSweepConfirmedAction(ctx context.Context, } } +// unlockReservationsOnRecoverAction is the action of the +// UnlockReservationsOnRecover state. It is entered via OnRecover from any +// in-flight state where the reservations are already locked, and it unlocks +// them before routing to Failed. Without this, a crash between +// PollPaymentAcceptedAction's LockReservation and the swap reaching a +// terminal state would leave the reservations permanently Locked in the +// local store, blocking any future InstantOut that wants to spend them. +func (f *FSM) unlockReservationsOnRecoverAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + return f.handleErrorAndUnlockReservations( + ctx, errors.New("instant out recovered from in-flight state"), + ) +} + // handleErrorAndUnlockReservations handles an error and unlocks the // reservations. -func (f *FSM) handleErrorAndUnlockReservations(ctx context.Context, +func (f *FSM) handleErrorAndUnlockReservations(_ context.Context, 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(ctx, time.Second*30) + // We very likely got here from a canceled parent context (caller + // timeout, daemon shutdown, etc.). Deriving with timeout from a + // canceled parent yields an already-done context, so neither the + // local UnlockReservation calls nor the server-side CancelInstantSwap + // RPC would ever get a chance to run. Detach from the caller's + // context entirely. + ctx, cancel := context.WithTimeout( + context.Background(), time.Second*30, + ) defer cancel() // Unlock the reservations. @@ -635,19 +733,23 @@ func (f *FSM) handleErrorAndUnlockReservations(ctx context.Context, } // 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. + // release the reservations. This runs in a goroutine because we + // want to fail the FSM early -- but it must use its OWN background + // context with timeout, not derive from the cancel above (which + // fires the moment this function returns). go func() { - ctx, cancel := context.WithTimeout(ctx, time.Second*30) + cancelCtx, cancel := context.WithTimeout( + context.Background(), time.Second*30, + ) defer cancel() _, cancelErr := f.cfg.InstantOutClient.CancelInstantSwap( - ctx, &swapserverrpc.CancelInstantSwapRequest{ + cancelCtx, &swapserverrpc.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. + // 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) } }() diff --git a/instantout/fsm.go b/instantout/fsm.go index c188b1582..518bd34a1 100644 --- a/instantout/fsm.go +++ b/instantout/fsm.go @@ -85,6 +85,14 @@ var ( // FailedHtlcSweep is the state where the htlc sweep failed. FailedHtlcSweep = fsm.StateType("FailedHtlcSweep") + // UnlockReservationsOnRecover is a transient state entered via + // OnRecover from any in-flight state that had already locked the + // underlying reservations. Its action unlocks them and routes the + // FSM to Failed, so a crash mid-swap does not leave reservations + // stuck Locked in the local store. + UnlockReservationsOnRecover = fsm.StateType( + "UnlockReservationsOnRecover") + // Failed is the state where the swap failed. Failed = fsm.StateType("InstantOutFailed") ) @@ -246,7 +254,11 @@ func (f *FSM) GetV1ReservationStates() fsm.States { Transitions: fsm.Transitions{ OnPaymentAccepted: BuildHtlc, fsm.OnError: Failed, - OnRecover: Failed, + // OnRecover must go through cleanup since + // PollPaymentAcceptedAction has already locked + // the reservations by the time the FSM can + // crash here. + OnRecover: UnlockReservationsOnRecover, }, Action: f.PollPaymentAcceptedAction, }, @@ -254,10 +266,18 @@ func (f *FSM) GetV1ReservationStates() fsm.States { Transitions: fsm.Transitions{ OnHtlcSigReceived: PushPreimage, fsm.OnError: Failed, - OnRecover: Failed, + // Same as SendPaymentAndPollAccepted -- the + // reservations are still locked at this point. + OnRecover: UnlockReservationsOnRecover, }, Action: f.BuildHTLCAction, }, + UnlockReservationsOnRecover: fsm.State{ + Transitions: fsm.Transitions{ + fsm.OnError: Failed, + }, + Action: f.unlockReservationsOnRecoverAction, + }, PushPreimage: fsm.State{ Transitions: fsm.Transitions{ OnSweeplessSweepPublished: WaitForSweeplessSweepConfirmed, diff --git a/instantout/instantout.go b/instantout/instantout.go index f8c89eb0c..c700ee917 100644 --- a/instantout/instantout.go +++ b/instantout/instantout.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "reflect" + "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" @@ -25,6 +26,8 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) +const muSig2CleanupTimeout = 5 * time.Second + // InstantOut holds the necessary information to execute an instant out swap. type InstantOut struct { // SwapHash is the hash of the swap. @@ -57,6 +60,9 @@ type InstantOut struct { // Value is the amount that is swapped. Value btcutil.Amount + // MaxSwapFee is the maximum off-chain swap fee accepted by the client. + MaxSwapFee btcutil.Amount + // keyLocator is the key locator that is used for the swap. keyLocator keychain.KeyLocator @@ -112,7 +118,10 @@ func (i *InstantOut) createMusig2Session(ctx context.Context, for idx, reservation := range i.Reservations { session, err := reservation.Musig2CreateSession(ctx, signer) if err != nil { - return nil, nil, err + cleanupErr := cleanupMuSig2Sessions( + ctx, signer, musig2Sessions[:idx], + ) + return nil, nil, errors.Join(err, cleanupErr) } musig2Sessions[idx] = session @@ -122,6 +131,32 @@ func (i *InstantOut) createMusig2Session(ctx context.Context, return musig2Sessions, clientNonces, nil } +// cleanupMuSig2Sessions removes completed or abandoned MuSig2 sessions from +// lnd. Cleanup uses a bounded context that survives cancellation of the swap +// action that created the sessions. +func cleanupMuSig2Sessions(ctx context.Context, signer lndclient.SignerClient, + sessions []*input.MuSig2SessionInfo) error { + + cleanupCtx, cancel := context.WithTimeout( + context.WithoutCancel(ctx), muSig2CleanupTimeout, + ) + defer cancel() + + var cleanupErr error + for _, session := range sessions { + if session == nil { + continue + } + + err := signer.MuSig2Cleanup(cleanupCtx, session.SessionID) + if err != nil { + cleanupErr = errors.Join(cleanupErr, err) + } + } + + return cleanupErr +} + // getInputReservations returns the input reservations for the instant out. func (i *InstantOut) getInputReservations() (InputReservations, error) { if len(i.Reservations) == 0 { @@ -263,12 +298,31 @@ func (i *InstantOut) signMusig2Tx(ctx context.Context, if err != nil { return nil, err } + if tx == nil { + return nil, errors.New("transaction is nil") + } + if len(tx.TxIn) != len(inputs) { + return nil, fmt.Errorf("invalid number of transaction inputs: "+ + "expected %d, got %d", len(inputs), len(tx.TxIn)) + } + if len(musig2sessions) != len(inputs) { + return nil, fmt.Errorf("invalid number of MuSig2 sessions: "+ + "expected %d, got %d", len(inputs), len(musig2sessions)) + } + if len(counterPartyNonces) != len(inputs) { + return nil, fmt.Errorf("invalid number of server nonces: "+ + "expected %d, got %d", len(inputs), len(counterPartyNonces)) + } prevOutFetcher := inputs.GetPrevoutFetcher() sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) sigs := make([][]byte, len(inputs)) for idx, reservation := range inputs { + if musig2sessions[idx] == nil { + return nil, fmt.Errorf("MuSig2 session %d is nil", idx) + } + if !reflect.DeepEqual(tx.TxIn[idx].PreviousOutPoint, reservation.Outpoint) { @@ -329,8 +383,30 @@ func (i *InstantOut) finalizeMusig2Transaction(ctx context.Context, if err != nil { return nil, err } + if tx == nil { + return nil, errors.New("transaction is nil") + } + if len(tx.TxIn) != len(inputs) { + return nil, fmt.Errorf("invalid number of transaction inputs: "+ + "expected %d, got %d", len(inputs), len(tx.TxIn)) + } + if len(musig2Sessions) != len(inputs) { + return nil, fmt.Errorf("invalid number of MuSig2 sessions: "+ + "expected %d, got %d", len(inputs), len(musig2Sessions)) + } + if len(serverSigs) != len(inputs) { + return nil, fmt.Errorf("invalid number of server signatures: "+ + "expected %d, got %d", len(inputs), len(serverSigs)) + } + + prevOutFetcher := inputs.GetPrevoutFetcher() + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) for idx := range inputs { + if musig2Sessions[idx] == nil { + return nil, fmt.Errorf("MuSig2 session %d is nil", idx) + } + haveAllSigs, finalSig, err := signer.MuSig2CombineSig( ctx, musig2Sessions[idx].SessionID, [][]byte{serverSigs[idx]}, @@ -343,7 +419,26 @@ func (i *InstantOut) finalizeMusig2Transaction(ctx context.Context, return nil, fmt.Errorf("missing sigs") } + // lnd removes a MuSig2 session automatically once all signatures + // have been combined. Clear the local entry so the caller's deferred + // cleanup only targets sessions abandoned on an error path. + musig2Sessions[idx] = nil + tx.TxIn[idx].Witness = wire.TxWitness{finalSig} + + vm, err := txscript.NewEngine( + inputs[idx].PkScript, tx, idx, + txscript.StandardVerifyFlags, nil, sigHashes, + int64(inputs[idx].Value), prevOutFetcher, + ) + if err != nil { + return nil, fmt.Errorf("unable to verify final MuSig2 "+ + "signature for input %d: %w", idx, err) + } + if err := vm.Execute(); err != nil { + return nil, fmt.Errorf("invalid final MuSig2 signature "+ + "for input %d: %w", idx, err) + } } return tx, nil diff --git a/instantout/instantout_test.go b/instantout/instantout_test.go new file mode 100644 index 000000000..ea2086591 --- /dev/null +++ b/instantout/instantout_test.go @@ -0,0 +1,203 @@ +package instantout + +import ( + "context" + "testing" + + "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" + "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" +) + +type invalidFinalSigSigner struct { + lndclient.SignerClient +} + +func (s *invalidFinalSigSigner) MuSig2CombineSig(context.Context, [32]byte, + [][]byte) (bool, []byte, error) { + + return true, make([]byte, 64), nil +} + +type cleanupTrackingSigner struct { + lndclient.SignerClient + + cleaned [][32]byte +} + +func (s *cleanupTrackingSigner) MuSig2Cleanup(_ context.Context, + sessionID [32]byte) error { + + s.cleaned = append(s.cleaned, sessionID) + return nil +} + +// TestMuSig2VectorLengthValidation verifies that malformed server-controlled +// vectors are rejected before they can be indexed. +func TestMuSig2VectorLengthValidation(t *testing.T) { + _, pubKey := btcec.PrivKeyFromBytes([]byte{1}) + instantOut := &InstantOut{ + Reservations: []*reservation.Reservation{ + { + ClientPubkey: pubKey, + ServerPubkey: pubKey, + Value: btcutil.Amount(100_000), + Expiry: 200, + Outpoint: &wire.OutPoint{}, + }, + }, + } + tx := wire.NewMsgTx(2) + tx.AddTxIn(&wire.TxIn{}) + sessions := []*input.MuSig2SessionInfo{{}} + + require.NotPanics(t, func() { + _, err := instantOut.signMusig2Tx( + context.Background(), nil, tx, sessions, nil, + ) + require.ErrorContains(t, err, "server nonces") + }) + + require.NotPanics(t, func() { + _, err := instantOut.finalizeMusig2Transaction( + context.Background(), nil, sessions, tx, nil, + ) + require.ErrorContains(t, err, "server signatures") + }) +} + +// TestFinalizeMuSig2TransactionVerifiesSignature verifies that a combined +// signature is validated locally before the transaction can be used as the +// instant-out safety net. +func TestFinalizeMuSig2TransactionVerifiesSignature(t *testing.T) { + _, pubKey := btcec.PrivKeyFromBytes([]byte{1}) + res := &reservation.Reservation{ + ClientPubkey: pubKey, + ServerPubkey: pubKey, + Value: btcutil.Amount(100_000), + Expiry: 200, + Outpoint: &wire.OutPoint{}, + } + instantOut := &InstantOut{ + Reservations: []*reservation.Reservation{res}, + } + tx := wire.NewMsgTx(2) + tx.AddTxIn(&wire.TxIn{PreviousOutPoint: *res.Outpoint}) + tx.AddTxOut(&wire.TxOut{Value: 90_000}) + + sessions := []*input.MuSig2SessionInfo{{}} + _, err := instantOut.finalizeMusig2Transaction( + context.Background(), &invalidFinalSigSigner{}, + sessions, tx, [][]byte{{1}}, + ) + require.ErrorContains(t, err, "invalid final MuSig2 signature") + require.Nil(t, sessions[0]) +} + +// TestCleanupMuSig2Sessions verifies that all allocated sessions are released +// while nil entries from partial session creation are skipped. +func TestCleanupMuSig2Sessions(t *testing.T) { + firstID := [32]byte{1} + secondID := [32]byte{2} + signer := &cleanupTrackingSigner{} + + err := cleanupMuSig2Sessions( + t.Context(), signer, []*input.MuSig2SessionInfo{ + {SessionID: firstID}, nil, {SessionID: secondID}, + }, + ) + require.NoError(t, err) + require.Equal(t, [][32]byte{firstID, secondID}, signer.cleaned) +} + +// TestPushPreimageRejectsExpiringReservation verifies that recovery takes the +// on-chain fallback before revealing the preimage when a reservation is too +// close to its server-controlled timeout. +func TestPushPreimageRejectsExpiringReservation(t *testing.T) { + instantOutFSM := &FSM{ + StateMachine: &fsm.StateMachine{}, + InstantOut: &InstantOut{ + Reservations: []*reservation.Reservation{ + { + ID: reservation.ID{1}, + Expiry: 139, + }, + }, + }, + } + + event := instantOutFSM.PushPreimageAction( + t.Context(), &RecoverInstantOutCtx{currentHeight: 100}, + ) + require.Equal(t, OnErrorPublishHtlc, event) + require.ErrorContains( + t, instantOutFSM.LastActionError, "before recovery safety height", + ) +} + +// TestValidateInstantOutInvoiceAmount verifies enforcement of the fee cap at +// millisatoshi precision. +func TestValidateInstantOutInvoiceAmount(t *testing.T) { + const ( + swapAmount = btcutil.Amount(100_000) + maxSwapFee = btcutil.Amount(200) + ) + + tests := []struct { + name string + invoiceAmount lnwire.MilliSatoshi + maxSwapFee btcutil.Amount + expectErr bool + }{ + { + name: "exact fee cap", + invoiceAmount: lnwire.NewMSatFromSatoshis( + swapAmount + maxSwapFee, + ), + maxSwapFee: maxSwapFee, + }, + { + name: "one millisatoshi over fee cap", + invoiceAmount: lnwire.NewMSatFromSatoshis( + swapAmount+maxSwapFee, + ) + 1, + maxSwapFee: maxSwapFee, + expectErr: true, + }, + { + name: "discounted invoice", + invoiceAmount: lnwire.NewMSatFromSatoshis( + swapAmount - 1, + ), + maxSwapFee: 0, + }, + { + name: "negative cap", + invoiceAmount: lnwire.NewMSatFromSatoshis( + swapAmount, + ), + maxSwapFee: -1, + expectErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := validateInstantOutInvoiceAmount( + tc.invoiceAmount, swapAmount, tc.maxSwapFee, + ) + if tc.expectErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/instantout/manager.go b/instantout/manager.go index 37ccb681b..e96791e38 100644 --- a/instantout/manager.go +++ b/instantout/manager.go @@ -119,8 +119,11 @@ func (m *Manager) recoverInstantOuts(ctx context.Context) error { // As SendEvent can block, we'll start a goroutine to process // the event. + recoverCtx := &RecoverInstantOutCtx{ + currentHeight: m.currentHeight, + } go func() { - err := instantOutFSM.SendEvent(ctx, OnRecover, nil) + err := instantOutFSM.SendEvent(ctx, OnRecover, recoverCtx) if err != nil { log.Errorf("FSM %v Error sending recover "+ "event %v, state: %v", @@ -135,7 +138,12 @@ func (m *Manager) recoverInstantOuts(ctx context.Context) error { // NewInstantOut creates a new instantout. func (m *Manager) NewInstantOut(ctx context.Context, - reservations []reservation.ID, sweepAddress string) (*FSM, error) { + reservations []reservation.ID, sweepAddress string, + maxSwapFee btcutil.Amount) (*FSM, error) { + + if maxSwapFee < 0 { + return nil, fmt.Errorf("maximum swap fee must not be negative") + } var ( sweepAddr btcutil.Address @@ -148,6 +156,24 @@ func (m *Manager) NewInstantOut(ctx context.Context, if err != nil { return nil, err } + + if !sweepAddr.IsForNet(m.cfg.Network) { + return nil, fmt.Errorf("sweep address %s is not "+ + "valid for network %s", sweepAddress, + m.cfg.Network.Name) + } + + switch sweepAddr.(type) { + case *btcutil.AddressTaproot, + *btcutil.AddressWitnessScriptHash, + *btcutil.AddressWitnessPubKeyHash, + *btcutil.AddressScriptHash, + *btcutil.AddressPubKeyHash: + + default: + return nil, fmt.Errorf("unsupported sweep address "+ + "type %T", sweepAddr) + } } m.Lock() @@ -158,6 +184,7 @@ func (m *Manager) NewInstantOut(ctx context.Context, initationHeight: m.currentHeight, protocolVersion: CurrentProtocolVersion(), sweepAddress: sweepAddr, + maxSwapFee: maxSwapFee, } instantOut, err := NewFSM(m.cfg, ProtocolVersionFullReservation) diff --git a/instantout/reservation/actions.go b/instantout/reservation/actions.go index 9e62c0151..7758d6b8f 100644 --- a/instantout/reservation/actions.go +++ b/instantout/reservation/actions.go @@ -2,16 +2,189 @@ package reservation import ( "context" + "errors" + "fmt" + "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/lnrpc" ) -// InitReservationContext contains the request parameters for a reservation. -type InitReservationContext struct { +const ( + // 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) +) + +var ( + // The allowed delta between what we accept as the expiry height and + // the actual expiry height. + expiryDelta = uint32(3) + + // defaultPrepayTimeout is the default timeout for the prepayment. + DefaultPrepayTimeout = time.Minute * 120 +) + +// ClientRequestedInitContext contains the request parameters for a reservation. +type ClientRequestedInitContext struct { + value btcutil.Amount + relativeExpiry uint32 + heightHint uint32 + maxPrepaymentAmt btcutil.Amount +} + +// InitFromClientRequestAction is the action that is executed when the +// reservation state machine is initialized from a client request. It creates +// the reservation in the database and sends the reservation request to the +// server. +func (f *FSM) InitFromClientRequestAction(ctx context.Context, + eventCtx fsm.EventContext) fsm.EventType { + + // Check if the context is of the correct type. + reservationRequest, ok := eventCtx.(*ClientRequestedInitContext) + if !ok { + return f.HandleError(fsm.ErrInvalidContextType) + } + + // Create the reservation in the database. + keyRes, err := f.cfg.Wallet.DeriveNextKey(ctx, KeyFamily) + if err != nil { + return f.HandleError(err) + } + + // Send the request to the server. + requestResponse, err := f.cfg.ReservationClient.RequestReservation( + ctx, &swapserverrpc.RequestReservationRequest{ + Value: uint64(reservationRequest.value), + Expiry: reservationRequest.relativeExpiry, + ClientKey: keyRes.PubKey.SerializeCompressed(), + }, + ) + if err != nil { + return f.HandleError(err) + } + + expectedExpiry := reservationRequest.relativeExpiry + + reservationRequest.heightHint + + // Check that the expiry is in the delta. Compare as int64 so the + // lower bound stays meaningful when expectedExpiry < expiryDelta + // (which would otherwise underflow the uint32 and accept any + // response below the upper bound). + if int64(requestResponse.Expiry) < int64(expectedExpiry)-int64(expiryDelta) || + int64(requestResponse.Expiry) > int64(expectedExpiry)+int64(expiryDelta) { + + return f.HandleError( + fmt.Errorf("unexpected expiry height: %v, expected %v", + requestResponse.Expiry, expectedExpiry)) + } + + prepayment, err := f.cfg.LightningClient.DecodePaymentRequest( + ctx, requestResponse.Invoice, + ) + if err != nil { + return f.HandleError(err) + } + + if prepayment.Value.ToSatoshis() > reservationRequest.maxPrepaymentAmt { + return f.HandleError( + errors.New("prepayment amount too high")) + } + + serverKey, err := btcec.ParsePubKey(requestResponse.ServerKey) + if err != nil { + return f.HandleError(err) + } + + var Id ID + copy(Id[:], requestResponse.ReservationId) + + reservation, err := NewReservation( + Id, serverKey, keyRes.PubKey, reservationRequest.value, + requestResponse.Expiry, reservationRequest.heightHint, + keyRes.KeyLocator, ProtocolVersionClientInitiated, + ) + if err != nil { + return f.HandleError(err) + } + reservation.PrepayInvoice = requestResponse.Invoice + + // Persist the row with state = Init so a crash before the next + // state transition leaves a recoverable row. Without this, NewReservation + // produces State = fsm.EmptyState (zero value), which has no OnRecover + // transition in the client-initiated state map, and recovery would + // silently leave the row stuck forever. + reservation.State = Init + f.reservation = reservation + + // Create the reservation in the database. + err = f.cfg.Store.CreateReservation(ctx, reservation) + if err != nil { + return f.HandleError(err) + } + + return OnClientInitialized +} + +// SendPrepayment is the action that is executed when the reservation +// is initialized from a client request. It dispatches the prepayment to the +// server and wait for it to be settled, signaling confirmation of the +// reservation. +func (f *FSM) SendPrepayment(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + prepayment, err := f.cfg.LightningClient.DecodePaymentRequest( + ctx, f.reservation.PrepayInvoice, + ) + if err != nil { + return f.HandleError(err) + } + + payReq := lndclient.SendPaymentRequest{ + Invoice: f.reservation.PrepayInvoice, + Timeout: DefaultPrepayTimeout, + MaxFee: getMaxRoutingFee(prepayment.Value.ToSatoshis()), + } + // Send the prepayment to the server. + payChan, errChan, err := f.cfg.RouterClient.SendPayment( + ctx, payReq, + ) + if err != nil { + return f.HandleError(err) + } + + for { + select { + case <-ctx.Done(): + return fsm.NoOp + + case err := <-errChan: + return f.HandleError(err) + + case prepayResp := <-payChan: + if prepayResp.State == lnrpc.Payment_FAILED { + return f.HandleError( + fmt.Errorf("prepayment failed: %v", + prepayResp.FailureReason)) + } + if prepayResp.State == lnrpc.Payment_SUCCEEDED { + return OnBroadcast + } + } + } +} + +// ServerRequestedInitContext contains the request parameters for a reservation. +type ServerRequestedInitContext struct { reservationID ID serverPubkey *btcec.PublicKey value btcutil.Amount @@ -19,14 +192,14 @@ type InitReservationContext struct { heightHint uint32 } -// 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 (f *FSM) InitAction(ctx context.Context, +// InitFromServerRequestAction is the action that is executed when the +// reservation state machine is initialized from a server request. It creates +// the reservation in the database and dispatches the payment to the server. +func (f *FSM) InitFromServerRequestAction(ctx context.Context, eventCtx fsm.EventContext) fsm.EventType { // Check if the context is of the correct type. - reservationRequest, ok := eventCtx.(*InitReservationContext) + reservationRequest, ok := eventCtx.(*ServerRequestedInitContext) if !ok { return f.HandleError(fsm.ErrInvalidContextType) } @@ -240,3 +413,7 @@ func (f *FSM) handleAsyncError(ctx context.Context, err error) { f.Errorf("Error sending event: %v", err2) } } + +func getMaxRoutingFee(amt btcutil.Amount) btcutil.Amount { + return swap.CalcFee(amt, maxRoutingFeeBase, maxRoutingFeeRate) +} diff --git a/instantout/reservation/actions_test.go b/instantout/reservation/actions_test.go index 40e6509b1..e2322a6bb 100644 --- a/instantout/reservation/actions_test.go +++ b/instantout/reservation/actions_test.go @@ -31,8 +31,8 @@ var ( defaultExpiry = uint32(100) ) -func newValidInitReservationContext() *InitReservationContext { - return &InitReservationContext{ +func newValidInitReservationContext() *ServerRequestedInitContext { + return &ServerRequestedInitContext{ reservationID: ID{0x01}, serverPubkey: defaultPubkey, value: defaultValue, @@ -80,6 +80,26 @@ func (m *mockReservationClient) FetchL402(ctx context.Context, args.Error(1) } +func (m *mockReservationClient) QuoteReservation(ctx context.Context, + in *swapserverrpc.QuoteReservationRequest, opts ...grpc.CallOption) ( + *swapserverrpc.QuoteReservationResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.QuoteReservationResponse), + args.Error(1) +} + +func (m *mockReservationClient) RequestReservation(ctx context.Context, + in *swapserverrpc.RequestReservationRequest, opts ...grpc.CallOption) ( + *swapserverrpc.RequestReservationResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.RequestReservationResponse), + args.Error(1) +} + type mockStore struct { mock.Mock @@ -154,7 +174,7 @@ func TestInitReservationAction(t *testing.T) { StateMachine: &fsm.StateMachine{}, } - event := reservationFSM.InitAction(ctxb, tc.eventCtx) + event := reservationFSM.InitFromServerRequestAction(ctxb, tc.eventCtx) require.Equal(t, tc.expectedEvent, event) } } @@ -203,6 +223,7 @@ func TestSubscribeToConfirmationAction(t *testing.T) { blockHeight int32 blockErr error sendTxConf bool + outputValue btcutil.Amount confErr error expectedEvent fsm.EventType }{ @@ -210,8 +231,15 @@ func TestSubscribeToConfirmationAction(t *testing.T) { name: "success", blockHeight: 0, sendTxConf: true, + outputValue: defaultValue, expectedEvent: OnConfirmed, }, + { + name: "reservation value mismatch", + sendTxConf: true, + outputValue: defaultValue - 1, + expectedEvent: fsm.OnError, + }, { name: "expired", blockHeight: 100, @@ -273,7 +301,7 @@ func TestSubscribeToConfirmationAction(t *testing.T) { TxIn: []*wire.TxIn{}, TxOut: []*wire.TxOut{ { - Value: int64(defaultValue), + Value: int64(tc.outputValue), PkScript: pkScript, }, }, diff --git a/instantout/reservation/fsm.go b/instantout/reservation/fsm.go index 946d5102d..46ba40d6d 100644 --- a/instantout/reservation/fsm.go +++ b/instantout/reservation/fsm.go @@ -22,7 +22,11 @@ const ( // ProtocolVersionServerInitiated is the protocol version where the // server initiates the reservation. - ProtocolVersionServerInitiated ProtocolVersion = 0 + ProtocolVersionServerInitiated ProtocolVersion = 1 + + // ProtocolVersionClientInitiated is the protocol version where the + // client initiates the reservation. + ProtocolVersionClientInitiated ProtocolVersion = 2 ) const ( @@ -45,6 +49,12 @@ type Config struct { // swap server. ReservationClient swapserverrpc.ReservationServiceClient + // LightningClient is the lnd client used to handle invoices decoding. + LightningClient lndclient.LightningClient + + // RouterClient is used to send the offchain payments. + RouterClient lndclient.RouterClient + // NotificationManager is the manager that handles the notification // subscriptions. NotificationManager NotificationManager @@ -60,10 +70,10 @@ type FSM struct { } // NewFSM creates a new reservation FSM. -func NewFSM(cfg *Config) *FSM { +func NewFSM(cfg *Config, protocolVersion ProtocolVersion) *FSM { reservation := &Reservation{ State: fsm.EmptyState, - ProtocolVersion: CurrentProtocolVersion, + ProtocolVersion: protocolVersion, } return NewFSMFromReservation(cfg, reservation) @@ -82,6 +92,9 @@ func NewFSMFromReservation(cfg *Config, reservation *Reservation) *FSM { case ProtocolVersionServerInitiated: states = reservationFsm.GetServerInitiatedReservationStates() + case ProtocolVersionClientInitiated: + states = reservationFsm.GetClientInitiatedReservationStates() + default: states = make(fsm.States) } @@ -100,6 +113,10 @@ var ( // Init is the initial state of the reservation. Init = fsm.StateType("Init") + // SendPrepaymentPayment is the state where the client sends the payment to the + // server. + SendPrepaymentPayment = fsm.StateType("SendPayment") + // WaitForConfirmation is the state where we wait for the reservation // tx to be confirmed. WaitForConfirmation = fsm.StateType("WaitForConfirmation") @@ -127,6 +144,10 @@ var ( // requests a new reservation. OnServerRequest = fsm.EventType("OnServerRequest") + // OnClientInitialized is the event that is triggered when the client + // has initialized the reservation. + OnClientInitialized = fsm.EventType("OnClientInitialized") + // OnBroadcast is the event that is triggered when the reservation tx // has been broadcast. OnBroadcast = fsm.EventType("OnBroadcast") @@ -160,6 +181,80 @@ var ( OnUnlocked = fsm.EventType("OnUnlocked") ) +// GetClientInitiatedReservationStates returns the statemap that defines the +// reservation state machine, where the client initiates the reservation. +func (f *FSM) GetClientInitiatedReservationStates() fsm.States { + return fsm.States{ + fsm.EmptyState: fsm.State{ + Transitions: fsm.Transitions{ + OnClientInitialized: Init, + }, + Action: nil, + }, + Init: fsm.State{ + Transitions: fsm.Transitions{ + OnClientInitialized: SendPrepaymentPayment, + OnRecover: Failed, + fsm.OnError: Failed, + }, + Action: f.InitFromClientRequestAction, + }, + SendPrepaymentPayment: fsm.State{ + Transitions: fsm.Transitions{ + OnBroadcast: WaitForConfirmation, + OnRecover: SendPrepaymentPayment, + fsm.OnError: Failed, + }, + Action: f.SendPrepayment, + }, + WaitForConfirmation: fsm.State{ + Transitions: fsm.Transitions{ + OnRecover: WaitForConfirmation, + OnConfirmed: Confirmed, + OnTimedOut: TimedOut, + }, + Action: f.SubscribeToConfirmationAction, + }, + Confirmed: fsm.State{ + Transitions: fsm.Transitions{ + 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.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, + }, + } +} + // GetServerInitiatedReservationStates returns the statemap that defines the // reservation state machine, where the server initiates the reservation. func (f *FSM) GetServerInitiatedReservationStates() fsm.States { @@ -176,7 +271,7 @@ func (f *FSM) GetServerInitiatedReservationStates() fsm.States { OnRecover: Failed, fsm.OnError: Failed, }, - Action: f.InitAction, + Action: f.InitFromServerRequestAction, }, WaitForConfirmation: fsm.State{ Transitions: fsm.Transitions{ diff --git a/instantout/reservation/interfaces.go b/instantout/reservation/interfaces.go index 04bf830d3..23658d5a4 100644 --- a/instantout/reservation/interfaces.go +++ b/instantout/reservation/interfaces.go @@ -8,14 +8,18 @@ import ( ) var ( - ErrReservationAlreadyExists = fmt.Errorf("reservation already exists") - ErrReservationNotFound = fmt.Errorf("reservation not found") + ErrReservationAlreadyExists = fmt.Errorf("reservation already exists") + ErrReservationNotFound = fmt.Errorf("reservation not found") + ErrTooManyActiveReservations = fmt.Errorf( + "too many active reservations", + ) ) const ( - KeyFamily = int32(42068) - DefaultConfTarget = int32(3) - IdLength = 32 + KeyFamily = int32(42068) + DefaultConfTarget = int32(3) + IdLength = 32 + maxActiveReservations = 1000 ) // Store is the interface that stores the reservations. diff --git a/instantout/reservation/manager.go b/instantout/reservation/manager.go index 600febfe9..037e33d38 100644 --- a/instantout/reservation/manager.go +++ b/instantout/reservation/manager.go @@ -2,6 +2,7 @@ package reservation import ( "context" + "errors" "fmt" "strings" "sync" @@ -10,9 +11,31 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/swapserverrpc" reservationrpc "github.com/lightninglabs/loop/swapserverrpc" ) +var ( + // defaultWaitForStateTime is how long RequestReservationFromServer + // blocks waiting for the FSM to advance to SendPrepaymentPayment. + // The action that drives that transition performs a server RPC + // round-trip that itself creates an lnd hold invoice on the swap + // server side, plus an lnd DecodePaymentRequest, plus a local + // CreateReservation. Under load any one of these can take a few + // seconds, so 15s is too tight: when the RPC times out the FSM + // continues running in the background and may still pay the + // prepay invoice after the caller has been told the request + // failed. 60s gives realistic head-room. + defaultWaitForStateTime = time.Second * 60 +) + +// FSMSendEventReq contains the information needed to send an event to the FSM. +type FSMSendEventReq struct { + fsm *FSM + event fsm.EventType + eventCtx fsm.EventContext +} + // Manager manages the reservation state machines. type Manager struct { sync.Mutex @@ -23,6 +46,32 @@ type Manager struct { // activeReservations contains all the active reservationsFSMs. activeReservations map[ID]*FSM + + currentHeight int32 + + reqChan chan *FSMSendEventReq +} + +// finalStateObserver removes a reservation FSM from the active set once it +// reaches a terminal state. +type finalStateObserver struct { + manager *Manager + id ID + fsm *FSM +} + +// Notify implements the fsm.Observer interface. +func (o *finalStateObserver) Notify(notification fsm.Notification) { + if !isFinalState(notification.NextState) { + return + } + + o.manager.Lock() + defer o.manager.Unlock() + + if o.manager.activeReservations[o.id] == o.fsm { + delete(o.manager.activeReservations, o.id) + } } // NewManager creates a new reservation manager. @@ -30,6 +79,7 @@ func NewManager(cfg *Config) *Manager { return &Manager{ cfg: cfg, activeReservations: make(map[ID]*FSM), + reqChan: make(chan *FSMSendEventReq), } } @@ -42,7 +92,13 @@ func (m *Manager) Run(ctx context.Context, height int32, runCtx, cancel := context.WithCancel(ctx) defer cancel() - currentHeight := height + // Take the lock for the initial write so the race detector sees a + // consistent synchronisation rule (later writes in the new-block + // case already lock; concurrent reads in RequestReservationFromServer + // already lock). + m.Lock() + m.currentHeight = height + m.Unlock() err := m.RecoverReservations(runCtx) if err != nil { @@ -64,7 +120,9 @@ func (m *Manager) Run(ctx context.Context, height int32, select { case height := <-newBlockChan: log.Debugf("Received block %v", height) - currentHeight = height + m.Lock() + m.currentHeight = height + m.Unlock() case reservationRes, ok := <-ntfnChan: if !ok { @@ -76,13 +134,27 @@ func (m *Manager) Run(ctx context.Context, height int32, log.Debugf("Received reservation %x", reservationRes.ReservationId) - _, err := m.newReservation( - runCtx, uint32(currentHeight), reservationRes, + _, err := m.newReservationFromNtfn( + runCtx, uint32(m.currentHeight), reservationRes, ) if err != nil { - return err + log.Errorf("Unable to create reservation %x: %v", + reservationRes.ReservationId, err) } + case req := <-m.reqChan: + // We'll send the event in a goroutine to avoid blocking + // the main loop. + go func() { + err := req.fsm.SendEvent( + runCtx, req.event, req.eventCtx, + ) + if err != nil { + log.Errorf("Error sending event: %v", + err) + } + }() + case err := <-newBlockErrChan: return err @@ -93,9 +165,11 @@ func (m *Manager) Run(ctx context.Context, height int32, } } -// newReservation creates a new reservation from the reservation request. -func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, - req *reservationrpc.ServerReservationNotification) (*FSM, error) { +// newReservationFromNtfn creates a new reservation from the reservation +// notification. +func (m *Manager) newReservationFromNtfn(ctx context.Context, + currentHeight uint32, req *reservationrpc.ServerReservationNotification, +) (*FSM, error) { var reservationID ID err := reservationID.FromByteSlice( @@ -110,17 +184,42 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, return nil, err } + _, err = m.cfg.Store.GetReservation(ctx, reservationID) + switch { + case err == nil: + return nil, ErrReservationAlreadyExists + + case !errors.Is(err, ErrReservationNotFound): + return nil, err + } + // Create the reservation state machine. We need to pass in the runCtx // of the reservation manager so that the state machine will keep on // running even if the grpc conte - reservationFSM := NewFSM(m.cfg) + reservationFSM := NewFSM(m.cfg, ProtocolVersionServerInitiated) - // Add the reservation to the active reservations map. + // Add the reservation to the active reservations map. Check the map while + // holding the lock as concurrent callers may both have completed the store + // lookup above. m.Lock() + if _, ok := m.activeReservations[reservationID]; ok { + m.Unlock() + return nil, ErrReservationAlreadyExists + } + if len(m.activeReservations) >= maxActiveReservations { + m.Unlock() + return nil, ErrTooManyActiveReservations + } m.activeReservations[reservationID] = reservationFSM m.Unlock() - initContext := &InitReservationContext{ + reservationFSM.RegisterObserver(&finalStateObserver{ + manager: m, + id: reservationID, + fsm: reservationFSM, + }) + + initContext := &ServerRequestedInitContext{ reservationID: reservationID, serverPubkey: serverKey, value: btcutil.Amount(req.Value), @@ -130,9 +229,11 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, // Send the init event to the state machine. go func() { - err = reservationFSM.SendEvent(ctx, OnServerRequest, initContext) - if err != nil { - log.Errorf("Error sending init event: %v", err) + sendErr := reservationFSM.SendEvent( + ctx, OnServerRequest, initContext, + ) + if sendErr != nil { + log.Errorf("Error sending init event: %v", sendErr) } }() @@ -143,6 +244,12 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, fsm.WithWaitForStateOption(time.Second), ) if err != nil { + m.Lock() + if m.activeReservations[reservationID] == reservationFSM { + delete(m.activeReservations, reservationID) + } + m.Unlock() + if reservationFSM.LastActionError != nil { return nil, fmt.Errorf("error waiting for "+ "state: %v, last action error: %v", @@ -154,6 +261,73 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32, return reservationFSM, nil } +// RequestReservationFromServer sends a request to the server to create a new +// reservation. +func (m *Manager) RequestReservationFromServer(ctx context.Context, + value btcutil.Amount, expiry uint32, maxPrepaymentAmt btcutil.Amount) ( + *Reservation, error) { + + m.Lock() + currentHeight := m.currentHeight + m.Unlock() + // Create a new reservation req. + req := &ClientRequestedInitContext{ + value: value, + relativeExpiry: expiry, + heightHint: uint32(currentHeight), + maxPrepaymentAmt: maxPrepaymentAmt, + } + + reservationFSM := NewFSM(m.cfg, ProtocolVersionClientInitiated) + // Send the event to the main loop. reqChan is unbuffered so the + // raw send blocks until Run picks it up; if Run has already exited + // or the caller has cancelled, fall through with an error instead + // of hanging the RPC indefinitely. + select { + case m.reqChan <- &FSMSendEventReq{ + fsm: reservationFSM, + event: OnClientInitialized, + eventCtx: req, + }: + case <-ctx.Done(): + return nil, ctx.Err() + } + + // We'll now wait for the reservation to be in the state where we are + // sending the prepayment. + err := reservationFSM.DefaultObserver.WaitForState( + ctx, defaultWaitForStateTime, SendPrepaymentPayment, + fsm.WithAbortEarlyOnErrorOption(), + ) + if err != nil { + return nil, err + } + + // Now we can add the reservation to our active fsm. + m.Lock() + m.activeReservations[reservationFSM.reservation.ID] = reservationFSM + m.Unlock() + + return reservationFSM.reservation, nil +} + +// QuoteReservation quotes the server for a new reservation. +func (m *Manager) QuoteReservation(ctx context.Context, value btcutil.Amount, + expiry uint32) (btcutil.Amount, error) { + + quoteReq := &swapserverrpc.QuoteReservationRequest{ + Value: uint64(value), + Expiry: expiry, + } + + req, err := m.cfg.ReservationClient.QuoteReservation(ctx, quoteReq) + if err != nil { + return 0, err + } + + return btcutil.Amount(req.PrepayCost), nil +} + // RecoverReservations tries to recover all reservations that are still active // from the database. func (m *Manager) RecoverReservations(ctx context.Context) error { @@ -162,6 +336,16 @@ func (m *Manager) RecoverReservations(ctx context.Context) error { return err } + activeCount := 0 + for _, reservation := range reservations { + if !isFinalState(reservation.State) { + activeCount++ + } + } + if activeCount > maxActiveReservations { + return ErrTooManyActiveReservations + } + for _, reservation := range reservations { if isFinalState(reservation.State) { continue @@ -174,6 +358,11 @@ func (m *Manager) RecoverReservations(ctx context.Context) error { reservationFSM := NewFSMFromReservation(m.cfg, reservation) m.activeReservations[reservation.ID] = reservationFSM + reservationFSM.RegisterObserver(&finalStateObserver{ + manager: m, + id: reservation.ID, + fsm: reservationFSM, + }) // As SendEvent can block, we'll start a goroutine to process // the event. diff --git a/instantout/reservation/manager_test.go b/instantout/reservation/manager_test.go index 79455750c..7b7280f02 100644 --- a/instantout/reservation/manager_test.go +++ b/instantout/reservation/manager_test.go @@ -36,7 +36,7 @@ func TestManager(t *testing.T) { <-initChan // Create a new reservation. - reservationFSM, err := testContext.manager.newReservation( + reservationFSM, err := testContext.manager.newReservationFromNtfn( ctxb, uint32(testContext.mockLnd.Height), &swapserverrpc.ServerReservationNotification{ ReservationId: defaultReservationId[:], @@ -57,6 +57,7 @@ func TestManager(t *testing.T) { confTx := &wire.MsgTx{ TxOut: []*wire.TxOut{ { + Value: int64(defaultValue), PkScript: pkScript, }, }, @@ -97,6 +98,117 @@ func TestManager(t *testing.T) { // We'll now expect the reservation to be expired. err = reservationFSM.DefaultObserver.WaitForState(ctxb, 5*time.Second, Spent) require.NoError(t, err) + + testContext.manager.Lock() + _, ok := testContext.manager.activeReservations[defaultReservationId] + testContext.manager.Unlock() + require.False(t, ok) +} + +// TestManagerContinuesAfterInvalidNotification verifies that a malformed +// server notification doesn't stop the reservation manager from processing +// later notifications. +func TestManagerContinuesAfterInvalidNotification(t *testing.T) { + testContext := newManagerTestContext(t) + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + + initChan := make(chan struct{}) + errChan := make(chan error, 1) + go func() { + errChan <- testContext.manager.Run( + ctx, testContext.mockLnd.Height, initChan, + ) + }() + + <-initChan + + // A malformed ID is rejected by newReservationFromNtfn. The manager + // should log the error and continue processing the stream. + testContext.reservationNotificationChan <- &swapserverrpc.ServerReservationNotification{ + ReservationId: []byte{1}, + } + + testContext.reservationNotificationChan <- &swapserverrpc.ServerReservationNotification{ + ReservationId: defaultReservationId[:], + Value: uint64(defaultValue), + ServerKey: defaultPubkeyBytes, + Expiry: uint32(testContext.mockLnd.Height) + + defaultExpiry, + } + + select { + case <-testContext.mockLnd.RegisterConfChannel: + case err := <-errChan: + require.NoError(t, err) + t.Fatal("reservation manager stopped after malformed notification") + case <-time.After(5 * time.Second): + t.Fatal("valid reservation notification was not processed") + } + + cancel() + require.NoError(t, <-errChan) +} + +// TestManagerRejectsDuplicateReservation verifies that a duplicate server +// notification cannot replace the active FSM for an existing reservation. +func TestManagerRejectsDuplicateReservation(t *testing.T) { + testContext := newManagerTestContext(t) + ctx := t.Context() + req := &swapserverrpc.ServerReservationNotification{ + ReservationId: defaultReservationId[:], + Value: uint64(defaultValue), + ServerKey: defaultPubkeyBytes, + Expiry: uint32(testContext.mockLnd.Height) + + defaultExpiry, + } + + firstFSM, err := testContext.manager.newReservationFromNtfn( + ctx, uint32(testContext.mockLnd.Height), req, + ) + require.NoError(t, err) + + secondFSM, err := testContext.manager.newReservationFromNtfn( + ctx, uint32(testContext.mockLnd.Height), req, + ) + require.ErrorIs(t, err, ErrReservationAlreadyExists) + require.Nil(t, secondFSM) + require.Same( + t, firstFSM, + testContext.manager.activeReservations[defaultReservationId], + ) +} + +// TestManagerLimitsActiveReservations verifies that server notifications +// cannot grow the active FSM set without bound. +func TestManagerLimitsActiveReservations(t *testing.T) { + testContext := newManagerTestContext(t) + + for i := range maxActiveReservations { + var id ID + id[0] = byte(i) + id[1] = byte(i >> 8) + testContext.manager.activeReservations[id] = NewFSM( + testContext.manager.cfg, ProtocolVersionServerInitiated, + ) + } + + reservationFSM, err := testContext.manager.newReservationFromNtfn( + t.Context(), uint32(testContext.mockLnd.Height), + &swapserverrpc.ServerReservationNotification{ + ReservationId: defaultReservationId[:], + Value: uint64(defaultValue), + ServerKey: defaultPubkeyBytes, + Expiry: uint32(testContext.mockLnd.Height) + + defaultExpiry, + }, + ) + require.ErrorIs(t, err, ErrTooManyActiveReservations) + require.Nil(t, reservationFSM) + require.Len( + t, testContext.manager.activeReservations, + maxActiveReservations, + ) } // ManagerTestContext is a helper struct that contains all the necessary diff --git a/instantout/reservation/reservation.go b/instantout/reservation/reservation.go index 5a167d2e1..e450ecb1d 100644 --- a/instantout/reservation/reservation.go +++ b/instantout/reservation/reservation.go @@ -62,6 +62,9 @@ type Reservation struct { // Outpoint is the outpoint of the reservation. Outpoint *wire.OutPoint + // PrepayInvoice is the invoice that the client paid as a prepayment. + PrepayInvoice string + // InitiationHeight is the height at which the reservation was // initiated. InitiationHeight int32 @@ -142,8 +145,14 @@ func (r *Reservation) findReservationOutput(tx *wire.MsgTx) (*wire.OutPoint, return nil, err } + var foundScript bool for i, txOut := range tx.TxOut { if bytes.Equal(txOut.PkScript, pkScript) { + foundScript = true + if txOut.Value != int64(r.Value) { + continue + } + return &wire.OutPoint{ Hash: tx.TxHash(), Index: uint32(i), @@ -151,6 +160,11 @@ func (r *Reservation) findReservationOutput(tx *wire.MsgTx) (*wire.OutPoint, } } + if foundScript { + return nil, fmt.Errorf("reservation output value mismatch: "+ + "expected %d", r.Value) + } + return nil, errors.New("reservation output not found") } diff --git a/instantout/reservation/store.go b/instantout/reservation/store.go index 117d02c69..dabade502 100644 --- a/instantout/reservation/store.go +++ b/instantout/reservation/store.go @@ -84,6 +84,7 @@ func (r *SQLStore) CreateReservation(ctx context.Context, ClientKeyIndex: int32(reservation.KeyLocator.Index), InitiationHeight: reservation.InitiationHeight, ProtocolVersion: int32(reservation.ProtocolVersion), + PrepayInvoice: reservation.PrepayInvoice, } updateArgs := sqlc.InsertReservationUpdateParams{ @@ -180,6 +181,10 @@ func (r *SQLStore) GetReservation(ctx context.Context, return nil }) if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrReservationNotFound + } + return nil, err } @@ -289,6 +294,7 @@ func sqlReservationToReservation(row sqlc.Reservation, InitiationHeight: row.InitiationHeight, State: fsm.StateType(lastUpdate.UpdateState), ProtocolVersion: ProtocolVersion(row.ProtocolVersion), + PrepayInvoice: row.PrepayInvoice, }, nil } diff --git a/instantout/store.go b/instantout/store.go index 25d7fe704..0e2a5066f 100644 --- a/instantout/store.go +++ b/instantout/store.go @@ -104,7 +104,7 @@ func (s *SQLStore) CreateInstantLoopOut(ctx context.Context, AmountRequested: int64(instantOut.Value), CltvExpiry: instantOut.CltvExpiry, MaxMinerFee: 0, - MaxSwapFee: 0, + MaxSwapFee: int64(instantOut.MaxSwapFee), InitiationHeight: instantOut.initiationHeight, ProtocolVersion: int32(instantOut.protocolVersion), Label: "", @@ -368,6 +368,7 @@ func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context, protocolVersion: ProtocolVersion(row.ProtocolVersion), initiationHeight: row.InitiationHeight, Value: btcutil.Amount(row.AmountRequested), + MaxSwapFee: btcutil.Amount(row.MaxSwapFee), keyLocator: keychain.KeyLocator{ Family: keychain.KeyFamily(row.ClientKeyFamily), Index: uint32(row.ClientKeyIndex), diff --git a/loopd/daemon.go b/loopd/daemon.go index 319709d84..055721bd3 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -761,6 +761,8 @@ func (d *Daemon) initialize(withMacaroonService bool) error { ChainNotifier: d.lnd.ChainNotifier, ReservationClient: reservationClient, NotificationManager: notificationManager, + LightningClient: d.lnd.Client, + RouterClient: d.lnd.Router, } reservationManager = reservation.NewManager( diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 28b4f722e..81d72f2ae 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -1739,6 +1739,49 @@ func (s *swapClientServer) ListReservations(ctx context.Context, }, nil } +func (s *swapClientServer) ReservationRequest(ctx context.Context, + req *looprpc.ReservationRequestRequest) ( + *looprpc.ReservationRequestResponse, error) { + + if s.reservationManager == nil { + return nil, status.Error(codes.Unimplemented, + "Restart loop with --experimental") + } + + reservation, err := s.reservationManager.RequestReservationFromServer( + ctx, btcutil.Amount(req.Amt), req.Expiry, + btcutil.Amount(req.MaxPrepayAmt), + ) + if err != nil { + return nil, err + } + + return &looprpc.ReservationRequestResponse{ + Reservation: toClientReservation(reservation), + }, nil +} + +func (s *swapClientServer) ReservationQuote(ctx context.Context, + req *looprpc.ReservationQuoteRequest) ( + *looprpc.ReservationQuoteResponse, error) { + + if s.reservationManager == nil { + return nil, status.Error(codes.Unimplemented, + "Restart loop with --experimental") + } + + quote, err := s.reservationManager.QuoteReservation( + ctx, btcutil.Amount(req.Amt), req.Expiry, + ) + if err != nil { + return nil, err + } + + return &looprpc.ReservationQuoteResponse{ + PrepayAmt: uint64(quote), + }, nil +} + // InstantOut initiates an instant out swap. func (s *swapClientServer) InstantOut(ctx context.Context, req *looprpc.InstantOutRequest) (*looprpc.InstantOutResponse, @@ -1765,6 +1808,7 @@ func (s *swapClientServer) InstantOut(ctx context.Context, instantOutFsm, err := s.instantOutManager.NewInstantOut( ctx, reservationIds, req.DestAddr, + btcutil.Amount(req.MaxSwapFeeSat), ) if err != nil { return nil, err diff --git a/loopdb/sqlc/migrations/000014_reservation_protocol_version.down.sql b/loopdb/sqlc/migrations/000014_reservation_protocol_version.down.sql index d23af75ec..ca38f26ad 100644 --- a/loopdb/sqlc/migrations/000014_reservation_protocol_version.down.sql +++ b/loopdb/sqlc/migrations/000014_reservation_protocol_version.down.sql @@ -1,3 +1,3 @@ -- protocol_version is used to determine the version of the reservation protocol -- that was used to create the reservation. -ALTER TABLE reservations DROP COLUMN protocol_Version; +ALTER TABLE reservations DROP COLUMN protocol_version; diff --git a/loopdb/sqlc/migrations/000014_reservation_protocol_version.up.sql b/loopdb/sqlc/migrations/000014_reservation_protocol_version.up.sql index 29da580a0..464590abf 100644 --- a/loopdb/sqlc/migrations/000014_reservation_protocol_version.up.sql +++ b/loopdb/sqlc/migrations/000014_reservation_protocol_version.up.sql @@ -1,3 +1,3 @@ -- protocol_version is used to determine the version of the reservation protocol -- that was used to create the reservation. -ALTER TABLE reservations ADD COLUMN protocol_Version INTEGER NOT NULL DEFAULT 0; \ No newline at end of file +ALTER TABLE reservations ADD COLUMN protocol_version INTEGER NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/loopdb/sqlc/migrations/000022_reservation_prepay_invoice.down.sql b/loopdb/sqlc/migrations/000022_reservation_prepay_invoice.down.sql new file mode 100644 index 000000000..61ae44c69 --- /dev/null +++ b/loopdb/sqlc/migrations/000022_reservation_prepay_invoice.down.sql @@ -0,0 +1 @@ +ALTER TABLE reservations DROP COLUMN prepay_invoice; diff --git a/loopdb/sqlc/migrations/000022_reservation_prepay_invoice.up.sql b/loopdb/sqlc/migrations/000022_reservation_prepay_invoice.up.sql new file mode 100644 index 000000000..37075170f --- /dev/null +++ b/loopdb/sqlc/migrations/000022_reservation_prepay_invoice.up.sql @@ -0,0 +1,3 @@ +-- prepay_invoice is a field that will store the invoice of the prepay payment +-- that pays for the reservation. +ALTER TABLE reservations ADD COLUMN prepay_invoice TEXT NOT NULL DEFAULT ''; diff --git a/loopdb/sqlc/models.go b/loopdb/sqlc/models.go index 78a75d042..62bf5a66c 100644 --- a/loopdb/sqlc/models.go +++ b/loopdb/sqlc/models.go @@ -115,6 +115,7 @@ type Reservation struct { OutIndex sql.NullInt32 ConfirmationHeight sql.NullInt32 ProtocolVersion int32 + PrepayInvoice string } type ReservationUpdate struct { diff --git a/loopdb/sqlc/queries/reservations.sql b/loopdb/sqlc/queries/reservations.sql index ba95f53a7..f95923260 100644 --- a/loopdb/sqlc/queries/reservations.sql +++ b/loopdb/sqlc/queries/reservations.sql @@ -8,7 +8,8 @@ INSERT INTO reservations ( client_key_family, client_key_index, initiation_height, - protocol_version + protocol_version, + prepay_invoice ) VALUES ( $1, $2, @@ -18,7 +19,8 @@ INSERT INTO reservations ( $6, $7, $8, - $9 + $9, + $10 ); -- name: UpdateReservation :exec diff --git a/loopdb/sqlc/reservations.sql.go b/loopdb/sqlc/reservations.sql.go index 3a1ba8987..275f45b83 100644 --- a/loopdb/sqlc/reservations.sql.go +++ b/loopdb/sqlc/reservations.sql.go @@ -21,7 +21,8 @@ INSERT INTO reservations ( client_key_family, client_key_index, initiation_height, - protocol_version + protocol_version, + prepay_invoice ) VALUES ( $1, $2, @@ -31,7 +32,8 @@ INSERT INTO reservations ( $6, $7, $8, - $9 + $9, + $10 ) ` @@ -45,6 +47,7 @@ type CreateReservationParams struct { ClientKeyIndex int32 InitiationHeight int32 ProtocolVersion int32 + PrepayInvoice string } func (q *Queries) CreateReservation(ctx context.Context, arg CreateReservationParams) error { @@ -58,13 +61,14 @@ func (q *Queries) CreateReservation(ctx context.Context, arg CreateReservationPa arg.ClientKeyIndex, arg.InitiationHeight, arg.ProtocolVersion, + arg.PrepayInvoice, ) return err } const getReservation = `-- name: GetReservation :one SELECT - id, reservation_id, client_pubkey, server_pubkey, expiry, value, client_key_family, client_key_index, initiation_height, tx_hash, out_index, confirmation_height, protocol_version + id, reservation_id, client_pubkey, server_pubkey, expiry, value, client_key_family, client_key_index, initiation_height, tx_hash, out_index, confirmation_height, protocol_version, prepay_invoice FROM reservations WHERE @@ -88,6 +92,7 @@ func (q *Queries) GetReservation(ctx context.Context, reservationID []byte) (Res &i.OutIndex, &i.ConfirmationHeight, &i.ProtocolVersion, + &i.PrepayInvoice, ) return i, err } @@ -133,7 +138,7 @@ func (q *Queries) GetReservationUpdates(ctx context.Context, reservationID []byt const getReservations = `-- name: GetReservations :many SELECT - id, reservation_id, client_pubkey, server_pubkey, expiry, value, client_key_family, client_key_index, initiation_height, tx_hash, out_index, confirmation_height, protocol_version + id, reservation_id, client_pubkey, server_pubkey, expiry, value, client_key_family, client_key_index, initiation_height, tx_hash, out_index, confirmation_height, protocol_version, prepay_invoice FROM reservations ORDER BY @@ -163,6 +168,7 @@ func (q *Queries) GetReservations(ctx context.Context) ([]Reservation, error) { &i.OutIndex, &i.ConfirmationHeight, &i.ProtocolVersion, + &i.PrepayInvoice, ); err != nil { return nil, err } diff --git a/looprpc/client.pb.go b/looprpc/client.pb.go index ec3246482..882efd771 100644 --- a/looprpc/client.pb.go +++ b/looprpc/client.pb.go @@ -4457,6 +4457,212 @@ func (x *ClientReservation) GetExpiry() uint32 { return 0 } +type ReservationRequestRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The amount to reserve in satoshis. + Amt uint64 `protobuf:"varint,1,opt,name=amt,proto3" json:"amt,omitempty"` + // The relative expiry of the reservation in blocks. + Expiry uint32 `protobuf:"varint,2,opt,name=expiry,proto3" json:"expiry,omitempty"` + // The maximum amt in satoshis we allow for the prepayment. + MaxPrepayAmt uint64 `protobuf:"varint,3,opt,name=max_prepay_amt,json=maxPrepayAmt,proto3" json:"max_prepay_amt,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReservationRequestRequest) Reset() { + *x = ReservationRequestRequest{} + mi := &file_client_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReservationRequestRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReservationRequestRequest) ProtoMessage() {} + +func (x *ReservationRequestRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[48] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReservationRequestRequest.ProtoReflect.Descriptor instead. +func (*ReservationRequestRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{48} +} + +func (x *ReservationRequestRequest) GetAmt() uint64 { + if x != nil { + return x.Amt + } + return 0 +} + +func (x *ReservationRequestRequest) GetExpiry() uint32 { + if x != nil { + return x.Expiry + } + return 0 +} + +func (x *ReservationRequestRequest) GetMaxPrepayAmt() uint64 { + if x != nil { + return x.MaxPrepayAmt + } + return 0 +} + +type ReservationRequestResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Reservation *ClientReservation `protobuf:"bytes,1,opt,name=reservation,proto3" json:"reservation,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReservationRequestResponse) Reset() { + *x = ReservationRequestResponse{} + mi := &file_client_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReservationRequestResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReservationRequestResponse) ProtoMessage() {} + +func (x *ReservationRequestResponse) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[49] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReservationRequestResponse.ProtoReflect.Descriptor instead. +func (*ReservationRequestResponse) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{49} +} + +func (x *ReservationRequestResponse) GetReservation() *ClientReservation { + if x != nil { + return x.Reservation + } + return nil +} + +type ReservationQuoteRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The amount to reserve in satoshis. + Amt uint64 `protobuf:"varint,1,opt,name=amt,proto3" json:"amt,omitempty"` + // The relative expiry of the reservation in blocks. + Expiry uint32 `protobuf:"varint,2,opt,name=expiry,proto3" json:"expiry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReservationQuoteRequest) Reset() { + *x = ReservationQuoteRequest{} + mi := &file_client_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReservationQuoteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReservationQuoteRequest) ProtoMessage() {} + +func (x *ReservationQuoteRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[50] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReservationQuoteRequest.ProtoReflect.Descriptor instead. +func (*ReservationQuoteRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{50} +} + +func (x *ReservationQuoteRequest) GetAmt() uint64 { + if x != nil { + return x.Amt + } + return 0 +} + +func (x *ReservationQuoteRequest) GetExpiry() uint32 { + if x != nil { + return x.Expiry + } + return 0 +} + +type ReservationQuoteResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The prepay fee that will be charged for the reservation. + PrepayAmt uint64 `protobuf:"varint,1,opt,name=prepay_amt,json=prepayAmt,proto3" json:"prepay_amt,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReservationQuoteResponse) Reset() { + *x = ReservationQuoteResponse{} + mi := &file_client_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReservationQuoteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReservationQuoteResponse) ProtoMessage() {} + +func (x *ReservationQuoteResponse) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[51] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReservationQuoteResponse.ProtoReflect.Descriptor instead. +func (*ReservationQuoteResponse) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{51} +} + +func (x *ReservationQuoteResponse) GetPrepayAmt() uint64 { + if x != nil { + return x.PrepayAmt + } + return 0 +} + type InstantOutRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The reservations to use for the swap. @@ -4467,14 +4673,16 @@ type InstantOutRequest struct { OutgoingChanSet []uint64 `protobuf:"varint,2,rep,packed,name=outgoing_chan_set,json=outgoingChanSet,proto3" json:"outgoing_chan_set,omitempty"` // An optional address to sweep the onchain funds to. If not set, the funds // will be swept to the wallet's internal address. - DestAddr string `protobuf:"bytes,3,opt,name=dest_addr,json=destAddr,proto3" json:"dest_addr,omitempty"` + DestAddr string `protobuf:"bytes,3,opt,name=dest_addr,json=destAddr,proto3" json:"dest_addr,omitempty"` + // The maximum off-chain swap fee that may be charged for the swap. + MaxSwapFeeSat int64 `protobuf:"varint,4,opt,name=max_swap_fee_sat,json=maxSwapFeeSat,proto3" json:"max_swap_fee_sat,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InstantOutRequest) Reset() { *x = InstantOutRequest{} - mi := &file_client_proto_msgTypes[48] + mi := &file_client_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4486,7 +4694,7 @@ func (x *InstantOutRequest) String() string { func (*InstantOutRequest) ProtoMessage() {} func (x *InstantOutRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[48] + mi := &file_client_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4499,7 +4707,7 @@ func (x *InstantOutRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use InstantOutRequest.ProtoReflect.Descriptor instead. func (*InstantOutRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{48} + return file_client_proto_rawDescGZIP(), []int{52} } func (x *InstantOutRequest) GetReservationIds() [][]byte { @@ -4523,6 +4731,13 @@ func (x *InstantOutRequest) GetDestAddr() string { return "" } +func (x *InstantOutRequest) GetMaxSwapFeeSat() int64 { + if x != nil { + return x.MaxSwapFeeSat + } + return 0 +} + type InstantOutResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The hash of the swap preimage. @@ -4537,7 +4752,7 @@ type InstantOutResponse struct { func (x *InstantOutResponse) Reset() { *x = InstantOutResponse{} - mi := &file_client_proto_msgTypes[49] + mi := &file_client_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4549,7 +4764,7 @@ func (x *InstantOutResponse) String() string { func (*InstantOutResponse) ProtoMessage() {} func (x *InstantOutResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[49] + mi := &file_client_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4562,7 +4777,7 @@ func (x *InstantOutResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use InstantOutResponse.ProtoReflect.Descriptor instead. func (*InstantOutResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{49} + return file_client_proto_rawDescGZIP(), []int{53} } func (x *InstantOutResponse) GetInstantOutHash() []byte { @@ -4603,7 +4818,7 @@ type InstantOutQuoteRequest struct { func (x *InstantOutQuoteRequest) Reset() { *x = InstantOutQuoteRequest{} - mi := &file_client_proto_msgTypes[50] + mi := &file_client_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4615,7 +4830,7 @@ func (x *InstantOutQuoteRequest) String() string { func (*InstantOutQuoteRequest) ProtoMessage() {} func (x *InstantOutQuoteRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[50] + mi := &file_client_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4628,7 +4843,7 @@ func (x *InstantOutQuoteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use InstantOutQuoteRequest.ProtoReflect.Descriptor instead. func (*InstantOutQuoteRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{50} + return file_client_proto_rawDescGZIP(), []int{54} } func (x *InstantOutQuoteRequest) GetAmt() uint64 { @@ -4666,7 +4881,7 @@ type InstantOutQuoteResponse struct { func (x *InstantOutQuoteResponse) Reset() { *x = InstantOutQuoteResponse{} - mi := &file_client_proto_msgTypes[51] + mi := &file_client_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4678,7 +4893,7 @@ func (x *InstantOutQuoteResponse) String() string { func (*InstantOutQuoteResponse) ProtoMessage() {} func (x *InstantOutQuoteResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[51] + mi := &file_client_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4691,7 +4906,7 @@ func (x *InstantOutQuoteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use InstantOutQuoteResponse.ProtoReflect.Descriptor instead. func (*InstantOutQuoteResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{51} + return file_client_proto_rawDescGZIP(), []int{55} } func (x *InstantOutQuoteResponse) GetServiceFeeSat() int64 { @@ -4716,7 +4931,7 @@ type ListInstantOutsRequest struct { func (x *ListInstantOutsRequest) Reset() { *x = ListInstantOutsRequest{} - mi := &file_client_proto_msgTypes[52] + mi := &file_client_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4728,7 +4943,7 @@ func (x *ListInstantOutsRequest) String() string { func (*ListInstantOutsRequest) ProtoMessage() {} func (x *ListInstantOutsRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[52] + mi := &file_client_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4741,7 +4956,7 @@ func (x *ListInstantOutsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListInstantOutsRequest.ProtoReflect.Descriptor instead. func (*ListInstantOutsRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{52} + return file_client_proto_rawDescGZIP(), []int{56} } type ListInstantOutsResponse struct { @@ -4754,7 +4969,7 @@ type ListInstantOutsResponse struct { func (x *ListInstantOutsResponse) Reset() { *x = ListInstantOutsResponse{} - mi := &file_client_proto_msgTypes[53] + mi := &file_client_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4766,7 +4981,7 @@ func (x *ListInstantOutsResponse) String() string { func (*ListInstantOutsResponse) ProtoMessage() {} func (x *ListInstantOutsResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[53] + mi := &file_client_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4779,7 +4994,7 @@ func (x *ListInstantOutsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListInstantOutsResponse.ProtoReflect.Descriptor instead. func (*ListInstantOutsResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{53} + return file_client_proto_rawDescGZIP(), []int{57} } func (x *ListInstantOutsResponse) GetSwaps() []*InstantOut { @@ -4807,7 +5022,7 @@ type InstantOut struct { func (x *InstantOut) Reset() { *x = InstantOut{} - mi := &file_client_proto_msgTypes[54] + mi := &file_client_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4819,7 +5034,7 @@ func (x *InstantOut) String() string { func (*InstantOut) ProtoMessage() {} func (x *InstantOut) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[54] + mi := &file_client_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4832,7 +5047,7 @@ func (x *InstantOut) ProtoReflect() protoreflect.Message { // Deprecated: Use InstantOut.ProtoReflect.Descriptor instead. func (*InstantOut) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{54} + return file_client_proto_rawDescGZIP(), []int{58} } func (x *InstantOut) GetSwapHash() []byte { @@ -4880,7 +5095,7 @@ type NewStaticAddressRequest struct { func (x *NewStaticAddressRequest) Reset() { *x = NewStaticAddressRequest{} - mi := &file_client_proto_msgTypes[55] + mi := &file_client_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4892,7 +5107,7 @@ func (x *NewStaticAddressRequest) String() string { func (*NewStaticAddressRequest) ProtoMessage() {} func (x *NewStaticAddressRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[55] + mi := &file_client_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4905,7 +5120,7 @@ func (x *NewStaticAddressRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NewStaticAddressRequest.ProtoReflect.Descriptor instead. func (*NewStaticAddressRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{55} + return file_client_proto_rawDescGZIP(), []int{59} } func (x *NewStaticAddressRequest) GetClientKey() []byte { @@ -4927,7 +5142,7 @@ type NewStaticAddressResponse struct { func (x *NewStaticAddressResponse) Reset() { *x = NewStaticAddressResponse{} - mi := &file_client_proto_msgTypes[56] + mi := &file_client_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4939,7 +5154,7 @@ func (x *NewStaticAddressResponse) String() string { func (*NewStaticAddressResponse) ProtoMessage() {} func (x *NewStaticAddressResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[56] + mi := &file_client_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4952,7 +5167,7 @@ func (x *NewStaticAddressResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NewStaticAddressResponse.ProtoReflect.Descriptor instead. func (*NewStaticAddressResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{56} + return file_client_proto_rawDescGZIP(), []int{60} } func (x *NewStaticAddressResponse) GetAddress() string { @@ -4982,7 +5197,7 @@ type ListUnspentDepositsRequest struct { func (x *ListUnspentDepositsRequest) Reset() { *x = ListUnspentDepositsRequest{} - mi := &file_client_proto_msgTypes[57] + mi := &file_client_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4994,7 +5209,7 @@ func (x *ListUnspentDepositsRequest) String() string { func (*ListUnspentDepositsRequest) ProtoMessage() {} func (x *ListUnspentDepositsRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[57] + mi := &file_client_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5007,7 +5222,7 @@ func (x *ListUnspentDepositsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUnspentDepositsRequest.ProtoReflect.Descriptor instead. func (*ListUnspentDepositsRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{57} + return file_client_proto_rawDescGZIP(), []int{61} } func (x *ListUnspentDepositsRequest) GetMinConfs() int32 { @@ -5034,7 +5249,7 @@ type ListUnspentDepositsResponse struct { func (x *ListUnspentDepositsResponse) Reset() { *x = ListUnspentDepositsResponse{} - mi := &file_client_proto_msgTypes[58] + mi := &file_client_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5046,7 +5261,7 @@ func (x *ListUnspentDepositsResponse) String() string { func (*ListUnspentDepositsResponse) ProtoMessage() {} func (x *ListUnspentDepositsResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[58] + mi := &file_client_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5059,7 +5274,7 @@ func (x *ListUnspentDepositsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUnspentDepositsResponse.ProtoReflect.Descriptor instead. func (*ListUnspentDepositsResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{58} + return file_client_proto_rawDescGZIP(), []int{62} } func (x *ListUnspentDepositsResponse) GetUtxos() []*Utxo { @@ -5085,7 +5300,7 @@ type Utxo struct { func (x *Utxo) Reset() { *x = Utxo{} - mi := &file_client_proto_msgTypes[59] + mi := &file_client_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5097,7 +5312,7 @@ func (x *Utxo) String() string { func (*Utxo) ProtoMessage() {} func (x *Utxo) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[59] + mi := &file_client_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5110,7 +5325,7 @@ func (x *Utxo) ProtoReflect() protoreflect.Message { // Deprecated: Use Utxo.ProtoReflect.Descriptor instead. func (*Utxo) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{59} + return file_client_proto_rawDescGZIP(), []int{63} } func (x *Utxo) GetStaticAddress() string { @@ -5163,7 +5378,7 @@ type WithdrawDepositsRequest struct { func (x *WithdrawDepositsRequest) Reset() { *x = WithdrawDepositsRequest{} - mi := &file_client_proto_msgTypes[60] + mi := &file_client_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5175,7 +5390,7 @@ func (x *WithdrawDepositsRequest) String() string { func (*WithdrawDepositsRequest) ProtoMessage() {} func (x *WithdrawDepositsRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[60] + mi := &file_client_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5188,7 +5403,7 @@ func (x *WithdrawDepositsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WithdrawDepositsRequest.ProtoReflect.Descriptor instead. func (*WithdrawDepositsRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{60} + return file_client_proto_rawDescGZIP(), []int{64} } func (x *WithdrawDepositsRequest) GetOutpoints() []*lnrpc.OutPoint { @@ -5238,7 +5453,7 @@ type WithdrawDepositsResponse struct { func (x *WithdrawDepositsResponse) Reset() { *x = WithdrawDepositsResponse{} - mi := &file_client_proto_msgTypes[61] + mi := &file_client_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5250,7 +5465,7 @@ func (x *WithdrawDepositsResponse) String() string { func (*WithdrawDepositsResponse) ProtoMessage() {} func (x *WithdrawDepositsResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[61] + mi := &file_client_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5263,7 +5478,7 @@ func (x *WithdrawDepositsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WithdrawDepositsResponse.ProtoReflect.Descriptor instead. func (*WithdrawDepositsResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{61} + return file_client_proto_rawDescGZIP(), []int{65} } func (x *WithdrawDepositsResponse) GetWithdrawalTxHash() string { @@ -5292,7 +5507,7 @@ type ListStaticAddressDepositsRequest struct { func (x *ListStaticAddressDepositsRequest) Reset() { *x = ListStaticAddressDepositsRequest{} - mi := &file_client_proto_msgTypes[62] + mi := &file_client_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5304,7 +5519,7 @@ func (x *ListStaticAddressDepositsRequest) String() string { func (*ListStaticAddressDepositsRequest) ProtoMessage() {} func (x *ListStaticAddressDepositsRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[62] + mi := &file_client_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5317,7 +5532,7 @@ func (x *ListStaticAddressDepositsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListStaticAddressDepositsRequest.ProtoReflect.Descriptor instead. func (*ListStaticAddressDepositsRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{62} + return file_client_proto_rawDescGZIP(), []int{66} } func (x *ListStaticAddressDepositsRequest) GetStateFilter() DepositState { @@ -5344,7 +5559,7 @@ type ListStaticAddressDepositsResponse struct { func (x *ListStaticAddressDepositsResponse) Reset() { *x = ListStaticAddressDepositsResponse{} - mi := &file_client_proto_msgTypes[63] + mi := &file_client_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5356,7 +5571,7 @@ func (x *ListStaticAddressDepositsResponse) String() string { func (*ListStaticAddressDepositsResponse) ProtoMessage() {} func (x *ListStaticAddressDepositsResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[63] + mi := &file_client_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5369,7 +5584,7 @@ func (x *ListStaticAddressDepositsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListStaticAddressDepositsResponse.ProtoReflect.Descriptor instead. func (*ListStaticAddressDepositsResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{63} + return file_client_proto_rawDescGZIP(), []int{67} } func (x *ListStaticAddressDepositsResponse) GetFilteredDeposits() []*Deposit { @@ -5387,7 +5602,7 @@ type ListStaticAddressWithdrawalRequest struct { func (x *ListStaticAddressWithdrawalRequest) Reset() { *x = ListStaticAddressWithdrawalRequest{} - mi := &file_client_proto_msgTypes[64] + mi := &file_client_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5399,7 +5614,7 @@ func (x *ListStaticAddressWithdrawalRequest) String() string { func (*ListStaticAddressWithdrawalRequest) ProtoMessage() {} func (x *ListStaticAddressWithdrawalRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[64] + mi := &file_client_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5412,7 +5627,7 @@ func (x *ListStaticAddressWithdrawalRequest) ProtoReflect() protoreflect.Message // Deprecated: Use ListStaticAddressWithdrawalRequest.ProtoReflect.Descriptor instead. func (*ListStaticAddressWithdrawalRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{64} + return file_client_proto_rawDescGZIP(), []int{68} } type ListStaticAddressWithdrawalResponse struct { @@ -5425,7 +5640,7 @@ type ListStaticAddressWithdrawalResponse struct { func (x *ListStaticAddressWithdrawalResponse) Reset() { *x = ListStaticAddressWithdrawalResponse{} - mi := &file_client_proto_msgTypes[65] + mi := &file_client_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5437,7 +5652,7 @@ func (x *ListStaticAddressWithdrawalResponse) String() string { func (*ListStaticAddressWithdrawalResponse) ProtoMessage() {} func (x *ListStaticAddressWithdrawalResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[65] + mi := &file_client_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5450,7 +5665,7 @@ func (x *ListStaticAddressWithdrawalResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use ListStaticAddressWithdrawalResponse.ProtoReflect.Descriptor instead. func (*ListStaticAddressWithdrawalResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{65} + return file_client_proto_rawDescGZIP(), []int{69} } func (x *ListStaticAddressWithdrawalResponse) GetWithdrawals() []*StaticAddressWithdrawal { @@ -5468,7 +5683,7 @@ type ListStaticAddressSwapsRequest struct { func (x *ListStaticAddressSwapsRequest) Reset() { *x = ListStaticAddressSwapsRequest{} - mi := &file_client_proto_msgTypes[66] + mi := &file_client_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5480,7 +5695,7 @@ func (x *ListStaticAddressSwapsRequest) String() string { func (*ListStaticAddressSwapsRequest) ProtoMessage() {} func (x *ListStaticAddressSwapsRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[66] + mi := &file_client_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5493,7 +5708,7 @@ func (x *ListStaticAddressSwapsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListStaticAddressSwapsRequest.ProtoReflect.Descriptor instead. func (*ListStaticAddressSwapsRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{66} + return file_client_proto_rawDescGZIP(), []int{70} } type ListStaticAddressSwapsResponse struct { @@ -5506,7 +5721,7 @@ type ListStaticAddressSwapsResponse struct { func (x *ListStaticAddressSwapsResponse) Reset() { *x = ListStaticAddressSwapsResponse{} - mi := &file_client_proto_msgTypes[67] + mi := &file_client_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5518,7 +5733,7 @@ func (x *ListStaticAddressSwapsResponse) String() string { func (*ListStaticAddressSwapsResponse) ProtoMessage() {} func (x *ListStaticAddressSwapsResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[67] + mi := &file_client_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5531,7 +5746,7 @@ func (x *ListStaticAddressSwapsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListStaticAddressSwapsResponse.ProtoReflect.Descriptor instead. func (*ListStaticAddressSwapsResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{67} + return file_client_proto_rawDescGZIP(), []int{71} } func (x *ListStaticAddressSwapsResponse) GetSwaps() []*StaticAddressLoopInSwap { @@ -5549,7 +5764,7 @@ type StaticAddressSummaryRequest struct { func (x *StaticAddressSummaryRequest) Reset() { *x = StaticAddressSummaryRequest{} - mi := &file_client_proto_msgTypes[68] + mi := &file_client_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5561,7 +5776,7 @@ func (x *StaticAddressSummaryRequest) String() string { func (*StaticAddressSummaryRequest) ProtoMessage() {} func (x *StaticAddressSummaryRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[68] + mi := &file_client_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5574,7 +5789,7 @@ func (x *StaticAddressSummaryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticAddressSummaryRequest.ProtoReflect.Descriptor instead. func (*StaticAddressSummaryRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{68} + return file_client_proto_rawDescGZIP(), []int{72} } type StaticAddressSummaryResponse struct { @@ -5605,7 +5820,7 @@ type StaticAddressSummaryResponse struct { func (x *StaticAddressSummaryResponse) Reset() { *x = StaticAddressSummaryResponse{} - mi := &file_client_proto_msgTypes[69] + mi := &file_client_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5617,7 +5832,7 @@ func (x *StaticAddressSummaryResponse) String() string { func (*StaticAddressSummaryResponse) ProtoMessage() {} func (x *StaticAddressSummaryResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[69] + mi := &file_client_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5630,7 +5845,7 @@ func (x *StaticAddressSummaryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticAddressSummaryResponse.ProtoReflect.Descriptor instead. func (*StaticAddressSummaryResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{69} + return file_client_proto_rawDescGZIP(), []int{73} } func (x *StaticAddressSummaryResponse) GetStaticAddress() string { @@ -5727,7 +5942,7 @@ type Deposit struct { func (x *Deposit) Reset() { *x = Deposit{} - mi := &file_client_proto_msgTypes[70] + mi := &file_client_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5739,7 +5954,7 @@ func (x *Deposit) String() string { func (*Deposit) ProtoMessage() {} func (x *Deposit) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[70] + mi := &file_client_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5752,7 +5967,7 @@ func (x *Deposit) ProtoReflect() protoreflect.Message { // Deprecated: Use Deposit.ProtoReflect.Descriptor instead. func (*Deposit) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{70} + return file_client_proto_rawDescGZIP(), []int{74} } func (x *Deposit) GetId() []byte { @@ -5826,7 +6041,7 @@ type StaticAddressWithdrawal struct { func (x *StaticAddressWithdrawal) Reset() { *x = StaticAddressWithdrawal{} - mi := &file_client_proto_msgTypes[71] + mi := &file_client_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5838,7 +6053,7 @@ func (x *StaticAddressWithdrawal) String() string { func (*StaticAddressWithdrawal) ProtoMessage() {} func (x *StaticAddressWithdrawal) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[71] + mi := &file_client_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5851,7 +6066,7 @@ func (x *StaticAddressWithdrawal) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticAddressWithdrawal.ProtoReflect.Descriptor instead. func (*StaticAddressWithdrawal) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{71} + return file_client_proto_rawDescGZIP(), []int{75} } func (x *StaticAddressWithdrawal) GetTxId() string { @@ -5926,7 +6141,7 @@ type StaticAddressLoopInSwap struct { func (x *StaticAddressLoopInSwap) Reset() { *x = StaticAddressLoopInSwap{} - mi := &file_client_proto_msgTypes[72] + mi := &file_client_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5938,7 +6153,7 @@ func (x *StaticAddressLoopInSwap) String() string { func (*StaticAddressLoopInSwap) ProtoMessage() {} func (x *StaticAddressLoopInSwap) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[72] + mi := &file_client_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5951,7 +6166,7 @@ func (x *StaticAddressLoopInSwap) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticAddressLoopInSwap.ProtoReflect.Descriptor instead. func (*StaticAddressLoopInSwap) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{72} + return file_client_proto_rawDescGZIP(), []int{76} } func (x *StaticAddressLoopInSwap) GetSwapHash() []byte { @@ -6084,7 +6299,7 @@ type StaticAddressLoopInRequest struct { func (x *StaticAddressLoopInRequest) Reset() { *x = StaticAddressLoopInRequest{} - mi := &file_client_proto_msgTypes[73] + mi := &file_client_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6096,7 +6311,7 @@ func (x *StaticAddressLoopInRequest) String() string { func (*StaticAddressLoopInRequest) ProtoMessage() {} func (x *StaticAddressLoopInRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[73] + mi := &file_client_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6109,7 +6324,7 @@ func (x *StaticAddressLoopInRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticAddressLoopInRequest.ProtoReflect.Descriptor instead. func (*StaticAddressLoopInRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{73} + return file_client_proto_rawDescGZIP(), []int{77} } func (x *StaticAddressLoopInRequest) GetOutpoints() []string { @@ -6229,7 +6444,7 @@ type StaticAddressLoopInResponse struct { func (x *StaticAddressLoopInResponse) Reset() { *x = StaticAddressLoopInResponse{} - mi := &file_client_proto_msgTypes[74] + mi := &file_client_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6241,7 +6456,7 @@ func (x *StaticAddressLoopInResponse) String() string { func (*StaticAddressLoopInResponse) ProtoMessage() {} func (x *StaticAddressLoopInResponse) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[74] + mi := &file_client_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6254,7 +6469,7 @@ func (x *StaticAddressLoopInResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticAddressLoopInResponse.ProtoReflect.Descriptor instead. func (*StaticAddressLoopInResponse) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{74} + return file_client_proto_rawDescGZIP(), []int{78} } func (x *StaticAddressLoopInResponse) GetSwapHash() []byte { @@ -6383,7 +6598,7 @@ type AssetLoopOutRequest struct { func (x *AssetLoopOutRequest) Reset() { *x = AssetLoopOutRequest{} - mi := &file_client_proto_msgTypes[75] + mi := &file_client_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6395,7 +6610,7 @@ func (x *AssetLoopOutRequest) String() string { func (*AssetLoopOutRequest) ProtoMessage() {} func (x *AssetLoopOutRequest) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[75] + mi := &file_client_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6408,7 +6623,7 @@ func (x *AssetLoopOutRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AssetLoopOutRequest.ProtoReflect.Descriptor instead. func (*AssetLoopOutRequest) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{75} + return file_client_proto_rawDescGZIP(), []int{79} } func (x *AssetLoopOutRequest) GetAssetId() []byte { @@ -6463,7 +6678,7 @@ type AssetRfqInfo struct { func (x *AssetRfqInfo) Reset() { *x = AssetRfqInfo{} - mi := &file_client_proto_msgTypes[76] + mi := &file_client_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6475,7 +6690,7 @@ func (x *AssetRfqInfo) String() string { func (*AssetRfqInfo) ProtoMessage() {} func (x *AssetRfqInfo) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[76] + mi := &file_client_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6488,7 +6703,7 @@ func (x *AssetRfqInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AssetRfqInfo.ProtoReflect.Descriptor instead. func (*AssetRfqInfo) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{76} + return file_client_proto_rawDescGZIP(), []int{80} } func (x *AssetRfqInfo) GetPrepayRfqId() []byte { @@ -6577,7 +6792,7 @@ type FixedPoint struct { func (x *FixedPoint) Reset() { *x = FixedPoint{} - mi := &file_client_proto_msgTypes[77] + mi := &file_client_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6589,7 +6804,7 @@ func (x *FixedPoint) String() string { func (*FixedPoint) ProtoMessage() {} func (x *FixedPoint) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[77] + mi := &file_client_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6602,7 +6817,7 @@ func (x *FixedPoint) ProtoReflect() protoreflect.Message { // Deprecated: Use FixedPoint.ProtoReflect.Descriptor instead. func (*FixedPoint) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{77} + return file_client_proto_rawDescGZIP(), []int{81} } func (x *FixedPoint) GetCoefficient() string { @@ -6633,7 +6848,7 @@ type AssetLoopOutInfo struct { func (x *AssetLoopOutInfo) Reset() { *x = AssetLoopOutInfo{} - mi := &file_client_proto_msgTypes[78] + mi := &file_client_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6645,7 +6860,7 @@ func (x *AssetLoopOutInfo) String() string { func (*AssetLoopOutInfo) ProtoMessage() {} func (x *AssetLoopOutInfo) ProtoReflect() protoreflect.Message { - mi := &file_client_proto_msgTypes[78] + mi := &file_client_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6658,7 +6873,7 @@ func (x *AssetLoopOutInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AssetLoopOutInfo.ProtoReflect.Descriptor instead. func (*AssetLoopOutInfo) Descriptor() ([]byte, []int) { - return file_client_proto_rawDescGZIP(), []int{78} + return file_client_proto_rawDescGZIP(), []int{82} } func (x *AssetLoopOutInfo) GetAssetId() string { @@ -6964,11 +7179,24 @@ const file_client_proto_rawDesc = "" + "\x06amount\x18\x03 \x01(\x04R\x06amount\x12\x13\n" + "\x05tx_id\x18\x04 \x01(\tR\x04txId\x12\x12\n" + "\x04vout\x18\x05 \x01(\rR\x04vout\x12\x16\n" + - "\x06expiry\x18\x06 \x01(\rR\x06expiry\"\x85\x01\n" + + "\x06expiry\x18\x06 \x01(\rR\x06expiry\"k\n" + + "\x19ReservationRequestRequest\x12\x10\n" + + "\x03amt\x18\x01 \x01(\x04R\x03amt\x12\x16\n" + + "\x06expiry\x18\x02 \x01(\rR\x06expiry\x12$\n" + + "\x0emax_prepay_amt\x18\x03 \x01(\x04R\fmaxPrepayAmt\"Z\n" + + "\x1aReservationRequestResponse\x12<\n" + + "\vreservation\x18\x01 \x01(\v2\x1a.looprpc.ClientReservationR\vreservation\"C\n" + + "\x17ReservationQuoteRequest\x12\x10\n" + + "\x03amt\x18\x01 \x01(\x04R\x03amt\x12\x16\n" + + "\x06expiry\x18\x02 \x01(\rR\x06expiry\"9\n" + + "\x18ReservationQuoteResponse\x12\x1d\n" + + "\n" + + "prepay_amt\x18\x01 \x01(\x04R\tprepayAmt\"\xae\x01\n" + "\x11InstantOutRequest\x12'\n" + "\x0freservation_ids\x18\x01 \x03(\fR\x0ereservationIds\x12*\n" + "\x11outgoing_chan_set\x18\x02 \x03(\x04R\x0foutgoingChanSet\x12\x1b\n" + - "\tdest_addr\x18\x03 \x01(\tR\bdestAddr\"t\n" + + "\tdest_addr\x18\x03 \x01(\tR\bdestAddr\x12'\n" + + "\x10max_swap_fee_sat\x18\x04 \x01(\x03R\rmaxSwapFeeSat\"t\n" + "\x12InstantOutResponse\x12(\n" + "\x10instant_out_hash\x18\x01 \x01(\fR\x0einstantOutHash\x12\x1e\n" + "\vsweep_tx_id\x18\x02 \x01(\tR\tsweepTxId\x12\x14\n" + @@ -7203,7 +7431,7 @@ const file_client_proto_rawDesc = "" + "\x1eSUCCEEDED_TRANSITIONING_FAILED\x10\t\x12\x13\n" + "\x0fUNLOCK_DEPOSITS\x10\n" + "\x12\x1e\n" + - "\x1aFAILED_STATIC_ADDRESS_SWAP\x10\v2\xca\x14\n" + + "\x1aFAILED_STATIC_ADDRESS_SWAP\x10\v2\x82\x16\n" + "\n" + "SwapClient\x129\n" + "\aLoopOut\x12\x17.looprpc.LoopOutRequest\x1a\x15.looprpc.SwapResponse\x127\n" + @@ -7227,7 +7455,9 @@ const file_client_proto_rawDesc = "" + "\x12GetLiquidityParams\x12\".looprpc.GetLiquidityParamsRequest\x1a\x1c.looprpc.LiquidityParameters\x12]\n" + "\x12SetLiquidityParams\x12\".looprpc.SetLiquidityParamsRequest\x1a#.looprpc.SetLiquidityParamsResponse\x12K\n" + "\fSuggestSwaps\x12\x1c.looprpc.SuggestSwapsRequest\x1a\x1d.looprpc.SuggestSwapsResponse\x12W\n" + - "\x10ListReservations\x12 .looprpc.ListReservationsRequest\x1a!.looprpc.ListReservationsResponse\x12E\n" + + "\x10ListReservations\x12 .looprpc.ListReservationsRequest\x1a!.looprpc.ListReservationsResponse\x12]\n" + + "\x12ReservationRequest\x12\".looprpc.ReservationRequestRequest\x1a#.looprpc.ReservationRequestResponse\x12W\n" + + "\x10ReservationQuote\x12 .looprpc.ReservationQuoteRequest\x1a!.looprpc.ReservationQuoteResponse\x12E\n" + "\n" + "InstantOut\x12\x1a.looprpc.InstantOutRequest\x1a\x1b.looprpc.InstantOutResponse\x12T\n" + "\x0fInstantOutQuote\x12\x1f.looprpc.InstantOutQuoteRequest\x1a .looprpc.InstantOutQuoteResponse\x12T\n" + @@ -7255,7 +7485,7 @@ func file_client_proto_rawDescGZIP() []byte { } var file_client_proto_enumTypes = make([]protoimpl.EnumInfo, 10) -var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 80) +var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 84) var file_client_proto_goTypes = []any{ (AddressType)(0), // 0: looprpc.AddressType (SwapType)(0), // 1: looprpc.SwapType @@ -7315,69 +7545,73 @@ var file_client_proto_goTypes = []any{ (*ListReservationsRequest)(nil), // 55: looprpc.ListReservationsRequest (*ListReservationsResponse)(nil), // 56: looprpc.ListReservationsResponse (*ClientReservation)(nil), // 57: looprpc.ClientReservation - (*InstantOutRequest)(nil), // 58: looprpc.InstantOutRequest - (*InstantOutResponse)(nil), // 59: looprpc.InstantOutResponse - (*InstantOutQuoteRequest)(nil), // 60: looprpc.InstantOutQuoteRequest - (*InstantOutQuoteResponse)(nil), // 61: looprpc.InstantOutQuoteResponse - (*ListInstantOutsRequest)(nil), // 62: looprpc.ListInstantOutsRequest - (*ListInstantOutsResponse)(nil), // 63: looprpc.ListInstantOutsResponse - (*InstantOut)(nil), // 64: looprpc.InstantOut - (*NewStaticAddressRequest)(nil), // 65: looprpc.NewStaticAddressRequest - (*NewStaticAddressResponse)(nil), // 66: looprpc.NewStaticAddressResponse - (*ListUnspentDepositsRequest)(nil), // 67: looprpc.ListUnspentDepositsRequest - (*ListUnspentDepositsResponse)(nil), // 68: looprpc.ListUnspentDepositsResponse - (*Utxo)(nil), // 69: looprpc.Utxo - (*WithdrawDepositsRequest)(nil), // 70: looprpc.WithdrawDepositsRequest - (*WithdrawDepositsResponse)(nil), // 71: looprpc.WithdrawDepositsResponse - (*ListStaticAddressDepositsRequest)(nil), // 72: looprpc.ListStaticAddressDepositsRequest - (*ListStaticAddressDepositsResponse)(nil), // 73: looprpc.ListStaticAddressDepositsResponse - (*ListStaticAddressWithdrawalRequest)(nil), // 74: looprpc.ListStaticAddressWithdrawalRequest - (*ListStaticAddressWithdrawalResponse)(nil), // 75: looprpc.ListStaticAddressWithdrawalResponse - (*ListStaticAddressSwapsRequest)(nil), // 76: looprpc.ListStaticAddressSwapsRequest - (*ListStaticAddressSwapsResponse)(nil), // 77: looprpc.ListStaticAddressSwapsResponse - (*StaticAddressSummaryRequest)(nil), // 78: looprpc.StaticAddressSummaryRequest - (*StaticAddressSummaryResponse)(nil), // 79: looprpc.StaticAddressSummaryResponse - (*Deposit)(nil), // 80: looprpc.Deposit - (*StaticAddressWithdrawal)(nil), // 81: looprpc.StaticAddressWithdrawal - (*StaticAddressLoopInSwap)(nil), // 82: looprpc.StaticAddressLoopInSwap - (*StaticAddressLoopInRequest)(nil), // 83: looprpc.StaticAddressLoopInRequest - (*StaticAddressLoopInResponse)(nil), // 84: looprpc.StaticAddressLoopInResponse - (*AssetLoopOutRequest)(nil), // 85: looprpc.AssetLoopOutRequest - (*AssetRfqInfo)(nil), // 86: looprpc.AssetRfqInfo - (*FixedPoint)(nil), // 87: looprpc.FixedPoint - (*AssetLoopOutInfo)(nil), // 88: looprpc.AssetLoopOutInfo - nil, // 89: looprpc.LiquidityParameters.EasyAssetParamsEntry - (*lnrpc.OpenChannelRequest)(nil), // 90: lnrpc.OpenChannelRequest - (*swapserverrpc.RouteHint)(nil), // 91: looprpc.RouteHint - (*lnrpc.OutPoint)(nil), // 92: lnrpc.OutPoint + (*ReservationRequestRequest)(nil), // 58: looprpc.ReservationRequestRequest + (*ReservationRequestResponse)(nil), // 59: looprpc.ReservationRequestResponse + (*ReservationQuoteRequest)(nil), // 60: looprpc.ReservationQuoteRequest + (*ReservationQuoteResponse)(nil), // 61: looprpc.ReservationQuoteResponse + (*InstantOutRequest)(nil), // 62: looprpc.InstantOutRequest + (*InstantOutResponse)(nil), // 63: looprpc.InstantOutResponse + (*InstantOutQuoteRequest)(nil), // 64: looprpc.InstantOutQuoteRequest + (*InstantOutQuoteResponse)(nil), // 65: looprpc.InstantOutQuoteResponse + (*ListInstantOutsRequest)(nil), // 66: looprpc.ListInstantOutsRequest + (*ListInstantOutsResponse)(nil), // 67: looprpc.ListInstantOutsResponse + (*InstantOut)(nil), // 68: looprpc.InstantOut + (*NewStaticAddressRequest)(nil), // 69: looprpc.NewStaticAddressRequest + (*NewStaticAddressResponse)(nil), // 70: looprpc.NewStaticAddressResponse + (*ListUnspentDepositsRequest)(nil), // 71: looprpc.ListUnspentDepositsRequest + (*ListUnspentDepositsResponse)(nil), // 72: looprpc.ListUnspentDepositsResponse + (*Utxo)(nil), // 73: looprpc.Utxo + (*WithdrawDepositsRequest)(nil), // 74: looprpc.WithdrawDepositsRequest + (*WithdrawDepositsResponse)(nil), // 75: looprpc.WithdrawDepositsResponse + (*ListStaticAddressDepositsRequest)(nil), // 76: looprpc.ListStaticAddressDepositsRequest + (*ListStaticAddressDepositsResponse)(nil), // 77: looprpc.ListStaticAddressDepositsResponse + (*ListStaticAddressWithdrawalRequest)(nil), // 78: looprpc.ListStaticAddressWithdrawalRequest + (*ListStaticAddressWithdrawalResponse)(nil), // 79: looprpc.ListStaticAddressWithdrawalResponse + (*ListStaticAddressSwapsRequest)(nil), // 80: looprpc.ListStaticAddressSwapsRequest + (*ListStaticAddressSwapsResponse)(nil), // 81: looprpc.ListStaticAddressSwapsResponse + (*StaticAddressSummaryRequest)(nil), // 82: looprpc.StaticAddressSummaryRequest + (*StaticAddressSummaryResponse)(nil), // 83: looprpc.StaticAddressSummaryResponse + (*Deposit)(nil), // 84: looprpc.Deposit + (*StaticAddressWithdrawal)(nil), // 85: looprpc.StaticAddressWithdrawal + (*StaticAddressLoopInSwap)(nil), // 86: looprpc.StaticAddressLoopInSwap + (*StaticAddressLoopInRequest)(nil), // 87: looprpc.StaticAddressLoopInRequest + (*StaticAddressLoopInResponse)(nil), // 88: looprpc.StaticAddressLoopInResponse + (*AssetLoopOutRequest)(nil), // 89: looprpc.AssetLoopOutRequest + (*AssetRfqInfo)(nil), // 90: looprpc.AssetRfqInfo + (*FixedPoint)(nil), // 91: looprpc.FixedPoint + (*AssetLoopOutInfo)(nil), // 92: looprpc.AssetLoopOutInfo + nil, // 93: looprpc.LiquidityParameters.EasyAssetParamsEntry + (*lnrpc.OpenChannelRequest)(nil), // 94: lnrpc.OpenChannelRequest + (*swapserverrpc.RouteHint)(nil), // 95: looprpc.RouteHint + (*lnrpc.OutPoint)(nil), // 96: lnrpc.OutPoint } var file_client_proto_depIdxs = []int32{ - 90, // 0: looprpc.StaticOpenChannelRequest.open_channel_request:type_name -> lnrpc.OpenChannelRequest + 94, // 0: looprpc.StaticOpenChannelRequest.open_channel_request:type_name -> lnrpc.OpenChannelRequest 0, // 1: looprpc.LoopOutRequest.account_addr_type:type_name -> looprpc.AddressType - 85, // 2: looprpc.LoopOutRequest.asset_info:type_name -> looprpc.AssetLoopOutRequest - 86, // 3: looprpc.LoopOutRequest.asset_rfq_info:type_name -> looprpc.AssetRfqInfo - 91, // 4: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint + 89, // 2: looprpc.LoopOutRequest.asset_info:type_name -> looprpc.AssetLoopOutRequest + 90, // 3: looprpc.LoopOutRequest.asset_rfq_info:type_name -> looprpc.AssetRfqInfo + 95, // 4: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint 1, // 5: looprpc.SwapStatus.type:type_name -> looprpc.SwapType 2, // 6: looprpc.SwapStatus.state:type_name -> looprpc.SwapState 8, // 7: looprpc.SwapStatus.static_loop_in_state:type_name -> looprpc.StaticAddressLoopInSwapState 3, // 8: looprpc.SwapStatus.failure_reason:type_name -> looprpc.FailureReason - 88, // 9: looprpc.SwapStatus.asset_info:type_name -> looprpc.AssetLoopOutInfo + 92, // 9: looprpc.SwapStatus.asset_info:type_name -> looprpc.AssetLoopOutInfo 20, // 10: looprpc.ListSwapsRequest.list_swap_filter:type_name -> looprpc.ListSwapsFilter 9, // 11: looprpc.ListSwapsFilter.swap_type:type_name -> looprpc.ListSwapsFilter.SwapTypeFilter 18, // 12: looprpc.ListSwapsResponse.swaps:type_name -> looprpc.SwapStatus 24, // 13: looprpc.SweepHtlcResponse.not_requested:type_name -> looprpc.PublishNotRequested 25, // 14: looprpc.SweepHtlcResponse.published:type_name -> looprpc.PublishSucceeded 26, // 15: looprpc.SweepHtlcResponse.failed:type_name -> looprpc.PublishFailed - 91, // 16: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint - 85, // 17: looprpc.QuoteRequest.asset_info:type_name -> looprpc.AssetLoopOutRequest - 86, // 18: looprpc.OutQuoteResponse.asset_rfq_info:type_name -> looprpc.AssetRfqInfo - 91, // 19: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint + 95, // 16: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint + 89, // 17: looprpc.QuoteRequest.asset_info:type_name -> looprpc.AssetLoopOutRequest + 90, // 18: looprpc.OutQuoteResponse.asset_rfq_info:type_name -> looprpc.AssetRfqInfo + 95, // 19: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint 40, // 20: looprpc.TokensResponse.tokens:type_name -> looprpc.L402Token 41, // 21: looprpc.GetInfoResponse.loop_out_stats:type_name -> looprpc.LoopStats 41, // 22: looprpc.GetInfoResponse.loop_in_stats:type_name -> looprpc.LoopStats 47, // 23: looprpc.LiquidityParameters.rules:type_name -> looprpc.LiquidityRule 0, // 24: looprpc.LiquidityParameters.account_addr_type:type_name -> looprpc.AddressType - 89, // 25: looprpc.LiquidityParameters.easy_asset_params:type_name -> looprpc.LiquidityParameters.EasyAssetParamsEntry + 93, // 25: looprpc.LiquidityParameters.easy_asset_params:type_name -> looprpc.LiquidityParameters.EasyAssetParamsEntry 4, // 26: looprpc.LiquidityParameters.loop_in_source:type_name -> looprpc.LoopInSource 1, // 27: looprpc.LiquidityRule.swap_type:type_name -> looprpc.SwapType 5, // 28: looprpc.LiquidityRule.type:type_name -> looprpc.LiquidityRuleType @@ -7385,96 +7619,101 @@ var file_client_proto_depIdxs = []int32{ 6, // 30: looprpc.Disqualified.reason:type_name -> looprpc.AutoReason 14, // 31: looprpc.SuggestSwapsResponse.loop_out:type_name -> looprpc.LoopOutRequest 15, // 32: looprpc.SuggestSwapsResponse.loop_in:type_name -> looprpc.LoopInRequest - 83, // 33: looprpc.SuggestSwapsResponse.static_loop_in:type_name -> looprpc.StaticAddressLoopInRequest + 87, // 33: looprpc.SuggestSwapsResponse.static_loop_in:type_name -> looprpc.StaticAddressLoopInRequest 51, // 34: looprpc.SuggestSwapsResponse.disqualified:type_name -> looprpc.Disqualified 57, // 35: looprpc.ListReservationsResponse.reservations:type_name -> looprpc.ClientReservation - 64, // 36: looprpc.ListInstantOutsResponse.swaps:type_name -> looprpc.InstantOut - 69, // 37: looprpc.ListUnspentDepositsResponse.utxos:type_name -> looprpc.Utxo - 92, // 38: looprpc.WithdrawDepositsRequest.outpoints:type_name -> lnrpc.OutPoint - 7, // 39: looprpc.ListStaticAddressDepositsRequest.state_filter:type_name -> looprpc.DepositState - 80, // 40: looprpc.ListStaticAddressDepositsResponse.filtered_deposits:type_name -> looprpc.Deposit - 81, // 41: looprpc.ListStaticAddressWithdrawalResponse.withdrawals:type_name -> looprpc.StaticAddressWithdrawal - 82, // 42: looprpc.ListStaticAddressSwapsResponse.swaps:type_name -> looprpc.StaticAddressLoopInSwap - 7, // 43: looprpc.Deposit.state:type_name -> looprpc.DepositState - 80, // 44: looprpc.StaticAddressWithdrawal.deposits:type_name -> looprpc.Deposit - 8, // 45: looprpc.StaticAddressLoopInSwap.state:type_name -> looprpc.StaticAddressLoopInSwapState - 80, // 46: looprpc.StaticAddressLoopInSwap.deposits:type_name -> looprpc.Deposit - 91, // 47: looprpc.StaticAddressLoopInRequest.route_hints:type_name -> looprpc.RouteHint - 80, // 48: looprpc.StaticAddressLoopInResponse.used_deposits:type_name -> looprpc.Deposit - 87, // 49: looprpc.AssetRfqInfo.prepay_asset_rate:type_name -> looprpc.FixedPoint - 87, // 50: looprpc.AssetRfqInfo.swap_asset_rate:type_name -> looprpc.FixedPoint - 46, // 51: looprpc.LiquidityParameters.EasyAssetParamsEntry.value:type_name -> looprpc.EasyAssetAutoloopParams - 14, // 52: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest - 15, // 53: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest - 17, // 54: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest - 19, // 55: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest - 22, // 56: looprpc.SwapClient.SweepHtlc:input_type -> looprpc.SweepHtlcRequest - 27, // 57: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest - 53, // 58: looprpc.SwapClient.AbandonSwap:input_type -> looprpc.AbandonSwapRequest - 28, // 59: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest - 31, // 60: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest - 28, // 61: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest - 31, // 62: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest - 34, // 63: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest - 36, // 64: looprpc.SwapClient.GetL402Tokens:input_type -> looprpc.TokensRequest - 36, // 65: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest - 38, // 66: looprpc.SwapClient.FetchL402Token:input_type -> looprpc.FetchL402TokenRequest - 42, // 67: looprpc.SwapClient.GetInfo:input_type -> looprpc.GetInfoRequest - 12, // 68: looprpc.SwapClient.StopDaemon:input_type -> looprpc.StopDaemonRequest - 44, // 69: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest - 48, // 70: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest - 50, // 71: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest - 55, // 72: looprpc.SwapClient.ListReservations:input_type -> looprpc.ListReservationsRequest - 58, // 73: looprpc.SwapClient.InstantOut:input_type -> looprpc.InstantOutRequest - 60, // 74: looprpc.SwapClient.InstantOutQuote:input_type -> looprpc.InstantOutQuoteRequest - 62, // 75: looprpc.SwapClient.ListInstantOuts:input_type -> looprpc.ListInstantOutsRequest - 65, // 76: looprpc.SwapClient.NewStaticAddress:input_type -> looprpc.NewStaticAddressRequest - 67, // 77: looprpc.SwapClient.ListUnspentDeposits:input_type -> looprpc.ListUnspentDepositsRequest - 70, // 78: looprpc.SwapClient.WithdrawDeposits:input_type -> looprpc.WithdrawDepositsRequest - 72, // 79: looprpc.SwapClient.ListStaticAddressDeposits:input_type -> looprpc.ListStaticAddressDepositsRequest - 74, // 80: looprpc.SwapClient.ListStaticAddressWithdrawals:input_type -> looprpc.ListStaticAddressWithdrawalRequest - 76, // 81: looprpc.SwapClient.ListStaticAddressSwaps:input_type -> looprpc.ListStaticAddressSwapsRequest - 78, // 82: looprpc.SwapClient.GetStaticAddressSummary:input_type -> looprpc.StaticAddressSummaryRequest - 83, // 83: looprpc.SwapClient.StaticAddressLoopIn:input_type -> looprpc.StaticAddressLoopInRequest - 10, // 84: looprpc.SwapClient.StaticOpenChannel:input_type -> looprpc.StaticOpenChannelRequest - 16, // 85: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse - 16, // 86: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse - 18, // 87: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus - 21, // 88: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse - 23, // 89: looprpc.SwapClient.SweepHtlc:output_type -> looprpc.SweepHtlcResponse - 18, // 90: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus - 54, // 91: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse - 30, // 92: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse - 33, // 93: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse - 29, // 94: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse - 32, // 95: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse - 35, // 96: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse - 37, // 97: looprpc.SwapClient.GetL402Tokens:output_type -> looprpc.TokensResponse - 37, // 98: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse - 39, // 99: looprpc.SwapClient.FetchL402Token:output_type -> looprpc.FetchL402TokenResponse - 43, // 100: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse - 13, // 101: looprpc.SwapClient.StopDaemon:output_type -> looprpc.StopDaemonResponse - 45, // 102: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters - 49, // 103: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse - 52, // 104: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse - 56, // 105: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse - 59, // 106: looprpc.SwapClient.InstantOut:output_type -> looprpc.InstantOutResponse - 61, // 107: looprpc.SwapClient.InstantOutQuote:output_type -> looprpc.InstantOutQuoteResponse - 63, // 108: looprpc.SwapClient.ListInstantOuts:output_type -> looprpc.ListInstantOutsResponse - 66, // 109: looprpc.SwapClient.NewStaticAddress:output_type -> looprpc.NewStaticAddressResponse - 68, // 110: looprpc.SwapClient.ListUnspentDeposits:output_type -> looprpc.ListUnspentDepositsResponse - 71, // 111: looprpc.SwapClient.WithdrawDeposits:output_type -> looprpc.WithdrawDepositsResponse - 73, // 112: looprpc.SwapClient.ListStaticAddressDeposits:output_type -> looprpc.ListStaticAddressDepositsResponse - 75, // 113: looprpc.SwapClient.ListStaticAddressWithdrawals:output_type -> looprpc.ListStaticAddressWithdrawalResponse - 77, // 114: looprpc.SwapClient.ListStaticAddressSwaps:output_type -> looprpc.ListStaticAddressSwapsResponse - 79, // 115: looprpc.SwapClient.GetStaticAddressSummary:output_type -> looprpc.StaticAddressSummaryResponse - 84, // 116: looprpc.SwapClient.StaticAddressLoopIn:output_type -> looprpc.StaticAddressLoopInResponse - 11, // 117: looprpc.SwapClient.StaticOpenChannel:output_type -> looprpc.StaticOpenChannelResponse - 85, // [85:118] is the sub-list for method output_type - 52, // [52:85] is the sub-list for method input_type - 52, // [52:52] is the sub-list for extension type_name - 52, // [52:52] is the sub-list for extension extendee - 0, // [0:52] is the sub-list for field type_name + 57, // 36: looprpc.ReservationRequestResponse.reservation:type_name -> looprpc.ClientReservation + 68, // 37: looprpc.ListInstantOutsResponse.swaps:type_name -> looprpc.InstantOut + 73, // 38: looprpc.ListUnspentDepositsResponse.utxos:type_name -> looprpc.Utxo + 96, // 39: looprpc.WithdrawDepositsRequest.outpoints:type_name -> lnrpc.OutPoint + 7, // 40: looprpc.ListStaticAddressDepositsRequest.state_filter:type_name -> looprpc.DepositState + 84, // 41: looprpc.ListStaticAddressDepositsResponse.filtered_deposits:type_name -> looprpc.Deposit + 85, // 42: looprpc.ListStaticAddressWithdrawalResponse.withdrawals:type_name -> looprpc.StaticAddressWithdrawal + 86, // 43: looprpc.ListStaticAddressSwapsResponse.swaps:type_name -> looprpc.StaticAddressLoopInSwap + 7, // 44: looprpc.Deposit.state:type_name -> looprpc.DepositState + 84, // 45: looprpc.StaticAddressWithdrawal.deposits:type_name -> looprpc.Deposit + 8, // 46: looprpc.StaticAddressLoopInSwap.state:type_name -> looprpc.StaticAddressLoopInSwapState + 84, // 47: looprpc.StaticAddressLoopInSwap.deposits:type_name -> looprpc.Deposit + 95, // 48: looprpc.StaticAddressLoopInRequest.route_hints:type_name -> looprpc.RouteHint + 84, // 49: looprpc.StaticAddressLoopInResponse.used_deposits:type_name -> looprpc.Deposit + 91, // 50: looprpc.AssetRfqInfo.prepay_asset_rate:type_name -> looprpc.FixedPoint + 91, // 51: looprpc.AssetRfqInfo.swap_asset_rate:type_name -> looprpc.FixedPoint + 46, // 52: looprpc.LiquidityParameters.EasyAssetParamsEntry.value:type_name -> looprpc.EasyAssetAutoloopParams + 14, // 53: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest + 15, // 54: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest + 17, // 55: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest + 19, // 56: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest + 22, // 57: looprpc.SwapClient.SweepHtlc:input_type -> looprpc.SweepHtlcRequest + 27, // 58: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest + 53, // 59: looprpc.SwapClient.AbandonSwap:input_type -> looprpc.AbandonSwapRequest + 28, // 60: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest + 31, // 61: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest + 28, // 62: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest + 31, // 63: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest + 34, // 64: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest + 36, // 65: looprpc.SwapClient.GetL402Tokens:input_type -> looprpc.TokensRequest + 36, // 66: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest + 38, // 67: looprpc.SwapClient.FetchL402Token:input_type -> looprpc.FetchL402TokenRequest + 42, // 68: looprpc.SwapClient.GetInfo:input_type -> looprpc.GetInfoRequest + 12, // 69: looprpc.SwapClient.StopDaemon:input_type -> looprpc.StopDaemonRequest + 44, // 70: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest + 48, // 71: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest + 50, // 72: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest + 55, // 73: looprpc.SwapClient.ListReservations:input_type -> looprpc.ListReservationsRequest + 58, // 74: looprpc.SwapClient.ReservationRequest:input_type -> looprpc.ReservationRequestRequest + 60, // 75: looprpc.SwapClient.ReservationQuote:input_type -> looprpc.ReservationQuoteRequest + 62, // 76: looprpc.SwapClient.InstantOut:input_type -> looprpc.InstantOutRequest + 64, // 77: looprpc.SwapClient.InstantOutQuote:input_type -> looprpc.InstantOutQuoteRequest + 66, // 78: looprpc.SwapClient.ListInstantOuts:input_type -> looprpc.ListInstantOutsRequest + 69, // 79: looprpc.SwapClient.NewStaticAddress:input_type -> looprpc.NewStaticAddressRequest + 71, // 80: looprpc.SwapClient.ListUnspentDeposits:input_type -> looprpc.ListUnspentDepositsRequest + 74, // 81: looprpc.SwapClient.WithdrawDeposits:input_type -> looprpc.WithdrawDepositsRequest + 76, // 82: looprpc.SwapClient.ListStaticAddressDeposits:input_type -> looprpc.ListStaticAddressDepositsRequest + 78, // 83: looprpc.SwapClient.ListStaticAddressWithdrawals:input_type -> looprpc.ListStaticAddressWithdrawalRequest + 80, // 84: looprpc.SwapClient.ListStaticAddressSwaps:input_type -> looprpc.ListStaticAddressSwapsRequest + 82, // 85: looprpc.SwapClient.GetStaticAddressSummary:input_type -> looprpc.StaticAddressSummaryRequest + 87, // 86: looprpc.SwapClient.StaticAddressLoopIn:input_type -> looprpc.StaticAddressLoopInRequest + 10, // 87: looprpc.SwapClient.StaticOpenChannel:input_type -> looprpc.StaticOpenChannelRequest + 16, // 88: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse + 16, // 89: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse + 18, // 90: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus + 21, // 91: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse + 23, // 92: looprpc.SwapClient.SweepHtlc:output_type -> looprpc.SweepHtlcResponse + 18, // 93: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus + 54, // 94: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse + 30, // 95: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse + 33, // 96: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse + 29, // 97: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse + 32, // 98: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse + 35, // 99: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse + 37, // 100: looprpc.SwapClient.GetL402Tokens:output_type -> looprpc.TokensResponse + 37, // 101: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse + 39, // 102: looprpc.SwapClient.FetchL402Token:output_type -> looprpc.FetchL402TokenResponse + 43, // 103: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse + 13, // 104: looprpc.SwapClient.StopDaemon:output_type -> looprpc.StopDaemonResponse + 45, // 105: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters + 49, // 106: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse + 52, // 107: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse + 56, // 108: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse + 59, // 109: looprpc.SwapClient.ReservationRequest:output_type -> looprpc.ReservationRequestResponse + 61, // 110: looprpc.SwapClient.ReservationQuote:output_type -> looprpc.ReservationQuoteResponse + 63, // 111: looprpc.SwapClient.InstantOut:output_type -> looprpc.InstantOutResponse + 65, // 112: looprpc.SwapClient.InstantOutQuote:output_type -> looprpc.InstantOutQuoteResponse + 67, // 113: looprpc.SwapClient.ListInstantOuts:output_type -> looprpc.ListInstantOutsResponse + 70, // 114: looprpc.SwapClient.NewStaticAddress:output_type -> looprpc.NewStaticAddressResponse + 72, // 115: looprpc.SwapClient.ListUnspentDeposits:output_type -> looprpc.ListUnspentDepositsResponse + 75, // 116: looprpc.SwapClient.WithdrawDeposits:output_type -> looprpc.WithdrawDepositsResponse + 77, // 117: looprpc.SwapClient.ListStaticAddressDeposits:output_type -> looprpc.ListStaticAddressDepositsResponse + 79, // 118: looprpc.SwapClient.ListStaticAddressWithdrawals:output_type -> looprpc.ListStaticAddressWithdrawalResponse + 81, // 119: looprpc.SwapClient.ListStaticAddressSwaps:output_type -> looprpc.ListStaticAddressSwapsResponse + 83, // 120: looprpc.SwapClient.GetStaticAddressSummary:output_type -> looprpc.StaticAddressSummaryResponse + 88, // 121: looprpc.SwapClient.StaticAddressLoopIn:output_type -> looprpc.StaticAddressLoopInResponse + 11, // 122: looprpc.SwapClient.StaticOpenChannel:output_type -> looprpc.StaticOpenChannelResponse + 88, // [88:123] is the sub-list for method output_type + 53, // [53:88] is the sub-list for method input_type + 53, // [53:53] is the sub-list for extension type_name + 53, // [53:53] is the sub-list for extension extendee + 0, // [0:53] is the sub-list for field type_name } func init() { file_client_proto_init() } @@ -7496,7 +7735,7 @@ func file_client_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_client_proto_rawDesc), len(file_client_proto_rawDesc)), NumEnums: 10, - NumMessages: 80, + NumMessages: 84, NumExtensions: 0, NumServices: 1, }, diff --git a/looprpc/client.proto b/looprpc/client.proto index 844dade7b..3e80b136e 100644 --- a/looprpc/client.proto +++ b/looprpc/client.proto @@ -137,12 +137,25 @@ service SwapClient { */ rpc SuggestSwaps (SuggestSwapsRequest) returns (SuggestSwapsResponse); - /* loop: `listreservations` + /* loop: `reservations list` ListReservations returns a list of all reservations the server opened to us. */ rpc ListReservations (ListReservationsRequest) returns (ListReservationsResponse); + /* loop:`reservation request` +ReservationRequest requests a reservation from the server. +*/ + rpc ReservationRequest (ReservationRequestRequest) + returns (ReservationRequestResponse); + + /* loop:`reservation quote` + ReservationQuote returns a quote for a reservation with the provided + parameters. + */ + rpc ReservationQuote (ReservationQuoteRequest) + returns (ReservationQuoteResponse); + /* loop: `instantout` InstantOut initiates an instant out swap with the given parameters. */ @@ -1667,6 +1680,46 @@ message ClientReservation { uint32 expiry = 6; } +message ReservationRequestRequest { + /* + The amount to reserve in satoshis. + */ + uint64 amt = 1; + + /* + The relative expiry of the reservation in blocks. + */ + uint32 expiry = 2; + + /* + The maximum amt in satoshis we allow for the prepayment. + */ + uint64 max_prepay_amt = 3; +} + +message ReservationRequestResponse { + ClientReservation reservation = 1; +} + +message ReservationQuoteRequest { + /* + The amount to reserve in satoshis. + */ + uint64 amt = 1; + + /* + The relative expiry of the reservation in blocks. + */ + uint32 expiry = 2; +} + +message ReservationQuoteResponse { + /* + The prepay fee that will be charged for the reservation. + */ + uint64 prepay_amt = 1; +} + message InstantOutRequest { /* The reservations to use for the swap. @@ -1685,6 +1738,11 @@ message InstantOutRequest { will be swept to the wallet's internal address. */ string dest_addr = 3; + + /* + The maximum off-chain swap fee that may be charged for the swap. + */ + int64 max_swap_fee_sat = 4; } message InstantOutResponse { diff --git a/looprpc/client.swagger.json b/looprpc/client.swagger.json index 1c94febb9..ac902f363 100644 --- a/looprpc/client.swagger.json +++ b/looprpc/client.swagger.json @@ -130,7 +130,7 @@ }, "/v1/instantout/reservations": { "get": { - "summary": "loop: `listreservations`\nListReservations returns a list of all reservations the server opened to us.", + "summary": "loop: `reservations list`\nListReservations returns a list of all reservations the server opened to us.", "operationId": "SwapClient_ListReservations", "responses": { "200": { @@ -1917,6 +1917,11 @@ "dest_addr": { "type": "string", "description": "An optional address to sweep the onchain funds to. If not set, the funds\nwill be swept to the wallet's internal address." + }, + "max_swap_fee_sat": { + "type": "string", + "format": "int64", + "description": "The maximum off-chain swap fee that may be charged for the swap." } } }, @@ -2616,6 +2621,24 @@ "type": "object", "description": "PublishSucceeded is returned by SweepHtlc if publishing was requested in\nSweepHtlcRequest and it succeeded." }, + "looprpcReservationQuoteResponse": { + "type": "object", + "properties": { + "prepay_amt": { + "type": "string", + "format": "uint64", + "description": "The prepay fee that will be charged for the reservation." + } + } + }, + "looprpcReservationRequestResponse": { + "type": "object", + "properties": { + "reservation": { + "$ref": "#/definitions/looprpcClientReservation" + } + } + }, "looprpcRouteHint": { "type": "object", "properties": { diff --git a/looprpc/client_grpc.pb.go b/looprpc/client_grpc.pb.go index b03cc9e87..c91d40715 100644 --- a/looprpc/client_grpc.pb.go +++ b/looprpc/client_grpc.pb.go @@ -99,9 +99,16 @@ type SwapClientClient interface { // Note that only loop out suggestions are currently supported. // [EXPERIMENTAL]: endpoint is subject to change. SuggestSwaps(ctx context.Context, in *SuggestSwapsRequest, opts ...grpc.CallOption) (*SuggestSwapsResponse, error) - // loop: `listreservations` + // loop: `reservations list` // ListReservations returns a list of all reservations the server opened to us. ListReservations(ctx context.Context, in *ListReservationsRequest, opts ...grpc.CallOption) (*ListReservationsResponse, error) + // loop:`reservation request` + // ReservationRequest requests a reservation from the server. + ReservationRequest(ctx context.Context, in *ReservationRequestRequest, opts ...grpc.CallOption) (*ReservationRequestResponse, error) + // loop:`reservation quote` + // ReservationQuote returns a quote for a reservation with the provided + // parameters. + ReservationQuote(ctx context.Context, in *ReservationQuoteRequest, opts ...grpc.CallOption) (*ReservationQuoteResponse, error) // loop: `instantout` // InstantOut initiates an instant out swap with the given parameters. InstantOut(ctx context.Context, in *InstantOutRequest, opts ...grpc.CallOption) (*InstantOutResponse, error) @@ -366,6 +373,24 @@ func (c *swapClientClient) ListReservations(ctx context.Context, in *ListReserva return out, nil } +func (c *swapClientClient) ReservationRequest(ctx context.Context, in *ReservationRequestRequest, opts ...grpc.CallOption) (*ReservationRequestResponse, error) { + out := new(ReservationRequestResponse) + err := c.cc.Invoke(ctx, "/looprpc.SwapClient/ReservationRequest", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *swapClientClient) ReservationQuote(ctx context.Context, in *ReservationQuoteRequest, opts ...grpc.CallOption) (*ReservationQuoteResponse, error) { + out := new(ReservationQuoteResponse) + err := c.cc.Invoke(ctx, "/looprpc.SwapClient/ReservationQuote", in, out, opts...) + if err != nil { + return nil, err + } + 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...) @@ -559,9 +584,16 @@ type SwapClientServer interface { // Note that only loop out suggestions are currently supported. // [EXPERIMENTAL]: endpoint is subject to change. SuggestSwaps(context.Context, *SuggestSwapsRequest) (*SuggestSwapsResponse, error) - // loop: `listreservations` + // loop: `reservations list` // ListReservations returns a list of all reservations the server opened to us. ListReservations(context.Context, *ListReservationsRequest) (*ListReservationsResponse, error) + // loop:`reservation request` + // ReservationRequest requests a reservation from the server. + ReservationRequest(context.Context, *ReservationRequestRequest) (*ReservationRequestResponse, error) + // loop:`reservation quote` + // ReservationQuote returns a quote for a reservation with the provided + // parameters. + ReservationQuote(context.Context, *ReservationQuoteRequest) (*ReservationQuoteResponse, error) // loop: `instantout` // InstantOut initiates an instant out swap with the given parameters. InstantOut(context.Context, *InstantOutRequest) (*InstantOutResponse, error) @@ -674,6 +706,12 @@ 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) ReservationRequest(context.Context, *ReservationRequestRequest) (*ReservationRequestResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReservationRequest not implemented") +} +func (UnimplementedSwapClientServer) ReservationQuote(context.Context, *ReservationQuoteRequest) (*ReservationQuoteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReservationQuote not implemented") +} func (UnimplementedSwapClientServer) InstantOut(context.Context, *InstantOutRequest) (*InstantOutResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method InstantOut not implemented") } @@ -1104,6 +1142,42 @@ func _SwapClient_ListReservations_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _SwapClient_ReservationRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReservationRequestRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SwapClientServer).ReservationRequest(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.SwapClient/ReservationRequest", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SwapClientServer).ReservationRequest(ctx, req.(*ReservationRequestRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SwapClient_ReservationQuote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReservationQuoteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SwapClientServer).ReservationQuote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.SwapClient/ReservationQuote", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SwapClientServer).ReservationQuote(ctx, req.(*ReservationQuoteRequest)) + } + 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 { @@ -1407,6 +1481,14 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListReservations", Handler: _SwapClient_ListReservations_Handler, }, + { + MethodName: "ReservationRequest", + Handler: _SwapClient_ReservationRequest_Handler, + }, + { + MethodName: "ReservationQuote", + Handler: _SwapClient_ReservationQuote_Handler, + }, { MethodName: "InstantOut", Handler: _SwapClient_InstantOut_Handler, diff --git a/looprpc/perms.go b/looprpc/perms.go index d646f6671..eb7936872 100644 --- a/looprpc/perms.go +++ b/looprpc/perms.go @@ -177,18 +177,41 @@ var RequiredPermissions = map[string][]bakery.Op{ "/looprpc.SwapClient/ListReservations": {{ Entity: "swap", Action: "read", + }, { + Entity: "loop", + Action: "out", + }}, + "/looprpc.SwapClient/ReservationRequest": {{ + Entity: "swap", + Action: "execute", + }, { + Entity: "loop", + Action: "out", + }}, + "/looprpc.SwapClient/ReservationQuote": {{ + Entity: "swap", + Action: "read", }}, "/looprpc.SwapClient/InstantOut": {{ Entity: "swap", Action: "execute", + }, { + Entity: "loop", + Action: "out", }}, "/looprpc.SwapClient/InstantOutQuote": {{ Entity: "swap", Action: "read", + }, { + Entity: "loop", + Action: "out", }}, "/looprpc.SwapClient/ListInstantOuts": {{ Entity: "swap", Action: "read", + }, { + Entity: "loop", + Action: "out", }}, "/looprpc.SwapClient/StopDaemon": {{ Entity: "loop", diff --git a/looprpc/swapclient.pb.json.go b/looprpc/swapclient.pb.json.go index ef1297dc3..e6c8c019b 100644 --- a/looprpc/swapclient.pb.json.go +++ b/looprpc/swapclient.pb.json.go @@ -563,6 +563,56 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex callback(string(respBytes), nil) } + registry["looprpc.SwapClient.ReservationRequest"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &ReservationRequestRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewSwapClientClient(conn) + resp, err := client.ReservationRequest(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + + registry["looprpc.SwapClient.ReservationQuote"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &ReservationQuoteRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewSwapClientClient(conn) + resp, err := client.ReservationQuote(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + registry["looprpc.SwapClient.InstantOut"] = func(ctx context.Context, conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { diff --git a/swapserverrpc/reservation.pb.go b/swapserverrpc/reservation.pb.go index a1f73b43d..553a3e546 100644 --- a/swapserverrpc/reservation.pb.go +++ b/swapserverrpc/reservation.pb.go @@ -301,6 +301,248 @@ func (*ServerOpenReservationResponse) Descriptor() ([]byte, []int) { return file_reservation_proto_rawDescGZIP(), []int{3} } +// RequestReservationRequest is a request sent from the client to the server to +// request a new reservation UTXO. +type RequestReservationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // value is the value of the reservation in satoshis. + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + // expiry is the relative expiry of the reservation. + Expiry uint32 `protobuf:"varint,2,opt,name=expiry,proto3" json:"expiry,omitempty"` + // client_key is the public key of the client. + ClientKey []byte `protobuf:"bytes,3,opt,name=client_key,json=clientKey,proto3" json:"client_key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RequestReservationRequest) Reset() { + *x = RequestReservationRequest{} + mi := &file_reservation_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequestReservationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestReservationRequest) ProtoMessage() {} + +func (x *RequestReservationRequest) ProtoReflect() protoreflect.Message { + mi := &file_reservation_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RequestReservationRequest.ProtoReflect.Descriptor instead. +func (*RequestReservationRequest) Descriptor() ([]byte, []int) { + return file_reservation_proto_rawDescGZIP(), []int{4} +} + +func (x *RequestReservationRequest) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *RequestReservationRequest) GetExpiry() uint32 { + if x != nil { + return x.Expiry + } + return 0 +} + +func (x *RequestReservationRequest) GetClientKey() []byte { + if x != nil { + return x.ClientKey + } + return nil +} + +// RequestReservationResponse is a response sent from the server to the client +// to confirm a reservation request. +type RequestReservationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // reservation_id is the id of the reservation. + ReservationId []byte `protobuf:"bytes,1,opt,name=reservation_id,json=reservationId,proto3" json:"reservation_id,omitempty"` + // server_key is the public key of the server. + ServerKey []byte `protobuf:"bytes,2,opt,name=server_key,json=serverKey,proto3" json:"server_key,omitempty"` + // invoice is the invoice for the reservation that the client should pay. + Invoice string `protobuf:"bytes,3,opt,name=invoice,proto3" json:"invoice,omitempty"` + // expiry is the absolute expiry of the reservation. + Expiry uint32 `protobuf:"varint,4,opt,name=expiry,proto3" json:"expiry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RequestReservationResponse) Reset() { + *x = RequestReservationResponse{} + mi := &file_reservation_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequestReservationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestReservationResponse) ProtoMessage() {} + +func (x *RequestReservationResponse) ProtoReflect() protoreflect.Message { + mi := &file_reservation_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RequestReservationResponse.ProtoReflect.Descriptor instead. +func (*RequestReservationResponse) Descriptor() ([]byte, []int) { + return file_reservation_proto_rawDescGZIP(), []int{5} +} + +func (x *RequestReservationResponse) GetReservationId() []byte { + if x != nil { + return x.ReservationId + } + return nil +} + +func (x *RequestReservationResponse) GetServerKey() []byte { + if x != nil { + return x.ServerKey + } + return nil +} + +func (x *RequestReservationResponse) GetInvoice() string { + if x != nil { + return x.Invoice + } + return "" +} + +func (x *RequestReservationResponse) GetExpiry() uint32 { + if x != nil { + return x.Expiry + } + return 0 +} + +// QuoteReservationRequest is a request sent from the client to the server to +// request a quote for a reservation UTXO. +type QuoteReservationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // value is the value of the reservation in satoshis. + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + // expiry is the relative expiry of the reservation. + Expiry uint32 `protobuf:"varint,2,opt,name=expiry,proto3" json:"expiry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QuoteReservationRequest) Reset() { + *x = QuoteReservationRequest{} + mi := &file_reservation_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QuoteReservationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QuoteReservationRequest) ProtoMessage() {} + +func (x *QuoteReservationRequest) ProtoReflect() protoreflect.Message { + mi := &file_reservation_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QuoteReservationRequest.ProtoReflect.Descriptor instead. +func (*QuoteReservationRequest) Descriptor() ([]byte, []int) { + return file_reservation_proto_rawDescGZIP(), []int{6} +} + +func (x *QuoteReservationRequest) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *QuoteReservationRequest) GetExpiry() uint32 { + if x != nil { + return x.Expiry + } + return 0 +} + +// QuoteReservationResponse is a response sent from the server to the client to +// confirm a reservation quote request. +type QuoteReservationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // prepay_cost is the cost of the prepay. + PrepayCost uint64 `protobuf:"varint,1,opt,name=prepay_cost,json=prepayCost,proto3" json:"prepay_cost,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QuoteReservationResponse) Reset() { + *x = QuoteReservationResponse{} + mi := &file_reservation_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QuoteReservationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QuoteReservationResponse) ProtoMessage() {} + +func (x *QuoteReservationResponse) ProtoReflect() protoreflect.Message { + mi := &file_reservation_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QuoteReservationResponse.ProtoReflect.Descriptor instead. +func (*QuoteReservationResponse) Descriptor() ([]byte, []int) { + return file_reservation_proto_rawDescGZIP(), []int{7} +} + +func (x *QuoteReservationResponse) GetPrepayCost() uint64 { + if x != nil { + return x.PrepayCost + } + return 0 +} + var File_reservation_proto protoreflect.FileDescriptor const file_reservation_proto_rawDesc = "" + @@ -319,13 +561,32 @@ const file_reservation_proto_rawDesc = "" + "\x0ereservation_id\x18\x01 \x01(\fR\rreservationId\x12\x1d\n" + "\n" + "client_key\x18\x02 \x01(\fR\tclientKey\"\x1f\n" + - "\x1dServerOpenReservationResponse*Q\n" + + "\x1dServerOpenReservationResponse\"h\n" + + "\x19RequestReservationRequest\x12\x14\n" + + "\x05value\x18\x01 \x01(\x04R\x05value\x12\x16\n" + + "\x06expiry\x18\x02 \x01(\rR\x06expiry\x12\x1d\n" + + "\n" + + "client_key\x18\x03 \x01(\fR\tclientKey\"\x94\x01\n" + + "\x1aRequestReservationResponse\x12%\n" + + "\x0ereservation_id\x18\x01 \x01(\fR\rreservationId\x12\x1d\n" + + "\n" + + "server_key\x18\x02 \x01(\fR\tserverKey\x12\x18\n" + + "\ainvoice\x18\x03 \x01(\tR\ainvoice\x12\x16\n" + + "\x06expiry\x18\x04 \x01(\rR\x06expiry\"G\n" + + "\x17QuoteReservationRequest\x12\x14\n" + + "\x05value\x18\x01 \x01(\x04R\x05value\x12\x16\n" + + "\x06expiry\x18\x02 \x01(\rR\x06expiry\";\n" + + "\x18QuoteReservationResponse\x12\x1f\n" + + "\vprepay_cost\x18\x01 \x01(\x04R\n" + + "prepayCost*Q\n" + "\x1aReservationProtocolVersion\x12\x14\n" + "\x10RESERVATION_NONE\x10\x00\x12\x1d\n" + - "\x19RESERVATION_SERVER_NOTIFY\x10\x012\xef\x01\n" + + "\x19RESERVATION_SERVER_NOTIFY\x10\x012\xa7\x03\n" + "\x12ReservationService\x12w\n" + "\x1dReservationNotificationStream\x12'.looprpc.ReservationNotificationRequest\x1a&.looprpc.ServerReservationNotification\"\x03\x88\x02\x010\x01\x12`\n" + - "\x0fOpenReservation\x12%.looprpc.ServerOpenReservationRequest\x1a&.looprpc.ServerOpenReservationResponseB-Z+github.com/lightninglabs/loop/swapserverrpcb\x06proto3" + "\x0fOpenReservation\x12%.looprpc.ServerOpenReservationRequest\x1a&.looprpc.ServerOpenReservationResponse\x12]\n" + + "\x12RequestReservation\x12\".looprpc.RequestReservationRequest\x1a#.looprpc.RequestReservationResponse\x12W\n" + + "\x10QuoteReservation\x12 .looprpc.QuoteReservationRequest\x1a!.looprpc.QuoteReservationResponseB-Z+github.com/lightninglabs/loop/swapserverrpcb\x06proto3" var ( file_reservation_proto_rawDescOnce sync.Once @@ -340,23 +601,31 @@ func file_reservation_proto_rawDescGZIP() []byte { } var file_reservation_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_reservation_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_reservation_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_reservation_proto_goTypes = []any{ (ReservationProtocolVersion)(0), // 0: looprpc.ReservationProtocolVersion (*ReservationNotificationRequest)(nil), // 1: looprpc.ReservationNotificationRequest (*ServerReservationNotification)(nil), // 2: looprpc.ServerReservationNotification (*ServerOpenReservationRequest)(nil), // 3: looprpc.ServerOpenReservationRequest (*ServerOpenReservationResponse)(nil), // 4: looprpc.ServerOpenReservationResponse + (*RequestReservationRequest)(nil), // 5: looprpc.RequestReservationRequest + (*RequestReservationResponse)(nil), // 6: looprpc.RequestReservationResponse + (*QuoteReservationRequest)(nil), // 7: looprpc.QuoteReservationRequest + (*QuoteReservationResponse)(nil), // 8: looprpc.QuoteReservationResponse } var file_reservation_proto_depIdxs = []int32{ 0, // 0: looprpc.ReservationNotificationRequest.protocol_version:type_name -> looprpc.ReservationProtocolVersion 0, // 1: looprpc.ServerReservationNotification.protocol_version:type_name -> looprpc.ReservationProtocolVersion 1, // 2: looprpc.ReservationService.ReservationNotificationStream:input_type -> looprpc.ReservationNotificationRequest 3, // 3: looprpc.ReservationService.OpenReservation:input_type -> looprpc.ServerOpenReservationRequest - 2, // 4: looprpc.ReservationService.ReservationNotificationStream:output_type -> looprpc.ServerReservationNotification - 4, // 5: looprpc.ReservationService.OpenReservation:output_type -> looprpc.ServerOpenReservationResponse - 4, // [4:6] is the sub-list for method output_type - 2, // [2:4] is the sub-list for method input_type + 5, // 4: looprpc.ReservationService.RequestReservation:input_type -> looprpc.RequestReservationRequest + 7, // 5: looprpc.ReservationService.QuoteReservation:input_type -> looprpc.QuoteReservationRequest + 2, // 6: looprpc.ReservationService.ReservationNotificationStream:output_type -> looprpc.ServerReservationNotification + 4, // 7: looprpc.ReservationService.OpenReservation:output_type -> looprpc.ServerOpenReservationResponse + 6, // 8: looprpc.ReservationService.RequestReservation:output_type -> looprpc.RequestReservationResponse + 8, // 9: looprpc.ReservationService.QuoteReservation:output_type -> looprpc.QuoteReservationResponse + 6, // [6:10] is the sub-list for method output_type + 2, // [2:6] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name @@ -373,7 +642,7 @@ func file_reservation_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_reservation_proto_rawDesc), len(file_reservation_proto_rawDesc)), NumEnums: 1, - NumMessages: 4, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/swapserverrpc/reservation.proto b/swapserverrpc/reservation.proto index b582c6e69..0d713eb78 100644 --- a/swapserverrpc/reservation.proto +++ b/swapserverrpc/reservation.proto @@ -19,6 +19,14 @@ service ReservationService { // OpenReservation requests a new reservation UTXO from the server. rpc OpenReservation (ServerOpenReservationRequest) returns (ServerOpenReservationResponse); + + // RequestReservation requests a new reservation UTXO from the server. + rpc RequestReservation (RequestReservationRequest) + returns (RequestReservationResponse); + + // QuoteReservation requests a quote for a reservation UTXO from the server. + rpc QuoteReservation (QuoteReservationRequest) + returns (QuoteReservationResponse); } // ReservationNotificationRequest is an empty request sent from the client to @@ -62,6 +70,51 @@ message ServerOpenReservationRequest { message ServerOpenReservationResponse { } +// RequestReservationRequest is a request sent from the client to the server to +// request a new reservation UTXO. +message RequestReservationRequest { + // value is the value of the reservation in satoshis. + uint64 value = 1; + + // expiry is the relative expiry of the reservation. + uint32 expiry = 2; + + // client_key is the public key of the client. + bytes client_key = 3; +} + +// RequestReservationResponse is a response sent from the server to the client +// to confirm a reservation request. +message RequestReservationResponse { + // reservation_id is the id of the reservation. + bytes reservation_id = 1; + + // server_key is the public key of the server. + bytes server_key = 2; + + // invoice is the invoice for the reservation that the client should pay. + string invoice = 3; + + // expiry is the absolute expiry of the reservation. + uint32 expiry = 4; +} + +// QuoteReservationRequest is a request sent from the client to the server to +// request a quote for a reservation UTXO. +message QuoteReservationRequest { + // value is the value of the reservation in satoshis. + uint64 value = 1; + + // expiry is the relative expiry of the reservation. + uint32 expiry = 2; +} + +// QuoteReservationResponse is a response sent from the server to the client to +// confirm a reservation quote request. +message QuoteReservationResponse { + // prepay_cost is the cost of the prepay. + uint64 prepay_cost = 1; +} // ReservationProtocolVersion is the version of the reservation protocol. enum ReservationProtocolVersion { // RESERVATION_NONE is the default value and means that the reservation diff --git a/swapserverrpc/reservation_grpc.pb.go b/swapserverrpc/reservation_grpc.pb.go index 89deaf706..49585472b 100644 --- a/swapserverrpc/reservation_grpc.pb.go +++ b/swapserverrpc/reservation_grpc.pb.go @@ -24,6 +24,10 @@ type ReservationServiceClient interface { ReservationNotificationStream(ctx context.Context, in *ReservationNotificationRequest, opts ...grpc.CallOption) (ReservationService_ReservationNotificationStreamClient, error) // OpenReservation requests a new reservation UTXO from the server. OpenReservation(ctx context.Context, in *ServerOpenReservationRequest, opts ...grpc.CallOption) (*ServerOpenReservationResponse, error) + // RequestReservation requests a new reservation UTXO from the server. + RequestReservation(ctx context.Context, in *RequestReservationRequest, opts ...grpc.CallOption) (*RequestReservationResponse, error) + // QuoteReservation requests a quote for a reservation UTXO from the server. + QuoteReservation(ctx context.Context, in *QuoteReservationRequest, opts ...grpc.CallOption) (*QuoteReservationResponse, error) } type reservationServiceClient struct { @@ -76,6 +80,24 @@ func (c *reservationServiceClient) OpenReservation(ctx context.Context, in *Serv return out, nil } +func (c *reservationServiceClient) RequestReservation(ctx context.Context, in *RequestReservationRequest, opts ...grpc.CallOption) (*RequestReservationResponse, error) { + out := new(RequestReservationResponse) + err := c.cc.Invoke(ctx, "/looprpc.ReservationService/RequestReservation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reservationServiceClient) QuoteReservation(ctx context.Context, in *QuoteReservationRequest, opts ...grpc.CallOption) (*QuoteReservationResponse, error) { + out := new(QuoteReservationResponse) + err := c.cc.Invoke(ctx, "/looprpc.ReservationService/QuoteReservation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ReservationServiceServer is the server API for ReservationService service. // All implementations must embed UnimplementedReservationServiceServer // for forward compatibility @@ -86,6 +108,10 @@ type ReservationServiceServer interface { ReservationNotificationStream(*ReservationNotificationRequest, ReservationService_ReservationNotificationStreamServer) error // OpenReservation requests a new reservation UTXO from the server. OpenReservation(context.Context, *ServerOpenReservationRequest) (*ServerOpenReservationResponse, error) + // RequestReservation requests a new reservation UTXO from the server. + RequestReservation(context.Context, *RequestReservationRequest) (*RequestReservationResponse, error) + // QuoteReservation requests a quote for a reservation UTXO from the server. + QuoteReservation(context.Context, *QuoteReservationRequest) (*QuoteReservationResponse, error) mustEmbedUnimplementedReservationServiceServer() } @@ -99,6 +125,12 @@ func (UnimplementedReservationServiceServer) ReservationNotificationStream(*Rese func (UnimplementedReservationServiceServer) OpenReservation(context.Context, *ServerOpenReservationRequest) (*ServerOpenReservationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method OpenReservation not implemented") } +func (UnimplementedReservationServiceServer) RequestReservation(context.Context, *RequestReservationRequest) (*RequestReservationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestReservation not implemented") +} +func (UnimplementedReservationServiceServer) QuoteReservation(context.Context, *QuoteReservationRequest) (*QuoteReservationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QuoteReservation not implemented") +} func (UnimplementedReservationServiceServer) mustEmbedUnimplementedReservationServiceServer() {} // UnsafeReservationServiceServer may be embedded to opt out of forward compatibility for this service. @@ -151,6 +183,42 @@ func _ReservationService_OpenReservation_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _ReservationService_RequestReservation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestReservationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReservationServiceServer).RequestReservation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.ReservationService/RequestReservation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReservationServiceServer).RequestReservation(ctx, req.(*RequestReservationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReservationService_QuoteReservation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuoteReservationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReservationServiceServer).QuoteReservation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.ReservationService/QuoteReservation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReservationServiceServer).QuoteReservation(ctx, req.(*QuoteReservationRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ReservationService_ServiceDesc is the grpc.ServiceDesc for ReservationService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -162,6 +230,14 @@ var ReservationService_ServiceDesc = grpc.ServiceDesc{ MethodName: "OpenReservation", Handler: _ReservationService_OpenReservation_Handler, }, + { + MethodName: "RequestReservation", + Handler: _ReservationService_RequestReservation_Handler, + }, + { + MethodName: "QuoteReservation", + Handler: _ReservationService_QuoteReservation_Handler, + }, }, Streams: []grpc.StreamDesc{ {