@@ -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"
@@ -42,6 +43,15 @@ type ProofImporter interface {
42
43
replace bool , proofs ... * proof.AnnotatedProof ) error
43
44
}
44
45
46
+ // BurnSupplyCommitter is used by the chain porter to update the on-chain supply
47
+ // commitment when burns new 1st party burns are confirmed.
48
+ type BurnSupplyCommitter interface {
49
+ // SendBurnEvent sends a burn event to the supply commitment state
50
+ // machine.
51
+ SendBurnEvent (ctx context.Context , assetSpec asset.Specifier ,
52
+ burnLeaf universe.BurnLeaf ) error
53
+ }
54
+
45
55
// ChainPorterConfig is the main config for the chain porter.
46
56
type ChainPorterConfig struct {
47
57
// Signer implements the Taproot Asset level signing we need to sign a
@@ -90,6 +100,15 @@ type ChainPorterConfig struct {
90
100
// ErrChan is the main error channel the custodian will report back
91
101
// critical errors to the main server.
92
102
ErrChan chan <- error
103
+
104
+ // BurnSupplyCommitter is used to track supply changes (burns) and
105
+ // create periodic on-chain supply commitments.
106
+ BurnCommitter BurnSupplyCommitter
107
+
108
+ // DelegationKeyChecker is used to verify that we control the delegation
109
+ // key for a given asset, which is required for creating supply
110
+ // commitments.
111
+ DelegationKeyChecker address.DelegationKeyChecker
93
112
}
94
113
95
114
// ChainPorter is the main sub-system of the tapfreighter package. The porter
@@ -680,11 +699,20 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error {
680
699
}
681
700
}
682
701
702
+ // Send supply commitment events for all burned assets before
703
+ // confirming the transaction. This ensures that supply commitments
704
+ // are tracked before the burn is considered complete.
705
+ err := p .sendBurnSupplyCommitEvents (ctx , burns )
706
+ if err != nil {
707
+ return fmt .Errorf ("unable to send burn supply commit " +
708
+ "events: %w" , err )
709
+ }
710
+
683
711
// At this point we have the confirmation signal, so we can mark the
684
712
// parcel delivery as completed in the database.
685
713
anchorTXID := pkg .OutboundPkg .AnchorTx .TxHash ()
686
714
anchorTxBlockHeight := int32 (pkg .TransferTxConfEvent .BlockHeight )
687
- err : = p .cfg .ExportLog .LogAnchorTxConfirm (ctx , & AssetConfirmEvent {
715
+ err = p .cfg .ExportLog .LogAnchorTxConfirm (ctx , & AssetConfirmEvent {
688
716
AnchorTXID : anchorTXID ,
689
717
BlockHash : * pkg .TransferTxConfEvent .BlockHash ,
690
718
BlockHeight : anchorTxBlockHeight ,
@@ -1904,3 +1932,94 @@ func newAssetSendErrorEvent(err error, executedState SendState,
1904
1932
Transfer : pkg .OutboundPkg ,
1905
1933
}
1906
1934
}
1935
+
1936
+ // sendBurnSupplyCommitEvents sends supply commitment events for all burned
1937
+ // assets to track them in the supply commitment state machine.
1938
+ func (p * ChainPorter ) sendBurnSupplyCommitEvents (ctx context.Context ,
1939
+ burns []* AssetBurn ) error {
1940
+
1941
+ // If no supply commit manager is configured, skip this step.
1942
+ if p .cfg .BurnCommitter == nil {
1943
+ return nil
1944
+ }
1945
+
1946
+ // If no delegation key checker is configured, skip this step.
1947
+ if p .cfg .DelegationKeyChecker == nil {
1948
+ return nil
1949
+ }
1950
+
1951
+ // Filter out burns where we don't control the delegation key.
1952
+ burnsWithDelegation := fn .Filter (burns , func (burn * AssetBurn ) bool {
1953
+ var assetID asset.ID
1954
+ copy (assetID [:], burn .AssetID )
1955
+
1956
+ hasDelegationKey , err := p .cfg .DelegationKeyChecker .HasDelegationKey (
1957
+ ctx , assetID ,
1958
+ )
1959
+ if err != nil {
1960
+ log .Debugf ("Error checking delegation key for asset %x: %v" ,
1961
+ assetID , err )
1962
+ return false
1963
+ }
1964
+ if ! hasDelegationKey {
1965
+ log .Debugf ("Skipping supply commit burn event for asset %x: " +
1966
+ "delegation key not controlled locally" ,
1967
+ assetID )
1968
+ }
1969
+ return hasDelegationKey
1970
+ })
1971
+
1972
+ for _ , burn := range burnsWithDelegation {
1973
+ // Create the asset specifier for this burn.
1974
+ var assetID asset.ID
1975
+ copy (assetID [:], burn .AssetID )
1976
+
1977
+ var assetSpec asset.Specifier
1978
+ if burn .GroupKey != nil {
1979
+ // Parse the group key from the serialized bytes.
1980
+ groupKeyBytes := burn .GroupKey
1981
+ groupKey , err := btcec .ParsePubKey (groupKeyBytes )
1982
+ if err != nil {
1983
+ return fmt .Errorf ("unable to parse group key: %w" , err )
1984
+ }
1985
+
1986
+ assetSpec = asset .NewSpecifierOptionalGroupPubKey (
1987
+ assetID , groupKey ,
1988
+ )
1989
+ } else {
1990
+ assetSpec = asset .NewSpecifierFromId (assetID )
1991
+ }
1992
+
1993
+ // Create a NewBurnEvent for the supply commitment state machine.
1994
+ // We need to create a universe.BurnLeaf for this.
1995
+ burnLeaf := universe.BurnLeaf {
1996
+ UniverseKey : universe.AssetLeafKey {
1997
+ BaseLeafKey : universe.BaseLeafKey {
1998
+ OutPoint : wire.OutPoint {
1999
+ Hash : burn .AnchorTxid ,
2000
+ Index : 0 , // Burn outputs are typically at index 0
2001
+ },
2002
+ ScriptKey : nil , // Will be populated from burn proof if available
2003
+ },
2004
+ AssetID : assetID ,
2005
+ },
2006
+ // Note: In a full implementation, we would fetch the burn proof
2007
+ // from the asset and populate the BurnProof field.
2008
+ BurnProof : nil ,
2009
+ }
2010
+
2011
+ // Send the burn event to the supply commit manager.
2012
+ err := p .cfg .BurnCommitter .SendBurnEvent (
2013
+ ctx , assetSpec , burnLeaf ,
2014
+ )
2015
+ if err != nil {
2016
+ return fmt .Errorf ("unable to send burn event for " +
2017
+ "asset %x: %w" , assetID , err )
2018
+ }
2019
+
2020
+ log .Debugf ("Sent supply commit burn event for asset %x" ,
2021
+ assetID )
2022
+ }
2023
+
2024
+ return nil
2025
+ }
0 commit comments