Skip to content

multi: hook up burn and mint events to the supply commit state machine #1675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

Roasbeef
Copy link
Member

No description provided.

Copy link
Contributor

@ffranr ffranr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a quick look, didn't go through every commit.

Comment on lines 229 to 345
// Set up mocks
tt.setupMocks(mockStorage)

// Call the method
hasDelegation, err := book.HasDelegationKey(ctx, tt.assetID)

// Check results
if tt.expectedError != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.expectedError)
} else {
require.NoError(t, err)
}

require.Equal(t, tt.expectedHas, hasDelegation)

// Verify expectations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments can be removed here

Comment on lines +104 to +121
// BurnSupplyCommitter is used to track supply changes (burns) and
// create periodic on-chain supply commitments.
BurnCommitter BurnSupplyCommitter

// DelegationKeyChecker is used to verify that we control the delegation
// key for a given asset, which is required for creating supply
// commitments.
DelegationKeyChecker address.DelegationKeyChecker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ChainPorter already supports event emission, so I think we might be able to avoid leaking the concept of supply commitments into it, even if the logic is abstracted behind interfaces. Instead, the multi supply commit manager could subscribe as a consumer of ChainPorter events. Perhaps we define a new burn event emitted by the ChainPorter, and let the supply commit manager handle delegation key checks independently.

In other words, the ChainPorter just emits a burn event, and the multi supply commit manager does the rest. This would help keep the supply commitment feature cleanly separated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. Instead, the multi supply commit manager could subscribe as a consumer of ChainPorter events

What happens if the daemon crashes after that event is sent, but before the supply commit manager processes it? To make that durable, we'd need to have the event send in the main state machine loop as it's implemented now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I generally agree with you, but we need that event emission to be durable.

One route that we can use to generally solve this problem is to extend the actor framework I added in lnd, to enable one to pass in a queue or mailbox like struct (rn it hard codes a normal Go channel). We'd then be able to make a new table of arbitrary events (with the requirement that they can be serialized), and have that back the durable mailbox for a given actor.

This would allow us to easily generalize this pattern between any sub-system we have. It would simplify some other sub-systems, as then they wouldn't need some reconciliation loop at start up, they'd just continue to reed from their queues.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to this is that we have a similar gap right now with teh way the state machine works. The sends are non-blocking. What we want instead is that either the sens is blocking, or we wait on some extra channel/future until the update event has been written to disk.

To implement this, we can either implement a done call back in the event that we send over. Or extend the lnd state machine to make this a first class feature.

I lean towards the done call back, as we can do that in another PR and reduce update cycles.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it feels better to do things more explicitly here, since the event system is more of a "fire-and-forget" kind of system.

@guggero guggero self-requested a review August 7, 2025 14:15
…n control

This commit introduces a new DelegationKeyChecker interface that allows
verification of whether we control the delegation key for a given asset.
The interface is implemented by the address.Book, which checks if the
local key ring contains the private key corresponding to an asset's
delegation public key.

The implementation queries the asset group and metadata to extract the
delegation key, then attempts to locate the corresponding private key
in the local keystore. This verification is essential for ensuring only
authorized parties can create supply commitment proofs for assets with
delegation keys.
This commit extends the MultiStateMachineManager to implement the
MintCommitter and BurnSupplyCommitter interfaces. These new methods
provide a type-safe way to send mint and burn events to the supply
commitment state machines.

The SendMintEvent method accepts universe-specific types for minting
operations, while SendBurnEvent handles burn leaf submissions. Both
methods internally construct the appropriate event types and delegate
to the existing SendEvent infrastructure, maintaining consistency with
the state machine architecture.
This commit enhances the GardenKit configuration by adding two new
optional dependencies: DelegationKeyChecker and MintCommitter. These
additions enable the garden subsystem to verify delegation key ownership
and submit mint events to the supply commitment state machine.

