diff --git a/cmd/loop/staticaddr.go b/cmd/loop/staticaddr.go index c9626ba..bf1f7fc 100644 --- a/cmd/loop/staticaddr.go +++ b/cmd/loop/staticaddr.go @@ -39,14 +39,14 @@ func newStaticAddress(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, "new") } - client, cleanup, err := getAddressClient(ctx) + client, cleanup, err := getClient(ctx) if err != nil { return err } defer cleanup() - resp, err := client.NewAddress( - ctxb, &looprpc.NewAddressRequest{}, + resp, err := client.NewStaticAddress( + ctxb, &looprpc.NewStaticAddressRequest{}, ) if err != nil { return err @@ -86,16 +86,17 @@ func listUnspent(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, "listunspent") } - client, cleanup, err := getAddressClient(ctx) + client, cleanup, err := getClient(ctx) if err != nil { return err } defer cleanup() - resp, err := client.ListUnspent(ctxb, &looprpc.ListUnspentRequest{ - MinConfs: int32(ctx.Int("min_confs")), - MaxConfs: int32(ctx.Int("max_confs")), - }) + resp, err := client.ListUnspentDeposits( + ctxb, &looprpc.ListUnspentDepositsRequest{ + MinConfs: int32(ctx.Int("min_confs")), + MaxConfs: int32(ctx.Int("max_confs")), + }) if err != nil { return err } @@ -104,21 +105,3 @@ func listUnspent(ctx *cli.Context) error { return nil } - -func getAddressClient(ctx *cli.Context) (looprpc.StaticAddressClientClient, - func(), error) { - - rpcServer := ctx.GlobalString("rpcserver") - tlsCertPath, macaroonPath, err := extractPathArgs(ctx) - if err != nil { - return nil, nil, err - } - conn, err := getClientConn(rpcServer, tlsCertPath, macaroonPath) - if err != nil { - return nil, nil, err - } - cleanup := func() { conn.Close() } - - addressClient := looprpc.NewStaticAddressClientClient(conn) - return addressClient, cleanup, nil -} diff --git a/go.mod b/go.mod index fe6e456..67d59fd 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 github.com/jackc/pgconn v1.14.3 github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa - github.com/jackc/pgx/v4 v4.18.2 github.com/jessevdk/go-flags v1.4.0 github.com/lib/pq v1.10.7 github.com/lightninglabs/aperture v0.1.21-beta.0.20230705004936-87bb996a4030 @@ -98,6 +97,7 @@ require ( github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.2 // indirect github.com/jackpal/gateway v1.0.5 // indirect github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad // indirect github.com/jonboulle/clockwork v0.2.2 // indirect diff --git a/loopd/daemon.go b/loopd/daemon.go index d0b3fe9..9c0101e 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -21,7 +21,8 @@ import ( "github.com/lightninglabs/loop/loopd/perms" "github.com/lightninglabs/loop/loopdb" loop_looprpc "github.com/lightninglabs/loop/looprpc" - "github.com/lightninglabs/loop/staticaddr" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/deposit" loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightninglabs/loop/sweepbatcher" "github.com/lightningnetwork/lnd/clock" @@ -68,12 +69,6 @@ type Daemon struct { // same process. swapClientServer - // AddressServer is the embedded RPC server that satisfies the - // static address client RPC interface. We embed this struct so the - // Daemon itself can be registered to an existing grpc.Server to run as - // a subserver in the same process. - *staticaddr.AddressServer - // 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. @@ -239,7 +234,6 @@ func (d *Daemon) startWebServers() error { grpc.StreamInterceptor(streamInterceptor), ) loop_looprpc.RegisterSwapClientServer(d.grpcServer, d) - loop_looprpc.RegisterStaticAddressClientServer(d.grpcServer, d) // Register our debug server if it is compiled in. d.registerDebugServer() @@ -438,6 +432,11 @@ func (d *Daemon) initialize(withMacaroonService bool) error { swapClient.Conn, ) + // Create a static address server client. + staticAddressClient := loop_swaprpc.NewStaticAddressServerClient( + 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()) @@ -498,6 +497,9 @@ func (d *Daemon) initialize(withMacaroonService bool) error { var ( reservationManager *reservation.Manager instantOutManager *instantout.Manager + + staticAddressManager *address.Manager + depositManager *deposit.Manager ) // Create the reservation and instantout managers. if d.cfg.EnableExperimental { @@ -534,43 +536,50 @@ func (d *Daemon) initialize(withMacaroonService bool) error { instantOutManager = instantout.NewInstantOutManager( instantOutConfig, ) + + // Static address manager setup. + staticAddressStore := address.NewSqlStore(baseDb) + addrCfg := &address.ManagerConfig{ + AddressClient: staticAddressClient, + FetchL402: swapClient.Server.FetchL402, + Store: staticAddressStore, + WalletKit: d.lnd.WalletKit, + ChainParams: d.lnd.ChainParams, + } + staticAddressManager = address.NewManager(addrCfg) + + // Static address deposit manager setup. + depositStore := deposit.NewSqlStore(baseDb) + depoCfg := &deposit.ManagerConfig{ + AddressClient: staticAddressClient, + AddressManager: staticAddressManager, + SwapClient: swapClient, + Store: depositStore, + WalletKit: d.lnd.WalletKit, + ChainParams: d.lnd.ChainParams, + ChainNotifier: d.lnd.ChainNotifier, + Signer: d.lnd.Signer, + } + depositManager = deposit.NewManager(depoCfg) } // 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, - reservationManager: reservationManager, - instantOutManager: instantOutManager, + 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: reservationManager, + instantOutManager: instantOutManager, + staticAddressManager: staticAddressManager, + depositManager: depositManager, } - // Create a static address server client. - staticAddressClient := loop_swaprpc.NewStaticAddressServerClient( - swapClient.Conn, - ) - - store := staticaddr.NewSqlStore(baseDb) - - cfg := &staticaddr.ManagerConfig{ - AddressClient: staticAddressClient, - SwapClient: swapClient, - Store: store, - WalletKit: d.lnd.WalletKit, - ChainParams: d.lnd.ChainParams, - } - staticAddressManager := staticaddr.NewAddressManager(cfg) - - d.AddressServer = staticaddr.NewAddressServer( - staticAddressClient, staticAddressManager, - ) - // Retrieve all currently existing swaps from the database. swapsList, err := d.impl.FetchSwaps(d.mainCtx) if err != nil { @@ -662,20 +671,43 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } // Start the static address manager. - d.wg.Add(1) - go func() { - defer d.wg.Done() + if staticAddressManager != nil { + d.wg.Add(1) + go func() { + defer d.wg.Done() - log.Info("Starting static address manager...") - err = staticAddressManager.Run(d.mainCtx) - if err != nil && !errors.Is(context.Canceled, err) { - d.internalErrChan <- err - } + log.Info("Starting static address manager...") + err = staticAddressManager.Run(d.mainCtx) + if err != nil && !errors.Is(context.Canceled, err) { + d.internalErrChan <- err + } + log.Info("Static address manager stopped") + }() + } - log.Info("Static address manager stopped") - }() + // Start the static address deposit manager. + if depositManager != nil { + d.wg.Add(1) + go func() { + defer d.wg.Done() - staticAddressManager.WaitInitComplete() + // Lnd's GetInfo call supplies us with the current block + // height. + info, err := d.lnd.Client.GetInfo(d.mainCtx) + if err != nil { + d.internalErrChan <- err + return + } + + log.Info("Starting static address deposit manager...") + err = depositManager.Run(d.mainCtx, info.BlockHeight) + if err != nil && !errors.Is(context.Canceled, err) { + d.internalErrChan <- err + } + log.Info("Static address deposit manager stopped") + }() + depositManager.WaitInitComplete() + } // Last, start our internal error handler. This will return exactly one // error or nil on the main error channel to inform the caller that diff --git a/loopd/perms/perms.go b/loopd/perms/perms.go index 67d39f9..6458019 100644 --- a/loopd/perms/perms.go +++ b/loopd/perms/perms.go @@ -69,14 +69,14 @@ var RequiredPermissions = map[string][]bakery.Op{ Entity: "loop", Action: "in", }}, - "/looprpc.StaticAddressClient/NewAddress": {{ + "/looprpc.SwapClient/NewStaticAddress": {{ Entity: "swap", Action: "read", }, { Entity: "loop", Action: "in", }}, - "/looprpc.StaticAddressClient/ListUnspent": {{ + "/looprpc.SwapClient/ListUnspentDeposits": {{ Entity: "swap", Action: "read", }, { diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index f42e8d5..00b0ea0 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -24,6 +24,8 @@ import ( "github.com/lightninglabs/loop/liquidity" "github.com/lightninglabs/loop/loopdb" clientrpc "github.com/lightninglabs/loop/looprpc" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/deposit" "github.com/lightninglabs/loop/swap" looprpc "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" @@ -76,19 +78,21 @@ type swapClientServer struct { clientrpc.UnimplementedSwapClientServer clientrpc.UnimplementedDebugServer - config *Config - network lndclient.Network - impl *loop.Client - liquidityMgr *liquidity.Manager - lnd *lndclient.LndServices - reservationManager *reservation.Manager - instantOutManager *instantout.Manager - swaps map[lntypes.Hash]loop.SwapInfo - subscribers map[int]chan<- interface{} - statusChan chan loop.SwapInfo - 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 + instantOutManager *instantout.Manager + staticAddressManager *address.Manager + depositManager *deposit.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 @@ -1272,6 +1276,51 @@ func rpcInstantOut(instantOut *instantout.InstantOut) *clientrpc.InstantOut { } } +// NewStaticAddress is the rpc endpoint for loop clients to request a new static +// address. +func (s *swapClientServer) NewStaticAddress(ctx context.Context, + _ *clientrpc.NewStaticAddressRequest) ( + *clientrpc.NewStaticAddressResponse, error) { + + staticAddress, err := s.staticAddressManager.NewAddress(ctx) + if err != nil { + return nil, err + } + + return &clientrpc.NewStaticAddressResponse{ + Address: staticAddress.String(), + }, nil +} + +// ListUnspentDeposits returns a list of utxos behind the static address. +func (s *swapClientServer) ListUnspentDeposits(ctx context.Context, + req *clientrpc.ListUnspentDepositsRequest) ( + *clientrpc.ListUnspentDepositsResponse, error) { + + // List all unspent utxos the wallet sees, regardless of the number of + // confirmations. + staticAddress, utxos, err := s.staticAddressManager.ListUnspentRaw( + ctx, req.MinConfs, req.MaxConfs, + ) + if err != nil { + return nil, err + } + + // Prepare the list response. + var respUtxos []*clientrpc.Utxo + for _, u := range utxos { + utxo := &clientrpc.Utxo{ + StaticAddress: staticAddress.String(), + AmountSat: int64(u.Value), + Confirmations: u.Confirmations, + Outpoint: u.OutPoint.String(), + } + respUtxos = append(respUtxos, utxo) + } + + return &clientrpc.ListUnspentDepositsResponse{Utxos: respUtxos}, nil +} + func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) { switch reason { case liquidity.ReasonNone: diff --git a/loopdb/sqlc/migrations/000008_static_address_deposits.down.sql b/loopdb/sqlc/migrations/000008_static_address_deposits.down.sql new file mode 100644 index 0000000..1170fda --- /dev/null +++ b/loopdb/sqlc/migrations/000008_static_address_deposits.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS deposits; diff --git a/loopdb/sqlc/migrations/000008_static_address_deposits.up.sql b/loopdb/sqlc/migrations/000008_static_address_deposits.up.sql new file mode 100644 index 0000000..7898e60 --- /dev/null +++ b/loopdb/sqlc/migrations/000008_static_address_deposits.up.sql @@ -0,0 +1,43 @@ +-- deposits stores historic and unspent static address outputs. +CREATE TABLE IF NOT EXISTS deposits ( + -- id is the auto-incrementing primary key for a static address. + id INTEGER PRIMARY KEY, + + -- deposit_id is the unique identifier for the deposit. + deposit_id BLOB NOT NULL UNIQUE, + + -- tx_hash is the transaction hash of the deposit. + tx_hash BYTEA NOT NULL, + + -- output_index is the index of the output in the transaction. + out_index INT NOT NULL, + + -- amount is the amount of the deposit. + amount BIGINT NOT NULL, + + -- confirmation_height is the absolute height at which the deposit was + -- confirmed. + confirmation_height BIGINT NOT NULL, + + -- timeout_sweep_pk_script is the public key script that will be used to + -- sweep the deposit after has expired. + timeout_sweep_pk_script BYTEA NOT NULL, + + -- expiry_sweep_txid is the transaction id of the expiry sweep. + expiry_sweep_txid BLOB +); + +-- deposit_updates contains all the updates to a deposit. +CREATE TABLE IF NOT EXISTS deposit_updates ( + -- id is the auto incrementing primary key. + id INTEGER PRIMARY KEY, + + -- deposit_id is the unique identifier for the deposit. + deposit_id BLOB NOT NULL REFERENCES deposits(deposit_id), + + -- update_state is the state of the deposit at the time of the update. + update_state TEXT NOT NULL, + + -- update_timestamp is the timestamp of the update. + update_timestamp TIMESTAMP NOT NULL +); diff --git a/loopdb/sqlc/models.go b/loopdb/sqlc/models.go index 78d4828..3a9abe7 100644 --- a/loopdb/sqlc/models.go +++ b/loopdb/sqlc/models.go @@ -9,6 +9,24 @@ import ( "time" ) +type Deposit struct { + ID int32 + DepositID []byte + TxHash []byte + OutIndex int32 + Amount int64 + ConfirmationHeight int64 + TimeoutSweepPkScript []byte + ExpirySweepTxid []byte +} + +type DepositUpdate struct { + ID int32 + DepositID []byte + UpdateState string + UpdateTimestamp time.Time +} + type HtlcKey struct { SwapHash []byte SenderScriptPubkey []byte diff --git a/loopdb/sqlc/querier.go b/loopdb/sqlc/querier.go index 20f502e..a5f9393 100644 --- a/loopdb/sqlc/querier.go +++ b/loopdb/sqlc/querier.go @@ -9,16 +9,20 @@ import ( ) type Querier interface { + AllDeposits(ctx context.Context) ([]Deposit, error) AllStaticAddresses(ctx context.Context) ([]StaticAddress, error) ConfirmBatch(ctx context.Context, id int32) error + CreateDeposit(ctx context.Context, arg CreateDepositParams) error CreateReservation(ctx context.Context, arg CreateReservationParams) error CreateStaticAddress(ctx context.Context, arg CreateStaticAddressParams) error FetchLiquidityParams(ctx context.Context) ([]byte, error) GetBatchSweeps(ctx context.Context, batchID int32) ([]GetBatchSweepsRow, error) GetBatchSweptAmount(ctx context.Context, batchID int32) (int64, error) + GetDeposit(ctx context.Context, depositID []byte) (Deposit, error) GetInstantOutSwap(ctx context.Context, swapHash []byte) (GetInstantOutSwapRow, error) GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte) ([]InstantoutUpdate, error) GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsRow, error) + GetLatestDepositUpdate(ctx context.Context, depositID []byte) (DepositUpdate, error) GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopInSwapRow, error) GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, error) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopOutSwapRow, error) @@ -32,6 +36,7 @@ type Querier interface { GetSweepStatus(ctx context.Context, swapHash []byte) (bool, error) GetUnconfirmedBatches(ctx context.Context) ([]SweepBatch, error) InsertBatch(ctx context.Context, arg InsertBatchParams) (int32, error) + InsertDepositUpdate(ctx context.Context, arg InsertDepositUpdateParams) error InsertHtlcKeys(ctx context.Context, arg InsertHtlcKeysParams) error InsertInstantOut(ctx context.Context, arg InsertInstantOutParams) error InsertInstantOutUpdate(ctx context.Context, arg InsertInstantOutUpdateParams) error @@ -41,6 +46,7 @@ type Querier interface { InsertSwap(ctx context.Context, arg InsertSwapParams) error InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdateParams) error UpdateBatch(ctx context.Context, arg UpdateBatchParams) error + UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error UpdateReservation(ctx context.Context, arg UpdateReservationParams) error UpsertLiquidityParams(ctx context.Context, params []byte) error diff --git a/loopdb/sqlc/queries/static_address_deposits.sql b/loopdb/sqlc/queries/static_address_deposits.sql new file mode 100644 index 0000000..d09ca3f --- /dev/null +++ b/loopdb/sqlc/queries/static_address_deposits.sql @@ -0,0 +1,66 @@ +-- name: CreateDeposit :exec +INSERT INTO deposits ( + deposit_id, + tx_hash, + out_index, + amount, + confirmation_height, + timeout_sweep_pk_script, + expiry_sweep_txid +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7 + ); + +-- name: UpdateDeposit :exec +UPDATE deposits +SET + tx_hash = $2, + out_index = $3, + confirmation_height = $4, + expiry_sweep_txid = $5 +WHERE + deposits.deposit_id = $1; + +-- name: InsertDepositUpdate :exec +INSERT INTO deposit_updates ( + deposit_id, + update_state, + update_timestamp +) VALUES ( + $1, + $2, + $3 + ); + +-- name: GetDeposit :one +SELECT + * +FROM + deposits +WHERE + deposit_id = $1; + +-- name: AllDeposits :many +SELECT + * +FROM + deposits +ORDER BY + id ASC; + +-- name: GetLatestDepositUpdate :one +SELECT + * +FROM + deposit_updates +WHERE + deposit_id = $1 +ORDER BY + update_timestamp DESC +LIMIT 1; \ No newline at end of file diff --git a/loopdb/sqlc/static_address_deposits.sql.go b/loopdb/sqlc/static_address_deposits.sql.go new file mode 100644 index 0000000..8b4612d --- /dev/null +++ b/loopdb/sqlc/static_address_deposits.sql.go @@ -0,0 +1,197 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: static_address_deposits.sql + +package sqlc + +import ( + "context" + "time" +) + +const allDeposits = `-- name: AllDeposits :many +SELECT + id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid +FROM + deposits +ORDER BY + id ASC +` + +func (q *Queries) AllDeposits(ctx context.Context) ([]Deposit, error) { + rows, err := q.db.QueryContext(ctx, allDeposits) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Deposit + for rows.Next() { + var i Deposit + if err := rows.Scan( + &i.ID, + &i.DepositID, + &i.TxHash, + &i.OutIndex, + &i.Amount, + &i.ConfirmationHeight, + &i.TimeoutSweepPkScript, + &i.ExpirySweepTxid, + ); 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 createDeposit = `-- name: CreateDeposit :exec +INSERT INTO deposits ( + deposit_id, + tx_hash, + out_index, + amount, + confirmation_height, + timeout_sweep_pk_script, + expiry_sweep_txid +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7 + ) +` + +type CreateDepositParams struct { + DepositID []byte + TxHash []byte + OutIndex int32 + Amount int64 + ConfirmationHeight int64 + TimeoutSweepPkScript []byte + ExpirySweepTxid []byte +} + +func (q *Queries) CreateDeposit(ctx context.Context, arg CreateDepositParams) error { + _, err := q.db.ExecContext(ctx, createDeposit, + arg.DepositID, + arg.TxHash, + arg.OutIndex, + arg.Amount, + arg.ConfirmationHeight, + arg.TimeoutSweepPkScript, + arg.ExpirySweepTxid, + ) + return err +} + +const getDeposit = `-- name: GetDeposit :one +SELECT + id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid +FROM + deposits +WHERE + deposit_id = $1 +` + +func (q *Queries) GetDeposit(ctx context.Context, depositID []byte) (Deposit, error) { + row := q.db.QueryRowContext(ctx, getDeposit, depositID) + var i Deposit + err := row.Scan( + &i.ID, + &i.DepositID, + &i.TxHash, + &i.OutIndex, + &i.Amount, + &i.ConfirmationHeight, + &i.TimeoutSweepPkScript, + &i.ExpirySweepTxid, + ) + return i, err +} + +const getLatestDepositUpdate = `-- name: GetLatestDepositUpdate :one +SELECT + id, deposit_id, update_state, update_timestamp +FROM + deposit_updates +WHERE + deposit_id = $1 +ORDER BY + update_timestamp DESC +LIMIT 1 +` + +func (q *Queries) GetLatestDepositUpdate(ctx context.Context, depositID []byte) (DepositUpdate, error) { + row := q.db.QueryRowContext(ctx, getLatestDepositUpdate, depositID) + var i DepositUpdate + err := row.Scan( + &i.ID, + &i.DepositID, + &i.UpdateState, + &i.UpdateTimestamp, + ) + return i, err +} + +const insertDepositUpdate = `-- name: InsertDepositUpdate :exec +INSERT INTO deposit_updates ( + deposit_id, + update_state, + update_timestamp +) VALUES ( + $1, + $2, + $3 + ) +` + +type InsertDepositUpdateParams struct { + DepositID []byte + UpdateState string + UpdateTimestamp time.Time +} + +func (q *Queries) InsertDepositUpdate(ctx context.Context, arg InsertDepositUpdateParams) error { + _, err := q.db.ExecContext(ctx, insertDepositUpdate, arg.DepositID, arg.UpdateState, arg.UpdateTimestamp) + return err +} + +const updateDeposit = `-- name: UpdateDeposit :exec +UPDATE deposits +SET + tx_hash = $2, + out_index = $3, + confirmation_height = $4, + expiry_sweep_txid = $5 +WHERE + deposits.deposit_id = $1 +` + +type UpdateDepositParams struct { + DepositID []byte + TxHash []byte + OutIndex int32 + ConfirmationHeight int64 + ExpirySweepTxid []byte +} + +func (q *Queries) UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error { + _, err := q.db.ExecContext(ctx, updateDeposit, + arg.DepositID, + arg.TxHash, + arg.OutIndex, + arg.ConfirmationHeight, + arg.ExpirySweepTxid, + ) + return err +} diff --git a/loopdb/sqlc/static_addresses.sql.go b/loopdb/sqlc/static_addresses.sql.go index cfe1a46..23902a2 100644 --- a/loopdb/sqlc/static_addresses.sql.go +++ b/loopdb/sqlc/static_addresses.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.17.2 +// sqlc v1.25.0 // source: static_addresses.sql package sqlc diff --git a/looprpc/client.pb.go b/looprpc/client.pb.go index d479ebe..5e48c26 100644 --- a/looprpc/client.pb.go +++ b/looprpc/client.pb.go @@ -4043,7 +4043,7 @@ func (x *InstantOut) GetSweepTxId() string { return "" } -type NewAddressRequest struct { +type NewStaticAddressRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -4053,8 +4053,8 @@ type NewAddressRequest struct { ClientKey []byte `protobuf:"bytes,1,opt,name=client_key,json=clientKey,proto3" json:"client_key,omitempty"` } -func (x *NewAddressRequest) Reset() { - *x = NewAddressRequest{} +func (x *NewStaticAddressRequest) Reset() { + *x = NewStaticAddressRequest{} if protoimpl.UnsafeEnabled { mi := &file_client_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4062,13 +4062,13 @@ func (x *NewAddressRequest) Reset() { } } -func (x *NewAddressRequest) String() string { +func (x *NewStaticAddressRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NewAddressRequest) ProtoMessage() {} +func (*NewStaticAddressRequest) ProtoMessage() {} -func (x *NewAddressRequest) ProtoReflect() protoreflect.Message { +func (x *NewStaticAddressRequest) ProtoReflect() protoreflect.Message { mi := &file_client_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4080,19 +4080,19 @@ func (x *NewAddressRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NewAddressRequest.ProtoReflect.Descriptor instead. -func (*NewAddressRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use NewStaticAddressRequest.ProtoReflect.Descriptor instead. +func (*NewStaticAddressRequest) Descriptor() ([]byte, []int) { return file_client_proto_rawDescGZIP(), []int{43} } -func (x *NewAddressRequest) GetClientKey() []byte { +func (x *NewStaticAddressRequest) GetClientKey() []byte { if x != nil { return x.ClientKey } return nil } -type NewAddressResponse struct { +type NewStaticAddressResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -4105,8 +4105,8 @@ type NewAddressResponse struct { Expiry uint32 `protobuf:"varint,2,opt,name=expiry,proto3" json:"expiry,omitempty"` } -func (x *NewAddressResponse) Reset() { - *x = NewAddressResponse{} +func (x *NewStaticAddressResponse) Reset() { + *x = NewStaticAddressResponse{} if protoimpl.UnsafeEnabled { mi := &file_client_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4114,13 +4114,13 @@ func (x *NewAddressResponse) Reset() { } } -func (x *NewAddressResponse) String() string { +func (x *NewStaticAddressResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NewAddressResponse) ProtoMessage() {} +func (*NewStaticAddressResponse) ProtoMessage() {} -func (x *NewAddressResponse) ProtoReflect() protoreflect.Message { +func (x *NewStaticAddressResponse) ProtoReflect() protoreflect.Message { mi := &file_client_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4132,26 +4132,26 @@ func (x *NewAddressResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NewAddressResponse.ProtoReflect.Descriptor instead. -func (*NewAddressResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use NewStaticAddressResponse.ProtoReflect.Descriptor instead. +func (*NewStaticAddressResponse) Descriptor() ([]byte, []int) { return file_client_proto_rawDescGZIP(), []int{44} } -func (x *NewAddressResponse) GetAddress() string { +func (x *NewStaticAddressResponse) GetAddress() string { if x != nil { return x.Address } return "" } -func (x *NewAddressResponse) GetExpiry() uint32 { +func (x *NewStaticAddressResponse) GetExpiry() uint32 { if x != nil { return x.Expiry } return 0 } -type ListUnspentRequest struct { +type ListUnspentDepositsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -4165,8 +4165,8 @@ type ListUnspentRequest struct { MaxConfs int32 `protobuf:"varint,2,opt,name=max_confs,json=maxConfs,proto3" json:"max_confs,omitempty"` } -func (x *ListUnspentRequest) Reset() { - *x = ListUnspentRequest{} +func (x *ListUnspentDepositsRequest) Reset() { + *x = ListUnspentDepositsRequest{} if protoimpl.UnsafeEnabled { mi := &file_client_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4174,13 +4174,13 @@ func (x *ListUnspentRequest) Reset() { } } -func (x *ListUnspentRequest) String() string { +func (x *ListUnspentDepositsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListUnspentRequest) ProtoMessage() {} +func (*ListUnspentDepositsRequest) ProtoMessage() {} -func (x *ListUnspentRequest) ProtoReflect() protoreflect.Message { +func (x *ListUnspentDepositsRequest) ProtoReflect() protoreflect.Message { mi := &file_client_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4192,26 +4192,26 @@ func (x *ListUnspentRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListUnspentRequest.ProtoReflect.Descriptor instead. -func (*ListUnspentRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ListUnspentDepositsRequest.ProtoReflect.Descriptor instead. +func (*ListUnspentDepositsRequest) Descriptor() ([]byte, []int) { return file_client_proto_rawDescGZIP(), []int{45} } -func (x *ListUnspentRequest) GetMinConfs() int32 { +func (x *ListUnspentDepositsRequest) GetMinConfs() int32 { if x != nil { return x.MinConfs } return 0 } -func (x *ListUnspentRequest) GetMaxConfs() int32 { +func (x *ListUnspentDepositsRequest) GetMaxConfs() int32 { if x != nil { return x.MaxConfs } return 0 } -type ListUnspentResponse struct { +type ListUnspentDepositsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -4221,8 +4221,8 @@ type ListUnspentResponse struct { Utxos []*Utxo `protobuf:"bytes,1,rep,name=utxos,proto3" json:"utxos,omitempty"` } -func (x *ListUnspentResponse) Reset() { - *x = ListUnspentResponse{} +func (x *ListUnspentDepositsResponse) Reset() { + *x = ListUnspentDepositsResponse{} if protoimpl.UnsafeEnabled { mi := &file_client_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4230,13 +4230,13 @@ func (x *ListUnspentResponse) Reset() { } } -func (x *ListUnspentResponse) String() string { +func (x *ListUnspentDepositsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListUnspentResponse) ProtoMessage() {} +func (*ListUnspentDepositsResponse) ProtoMessage() {} -func (x *ListUnspentResponse) ProtoReflect() protoreflect.Message { +func (x *ListUnspentDepositsResponse) ProtoReflect() protoreflect.Message { mi := &file_client_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4248,12 +4248,12 @@ func (x *ListUnspentResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListUnspentResponse.ProtoReflect.Descriptor instead. -func (*ListUnspentResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ListUnspentDepositsResponse.ProtoReflect.Descriptor instead. +func (*ListUnspentDepositsResponse) Descriptor() ([]byte, []int) { return file_client_proto_rawDescGZIP(), []int{46} } -func (x *ListUnspentResponse) GetUtxos() []*Utxo { +func (x *ListUnspentDepositsResponse) GetUtxos() []*Utxo { if x != nil { return x.Utxos } @@ -4821,202 +4821,205 @@ var file_client_proto_rawDesc = []byte{ 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 0x49, 0x64, 0x22, 0x32, 0x0a, 0x11, 0x4e, 0x65, - 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x46, - 0x0a, 0x12, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x4e, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, - 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x61, - 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x22, 0x3a, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, - 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, - 0x05, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x52, 0x05, 0x75, 0x74, 0x78, - 0x6f, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x04, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x61, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x61, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, - 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 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, 0xeb, 0x02, 0x0a, - 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, - 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, - 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, - 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, - 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, - 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, - 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, - 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, - 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, - 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, - 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x41, - 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, 0x12, 0x31, 0x0a, 0x2d, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, - 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, - 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x08, 0x12, 0x2b, 0x0a, - 0x27, 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, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, - 0x4d, 0x54, 0x5f, 0x53, 0x57, 0x45, 0x50, 0x54, 0x10, 0x09, 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, 0x96, 0x0b, 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, 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, + 0x09, 0x73, 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x17, 0x4e, 0x65, + 0x77, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x4b, 0x65, 0x79, 0x22, 0x4c, 0x0a, 0x18, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x79, 0x22, 0x56, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, + 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x12, 0x1b, 0x0a, + 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x08, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x22, 0x42, 0x0a, 0x1b, 0x4c, 0x69, + 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x75, 0x74, 0x78, + 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x52, 0x05, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x8e, + 0x01, 0x0a, 0x04, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x61, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0xeb, 0x02, 0x0a, 0x0d, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, + 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, + 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, + 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x25, + 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, + 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, + 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, + 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, + 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x41, 0x42, 0x41, 0x4e, 0x44, + 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, 0x12, 0x31, 0x0a, 0x2d, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, + 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x5f, + 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x08, 0x12, 0x2b, 0x0a, 0x27, 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, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x54, 0x5f, 0x53, + 0x57, 0x45, 0x50, 0x54, 0x10, 0x09, 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, 0xd1, 0x0c, 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, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, + 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, + 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, + 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, - 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, - 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, - 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, - 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x57, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, - 0x75, 0x6f, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, - 0x4f, 0x75, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6f, - 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x4f, 0x75, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xa6, 0x01, - 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, - 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, - 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, - 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, - 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, + 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, + 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, + 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, + 0x10, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x4f, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, + 0x0f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x4e, 0x65, 0x77, + 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x60, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, + 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, + 0x70, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 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 ( @@ -5084,10 +5087,10 @@ var file_client_proto_goTypes = []interface{}{ (*ListInstantOutsRequest)(nil), // 47: looprpc.ListInstantOutsRequest (*ListInstantOutsResponse)(nil), // 48: looprpc.ListInstantOutsResponse (*InstantOut)(nil), // 49: looprpc.InstantOut - (*NewAddressRequest)(nil), // 50: looprpc.NewAddressRequest - (*NewAddressResponse)(nil), // 51: looprpc.NewAddressResponse - (*ListUnspentRequest)(nil), // 52: looprpc.ListUnspentRequest - (*ListUnspentResponse)(nil), // 53: looprpc.ListUnspentResponse + (*NewStaticAddressRequest)(nil), // 50: looprpc.NewStaticAddressRequest + (*NewStaticAddressResponse)(nil), // 51: looprpc.NewStaticAddressResponse + (*ListUnspentDepositsRequest)(nil), // 52: looprpc.ListUnspentDepositsRequest + (*ListUnspentDepositsResponse)(nil), // 53: looprpc.ListUnspentDepositsResponse (*Utxo)(nil), // 54: looprpc.Utxo (*swapserverrpc.RouteHint)(nil), // 55: looprpc.RouteHint } @@ -5116,7 +5119,7 @@ var file_client_proto_depIdxs = []int32{ 36, // 21: looprpc.SuggestSwapsResponse.disqualified:type_name -> looprpc.Disqualified 42, // 22: looprpc.ListReservationsResponse.reservations:type_name -> looprpc.ClientReservation 49, // 23: looprpc.ListInstantOutsResponse.swaps:type_name -> looprpc.InstantOut - 54, // 24: looprpc.ListUnspentResponse.utxos:type_name -> looprpc.Utxo + 54, // 24: looprpc.ListUnspentDepositsResponse.utxos:type_name -> looprpc.Utxo 7, // 25: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest 8, // 26: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest 10, // 27: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest @@ -5137,8 +5140,8 @@ var file_client_proto_depIdxs = []int32{ 43, // 42: looprpc.SwapClient.InstantOut:input_type -> looprpc.InstantOutRequest 45, // 43: looprpc.SwapClient.InstantOutQuote:input_type -> looprpc.InstantOutQuoteRequest 47, // 44: looprpc.SwapClient.ListInstantOuts:input_type -> looprpc.ListInstantOutsRequest - 50, // 45: looprpc.StaticAddressClient.NewAddress:input_type -> looprpc.NewAddressRequest - 52, // 46: looprpc.StaticAddressClient.ListUnspent:input_type -> looprpc.ListUnspentRequest + 50, // 45: looprpc.SwapClient.NewStaticAddress:input_type -> looprpc.NewStaticAddressRequest + 52, // 46: looprpc.SwapClient.ListUnspentDeposits:input_type -> looprpc.ListUnspentDepositsRequest 9, // 47: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse 9, // 48: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse 11, // 49: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus @@ -5159,8 +5162,8 @@ var file_client_proto_depIdxs = []int32{ 44, // 64: looprpc.SwapClient.InstantOut:output_type -> looprpc.InstantOutResponse 46, // 65: looprpc.SwapClient.InstantOutQuote:output_type -> looprpc.InstantOutQuoteResponse 48, // 66: looprpc.SwapClient.ListInstantOuts:output_type -> looprpc.ListInstantOutsResponse - 51, // 67: looprpc.StaticAddressClient.NewAddress:output_type -> looprpc.NewAddressResponse - 53, // 68: looprpc.StaticAddressClient.ListUnspent:output_type -> looprpc.ListUnspentResponse + 51, // 67: looprpc.SwapClient.NewStaticAddress:output_type -> looprpc.NewStaticAddressResponse + 53, // 68: looprpc.SwapClient.ListUnspentDeposits:output_type -> looprpc.ListUnspentDepositsResponse 47, // [47:69] is the sub-list for method output_type 25, // [25:47] is the sub-list for method input_type 25, // [25:25] is the sub-list for extension type_name @@ -5691,7 +5694,7 @@ func file_client_proto_init() { } } file_client_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NewAddressRequest); i { + switch v := v.(*NewStaticAddressRequest); i { case 0: return &v.state case 1: @@ -5703,7 +5706,7 @@ func file_client_proto_init() { } } file_client_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NewAddressResponse); i { + switch v := v.(*NewStaticAddressResponse); i { case 0: return &v.state case 1: @@ -5715,7 +5718,7 @@ func file_client_proto_init() { } } file_client_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUnspentRequest); i { + switch v := v.(*ListUnspentDepositsRequest); i { case 0: return &v.state case 1: @@ -5727,7 +5730,7 @@ func file_client_proto_init() { } } file_client_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUnspentResponse); i { + switch v := v.(*ListUnspentDepositsResponse); i { case 0: return &v.state case 1: @@ -5759,7 +5762,7 @@ func file_client_proto_init() { NumEnums: 7, NumMessages: 48, NumExtensions: 0, - NumServices: 2, + NumServices: 1, }, GoTypes: file_client_proto_goTypes, DependencyIndexes: file_client_proto_depIdxs, diff --git a/looprpc/client.proto b/looprpc/client.proto index 738fe03..42460d4 100644 --- a/looprpc/client.proto +++ b/looprpc/client.proto @@ -134,6 +134,18 @@ service SwapClient { */ rpc ListInstantOuts (ListInstantOutsRequest) returns (ListInstantOutsResponse); + + /* loop: `static newstaticaddress` + NewStaticAddress requests a new static address for loop-ins from the server. + */ + rpc NewStaticAddress (NewStaticAddressRequest) + returns (NewStaticAddressResponse); + + /* loop: `static listunspentdeposits` + ListUnspentDeposits returns a list of utxos deposited at a static address. + */ + rpc ListUnspentDeposits (ListUnspentDepositsRequest) + returns (ListUnspentDepositsResponse); } message LoopOutRequest { @@ -1431,26 +1443,14 @@ message InstantOut { string sweep_tx_id = 5; } -service StaticAddressClient { - /* - NewAddress requests a new static address for loop-ins from the server. - */ - rpc NewAddress (NewAddressRequest) returns (NewAddressResponse); - - /* - ListUnspent returns a list of utxos behind a static address. - */ - rpc ListUnspent (ListUnspentRequest) returns (ListUnspentResponse); -} - -message NewAddressRequest { +message NewStaticAddressRequest { /* The client's public key for the 2-of-2 MuSig2 taproot static address. */ bytes client_key = 1; } -message NewAddressResponse { +message NewStaticAddressResponse { /* The taproot static address. */ @@ -1462,7 +1462,7 @@ message NewAddressResponse { uint32 expiry = 2; } -message ListUnspentRequest { +message ListUnspentDepositsRequest { /* The number of minimum confirmations a utxo must have to be listed. */ @@ -1475,7 +1475,7 @@ message ListUnspentRequest { int32 max_confs = 2; } -message ListUnspentResponse { +message ListUnspentDepositsResponse { /* A list of utxos behind the static address. */ diff --git a/looprpc/client.swagger.json b/looprpc/client.swagger.json index a70776e..11a49fa 100644 --- a/looprpc/client.swagger.json +++ b/looprpc/client.swagger.json @@ -7,9 +7,6 @@ "tags": [ { "name": "SwapClient" - }, - { - "name": "StaticAddressClient" } ], "consumes": [ @@ -1056,7 +1053,7 @@ } } }, - "looprpcListUnspentResponse": { + "looprpcListUnspentDepositsResponse": { "type": "object", "properties": { "utxos": { @@ -1293,7 +1290,7 @@ } } }, - "looprpcNewAddressResponse": { + "looprpcNewStaticAddressResponse": { "type": "object", "properties": { "address": { diff --git a/looprpc/client_grpc.pb.go b/looprpc/client_grpc.pb.go index b7de6cb..56b8163 100644 --- a/looprpc/client_grpc.pb.go +++ b/looprpc/client_grpc.pb.go @@ -97,6 +97,12 @@ type SwapClientClient interface { //ListInstantOuts returns a list of all currently known instant out swaps and //their current status. ListInstantOuts(ctx context.Context, in *ListInstantOutsRequest, opts ...grpc.CallOption) (*ListInstantOutsResponse, error) + // loop: `static newstaticaddress` + //NewStaticAddress requests a new static address for loop-ins from the server. + NewStaticAddress(ctx context.Context, in *NewStaticAddressRequest, opts ...grpc.CallOption) (*NewStaticAddressResponse, error) + // loop: `static listunspentdeposits` + //ListUnspentDeposits returns a list of utxos deposited at a static address. + ListUnspentDeposits(ctx context.Context, in *ListUnspentDepositsRequest, opts ...grpc.CallOption) (*ListUnspentDepositsResponse, error) } type swapClientClient struct { @@ -310,6 +316,24 @@ func (c *swapClientClient) ListInstantOuts(ctx context.Context, in *ListInstantO return out, nil } +func (c *swapClientClient) NewStaticAddress(ctx context.Context, in *NewStaticAddressRequest, opts ...grpc.CallOption) (*NewStaticAddressResponse, error) { + out := new(NewStaticAddressResponse) + err := c.cc.Invoke(ctx, "/looprpc.SwapClient/NewStaticAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *swapClientClient) ListUnspentDeposits(ctx context.Context, in *ListUnspentDepositsRequest, opts ...grpc.CallOption) (*ListUnspentDepositsResponse, error) { + out := new(ListUnspentDepositsResponse) + err := c.cc.Invoke(ctx, "/looprpc.SwapClient/ListUnspentDeposits", 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 @@ -393,6 +417,12 @@ type SwapClientServer interface { //ListInstantOuts returns a list of all currently known instant out swaps and //their current status. ListInstantOuts(context.Context, *ListInstantOutsRequest) (*ListInstantOutsResponse, error) + // loop: `static newstaticaddress` + //NewStaticAddress requests a new static address for loop-ins from the server. + NewStaticAddress(context.Context, *NewStaticAddressRequest) (*NewStaticAddressResponse, error) + // loop: `static listunspentdeposits` + //ListUnspentDeposits returns a list of utxos deposited at a static address. + ListUnspentDeposits(context.Context, *ListUnspentDepositsRequest) (*ListUnspentDepositsResponse, error) mustEmbedUnimplementedSwapClientServer() } @@ -460,6 +490,12 @@ func (UnimplementedSwapClientServer) InstantOutQuote(context.Context, *InstantOu func (UnimplementedSwapClientServer) ListInstantOuts(context.Context, *ListInstantOutsRequest) (*ListInstantOutsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListInstantOuts not implemented") } +func (UnimplementedSwapClientServer) NewStaticAddress(context.Context, *NewStaticAddressRequest) (*NewStaticAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewStaticAddress not implemented") +} +func (UnimplementedSwapClientServer) ListUnspentDeposits(context.Context, *ListUnspentDepositsRequest) (*ListUnspentDepositsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListUnspentDeposits not implemented") +} func (UnimplementedSwapClientServer) mustEmbedUnimplementedSwapClientServer() {} // UnsafeSwapClientServer may be embedded to opt out of forward compatibility for this service. @@ -836,6 +872,42 @@ func _SwapClient_ListInstantOuts_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _SwapClient_NewStaticAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewStaticAddressRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SwapClientServer).NewStaticAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.SwapClient/NewStaticAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SwapClientServer).NewStaticAddress(ctx, req.(*NewStaticAddressRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SwapClient_ListUnspentDeposits_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListUnspentDepositsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SwapClientServer).ListUnspentDeposits(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.SwapClient/ListUnspentDeposits", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SwapClientServer).ListUnspentDeposits(ctx, req.(*ListUnspentDepositsRequest)) + } + 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) @@ -919,6 +991,14 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListInstantOuts", Handler: _SwapClient_ListInstantOuts_Handler, }, + { + MethodName: "NewStaticAddress", + Handler: _SwapClient_NewStaticAddress_Handler, + }, + { + MethodName: "ListUnspentDeposits", + Handler: _SwapClient_ListUnspentDeposits_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -929,133 +1009,3 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{ }, Metadata: "client.proto", } - -// StaticAddressClientClient is the client API for StaticAddressClient 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 StaticAddressClientClient interface { - // - //NewAddress requests a new static address for loop-ins from the server. - NewAddress(ctx context.Context, in *NewAddressRequest, opts ...grpc.CallOption) (*NewAddressResponse, error) - // - //ListUnspent returns a list of utxos behind a static address. - ListUnspent(ctx context.Context, in *ListUnspentRequest, opts ...grpc.CallOption) (*ListUnspentResponse, error) -} - -type staticAddressClientClient struct { - cc grpc.ClientConnInterface -} - -func NewStaticAddressClientClient(cc grpc.ClientConnInterface) StaticAddressClientClient { - return &staticAddressClientClient{cc} -} - -func (c *staticAddressClientClient) NewAddress(ctx context.Context, in *NewAddressRequest, opts ...grpc.CallOption) (*NewAddressResponse, error) { - out := new(NewAddressResponse) - err := c.cc.Invoke(ctx, "/looprpc.StaticAddressClient/NewAddress", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *staticAddressClientClient) ListUnspent(ctx context.Context, in *ListUnspentRequest, opts ...grpc.CallOption) (*ListUnspentResponse, error) { - out := new(ListUnspentResponse) - err := c.cc.Invoke(ctx, "/looprpc.StaticAddressClient/ListUnspent", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// StaticAddressClientServer is the server API for StaticAddressClient service. -// All implementations must embed UnimplementedStaticAddressClientServer -// for forward compatibility -type StaticAddressClientServer interface { - // - //NewAddress requests a new static address for loop-ins from the server. - NewAddress(context.Context, *NewAddressRequest) (*NewAddressResponse, error) - // - //ListUnspent returns a list of utxos behind a static address. - ListUnspent(context.Context, *ListUnspentRequest) (*ListUnspentResponse, error) - mustEmbedUnimplementedStaticAddressClientServer() -} - -// UnimplementedStaticAddressClientServer must be embedded to have forward compatible implementations. -type UnimplementedStaticAddressClientServer struct { -} - -func (UnimplementedStaticAddressClientServer) NewAddress(context.Context, *NewAddressRequest) (*NewAddressResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method NewAddress not implemented") -} -func (UnimplementedStaticAddressClientServer) ListUnspent(context.Context, *ListUnspentRequest) (*ListUnspentResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListUnspent not implemented") -} -func (UnimplementedStaticAddressClientServer) mustEmbedUnimplementedStaticAddressClientServer() {} - -// UnsafeStaticAddressClientServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to StaticAddressClientServer will -// result in compilation errors. -type UnsafeStaticAddressClientServer interface { - mustEmbedUnimplementedStaticAddressClientServer() -} - -func RegisterStaticAddressClientServer(s grpc.ServiceRegistrar, srv StaticAddressClientServer) { - s.RegisterService(&StaticAddressClient_ServiceDesc, srv) -} - -func _StaticAddressClient_NewAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(NewAddressRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(StaticAddressClientServer).NewAddress(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/looprpc.StaticAddressClient/NewAddress", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(StaticAddressClientServer).NewAddress(ctx, req.(*NewAddressRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _StaticAddressClient_ListUnspent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListUnspentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(StaticAddressClientServer).ListUnspent(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/looprpc.StaticAddressClient/ListUnspent", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(StaticAddressClientServer).ListUnspent(ctx, req.(*ListUnspentRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// StaticAddressClient_ServiceDesc is the grpc.ServiceDesc for StaticAddressClient service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var StaticAddressClient_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "looprpc.StaticAddressClient", - HandlerType: (*StaticAddressClientServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "NewAddress", - Handler: _StaticAddressClient_NewAddress_Handler, - }, - { - MethodName: "ListUnspent", - Handler: _StaticAddressClient_ListUnspent_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "client.proto", -} diff --git a/looprpc/staticaddressclient.pb.json.go b/looprpc/staticaddressclient.pb.json.go deleted file mode 100644 index afa5506..0000000 --- a/looprpc/staticaddressclient.pb.json.go +++ /dev/null @@ -1,73 +0,0 @@ -// Code generated by falafel 0.9.1. DO NOT EDIT. -// source: client.proto - -package looprpc - -import ( - "context" - - gateway "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "google.golang.org/grpc" - "google.golang.org/protobuf/encoding/protojson" -) - -func RegisterStaticAddressClientJSONCallbacks(registry map[string]func(ctx context.Context, - conn *grpc.ClientConn, reqJSON string, callback func(string, error))) { - - marshaler := &gateway.JSONPb{ - MarshalOptions: protojson.MarshalOptions{ - UseProtoNames: true, - EmitUnpopulated: true, - }, - } - - registry["looprpc.StaticAddressClient.NewAddress"] = func(ctx context.Context, - conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { - - req := &NewAddressRequest{} - err := marshaler.Unmarshal([]byte(reqJSON), req) - if err != nil { - callback("", err) - return - } - - client := NewStaticAddressClientClient(conn) - resp, err := client.NewAddress(ctx, req) - if err != nil { - callback("", err) - return - } - - respBytes, err := marshaler.Marshal(resp) - if err != nil { - callback("", err) - return - } - callback(string(respBytes), nil) - } - - registry["looprpc.StaticAddressClient.ListUnspent"] = func(ctx context.Context, - conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { - - req := &ListUnspentRequest{} - err := marshaler.Unmarshal([]byte(reqJSON), req) - if err != nil { - callback("", err) - return - } - - client := NewStaticAddressClientClient(conn) - resp, err := client.ListUnspent(ctx, req) - if err != nil { - callback("", err) - return - } - - respBytes, err := marshaler.Marshal(resp) - if err != nil { - callback("", err) - return - } - callback(string(respBytes), nil) - } -} diff --git a/looprpc/swapclient.pb.json.go b/looprpc/swapclient.pb.json.go index a715cdd..e9dcb59 100644 --- a/looprpc/swapclient.pb.json.go +++ b/looprpc/swapclient.pb.json.go @@ -537,4 +537,54 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex } callback(string(respBytes), nil) } + + registry["looprpc.SwapClient.NewStaticAddress"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &NewStaticAddressRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewSwapClientClient(conn) + resp, err := client.NewStaticAddress(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + + registry["looprpc.SwapClient.ListUnspentDeposits"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &ListUnspentDepositsRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewSwapClientClient(conn) + resp, err := client.ListUnspentDeposits(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } } diff --git a/staticaddr/interface.go b/staticaddr/address/interface.go similarity index 66% rename from staticaddr/interface.go rename to staticaddr/address/interface.go index 8b424fb..99021e8 100644 --- a/staticaddr/interface.go +++ b/staticaddr/address/interface.go @@ -1,39 +1,38 @@ -package staticaddr +package address import ( "context" "fmt" "github.com/btcsuite/btcd/btcec/v2" + "github.com/lightninglabs/loop/staticaddr" "github.com/lightningnetwork/lnd/keychain" ) var ( ErrAddressAlreadyExists = fmt.Errorf("address already exists") - ErrAddressNotFound = fmt.Errorf("address not found") ) -// AddressStore is the database interface that is used to store and retrieve +// Store is the database interface that is used to store and retrieve // static addresses. -type AddressStore interface { +type Store interface { // CreateStaticAddress inserts a new static address with its parameters // into the store. - CreateStaticAddress(ctx context.Context, - addrParams *AddressParameters) error + CreateStaticAddress(ctx context.Context, addrParams *Parameters) error // GetStaticAddress fetches static address parameters for a given // address ID. - GetStaticAddress(ctx context.Context, - pkScript []byte) (*AddressParameters, error) + GetStaticAddress(ctx context.Context, pkScript []byte) (*Parameters, + error) // GetAllStaticAddresses retrieves all static addresses from the store. - GetAllStaticAddresses(ctx context.Context) ( - []*AddressParameters, error) + GetAllStaticAddresses(ctx context.Context) ([]*Parameters, + error) } -// AddressParameters holds all the necessary information for the 2-of-2 multisig +// Parameters holds all the necessary information for the 2-of-2 multisig // address. -type AddressParameters struct { +type Parameters struct { // ClientPubkey is the client's pubkey for the static address. It is // used for the 2-of-2 funding output as well as for the client's // timeout path. @@ -54,5 +53,5 @@ type AddressParameters struct { KeyLocator keychain.KeyLocator // ProtocolVersion is the protocol version of the static address. - ProtocolVersion AddressProtocolVersion + ProtocolVersion staticaddr.AddressProtocolVersion } diff --git a/staticaddr/manager.go b/staticaddr/address/manager.go similarity index 75% rename from staticaddr/manager.go rename to staticaddr/address/manager.go index a963c86..7100977 100644 --- a/staticaddr/manager.go +++ b/staticaddr/address/manager.go @@ -1,4 +1,4 @@ -package staticaddr +package address import ( "bytes" @@ -10,8 +10,9 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btclog" "github.com/lightninglabs/lndclient" - "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/staticaddr" "github.com/lightninglabs/loop/staticaddr/script" "github.com/lightninglabs/loop/swap" staticaddressrpc "github.com/lightninglabs/loop/swapserverrpc" @@ -20,18 +21,22 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" ) +var ( + log btclog.Logger +) + // ManagerConfig holds the configuration for the address manager. type ManagerConfig struct { // AddressClient is the client that communicates with the loop server // to manage static addresses. AddressClient staticaddressrpc.StaticAddressServerClient - // SwapClient provides loop rpc functionality. - SwapClient *loop.Client + // FetchL402 is the function used to fetch the l402 token. + FetchL402 func(context.Context) error // Store is the database store that is used to store static address // related records. - Store AddressStore + Store Store // WalletKit is the wallet client that is used to derive new keys from // lnd's wallet. @@ -46,30 +51,20 @@ type ManagerConfig struct { type Manager struct { cfg *ManagerConfig - initChan chan struct{} - sync.Mutex } -// NewAddressManager creates a new address manager. -func NewAddressManager(cfg *ManagerConfig) *Manager { +// NewManager creates a new address manager. +func NewManager(cfg *ManagerConfig) *Manager { + log = staticaddr.GetLogger() return &Manager{ - cfg: cfg, - initChan: make(chan struct{}), + cfg: cfg, } } // Run runs the address manager. func (m *Manager) Run(ctx context.Context) error { - log.Debugf("Starting address manager.") - defer log.Debugf("Address manager stopped.") - - // Communicate to the caller that the address manager has completed its - // initialization. - close(m.initChan) - <-ctx.Done() - return nil } @@ -99,7 +94,7 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot, // We are fetching a new L402 token from the server. There is one static // address per L402 token allowed. - err = m.cfg.SwapClient.Server.FetchL402(ctx) + err = m.cfg.FetchL402(ctx) if err != nil { return nil, err } @@ -113,7 +108,7 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot, // Send our clientPubKey to the server and wait for the server to // respond with he serverPubKey and the static address CSV expiry. - protocolVersion := CurrentRPCProtocolVersion() + protocolVersion := staticaddr.CurrentRPCProtocolVersion() resp, err := m.cfg.AddressClient.ServerNewAddress( ctx, &staticaddressrpc.ServerNewAddressRequest{ ProtocolVersion: protocolVersion, @@ -146,7 +141,7 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot, // Create the static address from the parameters the server provided and // store all parameters in the database. - addrParams := &AddressParameters{ + addrParams := &Parameters{ ClientPubkey: clientPubKey.PubKey, ServerPubkey: serverPubKey, PkScript: pkScript, @@ -155,7 +150,9 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot, Family: clientPubKey.Family, Index: clientPubKey.Index, }, - ProtocolVersion: AddressProtocolVersion(protocolVersion), + ProtocolVersion: staticaddr.AddressProtocolVersion( + protocolVersion, + ), } err = m.cfg.Store.CreateStaticAddress(ctx, addrParams) if err != nil { @@ -172,7 +169,7 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot, return nil, err } - log.Infof("imported static address taproot script to lnd wallet: %v", + log.Infof("Imported static address taproot script to lnd wallet: %v", addr) return m.getTaprootAddress( @@ -197,12 +194,6 @@ func (m *Manager) getTaprootAddress(clientPubkey, ) } -// WaitInitComplete waits until the address manager has completed its setup. -func (m *Manager) WaitInitComplete() { - defer log.Debugf("Address manager initiation complete.") - <-m.initChan -} - // ListUnspentRaw returns a list of utxos at the static address. func (m *Manager) ListUnspentRaw(ctx context.Context, minConfs, maxConfs int32) (*btcutil.AddressTaproot, []*lnwallet.Utxo, error) { @@ -213,7 +204,7 @@ func (m *Manager) ListUnspentRaw(ctx context.Context, minConfs, return nil, nil, err case len(addresses) == 0: - return nil, nil, fmt.Errorf("no address found") + return nil, nil, nil case len(addresses) > 1: return nil, nil, fmt.Errorf("more than one address found") @@ -249,3 +240,52 @@ func (m *Manager) ListUnspentRaw(ctx context.Context, minConfs, return taprootAddress, filteredUtxos, nil } + +// GetStaticAddressParameters returns the parameters of the static address. +func (m *Manager) GetStaticAddressParameters(ctx context.Context) (*Parameters, + error) { + + params, err := m.cfg.Store.GetAllStaticAddresses(ctx) + if err != nil { + return nil, err + } + + if len(params) == 0 { + return nil, fmt.Errorf("no static address parameters found") + } + + return params[0], nil +} + +// GetStaticAddress returns a taproot address for the given client and server +// public keys and expiry. +func (m *Manager) GetStaticAddress(ctx context.Context) (*script.StaticAddress, + error) { + + params, err := m.GetStaticAddressParameters(ctx) + if err != nil { + return nil, err + } + + address, err := script.NewStaticAddress( + input.MuSig2Version100RC2, int64(params.Expiry), + params.ClientPubkey, params.ServerPubkey, + ) + if err != nil { + return nil, err + } + + return address, nil +} + +// ListUnspent returns a list of utxos at the static address. +func (m *Manager) ListUnspent(ctx context.Context, minConfs, + maxConfs int32) ([]*lnwallet.Utxo, error) { + + _, utxos, err := m.ListUnspentRaw(ctx, minConfs, maxConfs) + if err != nil { + return nil, err + } + + return utxos, nil +} diff --git a/staticaddr/address/manager_test.go b/staticaddr/address/manager_test.go new file mode 100644 index 0000000..9a7daaa --- /dev/null +++ b/staticaddr/address/manager_test.go @@ -0,0 +1,147 @@ +package address + +import ( + "context" + "encoding/hex" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/lightninglabs/loop/loopdb" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightninglabs/loop/swap" + "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +var ( + defaultServerPubkeyBytes, _ = hex.DecodeString("021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d") + + defaultServerPubkey, _ = btcec.ParsePubKey(defaultServerPubkeyBytes) + + defaultExpiry = uint32(100) +) + +type mockStaticAddressClient struct { + mock.Mock +} + +func (m *mockStaticAddressClient) ServerNewAddress(ctx context.Context, + in *swapserverrpc.ServerNewAddressRequest, opts ...grpc.CallOption) ( + *swapserverrpc.ServerNewAddressResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.ServerNewAddressResponse), + args.Error(1) +} + +func TestManager(t *testing.T) { + ctxb, cancel := context.WithCancel(context.Background()) + defer cancel() + + testContext := NewAddressManagerTestContext(t) + + // Start the manager. + go func() { + err := testContext.manager.Run(ctxb) + require.NoError(t, err) + }() + + // Create the expected static address. + expectedAddress, err := GenerateExpectedTaprootAddress(testContext) + require.NoError(t, err) + + // Create a new static address. + taprootAddress, err := testContext.manager.NewAddress(ctxb) + require.NoError(t, err) + + // The addresses have to match. + require.Equal(t, expectedAddress.String(), taprootAddress.String()) +} + +// GenerateExpectedTaprootAddress generates the expected taproot address that +// the predefined parameters are supposed to generate. +func GenerateExpectedTaprootAddress(t *ManagerTestContext) ( + *btcutil.AddressTaproot, error) { + + keyIndex := int32(0) + _, pubKey := test.CreateKey(keyIndex) + + keyDescriptor := &keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(swap.StaticAddressKeyFamily), + Index: uint32(keyIndex), + }, + PubKey: pubKey, + } + + staticAddress, err := script.NewStaticAddress( + input.MuSig2Version100RC2, int64(defaultExpiry), + keyDescriptor.PubKey, defaultServerPubkey, + ) + if err != nil { + return nil, err + } + + return btcutil.NewAddressTaproot( + schnorr.SerializePubKey(staticAddress.TaprootKey), + t.manager.cfg.ChainParams, + ) +} + +// ManagerTestContext is a helper struct that contains all the necessary +// components to test the static address manager. +type ManagerTestContext struct { + manager *Manager + context test.Context + mockLnd *test.LndMockServices + mockStaticAddressClient *mockStaticAddressClient +} + +// NewAddressManagerTestContext creates a new test context for the static +// address manager. +func NewAddressManagerTestContext(t *testing.T) *ManagerTestContext { + mockLnd := test.NewMockLnd() + lndContext := test.NewContext(t, mockLnd) + + dbFixture := loopdb.NewTestDB(t) + + store := NewSqlStore(dbFixture.BaseDB) + + mockStaticAddressClient := new(mockStaticAddressClient) + + mockStaticAddressClient.On( + "ServerNewAddress", mock.Anything, mock.Anything, mock.Anything, + ).Return( + &swapserverrpc.ServerNewAddressResponse{ + Params: &swapserverrpc.ServerAddressParameters{ + ServerKey: defaultServerPubkeyBytes, + Expiry: defaultExpiry, + }, + }, nil, + ) + + cfg := &ManagerConfig{ + Store: store, + WalletKit: mockLnd.WalletKit, + ChainParams: mockLnd.ChainParams, + AddressClient: mockStaticAddressClient, + FetchL402: func(context.Context) error { return nil }, + } + + manager := NewManager(cfg) + + return &ManagerTestContext{ + manager: manager, + context: lndContext, + mockLnd: mockLnd, + mockStaticAddressClient: mockStaticAddressClient, + } +} diff --git a/staticaddr/sql_store.go b/staticaddr/address/sql_store.go similarity index 63% rename from staticaddr/sql_store.go rename to staticaddr/address/sql_store.go index 7f14f7c..9d15fc9 100644 --- a/staticaddr/sql_store.go +++ b/staticaddr/address/sql_store.go @@ -1,13 +1,12 @@ -package staticaddr +package address import ( "context" - "errors" "github.com/btcsuite/btcd/btcec/v2" - "github.com/jackc/pgx/v4" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/loopdb/sqlc" + "github.com/lightninglabs/loop/staticaddr" "github.com/lightningnetwork/lnd/keychain" ) @@ -24,46 +23,9 @@ func NewSqlStore(db *loopdb.BaseDB) *SqlStore { } } -// ExecTx is a wrapper for txBody to abstract the creation and commit of a db -// transaction. The db transaction is embedded in a `*sqlc.Queries` that txBody -// needs to use when executing each one of the queries that need to be applied -// atomically. -func (s *SqlStore) ExecTx(ctx context.Context, txOptions loopdb.TxOptions, - txBody func(queries *sqlc.Queries) error) error { - - // Create the db transaction. - tx, err := s.baseDB.BeginTx(ctx, txOptions) - if err != nil { - return err - } - - // Rollback is safe to call even if the tx is already closed, so if the - // tx commits successfully, this is a no-op. - defer func() { - err := tx.Rollback() - switch { - // If the tx was already closed (it was successfully executed) - // we do not need to log that error. - case errors.Is(err, pgx.ErrTxClosed): - return - - // If this is an unexpected error, log it. - case err != nil: - log.Errorf("unable to rollback db tx: %v", err) - } - }() - - if err := txBody(s.baseDB.Queries.WithTx(tx)); err != nil { - return err - } - - // Commit transaction. - return tx.Commit() -} - // CreateStaticAddress creates a static address record in the database. func (s *SqlStore) CreateStaticAddress(ctx context.Context, - addrParams *AddressParameters) error { + addrParams *Parameters) error { createArgs := sqlc.CreateStaticAddressParams{ ClientPubkey: addrParams.ClientPubkey.SerializeCompressed(), @@ -80,7 +42,7 @@ func (s *SqlStore) CreateStaticAddress(ctx context.Context, // GetStaticAddress retrieves static address parameters for a given pkScript. func (s *SqlStore) GetStaticAddress(ctx context.Context, - pkScript []byte) (*AddressParameters, error) { + pkScript []byte) (*Parameters, error) { staticAddress, err := s.baseDB.Queries.GetStaticAddress(ctx, pkScript) if err != nil { @@ -91,15 +53,15 @@ func (s *SqlStore) GetStaticAddress(ctx context.Context, } // GetAllStaticAddresses returns all address known to the server. -func (s *SqlStore) GetAllStaticAddresses(ctx context.Context) ( - []*AddressParameters, error) { +func (s *SqlStore) GetAllStaticAddresses(ctx context.Context) ([]*Parameters, + error) { staticAddresses, err := s.baseDB.Queries.AllStaticAddresses(ctx) if err != nil { return nil, err } - var result []*AddressParameters + var result []*Parameters for _, address := range staticAddresses { res, err := s.toAddressParameters(address) if err != nil { @@ -120,7 +82,7 @@ func (s *SqlStore) Close() { // toAddressParameters transforms a database representation of a static address // to an AddressParameters struct. func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) ( - *AddressParameters, error) { + *Parameters, error) { clientPubkey, err := btcec.ParsePubKey(row.ClientPubkey) if err != nil { @@ -132,7 +94,7 @@ func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) ( return nil, err } - return &AddressParameters{ + return &Parameters{ ClientPubkey: clientPubkey, ServerPubkey: serverPubkey, PkScript: row.Pkscript, @@ -141,6 +103,6 @@ func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) ( Family: keychain.KeyFamily(row.ClientKeyFamily), Index: uint32(row.ClientKeyIndex), }, - ProtocolVersion: AddressProtocolVersion(row.ProtocolVersion), + ProtocolVersion: staticaddr.AddressProtocolVersion(row.ProtocolVersion), }, nil } diff --git a/staticaddr/deposit/actions.go b/staticaddr/deposit/actions.go new file mode 100644 index 0000000..d40eefa --- /dev/null +++ b/staticaddr/deposit/actions.go @@ -0,0 +1,148 @@ +package deposit + +import ( + "errors" + "fmt" + "strings" + + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/staticaddr/script" +) + +const ( + defaultConfTarget = 3 +) + +// PublishDepositExpirySweepAction creates and publishes the timeout transaction +// that spends the deposit from the static address timeout leaf to the +// predefined timeout sweep pkscript. +func (f *FSM) PublishDepositExpirySweepAction(_ fsm.EventContext) fsm.EventType { + msgTx := wire.NewMsgTx(2) + + params, err := f.cfg.AddressManager.GetStaticAddressParameters(f.ctx) + if err != nil { + return fsm.OnError + } + + // Add the deposit outpoint as input to the transaction. + msgTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: f.deposit.OutPoint, + Sequence: params.Expiry, + SignatureScript: nil, + }) + + // Estimate the fee rate of an expiry spend transaction. + feeRateEstimator, err := f.cfg.WalletKit.EstimateFeeRate( + f.ctx, defaultConfTarget, + ) + if err != nil { + return f.HandleError(fmt.Errorf("timeout sweep fee "+ + "estimation failed: %v", err)) + } + + weight := script.ExpirySpendWeight() + + fee := feeRateEstimator.FeeForWeight(weight) + + // We cap the fee at 20% of the deposit value. + if fee > f.deposit.Value/5 { + return f.HandleError(errors.New("fee is greater than 20% of " + + "the deposit value")) + } + + output := &wire.TxOut{ + Value: int64(f.deposit.Value - fee), + PkScript: f.deposit.TimeOutSweepPkScript, + } + msgTx.AddTxOut(output) + + txOut := &wire.TxOut{ + Value: int64(f.deposit.Value), + PkScript: params.PkScript, + } + + prevOut := []*wire.TxOut{txOut} + + signDesc, err := f.SignDescriptor() + if err != nil { + return f.HandleError(err) + } + + rawSigs, err := f.cfg.Signer.SignOutputRaw( + f.ctx, msgTx, []*lndclient.SignDescriptor{signDesc}, prevOut, + ) + if err != nil { + return f.HandleError(err) + } + + address, err := f.cfg.AddressManager.GetStaticAddress(f.ctx) + if err != nil { + return f.HandleError(err) + } + + sig := rawSigs[0] + msgTx.TxIn[0].Witness, err = address.GenTimeoutWitness(sig) + if err != nil { + return f.HandleError(err) + } + + txLabel := fmt.Sprintf("timeout sweep for deposit %v", + f.deposit.OutPoint) + + err = f.cfg.WalletKit.PublishTransaction(f.ctx, msgTx, txLabel) + if err != nil { + if !strings.Contains(err.Error(), "output already spent") { + log.Errorf("%v: %v", txLabel, err) + f.LastActionError = err + return fsm.OnError + } + } else { + f.Debugf("published timeout sweep with txid: %v", + msgTx.TxHash()) + } + + return OnExpiryPublished +} + +// WaitForExpirySweepAction waits for a sufficient number of confirmations +// before a timeout sweep is considered successful. +func (f *FSM) WaitForExpirySweepAction(_ fsm.EventContext) fsm.EventType { + spendChan, errSpendChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( //nolint:lll + f.ctx, nil, f.deposit.TimeOutSweepPkScript, defaultConfTarget, + int32(f.deposit.ConfirmationHeight), + ) + if err != nil { + return f.HandleError(err) + } + + select { + case err := <-errSpendChan: + log.Debugf("error while sweeping expired deposit: %v", err) + return fsm.OnError + + case confirmedTx := <-spendChan: + f.deposit.ExpirySweepTxid = confirmedTx.Tx.TxHash() + return OnExpirySwept + + case <-f.ctx.Done(): + return fsm.OnError + } +} + +// SweptExpiredDepositAction is the final action of the FSM. It signals to the +// manager that the deposit has been swept and the FSM can be removed. It also +// ends the state machine main loop by cancelling its context. +func (f *FSM) SweptExpiredDepositAction(_ fsm.EventContext) fsm.EventType { + select { + case <-f.ctx.Done(): + return fsm.OnError + + default: + f.finalizedDepositChan <- f.deposit.OutPoint + f.ctx.Done() + } + + return fsm.NoOp +} diff --git a/staticaddr/deposit/deposit.go b/staticaddr/deposit/deposit.go new file mode 100644 index 0000000..67ec9e7 --- /dev/null +++ b/staticaddr/deposit/deposit.go @@ -0,0 +1,107 @@ +package deposit + +import ( + "crypto/rand" + "fmt" + "sync" + + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/fsm" +) + +// ID is a unique identifier for a deposit. +type ID [IdLength]byte + +// FromByteSlice creates a deposit id from a byte slice. +func (r *ID) FromByteSlice(b []byte) error { + if len(b) != IdLength { + return fmt.Errorf("deposit id must be 32 bytes, got %d, %x", + len(b), b) + } + + copy(r[:], b) + + return nil +} + +// Deposit bundles an utxo at a static address together with manager-relevant +// data. +type Deposit struct { + // ID is the unique identifier of the deposit. + ID ID + + // State is the current state of the deposit. + State fsm.StateType + + // The outpoint of the deposit. + wire.OutPoint + + // Value is the amount of the deposit. + Value btcutil.Amount + + // ConfirmationHeight is the absolute height at which the deposit was + // first confirmed. + ConfirmationHeight int64 + + // TimeOutSweepPkScript is the pk script that is used to sweep the + // deposit to after it is expired. + TimeOutSweepPkScript []byte + + // ExpirySweepTxid is the transaction id of the expiry sweep. + ExpirySweepTxid chainhash.Hash + + sync.Mutex +} + +// IsInPendingState returns true if the deposit is pending. +func (d *Deposit) IsInPendingState() bool { + d.Lock() + defer d.Unlock() + + return !d.IsInFinalState() +} + +// IsInFinalState returns true if the deposit is final. +func (d *Deposit) IsInFinalState() bool { + d.Lock() + defer d.Unlock() + + return d.State == Expired || d.State == Failed +} + +func (d *Deposit) isExpired(currentHeight, expiry uint32) bool { + d.Lock() + defer d.Unlock() + + return currentHeight >= uint32(d.ConfirmationHeight)+expiry +} + +func (d *Deposit) getState() fsm.StateType { + d.Lock() + defer d.Unlock() + + return d.State +} + +func (d *Deposit) setState(state fsm.StateType) { + d.Lock() + defer d.Unlock() + + d.State = state +} + +func (d *Deposit) isInState(state fsm.StateType) bool { + d.Lock() + defer d.Unlock() + + return d.State == state +} + +// GetRandomDepositID generates a random deposit ID. +func GetRandomDepositID() (ID, error) { + var id ID + _, err := rand.Read(id[:]) + return id, err +} diff --git a/staticaddr/deposit/fsm.go b/staticaddr/deposit/fsm.go new file mode 100644 index 0000000..cf0e414 --- /dev/null +++ b/staticaddr/deposit/fsm.go @@ -0,0 +1,305 @@ +package deposit + +import ( + "context" + "errors" + "fmt" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/staticaddr" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" +) + +const ( + DefaultObserverSize = 20 +) + +var ( + ErrProtocolVersionNotSupported = errors.New("protocol version not " + + "supported") +) + +// States. +var ( + Deposited = fsm.StateType("Deposited") + + PublishExpiredDeposit = fsm.StateType("PublishExpiredDeposit") + + WaitForExpirySweep = fsm.StateType("WaitForExpirySweep") + + Expired = fsm.StateType("Expired") + + Failed = fsm.StateType("Failed") +) + +// Events. +var ( + OnStart = fsm.EventType("OnStart") + OnExpiry = fsm.EventType("OnExpiry") + OnExpiryPublished = fsm.EventType("OnExpiryPublished") + OnExpirySwept = fsm.EventType("OnExpirySwept") + OnRecover = fsm.EventType("OnRecover") +) + +// FSM is the state machine that handles the instant out. +type FSM struct { + *fsm.StateMachine + + cfg *ManagerConfig + + deposit *Deposit + + params *address.Parameters + + address *script.StaticAddress + + ctx context.Context + + blockNtfnChan chan uint32 + + finalizedDepositChan chan wire.OutPoint +} + +// NewFSM creates a new state machine that can action on all static address +// feature requests. +func NewFSM(ctx context.Context, deposit *Deposit, cfg *ManagerConfig, + finalizedDepositChan chan wire.OutPoint, + recoverStateMachine bool) (*FSM, error) { + + params, err := cfg.AddressManager.GetStaticAddressParameters(ctx) + if err != nil { + return nil, fmt.Errorf("unable to get static address "+ + "parameters: %v", err) + } + + address, err := cfg.AddressManager.GetStaticAddress(ctx) + if err != nil { + return nil, fmt.Errorf("unable to get static address: %v", err) + } + + depoFsm := &FSM{ + cfg: cfg, + deposit: deposit, + params: params, + address: address, + ctx: ctx, + blockNtfnChan: make(chan uint32), + finalizedDepositChan: finalizedDepositChan, + } + + depositStates := depoFsm.DepositStatesV0() + switch params.ProtocolVersion { + case staticaddr.ProtocolVersion_V0: + + default: + return nil, ErrProtocolVersionNotSupported + } + + if recoverStateMachine { + depoFsm.StateMachine = fsm.NewStateMachineWithState( + depositStates, deposit.State, + DefaultObserverSize, + ) + } else { + depoFsm.StateMachine = fsm.NewStateMachine( + depositStates, DefaultObserverSize, + ) + } + + depoFsm.ActionEntryFunc = depoFsm.updateDeposit + + go func() { + for { + select { + case currentHeight := <-depoFsm.blockNtfnChan: + err := depoFsm.handleBlockNotification( + currentHeight, + ) + if err != nil { + log.Errorf("error handling block "+ + "notification: %v", err) + } + + case <-ctx.Done(): + return + } + } + }() + + return depoFsm, nil +} + +// handleBlockNotification inspects the current block height and sends the +// OnExpiry event to publish the expiry sweep transaction if the deposit timed +// out, or it republishes the expiry sweep transaction if it was not yet swept. +func (f *FSM) handleBlockNotification(currentHeight uint32) error { + params, err := f.cfg.AddressManager.GetStaticAddressParameters(f.ctx) + if err != nil { + return err + } + + // If the deposit is expired but not yet sufficiently confirmed, we + // republish the expiry sweep transaction. + if f.deposit.isExpired(currentHeight, params.Expiry) { + if f.deposit.isInState(WaitForExpirySweep) { + f.PublishDepositExpirySweepAction(nil) + } else { + go func() { + err := f.SendEvent(OnExpiry, nil) + if err != nil { + log.Debugf("error sending OnExpiry "+ + "event: %v", err) + } + }() + } + } + + return nil +} + +// DepositStatesV0 returns the states a deposit can be in. +func (f *FSM) DepositStatesV0() fsm.States { + return fsm.States{ + fsm.EmptyState: fsm.State{ + Transitions: fsm.Transitions{ + OnStart: Deposited, + }, + Action: fsm.NoOpAction, + }, + Deposited: fsm.State{ + Transitions: fsm.Transitions{ + OnExpiry: PublishExpiredDeposit, + OnRecover: Deposited, + }, + Action: fsm.NoOpAction, + }, + PublishExpiredDeposit: fsm.State{ + Transitions: fsm.Transitions{ + OnRecover: PublishExpiredDeposit, + OnExpiryPublished: WaitForExpirySweep, + // If the timeout sweep failed we go back to + // Deposited, hoping that another timeout sweep + // attempt will be successful. Alternatively, + // the client can try to coop-spend the deposit. + fsm.OnError: Deposited, + }, + Action: f.PublishDepositExpirySweepAction, + }, + WaitForExpirySweep: fsm.State{ + Transitions: fsm.Transitions{ + OnExpirySwept: Expired, + // Upon recovery, we republish the sweep tx. + OnRecover: PublishExpiredDeposit, + // If the timeout sweep failed we go back to + // Deposited, hoping that another timeout sweep + // attempt will be successful. Alternatively, + // the client can try to coop-spend the deposit. + fsm.OnError: Deposited, + }, + Action: f.WaitForExpirySweepAction, + }, + Expired: fsm.State{ + Transitions: fsm.Transitions{ + OnExpiry: Expired, + }, + Action: f.SweptExpiredDepositAction, + }, + Failed: fsm.State{ + Transitions: fsm.Transitions{ + OnExpiry: Failed, + }, + Action: fsm.NoOpAction, + }, + } +} + +// DepositEntryFunction is called after every action and updates the deposit in +// the db. +func (f *FSM) updateDeposit(notification fsm.Notification) { + if f.deposit == nil { + return + } + + f.Debugf("NextState: %v, PreviousState: %v, Event: %v", + notification.NextState, notification.PreviousState, + notification.Event, + ) + + f.deposit.setState(notification.NextState) + + // Don't update the deposit if we are in an initial state or if we + // are transitioning from an initial state to a failed state. + d := f.deposit + if d.isInState(fsm.EmptyState) || d.isInState(Deposited) || + (notification.PreviousState == Deposited && d.isInState( + Failed, + )) { + + return + } + + err := f.cfg.Store.UpdateDeposit(f.ctx, f.deposit) + if err != nil { + f.Errorf("unable to update deposit: %v", err) + } +} + +// Infof logs an info message with the deposit outpoint. +func (f *FSM) Infof(format string, args ...interface{}) { + log.Infof( + "Deposit %v: "+format, + append( + []interface{}{f.deposit.OutPoint}, + args..., + )..., + ) +} + +// Debugf logs a debug message with the deposit outpoint. +func (f *FSM) Debugf(format string, args ...interface{}) { + log.Debugf( + "Deposit %v: "+format, + append( + []interface{}{f.deposit.OutPoint}, + args..., + )..., + ) +} + +// Errorf logs an error message with the deposit outpoint. +func (f *FSM) Errorf(format string, args ...interface{}) { + log.Errorf( + "Deposit %v: "+format, + append( + []interface{}{f.deposit.OutPoint}, + args..., + )..., + ) +} + +// SignDescriptor returns the sign descriptor for the static address output. +func (f *FSM) SignDescriptor() (*lndclient.SignDescriptor, error) { + address, err := f.cfg.AddressManager.GetStaticAddress(f.ctx) + if err != nil { + return nil, err + } + + return &lndclient.SignDescriptor{ + WitnessScript: address.TimeoutLeaf.Script, + KeyDesc: keychain.KeyDescriptor{ + PubKey: f.params.ClientPubkey, + }, + Output: wire.NewTxOut( + int64(f.deposit.Value), f.params.PkScript, + ), + HashType: txscript.SigHashDefault, + InputIndex: 0, + SignMethod: input.TaprootScriptSpendSignMethod, + }, nil +} diff --git a/staticaddr/deposit/interface.go b/staticaddr/deposit/interface.go new file mode 100644 index 0000000..dc98f16 --- /dev/null +++ b/staticaddr/deposit/interface.go @@ -0,0 +1,44 @@ +package deposit + +import ( + "context" + + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightningnetwork/lnd/lnwallet" +) + +const ( + IdLength = 32 +) + +// Store is the database interface that is used to store and retrieve +// static address deposits. +type Store interface { + // CreateDeposit inserts a new deposit into the store. + CreateDeposit(ctx context.Context, deposit *Deposit) error + + // UpdateDeposit updates the deposit in the database. + UpdateDeposit(ctx context.Context, deposit *Deposit) error + + // GetDeposit retrieves a deposit with depositID from the database. + GetDeposit(ctx context.Context, depositID ID) (*Deposit, error) + + // AllDeposits retrieves all deposits from the store. + AllDeposits(ctx context.Context) ([]*Deposit, error) +} + +// AddressManager handles fetching of address parameters. +type AddressManager interface { + // GetStaticAddressParameters returns the static address parameters. + GetStaticAddressParameters(ctx context.Context) (*address.Parameters, + error) + + // GetStaticAddress returns the deposit address for the given + // client and server public keys. + GetStaticAddress(ctx context.Context) (*script.StaticAddress, error) + + // ListUnspent returns a list of utxos at the static address. + ListUnspent(ctx context.Context, minConfs, + maxConfs int32) ([]*lnwallet.Utxo, error) +} diff --git a/staticaddr/deposit/manager.go b/staticaddr/deposit/manager.go new file mode 100644 index 0000000..eaf27fe --- /dev/null +++ b/staticaddr/deposit/manager.go @@ -0,0 +1,429 @@ +package deposit + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btclog" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/staticaddr" + staticaddressrpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + "github.com/lightningnetwork/lnd/lnwallet" +) + +const ( + // PollInterval is the interval in which we poll for new deposits to our + // static address. + PollInterval = 10 * time.Second + + // MinConfs is the minimum number of confirmations we require for a + // deposit to be considered available for loop-ins, coop-spends and + // timeouts. + MinConfs = 3 + + // MaxConfs is unset since we don't require a max number of + // confirmations for deposits. + MaxConfs = 0 +) + +var ( + log btclog.Logger +) + +// ManagerConfig holds the configuration for the address manager. +type ManagerConfig struct { + // AddressClient is the client that communicates with the loop server + // to manage static addresses. + AddressClient staticaddressrpc.StaticAddressServerClient + + // AddressManager is the address manager that is used to fetch static + // address parameters. + AddressManager AddressManager + + // SwapClient provides loop rpc functionality. + SwapClient *loop.Client + + // Store is the database store that is used to store static address + // related records. + Store Store + + // WalletKit is the wallet client that is used to derive new keys from + // lnd's wallet. + WalletKit lndclient.WalletKitClient + + // ChainParams is the chain configuration(mainnet, testnet...) this + // manager uses. + ChainParams *chaincfg.Params + + // ChainNotifier is the chain notifier that is used to listen for new + // blocks. + ChainNotifier lndclient.ChainNotifierClient + + // Signer is the signer client that is used to sign transactions. + Signer lndclient.SignerClient +} + +// Manager manages the address state machines. +type Manager struct { + cfg *ManagerConfig + + runCtx context.Context + + sync.Mutex + + // initChan signals the daemon that the address manager has completed + // its initialization. + initChan chan struct{} + + // activeDeposits contains all the active static address outputs. + activeDeposits map[wire.OutPoint]*FSM + + // initiationHeight stores the currently best known block height. + initiationHeight uint32 + + // currentHeight stores the currently best known block height. + currentHeight uint32 + + // deposits contains all the deposits that have ever been made to the + // static address. This field is used to store and recover deposits. It + // also serves as basis for reconciliation of newly detected deposits by + // matching them against deposits in this map that were already seen. + deposits map[wire.OutPoint]*Deposit + + // finalizedDepositChan is a channel that receives deposits that have + // been finalized. The manager will adjust its internal state and flush + // finalized deposits from its memory. + finalizedDepositChan chan wire.OutPoint +} + +// NewManager creates a new deposit manager. +func NewManager(cfg *ManagerConfig) *Manager { + log = staticaddr.GetLogger() + return &Manager{ + cfg: cfg, + initChan: make(chan struct{}), + activeDeposits: make(map[wire.OutPoint]*FSM), + deposits: make(map[wire.OutPoint]*Deposit), + finalizedDepositChan: make(chan wire.OutPoint), + } +} + +// Run runs the address manager. +func (m *Manager) Run(ctx context.Context, currentHeight uint32) error { + m.runCtx = ctx + + m.Lock() + m.currentHeight, m.initiationHeight = currentHeight, currentHeight + m.Unlock() + + newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.RegisterBlockEpochNtfn(m.runCtx) //nolint:lll + if err != nil { + return err + } + + // Recover previous deposits and static address parameters from the DB. + err = m.recover(m.runCtx) + if err != nil { + return err + } + + // Start the deposit notifier. + m.pollDeposits(ctx) + + // Communicate to the caller that the address manager has completed its + // initialization. + close(m.initChan) + + for { + select { + case height := <-newBlockChan: + m.Lock() + m.currentHeight = uint32(height) + m.Unlock() + + // Inform all active deposits about a new block arrival. + for _, fsm := range m.activeDeposits { + select { + case fsm.blockNtfnChan <- uint32(height): + + case <-m.runCtx.Done(): + return m.runCtx.Err() + } + } + case outpoint := <-m.finalizedDepositChan: + // If deposits notify us about their finalization, we + // update the manager's internal state and flush the + // finalized deposit from memory. + m.finalizeDeposit(outpoint) + + case err := <-newBlockErrChan: + return err + + case <-m.runCtx.Done(): + return m.runCtx.Err() + } + } +} + +// recover recovers static address parameters, previous deposits and state +// machines from the database and starts the deposit notifier. +func (m *Manager) recover(ctx context.Context) error { + log.Infof("Recovering static address parameters and deposits...") + + // Recover deposits. + deposits, err := m.cfg.Store.AllDeposits(ctx) + if err != nil { + return err + } + + for i, d := range deposits { + m.deposits[d.OutPoint] = deposits[i] + + // If the current deposit is final it wasn't active when we + // shut down the client last. So we don't need to start a fsm + // for it. + if d.IsInFinalState() { + continue + } + + log.Debugf("Recovering deposit %x", d.ID) + + // Create a state machine for a given deposit. + fsm, err := NewFSM( + m.runCtx, d, m.cfg, + m.finalizedDepositChan, true, + ) + if err != nil { + return err + } + + // Send the OnRecover event to the state machine. + go func() { + err = fsm.SendEvent(OnRecover, nil) + if err != nil { + log.Errorf("Error sending OnStart event: %v", + err) + } + }() + + m.activeDeposits[d.OutPoint] = fsm + } + + return nil +} + +// WaitInitComplete waits until the address manager has completed its setup. +func (m *Manager) WaitInitComplete() { + defer log.Debugf("Static address deposit manager initiation complete.") + <-m.initChan +} + +// pollDeposits polls new deposits to our static address and notifies the +// manager's event loop about them. +func (m *Manager) pollDeposits(ctx context.Context) { + log.Debugf("Waiting for new static address deposits...") + + go func() { + ticker := time.NewTicker(PollInterval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + err := m.reconcileDeposits(ctx) + if err != nil { + log.Errorf("unable to reconcile "+ + "deposits: %v", err) + } + + case <-ctx.Done(): + return + } + } + }() +} + +// reconcileDeposits fetches all spends to our static address from our lnd +// wallet and matches it against the deposits in our memory that we've seen so +// far. It picks the newly identified deposits and starts a state machine per +// deposit to track its progress. +func (m *Manager) reconcileDeposits(ctx context.Context) error { + log.Tracef("Reconciling new deposits...") + + utxos, err := m.cfg.AddressManager.ListUnspent( + ctx, MinConfs, MaxConfs, + ) + if err != nil { + return fmt.Errorf("unable to list new deposits: %v", err) + } + + newDeposits := m.filterNewDeposits(utxos) + if err != nil { + return fmt.Errorf("unable to filter new deposits: %v", err) + } + + if len(newDeposits) == 0 { + log.Tracef("No new deposits...") + return nil + } + + for _, utxo := range newDeposits { + deposit, err := m.createNewDeposit(ctx, utxo) + if err != nil { + return fmt.Errorf("unable to retain new deposit: %v", + err) + } + + log.Debugf("Received deposit: %v", deposit) + err = m.startDepositFsm(deposit) + if err != nil { + return fmt.Errorf("unable to start new deposit FSM: %v", + err) + } + } + + return nil +} + +// createNewDeposit transforms the wallet utxo into a deposit struct and stores +// it in our database and manager memory. +func (m *Manager) createNewDeposit(ctx context.Context, + utxo *lnwallet.Utxo) (*Deposit, error) { + + blockHeight, err := m.getBlockHeight(ctx, utxo) + if err != nil { + return nil, err + } + + // Get the sweep pk script. + addr, err := m.cfg.WalletKit.NextAddr( + ctx, lnwallet.DefaultAccountName, + walletrpc.AddressType_TAPROOT_PUBKEY, false, + ) + if err != nil { + return nil, err + } + + timeoutSweepPkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + return nil, err + } + + id, err := GetRandomDepositID() + if err != nil { + return nil, err + } + deposit := &Deposit{ + ID: id, + State: Deposited, + OutPoint: utxo.OutPoint, + Value: utxo.Value, + ConfirmationHeight: int64(blockHeight), + TimeOutSweepPkScript: timeoutSweepPkScript, + } + + err = m.cfg.Store.CreateDeposit(ctx, deposit) + if err != nil { + return nil, err + } + + m.Lock() + m.deposits[deposit.OutPoint] = deposit + m.Unlock() + + return deposit, nil +} + +// getBlockHeight retrieves the block height of a given utxo. +func (m *Manager) getBlockHeight(ctx context.Context, + utxo *lnwallet.Utxo) (uint32, error) { + + addressParams, err := m.cfg.AddressManager.GetStaticAddressParameters( + ctx, + ) + if err != nil { + return 0, fmt.Errorf("couldn't get confirmation height for "+ + "deposit, %v", err) + } + + notifChan, errChan, err := m.cfg.ChainNotifier.RegisterConfirmationsNtfn( //nolint:lll + ctx, &utxo.OutPoint.Hash, addressParams.PkScript, MinConfs, + int32(m.initiationHeight), + ) + if err != nil { + return 0, err + } + + select { + case tx := <-notifChan: + return tx.BlockHeight, nil + + case err := <-errChan: + return 0, err + + case <-ctx.Done(): + return 0, ctx.Err() + } +} + +// filterNewDeposits filters the given utxos for new deposits that we haven't +// seen before. +func (m *Manager) filterNewDeposits(utxos []*lnwallet.Utxo) []*lnwallet.Utxo { + m.Lock() + defer m.Unlock() + + var newDeposits []*lnwallet.Utxo + for _, utxo := range utxos { + _, ok := m.deposits[utxo.OutPoint] + if !ok { + newDeposits = append(newDeposits, utxo) + } + } + + return newDeposits +} + +// startDepositFsm creates a new state machine flow from the latest deposit to +// our static address. +func (m *Manager) startDepositFsm(deposit *Deposit) error { + // Create a state machine for a given deposit. + fsm, err := NewFSM( + m.runCtx, deposit, m.cfg, m.finalizedDepositChan, false, + ) + if err != nil { + return err + } + + // Send the start event to the state machine. + go func() { + err = fsm.SendEvent(OnStart, nil) + if err != nil { + log.Errorf("Error sending OnStart event: %v", err) + } + }() + + err = fsm.DefaultObserver.WaitForState(m.runCtx, time.Minute, Deposited) + if err != nil { + return err + } + + // Add the FSM to the active FSMs map. + m.Lock() + m.activeDeposits[deposit.OutPoint] = fsm + m.Unlock() + + return nil +} + +func (m *Manager) finalizeDeposit(outpoint wire.OutPoint) { + m.Lock() + delete(m.activeDeposits, outpoint) + delete(m.deposits, outpoint) + m.Unlock() +} diff --git a/staticaddr/deposit/manager_test.go b/staticaddr/deposit/manager_test.go new file mode 100644 index 0000000..ab91620 --- /dev/null +++ b/staticaddr/deposit/manager_test.go @@ -0,0 +1,314 @@ +package deposit + +import ( + "context" + "encoding/hex" + "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/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightninglabs/loop/swap" + "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +var ( + defaultServerPubkeyBytes, _ = hex.DecodeString("021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d") + + defaultServerPubkey, _ = btcec.ParsePubKey(defaultServerPubkeyBytes) + + defaultExpiry = uint32(100) + + defaultDepositConfirmations = uint32(3) + + confChan = make(chan *chainntnfs.TxConfirmation) + + confErrChan = make(chan error) + + blockChan = make(chan int32) + + blockErrChan = make(chan error) + + initChan = make(chan struct{}) + + finalizedDepositChan = make(chan wire.OutPoint) +) + +type mockStaticAddressClient struct { + mock.Mock +} + +func (m *mockStaticAddressClient) ServerNewAddress(ctx context.Context, + in *swapserverrpc.ServerNewAddressRequest, opts ...grpc.CallOption) ( + *swapserverrpc.ServerNewAddressResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.ServerNewAddressResponse), + args.Error(1) +} + +type mockAddressManager struct { + mock.Mock +} + +func (m *mockAddressManager) GetStaticAddressParameters(ctx context.Context) ( + *address.Parameters, error) { + + args := m.Called(ctx) + + return args.Get(0).(*address.Parameters), + args.Error(1) +} + +func (m *mockAddressManager) GetStaticAddress(ctx context.Context) ( + *script.StaticAddress, error) { + + args := m.Called(ctx) + + return args.Get(0).(*script.StaticAddress), + args.Error(1) +} + +func (m *mockAddressManager) ListUnspent(ctx context.Context, + minConfs, maxConfs int32) ([]*lnwallet.Utxo, error) { + + args := m.Called(ctx, minConfs, maxConfs) + + return args.Get(0).([]*lnwallet.Utxo), + args.Error(1) +} + +type mockStore struct { + mock.Mock +} + +func (s *mockStore) CreateDeposit(ctx context.Context, deposit *Deposit) error { + args := s.Called(ctx, deposit) + return args.Error(0) +} + +func (s *mockStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error { + args := s.Called(ctx, deposit) + return args.Error(0) +} + +func (s *mockStore) GetDeposit(ctx context.Context, depositID ID) (*Deposit, + error) { + + args := s.Called(ctx, depositID) + return args.Get(0).(*Deposit), args.Error(1) +} + +func (s *mockStore) AllDeposits(ctx context.Context) ([]*Deposit, error) { + args := s.Called(ctx) + return args.Get(0).([]*Deposit), args.Error(1) +} + +type MockChainNotifier struct { + mock.Mock +} + +func (m *MockChainNotifier) RegisterConfirmationsNtfn(ctx context.Context, + txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32, + _ ...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) +} + +// TestManager checks that the manager processes the right channel notifications +// while a deposit is expiring. +func TestManager(t *testing.T) { + ctxb, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Create the test context with required mocks. + testContext := newManagerTestContext(t) + + // Start the deposit manager. + go func() { + err := testContext.manager.Run( + ctxb, uint32(testContext.mockLnd.Height), + ) + require.NoError(t, err) + }() + + // Ensure that the manager has been initialized. + <-initChan + + // Notify about the last block before the expiry. + blockChan <- int32(defaultDepositConfirmations + defaultExpiry) + + // Ensure that the deposit state machine didn't sign for the expiry tx. + select { + case <-testContext.mockLnd.SignOutputRawChannel: + t.Fatal("received unexpected sign request") + + default: + } + + // Mine the expiry tx height. + blockChan <- int32(defaultDepositConfirmations + defaultExpiry) + + // Ensure that the deposit state machine didn't sign for the expiry tx. + <-testContext.mockLnd.SignOutputRawChannel + + // Ensure that the signed expiry transaction is published. + expiryTx := <-testContext.mockLnd.TxPublishChannel + + // Ensure that the deposit is waiting for a confirmation notification. + confChan <- &chainntnfs.TxConfirmation{ + BlockHeight: defaultDepositConfirmations + defaultExpiry + 3, + Tx: expiryTx, + } +} + +// 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 + mockStaticAddressClient *mockStaticAddressClient + mockAddressManager *mockAddressManager +} + +// newManagerTestContext creates a new test context for the reservation manager. +func newManagerTestContext(t *testing.T) *ManagerTestContext { + mockLnd := test.NewMockLnd() + lndContext := test.NewContext(t, mockLnd) + + mockStaticAddressClient := new(mockStaticAddressClient) + mockAddressManager := new(mockAddressManager) + mockStore := new(mockStore) + mockChainNotifier := new(MockChainNotifier) + + ID, err := GetRandomDepositID() + utxo := &lnwallet.Utxo{ + AddressType: lnwallet.TaprootPubkey, + Value: btcutil.Amount(100000), + Confirmations: int64(defaultDepositConfirmations), + PkScript: []byte("pkscript"), + OutPoint: wire.OutPoint{ + Hash: chainhash.Hash{}, + Index: 0xffffffff, + }, + } + require.NoError(t, err) + storedDeposits := []*Deposit{ + { + ID: ID, + State: Deposited, + OutPoint: utxo.OutPoint, + Value: utxo.Value, + ConfirmationHeight: 3, + TimeOutSweepPkScript: []byte{0x42, 0x21, 0x69}, + }, + } + + mockStore.On( + "AllDeposits", mock.Anything, + ).Return(storedDeposits, nil) + + mockStore.On( + "UpdateDeposit", mock.Anything, mock.Anything, + ).Return(nil) + + mockAddressManager.On( + "GetStaticAddressParameters", mock.Anything, + ).Return(&address.Parameters{ + Expiry: defaultExpiry, + }, nil) + + mockAddressManager.On( + "ListUnspent", mock.Anything, mock.Anything, mock.Anything, + ).Return([]*lnwallet.Utxo{utxo}, nil) + + // Define the expected return values for the mocks. + mockChainNotifier.On( + "RegisterConfirmationsNtfn", mock.Anything, mock.Anything, + mock.Anything, mock.Anything, mock.Anything, + ).Return(confChan, confErrChan, nil) + + mockChainNotifier.On("RegisterBlockEpochNtfn", mock.Anything).Return( + blockChan, blockErrChan, nil, + ) + + cfg := &ManagerConfig{ + AddressClient: mockStaticAddressClient, + AddressManager: mockAddressManager, + Store: mockStore, + WalletKit: mockLnd.WalletKit, + ChainParams: mockLnd.ChainParams, + ChainNotifier: mockChainNotifier, + Signer: mockLnd.Signer, + } + + manager := NewManager(cfg) + manager.initChan = initChan + manager.finalizedDepositChan = finalizedDepositChan + + testContext := &ManagerTestContext{ + manager: manager, + context: lndContext, + mockLnd: mockLnd, + mockStaticAddressClient: mockStaticAddressClient, + mockAddressManager: mockAddressManager, + } + + staticAddress := generateStaticAddress( + context.Background(), testContext, + ) + mockAddressManager.On( + "GetStaticAddress", mock.Anything, + ).Return(staticAddress, nil) + + return testContext +} + +func generateStaticAddress(ctx context.Context, + t *ManagerTestContext) *script.StaticAddress { + + keyDescriptor, err := t.mockLnd.WalletKit.DeriveNextKey( + ctx, swap.StaticAddressKeyFamily, + ) + require.NoError(t.context.T, err) + + staticAddress, err := script.NewStaticAddress( + input.MuSig2Version100RC2, int64(defaultExpiry), + keyDescriptor.PubKey, defaultServerPubkey, + ) + require.NoError(t.context.T, err) + + return staticAddress +} diff --git a/staticaddr/deposit/sql_store.go b/staticaddr/deposit/sql_store.go new file mode 100644 index 0000000..41df5ab --- /dev/null +++ b/staticaddr/deposit/sql_store.go @@ -0,0 +1,213 @@ +package deposit + +import ( + "context" + "database/sql" + + "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" +) + +// SqlStore is the backing store for static address deposits. +type SqlStore struct { + baseDB *loopdb.BaseDB + + clock clock.Clock +} + +// NewSqlStore constructs a new SQLStore from a BaseDB. The BaseDB is agnostic +// to the underlying driver which can be postgres or sqlite. +func NewSqlStore(db *loopdb.BaseDB) *SqlStore { + return &SqlStore{ + baseDB: db, + + clock: clock.NewDefaultClock(), + } +} + +// CreateDeposit creates a static address deposit record in the database. +func (s *SqlStore) CreateDeposit(ctx context.Context, deposit *Deposit) error { + createArgs := sqlc.CreateDepositParams{ + DepositID: deposit.ID[:], + TxHash: deposit.Hash[:], + OutIndex: int32(deposit.Index), + Amount: int64(deposit.Value), + ConfirmationHeight: deposit.ConfirmationHeight, + TimeoutSweepPkScript: deposit.TimeOutSweepPkScript, + } + + updateArgs := sqlc.InsertDepositUpdateParams{ + DepositID: deposit.ID[:], + UpdateTimestamp: s.clock.Now().UTC(), + UpdateState: string(deposit.getState()), + } + + return s.baseDB.ExecTx(ctx, &loopdb.SqliteTxOptions{}, + func(q *sqlc.Queries) error { + err := q.CreateDeposit(ctx, createArgs) + if err != nil { + return err + } + + return q.InsertDepositUpdate(ctx, updateArgs) + }) +} + +// UpdateDeposit updates the deposit in the database. +func (s *SqlStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error { + insertUpdateArgs := sqlc.InsertDepositUpdateParams{ + DepositID: deposit.ID[:], + UpdateTimestamp: s.clock.Now().UTC(), + UpdateState: string(deposit.getState()), + } + + var ( + txHash = deposit.Hash[:] + outIndex = sql.NullInt32{ + Int32: int32(deposit.Index), + Valid: true, + } + confirmationHeight = sql.NullInt64{ + Int64: deposit.ConfirmationHeight, + Valid: deposit.ConfirmationHeight != 0, + } + ) + + updateArgs := sqlc.UpdateDepositParams{ + DepositID: deposit.ID[:], + TxHash: txHash, + OutIndex: outIndex.Int32, + ConfirmationHeight: confirmationHeight.Int64, + ExpirySweepTxid: deposit.ExpirySweepTxid[:], + } + + return s.baseDB.ExecTx(ctx, &loopdb.SqliteTxOptions{}, + func(q *sqlc.Queries) error { + err := q.UpdateDeposit(ctx, updateArgs) + if err != nil { + return err + } + + return q.InsertDepositUpdate(ctx, insertUpdateArgs) + }) +} + +// GetDeposit retrieves the deposit from the database. +func (s *SqlStore) GetDeposit(ctx context.Context, id ID) (*Deposit, error) { + var deposit *Deposit + err := s.baseDB.ExecTx(ctx, loopdb.NewSqlReadOpts(), + func(q *sqlc.Queries) error { + row, err := q.GetDeposit(ctx, id[:]) + if err != nil { + return err + } + + latestUpdate, err := q.GetLatestDepositUpdate( + ctx, id[:], + ) + if err != nil { + return err + } + + deposit, err = s.toDeposit(row, latestUpdate) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, err + } + + return deposit, nil +} + +// AllDeposits retrieves all known deposits to our static address. +func (s *SqlStore) AllDeposits(ctx context.Context) ([]*Deposit, error) { + var allDeposits []*Deposit + + err := s.baseDB.ExecTx(ctx, loopdb.NewSqlReadOpts(), + func(q *sqlc.Queries) error { + var err error + + deposits, err := q.AllDeposits(ctx) + if err != nil { + return err + } + + for _, deposit := range deposits { + latestUpdate, err := q.GetLatestDepositUpdate( + ctx, deposit.DepositID, + ) + if err != nil { + return err + } + + d, err := s.toDeposit(deposit, latestUpdate) + if err != nil { + return err + } + + allDeposits = append(allDeposits, d) + } + + return nil + }) + if err != nil { + return nil, err + } + + return allDeposits, nil +} + +// toDeposit converts an sql deposit to a deposit. +func (s *SqlStore) toDeposit(row sqlc.Deposit, + lastUpdate sqlc.DepositUpdate) (*Deposit, error) { + + id := ID{} + err := id.FromByteSlice(row.DepositID) + 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 expirySweepTxid chainhash.Hash + if row.ExpirySweepTxid != nil { + hash, err := chainhash.NewHash(row.ExpirySweepTxid) + if err != nil { + return nil, err + } + expirySweepTxid = *hash + } + + return &Deposit{ + ID: id, + State: fsm.StateType(lastUpdate.UpdateState), + OutPoint: wire.OutPoint{ + Hash: *txHash, + Index: uint32(row.OutIndex), + }, + Value: btcutil.Amount(row.Amount), + ConfirmationHeight: row.ConfirmationHeight, + TimeOutSweepPkScript: row.TimeoutSweepPkScript, + ExpirySweepTxid: expirySweepTxid, + }, nil +} + +// Close closes the database connection. +func (s *SqlStore) Close() { + s.baseDB.DB.Close() +} diff --git a/staticaddr/log.go b/staticaddr/log.go index 53586b3..faa520a 100644 --- a/staticaddr/log.go +++ b/staticaddr/log.go @@ -22,3 +22,8 @@ func init() { func UseLogger(logger btclog.Logger) { log = logger } + +// GetLogger returns the logger for this package. +func GetLogger() btclog.Logger { + return log +} diff --git a/staticaddr/server.go b/staticaddr/server.go deleted file mode 100644 index a6273dd..0000000 --- a/staticaddr/server.go +++ /dev/null @@ -1,70 +0,0 @@ -package staticaddr - -import ( - "context" - - "github.com/lightninglabs/loop/looprpc" - staticaddressrpc "github.com/lightninglabs/loop/swapserverrpc" -) - -// AddressServer holds all fields for the address rpc server. -type AddressServer struct { - addressClient staticaddressrpc.StaticAddressServerClient - manager *Manager - looprpc.UnimplementedStaticAddressClientServer -} - -// NewAddressServer creates a new static address server. -func NewAddressServer(addressClient staticaddressrpc.StaticAddressServerClient, - manager *Manager) *AddressServer { - - return &AddressServer{ - addressClient: addressClient, - manager: manager, - } -} - -// NewAddress is the rpc endpoint for loop clients to request a new static -// address. -func (s *AddressServer) NewAddress(ctx context.Context, - _ *looprpc.NewAddressRequest) (*looprpc.NewAddressResponse, error) { - - address, err := s.manager.NewAddress(ctx) - if err != nil { - return nil, err - } - - log.Infof("New static loop-in address: %s\n", address.String()) - - return &looprpc.NewAddressResponse{ - Address: address.String(), - }, nil -} - -// ListUnspent returns a list of utxos behind the static address. -func (s *AddressServer) ListUnspent(ctx context.Context, - req *looprpc.ListUnspentRequest) (*looprpc.ListUnspentResponse, error) { - - // List all unspent utxos the wallet sees, regardless of the number of - // confirmations. - staticAddress, utxos, err := s.manager.ListUnspentRaw( - ctx, req.MinConfs, req.MaxConfs, - ) - if err != nil { - return nil, err - } - - // Prepare the list response. - var respUtxos []*looprpc.Utxo - for _, u := range utxos { - utxo := &looprpc.Utxo{ - StaticAddress: staticAddress.String(), - AmountSat: int64(u.Value), - Confirmations: u.Confirmations, - Outpoint: u.OutPoint.String(), - } - respUtxos = append(respUtxos, utxo) - } - - return &looprpc.ListUnspentResponse{Utxos: respUtxos}, nil -} diff --git a/test/walletkit_mock.go b/test/walletkit_mock.go index c26f7b4..c8f9c11 100644 --- a/test/walletkit_mock.go +++ b/test/walletkit_mock.go @@ -266,5 +266,5 @@ func (m *mockWalletKit) ImportPublicKey(ctx context.Context, func (m *mockWalletKit) ImportTaprootScript(ctx context.Context, tapscript *waddrmgr.Tapscript) (btcutil.Address, error) { - return nil, fmt.Errorf("unimplemented") + return nil, nil }