diff --git a/lnrpc/invoicesrpc/config_active.go b/lnrpc/invoicesrpc/config_active.go index b781be875..9568ed891 100644 --- a/lnrpc/invoicesrpc/config_active.go +++ b/lnrpc/invoicesrpc/config_active.go @@ -10,6 +10,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/netann" + "google.golang.org/protobuf/proto" ) // Config is the primary configuration struct for the invoices RPC server. It @@ -69,4 +70,8 @@ type Config struct { // GetAlias returns the peer's alias SCID if it exists given the // 32-byte ChannelID. GetAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error) + + // ParseAuxData is a function that can be used to parse the auxiliary + // data from the invoice. + ParseAuxData func(message proto.Message) error } diff --git a/lnrpc/invoicesrpc/invoices.swagger.json b/lnrpc/invoicesrpc/invoices.swagger.json index a51b102fb..4e8cb6ac5 100644 --- a/lnrpc/invoicesrpc/invoices.swagger.json +++ b/lnrpc/invoicesrpc/invoices.swagger.json @@ -784,6 +784,11 @@ "amp": { "$ref": "#/definitions/lnrpcAMP", "description": "Details relevant to AMP HTLCs, only populated if this is an AMP HTLC." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } }, "title": "Details of an HTLC that paid to an invoice" diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go index d18ab09d2..8ca02b260 100644 --- a/lnrpc/invoicesrpc/invoices_server.go +++ b/lnrpc/invoicesrpc/invoices_server.go @@ -222,8 +222,9 @@ func (r *ServerShell) RegisterWithRestServer(ctx context.Context, // methods routed towards it. // // NOTE: This is part of the lnrpc.GrpcHandler interface. -func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { +func (r *ServerShell) CreateSubServer( + configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, + lnrpc.MacaroonPerms, error) { subServer, macPermissions, err := createNewSubServer(configRegistry) if err != nil { @@ -264,6 +265,14 @@ func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest, return err } + // Give the aux data parser a chance to format the + // custom data in the invoice HTLCs. + err = s.cfg.ParseAuxData(rpcInvoice) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + if err := updateStream.Send(rpcInvoice); err != nil { return err } @@ -451,7 +460,19 @@ func (s *Server) LookupInvoiceV2(ctx context.Context, return nil, err } - return CreateRPCInvoice(&invoice, s.cfg.ChainParams) + rpcInvoice, err := CreateRPCInvoice(&invoice, s.cfg.ChainParams) + if err != nil { + return nil, err + } + + // Give the aux data parser a chance to format the custom data in the + // invoice HTLCs. + err = s.cfg.ParseAuxData(rpcInvoice) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", err) + } + + return rpcInvoice, nil } // HtlcModifier is a bidirectional streaming RPC that allows a client to diff --git a/lnrpc/invoicesrpc/utils.go b/lnrpc/invoicesrpc/utils.go index ce02769fb..955ba6acf 100644 --- a/lnrpc/invoicesrpc/utils.go +++ b/lnrpc/invoicesrpc/utils.go @@ -123,6 +123,16 @@ func CreateRPCInvoice(invoice *invoices.Invoice, MppTotalAmtMsat: uint64(htlc.MppTotalAmt), } + // The custom channel data is currently just the raw bytes of + // the encoded custom records. + customData, err := lnwire.CustomRecords( + htlc.CustomRecords, + ).Serialize() + if err != nil { + return nil, err + } + rpcHtlc.CustomChannelData = customData + // Populate any fields relevant to AMP payments. if htlc.AMP != nil { rootShare := htlc.AMP.Record.RootShare() diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 26ab31dd9..435e56320 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -12943,6 +12943,8 @@ type InvoiceHTLC struct { MppTotalAmtMsat uint64 `protobuf:"varint,10,opt,name=mpp_total_amt_msat,json=mppTotalAmtMsat,proto3" json:"mpp_total_amt_msat,omitempty"` // Details relevant to AMP HTLCs, only populated if this is an AMP HTLC. Amp *AMP `protobuf:"bytes,11,opt,name=amp,proto3" json:"amp,omitempty"` + // Custom channel data that might be populated in custom channels. + CustomChannelData []byte `protobuf:"bytes,12,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` } func (x *InvoiceHTLC) Reset() { @@ -13054,6 +13056,13 @@ func (x *InvoiceHTLC) GetAmp() *AMP { return nil } +func (x *InvoiceHTLC) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + // Details specific to AMP HTLCs. type AMP struct { state protoimpl.MessageState @@ -20199,7 +20208,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x0a, 0x12, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x22, 0xfc, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, + 0x74, 0x68, 0x73, 0x22, 0xac, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, @@ -20227,6 +20236,9 @@ var file_lightning_proto_rawDesc = []byte{ 0x04, 0x52, 0x0f, 0x6d, 0x70, 0x70, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x52, 0x03, 0x61, 0x6d, 0x70, + 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index b44b04caa..f8cfcaca6 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -3952,6 +3952,11 @@ message InvoiceHTLC { // Details relevant to AMP HTLCs, only populated if this is an AMP HTLC. AMP amp = 11; + + /* + Custom channel data that might be populated in custom channels. + */ + bytes custom_channel_data = 12; } // Details specific to AMP HTLCs. diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index 0e69fe1fd..9e2d2d25b 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -5602,6 +5602,11 @@ "amp": { "$ref": "#/definitions/lnrpcAMP", "description": "Details relevant to AMP HTLCs, only populated if this is an AMP HTLC." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } }, "title": "Details of an HTLC that paid to an invoice" diff --git a/rpcserver.go b/rpcserver.go index ef9d89312..6404845f6 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -788,7 +788,8 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, s.sweeper, tower, s.towerClientMgr, r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, genAmpInvoiceFeatures, s.getNodeAnnouncement, s.updateAndBrodcastSelfNode, parseAddr, - rpcsLog, s.aliasMgr, invoiceHtlcModifier, + rpcsLog, s.aliasMgr, r.implCfg.AuxDataParser, + invoiceHtlcModifier, ) if err != nil { return err @@ -6124,6 +6125,19 @@ func (r *rpcServer) LookupInvoice(ctx context.Context, return nil, err } + // Give the aux data parser a chance to format the custom data in the + // invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData(rpcInvoice) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", + err) + } + return rpcInvoice, nil } @@ -6179,6 +6193,21 @@ func (r *rpcServer) ListInvoices(ctx context.Context, if err != nil { return nil, err } + + // Give the aux data parser a chance to format the custom data + // in the invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + resp.Invoices[i], + ) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", + err) + } } return resp, nil @@ -6207,6 +6236,21 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, return err } + // Give the aux data parser a chance to format the + // custom data in the invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + rpcInvoice, + ) + }, + ) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + if err := updateStream.Send(rpcInvoice); err != nil { return err } @@ -6219,6 +6263,21 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, return err } + // Give the aux data parser a chance to format the + // custom data in the invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + rpcInvoice, + ) + }, + ) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + if err := updateStream.Send(rpcInvoice); err != nil { return err } diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 6f4c81336..5328c4919 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -11,6 +11,7 @@ import ( "github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lncfg" @@ -32,6 +33,7 @@ import ( "github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower/wtclient" + "google.golang.org/protobuf/proto" ) // subRPCServerConfigs is special sub-config in the main configuration that @@ -123,6 +125,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, modifiers ...netann.NodeAnnModifier) error, parseAddr func(addr string) (net.Addr, error), rpcLogger btclog.Logger, aliasMgr *aliasmgr.Manager, + auxDataParser fn.Option[AuxDataParser], invoiceHtlcModifier *invoices.HtlcModificationInterceptor) error { // First, we'll use reflect to obtain a version of the config struct @@ -271,6 +274,20 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, reflect.ValueOf(aliasMgr.GetPeerAlias), ) + parseAuxData := func(m proto.Message) error { + return fn.MapOptionZ( + auxDataParser, + func(p AuxDataParser) error { + return p.InlineParseCustomData( + m, + ) + }, + ) + } + subCfgValue.FieldByName("ParseAuxData").Set( + reflect.ValueOf(parseAuxData), + ) + case *neutrinorpc.Config: subCfgValue := extractReflectValue(subCfg)