Skip to content

Commit ab74541

Browse files
committed
tapfreighter: add delegation key filtering for burn supply commits
This commit extends the ChainPorterConfig with DelegationKeyChecker and BurnSupplyCommitter dependencies to enable supply commitment tracking for asset burns. When processing burn events, the chain porter now filters assets to only submit supply commitment events for those where we control the delegation key. The sendBurnSupplyCommitEvents method uses functional filtering to pre-process the burn list, ensuring we only attempt to create supply commitments for assets we're authorized to manage. This prevents unnecessary processing and potential errors for assets where we lack delegation authority.
1 parent 0e00aaf commit ab74541

File tree

1 file changed

+120
-1
lines changed

1 file changed

+120
-1
lines changed

tapfreighter/chain_porter.go

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/lightninglabs/taproot-assets/tappsbt"
2525
"github.com/lightninglabs/taproot-assets/tapscript"
2626
"github.com/lightninglabs/taproot-assets/tapsend"
27+
"github.com/lightninglabs/taproot-assets/universe"
2728
"github.com/lightningnetwork/lnd/chainntnfs"
2829
"github.com/lightningnetwork/lnd/lnwallet"
2930
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@@ -42,6 +43,15 @@ type ProofImporter interface {
4243
replace bool, proofs ...*proof.AnnotatedProof) error
4344
}
4445

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+
4555
// ChainPorterConfig is the main config for the chain porter.
4656
type ChainPorterConfig struct {
4757
// Signer implements the Taproot Asset level signing we need to sign a
@@ -90,6 +100,15 @@ type ChainPorterConfig struct {
90100
// ErrChan is the main error channel the custodian will report back
91101
// critical errors to the main server.
92102
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
93112
}
94113

95114
// ChainPorter is the main sub-system of the tapfreighter package. The porter
@@ -680,11 +699,20 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error {
680699
}
681700
}
682701

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+
683711
// At this point we have the confirmation signal, so we can mark the
684712
// parcel delivery as completed in the database.
685713
anchorTXID := pkg.OutboundPkg.AnchorTx.TxHash()
686714
anchorTxBlockHeight := int32(pkg.TransferTxConfEvent.BlockHeight)
687-
err := p.cfg.ExportLog.LogAnchorTxConfirm(ctx, &AssetConfirmEvent{
715+
err = p.cfg.ExportLog.LogAnchorTxConfirm(ctx, &AssetConfirmEvent{
688716
AnchorTXID: anchorTXID,
689717
BlockHash: *pkg.TransferTxConfEvent.BlockHash,
690718
BlockHeight: anchorTxBlockHeight,
@@ -1904,3 +1932,94 @@ func newAssetSendErrorEvent(err error, executedState SendState,
19041932
Transfer: pkg.OutboundPkg,
19051933
}
19061934
}
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

Comments
 (0)