Merge pull request #632 from sputn1ck/instantloopout_2

[2/?] Instant loop out:  Add reservations
update-to-v0.27.0-beta
Konstantin Nick 3 months ago committed by GitHub
commit e9d374a341
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -19,6 +19,7 @@ import (
"github.com/lightninglabs/loop/sweep"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
)
@ -148,6 +149,7 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
LndServices: cfg.Lnd,
Server: swapServerClient,
Store: loopDB,
Conn: swapServerClient.conn,
LsatStore: lsatStore,
CreateExpiryTimer: func(d time.Duration) <-chan time.Time {
return time.NewTimer(d).C
@ -200,6 +202,11 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
return client, cleanup, nil
}
// GetConn returns the gRPC connection to the server.
func (s *Client) GetConn() *grpc.ClientConn {
return s.clientConfig.Conn
}
// FetchSwaps returns all loop in and out swaps currently in the database.
func (s *Client) FetchSwaps(ctx context.Context) ([]*SwapInfo, error) {
loopOutSwaps, err := s.Store.FetchLoopOutSwaps(ctx)

@ -147,7 +147,7 @@ func main() {
monitorCommand, quoteCommand, listAuthCommand,
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
getInfoCommand, abandonSwapCommand,
getInfoCommand, abandonSwapCommand, reservationsCommands,
}
err := app.Run(os.Args)

@ -0,0 +1,55 @@
package main
import (
"context"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
)
var reservationsCommands = cli.Command{
Name: "reservations",
ShortName: "r",
Usage: "manage reservations",
Description: `
With loopd running, you can use this command to manage your
reservations. Reservations are 2-of-2 multisig utxos that
the loop server can open to clients. The reservations are used
to enable instant swaps.
`,
Subcommands: []cli.Command{
listReservationsCommand,
},
}
var (
listReservationsCommand = cli.Command{
Name: "list",
ShortName: "l",
Usage: "list all reservations",
ArgsUsage: "",
Description: `
List all reservations.
`,
Action: listReservations,
}
)
func listReservations(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListReservations(
context.Background(), &looprpc.ListReservationsRequest{},
)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}

@ -6,12 +6,14 @@ import (
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"google.golang.org/grpc"
)
// clientConfig contains config items for the swap client.
type clientConfig struct {
LndServices *lndclient.LndServices
Server swapServerClient
Conn *grpc.ClientConn
Store loopdb.SwapStore
LsatStore lsat.Store
CreateExpiryTimer func(expiry time.Duration) <-chan time.Time

@ -206,6 +206,8 @@ func (s *StateMachine) SendEvent(event EventType, eventCtx EventContext) error {
// current state.
state, err := s.getNextState(event)
if err != nil {
log.Errorf("unable to get next state: %v from event: "+
"%v, current state: %v", err, event, s.current)
return ErrEventRejected
}

@ -10,6 +10,7 @@ import (
"sort"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/instantout/reservation"
)
func main() {
@ -41,6 +42,13 @@ func run() error {
return err
}
case "reservation":
reservationFSM := &reservation.FSM{}
err = writeMermaidFile(fp, reservationFSM.GetReservationStates())
if err != nil {
return err
}
default:
fmt.Println("Missing or wrong argument: fsm must be one of:")
fmt.Println("\treservations")

@ -0,0 +1,176 @@
package reservation
import (
"context"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop/fsm"
looprpc "github.com/lightninglabs/loop/swapserverrpc"
)
// InitReservationContext contains the request parameters for a reservation.
type InitReservationContext struct {
reservationID ID
serverPubkey *btcec.PublicKey
value btcutil.Amount
expiry uint32
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 (r *FSM) InitAction(eventCtx fsm.EventContext) fsm.EventType {
// Check if the context is of the correct type.
reservationRequest, ok := eventCtx.(*InitReservationContext)
if !ok {
return r.HandleError(fsm.ErrInvalidContextType)
}
keyRes, err := r.cfg.Wallet.DeriveNextKey(
r.ctx, KeyFamily,
)
if err != nil {
return r.HandleError(err)
}
// Send the client reservation details to the server.
log.Debugf("Dispatching reservation to server: %x",
reservationRequest.reservationID)
request := &looprpc.ServerOpenReservationRequest{
ReservationId: reservationRequest.reservationID[:],
ClientKey: keyRes.PubKey.SerializeCompressed(),
}
_, err = r.cfg.ReservationClient.OpenReservation(r.ctx, request)
if err != nil {
return r.HandleError(err)
}
reservation, err := NewReservation(
reservationRequest.reservationID,
reservationRequest.serverPubkey,
keyRes.PubKey,
reservationRequest.value,
reservationRequest.expiry,
reservationRequest.heightHint,
keyRes.KeyLocator,
)
if err != nil {
return r.HandleError(err)
}
r.reservation = reservation
// Create the reservation in the database.
err = r.cfg.Store.CreateReservation(r.ctx, reservation)
if err != nil {
return r.HandleError(err)
}
return OnBroadcast
}
// SubscribeToConfirmationAction is the action that is executed when the
// reservation is waiting for confirmation. It subscribes to the confirmation
// of the reservation transaction.
func (r *FSM) SubscribeToConfirmationAction(_ fsm.EventContext) fsm.EventType {
pkscript, err := r.reservation.GetPkScript()
if err != nil {
return r.HandleError(err)
}
callCtx, cancel := context.WithCancel(r.ctx)
defer cancel()
// Subscribe to the confirmation of the reservation transaction.
log.Debugf("Subscribing to conf for reservation: %x pkscript: %x, "+
"initiation height: %v", r.reservation.ID, pkscript,
r.reservation.InitiationHeight)
confChan, errConfChan, err := r.cfg.ChainNotifier.RegisterConfirmationsNtfn(
callCtx, nil, pkscript, DefaultConfTarget,
r.reservation.InitiationHeight,
)
if err != nil {
r.Errorf("unable to subscribe to conf notification: %v", err)
return r.HandleError(err)
}
blockChan, errBlockChan, err := r.cfg.ChainNotifier.RegisterBlockEpochNtfn(
callCtx,
)
if err != nil {
r.Errorf("unable to subscribe to block notifications: %v", err)
return r.HandleError(err)
}
// We'll now wait for the confirmation of the reservation transaction.
for {
select {
case err := <-errConfChan:
r.Errorf("conf subscription error: %v", err)
return r.HandleError(err)
case err := <-errBlockChan:
r.Errorf("block subscription error: %v", err)
return r.HandleError(err)
case confInfo := <-confChan:
r.Debugf("reservation confirmed: %v", confInfo)
outpoint, err := r.reservation.findReservationOutput(
confInfo.Tx,
)
if err != nil {
return r.HandleError(err)
}
r.reservation.ConfirmationHeight = confInfo.BlockHeight
r.reservation.Outpoint = outpoint
return OnConfirmed
case block := <-blockChan:
r.Debugf("block received: %v expiry: %v", block,
r.reservation.Expiry)
if uint32(block) >= r.reservation.Expiry {
return OnTimedOut
}
case <-r.ctx.Done():
return fsm.NoOp
}
}
}
// ReservationConfirmedAction waits for the reservation to be either expired or
// waits for other actions to happen.
func (r *FSM) ReservationConfirmedAction(_ fsm.EventContext) fsm.EventType {
blockHeightChan, errEpochChan, err := r.cfg.ChainNotifier.
RegisterBlockEpochNtfn(r.ctx)
if err != nil {
return r.HandleError(err)
}
for {
select {
case err := <-errEpochChan:
return r.HandleError(err)
case blockHeight := <-blockHeightChan:
expired := blockHeight >= int32(r.reservation.Expiry)
if expired {
r.Debugf("Reservation %v expired",
r.reservation.ID)
return OnTimedOut
}
case <-r.ctx.Done():
return fsm.NoOp
}
}
}

@ -0,0 +1,371 @@
package reservation
import (
"context"
"encoding/hex"
"errors"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
var (
defaultPubkeyBytes, _ = hex.DecodeString("021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d")
defaultPubkey, _ = btcec.ParsePubKey(defaultPubkeyBytes)
defaultValue = btcutil.Amount(100)
defaultExpiry = uint32(100)
)
func newValidInitReservationContext() *InitReservationContext {
return &InitReservationContext{
reservationID: ID{0x01},
serverPubkey: defaultPubkey,
value: defaultValue,
expiry: defaultExpiry,
heightHint: 0,
}
}
func newValidClientReturn() *swapserverrpc.ServerOpenReservationResponse {
return &swapserverrpc.ServerOpenReservationResponse{}
}
type mockReservationClient struct {
mock.Mock
}
func (m *mockReservationClient) OpenReservation(ctx context.Context,
in *swapserverrpc.ServerOpenReservationRequest,
opts ...grpc.CallOption) (*swapserverrpc.ServerOpenReservationResponse,
error) {
args := m.Called(ctx, in, opts)
return args.Get(0).(*swapserverrpc.ServerOpenReservationResponse),
args.Error(1)
}
func (m *mockReservationClient) ReservationNotificationStream(
ctx context.Context, in *swapserverrpc.ReservationNotificationRequest,
opts ...grpc.CallOption,
) (swapserverrpc.ReservationService_ReservationNotificationStreamClient,
error) {
args := m.Called(ctx, in, opts)
return args.Get(0).(swapserverrpc.ReservationService_ReservationNotificationStreamClient),
args.Error(1)
}
func (m *mockReservationClient) FetchL402(ctx context.Context,
in *swapserverrpc.FetchL402Request,
opts ...grpc.CallOption) (*swapserverrpc.FetchL402Response, error) {
args := m.Called(ctx, in, opts)
return args.Get(0).(*swapserverrpc.FetchL402Response),
args.Error(1)
}
type mockStore struct {
mock.Mock
Store
}
func (m *mockStore) CreateReservation(ctx context.Context,
reservation *Reservation) error {
args := m.Called(ctx, reservation)
return args.Error(0)
}
// TestInitReservationAction tests the InitReservationAction of the reservation
// state machine.
func TestInitReservationAction(t *testing.T) {
tests := []struct {
name string
eventCtx fsm.EventContext
mockStoreErr error
mockClientReturn *swapserverrpc.ServerOpenReservationResponse
mockClientErr error
expectedEvent fsm.EventType
}{
{
name: "success",
eventCtx: newValidInitReservationContext(),
mockClientReturn: newValidClientReturn(),
expectedEvent: OnBroadcast,
},
{
name: "invalid context",
eventCtx: struct{}{},
expectedEvent: fsm.OnError,
},
{
name: "reservation server error",
eventCtx: newValidInitReservationContext(),
mockClientErr: errors.New("reservation server error"),
expectedEvent: fsm.OnError,
},
{
name: "store error",
eventCtx: newValidInitReservationContext(),
mockClientReturn: newValidClientReturn(),
mockStoreErr: errors.New("store error"),
expectedEvent: fsm.OnError,
},
}
for _, tc := range tests {
ctxb := context.Background()
mockLnd := test.NewMockLnd()
mockReservationClient := new(mockReservationClient)
mockReservationClient.On(
"OpenReservation", mock.Anything,
mock.Anything, mock.Anything,
).Return(tc.mockClientReturn, tc.mockClientErr)
mockStore := new(mockStore)
mockStore.On(
"CreateReservation", mock.Anything, mock.Anything,
).Return(tc.mockStoreErr)
reservationFSM := &FSM{
ctx: ctxb,
cfg: &Config{
Wallet: mockLnd.WalletKit,
ChainNotifier: mockLnd.ChainNotifier,
ReservationClient: mockReservationClient,
Store: mockStore,
},
StateMachine: &fsm.StateMachine{},
}
event := reservationFSM.InitAction(tc.eventCtx)
require.Equal(t, tc.expectedEvent, event)
}
}
type MockChainNotifier struct {
mock.Mock
}
func (m *MockChainNotifier) RegisterConfirmationsNtfn(ctx context.Context,
txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32,
options ...lndclient.NotifierOption) (chan *chainntnfs.TxConfirmation,
chan error, error) {
args := m.Called(ctx, txid, pkScript, numConfs, heightHint)
return args.Get(0).(chan *chainntnfs.TxConfirmation), args.Get(1).(chan error), args.Error(2)
}
func (m *MockChainNotifier) RegisterBlockEpochNtfn(ctx context.Context) (
chan int32, chan error, error) {
args := m.Called(ctx)
return args.Get(0).(chan int32), args.Get(1).(chan error), args.Error(2)
}
func (m *MockChainNotifier) RegisterSpendNtfn(ctx context.Context,
outpoint *wire.OutPoint, pkScript []byte, heightHint int32) (
chan *chainntnfs.SpendDetail, chan error, error) {
args := m.Called(ctx, pkScript, heightHint)
return args.Get(0).(chan *chainntnfs.SpendDetail), args.Get(1).(chan error), args.Error(2)
}
// TestSubscribeToConfirmationAction tests the SubscribeToConfirmationAction of
// the reservation state machine.
func TestSubscribeToConfirmationAction(t *testing.T) {
tests := []struct {
name string
blockHeight int32
blockErr error
sendTxConf bool
confErr error
expectedEvent fsm.EventType
}{
{
name: "success",
blockHeight: 0,
sendTxConf: true,
expectedEvent: OnConfirmed,
},
{
name: "expired",
blockHeight: 100,
expectedEvent: OnTimedOut,
},
{
name: "block error",
blockHeight: 0,
blockErr: errors.New("block error"),
expectedEvent: fsm.OnError,
},
{
name: "tx confirmation error",
blockHeight: 0,
confErr: errors.New("tx confirmation error"),
expectedEvent: fsm.OnError,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
chainNotifier := new(MockChainNotifier)
// Create the FSM.
r := NewFSMFromReservation(
context.Background(), &Config{
ChainNotifier: chainNotifier,
},
&Reservation{
Expiry: defaultExpiry,
ServerPubkey: defaultPubkey,
ClientPubkey: defaultPubkey,
Value: defaultValue,
},
)
pkScript, err := r.reservation.GetPkScript()
require.NoError(t, err)
confChan := make(chan *chainntnfs.TxConfirmation)
confErrChan := make(chan error)
blockChan := make(chan int32)
blockErrChan := make(chan error)
// Define the expected return values for the mocks.
chainNotifier.On(
"RegisterConfirmationsNtfn", mock.Anything, mock.Anything,
mock.Anything, mock.Anything, mock.Anything,
).Return(confChan, confErrChan, nil)
chainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return(
blockChan, blockErrChan, nil,
)
go func() {
// Send the tx confirmation.
if tc.sendTxConf {
confChan <- &chainntnfs.TxConfirmation{
Tx: &wire.MsgTx{
TxIn: []*wire.TxIn{},
TxOut: []*wire.TxOut{
{
Value: int64(defaultValue),
PkScript: pkScript,
},
},
},
}
}
}()
go func() {
// Send the block notification.
if tc.blockHeight != 0 {
blockChan <- tc.blockHeight
}
}()
go func() {
// Send the block notification error.
if tc.blockErr != nil {
blockErrChan <- tc.blockErr
}
}()
go func() {
// Send the tx confirmation error.
if tc.confErr != nil {
confErrChan <- tc.confErr
}
}()
eventType := r.SubscribeToConfirmationAction(nil)
// Assert that the return value is as expected
require.Equal(t, tc.expectedEvent, eventType)
// Assert that the expected functions were called on the mocks
chainNotifier.AssertExpectations(t)
})
}
}
// TestReservationConfirmedAction tests the ReservationConfirmedAction of the
// reservation state machine.
func TestReservationConfirmedAction(t *testing.T) {
tests := []struct {
name string
blockHeight int32
blockErr error
expectedEvent fsm.EventType
}{
{
name: "expired",
blockHeight: 100,
expectedEvent: OnTimedOut,
},
{
name: "block error",
blockHeight: 0,
blockErr: errors.New("block error"),
expectedEvent: fsm.OnError,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
chainNotifier := new(MockChainNotifier)
// Create the FSM.
r := NewFSMFromReservation(
context.Background(), &Config{
ChainNotifier: chainNotifier,
},
&Reservation{
Expiry: defaultExpiry,
},
)
blockChan := make(chan int32)
blockErrChan := make(chan error)
// Define our expected return values for the mocks.
chainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return(
blockChan, blockErrChan, nil,
)
go func() {
// Send the block notification.
if tc.blockHeight != 0 {
blockChan <- tc.blockHeight
}
}()
go func() {
// Send the block notification error.
if tc.blockErr != nil {
blockErrChan <- tc.blockErr
}
}()
eventType := r.ReservationConfirmedAction(nil)
require.Equal(t, tc.expectedEvent, eventType)
// Assert that the expected functions were called on the mocks
chainNotifier.AssertExpectations(t)
})
}
}

@ -0,0 +1,229 @@
package reservation
import (
"context"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/fsm"
looprpc "github.com/lightninglabs/loop/swapserverrpc"
)
const (
// defaultObserverSize is the size of the fsm observer channel.
defaultObserverSize = 15
)
// Config contains all the services that the reservation FSM needs to operate.
type Config struct {
// Store is the database store for the reservations.
Store Store
// Wallet handles the key derivation for the reservation.
Wallet lndclient.WalletKitClient
// ChainNotifier is used to subscribe to block notifications.
ChainNotifier lndclient.ChainNotifierClient
// ReservationClient is the client used to communicate with the
// swap server.
ReservationClient looprpc.ReservationServiceClient
// FetchL402 is the function used to fetch the l402 token.
FetchL402 func(context.Context) error
}
// FSM is the state machine that manages the reservation lifecycle.
type FSM struct {
*fsm.StateMachine
cfg *Config
reservation *Reservation
ctx context.Context
}
// NewFSM creates a new reservation FSM.
func NewFSM(ctx context.Context, cfg *Config) *FSM {
reservation := &Reservation{
State: fsm.EmptyState,
}
return NewFSMFromReservation(ctx, cfg, reservation)
}
// NewFSMFromReservation creates a new reservation FSM from an existing
// reservation recovered from the database.
func NewFSMFromReservation(ctx context.Context, cfg *Config,
reservation *Reservation) *FSM {
reservationFsm := &FSM{
ctx: ctx,
cfg: cfg,
reservation: reservation,
}
reservationFsm.StateMachine = fsm.NewStateMachineWithState(
reservationFsm.GetReservationStates(), reservation.State,
defaultObserverSize,
)
reservationFsm.ActionEntryFunc = reservationFsm.updateReservation
return reservationFsm
}
// States.
var (
// Init is the initial state of the reservation.
Init = fsm.StateType("Init")
// WaitForConfirmation is the state where we wait for the reservation
// tx to be confirmed.
WaitForConfirmation = fsm.StateType("WaitForConfirmation")
// Confirmed is the state where the reservation tx has been confirmed.
Confirmed = fsm.StateType("Confirmed")
// TimedOut is the state where the reservation has timed out.
TimedOut = fsm.StateType("TimedOut")
// Failed is the state where the reservation has failed.
Failed = fsm.StateType("Failed")
// Spent is the state where a spend tx has been confirmed.
Spent = fsm.StateType("Spent")
// Locked is the state where the reservation is locked and can't be
// used for instant out swaps.
Locked = fsm.StateType("Locked")
)
// Events.
var (
// OnServerRequest is the event that is triggered when the server
// requests a new reservation.
OnServerRequest = fsm.EventType("OnServerRequest")
// OnBroadcast is the event that is triggered when the reservation tx
// has been broadcast.
OnBroadcast = fsm.EventType("OnBroadcast")
// OnConfirmed is the event that is triggered when the reservation tx
// has been confirmed.
OnConfirmed = fsm.EventType("OnConfirmed")
// OnTimedOut is the event that is triggered when the reservation has
// timed out.
OnTimedOut = fsm.EventType("OnTimedOut")
// OnSwept is the event that is triggered when the reservation has been
// swept by the server.
OnSwept = fsm.EventType("OnSwept")
// OnRecover is the event that is triggered when the reservation FSM
// recovers from a restart.
OnRecover = fsm.EventType("OnRecover")
)
// GetReservationStates returns the statemap that defines the reservation
// state machine.
func (f *FSM) GetReservationStates() fsm.States {
return fsm.States{
fsm.EmptyState: fsm.State{
Transitions: fsm.Transitions{
OnServerRequest: Init,
},
Action: nil,
},
Init: fsm.State{
Transitions: fsm.Transitions{
OnBroadcast: WaitForConfirmation,
OnRecover: Failed,
fsm.OnError: Failed,
},
Action: f.InitAction,
},
WaitForConfirmation: fsm.State{
Transitions: fsm.Transitions{
OnRecover: WaitForConfirmation,
OnConfirmed: Confirmed,
OnTimedOut: TimedOut,
},
Action: f.SubscribeToConfirmationAction,
},
Confirmed: fsm.State{
Transitions: fsm.Transitions{
OnTimedOut: TimedOut,
OnRecover: Confirmed,
},
Action: f.ReservationConfirmedAction,
},
TimedOut: fsm.State{
Action: fsm.NoOpAction,
},
Failed: fsm.State{
Action: fsm.NoOpAction,
},
}
}
// updateReservation updates the reservation in the database. This function
// is called after every new state transition.
func (r *FSM) updateReservation(notification fsm.Notification) {
if r.reservation == nil {
return
}
r.Debugf(
"NextState: %v, PreviousState: %v, Event: %v",
notification.NextState, notification.PreviousState,
notification.Event,
)
r.reservation.State = notification.NextState
// Don't update the reservation if we are in an initial state or if we
// are transitioning from an initial state to a failed state.
if r.reservation.State == fsm.EmptyState ||
r.reservation.State == Init ||
(notification.PreviousState == Init &&
r.reservation.State == Failed) {
return
}
err := r.cfg.Store.UpdateReservation(r.ctx, r.reservation)
if err != nil {
r.Errorf("unable to update reservation: %v", err)
}
}
func (r *FSM) Infof(format string, args ...interface{}) {
log.Infof(
"Reservation %x: "+format,
append([]interface{}{r.reservation.ID}, args...)...,
)
}
func (r *FSM) Debugf(format string, args ...interface{}) {
log.Debugf(
"Reservation %x: "+format,
append([]interface{}{r.reservation.ID}, args...)...,
)
}
func (r *FSM) Errorf(format string, args ...interface{}) {
log.Errorf(
"Reservation %x: "+format,
append([]interface{}{r.reservation.ID}, args...)...,
)
}
// isFinalState returns true if the state is a final state.
func isFinalState(state fsm.StateType) bool {
switch state {
case Failed, TimedOut, Spent:
return true
}
return false
}

@ -0,0 +1,33 @@
package reservation
import (
"context"
"fmt"
)
var (
ErrReservationAlreadyExists = fmt.Errorf("reservation already exists")
ErrReservationNotFound = fmt.Errorf("reservation not found")
)
const (
KeyFamily = int32(42068)
DefaultConfTarget = int32(3)
IdLength = 32
)
// Store is the interface that stores the reservations.
type Store interface {
// CreateReservation stores the reservation in the database.
CreateReservation(ctx context.Context, reservation *Reservation) error
// UpdateReservation updates the reservation in the database.
UpdateReservation(ctx context.Context, reservation *Reservation) error
// GetReservation retrieves the reservation from the database.
GetReservation(ctx context.Context, id ID) (*Reservation, error)
// ListReservations lists all existing reservations the client has ever
// made.
ListReservations(ctx context.Context) ([]*Reservation, error)
}

@ -0,0 +1,26 @@
package reservation
import (
"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
)
// Subsystem defines the sub system name of this package.
const Subsystem = "RSRV"
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
UseLogger(build.NewSubLogger(Subsystem, nil))
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}

@ -0,0 +1,271 @@
package reservation
import (
"context"
"fmt"
"sync"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop/fsm"
reservationrpc "github.com/lightninglabs/loop/swapserverrpc"
)
// Manager manages the reservation state machines.
type Manager struct {
// cfg contains all the services that the reservation manager needs to
// operate.
cfg *Config
// activeReservations contains all the active reservationsFSMs.
activeReservations map[ID]*FSM
sync.Mutex
}
// NewManager creates a new reservation manager.
func NewManager(cfg *Config) *Manager {
return &Manager{
cfg: cfg,
activeReservations: make(map[ID]*FSM),
}
}
// Run runs the reservation manager.
func (m *Manager) Run(ctx context.Context, height int32) error {
// todo(sputn1ck): recover swaps on startup
log.Debugf("Starting reservation manager")
runCtx, cancel := context.WithCancel(ctx)
defer cancel()
currentHeight := height
err := m.RecoverReservations(runCtx)
if err != nil {
return err
}
newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.
RegisterBlockEpochNtfn(runCtx)
if err != nil {
return err
}
reservationResChan := make(
chan *reservationrpc.ServerReservationNotification,
)
err = m.RegisterReservationNotifications(runCtx, reservationResChan)
if err != nil {
return err
}
for {
select {
case height := <-newBlockChan:
log.Debugf("Received block %v", height)
currentHeight = height
case reservationRes := <-reservationResChan:
log.Debugf("Received reservation %x",
reservationRes.ReservationId)
_, err := m.newReservation(
runCtx, uint32(currentHeight), reservationRes,
)
if err != nil {
return err
}
case err := <-newBlockErrChan:
return err
case <-runCtx.Done():
log.Debugf("Stopping reservation manager")
return nil
}
}
}
// newReservation creates a new reservation from the reservation request.
func (m *Manager) newReservation(ctx context.Context, currentHeight uint32,
req *reservationrpc.ServerReservationNotification) (*FSM, error) {
var reservationID ID
err := reservationID.FromByteSlice(
req.ReservationId,
)
if err != nil {
return nil, err
}
serverKey, err := btcec.ParsePubKey(req.ServerKey)
if err != nil {
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(
ctx, m.cfg,
)
// Add the reservation to the active reservations map.
m.Lock()
m.activeReservations[reservationID] = reservationFSM
m.Unlock()
initContext := &InitReservationContext{
reservationID: reservationID,
serverPubkey: serverKey,
value: btcutil.Amount(req.Value),
expiry: req.Expiry,
heightHint: currentHeight,
}
// Send the init event to the state machine.
go func() {
err = reservationFSM.SendEvent(OnServerRequest, initContext)
if err != nil {
log.Errorf("Error sending init event: %v", err)
}
}()
// We'll now wait for the reservation to be in the state where it is
// waiting to be confirmed.
err = reservationFSM.DefaultObserver.WaitForState(
ctx, 5*time.Second, WaitForConfirmation,
fsm.WithWaitForStateOption(time.Second),
)
if err != nil {
if reservationFSM.LastActionError != nil {
return nil, fmt.Errorf("error waiting for "+
"state: %v, last action error: %v",
err, reservationFSM.LastActionError)
}
return nil, err
}
return reservationFSM, nil
}
// RegisterReservationNotifications registers a new reservation notification
// stream.
func (m *Manager) RegisterReservationNotifications(
ctx context.Context, reservationChan chan *reservationrpc.
ServerReservationNotification) error {
// In order to create a valid lsat we first are going to call
// the FetchL402 method.
err := m.cfg.FetchL402(ctx)
if err != nil {
return err
}
// We'll now subscribe to the reservation notifications.
reservationStream, err := m.cfg.ReservationClient.
ReservationNotificationStream(
ctx, &reservationrpc.ReservationNotificationRequest{},
)
if err != nil {
return err
}
// We'll now start a goroutine that will forward all the reservation
// notifications to the reservationChan.
go func() {
for {
reservationRes, err := reservationStream.Recv()
if err == nil && reservationRes != nil {
log.Debugf("Received reservation %x",
reservationRes.ReservationId)
reservationChan <- reservationRes
continue
}
log.Errorf("Error receiving "+
"reservation: %v", err)
reconnectTimer := time.NewTimer(time.Second * 10)
// If we encounter an error, we'll
// try to reconnect.
for {
select {
case <-ctx.Done():
return
case <-reconnectTimer.C:
err = m.RegisterReservationNotifications(
ctx, reservationChan,
)
if err == nil {
log.Debugf(
"Successfully " +
"reconnected",
)
reconnectTimer.Stop()
// If we were able to
// reconnect, we'll
// return.
return
}
log.Errorf("Error "+
"reconnecting: %v",
err)
reconnectTimer.Reset(
time.Second * 10,
)
}
}
}
}()
return nil
}
// RecoverReservations tries to recover all reservations that are still active
// from the database.
func (m *Manager) RecoverReservations(ctx context.Context) error {
reservations, err := m.cfg.Store.ListReservations(ctx)
if err != nil {
return err
}
for _, reservation := range reservations {
if isFinalState(reservation.State) {
continue
}
log.Debugf("Recovering reservation %x", reservation.ID)
fsmCtx := context.WithValue(ctx, reservation.ID, nil)
reservationFSM := NewFSMFromReservation(
fsmCtx, m.cfg, reservation,
)
m.activeReservations[reservation.ID] = reservationFSM
// As SendEvent can block, we'll start a goroutine to process
// the event.
go func() {
err := reservationFSM.SendEvent(OnRecover, nil)
if err != nil {
log.Errorf("FSM %v Error sending recover "+
"event %v, state: %v",
reservationFSM.reservation.ID, err,
reservationFSM.reservation.State)
}
}()
}
return nil
}
// GetReservations retrieves all reservations from the database.
func (m *Manager) GetReservations(ctx context.Context) ([]*Reservation, error) {
return m.cfg.Store.ListReservations(ctx)
}

@ -0,0 +1,160 @@
package reservation
import (
"context"
"encoding/hex"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
var (
defaultReservationId = mustDecodeID("17cecc61ab4aafebdc0542dabdae0d0cb8907ec1c9c8ae387bc5a3309ca8b600")
)
func TestManager(t *testing.T) {
ctxb, cancel := context.WithCancel(context.Background())
defer cancel()
testContext := newManagerTestContext(t)
// Start the manager.
go func() {
err := testContext.manager.Run(ctxb, testContext.mockLnd.Height)
require.NoError(t, err)
}()
// Create a new reservation.
fsm, err := testContext.manager.newReservation(
ctxb, uint32(testContext.mockLnd.Height),
&swapserverrpc.ServerReservationNotification{
ReservationId: defaultReservationId[:],
Value: uint64(defaultValue),
ServerKey: defaultPubkeyBytes,
Expiry: uint32(testContext.mockLnd.Height) + defaultExpiry,
},
)
require.NoError(t, err)
// We'll expect the spendConfirmation to be sent to the server.
pkScript, err := fsm.reservation.GetPkScript()
require.NoError(t, err)
conf := <-testContext.mockLnd.RegisterConfChannel
require.Equal(t, conf.PkScript, pkScript)
confTx := &wire.MsgTx{
TxOut: []*wire.TxOut{
{
PkScript: pkScript,
},
},
}
// We'll now confirm the spend.
conf.ConfChan <- &chainntnfs.TxConfirmation{
BlockHeight: uint32(testContext.mockLnd.Height),
Tx: confTx,
}
// We'll now expect the reservation to be confirmed.
err = fsm.DefaultObserver.WaitForState(ctxb, 5*time.Second, Confirmed)
require.NoError(t, err)
// We'll now expire the reservation.
err = testContext.mockLnd.NotifyHeight(
testContext.mockLnd.Height + int32(defaultExpiry),
)
require.NoError(t, err)
// We'll now expect the reservation to be expired.
err = fsm.DefaultObserver.WaitForState(ctxb, 5*time.Second, TimedOut)
require.NoError(t, err)
}
// ManagerTestContext is a helper struct that contains all the necessary
// components to test the reservation manager.
type ManagerTestContext struct {
manager *Manager
context test.Context
mockLnd *test.LndMockServices
reservationNotificationChan chan *swapserverrpc.ServerReservationNotification
mockReservationClient *mockReservationClient
}
// newManagerTestContext creates a new test context for the reservation manager.
func newManagerTestContext(t *testing.T) *ManagerTestContext {
mockLnd := test.NewMockLnd()
lndContext := test.NewContext(t, mockLnd)
dbFixture := loopdb.NewTestDB(t)
store := NewSQLStore(dbFixture)
mockReservationClient := new(mockReservationClient)
sendChan := make(chan *swapserverrpc.ServerReservationNotification)
mockReservationClient.On(
"ReservationNotificationStream", mock.Anything, mock.Anything,
mock.Anything,
).Return(
&dummyReservationNotificationServer{
SendChan: sendChan,
}, nil,
)
mockReservationClient.On(
"OpenReservation", mock.Anything, mock.Anything, mock.Anything,
).Return(
&swapserverrpc.ServerOpenReservationResponse{}, nil,
)
cfg := &Config{
Store: store,
Wallet: mockLnd.WalletKit,
ChainNotifier: mockLnd.ChainNotifier,
FetchL402: func(context.Context) error { return nil },
ReservationClient: mockReservationClient,
}
manager := NewManager(cfg)
return &ManagerTestContext{
manager: manager,
context: lndContext,
mockLnd: mockLnd,
mockReservationClient: mockReservationClient,
reservationNotificationChan: sendChan,
}
}
type dummyReservationNotificationServer struct {
grpc.ClientStream
// SendChan is the channel that is used to send notifications.
SendChan chan *swapserverrpc.ServerReservationNotification
}
func (d *dummyReservationNotificationServer) Recv() (
*swapserverrpc.ServerReservationNotification, error) {
return <-d.SendChan, nil
}
func mustDecodeID(id string) ID {
bytes, err := hex.DecodeString(id)
if err != nil {
panic(err)
}
var decoded ID
copy(decoded[:], bytes)
return decoded
}

@ -0,0 +1,137 @@
package reservation
import (
"bytes"
"errors"
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/fsm"
reservation_script "github.com/lightninglabs/loop/instantout/reservation/script"
"github.com/lightningnetwork/lnd/keychain"
)
// ID is a unique identifier for a reservation.
type ID [IdLength]byte
// FromByteSlice creates a reservation id from a byte slice.
func (r *ID) FromByteSlice(b []byte) error {
if len(b) != IdLength {
return fmt.Errorf("reservation id must be 32 bytes, got %d, %x",
len(b), b)
}
copy(r[:], b)
return nil
}
// Reservation holds all the necessary information for the 2-of-2 multisig
// reservation utxo.
type Reservation struct {
// ID is the unique identifier of the reservation.
ID ID
// State is the current state of the reservation.
State fsm.StateType
// ClientPubkey is the client's pubkey.
ClientPubkey *btcec.PublicKey
// ServerPubkey is the server's pubkey.
ServerPubkey *btcec.PublicKey
// Value is the amount of the reservation.
Value btcutil.Amount
// Expiry is the absolute block height at which the reservation expires.
Expiry uint32
// KeyLocator is the key locator of the client's key.
KeyLocator keychain.KeyLocator
// Outpoint is the outpoint of the reservation.
Outpoint *wire.OutPoint
// InitiationHeight is the height at which the reservation was
// initiated.
InitiationHeight int32
// ConfirmationHeight is the height at which the reservation was
// confirmed.
ConfirmationHeight uint32
}
func NewReservation(id ID, serverPubkey, clientPubkey *btcec.PublicKey,
value btcutil.Amount, expiry, heightHint uint32,
keyLocator keychain.KeyLocator) (*Reservation,
error) {
if id == [32]byte{} {
return nil, errors.New("id is empty")
}
if clientPubkey == nil {
return nil, errors.New("client pubkey is nil")
}
if serverPubkey == nil {
return nil, errors.New("server pubkey is nil")
}
if expiry == 0 {
return nil, errors.New("expiry is 0")
}
if value == 0 {
return nil, errors.New("value is 0")
}
if keyLocator.Family == 0 {
return nil, errors.New("key locator family is 0")
}
return &Reservation{
ID: id,
Value: value,
ClientPubkey: clientPubkey,
ServerPubkey: serverPubkey,
KeyLocator: keyLocator,
Expiry: expiry,
InitiationHeight: int32(heightHint),
}, nil
}
// GetPkScript returns the pk script of the reservation.
func (r *Reservation) GetPkScript() ([]byte, error) {
// Now that we have all the required data, we can create the pk script.
pkScript, err := reservation_script.ReservationScript(
r.Expiry, r.ServerPubkey, r.ClientPubkey,
)
if err != nil {
return nil, err
}
return pkScript, nil
}
func (r *Reservation) findReservationOutput(tx *wire.MsgTx) (*wire.OutPoint,
error) {
pkScript, err := r.GetPkScript()
if err != nil {
return nil, err
}
for i, txOut := range tx.TxOut {
if bytes.Equal(txOut.PkScript, pkScript) {
return &wire.OutPoint{
Hash: tx.TxHash(),
Index: uint32(i),
}, nil
}
}
return nil, errors.New("reservation output not found")
}

@ -0,0 +1,17 @@
```mermaid
stateDiagram-v2
[*] --> Init: OnServerRequest
Confirmed
Confirmed --> TimedOut: OnTimedOut
Confirmed --> Confirmed: OnRecover
Failed
Init
Init --> Failed: OnError
Init --> WaitForConfirmation: OnBroadcast
Init --> Failed: OnRecover
TimedOut
WaitForConfirmation
WaitForConfirmation --> WaitForConfirmation: OnRecover
WaitForConfirmation --> Confirmed: OnConfirmed
WaitForConfirmation --> TimedOut: OnTimedOut
```

@ -0,0 +1,298 @@
package reservation
import (
"context"
"database/sql"
"errors"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/keychain"
)
// BaseDB is the interface that contains all the queries generated
// by sqlc for the reservation table.
type BaseDB interface {
// CreateReservation stores the reservation in the database.
CreateReservation(ctx context.Context,
arg sqlc.CreateReservationParams) error
// GetReservation retrieves the reservation from the database.
GetReservation(ctx context.Context,
reservationID []byte) (sqlc.Reservation, error)
// GetReservationUpdates fetches all updates for a reservation.
GetReservationUpdates(ctx context.Context,
reservationID []byte) ([]sqlc.ReservationUpdate, error)
// GetReservations lists all existing reservations the client has ever
// made.
GetReservations(ctx context.Context) ([]sqlc.Reservation, error)
// UpdateReservation inserts a new reservation update.
UpdateReservation(ctx context.Context,
arg sqlc.UpdateReservationParams) error
// ExecTx allows for executing a function in the context of a database
// transaction.
ExecTx(ctx context.Context, txOptions loopdb.TxOptions,
txBody func(*sqlc.Queries) error) error
}
// SQLStore manages the reservations in the database.
type SQLStore struct {
baseDb BaseDB
clock clock.Clock
}
// NewSQLStore creates a new SQLStore.
func NewSQLStore(db BaseDB) *SQLStore {
return &SQLStore{
baseDb: db,
clock: clock.NewDefaultClock(),
}
}
// CreateReservation stores the reservation in the database.
func (r *SQLStore) CreateReservation(ctx context.Context,
reservation *Reservation) error {
args := sqlc.CreateReservationParams{
ReservationID: reservation.ID[:],
ClientPubkey: reservation.ClientPubkey.SerializeCompressed(),
ServerPubkey: reservation.ServerPubkey.SerializeCompressed(),
Expiry: int32(reservation.Expiry),
Value: int64(reservation.Value),
ClientKeyFamily: int32(reservation.KeyLocator.Family),
ClientKeyIndex: int32(reservation.KeyLocator.Index),
InitiationHeight: reservation.InitiationHeight,
}
updateArgs := sqlc.InsertReservationUpdateParams{
ReservationID: reservation.ID[:],
UpdateTimestamp: r.clock.Now().UTC(),
UpdateState: string(reservation.State),
}
return r.baseDb.ExecTx(ctx, &loopdb.SqliteTxOptions{},
func(q *sqlc.Queries) error {
err := q.CreateReservation(ctx, args)
if err != nil {
return err
}
return q.InsertReservationUpdate(ctx, updateArgs)
})
}
// UpdateReservation updates the reservation in the database.
func (r *SQLStore) UpdateReservation(ctx context.Context,
reservation *Reservation) error {
var txHash []byte
var outIndex sql.NullInt32
if reservation.Outpoint != nil {
txHash = reservation.Outpoint.Hash[:]
outIndex = sql.NullInt32{
Int32: int32(reservation.Outpoint.Index),
Valid: true,
}
}
insertUpdateArgs := sqlc.InsertReservationUpdateParams{
ReservationID: reservation.ID[:],
UpdateTimestamp: r.clock.Now().UTC(),
UpdateState: string(reservation.State),
}
updateArgs := sqlc.UpdateReservationParams{
ReservationID: reservation.ID[:],
TxHash: txHash,
OutIndex: outIndex,
ConfirmationHeight: marshalSqlNullInt32(
int32(reservation.ConfirmationHeight),
),
}
return r.baseDb.ExecTx(ctx, &loopdb.SqliteTxOptions{},
func(q *sqlc.Queries) error {
err := q.UpdateReservation(ctx, updateArgs)
if err != nil {
return err
}
return q.InsertReservationUpdate(ctx, insertUpdateArgs)
})
}
// GetReservation retrieves the reservation from the database.
func (r *SQLStore) GetReservation(ctx context.Context,
reservationId ID) (*Reservation, error) {
var reservation *Reservation
err := r.baseDb.ExecTx(ctx, loopdb.NewSqlReadOpts(),
func(q *sqlc.Queries) error {
var err error
reservationRow, err := q.GetReservation(
ctx, reservationId[:],
)
if err != nil {
return err
}
reservationUpdates, err := q.GetReservationUpdates(
ctx, reservationId[:],
)
if err != nil {
return err
}
if len(reservationUpdates) == 0 {
return errors.New("no reservation updates")
}
reservation, err = sqlReservationToReservation(
reservationRow,
reservationUpdates[len(reservationUpdates)-1],
)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return reservation, nil
}
// ListReservations lists all existing reservations the client has ever made.
func (r *SQLStore) ListReservations(ctx context.Context) ([]*Reservation,
error) {
var result []*Reservation
err := r.baseDb.ExecTx(ctx, loopdb.NewSqlReadOpts(),
func(q *sqlc.Queries) error {
var err error
reservations, err := q.GetReservations(ctx)
if err != nil {
return err
}
for _, reservation := range reservations {
reservationUpdates, err := q.GetReservationUpdates(
ctx, reservation.ReservationID,
)
if err != nil {
return err
}
if len(reservationUpdates) == 0 {
return errors.New(
"no reservation updates",
)
}
res, err := sqlReservationToReservation(
reservation, reservationUpdates[len(
reservationUpdates,
)-1],
)
if err != nil {
return err
}
result = append(result, res)
}
return nil
})
if err != nil {
return nil, err
}
return result, nil
}
// sqlReservationToReservation converts a sql reservation to a reservation.
func sqlReservationToReservation(row sqlc.Reservation,
lastUpdate sqlc.ReservationUpdate) (*Reservation,
error) {
id := ID{}
err := id.FromByteSlice(row.ReservationID)
if err != nil {
return nil, err
}
clientPubkey, err := btcec.ParsePubKey(row.ClientPubkey)
if err != nil {
return nil, err
}
serverPubkey, err := btcec.ParsePubKey(row.ServerPubkey)
if err != nil {
return nil, err
}
var txHash *chainhash.Hash
if row.TxHash != nil {
txHash, err = chainhash.NewHash(row.TxHash)
if err != nil {
return nil, err
}
}
var outpoint *wire.OutPoint
if row.OutIndex.Valid {
outpoint = wire.NewOutPoint(
txHash, uint32(unmarshalSqlNullInt32(row.OutIndex)),
)
}
return &Reservation{
ID: id,
ClientPubkey: clientPubkey,
ServerPubkey: serverPubkey,
Expiry: uint32(row.Expiry),
Value: btcutil.Amount(row.Value),
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamily(row.ClientKeyFamily),
Index: uint32(row.ClientKeyIndex),
},
Outpoint: outpoint,
ConfirmationHeight: uint32(
unmarshalSqlNullInt32(row.ConfirmationHeight),
),
InitiationHeight: row.InitiationHeight,
State: fsm.StateType(lastUpdate.UpdateState),
}, nil
}
// marshalSqlNullInt32 converts an int32 to a sql.NullInt32.
func marshalSqlNullInt32(i int32) sql.NullInt32 {
return sql.NullInt32{
Int32: i,
Valid: i != 0,
}
}
// unmarshalSqlNullInt32 converts a sql.NullInt32 to an int32.
func unmarshalSqlNullInt32(i sql.NullInt32) int32 {
if i.Valid {
return i.Int32
}
return 0
}

@ -0,0 +1,96 @@
package reservation
import (
"context"
"crypto/rand"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/stretchr/testify/require"
)
// TestSqlStore tests the basic functionality of the SQLStore.
func TestSqlStore(t *testing.T) {
ctxb := context.Background()
testDb := loopdb.NewTestDB(t)
defer testDb.Close()
store := NewSQLStore(testDb)
// Create a reservation and store it.
reservation := &Reservation{
ID: getRandomReservationID(),
State: fsm.StateType("init"),
ClientPubkey: defaultPubkey,
ServerPubkey: defaultPubkey,
Value: 100,
Expiry: 100,
KeyLocator: keychain.KeyLocator{
Family: 1,
Index: 1,
},
}
err := store.CreateReservation(ctxb, reservation)
require.NoError(t, err)
// Get the reservation and compare it.
reservation2, err := store.GetReservation(ctxb, reservation.ID)
require.NoError(t, err)
require.Equal(t, reservation, reservation2)
// Update the reservation and compare it.
reservation.State = fsm.StateType("state2")
err = store.UpdateReservation(ctxb, reservation)
require.NoError(t, err)
reservation2, err = store.GetReservation(ctxb, reservation.ID)
require.NoError(t, err)
require.Equal(t, reservation, reservation2)
// Add an outpoint to the reservation and compare it.
reservation.Outpoint = &wire.OutPoint{
Hash: chainhash.Hash{0x01},
Index: 0,
}
reservation.State = Confirmed
err = store.UpdateReservation(ctxb, reservation)
require.NoError(t, err)
reservation2, err = store.GetReservation(ctxb, reservation.ID)
require.NoError(t, err)
require.Equal(t, reservation, reservation2)
// Add a second reservation.
reservation3 := &Reservation{
ID: getRandomReservationID(),
State: fsm.StateType("init"),
ClientPubkey: defaultPubkey,
ServerPubkey: defaultPubkey,
Value: 99,
Expiry: 100,
KeyLocator: keychain.KeyLocator{
Family: 1,
Index: 1,
},
}
err = store.CreateReservation(ctxb, reservation3)
require.NoError(t, err)
reservations, err := store.ListReservations(ctxb)
require.NoError(t, err)
require.Equal(t, 2, len(reservations))
}
// getRandomReservationID generates a random reservation ID.
func getRandomReservationID() ID {
var id ID
rand.Read(id[:]) // nolint: errcheck
return id
}

@ -168,7 +168,7 @@ type Config struct {
TotalPaymentTimeout time.Duration `long:"totalpaymenttimeout" description:"The timeout to use for off-chain payments."`
MaxPaymentRetries int `long:"maxpaymentretries" description:"The maximum number of times an off-chain payment may be retried."`
EnableExperimental bool `long:"experimental" description:"Enable experimental features: taproot HTLCs and MuSig2 loop out sweeps."`
EnableExperimental bool `long:"experimental" description:"Enable experimental features: reservations"`
Lnd *lndConfig `group:"lnd" namespace:"lnd"`

@ -18,7 +18,11 @@ import (
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/loopd/perms"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/instantout/reservation"
loop_looprpc "github.com/lightninglabs/loop/looprpc"
loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
@ -62,6 +66,10 @@ type Daemon struct {
// same process.
swapClientServer
// reservationManager is the manager that handles all reservation state
// machines.
reservationManager *reservation.Manager
// ErrChan is an error channel that users of the Daemon struct must use
// to detect runtime errors and also whether a shutdown is fully
// completed.
@ -226,7 +234,7 @@ func (d *Daemon) startWebServers() error {
grpc.UnaryInterceptor(unaryInterceptor),
grpc.StreamInterceptor(streamInterceptor),
)
looprpc.RegisterSwapClientServer(d.grpcServer, d)
loop_looprpc.RegisterSwapClientServer(d.grpcServer, d)
// Register our debug server if it is compiled in.
d.registerDebugServer()
@ -286,7 +294,7 @@ func (d *Daemon) startWebServers() error {
restProxyDest, "[::]", "[::1]", 1,
)
}
err = looprpc.RegisterSwapClientHandlerFromEndpoint(
err = loop_looprpc.RegisterSwapClientHandlerFromEndpoint(
ctx, mux, restProxyDest, proxyOpts,
)
if err != nil {
@ -399,7 +407,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
return err
}
swapDb, _, err := openDatabase(d.cfg, chainParams)
swapDb, baseDb, err := openDatabase(d.cfg, chainParams)
if err != nil {
return err
}
@ -413,6 +421,15 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
}
d.clientCleanup = clientCleanup
// Create a reservation server client.
reservationClient := loop_swaprpc.NewReservationServiceClient(
swapClient.Conn,
)
// Both the client RPC server and the swap server client should stop
// on main context cancel. So we create it early and pass it down.
d.mainCtx, d.mainCtxCancel = context.WithCancel(context.Background())
// Add our debug permissions to our main set of required permissions
// if compiled in.
for endpoint, perm := range debugRequiredPermissions {
@ -466,17 +483,34 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
}
}
// Create the reservation rpc server.
if d.cfg.EnableExperimental {
reservationStore := reservation.NewSQLStore(baseDb)
reservationConfig := &reservation.Config{
Store: reservationStore,
Wallet: d.lnd.WalletKit,
ChainNotifier: d.lnd.ChainNotifier,
ReservationClient: reservationClient,
FetchL402: swapClient.Server.FetchL402,
}
d.reservationManager = reservation.NewManager(
reservationConfig,
)
}
// Now finally fully initialize the swap client RPC server instance.
d.swapClientServer = swapClientServer{
config: d.cfg,
network: lndclient.Network(d.cfg.Network),
impl: swapClient,
liquidityMgr: getLiquidityManager(swapClient),
lnd: &d.lnd.LndServices,
swaps: make(map[lntypes.Hash]loop.SwapInfo),
subscribers: make(map[int]chan<- interface{}),
statusChan: make(chan loop.SwapInfo),
mainCtx: d.mainCtx,
config: d.cfg,
network: lndclient.Network(d.cfg.Network),
impl: swapClient,
liquidityMgr: getLiquidityManager(swapClient),
lnd: &d.lnd.LndServices,
swaps: make(map[lntypes.Hash]loop.SwapInfo),
subscribers: make(map[int]chan<- interface{}),
statusChan: make(chan loop.SwapInfo),
mainCtx: d.mainCtx,
reservationManager: d.reservationManager,
}
// Retrieve all currently existing swaps from the database.
@ -543,6 +577,32 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
log.Info("Liquidity manager stopped")
}()
// Start the reservation manager.
if d.reservationManager != nil {
d.wg.Add(1)
go func() {
defer d.wg.Done()
// We need to know the current block height to properly
// initialize the reservation manager.
getInfo, err := d.lnd.Client.GetInfo(d.mainCtx)
if err != nil {
d.internalErrChan <- err
return
}
log.Info("Starting reservation manager")
defer log.Info("Reservation manager stopped")
err = d.reservationManager.Run(
d.mainCtx, int32(getInfo.BlockHeight),
)
if err != nil && !errors.Is(err, context.Canceled) {
d.internalErrChan <- err
}
}()
}
// Last, start our internal error handler. This will return exactly one
// error or nil on the main error channel to inform the caller that
// something went wrong or that shutdown is complete. We don't add to

@ -6,6 +6,7 @@ import (
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/liquidity"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightningnetwork/lnd"
@ -38,6 +39,9 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) {
root, liquidity.Subsystem, intercept, liquidity.UseLogger,
)
lnd.AddSubLogger(root, fsm.Subsystem, intercept, fsm.UseLogger)
lnd.AddSubLogger(
root, reservation.Subsystem, intercept, reservation.UseLogger,
)
}
// genSubLogger creates a logger for a subsystem. We provide an instance of

@ -96,4 +96,8 @@ var RequiredPermissions = map[string][]bakery.Op{
Entity: "loop",
Action: "in",
}},
"/looprpc.SwapClient/ListReservations": {{
Entity: "reservation",
Action: "read",
}},
}

@ -18,6 +18,7 @@ import (
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/liquidity"
"github.com/lightninglabs/loop/loopdb"
@ -74,17 +75,18 @@ type swapClientServer struct {
clientrpc.UnimplementedSwapClientServer
clientrpc.UnimplementedDebugServer
config *Config
network lndclient.Network
impl *loop.Client
liquidityMgr *liquidity.Manager
lnd *lndclient.LndServices
swaps map[lntypes.Hash]loop.SwapInfo
subscribers map[int]chan<- interface{}
statusChan chan loop.SwapInfo
nextSubscriberID int
swapsLock sync.Mutex
mainCtx context.Context
config *Config
network lndclient.Network
impl *loop.Client
liquidityMgr *liquidity.Manager
lnd *lndclient.LndServices
reservationManager *reservation.Manager
swaps map[lntypes.Hash]loop.SwapInfo
subscribers map[int]chan<- interface{}
statusChan chan loop.SwapInfo
nextSubscriberID int
swapsLock sync.Mutex
mainCtx context.Context
}
// LoopOut initiates a loop out swap with the given parameters. The call returns
@ -1138,6 +1140,29 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context,
return resp, nil
}
// ListReservations lists all existing reservations the client has ever made.
func (s *swapClientServer) ListReservations(ctx context.Context,
_ *clientrpc.ListReservationsRequest) (
*clientrpc.ListReservationsResponse, error) {
if s.reservationManager == nil {
return nil, status.Error(codes.Unimplemented,
"Restart loop with --experimental")
}
reservations, err := s.reservationManager.GetReservations(
ctx,
)
if err != nil {
return nil, err
}
return &clientrpc.ListReservationsResponse{
Reservations: ToClientReservations(
reservations,
),
}, nil
}
func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) {
switch reason {
case liquidity.ReasonNone:
@ -1397,3 +1422,40 @@ func getPublicationDeadline(unixTimestamp uint64) time.Time {
return time.Unix(int64(unixTimestamp), 0)
}
}
// ToClientReservations converts a slice of server
// reservations to a slice of client reservations.
func ToClientReservations(
res []*reservation.Reservation) []*clientrpc.ClientReservation {
var result []*clientrpc.ClientReservation
for _, r := range res {
result = append(result, toClientReservation(r))
}
return result
}
// toClientReservation converts a server reservation to a
// client reservation.
func toClientReservation(
res *reservation.Reservation) *clientrpc.ClientReservation {
var (
txid []byte
vout uint32
)
if res.Outpoint != nil {
txid = res.Outpoint.Hash[:]
vout = res.Outpoint.Index
}
return &clientrpc.ClientReservation{
ReservationId: res.ID[:],
State: string(res.State),
Amount: uint64(res.Value),
TxId: txid,
Vout: vout,
Expiry: res.Expiry,
}
}

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS reservation_updates;
DROP TABLE IF EXISTS reservations;

@ -0,0 +1,56 @@
-- reservations contains all the information about a reservation.
CREATE TABLE IF NOT EXISTS reservations (
-- id is the auto incrementing primary key.
id INTEGER PRIMARY KEY,
-- reservation_id is the unique identifier for the reservation.
reservation_id BLOB NOT NULL UNIQUE,
-- client_pubkey is the public key of the client.
client_pubkey BLOB NOT NULL,
-- server_pubkey is the public key of the server.
server_pubkey BLOB NOT NULL,
-- expiry is the absolute expiry height of the reservation.
expiry INTEGER NOT NULL,
-- value is the value of the reservation.
value BIGINT NOT NULL,
-- client_key_family is the key family of the client.
client_key_family INTEGER NOT NULL,
-- client_key_index is the key index of the client.
client_key_index INTEGER NOT NULL,
-- initiation_height is the height at which the reservation was initiated.
initiation_height INTEGER NOT NULL,
-- tx_hash is the hash of the transaction that created the reservation.
tx_hash BLOB,
-- out_index is the index of the output that created the reservation.
out_index INTEGER,
-- confirmation_height is the height at which the reservation was confirmed.
confirmation_height INTEGER
);
CREATE INDEX IF NOT EXISTS reservations_reservation_id_idx ON reservations(reservation_id);
-- reservation_updates contains all the updates to a reservation.
CREATE TABLE IF NOT EXISTS reservation_updates (
-- id is the auto incrementing primary key.
id INTEGER PRIMARY KEY,
-- reservation_id is the unique identifier for the reservation.
reservation_id BLOB NOT NULL REFERENCES reservations(reservation_id),
-- update_state is the state of the reservation at the time of the update.
update_state TEXT NOT NULL,
-- update_timestamp is the timestamp of the update.
update_timestamp TIMESTAMP NOT NULL
);

@ -5,6 +5,7 @@
package sqlc
import (
"database/sql"
"time"
)
@ -43,6 +44,28 @@ type LoopoutSwap struct {
PublicationDeadline time.Time
}
type Reservation struct {
ID int32
ReservationID []byte
ClientPubkey []byte
ServerPubkey []byte
Expiry int32
Value int64
ClientKeyFamily int32
ClientKeyIndex int32
InitiationHeight int32
TxHash []byte
OutIndex sql.NullInt32
ConfirmationHeight sql.NullInt32
}
type ReservationUpdate struct {
ID int32
ReservationID []byte
UpdateState string
UpdateTimestamp time.Time
}
type Swap struct {
ID int32
SwapHash []byte

@ -9,17 +9,23 @@ import (
)
type Querier interface {
CreateReservation(ctx context.Context, arg CreateReservationParams) error
FetchLiquidityParams(ctx context.Context) ([]byte, error)
GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopInSwapRow, error)
GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, error)
GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopOutSwapRow, error)
GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, error)
GetReservation(ctx context.Context, reservationID []byte) (Reservation, error)
GetReservationUpdates(ctx context.Context, reservationID []byte) ([]ReservationUpdate, error)
GetReservations(ctx context.Context) ([]Reservation, error)
GetSwapUpdates(ctx context.Context, swapHash []byte) ([]SwapUpdate, error)
InsertHtlcKeys(ctx context.Context, arg InsertHtlcKeysParams) error
InsertLoopIn(ctx context.Context, arg InsertLoopInParams) error
InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) error
InsertReservationUpdate(ctx context.Context, arg InsertReservationUpdateParams) error
InsertSwap(ctx context.Context, arg InsertSwapParams) error
InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdateParams) error
UpdateReservation(ctx context.Context, arg UpdateReservationParams) error
UpsertLiquidityParams(ctx context.Context, params []byte) error
}

