config: expose distinct accept-amp flag

This mirrors the accept-keysend flag, but also permits users to
eventually toggle off keysend separately from AMP.
This commit is contained in:
Conner Fromknecht 2021-05-06 09:19:05 -07:00
parent 64d07558d9
commit 2ecd1de713
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
6 changed files with 53 additions and 32 deletions

@ -338,6 +338,8 @@ type Config struct {
AcceptKeySend bool `long:"accept-keysend" description:"If true, spontaneous payments through keysend will be accepted. [experimental]"` AcceptKeySend bool `long:"accept-keysend" description:"If true, spontaneous payments through keysend will be accepted. [experimental]"`
AcceptAMP bool `long:"accept-amp" description:"If true, spontaneous payments via AMP will be accepted."`
KeysendHoldTime time.Duration `long:"keysend-hold-time" description:"If non-zero, keysend payments are accepted but not immediately settled. If the payment isn't settled manually after the specified time, it is canceled automatically. [experimental]"` KeysendHoldTime time.Duration `long:"keysend-hold-time" description:"If non-zero, keysend payments are accepted but not immediately settled. If the payment isn't settled manually after the specified time, it is canceled automatically. [experimental]"`
GcCanceledInvoicesOnStartup bool `long:"gc-canceled-invoices-on-startup" description:"If true, we'll attempt to garbage collect canceled invoices upon start."` GcCanceledInvoicesOnStartup bool `long:"gc-canceled-invoices-on-startup" description:"If true, we'll attempt to garbage collect canceled invoices upon start."`

@ -57,6 +57,10 @@ type RegistryConfig struct {
// send payments. // send payments.
AcceptKeySend bool AcceptKeySend bool
// AcceptAMP indicates whether we want to accept spontaneous AMP
// payments.
AcceptAMP bool
// GcCanceledInvoicesOnStartup if set, we'll attempt to garbage collect // GcCanceledInvoicesOnStartup if set, we'll attempt to garbage collect
// all canceled invoices upon start. // all canceled invoices upon start.
GcCanceledInvoicesOnStartup bool GcCanceledInvoicesOnStartup bool
@ -884,28 +888,33 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
amp: payload.AMPRecord(), amp: payload.AMPRecord(),
} }
// Process keysend if present. Do this outside of the lock, because switch {
// AddInvoice obtains its own lock. This is no problem, because the
// operation is idempotent.
if i.cfg.AcceptKeySend {
if ctx.amp != nil {
err := i.processAMP(ctx)
if err != nil {
ctx.log(fmt.Sprintf("amp error: %v", err))
return NewFailResolution( // If we are accepting spontaneous AMP payments and this payload
circuitKey, currentHeight, ResultAmpError, // contains an AMP record, create an AMP invoice that will be settled
), nil // below.
} case i.cfg.AcceptAMP && ctx.amp != nil:
} else { err := i.processAMP(ctx)
err := i.processKeySend(ctx) if err != nil {
if err != nil { ctx.log(fmt.Sprintf("amp error: %v", err))
ctx.log(fmt.Sprintf("keysend error: %v", err))
return NewFailResolution( return NewFailResolution(
circuitKey, currentHeight, ResultKeySendError, circuitKey, currentHeight, ResultAmpError,
), nil ), nil
} }
// If we are accepting spontaneous keysend payments, create a regular
// invoice that will be settled below. We also enforce that this is only
// done when no AMP payload is present since it will only be settle-able
// by regular HTLCs.
case i.cfg.AcceptKeySend && ctx.amp == nil:
err := i.processKeySend(ctx)
if err != nil {
ctx.log(fmt.Sprintf("keysend error: %v", err))
return NewFailResolution(
circuitKey, currentHeight, ResultKeySendError,
), nil
} }
} }

