multi: update loop out confirmations to scale above threshold

pull/222/head
carla 4 years ago
parent c25c728d9e
commit 186ced8970
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91

@ -117,6 +117,15 @@ type ClientConfig struct {
// HtlcConfirmations is the number of confirmations we wait for an on
// chain htlc.
HtlcConfirmations uint32
// LoopOutConfirmationThreshold is the amount at which we scale loop
// out confirmations up to loopOutThresholdConfirmations.
LoopOutConfirmationThreshold btcutil.Amount
// LoopOutThresholdConfirmations is the number of confirmations we
// require for loop out on chain transactions when the amount is greater
// than or equal to loopOutConfirmationThreshold.
LoopOutThresholdConfirmations uint32
}
// NewClient returns a new instance to initiate swaps with.
@ -150,12 +159,14 @@ func NewClient(dbDir string, cfg *ClientConfig) (*Client, func(), error) {
}
executor := newExecutor(&executorConfig{
lnd: cfg.Lnd,
store: store,
sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer,
loopOutMaxParts: cfg.LoopOutMaxParts,
htlcConfirmations: cfg.HtlcConfirmations,
lnd: cfg.Lnd,
store: store,
sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer,
loopOutMaxParts: cfg.LoopOutMaxParts,
htlcConfirmations: cfg.HtlcConfirmations,
loopOutConfirmationThreshold: cfg.LoopOutConfirmationThreshold,
loopOutThresholdConfirmations: cfg.LoopOutThresholdConfirmations,
})
client := &Client{
@ -326,7 +337,13 @@ func (s *Client) resumeSwaps(ctx context.Context,
if pend.State().State.Type() != loopdb.StateTypePending {
continue
}
swap, err := resumeLoopOutSwap(ctx, swapCfg, pend)
params := newConfirmationParams(
s.executor.loopOutConfirmationThreshold,
s.executor.loopOutThresholdConfirmations,
)
swap, err := resumeLoopOutSwap(ctx, swapCfg, pend, params)
if err != nil {
log.Errorf("resuming loop out swap: %v", err)
continue
@ -372,8 +389,12 @@ func (s *Client) LoopOut(globalCtx context.Context,
// Create a new swap object for this swap.
initiationHeight := s.executor.height()
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server)
params := newConfirmationParams(
s.executor.loopOutConfirmationThreshold,
s.executor.loopOutThresholdConfirmations,
)
swap, err := newLoopOutSwap(
globalCtx, swapCfg, initiationHeight, request,
globalCtx, swapCfg, initiationHeight, request, params,
)
if err != nil {
return nil, nil, err

@ -57,7 +57,7 @@ func TestSuccess(t *testing.T) {
// Expect client to register for conf.
confIntent := ctx.AssertRegisterConf(
ctx.swapClient.executor.executorConfig.htlcConfirmations,
ctx.loopOutConfirmations(testRequest.Amount),
)
testSuccess(ctx, testRequest.Amount, *hash,
@ -85,7 +85,7 @@ func TestFailOffchain(t *testing.T) {
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
ctx.AssertRegisterConf(
ctx.swapClient.executor.executorConfig.htlcConfirmations,
ctx.loopOutConfirmations(testRequest.Amount),
)
signalSwapPaymentResult(
@ -235,7 +235,7 @@ func testResume(t *testing.T, expired, preimageRevealed, expectSuccess bool) {
// Expect client to register for conf.
confIntent := ctx.AssertRegisterConf(
ctx.swapClient.executor.executorConfig.htlcConfirmations,
ctx.loopOutConfirmations(testRequest.Amount),
)
signalSwapPaymentResult(nil)

@ -7,6 +7,7 @@ import (
"sync/atomic"
"time"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/loop/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/sweep"
@ -28,6 +29,15 @@ type executorConfig struct {
// htlcConfirmations is the number of confirmations we wait for an
// on chain htlc.
htlcConfirmations uint32
// loopOutConfirmationThreshold is the amount at which we scale loop
// out confirmations up to loopOutThresholdConfirmations.
loopOutConfirmationThreshold btcutil.Amount
// loopOutThresholdConfirmations is the number of confirmations we
// require for loop out on chain transactions when the amount is greater
// than or equal to loopOutConfirmationThreshold.
loopOutThresholdConfirmations uint32
}
// executor is responsible for executing swaps.
@ -122,7 +132,7 @@ func (s *executor) run(mainCtx context.Context,
s.executorConfig.htlcConfirmations,
)
newSwap.execute(mainCtx, cfg, height)
_ = newSwap.execute(mainCtx, cfg, height)
select {
case swapDoneChan <- swapID:

@ -19,6 +19,15 @@ var (
defaultMaxLogFileSize = 10
defaultLoopOutMaxParts = uint32(5)
defaultHtlcConfs = uint32(1)
// defaultThresholdConfs is the number of confirmations we scale loop
// out on chain transactions to require if our swap is greater than or
// equal to our confirmation threshold.
defaultThresholdConfs = uint32(3)
// defaultConfThreshold is the default threshold at which we require
// more confirmations for on chain loop out transactions.
defaultConfThreshold int64 = 4294967
)
type lndConfig struct {
@ -49,7 +58,9 @@ type Config struct {
LoopOutMaxParts uint32 `long:"loopoutmaxparts" description:"The maximum number of payment parts that may be used for a loop out swap."`
HtlcConfirmations uint32 `long:"htlcconfs" description:"Confirmation target for on chain htlcs (blocks)."`
HtlcConfirmations uint32 `long:"htlcconfs" description:"Confirmation target for on chain htlcs (blocks)."`
LoopOutConfThreshold int64 `long:"outconfthreshold" description:"The swap amount at which we scale confirmations to outthresholdconfs (sats)."`
LoopOutThresholdConfs uint32 `long:"outthresholdconfs" description:"Confirmations required for loop out swaps with outconfthreshold amount or higher (blocks)."`
Lnd *lndConfig `group:"lnd" namespace:"lnd"`
Proxy string `long:"proxy" description:"The host:port of a SOCKS proxy through which all connections to the swap server will be established over."`
@ -65,18 +76,20 @@ const (
// DefaultConfig returns all default values for the Config struct.
func DefaultConfig() Config {
return Config{
Network: "mainnet",
RPCListen: "localhost:11010",
RESTListen: "localhost:8081",
Insecure: false,
LogDir: defaultLogDir,
MaxLogFiles: defaultMaxLogFiles,
MaxLogFileSize: defaultMaxLogFileSize,
DebugLevel: defaultLogLevel,
MaxLSATCost: lsat.DefaultMaxCostSats,
MaxLSATFee: lsat.DefaultMaxRoutingFeeSats,
LoopOutMaxParts: defaultLoopOutMaxParts,
HtlcConfirmations: defaultHtlcConfs,
Network: "mainnet",
RPCListen: "localhost:11010",
RESTListen: "localhost:8081",
Insecure: false,
LogDir: defaultLogDir,
MaxLogFiles: defaultMaxLogFiles,
MaxLogFileSize: defaultMaxLogFileSize,
DebugLevel: defaultLogLevel,
MaxLSATCost: lsat.DefaultMaxCostSats,
MaxLSATFee: lsat.DefaultMaxRoutingFeeSats,
LoopOutMaxParts: defaultLoopOutMaxParts,
HtlcConfirmations: defaultHtlcConfs,
LoopOutThresholdConfs: defaultThresholdConfs,
LoopOutConfThreshold: defaultConfThreshold,
Lnd: &lndConfig{
Host: "localhost:10009",
},

@ -19,15 +19,17 @@ func getClient(config *Config, lnd *lndclient.LndServices) (*loop.Client,
}
clientConfig := &loop.ClientConfig{
ServerAddress: config.SwapServer,
ProxyAddress: config.Proxy,
Insecure: config.Insecure,
TLSPathServer: config.TLSPathSwapSrv,
Lnd: lnd,
MaxLsatCost: btcutil.Amount(config.MaxLSATCost),
MaxLsatFee: btcutil.Amount(config.MaxLSATFee),
LoopOutMaxParts: config.LoopOutMaxParts,
HtlcConfirmations: config.HtlcConfirmations,
ServerAddress: config.SwapServer,
ProxyAddress: config.Proxy,
Insecure: config.Insecure,
TLSPathServer: config.TLSPathSwapSrv,
Lnd: lnd,
MaxLsatCost: btcutil.Amount(config.MaxLSATCost),
MaxLsatFee: btcutil.Amount(config.MaxLSATFee),
LoopOutMaxParts: config.LoopOutMaxParts,
HtlcConfirmations: config.HtlcConfirmations,
LoopOutConfirmationThreshold: btcutil.Amount(config.LoopOutConfThreshold),
LoopOutThresholdConfirmations: config.LoopOutThresholdConfs,
}
swapClient, cleanUp, err := loop.NewClient(storeDir, clientConfig)

@ -58,6 +58,44 @@ type loopOutSwap struct {
swapPaymentChan chan lndclient.PaymentResult
prePaymentChan chan lndclient.PaymentResult
// confirmationParams contains additional information required for
// scaling loop out confirmations.
confirmationParams
}
// confirmationParams contains the values we need to scale a swap's
// confirmations after a set threshold.
type confirmationParams struct {
// threshold is the amount at which we scale up our confirmations.
threshold btcutil.Amount
// thresholdConfs is the number of confirmations we scale to if at
// the threshold amount.
thresholdConfs uint32
}
// newConfirmationParams returns a set of confirmation parameters.
func newConfirmationParams(threshold btcutil.Amount,
confirmations uint32) confirmationParams {
return confirmationParams{
threshold: threshold,
thresholdConfs: confirmations,
}
}
// confirmations returns the number of confirmations a swap should have based on
// the amount of the swap. If it is below our threshold, we return the default
// confirmation amount. Otherwise we return our threshold confirmations.
func (c confirmationParams) confirmations(amt btcutil.Amount,
defaultConf uint32) uint32 {
if amt < c.threshold {
return defaultConf
}
return c.thresholdConfs
}
// executeConfig contains extra configuration to execute the swap.
@ -89,7 +127,8 @@ func newExecuteConfig(sweeper *sweep.Sweeper, statusChan chan<- SwapInfo,
// newLoopOutSwap initiates a new swap with the server and returns a
// corresponding swap object.
func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
currentHeight int32, request *OutRequest) (*loopOutSwap, error) {
currentHeight int32, request *OutRequest,
params confirmationParams) (*loopOutSwap, error) {
// Generate random preimage.
var swapPreimage [32]byte
@ -177,9 +216,10 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
swapKit.log.Infof("Htlc address: %v", htlc.Address)
swap := &loopOutSwap{
LoopOutContract: contract,
swapKit: *swapKit,
htlc: htlc,
LoopOutContract: contract,
swapKit: *swapKit,
htlc: htlc,
confirmationParams: params,
}
// Persist the data before exiting this function, so that the caller
@ -195,7 +235,8 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
// resumeLoopOutSwap returns a swap object representing a pending swap that has
// been restored from the database.
func resumeLoopOutSwap(reqContext context.Context, cfg *swapConfig,
pend *loopdb.LoopOut) (*loopOutSwap, error) {
pend *loopdb.LoopOut, params confirmationParams) (*loopOutSwap,
error) {
hash := lntypes.Hash(sha256.Sum256(pend.Contract.Preimage[:]))
@ -216,9 +257,10 @@ func resumeLoopOutSwap(reqContext context.Context, cfg *swapConfig,
// Create the swap.
swap := &loopOutSwap{
LoopOutContract: *pend.Contract,
swapKit: *swapKit,
htlc: htlc,
LoopOutContract: *pend.Contract,
swapKit: *swapKit,
htlc: htlc,
confirmationParams: params,
}
lastUpdate := pend.LastUpdate()
@ -573,19 +615,24 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
*chainntnfs.TxConfirmation, error) {
// Get the number of confirmations we require for this amount.
confs := s.confirmations(
s.AmountRequested, s.executeConfig.htlcConfirmations,
)
// 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 conf ntfn for %v confs for swap script on chain "+
"(hh=%v)", confs, s.InitiationHeight,
)
ctx, cancel := context.WithCancel(globalCtx)
defer cancel()
htlcConfChan, htlcErrChan, err :=
s.lnd.ChainNotifier.RegisterConfirmationsNtfn(
ctx, nil, s.htlc.PkScript,
int32(s.executeConfig.htlcConfirmations),
ctx, nil, s.htlc.PkScript, int32(confs),
s.InitiationHeight,
)
if err != nil {

@ -22,6 +22,9 @@ import (
var (
testLoopOutMaxParts uint32 = 5
testHtlcConfs uint32 = 1
testLoopOutParams = newConfirmationParams(
btcutil.Amount(40000), 3,
)
)
// TestLoopOutPaymentParameters tests the first part of the loop out process up
@ -58,7 +61,7 @@ func TestLoopOutPaymentParameters(t *testing.T) {
req.OutgoingChanSet = loopdb.ChannelSet{2, 3}
swap, err := newLoopOutSwap(
context.Background(), cfg, height, &req,
context.Background(), cfg, height, &req, testLoopOutParams,
)
if err != nil {
t.Fatal(err)
@ -117,7 +120,10 @@ 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(testHtlcConfs)
confs := testLoopOutParams.confirmations(
swap.AmountRequested, swap.executeConfig.htlcConfirmations,
)
ctx.AssertRegisterConf(confs)
// 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.
@ -154,6 +160,7 @@ func TestLateHtlcPublish(t *testing.T) {
swap, err := newLoopOutSwap(
context.Background(), cfg, height, testRequest,
testLoopOutParams,
)
if err != nil {
t.Fatal(err)
@ -189,7 +196,10 @@ func TestLateHtlcPublish(t *testing.T) {
signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
// Expect client to register for conf
ctx.AssertRegisterConf(testHtlcConfs)
confs := testLoopOutParams.confirmations(
swap.AmountRequested, swap.executeConfig.htlcConfirmations,
)
ctx.AssertRegisterConf(confs)
// // Wait too long before publishing htlc.
blockEpochChan <- swap.CltvExpiry - 10
@ -239,6 +249,7 @@ func TestCustomSweepConfTarget(t *testing.T) {
swap, err := newLoopOutSwap(
context.Background(), cfg, ctx.Lnd.Height, testRequest,
testLoopOutParams,
)
if err != nil {
t.Fatal(err)
@ -285,7 +296,10 @@ func TestCustomSweepConfTarget(t *testing.T) {
signalPrepaymentResult(nil)
// Notify the confirmation notification for the HTLC.
ctx.AssertRegisterConf(testHtlcConfs)
confs := testLoopOutParams.confirmations(
swap.AmountRequested, swap.executeConfig.htlcConfirmations,
)
ctx.AssertRegisterConf(confs)
blockEpochChan <- ctx.Lnd.Height + 1
@ -446,6 +460,7 @@ func TestPreimagePush(t *testing.T) {
swap, err := newLoopOutSwap(
context.Background(), cfg, ctx.Lnd.Height, testRequest,
testLoopOutParams,
)
require.NoError(t, err)
@ -486,7 +501,10 @@ func TestPreimagePush(t *testing.T) {
signalPrepaymentResult(nil)
// Notify the confirmation notification for the HTLC.
ctx.AssertRegisterConf(testHtlcConfs)
confs := testLoopOutParams.confirmations(
swap.AmountRequested, swap.executeConfig.htlcConfirmations,
)
ctx.AssertRegisterConf(confs)
blockEpochChan <- ctx.Lnd.Height + 1

@ -25,6 +25,9 @@ var (
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
})
testLoopOutConfirmationThreshold = btcutil.Amount(40000)
testLoopOutThresholdConfirmations uint32 = 3
)
// testContext contains functionality to support client unit tests.
@ -48,11 +51,13 @@ func newSwapClient(config *clientConfig) *Client {
lndServices := config.LndServices
executor := newExecutor(&executorConfig{
lnd: lndServices,
store: config.Store,
sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer,
htlcConfirmations: 1,
lnd: lndServices,
store: config.Store,
sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer,
htlcConfirmations: 1,
loopOutConfirmationThreshold: testLoopOutConfirmationThreshold,
loopOutThresholdConfirmations: testLoopOutThresholdConfirmations,
})
return &Client{
@ -262,3 +267,16 @@ func (ctx *testContext) assertPreimagePush(preimage lntypes.Preimage) {
ctx.T.Fatalf("preimage not pushed")
}
}
// loopOutConfirmations returns the number of confirmations that our
// server will assign to a loop out swap of the amount provided.
func (ctx *testContext) loopOutConfirmations(amt btcutil.Amount) uint32 {
params := newConfirmationParams(
ctx.swapClient.executor.loopOutConfirmationThreshold,
ctx.swapClient.executor.loopOutThresholdConfirmations,
)
return params.confirmations(
amt, ctx.swapClient.executor.htlcConfirmations,
)
}

Loading…
Cancel
Save