@ -0,0 +1,66 @@
-- name: CreateReservation :exec
INSERT INTO reservations (
reservation_id,
client_pubkey,
server_pubkey,
expiry,
value,
client_key_family,
client_key_index,
initiation_height
) VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8
);
-- name: UpdateReservation :exec
UPDATE reservations
SET
tx_hash = $2,
out_index = $3,
confirmation_height = $4
WHERE
reservations.reservation_id = $1;
-- name: InsertReservationUpdate :exec
INSERT INTO reservation_updates (
reservation_id,
update_state,
update_timestamp
) VALUES (
$1,
$2,
$3
);
-- name: GetReservation :one
SELECT
*
FROM
reservations
WHERE
reservation_id = $1;
-- name: GetReservations :many
SELECT
*
FROM
reservations
ORDER BY
id ASC;
-- name: GetReservationUpdates :many
SELECT
reservation_updates.*
FROM
reservation_updates
WHERE
reservation_id = $1
ORDER BY
id ASC;

@ -0,0 +1,222 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.17.2
// source: reservations.sql
package sqlc
import (
"context"
"database/sql"
"time"
)
const createReservation = `-- name: CreateReservation :exec
INSERT INTO reservations (
reservation_id,
client_pubkey,
server_pubkey,
expiry,
value,
client_key_family,
client_key_index,
initiation_height
) VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8
)
`
type CreateReservationParams struct {
ReservationID []byte
ClientPubkey []byte
ServerPubkey []byte
Expiry int32
Value int64
ClientKeyFamily int32
ClientKeyIndex int32
InitiationHeight int32
}
func (q *Queries) CreateReservation(ctx context.Context, arg CreateReservationParams) error {
_, err := q.db.ExecContext(ctx, createReservation,
arg.ReservationID,
arg.ClientPubkey,
arg.ServerPubkey,
arg.Expiry,
arg.Value,
arg.ClientKeyFamily,
arg.ClientKeyIndex,
arg.InitiationHeight,
)
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
FROM
reservations
WHERE
reservation_id = $1
`
func (q *Queries) GetReservation(ctx context.Context, reservationID []byte) (Reservation, error) {
row := q.db.QueryRowContext(ctx, getReservation, reservationID)
var i Reservation
err := row.Scan(
&i.ID,
&i.ReservationID,
&i.ClientPubkey,
&i.ServerPubkey,
&i.Expiry,
&i.Value,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.InitiationHeight,
&i.TxHash,
&i.OutIndex,
&i.ConfirmationHeight,
)
return i, err
}
const getReservationUpdates = `-- name: GetReservationUpdates :many
SELECT
reservation_updates.id, reservation_updates.reservation_id, reservation_updates.update_state, reservation_updates.update_timestamp
FROM
reservation_updates
WHERE
reservation_id = $1
ORDER BY
id ASC
`
func (q *Queries) GetReservationUpdates(ctx context.Context, reservationID []byte) ([]ReservationUpdate, error) {
rows, err := q.db.QueryContext(ctx, getReservationUpdates, reservationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ReservationUpdate
for rows.Next() {
var i ReservationUpdate
if err := rows.Scan(
&i.ID,
&i.ReservationID,
&i.UpdateState,
&i.UpdateTimestamp,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const 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
FROM
reservations
ORDER BY
id ASC
`
func (q *Queries) GetReservations(ctx context.Context) ([]Reservation, error) {
rows, err := q.db.QueryContext(ctx, getReservations)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Reservation
for rows.Next() {
var i Reservation
if err := rows.Scan(
&i.ID,
&i.ReservationID,
&i.ClientPubkey,
&i.ServerPubkey,
&i.Expiry,
&i.Value,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.InitiationHeight,
&i.TxHash,
&i.OutIndex,
&i.ConfirmationHeight,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertReservationUpdate = `-- name: InsertReservationUpdate :exec
INSERT INTO reservation_updates (
reservation_id,
update_state,
update_timestamp
) VALUES (
$1,
$2,
$3
)
`
type InsertReservationUpdateParams struct {
ReservationID []byte
UpdateState string
UpdateTimestamp time.Time
}
func (q *Queries) InsertReservationUpdate(ctx context.Context, arg InsertReservationUpdateParams) error {
_, err := q.db.ExecContext(ctx, insertReservationUpdate, arg.ReservationID, arg.UpdateState, arg.UpdateTimestamp)
return err
}
const updateReservation = `-- name: UpdateReservation :exec
UPDATE reservations
SET
tx_hash = $2,
out_index = $3,
confirmation_height = $4
WHERE
reservations.reservation_id = $1
`
type UpdateReservationParams struct {
ReservationID []byte
TxHash []byte
OutIndex sql.NullInt32
ConfirmationHeight sql.NullInt32
}
func (q *Queries) UpdateReservation(ctx context.Context, arg UpdateReservationParams) error {
_, err := q.db.ExecContext(ctx, updateReservation,
arg.ReservationID,
arg.TxHash,
arg.OutIndex,
arg.ConfirmationHeight,
)
return err
}

@ -468,9 +468,9 @@ type ListSwapsFilter_SwapTypeFilter int32
const (
// ANY indicates that no filter is applied.
ListSwapsFilter_ANY ListSwapsFilter_SwapTypeFilter = 0
// LOOP_OUT indicates an loop out swap (off-chain to on-chain)
// LOOP_OUT indicates an loop out swap (off-chain to on-chain).
ListSwapsFilter_LOOP_OUT ListSwapsFilter_SwapTypeFilter = 1
// LOOP_IN indicates a loop in swap (on-chain to off-chain)
// LOOP_IN indicates a loop in swap (on-chain to off-chain).
ListSwapsFilter_LOOP_IN ListSwapsFilter_SwapTypeFilter = 2
)
@ -3390,6 +3390,192 @@ func (*AbandonSwapResponse) Descriptor() ([]byte, []int) {
return file_client_proto_rawDescGZIP(), []int{32}
}
type ListReservationsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ListReservationsRequest) Reset() {
*x = ListReservationsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_client_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListReservationsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListReservationsRequest) ProtoMessage() {}
func (x *ListReservationsRequest) ProtoReflect() protoreflect.Message {
mi := &file_client_proto_msgTypes[33]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListReservationsRequest.ProtoReflect.Descriptor instead.
func (*ListReservationsRequest) Descriptor() ([]byte, []int) {
return file_client_proto_rawDescGZIP(), []int{33}
}
type ListReservationsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
//
//The list of all currently known reservations and their status.
Reservations []*ClientReservation `protobuf:"bytes,1,rep,name=reservations,proto3" json:"reservations,omitempty"`
}
func (x *ListReservationsResponse) Reset() {
*x = ListReservationsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_client_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListReservationsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListReservationsResponse) ProtoMessage() {}
func (x *ListReservationsResponse) ProtoReflect() protoreflect.Message {
mi := &file_client_proto_msgTypes[34]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListReservationsResponse.ProtoReflect.Descriptor instead.
func (*ListReservationsResponse) Descriptor() ([]byte, []int) {
return file_client_proto_rawDescGZIP(), []int{34}
}
func (x *ListReservationsResponse) GetReservations() []*ClientReservation {
if x != nil {
return x.Reservations
}
return nil
}
type ClientReservation struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
//
//The reservation id that identifies this reservation.
ReservationId []byte `protobuf:"bytes,1,opt,name=reservation_id,json=reservationId,proto3" json:"reservation_id,omitempty"`
//
//The state the reservation is in.
State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"`
//
//The amount that the reservation is for.
Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"`
//
//The transaction id of the reservation.
TxId []byte `protobuf:"bytes,4,opt,name=tx_id,json=txId,proto3" json:"tx_id,omitempty"`
//
//The vout of the reservation.
Vout uint32 `protobuf:"varint,5,opt,name=vout,proto3" json:"vout,omitempty"`
//
//The expiry of the reservation.
Expiry uint32 `protobuf:"varint,6,opt,name=expiry,proto3" json:"expiry,omitempty"`
}
func (x *ClientReservation) Reset() {
*x = ClientReservation{}
if protoimpl.UnsafeEnabled {
mi := &file_client_proto_msgTypes[35]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ClientReservation) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ClientReservation) ProtoMessage() {}
func (x *ClientReservation) ProtoReflect() protoreflect.Message {
mi := &file_client_proto_msgTypes[35]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ClientReservation.ProtoReflect.Descriptor instead.
func (*ClientReservation) Descriptor() ([]byte, []int) {
return file_client_proto_rawDescGZIP(), []int{35}
}
func (x *ClientReservation) GetReservationId() []byte {
if x != nil {
return x.ReservationId
}
return nil
}
func (x *ClientReservation) GetState() string {
if x != nil {
return x.State
}
return ""
}
func (x *ClientReservation) GetAmount() uint64 {
if x != nil {
return x.Amount
}
return 0
}
func (x *ClientReservation) GetTxId() []byte {
if x != nil {
return x.TxId
}
return nil
}
func (x *ClientReservation) GetVout() uint32 {
if x != nil {
return x.Vout
}
return 0
}
func (x *ClientReservation) GetExpiry() uint32 {
if x != nil {
return x.Expiry
}
return 0
}
var File_client_proto protoreflect.FileDescriptor
var file_client_proto_rawDesc = []byte{
@ -3805,142 +3991,166 @@ var file_client_proto_rawDesc = []byte{
0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e,
0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x15,
0x0a, 0x13, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x5f,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12,
0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59,
0x10, 0x01, 0x2a, 0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c,
0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73, 0x0a, 0x09, 0x53, 0x77, 0x61,
0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41,
0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47,
0x45, 0x5f, 0x52, 0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e,
0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x02,
0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x0a, 0x0a,
0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56,
0x4f, 0x49, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x2a, 0xbe,
0x02, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53,
0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49,
0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x43,
0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52,
0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54,
0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f,
0x55, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49,
0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x46,
0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45,
0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x46, 0x41, 0x49,
0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f,
0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x1c,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73,
0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x22, 0x5a, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0c,
0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c,
0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa9, 0x01, 0x0a,
0x11, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65,
0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04,
0x76, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x76, 0x6f, 0x75, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x2a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x44, 0x44, 0x52, 0x45,
0x53, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
0x00, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42,
0x4b, 0x45, 0x59, 0x10, 0x01, 0x2a, 0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70,
0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x00, 0x12,
0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73, 0x0a, 0x09,
0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x49,
0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50, 0x52, 0x45, 0x49,
0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12,
0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45,
0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03,
0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f,
0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10,
0x05, 0x2a, 0xbe, 0x02, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17,
0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f,
0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49,
0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45,
0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49,
0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55,
0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46,
0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c,
0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, 0x12, 0x31, 0x0a, 0x2d,
0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f,
0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49,
0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x46,
0x49, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x08, 0x2a,
0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65,
0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x01,
0x2a, 0xa6, 0x03, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12,
0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55,
0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x4e,
0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45,
0x50, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x45,
0x4c, 0x41, 0x50, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48,
0x54, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53,
0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x05, 0x12, 0x19, 0x0a,
0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, 0x4e,
0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x10, 0x07,
0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x4f, 0x46, 0x46, 0x10,
0x08, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09, 0x12, 0x17, 0x0a, 0x13, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f,
0x49, 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x4f, 0x4b,
0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49,
0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x55, 0x54, 0x4f, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46,
0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0d, 0x32, 0xca, 0x08, 0x0a, 0x0a, 0x53, 0x77,
0x61, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, 0x70,
0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f,
0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x16, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07,
0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x53,
0x77, 0x61, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77,
0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x53,
0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f,
0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61,
0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f,
0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f,
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49,
0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52,
0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10,
0x06, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, 0x12,
0x31, 0x0a, 0x2d, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x43,
0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45,
0x10, 0x08, 0x2a, 0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52,
0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c,
0x44, 0x10, 0x01, 0x2a, 0xa6, 0x03, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73,
0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45,
0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12,
0x1a, 0x0a, 0x16, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53,
0x57, 0x45, 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45,
0x54, 0x5f, 0x45, 0x4c, 0x41, 0x50, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c,
0x49, 0x47, 0x48, 0x54, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x05,
0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x4d, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41,
0x59, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53,
0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x4f,
0x46, 0x46, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09, 0x12, 0x17,
0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f,
0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59,
0x5f, 0x4f, 0x4b, 0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53, 0x55,
0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x55,
0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e,
0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0d, 0x32, 0xa3, 0x09, 0x0a,
0x0a, 0x53, 0x77, 0x61, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x4c,
0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e,
0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49,
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x39, 0x0a, 0x07, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77,
0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69,
0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73,
0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39,
0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x41, 0x62, 0x61,
0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65,
0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65,
0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74,
0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f,
0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, 0x6f,
0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x72,
0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72,
0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b,
0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f,
0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12,
0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66,
0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69,
0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50,
0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79,
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x53, 0x65,
0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72,
0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65,
0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e,
0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a,
0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e,
0x66, 0x6f, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75,
0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69,
0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64,
0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5d, 0x0a,
0x12, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72,
0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65,
0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73,
0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69,
0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67,
0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61,
0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61,
0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c,
0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77,
0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x4c, 0x69, 0x73,
0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65,
0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c,
0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
@ -3956,7 +4166,7 @@ func file_client_proto_rawDescGZIP() []byte {
}
var file_client_proto_enumTypes = make([]protoimpl.EnumInfo, 7)
var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 33)
var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 36)
var file_client_proto_goTypes = []interface{}{
(AddressType)(0), // 0: looprpc.AddressType
(SwapType)(0), // 1: looprpc.SwapType
@ -3998,19 +4208,22 @@ var file_client_proto_goTypes = []interface{}{
(*SuggestSwapsResponse)(nil), // 37: looprpc.SuggestSwapsResponse
(*AbandonSwapRequest)(nil), // 38: looprpc.AbandonSwapRequest
(*AbandonSwapResponse)(nil), // 39: looprpc.AbandonSwapResponse
(*swapserverrpc.RouteHint)(nil), // 40: looprpc.RouteHint
(*ListReservationsRequest)(nil), // 40: looprpc.ListReservationsRequest
(*ListReservationsResponse)(nil), // 41: looprpc.ListReservationsResponse
(*ClientReservation)(nil), // 42: looprpc.ClientReservation
(*swapserverrpc.RouteHint)(nil), // 43: looprpc.RouteHint
}
var file_client_proto_depIdxs = []int32{
0, // 0: looprpc.LoopOutRequest.account_addr_type:type_name -> looprpc.AddressType
40, // 1: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint
43, // 1: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint
1, // 2: looprpc.SwapStatus.type:type_name -> looprpc.SwapType
2, // 3: looprpc.SwapStatus.state:type_name -> looprpc.SwapState
3, // 4: looprpc.SwapStatus.failure_reason:type_name -> looprpc.FailureReason
13, // 5: looprpc.ListSwapsRequest.list_swap_filter:type_name -> looprpc.ListSwapsFilter
6, // 6: looprpc.ListSwapsFilter.swap_type:type_name -> looprpc.ListSwapsFilter.SwapTypeFilter
11, // 7: looprpc.ListSwapsResponse.swaps:type_name -> looprpc.SwapStatus
40, // 8: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint
40, // 9: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint
43, // 8: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint
43, // 9: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint
26, // 10: looprpc.TokensResponse.tokens:type_name -> looprpc.LsatToken
27, // 11: looprpc.GetInfoResponse.loop_out_stats:type_name -> looprpc.LoopStats
27, // 12: looprpc.GetInfoResponse.loop_in_stats:type_name -> looprpc.LoopStats
@ -4023,43 +4236,46 @@ var file_client_proto_depIdxs = []int32{
7, // 19: looprpc.SuggestSwapsResponse.loop_out:type_name -> looprpc.LoopOutRequest
8, // 20: looprpc.SuggestSwapsResponse.loop_in:type_name -> looprpc.LoopInRequest
36, // 21: looprpc.SuggestSwapsResponse.disqualified:type_name -> looprpc.Disqualified
7, // 22: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest
8, // 23: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest
10, // 24: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest
12, // 25: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest
15, // 26: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest
38, // 27: looprpc.SwapClient.AbandonSwap:input_type -> looprpc.AbandonSwapRequest
16, // 28: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest
19, // 29: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest
16, // 30: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest
19, // 31: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest
22, // 32: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest
24, // 33: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest
28, // 34: looprpc.SwapClient.GetInfo:input_type -> looprpc.GetInfoRequest
30, // 35: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest
33, // 36: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest
35, // 37: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest
9, // 38: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse
9, // 39: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse
11, // 40: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus
14, // 41: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse
11, // 42: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus
39, // 43: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse
18, // 44: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse
21, // 45: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse
17, // 46: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse
20, // 47: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse
23, // 48: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse
25, // 49: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse
29, // 50: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse
31, // 51: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters
34, // 52: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse
37, // 53: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse
38, // [38:54] is the sub-list for method output_type
22, // [22:38] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension type_name
22, // [22:22] is the sub-list for extension extendee
0, // [0:22] is the sub-list for field type_name
42, // 22: looprpc.ListReservationsResponse.reservations:type_name -> looprpc.ClientReservation
7, // 23: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest
8, // 24: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest
10, // 25: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest
12, // 26: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest
15, // 27: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest
38, // 28: looprpc.SwapClient.AbandonSwap:input_type -> looprpc.AbandonSwapRequest
16, // 29: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest
19, // 30: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest
16, // 31: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest
19, // 32: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest
22, // 33: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest
24, // 34: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest
28, // 35: looprpc.SwapClient.GetInfo:input_type -> looprpc.GetInfoRequest
30, // 36: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest
33, // 37: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest
35, // 38: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest
40, // 39: looprpc.SwapClient.ListReservations:input_type -> looprpc.ListReservationsRequest
9, // 40: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse
9, // 41: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse
11, // 42: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus
14, // 43: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse
11, // 44: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus
39, // 45: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse
18, // 46: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse
21, // 47: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse
17, // 48: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse
20, // 49: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse
23, // 50: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse
25, // 51: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse
29, // 52: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse
31, // 53: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters
34, // 54: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse
37, // 55: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse
41, // 56: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse
40, // [40:57] is the sub-list for method output_type
23, // [23:40] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
}
func init() { file_client_proto_init() }
@ -4464,6 +4680,42 @@ func file_client_proto_init() {
return nil
}
}
file_client_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListReservationsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_client_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListReservationsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_client_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ClientReservation); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -4471,7 +4723,7 @@ func file_client_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_client_proto_rawDesc,
NumEnums: 7,
NumMessages: 33,
NumMessages: 36,
NumExtensions: 0,
NumServices: 1,
},

@ -109,6 +109,12 @@ service SwapClient {
[EXPERIMENTAL]: endpoint is subject to change.
*/
rpc SuggestSwaps (SuggestSwapsRequest) returns (SuggestSwapsResponse);
/* loop: `listreservations`
ListReservations returns a list of all reservations the server opened to us.
*/
rpc ListReservations (ListReservationsRequest)
returns (ListReservationsResponse);
}
message LoopOutRequest {
@ -1242,4 +1248,42 @@ message AbandonSwapRequest {
}
message AbandonSwapResponse {
}
message ListReservationsRequest {
}
message ListReservationsResponse {
/*
The list of all currently known reservations and their status.
*/
repeated ClientReservation reservations = 1;
}
message ClientReservation {
/*
The reservation id that identifies this reservation.
*/
bytes reservation_id = 1;
/*
The state the reservation is in.
*/
string state = 2;
/*
The amount that the reservation is for.
*/
uint64 amount = 3;
/*
The transaction id of the reservation.
*/
bytes tx_id = 4;
/*
The vout of the reservation.
*/
uint32 vout = 5;
/*
The expiry of the reservation.
*/
uint32 expiry = 6;
}

@ -465,7 +465,7 @@
"parameters": [
{
"name": "list_swap_filter.swap_type",
"description": "The type of the swap.\n\n - ANY: ANY indicates that no filter is applied.\n - LOOP_OUT: LOOP_OUT indicates an loop out swap (off-chain to on-chain)\n - LOOP_IN: LOOP_IN indicates a loop in swap (on-chain to off-chain)",
"description": "The type of the swap.\n\n - ANY: ANY indicates that no filter is applied.\n - LOOP_OUT: LOOP_OUT indicates an loop out swap (off-chain to on-chain).\n - LOOP_IN: LOOP_IN indicates a loop in swap (on-chain to off-chain).",
"in": "query",
"required": false,
"type": "string",
@ -549,7 +549,7 @@
"LOOP_IN"
],
"default": "ANY",
"title": "- ANY: ANY indicates that no filter is applied.\n - LOOP_OUT: LOOP_OUT indicates an loop out swap (off-chain to on-chain)\n - LOOP_IN: LOOP_IN indicates a loop in swap (on-chain to off-chain)"
"description": " - ANY: ANY indicates that no filter is applied.\n - LOOP_OUT: LOOP_OUT indicates an loop out swap (off-chain to on-chain).\n - LOOP_IN: LOOP_IN indicates a loop in swap (on-chain to off-chain)."
},
"looprpcAbandonSwapResponse": {
"type": "object"
@ -585,6 +585,40 @@
"default": "AUTO_REASON_UNKNOWN",
"description": " - AUTO_REASON_BUDGET_NOT_STARTED: Budget not started indicates that we do not recommend any swaps because\nthe start time for our budget has not arrived yet.\n - AUTO_REASON_SWEEP_FEES: Sweep fees indicates that the estimated fees to sweep swaps are too high\nright now.\n - AUTO_REASON_BUDGET_ELAPSED: Budget elapsed indicates that the autoloop budget for the period has been\nelapsed.\n - AUTO_REASON_IN_FLIGHT: In flight indicates that the limit on in-flight automatically dispatched\nswaps has already been reached.\n - AUTO_REASON_SWAP_FEE: Swap fee indicates that the server fee for a specific swap is too high.\n - AUTO_REASON_MINER_FEE: Miner fee indicates that the miner fee for a specific swap is to high.\n - AUTO_REASON_PREPAY: Prepay indicates that the prepay fee for a specific swap is too high.\n - AUTO_REASON_FAILURE_BACKOFF: Failure backoff indicates that a swap has recently failed for this target,\nand the backoff period has not yet passed.\n - AUTO_REASON_LOOP_OUT: Loop out indicates that a loop out swap is currently utilizing the channel,\nso it is not eligible.\n - AUTO_REASON_LOOP_IN: Loop In indicates that a loop in swap is currently in flight for the peer,\nso it is not eligible.\n - AUTO_REASON_LIQUIDITY_OK: Liquidity ok indicates that a target meets the liquidity balance expressed\nin its rule, so no swap is needed.\n - AUTO_REASON_BUDGET_INSUFFICIENT: Budget insufficient indicates that we cannot perform a swap because we do\nnot have enough pending budget available. This differs from budget elapsed,\nbecause we still have some budget available, but we have allocated it to\nother swaps.\n - AUTO_REASON_FEE_INSUFFICIENT: Fee insufficient indicates that the fee estimate for a swap is higher than\nthe portion of total swap amount that we allow fees to consume."
},
"looprpcClientReservation": {
"type": "object",
"properties": {
"reservation_id": {
"type": "string",
"format": "byte",
"description": "The reservation id that identifies this reservation."
},
"state": {
"type": "string",
"description": "The state the reservation is in."
},
"amount": {
"type": "string",
"format": "uint64",
"description": "The amount that the reservation is for."
},
"tx_id": {
"type": "string",
"format": "byte",
"description": "The transaction id of the reservation."
},
"vout": {
"type": "integer",
"format": "int64",
"description": "The vout of the reservation."
},
"expiry": {
"type": "integer",
"format": "int64",
"description": "The expiry of the reservation."
}
}
},
"looprpcDisqualified": {
"type": "object",
"properties": {
@ -889,6 +923,18 @@
],
"default": "UNKNOWN"
},
"looprpcListReservationsResponse": {
"type": "object",
"properties": {
"reservations": {
"type": "array",
"items": {
"$ref": "#/definitions/looprpcClientReservation"
},
"description": "The list of all currently known reservations and their status."
}
}
},
"looprpcListSwapsFilter": {
"type": "object",
"properties": {

@ -83,6 +83,9 @@ 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`
//ListReservations returns a list of all reservations the server opened to us.
ListReservations(ctx context.Context, in *ListReservationsRequest, opts ...grpc.CallOption) (*ListReservationsResponse, error)
}
type swapClientClient struct {
@ -260,6 +263,15 @@ func (c *swapClientClient) SuggestSwaps(ctx context.Context, in *SuggestSwapsReq
return out, nil
}
func (c *swapClientClient) ListReservations(ctx context.Context, in *ListReservationsRequest, opts ...grpc.CallOption) (*ListReservationsResponse, error) {
out := new(ListReservationsResponse)
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/ListReservations", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// SwapClientServer is the server API for SwapClient service.
// All implementations must embed UnimplementedSwapClientServer
// for forward compatibility
@ -329,6 +341,9 @@ 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`
//ListReservations returns a list of all reservations the server opened to us.
ListReservations(context.Context, *ListReservationsRequest) (*ListReservationsResponse, error)
mustEmbedUnimplementedSwapClientServer()
}
@ -384,6 +399,9 @@ func (UnimplementedSwapClientServer) SetLiquidityParams(context.Context, *SetLiq
func (UnimplementedSwapClientServer) SuggestSwaps(context.Context, *SuggestSwapsRequest) (*SuggestSwapsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SuggestSwaps not implemented")
}
func (UnimplementedSwapClientServer) ListReservations(context.Context, *ListReservationsRequest) (*ListReservationsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListReservations not implemented")
}
func (UnimplementedSwapClientServer) mustEmbedUnimplementedSwapClientServer() {}
// UnsafeSwapClientServer may be embedded to opt out of forward compatibility for this service.
@ -688,6 +706,24 @@ func _SwapClient_SuggestSwaps_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
func _SwapClient_ListReservations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListReservationsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SwapClientServer).ListReservations(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.SwapClient/ListReservations",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SwapClientServer).ListReservations(ctx, req.(*ListReservationsRequest))
}
return interceptor(ctx, in, info, handler)
}
// SwapClient_ServiceDesc is the grpc.ServiceDesc for SwapClient service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -755,6 +791,10 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{
MethodName: "SuggestSwaps",
Handler: _SwapClient_SuggestSwaps_Handler,
},
{
MethodName: "ListReservations",
Handler: _SwapClient_ListReservations_Handler,
},
},
Streams: []grpc.StreamDesc{
{

@ -437,4 +437,29 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex
}
callback(string(respBytes), nil)
}
registry["looprpc.SwapClient.ListReservations"] = func(ctx context.Context,
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
req := &ListReservationsRequest{}
err := marshaler.Unmarshal([]byte(reqJSON), req)
if err != nil {
callback("", err)
return
}
client := NewSwapClientClient(conn)
resp, err := client.ListReservations(ctx, req)
if err != nil {
callback("", err)
return
}
respBytes, err := marshaler.Marshal(resp)
if err != nil {
callback("", err)
return
}
callback(string(respBytes), nil)
}
}

@ -1,2 +1,3 @@
#!/usr/bin/env bash
go run ./fsm/stateparser/stateparser.go --out ./fsm/example_fsm.md --fsm example
go run ./fsm/stateparser/stateparser.go --out ./fsm/example_fsm.md --fsm example
go run ./fsm/stateparser/stateparser.go --out ./reservation/reservation_fsm.md --fsm reservation

@ -0,0 +1,468 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.6.1
// source: reservation.proto
// We can't change this to swapserverrpc, it would be a breaking change because
// the package name is also contained in the HTTP URIs and old clients would
// call the wrong endpoints. Luckily with the go_package option we can have
// different golang and RPC package names to fix protobuf namespace conflicts.
package swapserverrpc
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// ReservationProtocolVersion is the version of the reservation protocol.
type ReservationProtocolVersion int32
const (
// RESERVATION_NONE is the default value and means that the reservation
// protocol version is not set.
ReservationProtocolVersion_RESERVATION_NONE ReservationProtocolVersion = 0
// RESERVATION_SERVER_REQUEST is the first version of the reservation
// protocol where the server notifies the client about a reservation.
ReservationProtocolVersion_RESERVATION_SERVER_NOTIFY ReservationProtocolVersion = 1
)
// Enum value maps for ReservationProtocolVersion.
var (
ReservationProtocolVersion_name = map[int32]string{
0: "RESERVATION_NONE",
1: "RESERVATION_SERVER_NOTIFY",
}
ReservationProtocolVersion_value = map[string]int32{
"RESERVATION_NONE": 0,
"RESERVATION_SERVER_NOTIFY": 1,
}
)
func (x ReservationProtocolVersion) Enum() *ReservationProtocolVersion {
p := new(ReservationProtocolVersion)
*p = x
return p
}
func (x ReservationProtocolVersion) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ReservationProtocolVersion) Descriptor() protoreflect.EnumDescriptor {
return file_reservation_proto_enumTypes[0].Descriptor()
}
func (ReservationProtocolVersion) Type() protoreflect.EnumType {
return &file_reservation_proto_enumTypes[0]
}
func (x ReservationProtocolVersion) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ReservationProtocolVersion.Descriptor instead.
func (ReservationProtocolVersion) EnumDescriptor() ([]byte, []int) {
return file_reservation_proto_rawDescGZIP(), []int{0}
}
// ReservationNotificationRequest is an empty request sent from the client to
// the server to open a stream to receive reservation notifications.
type ReservationNotificationRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ReservationNotificationRequest) Reset() {
*x = ReservationNotificationRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_reservation_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReservationNotificationRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReservationNotificationRequest) ProtoMessage() {}
func (x *ReservationNotificationRequest) ProtoReflect() protoreflect.Message {
mi := &file_reservation_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReservationNotificationRequest.ProtoReflect.Descriptor instead.
func (*ReservationNotificationRequest) Descriptor() ([]byte, []int) {
return file_reservation_proto_rawDescGZIP(), []int{0}
}
// ServerReservationNotification is a notification sent from the server to the
// client if the server wants to open a reservation.
type ServerReservationNotification struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// reservation_id is the id of the reservation.
ReservationId []byte `protobuf:"bytes,1,opt,name=reservation_id,json=reservationId,proto3" json:"reservation_id,omitempty"`
// value is the value of the reservation in satoshis.
Value uint64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
// server_key is the public key of the server.
ServerKey []byte `protobuf:"bytes,3,opt,name=server_key,json=serverKey,proto3" json:"server_key,omitempty"`
// expiry is the absolute expiry of the reservation.
Expiry uint32 `protobuf:"varint,4,opt,name=expiry,proto3" json:"expiry,omitempty"`
// protocol_version is the version of the reservation protocol.
ProtocolVersion ReservationProtocolVersion `protobuf:"varint,5,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ReservationProtocolVersion" json:"protocol_version,omitempty"`
}
func (x *ServerReservationNotification) Reset() {
*x = ServerReservationNotification{}
if protoimpl.UnsafeEnabled {
mi := &file_reservation_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServerReservationNotification) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerReservationNotification) ProtoMessage() {}
func (x *ServerReservationNotification) ProtoReflect() protoreflect.Message {
mi := &file_reservation_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerReservationNotification.ProtoReflect.Descriptor instead.
func (*ServerReservationNotification) Descriptor() ([]byte, []int) {
return file_reservation_proto_rawDescGZIP(), []int{1}
}
func (x *ServerReservationNotification) GetReservationId() []byte {
if x != nil {
return x.ReservationId
}
return nil
}
func (x *ServerReservationNotification) GetValue() uint64 {
if x != nil {
return x.Value
}
return 0
}
func (x *ServerReservationNotification) GetServerKey() []byte {
if x != nil {
return x.ServerKey
}
return nil
}
func (x *ServerReservationNotification) GetExpiry() uint32 {
if x != nil {
return x.Expiry
}
return 0
}
func (x *ServerReservationNotification) GetProtocolVersion() ReservationProtocolVersion {
if x != nil {
return x.ProtocolVersion
}
return ReservationProtocolVersion_RESERVATION_NONE
}
// ServerOpenReservationRequest is a request sent from the client to the server
// to confirm a reservation opening.
type ServerOpenReservationRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// reservation_id is the id of the reservation.
ReservationId []byte `protobuf:"bytes,1,opt,name=reservation_id,json=reservationId,proto3" json:"reservation_id,omitempty"`
// client_key is the public key of the client.
ClientKey []byte `protobuf:"bytes,2,opt,name=client_key,json=clientKey,proto3" json:"client_key,omitempty"`
}
func (x *ServerOpenReservationRequest) Reset() {
*x = ServerOpenReservationRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_reservation_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServerOpenReservationRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerOpenReservationRequest) ProtoMessage() {}
func (x *ServerOpenReservationRequest) ProtoReflect() protoreflect.Message {
mi := &file_reservation_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerOpenReservationRequest.ProtoReflect.Descriptor instead.
func (*ServerOpenReservationRequest) Descriptor() ([]byte, []int) {
return file_reservation_proto_rawDescGZIP(), []int{2}
}
func (x *ServerOpenReservationRequest) GetReservationId() []byte {
if x != nil {
return x.ReservationId
}
return nil
}
func (x *ServerOpenReservationRequest) GetClientKey() []byte {
if x != nil {
return x.ClientKey
}
return nil
}
// ServerOpenReservationResponse is a response sent from the server to the
// client to confirm a reservation opening.
type ServerOpenReservationResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ServerOpenReservationResponse) Reset() {
*x = ServerOpenReservationResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_reservation_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServerOpenReservationResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerOpenReservationResponse) ProtoMessage() {}
func (x *ServerOpenReservationResponse) ProtoReflect() protoreflect.Message {
mi := &file_reservation_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerOpenReservationResponse.ProtoReflect.Descriptor instead.
func (*ServerOpenReservationResponse) Descriptor() ([]byte, []int) {
return file_reservation_proto_rawDescGZIP(), []int{3}
}
var File_reservation_proto protoreflect.FileDescriptor
var file_reservation_proto_rawDesc = []byte{
0x0a, 0x11, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x22, 0x20, 0x0a, 0x1e,
0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe3,
0x01, 0x0a, 0x1d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a,
0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06,
0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x65, 0x78,
0x70, 0x69, 0x72, 0x79, 0x12, 0x4e, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x22, 0x64, 0x0a, 0x1c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4f, 0x70,
0x65, 0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65,
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x51, 0x0a, 0x1a, 0x52,
0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x53,
0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12,
0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53,
0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59, 0x10, 0x01, 0x32, 0xea,
0x01, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x72, 0x0a, 0x1d, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x27, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x26, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x60, 0x0a, 0x0f, 0x4f, 0x70, 0x65,
0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4f, 0x70, 0x65,
0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e,
0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61,
0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_reservation_proto_rawDescOnce sync.Once
file_reservation_proto_rawDescData = file_reservation_proto_rawDesc
)
func file_reservation_proto_rawDescGZIP() []byte {
file_reservation_proto_rawDescOnce.Do(func() {
file_reservation_proto_rawDescData = protoimpl.X.CompressGZIP(file_reservation_proto_rawDescData)
})
return file_reservation_proto_rawDescData
}
var file_reservation_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_reservation_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_reservation_proto_goTypes = []interface{}{
(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
}
var file_reservation_proto_depIdxs = []int32{
0, // 0: looprpc.ServerReservationNotification.protocol_version:type_name -> looprpc.ReservationProtocolVersion
1, // 1: looprpc.ReservationService.ReservationNotificationStream:input_type -> looprpc.ReservationNotificationRequest
3, // 2: looprpc.ReservationService.OpenReservation:input_type -> looprpc.ServerOpenReservationRequest
2, // 3: looprpc.ReservationService.ReservationNotificationStream:output_type -> looprpc.ServerReservationNotification
4, // 4: looprpc.ReservationService.OpenReservation:output_type -> looprpc.ServerOpenReservationResponse
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_reservation_proto_init() }
func file_reservation_proto_init() {
if File_reservation_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_reservation_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReservationNotificationRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_reservation_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServerReservationNotification); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_reservation_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServerOpenReservationRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_reservation_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServerOpenReservationResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_reservation_proto_rawDesc,
NumEnums: 1,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_reservation_proto_goTypes,
DependencyIndexes: file_reservation_proto_depIdxs,
EnumInfos: file_reservation_proto_enumTypes,
MessageInfos: file_reservation_proto_msgTypes,
}.Build()
File_reservation_proto = out.File
file_reservation_proto_rawDesc = nil
file_reservation_proto_goTypes = nil
file_reservation_proto_depIdxs = nil
}

@ -0,0 +1,70 @@
syntax = "proto3";
// We can't change this to swapserverrpc, it would be a breaking change because
// the package name is also contained in the HTTP URIs and old clients would
// call the wrong endpoints. Luckily with the go_package option we can have
// different golang and RPC package names to fix protobuf namespace conflicts.
package looprpc;
option go_package = "github.com/lightninglabs/loop/swapserverrpc";
service ReservationService {
// ReservationNotificationStream is a server side stream that sends
// notifications if the server wants to open a reservation to the client.
rpc ReservationNotificationStream (ReservationNotificationRequest)
returns (stream ServerReservationNotification);
// OpenReservation requests a new reservation UTXO from the server.
rpc OpenReservation (ServerOpenReservationRequest)
returns (ServerOpenReservationResponse);
}
// ReservationNotificationRequest is an empty request sent from the client to
// the server to open a stream to receive reservation notifications.
message ReservationNotificationRequest {
}
// ServerReservationNotification is a notification sent from the server to the
// client if the server wants to open a reservation.
message ServerReservationNotification {
// reservation_id is the id of the reservation.
bytes reservation_id = 1;
// value is the value of the reservation in satoshis.
uint64 value = 2;
// server_key is the public key of the server.
bytes server_key = 3;
// expiry is the absolute expiry of the reservation.
uint32 expiry = 4;
// protocol_version is the version of the reservation protocol.
ReservationProtocolVersion protocol_version = 5;
}
// ServerOpenReservationRequest is a request sent from the client to the server
// to confirm a reservation opening.
message ServerOpenReservationRequest {
// reservation_id is the id of the reservation.
bytes reservation_id = 1;
// client_key is the public key of the client.
bytes client_key = 2;
}
// ServerOpenReservationResponse is a response sent from the server to the
// client to confirm a reservation opening.
message ServerOpenReservationResponse {
}
// ReservationProtocolVersion is the version of the reservation protocol.
enum ReservationProtocolVersion {
// RESERVATION_NONE is the default value and means that the reservation
// protocol version is not set.
RESERVATION_NONE = 0;
// RESERVATION_SERVER_REQUEST is the first version of the reservation
// protocol where the server notifies the client about a reservation.
RESERVATION_SERVER_NOTIFY = 1;
};

