From 0eb263850d0e04c39757af3f09788a837c02de24 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 14 Aug 2024 11:44:47 +0200 Subject: [PATCH] multi: report blinded path receives to Mission Control in the new "blinded-paths" namespace. --- invoices/interface.go | 5 ++++ invoices/invoiceregistry.go | 42 ++++++++++++++++++++++++++++++ invoices/test_utils_test.go | 4 +++ invoices/update.go | 2 ++ routing/missioncontrol_test.go | 4 ++- server.go | 47 ++++++++++++++++++++-------------- 6 files changed, 84 insertions(+), 20 deletions(-) diff --git a/invoices/interface.go b/invoices/interface.go index f48aa37b604..5f064a837c3 100644 --- a/invoices/interface.go +++ b/invoices/interface.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/lntypes" @@ -114,6 +115,10 @@ type Payload interface { // TotalAmtMsat returns the total amount sent to the final hop, as set // by the payee. TotalAmtMsat() lnwire.MilliSatoshi + + // BlindingPoint returns the route blinding point parsed from the onion + // payload. + BlindingPoint() *btcec.PublicKey } // InvoiceQuery represents a query to the invoice database. The query allows a diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 472c55048ec..63937576966 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -8,11 +8,13 @@ import ( "sync/atomic" "time" + "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/record" + "github.com/lightningnetwork/lnd/routing/route" ) var ( @@ -74,6 +76,11 @@ type RegistryConfig struct { // KeysendHoldTime indicates for how long we want to accept and hold // spontaneous keysend payments. KeysendHoldTime time.Duration + + // ReportBlindedPathReceive can be used to indicate the successful + // receive along a chosen blinded path. + ReportBlindedPathReceive func(invoiceAddIndex uint64, + rt *models.MCRoute) error } // htlcReleaseEvent describes an htlc auto-release event. It is used to release @@ -931,6 +938,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, metadata: payload.Metadata(), pathID: payload.PathID(), totalAmtMsat: payload.TotalAmtMsat(), + blindingPoint: payload.BlindingPoint(), } switch { @@ -1024,9 +1032,13 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( var ( resolution HtlcResolution updateSubscribers bool + blindedPath *models.BlindedPathInfo + addIndex uint64 ) callback := func(inv *Invoice) (*InvoiceUpdateDesc, error) { + addIndex = inv.AddIndex + updateDesc, res, err := updateInvoice(ctx, inv) if err != nil { return nil, err @@ -1039,6 +1051,24 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( // Assign resolution to outer scope variable. resolution = res + if ctx.blindingPoint == nil { + return updateDesc, nil + } + + // If this invoice was created before we started persisting the + // blinded path info for an invoice, exit. + if inv.BlindedPaths == nil { + return updateDesc, nil + } + + path, ok := inv.BlindedPaths[route.NewVertex(ctx.blindingPoint)] + if !ok { + return nil, fmt.Errorf("expected a blinding path " + + "matching the received ephemeral key") + } + + blindedPath = path + return updateDesc, nil } @@ -1078,6 +1108,18 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( return nil, nil, err } + if blindedPath != nil { + // Regardless of what we will do with the HTLC, we want to + // report to mission control that the blinded path route it + // chose successfully reached us. + err = i.cfg.ReportBlindedPathReceive( + addIndex, blindedPath.Route, + ) + if err != nil { + return nil, nil, err + } + } + var invoiceToExpire invoiceExpiry switch res := resolution.(type) { diff --git a/invoices/test_utils_test.go b/invoices/test_utils_test.go index ed7bfccddcc..fdfa66e17f7 100644 --- a/invoices/test_utils_test.go +++ b/invoices/test_utils_test.go @@ -46,6 +46,10 @@ func (p *mockPayload) PathID() *chainhash.Hash { return p.pathID } +func (p *mockPayload) BlindingPoint() *btcec.PublicKey { + return nil +} + func (p *mockPayload) TotalAmtMsat() lnwire.MilliSatoshi { return p.totalAmtMsat } diff --git a/invoices/update.go b/invoices/update.go index d14bafee089..224e7fac819 100644 --- a/invoices/update.go +++ b/invoices/update.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/amp" "github.com/lightningnetwork/lnd/lntypes" @@ -27,6 +28,7 @@ type invoiceUpdateCtx struct { metadata []byte pathID *chainhash.Hash totalAmtMsat lnwire.MilliSatoshi + blindingPoint *btcec.PublicKey } // invoiceRef returns an identifier that can be used to lookup or update the diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go index 09cad1f227a..40b58fea7e3 100644 --- a/routing/missioncontrol_test.go +++ b/routing/missioncontrol_test.go @@ -151,7 +151,9 @@ func (ctx *mcTestContext) reportFailure(amt lnwire.MilliSatoshi, // reportSuccess reports a success by using a test route. func (ctx *mcTestContext) reportSuccess() { - err := ctx.mc.ReportPaymentSuccess(ctx.pid, mcTestRoute) + err := ctx.mc.ReportPaymentSuccess( + ctx.pid, mcTestRoute, + ) if err != nil { ctx.t.Fatal(err) } diff --git a/server.go b/server.go index 517050a4371..a285c3eb527 100644 --- a/server.go +++ b/server.go @@ -559,17 +559,6 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return nil, err } - registryConfig := invoices.RegistryConfig{ - FinalCltvRejectDelta: lncfg.DefaultFinalCltvRejectDelta, - HtlcHoldDuration: invoices.DefaultHtlcHoldDuration, - Clock: clock.NewDefaultClock(), - AcceptKeySend: cfg.AcceptKeySend, - AcceptAMP: cfg.AcceptAMP, - GcCanceledInvoicesOnStartup: cfg.GcCanceledInvoicesOnStartup, - GcCanceledInvoicesOnTheFly: cfg.GcCanceledInvoicesOnTheFly, - KeysendHoldTime: cfg.KeysendHoldTime, - } - s := &server{ cfg: cfg, graphDB: dbs.GraphDB.ChannelGraph(), @@ -628,14 +617,6 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return nil, err } - expiryWatcher := invoices.NewInvoiceExpiryWatcher( - clock.NewDefaultClock(), cfg.Invoices.HoldExpiryDelta, - uint32(currentHeight), currentHash, cc.ChainNotifier, - ) - s.invoices = invoices.NewRegistry( - dbs.InvoiceDB, expiryWatcher, ®istryConfig, - ) - s.htlcNotifier = htlcswitch.NewHtlcNotifier(time.Now) thresholdSats := btcutil.Amount(cfg.MaxFeeExposure) @@ -950,6 +931,34 @@ func newServer(cfg *Config, listenAddrs []net.Addr, "default namespace: %w", err) } + blindedPathMC, err := s.missionController.GetNamespacedStore( + "blinded-path", + ) + if err != nil { + return nil, fmt.Errorf("can't create mission control in the "+ + "blinded path namespace: %w", err) + } + + registryConfig := invoices.RegistryConfig{ + FinalCltvRejectDelta: lncfg.DefaultFinalCltvRejectDelta, + HtlcHoldDuration: invoices.DefaultHtlcHoldDuration, + Clock: clock.NewDefaultClock(), + AcceptKeySend: cfg.AcceptKeySend, + AcceptAMP: cfg.AcceptAMP, + GcCanceledInvoicesOnStartup: cfg.GcCanceledInvoicesOnStartup, + GcCanceledInvoicesOnTheFly: cfg.GcCanceledInvoicesOnTheFly, + KeysendHoldTime: cfg.KeysendHoldTime, + ReportBlindedPathReceive: blindedPathMC.ReportPaymentSuccess, + } + + expiryWatcher := invoices.NewInvoiceExpiryWatcher( + clock.NewDefaultClock(), cfg.Invoices.HoldExpiryDelta, + uint32(currentHeight), currentHash, cc.ChainNotifier, + ) + s.invoices = invoices.NewRegistry( + dbs.InvoiceDB, expiryWatcher, ®istryConfig, + ) + srvrLog.Debugf("Instantiating payment session source with config: "+ "AttemptCost=%v + %v%%, MinRouteProbability=%v", int64(routingConfig.AttemptCost),