mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
multi: store loop out htlc confirmations on disk
To allow users to specify differing confirmation targets, we store the swap conf target per-swap. This makes us restart safe, so we do not forget confirmation values for swaps that are in flight when we restart.
This commit is contained in:
parent
e15549e9af
commit
1877b7f08b
7 changed files with 70 additions and 12 deletions
|
|
@ -36,6 +36,8 @@ var (
|
|||
|
||||
swapInvoiceDesc = "swap"
|
||||
prepayInvoiceDesc = "prepay"
|
||||
|
||||
defaultConfirmations = int32(loopdb.DefaultLoopOutHtlcConfirmations)
|
||||
)
|
||||
|
||||
// TestSuccess tests the loop out happy flow.
|
||||
|
|
@ -57,7 +59,7 @@ func TestSuccess(t *testing.T) {
|
|||
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
|
||||
|
||||
// Expect client to register for conf.
|
||||
confIntent := ctx.AssertRegisterConf(false)
|
||||
confIntent := ctx.AssertRegisterConf(false, defaultConfirmations)
|
||||
|
||||
testSuccess(ctx, testRequest.Amount, info.SwapHash,
|
||||
signalPrepaymentResult, signalSwapPaymentResult, false,
|
||||
|
|
@ -83,7 +85,7 @@ func TestFailOffchain(t *testing.T) {
|
|||
signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
|
||||
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
|
||||
|
||||
ctx.AssertRegisterConf(false)
|
||||
ctx.AssertRegisterConf(false, defaultConfirmations)
|
||||
|
||||
signalSwapPaymentResult(
|
||||
errors.New(lndclient.PaymentResultUnknownPaymentHash),
|
||||
|
|
@ -196,6 +198,7 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
|
|||
DestAddr: dest,
|
||||
SwapInvoice: swapPayReq,
|
||||
SweepConfTarget: 2,
|
||||
HtlcConfirmations: loopdb.DefaultLoopOutHtlcConfirmations,
|
||||
MaxSwapRoutingFee: 70000,
|
||||
PrepayInvoice: prePayReq,
|
||||
SwapContract: loopdb.SwapContract{
|
||||
|
|
@ -232,7 +235,9 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
|
|||
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
|
||||
|
||||
// Expect client to register for conf.
|
||||
confIntent := ctx.AssertRegisterConf(preimageRevealed)
|
||||
confIntent := ctx.AssertRegisterConf(
|
||||
preimageRevealed, defaultConfirmations,
|
||||
)
|
||||
|
||||
signalSwapPaymentResult(nil)
|
||||
signalPrepaymentResult(nil)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ type LoopOutContract struct {
|
|||
// client sweep tx.
|
||||
SweepConfTarget int32
|
||||
|
||||
// HtlcConfirmations is the number of confirmations we require the on
|
||||
// chain htlc to have before proceeding with the swap.
|
||||
HtlcConfirmations uint32
|
||||
|
||||
// OutgoingChanSet is the set of short ids of channels that may be used.
|
||||
// If empty, any channel may be used.
|
||||
OutgoingChanSet ChannelSet
|
||||
|
|
|
|||
|
|
@ -77,11 +77,23 @@ var (
|
|||
// value: concatenation of uint64 channel ids
|
||||
outgoingChanSetKey = []byte("outgoing-chan-set")
|
||||
|
||||
// confirmationsKey is the key that stores the number of confirmations
|
||||
// that were requested for a loop out swap.
|
||||
//
|
||||
// path: loopOutBucket -> swapBucket[hash] -> confirmationsKey
|
||||
//
|
||||
// value: uint32 confirmation value
|
||||
confirmationsKey = []byte("confirmations")
|
||||
|
||||
byteOrder = binary.BigEndian
|
||||
|
||||
keyLength = 33
|
||||
)
|
||||
|
||||
// DefaultLoopOutHtlcConfirmations is the default number of confirmations we
|
||||
// set for a loop out htlc.
|
||||
const DefaultLoopOutHtlcConfirmations uint32 = 1
|
||||
|
||||
// fileExists returns true if the file exists, and false otherwise.
|
||||
func fileExists(path string) bool {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
|
|
@ -242,6 +254,23 @@ func (s *boltSwapStore) FetchLoopOutSwaps() ([]*LoopOut, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Set our default number of confirmations for the swap.
|
||||
contract.HtlcConfirmations = DefaultLoopOutHtlcConfirmations
|
||||
|
||||
// If we have the number of confirmations stored for
|
||||
// this swap, we overwrite our default with the stored
|
||||
// value.
|
||||
confBytes := swapBucket.Get(confirmationsKey)
|
||||
if confBytes != nil {
|
||||
r := bytes.NewReader(confBytes)
|
||||
err := binary.Read(
|
||||
r, byteOrder, &contract.HtlcConfirmations,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
updates, err := deserializeUpdates(swapBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -471,6 +500,18 @@ func (s *boltSwapStore) CreateLoopOut(hash lntypes.Hash,
|
|||
return err
|
||||
}
|
||||
|
||||
// Write our confirmation target under its own key.
|
||||
var buf bytes.Buffer
|
||||
err = binary.Write(&buf, byteOrder, swap.HtlcConfirmations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = swapBucket.Put(confirmationsKey, buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finally, we'll create an empty updates bucket for this swap
|
||||
// to track any future updates to the swap itself.
|
||||
_, err = swapBucket.CreateBucket(updatesBucketKey)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ func TestLoopOutStore(t *testing.T) {
|
|||
SwapInvoice: "swapinvoice",
|
||||
MaxSwapRoutingFee: 30,
|
||||
SweepConfTarget: 2,
|
||||
HtlcConfirmations: 2,
|
||||
SwapPublicationDeadline: time.Unix(0, initiationTime.UnixNano()),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
DestAddr: request.DestAddr,
|
||||
MaxSwapRoutingFee: request.MaxSwapRoutingFee,
|
||||
SweepConfTarget: request.SweepConfTarget,
|
||||
HtlcConfirmations: loopdb.DefaultLoopOutHtlcConfirmations,
|
||||
PrepayInvoice: swapResp.prepayInvoice,
|
||||
MaxPrepayRoutingFee: request.MaxPrepayRoutingFee,
|
||||
SwapPublicationDeadline: request.SwapPublicationDeadline,
|
||||
|
|
@ -606,8 +607,8 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
// Wait for confirmation of the on-chain htlc by watching for a tx
|
||||
// producing the swap script output.
|
||||
s.log.Infof(
|
||||
"Register conf ntfn for swap script on chain (hh=%v)",
|
||||
s.InitiationHeight,
|
||||
"Register %v conf ntfn for swap script on chain (hh=%v)",
|
||||
s.HtlcConfirmations, s.InitiationHeight,
|
||||
)
|
||||
|
||||
// If we've revealed the preimage in a previous run, we expect to have
|
||||
|
|
@ -624,8 +625,8 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
|
|||
defer cancel()
|
||||
htlcConfChan, htlcErrChan, err :=
|
||||
s.lnd.ChainNotifier.RegisterConfirmationsNtfn(
|
||||
ctx, s.htlcTxHash, s.htlc.PkScript, 1,
|
||||
s.InitiationHeight,
|
||||
ctx, s.htlcTxHash, s.htlc.PkScript,
|
||||
int32(s.HtlcConfirmations), s.InitiationHeight,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ func TestLoopOutPaymentParameters(t *testing.T) {
|
|||
|
||||
// Swap is expected to register for confirmation of the htlc. Assert
|
||||
// this to prevent a blocked channel in the mock.
|
||||
ctx.AssertRegisterConf(false)
|
||||
ctx.AssertRegisterConf(false, defaultConfirmations)
|
||||
|
||||
// Cancel the swap. There is nothing else we need to assert. The payment
|
||||
// parameters don't play a role in the remainder of the swap process.
|
||||
|
|
@ -191,7 +191,7 @@ func TestLateHtlcPublish(t *testing.T) {
|
|||
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
|
||||
|
||||
// Expect client to register for conf
|
||||
ctx.AssertRegisterConf(false)
|
||||
ctx.AssertRegisterConf(false, defaultConfirmations)
|
||||
|
||||
// // Wait too long before publishing htlc.
|
||||
blockEpochChan <- int32(swap.CltvExpiry - 10)
|
||||
|
|
@ -290,7 +290,7 @@ func TestCustomSweepConfTarget(t *testing.T) {
|
|||
signalPrepaymentResult(nil)
|
||||
|
||||
// Notify the confirmation notification for the HTLC.
|
||||
ctx.AssertRegisterConf(false)
|
||||
ctx.AssertRegisterConf(false, defaultConfirmations)
|
||||
|
||||
blockEpochChan <- ctx.Lnd.Height + 1
|
||||
|
||||
|
|
@ -494,7 +494,7 @@ func TestPreimagePush(t *testing.T) {
|
|||
signalPrepaymentResult(nil)
|
||||
|
||||
// Notify the confirmation notification for the HTLC.
|
||||
ctx.AssertRegisterConf(false)
|
||||
ctx.AssertRegisterConf(false, defaultConfirmations)
|
||||
|
||||
blockEpochChan <- ctx.Lnd.Height + 1
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Context contains shared test context functions.
|
||||
|
|
@ -113,7 +114,7 @@ func (ctx *Context) AssertTrackPayment() TrackPaymentMessage {
|
|||
}
|
||||
|
||||
// AssertRegisterConf asserts that a register for conf has been received.
|
||||
func (ctx *Context) AssertRegisterConf(expectTxHash bool) *ConfRegistration {
|
||||
func (ctx *Context) AssertRegisterConf(expectTxHash bool, confs int32) *ConfRegistration {
|
||||
ctx.T.Helper()
|
||||
|
||||
// Expect client to register for conf
|
||||
|
|
@ -127,6 +128,11 @@ func (ctx *Context) AssertRegisterConf(expectTxHash bool) *ConfRegistration {
|
|||
case !expectTxHash && confIntent.TxID != nil:
|
||||
ctx.T.Fatalf("expected script only registration")
|
||||
}
|
||||
|
||||
// Require that we registered for the number of confirmations
|
||||
// the test expects.
|
||||
require.Equal(ctx.T, confs, confIntent.NumConfs)
|
||||
|
||||
case <-time.After(Timeout):
|
||||
ctx.T.Fatalf("htlc confirmed not subscribed to")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue