From 4e81f5ddb52c13bef78afa40e0c94277d51f7c90 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Fri, 30 Jun 2023 12:06:02 -0700 Subject: [PATCH] challenger: move challenger logic to its own package The new package contains a new interface (`Challenger`) that any new challenger must implement. Because aperture uses the new interface instead of using directly the `LndChallenger` struct I added the `Stop()` method to the `mint.Challenger`. Instead of also adding the `Start()` method the constructor returns a Challenger already "started". --- aperture.go | 40 +++++++-------- challenger/interface.go | 38 ++++++++++++++ challenger.go => challenger/lnd.go | 54 ++++++-------------- challenger_test.go => challenger/lnd_test.go | 2 +- challenger/log.go | 26 ++++++++++ mint/mint.go | 22 +++++--- mint/mock_test.go | 12 ++++- 7 files changed, 128 insertions(+), 66 deletions(-) create mode 100644 challenger/interface.go rename challenger.go => challenger/lnd.go (87%) rename challenger_test.go => challenger/lnd_test.go (99%) create mode 100644 challenger/log.go diff --git a/aperture.go b/aperture.go index 24c6b90..7e84b7e 100644 --- a/aperture.go +++ b/aperture.go @@ -21,6 +21,7 @@ import ( flags "github.com/jessevdk/go-flags" "github.com/lightninglabs/aperture/aperturedb" "github.com/lightninglabs/aperture/auth" + "github.com/lightninglabs/aperture/challenger" "github.com/lightninglabs/aperture/mint" "github.com/lightninglabs/aperture/proxy" "github.com/lightninglabs/lightning-node-connect/hashmailrpc" @@ -76,6 +77,10 @@ const ( // hashMailRESTPrefix is the prefix a REST request URI has when it is // meant for the hashmailrpc server to be handled. hashMailRESTPrefix = "/v1/lightning-node-connect/hashmail" + + // invoiceMacaroonName is the name of the invoice macaroon belonging + // to the target lnd node. + invoiceMacaroonName = "invoice.macaroon" ) var ( @@ -163,7 +168,7 @@ type Aperture struct { etcdClient *clientv3.Client db *sql.DB - challenger *LndChallenger + challenger challenger.Challenger httpsServer *http.Server torHTTPServer *http.Server proxy *proxy.Proxy @@ -284,37 +289,32 @@ func (a *Aperture) Start(errChan chan error) error { log.Infof("Using %v as database backend", a.cfg.DatabaseBackend) - // Create our challenger that uses our backing lnd node to create - // invoices and check their settlement status. - genInvoiceReq := func(price int64) (*lnrpc.Invoice, error) { - return &lnrpc.Invoice{ - Memo: "LSAT", - Value: price, - }, nil - } - if !a.cfg.Authenticator.Disable { + genInvoiceReq := func(price int64) (*lnrpc.Invoice, error) { + return &lnrpc.Invoice{ + Memo: "LSAT", + Value: price, + }, nil + } + authCfg := a.cfg.Authenticator client, err := lndclient.NewBasicClient( - authCfg.LndHost, authCfg.TLSPath, authCfg.MacDir, - authCfg.Network, lndclient.MacFilename( + authCfg.LndHost, authCfg.TLSPath, + authCfg.MacDir, authCfg.Network, + lndclient.MacFilename( invoiceMacaroonName, ), ) if err != nil { return err } - - a.challenger, err = NewLndChallenger( - client, genInvoiceReq, context.Background, errChan, + a.challenger, err = challenger.NewLndChallenger( + client, genInvoiceReq, context.Background, + errChan, ) if err != nil { return err } - err = a.challenger.Start() - if err != nil { - return err - } } // Create the proxy and connect it to lnd. @@ -750,7 +750,7 @@ func initTorListener(cfg *Config, store tor.OnionStore) (*tor.Controller, } // createProxy creates the proxy with all the services it needs. -func createProxy(cfg *Config, challenger *LndChallenger, +func createProxy(cfg *Config, challenger challenger.Challenger, store mint.SecretStore) (*proxy.Proxy, func(), error) { minter := mint.New(&mint.Config{ diff --git a/challenger/interface.go b/challenger/interface.go new file mode 100644 index 0000000..207a2c1 --- /dev/null +++ b/challenger/interface.go @@ -0,0 +1,38 @@ +package challenger + +import ( + "context" + + "github.com/lightninglabs/aperture/auth" + "github.com/lightninglabs/aperture/mint" + "github.com/lightningnetwork/lnd/lnrpc" + "google.golang.org/grpc" +) + +// InvoiceRequestGenerator is a function type that returns a new request for the +// lnrpc.AddInvoice call. +type InvoiceRequestGenerator func(price int64) (*lnrpc.Invoice, error) + +// InvoiceClient is an interface that only implements part of a full lnd client, +// namely the part around the invoices we need for the challenger to work. +type InvoiceClient interface { + // ListInvoices returns a paginated list of all invoices known to lnd. + ListInvoices(ctx context.Context, in *lnrpc.ListInvoiceRequest, + opts ...grpc.CallOption) (*lnrpc.ListInvoiceResponse, error) + + // SubscribeInvoices subscribes to updates on invoices. + SubscribeInvoices(ctx context.Context, in *lnrpc.InvoiceSubscription, + opts ...grpc.CallOption) ( + lnrpc.Lightning_SubscribeInvoicesClient, error) + + // AddInvoice adds a new invoice to lnd. + AddInvoice(ctx context.Context, in *lnrpc.Invoice, + opts ...grpc.CallOption) (*lnrpc.AddInvoiceResponse, error) +} + +// Challenger is an interface that combines the mint.Challenger and the +// auth.InvoiceChecker interfaces. +type Challenger interface { + mint.Challenger + auth.InvoiceChecker +} diff --git a/challenger.go b/challenger/lnd.go similarity index 87% rename from challenger.go rename to challenger/lnd.go index 0de1131..9e10a02 100644 --- a/challenger.go +++ b/challenger/lnd.go @@ -1,4 +1,4 @@ -package aperture +package challenger import ( "context" @@ -9,34 +9,10 @@ import ( "sync" "time" - "github.com/lightninglabs/aperture/auth" - "github.com/lightninglabs/aperture/mint" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" - "google.golang.org/grpc" ) -// InvoiceRequestGenerator is a function type that returns a new request for the -// lnrpc.AddInvoice call. -type InvoiceRequestGenerator func(price int64) (*lnrpc.Invoice, error) - -// InvoiceClient is an interface that only implements part of a full lnd client, -// namely the part around the invoices we need for the challenger to work. -type InvoiceClient interface { - // ListInvoices returns a paginated list of all invoices known to lnd. - ListInvoices(ctx context.Context, in *lnrpc.ListInvoiceRequest, - opts ...grpc.CallOption) (*lnrpc.ListInvoiceResponse, error) - - // SubscribeInvoices subscribes to updates on invoices. - SubscribeInvoices(ctx context.Context, in *lnrpc.InvoiceSubscription, - opts ...grpc.CallOption) ( - lnrpc.Lightning_SubscribeInvoicesClient, error) - - // AddInvoice adds a new invoice to lnd. - AddInvoice(ctx context.Context, in *lnrpc.Invoice, - opts ...grpc.CallOption) (*lnrpc.AddInvoiceResponse, error) -} - // LndChallenger is a challenger that uses an lnd backend to create new LSAT // payment challenges. type LndChallenger struct { @@ -55,16 +31,9 @@ type LndChallenger struct { wg sync.WaitGroup } -// A compile time flag to ensure the LndChallenger satisfies the -// mint.Challenger and auth.InvoiceChecker interface. -var _ mint.Challenger = (*LndChallenger)(nil) -var _ auth.InvoiceChecker = (*LndChallenger)(nil) - -const ( - // invoiceMacaroonName is the name of the invoice macaroon belonging - // to the target lnd node. - invoiceMacaroonName = "invoice.macaroon" -) +// A compile time flag to ensure the LndChallenger satisfies the Challenger +// interface. +var _ Challenger = (*LndChallenger)(nil) // NewLndChallenger creates a new challenger that uses the given connection to // an lnd backend to create payment challenges. @@ -84,7 +53,7 @@ func NewLndChallenger(client InvoiceClient, } invoicesMtx := &sync.Mutex{} - return &LndChallenger{ + challenger := &LndChallenger{ client: client, clientCtx: ctxFunc, genInvoiceReq: genInvoiceReq, @@ -93,7 +62,14 @@ func NewLndChallenger(client InvoiceClient, invoicesCond: sync.NewCond(invoicesMtx), quit: make(chan struct{}), errChan: errChan, - }, nil + } + + err := challenger.Start() + if err != nil { + return nil, fmt.Errorf("unable to start challenger: %w", err) + } + + return challenger, nil } // Start starts the challenger's main work which is to keep track of all @@ -263,7 +239,9 @@ func (l *LndChallenger) Stop() { // request (invoice) and the corresponding payment hash. // // NOTE: This is part of the mint.Challenger interface. -func (l *LndChallenger) NewChallenge(price int64) (string, lntypes.Hash, error) { +func (l *LndChallenger) NewChallenge(price int64) (string, lntypes.Hash, + error) { + // Obtain a new invoice from lnd first. We need to know the payment hash // so we can add it as a caveat to the macaroon. invoice, err := l.genInvoiceReq(price) diff --git a/challenger_test.go b/challenger/lnd_test.go similarity index 99% rename from challenger_test.go rename to challenger/lnd_test.go index f7ab606..82f7620 100644 --- a/challenger_test.go +++ b/challenger/lnd_test.go @@ -1,4 +1,4 @@ -package aperture +package challenger import ( "context" diff --git a/challenger/log.go b/challenger/log.go new file mode 100644 index 0000000..99033f0 --- /dev/null +++ b/challenger/log.go @@ -0,0 +1,26 @@ +package challenger + +import ( + "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/build" +) + +// Subsystem defines the sub system name of this package. +const Subsystem = "CHLL" + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger(Subsystem, nil)) +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/mint/mint.go b/mint/mint.go index 21b5dae..41bad66 100644 --- a/mint/mint.go +++ b/mint/mint.go @@ -29,6 +29,9 @@ type Challenger interface { // to avoid having to decode the payment request in order to retrieve // its payment hash. NewChallenge(price int64) (string, lntypes.Hash, error) + + // Stop shuts down the challenger. + Stop() } // SecretStore is the store responsible for storing LSAT secrets. These secrets @@ -36,12 +39,14 @@ type Challenger interface { type SecretStore interface { // NewSecret creates a new cryptographically random secret which is // keyed by the given hash. - NewSecret(context.Context, [sha256.Size]byte) ([lsat.SecretSize]byte, error) + NewSecret(context.Context, [sha256.Size]byte) ([lsat.SecretSize]byte, + error) // GetSecret returns the cryptographically random secret that // corresponds to the given hash. If there is no secret, then // ErrSecretNotFound is returned. - GetSecret(context.Context, [sha256.Size]byte) ([lsat.SecretSize]byte, error) + GetSecret(context.Context, [sha256.Size]byte) ([lsat.SecretSize]byte, + error) // RevokeSecret removes the cryptographically random secret that // corresponds to the given hash. This acts as a NOP if the secret does @@ -55,16 +60,19 @@ type ServiceLimiter interface { // ServiceCapabilities returns the capabilities caveats for each // service. This determines which capabilities of each service can be // accessed. - ServiceCapabilities(context.Context, ...lsat.Service) ([]lsat.Caveat, error) + ServiceCapabilities(context.Context, ...lsat.Service) ([]lsat.Caveat, + error) // ServiceConstraints returns the constraints for each service. This // enforces additional constraints on a particular service/service // capability. - ServiceConstraints(context.Context, ...lsat.Service) ([]lsat.Caveat, error) + ServiceConstraints(context.Context, ...lsat.Service) ([]lsat.Caveat, + error) // ServiceTimeouts returns the timeout caveat for each service. This // will determine if and when service access can expire. - ServiceTimeouts(context.Context, ...lsat.Service) ([]lsat.Caveat, error) + ServiceTimeouts(context.Context, ...lsat.Service) ([]lsat.Caveat, + error) } // Config packages all of the required dependencies to instantiate a new LSAT @@ -245,7 +253,9 @@ type VerificationParams struct { } // VerifyLSAT attempts to verify an LSAT with the given parameters. -func (m *Mint) VerifyLSAT(ctx context.Context, params *VerificationParams) error { +func (m *Mint) VerifyLSAT(ctx context.Context, + params *VerificationParams) error { + // We'll first perform a quick check to determine if a valid preimage // was provided. id, err := lsat.DecodeIdentifier(bytes.NewReader(params.Macaroon.Id())) diff --git a/mint/mock_test.go b/mint/mock_test.go index 6156540..e2a0b26 100644 --- a/mint/mock_test.go +++ b/mint/mock_test.go @@ -26,7 +26,17 @@ func newMockChallenger() *mockChallenger { return &mockChallenger{} } -func (d *mockChallenger) NewChallenge(price int64) (string, lntypes.Hash, error) { +func (d *mockChallenger) Start() error { + return nil +} + +func (d *mockChallenger) Stop() { + // Nothing to do here. +} + +func (d *mockChallenger) NewChallenge(price int64) (string, lntypes.Hash, + error) { + return testPayReq, testHash, nil }