@ -1290,7 +1290,7 @@ func TestAMPWithoutMPPPayload(t *testing.T) {
ctx := newTestContext(t) ctx := newTestContext(t)
defer ctx.cleanup() defer ctx.cleanup()
ctx.registry.cfg.AcceptKeySend = true ctx.registry.cfg.AcceptAMP = true
const ( const (
shardAmt = lnwire.MilliSatoshi(10) shardAmt = lnwire.MilliSatoshi(10)
@ -1320,37 +1320,37 @@ func TestAMPWithoutMPPPayload(t *testing.T) {
func TestSpontaneousAmpPayment(t *testing.T) { func TestSpontaneousAmpPayment(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
keySendEnabled bool ampEnabled bool
failReconstruction bool failReconstruction bool
numShards int numShards int
}{ }{
{ {
name: "enabled valid one shard", name: "enabled valid one shard",
keySendEnabled: true, ampEnabled: true,
failReconstruction: false, failReconstruction: false,
numShards: 1, numShards: 1,
}, },
{ {
name: "enabled valid multiple shards", name: "enabled valid multiple shards",
keySendEnabled: true, ampEnabled: true,
failReconstruction: false, failReconstruction: false,
numShards: 3, numShards: 3,
}, },
{ {
name: "enabled invalid one shard", name: "enabled invalid one shard",
keySendEnabled: true, ampEnabled: true,
failReconstruction: true, failReconstruction: true,
numShards: 1, numShards: 1,
}, },
{ {
name: "enabled invalid multiple shards", name: "enabled invalid multiple shards",
keySendEnabled: true, ampEnabled: true,
failReconstruction: true, failReconstruction: true,
numShards: 3, numShards: 3,
}, },
{ {
name: "disabled valid multiple shards", name: "disabled valid multiple shards",
keySendEnabled: false, ampEnabled: false,
failReconstruction: false, failReconstruction: false,
numShards: 3, numShards: 3,
}, },
@ -1360,7 +1360,7 @@ func TestSpontaneousAmpPayment(t *testing.T) {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
testSpontaneousAmpPayment( testSpontaneousAmpPayment(
t, test.keySendEnabled, test.failReconstruction, t, test.ampEnabled, test.failReconstruction,
test.numShards, test.numShards,
) )
}) })
@ -1369,14 +1369,14 @@ func TestSpontaneousAmpPayment(t *testing.T) {
// testSpontaneousAmpPayment runs a specific spontaneous AMP test case. // testSpontaneousAmpPayment runs a specific spontaneous AMP test case.
func testSpontaneousAmpPayment( func testSpontaneousAmpPayment(
t *testing.T, keySendEnabled, failReconstruction bool, numShards int) { t *testing.T, ampEnabled, failReconstruction bool, numShards int) {
defer timeout()() defer timeout()()
ctx := newTestContext(t) ctx := newTestContext(t)
defer ctx.cleanup() defer ctx.cleanup()
ctx.registry.cfg.AcceptKeySend = keySendEnabled ctx.registry.cfg.AcceptAMP = ampEnabled
allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0)
require.Nil(t, err) require.Nil(t, err)
@ -1471,7 +1471,7 @@ func testSpontaneousAmpPayment(
// When keysend is disabled all HTLC should fail with invoice // When keysend is disabled all HTLC should fail with invoice
// not found, since one is not inserted before executing // not found, since one is not inserted before executing
// UpdateInvoice. // UpdateInvoice.
if !keySendEnabled { if !ampEnabled {
require.NotNil(t, resolution) require.NotNil(t, resolution)
checkFailResolution(t, resolution, ResultInvoiceNotFound) checkFailResolution(t, resolution, ResultInvoiceNotFound)
continue continue
@ -1515,7 +1515,7 @@ func testSpontaneousAmpPayment(
} }
// No need to check the hodl chans when keysend is not enabled. // No need to check the hodl chans when keysend is not enabled.
if !keySendEnabled { if !ampEnabled {
return return
} }

@ -210,6 +210,7 @@ type NodeConfig struct {
ProfilePort int ProfilePort int
AcceptKeySend bool AcceptKeySend bool
AcceptAMP bool
FeeURL string FeeURL string
@ -297,6 +298,10 @@ func (cfg NodeConfig) genArgs() []string {
args = append(args, "--accept-keysend") args = append(args, "--accept-keysend")
} }
if cfg.AcceptAMP {
args = append(args, "--accept-amp")
}
if cfg.Etcd { if cfg.Etcd {
args = append(args, "--db.backend=etcd") args = append(args, "--db.backend=etcd")
args = append(args, "--db.etcd.embedded") args = append(args, "--db.etcd.embedded")

@ -375,6 +375,10 @@
; automatically. [experimental] ; automatically. [experimental]
; keysend-hold-time=true ; keysend-hold-time=true
; If true, spontaneous payments through AMP will be accepted. Payments to AMP
; invoices will be accepted regardless of this setting.
; accept-amp=true
; If set, lnd will use anchor channels by default if the remote channel party ; If set, lnd will use anchor channels by default if the remote channel party
; supports them. Note that lnd will require 1 UTXO to be reserved for this ; supports them. Note that lnd will require 1 UTXO to be reserved for this
; channel type if it is enabled. ; channel type if it is enabled.

@ -427,6 +427,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
HtlcHoldDuration: invoices.DefaultHtlcHoldDuration, HtlcHoldDuration: invoices.DefaultHtlcHoldDuration,
Clock: clock.NewDefaultClock(), Clock: clock.NewDefaultClock(),
AcceptKeySend: cfg.AcceptKeySend, AcceptKeySend: cfg.AcceptKeySend,
AcceptAMP: cfg.AcceptAMP,
GcCanceledInvoicesOnStartup: cfg.GcCanceledInvoicesOnStartup, GcCanceledInvoicesOnStartup: cfg.GcCanceledInvoicesOnStartup,
GcCanceledInvoicesOnTheFly: cfg.GcCanceledInvoicesOnTheFly, GcCanceledInvoicesOnTheFly: cfg.GcCanceledInvoicesOnTheFly,
KeysendHoldTime: cfg.KeysendHoldTime, KeysendHoldTime: cfg.KeysendHoldTime,