Skip to content

Commit 9402f2f

Browse files
committed
tapgarden: add delegation key filtering for mint supply commits
This commit enhances the BatchCaretaker to filter mint events based on delegation key ownership. The sendSupplyCommitEvents method now uses functional filtering to ensure supply commitment events are only sent for assets where we control the delegation key. Similar to the burn event filtering in tapfreighter, this change ensures the caretaker respects delegation authority when creating supply proofs for newly minted assets. The filtering happens early in the process to avoid unnecessary proof construction and event submission for assets we're not authorized to manage.
1 parent 5dc1877 commit 9402f2f

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

tapgarden/caretaker.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,17 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error)
11481148
return 0, fmt.Errorf("unable to confirm batch: %w", err)
11491149
}
11501150

1151+
// Send supply commitment events for all minted assets before
1152+
// finalizing the batch. This ensures that supply commitments
1153+
// are tracked before the batch is considered complete.
1154+
err = b.sendSupplyCommitEvents(
1155+
ctx, anchorAssets, nonAnchorAssets, mintingProofs,
1156+
)
1157+
if err != nil {
1158+
return 0, fmt.Errorf("unable to send supply commit "+
1159+
"events: %w", err)
1160+
}
1161+
11511162
// Now that we've confirmed the batch, we'll hand over the
11521163
// proofs to the re-org watcher.
11531164
if err := b.cfg.ProofWatcher.WatchProofs(
@@ -1432,6 +1443,116 @@ func GenHeaderVerifier(ctx context.Context,
14321443
}
14331444
}
14341445

1446+
// sendSupplyCommitEvents sends supply commitment events for all minted assets
1447+
// in the batch to track them in the supply commitment state machine.
1448+
func (b *BatchCaretaker) sendSupplyCommitEvents(ctx context.Context,
1449+
anchorAssets, nonAnchorAssets []*asset.Asset,
1450+
mintingProofs proof.AssetProofs) error {
1451+
1452+
// If no supply commit manager is configured, skip this step.
1453+
if b.cfg.MintSupplyCommitter == nil {
1454+
return nil
1455+
}
1456+
1457+
// If no delegation key checker is configured, skip this step. As if we
1458+
// need this to figure out if we made the asset or not.
1459+
if b.cfg.DelegationKeyChecker == nil {
1460+
return nil
1461+
}
1462+
1463+
// We'll combine the anchor and non-anchor assets into a single slice
1464+
// that we'll run through below.
1465+
allAssets := append(anchorAssets, nonAnchorAssets...)
1466+
1467+
delChecker := b.cfg.DelegationKeyChecker
1468+
1469+
// We filter the assets to only include those that have a delegation
1470+
// key. As only those have a supply commitment maintained.
1471+
assetsWithDelegation := fn.Filter(allAssets, func(a *asset.Asset) bool {
1472+
hasDelegationKey, err := delChecker.HasDelegationKey(
1473+
ctx, a.ID(),
1474+
)
1475+
if err != nil {
1476+
log.Debugf("Error checking delegation key for "+
1477+
"asset %x: %v", a.ID(), err)
1478+
return false
1479+
}
1480+
if !hasDelegationKey {
1481+
log.Debugf("Skipping supply commit event for "+
1482+
"asset %x: delegation key not controlled "+
1483+
"locally",
1484+
a.ID())
1485+
}
1486+
return hasDelegationKey
1487+
})
1488+
1489+
// For each of the assets that we just created with a delegation key,
1490+
// we'll create then send a supply commit event so the committer can
1491+
// take care of it.
1492+
for _, mintedAsset := range assetsWithDelegation {
1493+
// First, we'll extract the minting proof based on the srcipt
1494+
// key, and extract the very last proof from that.
1495+
scriptKey := asset.ToSerialized(mintedAsset.ScriptKey.PubKey)
1496+
mintingProof, ok := mintingProofs[scriptKey]
1497+
if !ok {
1498+
return fmt.Errorf("missing minting proof for asset "+
1499+
"with script key %x", scriptKey[:])
1500+
}
1501+
1502+
proofBlob, err := proof.EncodeAsProofFile(mintingProof)
1503+
if err != nil {
1504+
return fmt.Errorf("unable to encode proof as "+
1505+
"file: %w", err)
1506+
}
1507+
proofFile, err := proof.DecodeFile(proofBlob)
1508+
if err != nil {
1509+
return fmt.Errorf("unable to decode proof "+
1510+
"file: %w", err)
1511+
}
1512+
leafProof, err := proofFile.LastProof()
1513+
if err != nil {
1514+
return fmt.Errorf("unable to get leaf proof: %w", err)
1515+
}
1516+
1517+
// With the proof extracted, we can now create the universe
1518+
// key and leaf.
1519+
universeKey := universe.BaseLeafKey{
1520+
OutPoint: mintedAsset.Genesis.FirstPrevOut,
1521+
ScriptKey: &mintedAsset.ScriptKey,
1522+
}
1523+
uniqueLeafKey := universe.AssetLeafKey{
1524+
BaseLeafKey: universeKey,
1525+
AssetID: mintedAsset.ID(),
1526+
}
1527+
universeLeaf := universe.Leaf{
1528+
GenesisWithGroup: universe.GenesisWithGroup{
1529+
Genesis: mintedAsset.Genesis,
1530+
GroupKey: mintedAsset.GroupKey,
1531+
},
1532+
RawProof: proofBlob,
1533+
Asset: &leafProof.Asset,
1534+
Amt: mintedAsset.Amount,
1535+
}
1536+
assetSpec := asset.NewSpecifierOptionalGroupKey(
1537+
mintedAsset.ID(), mintedAsset.GroupKey,
1538+
)
1539+
1540+
// Finally we send all of the above to the supply commiter.
1541+
err = b.cfg.MintSupplyCommitter.SendMintEvent(
1542+
ctx, assetSpec, uniqueLeafKey, universeLeaf,
1543+
)
1544+
if err != nil {
1545+
return fmt.Errorf("unable to send mint event for "+
1546+
"asset %x: %w", mintedAsset.ID(), err)
1547+
}
1548+
1549+
log.Debugf("Sent supply commit mint event for asset %x",
1550+
mintedAsset.ID())
1551+
}
1552+
1553+
return nil
1554+
}
1555+
14351556
// assetGroupCacheSize is the size of the cache for group keys.
14361557
const assetGroupCacheSize = 10000
14371558

0 commit comments

Comments
 (0)