The GardenKit struct serves as the central configuration hub for the
tapgarden package, and these new fields allow components like the
BatchCaretaker to conditionally enable supply commitment functionality
when the required dependencies are available.
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.
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.
This commit completes the integration by wiring the address book's
DelegationKeyChecker implementation to both the GardenKit and
ChainPorterConfig. With this configuration, both minting and burning
operations now properly verify delegation key ownership before
submitting supply commitment events.

The address book serves as the central authority for key ownership
verification, leveraging its existing access to the key ring and
asset metadata storage. This ensures consistent delegation checking
across all supply commitment operations.
This commit introduces comprehensive test coverage for the delegation
key filtering functionality in the BatchCaretaker. The tests verify
that mint supply commitment events are only sent for assets where we
control the delegation key.

The test suite covers three key scenarios: all assets having delegation
keys, partial delegation key ownership, and no delegation keys. Mock
implementations of the MintCommitter and DelegationKeyChecker interfaces
enable isolated testing of the filtering logic without requiring actual
key management infrastructure.
This commit adds a unified test suite for burn supply commitment event
processing in the ChainPorter. The tests verify delegation key filtering,
error handling, and various burn scenarios through a table-driven
approach with a helper function for cleaner test setup.

The test coverage includes successful burn processing with mixed group
keys, delegation key filtering scenarios, error handling for both the
delegation checker and burn committer, handling of missing dependencies,
and validation of invalid group key bytes. The setupChainPorterTest
helper function eliminates repetitive mock configuration, making the
tests more maintainable and focused on the scenarios being tested.
@Roasbeef Roasbeef force-pushed the state-machine-hookup branch from 58d8271 to b1b0ad7 Compare August 8, 2025 03:40
@Roasbeef Roasbeef marked this pull request as ready for review August 8, 2025 03:40
Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! LGTM pending CI fixes.

}

log.Debugf("Sent supply commit burn event for asset %x",
assetID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this will double hex encode, can just use %s as we have a .String() method on the asset ID.

Comment on lines +104 to +121
// BurnSupplyCommitter is used to track supply changes (burns) and
// create periodic on-chain supply commitments.
BurnCommitter BurnSupplyCommitter

// DelegationKeyChecker is used to verify that we control the delegation
// key for a given asset, which is required for creating supply
// commitments.
DelegationKeyChecker address.DelegationKeyChecker
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it feels better to do things more explicitly here, since the event system is more of a "fire-and-forget" kind of system.

// Send supply commitment events for all minted assets before
// finalizing the batch. This ensures that supply commitments
// are tracked before the batch is considered complete.
err = b.sendSupplyCommitEvents(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do this before we call MarkBatchConfirmed? If we shut down while sending the supply commit events, the batch will already be marked as confirmed in the DB, so it won't be re-tried on next startup (and things aren't done atomically here).

@coveralls
Copy link

Pull Request Test Coverage Report for Build 16897047482

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 184 of 280 (65.71%) changed or added relevant lines in 5 files are covered.
  • 118 unchanged lines in 15 files lost coverage.
  • Overall coverage decreased (-8.0%) to 48.719%

Changes Missing Coverage Covered Lines Changed/Added Lines %
tapcfg/server.go 45 48 93.75%
universe/supplycommit/multi_sm_manager.go 9 17 52.94%
address/book.go 19 31 61.29%
tapgarden/caretaker.go 74 101 73.27%
tapfreighter/chain_porter.go 37 83 44.58%
Files with Coverage Reduction New Missed Lines %
fn/context_guard.go 1 91.94%
asset/asset.go 2 71.17%
rpcserver.go 2 61.5%
tapdb/assets_common.go 2 72.39%
tapdb/multiverse.go 2 77.22%
tapdb/sqlc/mssmt.sql.go 2 48.34%
universe/archive.go 3 79.29%
proof/courier.go 4 78.69%
tapdb/mssmt.go 4 63.64%
tapdb/assets_store.go 7 77.78%
Totals Coverage Status
Change from base Build 16818173014: -8.0%
Covered Lines: 51981
Relevant Lines: 106695

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🏗 In progress
Development

Successfully merging this pull request may close these issues.

4 participants