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".
This commit is contained in:
positiveblue
2023-06-30 12:06:02 -07:00
parent a04f3b1e0f
commit 4e81f5ddb5
7 changed files with 128 additions and 66 deletions

View File

@@ -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{

38
challenger/interface.go Normal file
View File

@@ -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
}

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
package aperture
package challenger
import (
"context"

26
challenger/log.go Normal file
View File

@@ -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
}

View File

@@ -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()))

View File

@@ -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
}