@@ -2687,7 +2687,8 @@ func (r *rpcServer) CommitVirtualPsbts(ctx context.Context,
26872687 // Make sure the assets given fully satisfy the input commitments.
26882688 allPackets := append ([]* tappsbt.VPacket {}, activePackets ... )
26892689 allPackets = append (allPackets , passivePackets ... )
2690- err = r .validateInputAssets (ctx , pkt , allPackets )
2690+ purgedAssets := r .collectPurgedAssets (ctx , allPackets )
2691+ err = r .validateInputAssets (ctx , pkt , allPackets , purgedAssets )
26912692 if err != nil {
26922693 return nil , fmt .Errorf ("error validating input assets: %w" , err )
26932694 }
@@ -2877,10 +2878,77 @@ func (r *rpcServer) CommitVirtualPsbts(ctx context.Context,
28772878 return response , nil
28782879}
28792880
2881+ // collectPurgedAssets gathers the assets that are being purged for each input
2882+ // commitment across all virtual packets.
2883+ func (r * rpcServer ) collectPurgedAssets (ctx context.Context ,
2884+ vPackets []* tappsbt.VPacket ) map [wire.OutPoint ][]* asset.Asset {
2885+
2886+ purgedAssetsDeDup := make (map [wire.OutPoint ]map [[32 ]byte ]* asset.Asset )
2887+ for _ , vPkt := range vPackets {
2888+ for _ , vIn := range vPkt .Inputs {
2889+ inputAsset := vIn .Asset ()
2890+ outpoint := vIn .PrevID .OutPoint
2891+
2892+ input , err := r .cfg .AssetStore .FetchCommitment (
2893+ ctx , inputAsset .ID (), outpoint ,
2894+ inputAsset .GroupKey , & inputAsset .ScriptKey ,
2895+ true ,
2896+ )
2897+ if err != nil {
2898+ // If we can't fetch the input commitment, it
2899+ // means this input asset isn't ours. We cannot
2900+ // find out if there were any purged assets in
2901+ // the commitment, so we just rely on all assets
2902+ // being present. If some purged assets are
2903+ // missing, then the anchor input equality check
2904+ // further down will fail.
2905+ rpcsLog .Warnf ("Could not fetch input " +
2906+ "commitment for outpoint %v: %v" ,
2907+ outpoint , err )
2908+
2909+ continue
2910+ }
2911+
2912+ assetsToPurge := tapsend .ExtractUnSpendable (
2913+ input .Commitment ,
2914+ )
2915+ if len (assetsToPurge ) == 0 {
2916+ continue
2917+ }
2918+
2919+ assetMap := purgedAssetsDeDup [outpoint ]
2920+ if assetMap == nil {
2921+ assetMap = make (map [[32 ]byte ]* asset.Asset )
2922+ purgedAssetsDeDup [outpoint ] = assetMap
2923+ }
2924+
2925+ for _ , a := range assetsToPurge {
2926+ key := a .AssetCommitmentKey ()
2927+ assetMap [key ] = a
2928+ }
2929+ }
2930+ }
2931+
2932+ // With the assets de-duplicated by their asset commitment key, we can
2933+ // now collect them grouped by input outpoint.
2934+ purgedAssets := make (map [wire.OutPoint ][]* asset.Asset )
2935+ for outpoint , assets := range purgedAssetsDeDup {
2936+ for key := range assets {
2937+ purgedAssets [outpoint ] = append (
2938+ purgedAssets [outpoint ], assets [key ],
2939+ )
2940+ }
2941+ }
2942+
2943+ return purgedAssets
2944+ }
2945+
28802946// validateInputAssets makes sure that the input assets are correct and their
28812947// combined commitments match the inputs of the BTC level anchor transaction.
2948+ // The purgedAssets map contains the commitment leftovers per anchor input.
28822949func (r * rpcServer ) validateInputAssets (ctx context.Context ,
2883- btcPkt * psbt.Packet , vPackets []* tappsbt.VPacket ) error {
2950+ btcPkt * psbt.Packet , vPackets []* tappsbt.VPacket ,
2951+ purgedAssets map [wire.OutPoint ][]* asset.Asset ) error {
28842952
28852953 err := tapsend .ValidateVPacketVersions (vPackets )
28862954 if err != nil {
@@ -2939,65 +3007,6 @@ func (r *rpcServer) validateInputAssets(ctx context.Context,
29393007 }
29403008 }
29413009
2942- // We also want to make sure we actually have the assets that are being
2943- // spent in our database. We fetch the input commitments of all packets
2944- // to asset that. And while we're doing that, we also extract all the
2945- // pruned assets that are not re-created in the outputs, which we need
2946- // for the final validation. We de-duplicate the pruned assets in a
2947- // temporary map keyed by input outpoint and the asset commitment key.
2948- purgedAssetsDeDup := make (map [wire.OutPoint ]map [[32 ]byte ]* asset.Asset )
2949- for _ , vPkt := range vPackets {
2950- for _ , vIn := range vPkt .Inputs {
2951- inputAsset := vIn .Asset ()
2952- outpoint := vIn .PrevID .OutPoint
2953-
2954- input , err := r .cfg .AssetStore .FetchCommitment (
2955- ctx , inputAsset .ID (), outpoint ,
2956- inputAsset .GroupKey , & inputAsset .ScriptKey ,
2957- true ,
2958- )
2959- if err != nil {
2960- // If we can't fetch the input commitment, it
2961- // means this input asset isn't ours. We cannot
2962- // find out if there were any purged assets in
2963- // the commitment, so we just rely on all assets
2964- // being present. If some purged assets are
2965- // missing, then the anchor input equality check
2966- // further down will fail.
2967- rpcsLog .Warnf ("Could not fetch input " +
2968- "commitment for outpoint %v: %v" ,
2969- outpoint , err )
2970-
2971- continue
2972- }
2973-
2974- assetsToPurge := tapsend .ExtractUnSpendable (
2975- input .Commitment ,
2976- )
2977- for _ , a := range assetsToPurge {
2978- key := a .AssetCommitmentKey ()
2979- if purgedAssetsDeDup [outpoint ] == nil {
2980- purgedAssetsDeDup [outpoint ] = make (
2981- map [[32 ]byte ]* asset.Asset ,
2982- )
2983- }
2984-
2985- purgedAssetsDeDup [outpoint ][key ] = a
2986- }
2987- }
2988- }
2989-
2990- // With the assets de-duplicated by their asset commitment key, we can
2991- // now collect them grouped by input outpoint.
2992- purgedAssets := make (map [wire.OutPoint ][]* asset.Asset )
2993- for outpoint , assets := range purgedAssetsDeDup {
2994- for key := range assets {
2995- purgedAssets [outpoint ] = append (
2996- purgedAssets [outpoint ], assets [key ],
2997- )
2998- }
2999- }
3000-
30013010 // At this point all the virtual packet inputs and outputs should fully
30023011 // match the BTC level anchor transaction. Version 0 assets should also
30033012 // be signed now.
@@ -3072,7 +3081,8 @@ func (r *rpcServer) PublishAndLogTransfer(ctx context.Context,
30723081 // sure everything is in order. We start by validating the inputs.
30733082 allPackets := append ([]* tappsbt.VPacket {}, activePackets ... )
30743083 allPackets = append (allPackets , passivePackets ... )
3075- err = r .validateInputAssets (ctx , pkt , allPackets )
3084+ purgedAssets := r .collectPurgedAssets (ctx , allPackets )
3085+ err = r .validateInputAssets (ctx , pkt , allPackets , purgedAssets )
30763086 if err != nil {
30773087 return nil , fmt .Errorf ("error validating input assets: %w" , err )
30783088 }
0 commit comments