@ -0,0 +1,171 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package swapserverrpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// ReservationServiceClient is the client API for ReservationService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ReservationServiceClient interface {
// ReservationNotificationStream is a server side stream that sends
// notifications if the server wants to open a reservation to the client.
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)
}
type reservationServiceClient struct {
cc grpc.ClientConnInterface
}
func NewReservationServiceClient(cc grpc.ClientConnInterface) ReservationServiceClient {
return &reservationServiceClient{cc}
}
func (c *reservationServiceClient) ReservationNotificationStream(ctx context.Context, in *ReservationNotificationRequest, opts ...grpc.CallOption) (ReservationService_ReservationNotificationStreamClient, error) {
stream, err := c.cc.NewStream(ctx, &ReservationService_ServiceDesc.Streams[0], "/looprpc.ReservationService/ReservationNotificationStream", opts...)
if err != nil {
return nil, err
}
x := &reservationServiceReservationNotificationStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type ReservationService_ReservationNotificationStreamClient interface {
Recv() (*ServerReservationNotification, error)
grpc.ClientStream
}
type reservationServiceReservationNotificationStreamClient struct {
grpc.ClientStream
}
func (x *reservationServiceReservationNotificationStreamClient) Recv() (*ServerReservationNotification, error) {
m := new(ServerReservationNotification)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *reservationServiceClient) OpenReservation(ctx context.Context, in *ServerOpenReservationRequest, opts ...grpc.CallOption) (*ServerOpenReservationResponse, error) {
out := new(ServerOpenReservationResponse)
err := c.cc.Invoke(ctx, "/looprpc.ReservationService/OpenReservation", 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
type ReservationServiceServer interface {
// ReservationNotificationStream is a server side stream that sends
// notifications if the server wants to open a reservation to the client.
ReservationNotificationStream(*ReservationNotificationRequest, ReservationService_ReservationNotificationStreamServer) error
// OpenReservation requests a new reservation UTXO from the server.
OpenReservation(context.Context, *ServerOpenReservationRequest) (*ServerOpenReservationResponse, error)
mustEmbedUnimplementedReservationServiceServer()
}
// UnimplementedReservationServiceServer must be embedded to have forward compatible implementations.
type UnimplementedReservationServiceServer struct {
}
func (UnimplementedReservationServiceServer) ReservationNotificationStream(*ReservationNotificationRequest, ReservationService_ReservationNotificationStreamServer) error {
return status.Errorf(codes.Unimplemented, "method ReservationNotificationStream not implemented")
}
func (UnimplementedReservationServiceServer) OpenReservation(context.Context, *ServerOpenReservationRequest) (*ServerOpenReservationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method OpenReservation not implemented")
}
func (UnimplementedReservationServiceServer) mustEmbedUnimplementedReservationServiceServer() {}
// UnsafeReservationServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ReservationServiceServer will
// result in compilation errors.
type UnsafeReservationServiceServer interface {
mustEmbedUnimplementedReservationServiceServer()
}
func RegisterReservationServiceServer(s grpc.ServiceRegistrar, srv ReservationServiceServer) {
s.RegisterService(&ReservationService_ServiceDesc, srv)
}
func _ReservationService_ReservationNotificationStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ReservationNotificationRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(ReservationServiceServer).ReservationNotificationStream(m, &reservationServiceReservationNotificationStreamServer{stream})
}
type ReservationService_ReservationNotificationStreamServer interface {
Send(*ServerReservationNotification) error
grpc.ServerStream
}
type reservationServiceReservationNotificationStreamServer struct {
grpc.ServerStream
}
func (x *reservationServiceReservationNotificationStreamServer) Send(m *ServerReservationNotification) error {
return x.ServerStream.SendMsg(m)
}
func _ReservationService_OpenReservation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ServerOpenReservationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReservationServiceServer).OpenReservation(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.ReservationService/OpenReservation",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReservationServiceServer).OpenReservation(ctx, req.(*ServerOpenReservationRequest))
}
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)
var ReservationService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "looprpc.ReservationService",
HandlerType: (*ReservationServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "OpenReservation",
Handler: _ReservationService_OpenReservation_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "ReservationNotificationStream",
Handler: _ReservationService_ReservationNotificationStream_Handler,
ServerStreams: true,
},
},
Metadata: "reservation.proto",
}
Loading…
Cancel
Save