@@ -2,6 +2,7 @@ package tapchannel
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
67 "sync"
78
@@ -13,6 +14,7 @@ import (
1314 "github.com/lightninglabs/taproot-assets/rfqmath"
1415 "github.com/lightninglabs/taproot-assets/rfqmsg"
1516 "github.com/lightninglabs/taproot-assets/taprpc"
17+ "github.com/lightningnetwork/lnd/invoices"
1618 "github.com/lightningnetwork/lnd/lnrpc"
1719 "github.com/lightningnetwork/lnd/lnwire"
1820 "github.com/lightningnetwork/lnd/routing/route"
@@ -77,6 +79,10 @@ type InvoiceManagerConfig struct {
7779 // accepted quotes for determining the incoming value of invoice related
7880 // HTLCs.
7981 RfqManager RfqManager
82+
83+ // LndClient is the lnd client that will be used to interact with the
84+ // lnd node.
85+ LightningClient lndclient.LightningClient
8086}
8187
8288// AuxInvoiceManager is a Taproot Asset auxiliary invoice manager that can be
@@ -206,7 +212,7 @@ func (s *AuxInvoiceManager) handleInvoiceAccept(ctx context.Context,
206212 }
207213
208214 // We now run some validation checks on the asset HTLC.
209- err = s .validateAssetHTLC (ctx , htlc )
215+ err = s .validateAssetHTLC (ctx , htlc , resp . CircuitKey )
210216 if err != nil {
211217 log .Errorf ("Failed to validate asset HTLC: %v" , err )
212218
@@ -390,7 +396,7 @@ func isAssetInvoice(invoice *lnrpc.Invoice, rfqLookup RfqLookup) bool {
390396
391397// validateAssetHTLC runs a couple of checks on the provided asset HTLC.
392398func (s * AuxInvoiceManager ) validateAssetHTLC (ctx context.Context ,
393- htlc * rfqmsg.Htlc ) error {
399+ htlc * rfqmsg.Htlc , circuitKey invoices. CircuitKey ) error {
394400
395401 rfqID := htlc .RfqID .ValOpt ().UnsafeFromSome ()
396402
@@ -403,6 +409,7 @@ func (s *AuxInvoiceManager) validateAssetHTLC(ctx context.Context,
403409
404410 // Check for each of the asset balances of the HTLC that the identifier
405411 // matches that of the RFQ quote.
412+ assetIDs := fn .NewSet [asset.ID ]()
406413 for _ , v := range htlc .Balances () {
407414 match , err := s .cfg .RfqManager .AssetMatchesSpecifier (
408415 ctx , identifier , v .AssetID .Val ,
@@ -415,6 +422,47 @@ func (s *AuxInvoiceManager) validateAssetHTLC(ctx context.Context,
415422 return fmt .Errorf ("asset ID %s does not match %s" ,
416423 v .AssetID .Val .String (), identifier .String ())
417424 }
425+
426+ assetIDs .Add (v .AssetID .Val )
427+ }
428+
429+ // We also need to validate that the HTLC is actually the correct asset
430+ // and arrived through the correct asset channel.
431+ channels , err := s .cfg .LightningClient .ListChannels (ctx , true , false )
432+ if err != nil {
433+ return fmt .Errorf ("unable to list channels: %w" , err )
434+ }
435+
436+ var inboundChannel * lndclient.ChannelInfo
437+ for _ , channel := range channels {
438+ if channel .ChannelID == circuitKey .ChanID .ToUint64 () {
439+ inboundChannel = & channel
440+ break
441+ }
442+ }
443+
444+ if inboundChannel == nil {
445+ return fmt .Errorf ("unable to find channel with short channel " +
446+ "ID %d" , circuitKey .ChanID .ToUint64 ())
447+ }
448+
449+ if len (inboundChannel .CustomChannelData ) == 0 {
450+ return fmt .Errorf ("channel %d does not have custom channel " +
451+ "data, can't accept asset HTLC over non-asset channel" ,
452+ inboundChannel .ChannelID )
453+ }
454+
455+ var assetData rfqmsg.JsonAssetChannel
456+ err = json .Unmarshal (inboundChannel .CustomChannelData , & assetData )
457+ if err != nil {
458+ return fmt .Errorf ("unable to unmarshal channel asset data: %w" ,
459+ err )
460+ }
461+
462+ if ! assetData .HasAllAssetIDs (assetIDs ) {
463+ return fmt .Errorf ("channel %d does not have all asset IDs " +
464+ "required for HTLC settlement" ,
465+ inboundChannel .ChannelID )
418466 }
419467
420468 return nil
0 commit comments