Skip to content

multi: add new strict-verify CLI option to control invoice usage #175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions aperture.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func (a *Aperture) Start(errChan chan error) error {

a.challenger, err = challenger.NewLNCChallenger(
session, lncStore, a.cfg.InvoiceBatchSize,
genInvoiceReq, errChan,
genInvoiceReq, errChan, a.cfg.StrictVerify,
)
if err != nil {
return fmt.Errorf("unable to start lnc "+
Expand All @@ -361,7 +361,7 @@ func (a *Aperture) Start(errChan chan error) error {

a.challenger, err = challenger.NewLndChallenger(
client, a.cfg.InvoiceBatchSize, genInvoiceReq,
context.Background, errChan,
context.Background, errChan, a.cfg.StrictVerify,
)
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions challenger/lnc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type LNCChallenger struct {
// connect to an lnd backend to create payment challenges.
func NewLNCChallenger(session *lnc.Session, lncStore lnc.Store,
invoiceBatchSize int, genInvoiceReq InvoiceRequestGenerator,
errChan chan<- error) (*LNCChallenger, error) {
errChan chan<- error, strictVerify bool) (*LNCChallenger, error) {

nodeConn, err := lnc.NewNodeConn(session, lncStore)
if err != nil {
Expand All @@ -35,7 +35,7 @@ func NewLNCChallenger(session *lnc.Session, lncStore lnc.Store,

lndChallenger, err := NewLndChallenger(
client, invoiceBatchSize, genInvoiceReq, nodeConn.CtxFunc,
errChan,
errChan, strictVerify,
)
if err != nil {
return nil, err
Expand Down
24 changes: 21 additions & 3 deletions challenger/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type LndChallenger struct {
invoicesCancel func()
invoicesCond *sync.Cond

// strictVerify indicates whether we should verify the invoice states,
// or rely on the higher level preimage verification.
strictVerify bool

errChan chan<- error

quit chan struct{}
Expand All @@ -38,9 +42,8 @@ var _ Challenger = (*LndChallenger)(nil)
// NewLndChallenger creates a new challenger that uses the given connection to
// an lnd backend to create payment challenges.
func NewLndChallenger(client InvoiceClient, batchSize int,
genInvoiceReq InvoiceRequestGenerator,
ctxFunc func() context.Context,
errChan chan<- error) (*LndChallenger, error) {
genInvoiceReq InvoiceRequestGenerator, ctxFunc func() context.Context,
errChan chan<- error, strictVerification bool) (*LndChallenger, error) {

// Make sure we have a valid context function. This will be called to
// create a new context for each call to the lnd client.
Expand All @@ -63,6 +66,7 @@ func NewLndChallenger(client InvoiceClient, batchSize int,
invoicesCond: sync.NewCond(invoicesMtx),
quit: make(chan struct{}),
errChan: errChan,
strictVerify: strictVerification,
}

err := challenger.Start()
Expand All @@ -78,6 +82,14 @@ func NewLndChallenger(client InvoiceClient, batchSize int,
// invoices on startup and a subscription to all subsequent invoice updates
// is created.
func (l *LndChallenger) Start() error {
// If we aren't doing strict verification, then we can just exit here as
// we don't need the invoice state.
if !l.strictVerify {
log.Infof("Skipping invoice state tracking strict_verify=%v",
l.strictVerify)
return nil
}

// These are the default values for the subscription. In case there are
// no invoices yet, this will instruct lnd to just send us all updates.
// If there are existing invoices, these indices will be updated to
Expand Down Expand Up @@ -303,6 +315,12 @@ func (l *LndChallenger) NewChallenge(price int64) (string, lntypes.Hash,
func (l *LndChallenger) VerifyInvoiceStatus(hash lntypes.Hash,
state lnrpc.Invoice_InvoiceState, timeout time.Duration) error {

// If we're not doing strict verification, we can skip this check.
if !l.strictVerify {
log.Tracef("Skipping invoice state check, pay_hash=%v", hash)
return nil
}

// Prevent the challenger to be shut down while we're still waiting for
// status updates.
l.wg.Add(1)
Expand Down
3 changes: 2 additions & 1 deletion challenger/lnd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func newChallenger() (*LndChallenger, *mockInvoiceClient, chan error) {
invoicesMtx: invoicesMtx,
invoicesCond: sync.NewCond(invoicesMtx),
errChan: mainErrChan,
strictVerify: true,
}, mockClient, mainErrChan
}

Expand All @@ -142,7 +143,7 @@ func TestLndChallenger(t *testing.T) {
// First of all, test that the NewLndChallenger doesn't allow a nil
// invoice generator function.
errChan := make(chan error)
_, err := NewLndChallenger(nil, 1, nil, nil, errChan)
_, err := NewLndChallenger(nil, 1, nil, nil, errChan, true)
require.Error(t, err)

// Now mock the lnd backend and create a challenger instance that we can
Expand Down
7 changes: 7 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var (
defaultLogLevel = "info"
defaultLogFilename = "aperture.log"
defaultInvoiceBatchSize = 100000
defaultStrictVerify = false

defaultSqliteDatabaseFileName = "aperture.db"

Expand Down Expand Up @@ -224,6 +225,11 @@ type Config struct {
// request.
InvoiceBatchSize int `long:"invoicebatchsize" description:"The number of invoices to fetch in a single request."`

// StrictVerify is a flag that indicates whether we should verify the
// invoice status strictly or not. If set to true, then this requires
// all invoices to be read from disk at start up.
StrictVerify bool `long:"strictverify" description:"Whether to verify the invoice status strictly or not."`

// Logging controls various aspects of aperture logging.
Logging *build.LogConfig `group:"logging" namespace:"logging"`

Expand Down Expand Up @@ -274,5 +280,6 @@ func NewConfig() *Config {
InvoiceBatchSize: defaultInvoiceBatchSize,
Logging: build.DefaultLogConfig(),
Blocklist: []string{},
StrictVerify: defaultStrictVerify,
}
}
45 changes: 45 additions & 0 deletions sample-conf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,42 @@ servestatic: false
# Valid options include: trace, debug, info, warn, error, critical, off.
debuglevel: "debug"

# Custom path to a config file.
configfile: "/path/to/your/aperture.yaml"

# Directory to place all of aperture's files in.
basedir: "/path/to/.aperture"

# Whether the proxy should create a valid certificate through Let's Encrypt for
# the fully qualifying domain name.
autocert: false
servername: aperture.example.com

# Whether to listen on an insecure connection, disabling TLS for incoming
# connections.
insecure: false

# Whether we should verify the invoice status strictly or not. If set to true,
# then this requires all invoices to be read from disk at start up.
strictverify: false

# The number of invoices to fetch in a single request when interacting with LND.
invoicebatchsize: 100000

# The port on which the pprof profile will be served. If no port is provided,
# the profile will not be served.
profile: 9999

# The maximum amount of time a connection may be idle before being closed.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
idletimeout: 2m

# The maximum amount of time to wait for a request to be fully read.
readtimeout: 15s

# The maximum amount of time to wait for a response to be fully written.
writetimeout: 30s

# Settings for the lnd node used to generate payment requests. All of these
# options are required.
authenticator:
Expand Down Expand Up @@ -76,6 +103,9 @@ sqlite:
# The full path to the database.
dbfile: "/path/to/.aperture/aperture.db"

# Skip applying migrations on startup.
skipmigrations: false

# Settings for the postgres instance which the proxy will use to reliably store
# and retrieve token information.
postgres:
Expand All @@ -93,6 +123,9 @@ postgres:
# server.
requireSSL: true

# Skip applying migrations on startup.
skipmigrations: false

# Settings for the etcd instance which the proxy will use to reliably store and
# retrieve token information.
etcd:
Expand Down Expand Up @@ -224,6 +257,10 @@ hashmail:
enabled: true
messagerate: 20ms
messageburstallowance: 1000

# The time after the last activity that a mailbox should be removed.
# Set to -1s to disable. Valid time units are "ns", "us", "ms", "s", "m", "h".
staletimeout: -1s # Example: 5m for 5 minutes, or -1s to disable

# Enable the prometheus metrics exporter so that a prometheus server can scrape
# the metrics.
Expand All @@ -238,6 +275,14 @@ logging:
disable: false
callsite: off
notimestamps: true

# Log level for console output.
# Valid options include: trace, debug, info, warn, error, critical, off.
level: "info"
file:
disable: false
callsite: long

# Log level for file output.
# Valid options include: trace, debug, info, warn, error, critical, off.
level: "info"
Loading