diff --git a/client.go b/client.go index 66ee81e64..f947cd08e 100644 --- a/client.go +++ b/client.go @@ -22,6 +22,7 @@ import ( "github.com/lightninglabs/loop/sweepbatcher" "github.com/lightninglabs/loop/utils" "github.com/lightninglabs/taproot-assets/rpcutils" + "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing/route" "google.golang.org/grpc" @@ -485,7 +486,10 @@ func (s *Client) Run(ctx context.Context, statusChan chan<- SwapInfo) error { func (s *Client) resumeSwaps(ctx context.Context, loopOutSwaps []*loopdb.LoopOut, loopInSwaps []*loopdb.LoopIn) { - swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.AssetClient) + swapCfg := newSwapConfig( + s.lndServices, s.Store, s.Server, s.AssetClient, + clock.NewDefaultClock(), + ) for _, pend := range loopOutSwaps { if pend.State().State.Type() != loopdb.StateTypePending { @@ -578,6 +582,7 @@ func (s *Client) LoopOut(globalCtx context.Context, // Create a new swap object for this swap. swapCfg := newSwapConfig( s.lndServices, s.Store, s.Server, s.AssetClient, + clock.NewDefaultClock(), ) initResult, err := newLoopOutSwap( @@ -759,7 +764,10 @@ func (s *Client) LoopIn(globalCtx context.Context, // Create a new swap object for this swap. initiationHeight := s.executor.height() - swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.AssetClient) + swapCfg := newSwapConfig( + s.lndServices, s.Store, s.Server, s.AssetClient, + clock.NewDefaultClock(), + ) initResult, err := newLoopInSwap( globalCtx, swapCfg, initiationHeight, request, ) diff --git a/cost_migration_test.go b/cost_migration_test.go index 6ff2fc962..082895f2e 100644 --- a/cost_migration_test.go +++ b/cost_migration_test.go @@ -9,6 +9,7 @@ import ( "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" @@ -25,6 +26,7 @@ func TestCalculateLoopOutCost(t *testing.T) { lnd: &lnd.LndServices, store: store, server: server, + clock: clock.NewTestClock(time.Unix(123, 0)), } height := int32(600) @@ -118,6 +120,7 @@ func TestCostMigration(t *testing.T) { lnd: &lnd.LndServices, store: store, server: server, + clock: clock.NewTestClock(time.Unix(123, 0)), } height := int32(600) diff --git a/loopd/daemon.go b/loopd/daemon.go index 32371df13..880e19621 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -1005,7 +1005,8 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } loop.Resume( - d.mainCtx, notificationManager, swapClient.Store, d.impl.Conn, d.lnd, + d.mainCtx, notificationManager, swapClient.Store, + d.impl.Conn, d.lnd, clock.NewDefaultClock(), ) // Last, start our internal error handler. This will return exactly one diff --git a/loopin.go b/loopin.go index 989d6733f..ae6802c80 100644 --- a/loopin.go +++ b/loopin.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "sync" - "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" @@ -264,7 +263,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, // Instantiate a struct that contains all required data to start the // swap. - initiationTime := time.Now() + initiationTime := cfg.clock.Now() contract := loopdb.LoopInContract{ HtlcConfTarget: request.HtlcConfTarget, @@ -837,7 +836,7 @@ func (s *loopInSwap) publishOnChainHtlc(ctx context.Context) (bool, error) { // the fee for publishing the htlc. s.cost.Onchain = fee - s.lastUpdateTime = time.Now() + s.lastUpdateTime = s.clock.Now() if err := s.persistState(ctx); err != nil { return false, fmt.Errorf("persist htlc tx: %v", err) } @@ -1210,7 +1209,7 @@ func (s *loopInSwap) persistState(ctx context.Context) error { // setState updates the swap state and last update timestamp. func (s *loopInSwap) setState(state loopdb.SwapState) { - s.lastUpdateTime = time.Now() + s.lastUpdateTime = s.clock.Now() s.state = state } diff --git a/loopin_test.go b/loopin_test.go index 9ff97f733..156f1d601 100644 --- a/loopin_test.go +++ b/loopin_test.go @@ -13,6 +13,7 @@ import ( "github.com/lightninglabs/loop/test" "github.com/lightninglabs/loop/utils" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/clock" invpkg "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing/route" @@ -129,8 +130,12 @@ func testLoopInSuccess(t *testing.T) { ctx := newLoopInTestContext(t) height := int32(600) + startTime := time.Unix(123, 0) - cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil) + cfg := newSwapConfig( + &ctx.lnd.LndServices, ctx.store, ctx.server, nil, + clock.NewTestClock(startTime), + ) expectedLastHop := &route.Vertex{0x02} @@ -144,6 +149,7 @@ func testLoopInSuccess(t *testing.T) { require.NoError(t, err) inSwap := initResult.swap + require.Equal(t, startTime, inSwap.InitiationTime) ctx.store.AssertLoopInStored() @@ -279,7 +285,10 @@ func testLoopInTimeout(t *testing.T, externalValue int64) { height := int32(600) - cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil) + cfg := newSwapConfig( + &ctx.lnd.LndServices, ctx.store, ctx.server, nil, + clock.NewTestClock(time.Unix(123, 0)), + ) req := testLoopInRequest if externalValue != 0 { @@ -490,7 +499,10 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool, ctxb := context.Background() ctx := newLoopInTestContext(t) - cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil) + cfg := newSwapConfig( + &ctx.lnd.LndServices, ctx.store, ctx.server, nil, + clock.NewTestClock(time.Unix(123, 0)), + ) // Create sender and receiver keys. _, senderPubKey := test.CreateKey(1) @@ -845,7 +857,10 @@ func advanceToPublishedHtlc(t *testing.T, ctx *loopInTestContext) SwapInfo { func startNewLoopIn(t *testing.T, ctx *loopInTestContext, height int32) ( *swapConfig, *loopInSwap, error) { - cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil) + cfg := newSwapConfig( + &ctx.lnd.LndServices, ctx.store, ctx.server, nil, + clock.NewTestClock(time.Unix(123, 0)), + ) req := &testLoopInRequest diff --git a/loopout.go b/loopout.go index 11990d57c..a18c83ed1 100644 --- a/loopout.go +++ b/loopout.go @@ -25,6 +25,7 @@ import ( "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/rfqmsg" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" paymentsdb "github.com/lightningnetwork/lnd/payments/db" @@ -192,7 +193,7 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig, // Instantiate a struct that contains all required data to start the // swap. - initiationTime := time.Now() + initiationTime := cfg.clock.Now() contract := loopdb.LoopOutContract{ SwapInvoice: swapResp.swapInvoice, @@ -633,7 +634,7 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error { // persistState updates the swap state and sends out an update notification. func (s *loopOutSwap) persistState(ctx context.Context) error { - updateTime := time.Now() + updateTime := s.clock.Now() s.lastUpdateTime = updateTime @@ -864,12 +865,12 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context, payCtx, cancel := context.WithCancel(ctx) defer cancel() - start := time.Now() + start := s.clock.Now() paymentStatus, attempts, err := s.sendPaymentWithRetry( payCtx, hash, &req, maxRetries, routingPlugin, pluginType, ) - dt := time.Since(start) + dt := s.clock.Now().Sub(start) paymentSuccess := err == nil && paymentStatus.State == lnrpc.Payment_SUCCEEDED @@ -1543,6 +1544,7 @@ type resumeManager struct { swapStore loopdb.SwapStore swapClient swapserverrpc.SwapServerClient lnd *lndclient.GrpcLndServices + clock clock.Clock reqChan chan *swapserverrpc.ServerUnfinishedSwapNotification } @@ -1552,13 +1554,14 @@ type resumeManager struct { func Resume(ctx context.Context, ntfnManager NotificationManager, swapStore loopdb.SwapStore, swapClientConn *grpc.ClientConn, - lnd *lndclient.GrpcLndServices) { + lnd *lndclient.GrpcLndServices, clock clock.Clock) { resumeManager := &resumeManager{ ntfnManager: ntfnManager, swapStore: swapStore, swapClient: swapserverrpc.NewSwapServerClient(swapClientConn), lnd: lnd, + clock: clock, reqChan: make(chan *swapserverrpc.ServerUnfinishedSwapNotification, 1), } go resumeManager.start(ctx) @@ -1717,7 +1720,7 @@ func (m *resumeManager) resumeLoopOutPayment(ctx context.Context, cost.Server = payResp.Value.ToSatoshis() - amtRequested cost.Offchain = payResp.Fee.ToSatoshis() // Payment succeeded. - updateTime := time.Now() + updateTime := m.clock.Now() // Update state in store. err = m.swapStore.UpdateLoopOut( diff --git a/loopout_test.go b/loopout_test.go index 25c9e230a..026d571cb 100644 --- a/loopout_test.go +++ b/loopout_test.go @@ -18,6 +18,7 @@ import ( "github.com/lightninglabs/loop/sweep" "github.com/lightninglabs/loop/sweepbatcher" "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -57,11 +58,13 @@ func testLoopOutPaymentParameters(t *testing.T) { } height := int32(600) + startTime := time.Unix(123, 0) cfg := &swapConfig{ lnd: &lnd.LndServices, store: store, server: server, + clock: clock.NewTestClock(startTime), } sweeper := &sweep.Sweeper{Lnd: &lnd.LndServices} @@ -82,6 +85,7 @@ func testLoopOutPaymentParameters(t *testing.T) { ) require.NoError(t, err) swap := initResult.swap + require.Equal(t, startTime, swap.InitiationTime) // Execute the swap in its own goroutine. errChan := make(chan error) @@ -193,7 +197,10 @@ func testLateHtlcPublish(t *testing.T) { height := int32(600) - cfg := newSwapConfig(&lnd.LndServices, store, server, nil) + cfg := newSwapConfig( + &lnd.LndServices, store, server, nil, + clock.NewTestClock(time.Unix(123, 0)), + ) testRequest.Expiry = height + testLoopOutMinOnChainCltvDelta @@ -296,6 +303,7 @@ func testCustomSweepConfTarget(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, + clock.NewTestClock(time.Unix(123, 0)), ) initResult, err := newLoopOutSwap( @@ -539,6 +547,7 @@ func testPreimagePush(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, + clock.NewTestClock(time.Unix(123, 0)), ) initResult, err := newLoopOutSwap( @@ -797,6 +806,7 @@ func testFailedOffChainCancelation(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, + clock.NewTestClock(time.Unix(123, 0)), ) initResult, err := newLoopOutSwap( @@ -951,6 +961,7 @@ func TestLoopOutMuSig2Sweep(t *testing.T) { cfg := newSwapConfig( &lnd.LndServices, loopdb.NewStoreMock(t), server, nil, + clock.NewTestClock(time.Unix(123, 0)), ) initResult, err := newLoopOutSwap( diff --git a/staticaddr/deposit/manager.go b/staticaddr/deposit/manager.go index af8820302..2f0239016 100644 --- a/staticaddr/deposit/manager.go +++ b/staticaddr/deposit/manager.go @@ -20,7 +20,7 @@ const ( // MinConfs is the minimum number of confirmations we require for a // deposit to be considered available for loop-ins, coop-spends and // timeouts. - MinConfs = 6 + MinConfs = 3 // MaxConfs is unset since we don't require a max number of // confirmations for deposits. diff --git a/staticaddr/loopin/actions.go b/staticaddr/loopin/actions.go index 70a27811f..eadf7ba5c 100644 --- a/staticaddr/loopin/actions.go +++ b/staticaddr/loopin/actions.go @@ -30,6 +30,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" + "google.golang.org/grpc/status" ) const ( @@ -146,6 +147,10 @@ func (f *FSM) InitHtlcAction(ctx context.Context, ctx, loopInReq, ) if err != nil { + // Check if this is an insufficient confirmations error and log + // the details to help the user understand what's needed. + logInsufficientConfirmationsDetails(err) + err = fmt.Errorf("unable to initiate the loop-in with the "+ "server: %w", err) @@ -914,3 +919,30 @@ func byteSliceTo66ByteSlice(b []byte) ([musig2.PubNonceSize]byte, error) { return res, nil } + +// logInsufficientConfirmationsDetails extracts and logs the per-deposit +// confirmation details from a gRPC error if present. +func logInsufficientConfirmationsDetails(err error) { + st, ok := status.FromError(err) + if !ok { + return + } + + for _, detail := range st.Details() { + confDetails, ok := + detail.(*swapserverrpc.InsufficientConfirmationsDetails) + + if !ok { + continue + } + + log.Warnf("Insufficient deposit confirmations, max wait: %d blocks", + confDetails.MaxBlocksToWait) + + for _, dep := range confDetails.Deposits { + log.Warnf(" Deposit %s: %d/%d confirmations (need %d more blocks)", + dep.Outpoint, dep.CurrentConfirmations, + dep.RequiredConfirmations, dep.BlocksToWait) + } + } +} diff --git a/staticaddr/loopin/manager.go b/staticaddr/loopin/manager.go index 444ab5856..97f6e41d8 100644 --- a/staticaddr/loopin/manager.go +++ b/staticaddr/loopin/manager.go @@ -850,11 +850,13 @@ func (m *Manager) GetAllSwaps(ctx context.Context) ([]*StaticAddressLoopIn, return swaps, nil } -// SelectDeposits sorts the deposits by amount in descending order, then by -// blocks-until-expiry in ascending order. It then selects the deposits that -// are needed to cover the amount requested without leaving a dust change. It -// returns an error if the sum of deposits minus dust is less than the requested -// amount. +// SelectDeposits sorts the deposits to optimize for successful swaps with +// dynamic confirmation requirements: 1) more confirmations first (higher chance +// of server acceptance), 2) larger amounts first (to minimize number of deposits +// used), 3) expiring sooner first (to use time-sensitive deposits). It then +// selects the deposits that are needed to cover the amount requested without +// leaving a dust change. It returns an error if the sum of deposits minus dust +// is less than the requested amount. func SelectDeposits(targetAmount btcutil.Amount, unfilteredDeposits []*deposit.Deposit, csvExpiry uint32, blockHeight uint32) ([]*deposit.Deposit, error) { @@ -875,17 +877,28 @@ func SelectDeposits(targetAmount btcutil.Amount, deposits = append(deposits, d) } - // Sort the deposits by amount in descending order, then by - // blocks-until-expiry in ascending order. + // Sort deposits to optimize for successful swaps with dynamic + // confirmation requirements: + // 1. More confirmations first (higher chance of server acceptance) + // 2. Larger amounts first (to minimize number of deposits used) sort.Slice(deposits, func(i, j int) bool { - if deposits[i].Value == deposits[j].Value { - iExp := uint32(deposits[i].ConfirmationHeight) + - csvExpiry - blockHeight - jExp := uint32(deposits[j].ConfirmationHeight) + - csvExpiry - blockHeight - - return iExp < jExp + // Primary: more confirmations first. Guard against the + // theoretical case where ConfirmationHeight > blockHeight + // (e.g. during a transient reorg inconsistency). + var iConfs, jConfs uint32 + if blockHeight > uint32(deposits[i].ConfirmationHeight) { + iConfs = blockHeight - + uint32(deposits[i].ConfirmationHeight) + } + if blockHeight > uint32(deposits[j].ConfirmationHeight) { + jConfs = blockHeight - + uint32(deposits[j].ConfirmationHeight) } + if iConfs != jConfs { + return iConfs > jConfs + } + + // Secondary: larger amounts first. return deposits[i].Value > deposits[j].Value }) diff --git a/staticaddr/loopin/manager_test.go b/staticaddr/loopin/manager_test.go index d908a9e16..d367a3dc2 100644 --- a/staticaddr/loopin/manager_test.go +++ b/staticaddr/loopin/manager_test.go @@ -26,10 +26,14 @@ type testCase struct { // TestSelectDeposits tests the selectDeposits function, which selects // deposits that can cover a target value while respecting the dust limit. +// Sorting priority: 1) more confirmations first, 2) larger amounts first, +// 3) expiring sooner first. func TestSelectDeposits(t *testing.T) { + // Note: confirmations = blockHeight - ConfirmationHeight + // Lower ConfirmationHeight means more confirmations at a given block. d1, d2, d3, d4 := &deposit.Deposit{ Value: 1_000_000, - ConfirmationHeight: 5_000, + ConfirmationHeight: 5_000, // most confs at height 5100 }, &deposit.Deposit{ Value: 2_000_000, ConfirmationHeight: 5_001, @@ -38,7 +42,7 @@ func TestSelectDeposits(t *testing.T) { ConfirmationHeight: 5_002, }, &deposit.Deposit{ Value: 3_000_000, - ConfirmationHeight: 5_003, + ConfirmationHeight: 5_003, // fewest confs at height 5100 } d1.Hash = chainhash.Hash{1} d1.Index = 0 @@ -49,32 +53,51 @@ func TestSelectDeposits(t *testing.T) { d4.Hash = chainhash.Hash{4} d4.Index = 0 + // Use a realistic block height and csv expiry for all standard + // test cases. csvExpiry must be large enough that deposits remain + // swappable at this block height. + const ( + testBlockHeight uint32 = 5_100 + testCsvExpiry uint32 = 2_500 + ) + testCases := []testCase{ { name: "single deposit exact target", deposits: []*deposit.Deposit{d1}, targetValue: 1_000_000, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{d1}, expectedErr: "", }, { - name: "prefer larger deposit when both cover", + // d1 has more confirmations, so it's preferred even + // though d2 is larger. + name: "prefer more confirmed deposit over larger", deposits: []*deposit.Deposit{d1, d2}, targetValue: 1_000_000, - expected: []*deposit.Deposit{d2}, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, + expected: []*deposit.Deposit{d1}, expectedErr: "", }, { - name: "prefer largest among three when one is enough", + // d1 has the most confirmations among d1, d2, d3. + name: "prefer most confirmed among three", deposits: []*deposit.Deposit{d1, d2, d3}, targetValue: 1_000_000, - expected: []*deposit.Deposit{d3}, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, + expected: []*deposit.Deposit{d1}, expectedErr: "", }, { name: "single deposit insufficient by 1", deposits: []*deposit.Deposit{d1}, targetValue: 1_000_001, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{}, expectedErr: "not enough deposits to cover", }, @@ -82,6 +105,8 @@ func TestSelectDeposits(t *testing.T) { name: "target leaves exact dust limit change", deposits: []*deposit.Deposit{d1}, targetValue: 1_000_000 - dustLimit, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{d1}, expectedErr: "", }, @@ -89,6 +114,8 @@ func TestSelectDeposits(t *testing.T) { name: "target leaves dust change (just over)", deposits: []*deposit.Deposit{d1}, targetValue: 1_000_000 - dustLimit + 1, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{}, expectedErr: "not enough deposits to cover", }, @@ -96,6 +123,8 @@ func TestSelectDeposits(t *testing.T) { name: "all deposits exactly match target", deposits: []*deposit.Deposit{d1, d2, d3}, targetValue: d1.Value + d2.Value + d3.Value, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{d1, d2, d3}, expectedErr: "", }, @@ -103,6 +132,8 @@ func TestSelectDeposits(t *testing.T) { name: "sum minus dust limit is allowed (change == dust)", deposits: []*deposit.Deposit{d1, d2, d3}, targetValue: d1.Value + d2.Value + d3.Value - dustLimit, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{d1, d2, d3}, expectedErr: "", }, @@ -110,14 +141,20 @@ func TestSelectDeposits(t *testing.T) { name: "sum minus dust limit plus 1 is not allowed (dust change)", deposits: []*deposit.Deposit{d1, d2, d3}, targetValue: d1.Value + d2.Value + d3.Value - dustLimit + 1, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{}, expectedErr: "not enough deposits to cover", }, { - name: "tie by value, prefer earlier expiry", + // d3 and d4 have the same value but d3 has more + // confirmations (lower ConfirmationHeight), so it + // wins at the primary sort level. + name: "same value, prefer more confirmed", deposits: []*deposit.Deposit{d3, d4}, - targetValue: d4.Value - dustLimit, // d3/d4 have the - // same value but different expiration. + targetValue: d4.Value - dustLimit, + csvExpiry: testCsvExpiry, + blockHeight: testBlockHeight, expected: []*deposit.Deposit{d3}, expectedErr: "", }, diff --git a/swap.go b/swap.go index 204dd461d..43a445eb1 100644 --- a/swap.go +++ b/swap.go @@ -9,6 +9,7 @@ import ( "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/utils" + "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" ) @@ -83,15 +84,18 @@ type swapConfig struct { store loopdb.SwapStore server swapServerClient assets *assets.TapdClient + clock clock.Clock } func newSwapConfig(lnd *lndclient.LndServices, store loopdb.SwapStore, - server swapServerClient, assets *assets.TapdClient) *swapConfig { + server swapServerClient, assets *assets.TapdClient, + clock clock.Clock) *swapConfig { return &swapConfig{ lnd: lnd, store: store, server: server, assets: assets, + clock: clock, } } diff --git a/swapserverrpc/staticaddr.pb.go b/swapserverrpc/staticaddr.pb.go index b903ece75..fc019f63c 100644 --- a/swapserverrpc/staticaddr.pb.go +++ b/swapserverrpc/staticaddr.pb.go @@ -1208,6 +1208,143 @@ func (*PushStaticAddressSweeplessSigsResponse) Descriptor() ([]byte, []int) { return file_staticaddr_proto_rawDescGZIP(), []int{16} } +// InsufficientConfirmationsDetails provides structured information about +// deposits that don't meet dynamic confirmation requirements. This is +// attached as error details to FailedPrecondition errors. +type InsufficientConfirmationsDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Status of each deposit that has insufficient confirmations. + Deposits []*DepositConfirmationStatus `protobuf:"bytes,1,rep,name=deposits,proto3" json:"deposits,omitempty"` + // Maximum blocks to wait across all deposits. + MaxBlocksToWait int32 `protobuf:"varint,2,opt,name=max_blocks_to_wait,json=maxBlocksToWait,proto3" json:"max_blocks_to_wait,omitempty"` +} + +func (x *InsufficientConfirmationsDetails) Reset() { + *x = InsufficientConfirmationsDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_staticaddr_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InsufficientConfirmationsDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InsufficientConfirmationsDetails) ProtoMessage() {} + +func (x *InsufficientConfirmationsDetails) ProtoReflect() protoreflect.Message { + mi := &file_staticaddr_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InsufficientConfirmationsDetails.ProtoReflect.Descriptor instead. +func (*InsufficientConfirmationsDetails) Descriptor() ([]byte, []int) { + return file_staticaddr_proto_rawDescGZIP(), []int{17} +} + +func (x *InsufficientConfirmationsDetails) GetDeposits() []*DepositConfirmationStatus { + if x != nil { + return x.Deposits + } + return nil +} + +func (x *InsufficientConfirmationsDetails) GetMaxBlocksToWait() int32 { + if x != nil { + return x.MaxBlocksToWait + } + return 0 +} + +// DepositConfirmationStatus reports the confirmation state of a single deposit +// that does not meet the required confirmation threshold. +type DepositConfirmationStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The deposit outpoint in format txid:idx. + Outpoint string `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` + // Current number of confirmations the deposit has. + CurrentConfirmations int32 `protobuf:"varint,2,opt,name=current_confirmations,json=currentConfirmations,proto3" json:"current_confirmations,omitempty"` + // Required number of confirmations based on risk assessment. + RequiredConfirmations int32 `protobuf:"varint,3,opt,name=required_confirmations,json=requiredConfirmations,proto3" json:"required_confirmations,omitempty"` + // Estimated blocks to wait until requirement is met. + BlocksToWait int32 `protobuf:"varint,4,opt,name=blocks_to_wait,json=blocksToWait,proto3" json:"blocks_to_wait,omitempty"` +} + +func (x *DepositConfirmationStatus) Reset() { + *x = DepositConfirmationStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_staticaddr_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DepositConfirmationStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DepositConfirmationStatus) ProtoMessage() {} + +func (x *DepositConfirmationStatus) ProtoReflect() protoreflect.Message { + mi := &file_staticaddr_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DepositConfirmationStatus.ProtoReflect.Descriptor instead. +func (*DepositConfirmationStatus) Descriptor() ([]byte, []int) { + return file_staticaddr_proto_rawDescGZIP(), []int{18} +} + +func (x *DepositConfirmationStatus) GetOutpoint() string { + if x != nil { + return x.Outpoint + } + return "" +} + +func (x *DepositConfirmationStatus) GetCurrentConfirmations() int32 { + if x != nil { + return x.CurrentConfirmations + } + return 0 +} + +func (x *DepositConfirmationStatus) GetRequiredConfirmations() int32 { + if x != nil { + return x.RequiredConfirmations + } + return 0 +} + +func (x *DepositConfirmationStatus) GetBlocksToWait() int32 { + if x != nil { + return x.BlocksToWait + } + return 0 +} + var File_staticaddr_proto protoreflect.FileDescriptor var file_staticaddr_proto_rawDesc = []byte{ @@ -1398,56 +1535,77 @@ var file_staticaddr_proto_rawDesc = []byte{ 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x28, 0x0a, 0x26, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, - 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, - 0x26, 0x0a, 0x1c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x06, 0x0a, 0x02, 0x56, 0x30, 0x10, 0x00, 0x32, 0xa1, 0x05, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, - 0x57, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x8f, 0x01, 0x0a, 0x20, 0x49, 0x6e, 0x73, 0x75, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x3e, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x54, 0x6f, 0x57, 0x61, 0x69, + 0x74, 0x22, 0xc9, 0x01, 0x0a, 0x19, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x15, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x15, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x54, 0x6f, 0x57, 0x61, 0x69, 0x74, 0x2a, 0x26, 0x0a, + 0x1c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, + 0x02, 0x56, 0x30, 0x10, 0x00, 0x32, 0xa1, 0x05, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x57, 0x0a, + 0x10, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x4e, 0x65, 0x77, 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, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 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, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x65, 0x0a, 0x1a, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x72, 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x29, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x19, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, - 0x12, 0x29, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, - 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, - 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x1e, 0x50, 0x75, 0x73, 0x68, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, - 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x12, 0x2e, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, - 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, - 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, - 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61, - 0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x12, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x65, 0x0a, 0x1a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, + 0x19, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x29, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x72, 0x0a, 0x19, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x12, 0x29, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x1e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, + 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x12, 0x2e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61, 0x70, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1463,7 +1621,7 @@ func file_staticaddr_proto_rawDescGZIP() []byte { } var file_staticaddr_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_staticaddr_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_staticaddr_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_staticaddr_proto_goTypes = []any{ (StaticAddressProtocolVersion)(0), // 0: looprpc.StaticAddressProtocolVersion (*ServerNewAddressRequest)(nil), // 1: looprpc.ServerNewAddressRequest @@ -1483,17 +1641,19 @@ var file_staticaddr_proto_goTypes = []any{ (*PushStaticAddressSweeplessSigsRequest)(nil), // 15: looprpc.PushStaticAddressSweeplessSigsRequest (*ClientSweeplessSigningInfo)(nil), // 16: looprpc.ClientSweeplessSigningInfo (*PushStaticAddressSweeplessSigsResponse)(nil), // 17: looprpc.PushStaticAddressSweeplessSigsResponse - nil, // 18: looprpc.ServerPsbtWithdrawRequest.DepositToNoncesEntry - nil, // 19: looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry - nil, // 20: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry - (*PrevoutInfo)(nil), // 21: looprpc.PrevoutInfo + (*InsufficientConfirmationsDetails)(nil), // 18: looprpc.InsufficientConfirmationsDetails + (*DepositConfirmationStatus)(nil), // 19: looprpc.DepositConfirmationStatus + nil, // 20: looprpc.ServerPsbtWithdrawRequest.DepositToNoncesEntry + nil, // 21: looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry + nil, // 22: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry + (*PrevoutInfo)(nil), // 23: looprpc.PrevoutInfo } var file_staticaddr_proto_depIdxs = []int32{ 0, // 0: looprpc.ServerNewAddressRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion 3, // 1: looprpc.ServerNewAddressResponse.params:type_name -> looprpc.ServerAddressParameters - 21, // 2: looprpc.ServerWithdrawRequest.outpoints:type_name -> looprpc.PrevoutInfo - 18, // 3: looprpc.ServerPsbtWithdrawRequest.deposit_to_nonces:type_name -> looprpc.ServerPsbtWithdrawRequest.DepositToNoncesEntry - 19, // 4: looprpc.ServerPsbtWithdrawResponse.signing_info:type_name -> looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry + 23, // 2: looprpc.ServerWithdrawRequest.outpoints:type_name -> looprpc.PrevoutInfo + 20, // 3: looprpc.ServerPsbtWithdrawRequest.deposit_to_nonces:type_name -> looprpc.ServerPsbtWithdrawRequest.DepositToNoncesEntry + 21, // 4: looprpc.ServerPsbtWithdrawResponse.signing_info:type_name -> looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry 0, // 5: looprpc.ServerStaticAddressLoopInRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion 11, // 6: looprpc.ServerStaticAddressLoopInResponse.standard_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo 11, // 7: looprpc.ServerStaticAddressLoopInResponse.high_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo @@ -1501,26 +1661,27 @@ var file_staticaddr_proto_depIdxs = []int32{ 13, // 9: looprpc.PushStaticAddressHtlcSigsRequest.standard_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo 13, // 10: looprpc.PushStaticAddressHtlcSigsRequest.high_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo 13, // 11: looprpc.PushStaticAddressHtlcSigsRequest.extreme_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo - 20, // 12: looprpc.PushStaticAddressSweeplessSigsRequest.signing_info:type_name -> looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry - 8, // 13: looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry.value:type_name -> looprpc.ServerPsbtWithdrawSigningInfo - 16, // 14: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry.value:type_name -> looprpc.ClientSweeplessSigningInfo - 1, // 15: looprpc.StaticAddressServer.ServerNewAddress:input_type -> looprpc.ServerNewAddressRequest - 4, // 16: looprpc.StaticAddressServer.ServerWithdrawDeposits:input_type -> looprpc.ServerWithdrawRequest - 6, // 17: looprpc.StaticAddressServer.ServerPsbtWithdrawDeposits:input_type -> looprpc.ServerPsbtWithdrawRequest - 9, // 18: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:input_type -> looprpc.ServerStaticAddressLoopInRequest - 12, // 19: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:input_type -> looprpc.PushStaticAddressHtlcSigsRequest - 15, // 20: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:input_type -> looprpc.PushStaticAddressSweeplessSigsRequest - 2, // 21: looprpc.StaticAddressServer.ServerNewAddress:output_type -> looprpc.ServerNewAddressResponse - 5, // 22: looprpc.StaticAddressServer.ServerWithdrawDeposits:output_type -> looprpc.ServerWithdrawResponse - 7, // 23: looprpc.StaticAddressServer.ServerPsbtWithdrawDeposits:output_type -> looprpc.ServerPsbtWithdrawResponse - 10, // 24: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:output_type -> looprpc.ServerStaticAddressLoopInResponse - 14, // 25: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:output_type -> looprpc.PushStaticAddressHtlcSigsResponse - 17, // 26: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:output_type -> looprpc.PushStaticAddressSweeplessSigsResponse - 21, // [21:27] is the sub-list for method output_type - 15, // [15:21] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 22, // 12: looprpc.PushStaticAddressSweeplessSigsRequest.signing_info:type_name -> looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry + 19, // 13: looprpc.InsufficientConfirmationsDetails.deposits:type_name -> looprpc.DepositConfirmationStatus + 8, // 14: looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry.value:type_name -> looprpc.ServerPsbtWithdrawSigningInfo + 16, // 15: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry.value:type_name -> looprpc.ClientSweeplessSigningInfo + 1, // 16: looprpc.StaticAddressServer.ServerNewAddress:input_type -> looprpc.ServerNewAddressRequest + 4, // 17: looprpc.StaticAddressServer.ServerWithdrawDeposits:input_type -> looprpc.ServerWithdrawRequest + 6, // 18: looprpc.StaticAddressServer.ServerPsbtWithdrawDeposits:input_type -> looprpc.ServerPsbtWithdrawRequest + 9, // 19: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:input_type -> looprpc.ServerStaticAddressLoopInRequest + 12, // 20: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:input_type -> looprpc.PushStaticAddressHtlcSigsRequest + 15, // 21: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:input_type -> looprpc.PushStaticAddressSweeplessSigsRequest + 2, // 22: looprpc.StaticAddressServer.ServerNewAddress:output_type -> looprpc.ServerNewAddressResponse + 5, // 23: looprpc.StaticAddressServer.ServerWithdrawDeposits:output_type -> looprpc.ServerWithdrawResponse + 7, // 24: looprpc.StaticAddressServer.ServerPsbtWithdrawDeposits:output_type -> looprpc.ServerPsbtWithdrawResponse + 10, // 25: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:output_type -> looprpc.ServerStaticAddressLoopInResponse + 14, // 26: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:output_type -> looprpc.PushStaticAddressHtlcSigsResponse + 17, // 27: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:output_type -> looprpc.PushStaticAddressSweeplessSigsResponse + 22, // [22:28] is the sub-list for method output_type + 16, // [16:22] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_staticaddr_proto_init() } @@ -1734,6 +1895,30 @@ func file_staticaddr_proto_init() { return nil } } + file_staticaddr_proto_msgTypes[17].Exporter = func(v any, i int) any { + switch v := v.(*InsufficientConfirmationsDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_staticaddr_proto_msgTypes[18].Exporter = func(v any, i int) any { + switch v := v.(*DepositConfirmationStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1741,7 +1926,7 @@ func file_staticaddr_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_staticaddr_proto_rawDesc, NumEnums: 1, - NumMessages: 20, + NumMessages: 22, NumExtensions: 0, NumServices: 1, }, diff --git a/swapserverrpc/staticaddr.proto b/swapserverrpc/staticaddr.proto index b590f4d44..c780a62dc 100644 --- a/swapserverrpc/staticaddr.proto +++ b/swapserverrpc/staticaddr.proto @@ -279,3 +279,30 @@ message ClientSweeplessSigningInfo { message PushStaticAddressSweeplessSigsResponse { } + +// InsufficientConfirmationsDetails provides structured information about +// deposits that don't meet dynamic confirmation requirements. This is +// attached as error details to FailedPrecondition errors. +message InsufficientConfirmationsDetails { + // Status of each deposit that has insufficient confirmations. + repeated DepositConfirmationStatus deposits = 1; + + // Maximum blocks to wait across all deposits. + int32 max_blocks_to_wait = 2; +} + +// DepositConfirmationStatus reports the confirmation state of a single deposit +// that does not meet the required confirmation threshold. +message DepositConfirmationStatus { + // The deposit outpoint in format txid:idx. + string outpoint = 1; + + // Current number of confirmations the deposit has. + int32 current_confirmations = 2; + + // Required number of confirmations based on risk assessment. + int32 required_confirmations = 3; + + // Estimated blocks to wait until requirement is met. + int32 blocks_to_wait = 4; +}