Skip to content
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

[feature] Use LNC to connect to the LND node #98

Merged
merged 9 commits into from
Jul 5, 2023
40 changes: 20 additions & 20 deletions aperture.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, but in the future we can insert relevant endpoint/context information here so someone can build a UI/dashboard on top of lnd/aperture to do things like track the amount earned from a given endpoint, etc.

To do this, we'd need to expose the memo as part of the challenger interface, as only much later in the pipeline do we have the full request context information.

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.
Expand Down Expand Up @@ -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{
Expand Down
38 changes: 38 additions & 0 deletions challenger/interface.go
Original file line number Diff line number Diff line change
@@ -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
}
54 changes: 16 additions & 38 deletions challenger.go → challenger/lnd.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package aperture
package challenger

import (
"context"
Expand All @@ -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 {
Expand All @@ -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.
Expand All @@ -84,7 +53,7 @@ func NewLndChallenger(client InvoiceClient,
}

invoicesMtx := &sync.Mutex{}
return &LndChallenger{
challenger := &LndChallenger{
client: client,
clientCtx: ctxFunc,
genInvoiceReq: genInvoiceReq,
Expand All @@ -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
Expand Down Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re the above comment, we could pass in the additional context here as an optional argument. Then the invoice creation has a chance to tag any extra details.

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)
Expand Down
2 changes: 1 addition & 1 deletion challenger_test.go → challenger/lnd_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package aperture
package challenger

import (
"context"
Expand Down
26 changes: 26 additions & 0 deletions challenger/log.go
Original file line number Diff line number Diff line change
@@ -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
}
22 changes: 16 additions & 6 deletions mint/mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,24 @@ 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
// are required for proper verification of each minted LSAT.
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
Expand All @@ -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
Expand Down Expand Up @@ -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()))
Expand Down
12 changes: 11 additions & 1 deletion mint/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down