@@ -24,6 +24,7 @@ import (
24
24
"github.com/lightninglabs/taproot-assets/tappsbt"
25
25
"github.com/lightninglabs/taproot-assets/tapscript"
26
26
"github.com/lightninglabs/taproot-assets/tapsend"
27
+ "github.com/lightninglabs/taproot-assets/universe"
27
28
"github.com/lightningnetwork/lnd/chainntnfs"
28
29
"github.com/lightningnetwork/lnd/lnwallet"
29
30
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@@ -49,6 +50,15 @@ type ProofImporter interface {
49
50
replace bool , proofs ... * proof.AnnotatedProof ) error
50
51
}
51
52
53
+ // BurnSupplyCommitter is used by the chain porter to update the on-chain supply
54
+ // commitment when burns new 1st party burns are confirmed.
55
+ type BurnSupplyCommitter interface {
56
+ // SendBurnEvent sends a burn event to the supply commitment state
57
+ // machine.
58
+ SendBurnEvent (ctx context.Context , assetSpec asset.Specifier ,
59
+ burnLeaf universe.BurnLeaf ) error
60
+ }
61
+
52
62
// ChainPorterConfig is the main config for the chain porter.
53
63
type ChainPorterConfig struct {
54
64
// ChainParams are the chain parameters for the chain porter.
@@ -100,6 +110,15 @@ type ChainPorterConfig struct {
100
110
// ErrChan is the main error channel the custodian will report back
101
111
// critical errors to the main server.
102
112
ErrChan chan <- error
113
+
114
+ // BurnSupplyCommitter is used to track supply changes (burns) and
115
+ // create periodic on-chain supply commitments.
116
+ BurnCommitter BurnSupplyCommitter
117
+
118
+ // DelegationKeyChecker is used to verify that we control the delegation
119
+ // key for a given asset, which is required for creating supply
120
+ // commitments.
121
+ DelegationKeyChecker address.DelegationKeyChecker
103
122
}
104
123
105
124
// ChainPorter is the main sub-system of the tapfreighter package. The porter
@@ -677,6 +696,94 @@ func (p *ChainPorter) storeProofs(sendPkg *sendPackage) error {
677
696
return nil
678
697
}
679
698
699
+ // sendBurnSupplyCommitEvents sends supply commitment events for all burned
700
+ // assets to track them in the supply commitment state machine.
701
+ func (p * ChainPorter ) sendBurnSupplyCommitEvents (ctx context.Context ,
702
+ burns []* AssetBurn ) error {
703
+
704
+ // If no supply commit manager is configured, skip this step.
705
+ if p .cfg .BurnCommitter == nil {
706
+ return nil
707
+ }
708
+
709
+ // If no delegation key checker is configured, skip this step. We need
710
+ // it to figure out if this is an asset we created or not.
711
+ if p .cfg .DelegationKeyChecker == nil {
712
+ return nil
713
+ }
714
+
715
+ delChecker := p .cfg .DelegationKeyChecker
716
+
717
+ // We'll use a filter predicate to filter out the burns that we didn't
718
+ // do ourselves, i.e., those that don't have a delegation key.
719
+ burnsWithDelegation := fn .Filter (burns , func (burn * AssetBurn ) bool {
720
+ var assetID asset.ID
721
+ copy (assetID [:], burn .AssetID )
722
+
723
+ // If the asset doesn't have a group, then this will return
724
+ // false.
725
+ hasDelegationKey , err := delChecker .HasDelegationKey (
726
+ ctx , assetID ,
727
+ )
728
+ if err != nil {
729
+ log .Debugf ("Error checking delegation key for " +
730
+ "asset %x: %v" , assetID , err )
731
+ return false
732
+ }
733
+
734
+ if ! hasDelegationKey {
735
+ log .Debugf ("Skipping supply commit burn event " +
736
+ "for asset %x: delegation key not controlled " +
737
+ "locally" ,
738
+ assetID )
739
+ }
740
+
741
+ return hasDelegationKey
742
+ })
743
+
744
+ for _ , burn := range burnsWithDelegation {
745
+ var assetID asset.ID
746
+ copy (assetID [:], burn .AssetID )
747
+
748
+ groupKeyBytes := burn .GroupKey
749
+ groupKey , err := btcec .ParsePubKey (groupKeyBytes )
750
+ if err != nil {
751
+ return fmt .Errorf ("unable to parse group key: %w" , err )
752
+ }
753
+
754
+ assetSpec := asset .NewSpecifierOptionalGroupPubKey (
755
+ assetID , groupKey ,
756
+ )
757
+
758
+ // Create a NewBurnEvent for the supply commitment state machine.
759
+ // We need to create a universe.BurnLeaf for this.
760
+ burnLeaf := universe.BurnLeaf {
761
+ UniverseKey : universe.AssetLeafKey {
762
+ BaseLeafKey : universe.BaseLeafKey {
763
+ ScriptKey : burn .ScriptKey ,
764
+ OutPoint : burn .OutPoint ,
765
+ },
766
+ AssetID : assetID ,
767
+ },
768
+ BurnProof : burn .Proof ,
769
+ }
770
+
771
+ // Send the burn event to the supply commit manager.
772
+ err = p .cfg .BurnCommitter .SendBurnEvent (
773
+ ctx , assetSpec , burnLeaf ,
774
+ )
775
+ if err != nil {
776
+ return fmt .Errorf ("unable to send burn event for " +
777
+ "asset %x: %w" , assetID , err )
778
+ }
779
+
780
+ log .Debugf ("Sent supply commit burn event for asset %x" ,
781
+ assetID )
782
+ }
783
+
784
+ return nil
785
+ }
786
+
680
787
// storePackageAnchorTxConf logs the on-chain confirmation of the transfer
681
788
// anchor transaction for the given package.
682
789
func (p * ChainPorter ) storePackageAnchorTxConf (pkg * sendPackage ) error {
@@ -728,6 +835,12 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error {
728
835
Amount : o .Amount ,
729
836
AnchorTxid : pkg .OutboundPkg .AnchorTx .TxHash (),
730
837
Note : pkg .Note ,
838
+ ScriptKey : & o .Asset .ScriptKey ,
839
+ Proof : o .ProofSuffix ,
840
+ OutPoint : wire.OutPoint {
841
+ Hash : pkg .OutboundPkg .AnchorTx .TxHash (),
842
+ Index : o .AnchorOutputIndex ,
843
+ },
731
844
}
732
845
733
846
if o .Asset .GroupKey != nil {
@@ -739,11 +852,20 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error {
739
852
}
740
853
}
741
854
855
+ // Send supply commitment events for all burned assets before confirming
856
+ // the transaction. This ensures that supply commitments are tracked
857
+ // before the burn is considered complete.
858
+ err := p .sendBurnSupplyCommitEvents (ctx , burns )
859
+ if err != nil {
860
+ return fmt .Errorf ("unable to send burn supply commit " +
861
+ "events: %w" , err )
862
+ }
863
+
742
864
// At this point we have the confirmation signal, so we can mark the
743
865
// parcel delivery as completed in the database.
744
866
anchorTXID := pkg .OutboundPkg .AnchorTx .TxHash ()
745
867
anchorTxBlockHeight := int32 (pkg .TransferTxConfEvent .BlockHeight )
746
- err : = p .cfg .ExportLog .LogAnchorTxConfirm (ctx , & AssetConfirmEvent {
868
+ err = p .cfg .ExportLog .LogAnchorTxConfirm (ctx , & AssetConfirmEvent {
747
869
AnchorTXID : anchorTXID ,
748
870
BlockHash : * pkg .TransferTxConfEvent .BlockHash ,
749
871
BlockHeight : anchorTxBlockHeight ,
0 commit comments