-
Notifications
You must be signed in to change notification settings - Fork 132
Refactor: Split Pre-Commitment Logic to Support New Asset Groups #1589
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
base: main
Are you sure you want to change the base?
Conversation
Pull Request Test Coverage Report for Build 15737474312Details
💛 - Coveralls |
// - Present when the group key is already known—either reused from an | ||
// existing group at funding time or generated once the batch is | ||
// sealed. | ||
// - Absent while an unsealed batch without a prior group key is still |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this handles the case where you're creating the very first tranche, that's unconfirmed, but then you attempt to create a subsequent tranche?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it can handle issuing into an existing asset group and also creating a new asset group.
@@ -429,12 +429,6 @@ func (m *MintingBatch) validateUniCommitment(newSeedling Seedling) error { | |||
"with the universe commitment flag enabled") | |||
} | |||
|
|||
if !newSeedling.HasGroupKey() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the time of validation, the group key may not yet exist and could be
generated later during batch sealing.
How can the group key be generated later on? We need to know the group key upfront to create any of the set of relevant commitments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, the if !newSeedling.HasGroupKey()
check within ChainPlanter.prepAssetSeedling
was used to verify that a seedling had its GroupInfo
set upon being added to a batch. This worked correctly for seedlings requested through c.seedlingReqs
that already had a group key. However, this check created an issue for seedlings whose group key is generated later, during the batch seal step. In this scenario, the seedlings would not yet have their GroupInfo
set, causing the HasGroupKey()
check to fail incorrectly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've revised this once more in the latest commits. It's now:
if !newSeedling.EnableEmission && !newSeedling.HasGroupKey() {
return fmt.Errorf("universe commitment enabled: " +
"seedling must either create a new asset " +
"group or issue into an existing one")
}
which i think is what we want.
// If there are more than one asset with the same | ||
// name, we need to check the metadata hash to find the | ||
// correct one. | ||
if bytes.Equal(rpcGen.MetaHash, metaHash[:]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this change violate the assumptions of any of the other tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check was somewhat odd. The assertion logic attempted to reconstruct what it believed the asset’s metadata reveal should be, then asserted that the resulting metadata hash matched. However, if the supply commitment delegation key is generated during minting and not specified in the minting request, the assertion logic can't reliably know the full metadata reveal in advance. We need to clarify what this metadata hash assertion is actually verifying, given that some metadata fields are only determined after minting completes.
e602e77
to
58400a1
Compare
Failing itest is: testMintFundSealAssets. It's flakey, it doesn't always fail.
Error message:
It fails locally and in github CI. |
58400a1
to
db1af60
Compare
db1af60
to
8f82914
Compare
8f82914
to
e36acd4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 19 of 19 files at r2, all commit messages.
Reviewable status: 19 of 25 files reviewed, 14 unresolved discussions (waiting on @ffranr and @guggero)
tapgarden/batch.go
line 427 at r2 (raw file):
// it violates the constraints of the universe commitment // feature. if !newSeedling.EnableEmission && !newSeedling.HasGroupKey() {
So the prior logic here was essentially an OR
, what prompted the change to instead assert the conjunction?'
The error message here also seems to indicate that the block here was still intended to be an OR
?
tapdb/asset_minting.go
line 1476 at r2 (raw file):
// public key. func (a *AssetMintingStore) FetchDelegationKey(ctx context.Context, groupKey btcec.PublicKey) (fn.Option[tapgarden.DelegationKey], error) {
IIRC, we actually set things up s.t this key can change across minted batches. If this is the case, then we may need to update the logic here to return a slice, and factor in that the delegation key can change over time.
For ref, I understand the delegation key to serve two roles:
- The internal key used in the pre commitment output.
- The key used to sign the new blob for grouped assets that opt into an on-chain universe commitment.
tapgarden/planter.go
line 584 at r2 (raw file):
// the pending batch. if err == nil && sealedBatch != nil { batch = sealedBatch
Do we need to fold the update call below into this block? As either way we're updating the state of the batch, which is then passed into newCaretakerForBatch
.
tapgarden/planter.go
line 1986 at r2 (raw file):
// the pending batch. if err == nil && sealedBatch != nil { c.pendingBatch = sealedBatch
Ah so then the main shifted here is returning a new batch vs mutating the param tha was passed in.
tapgarden/planter.go
line 2453 at r2 (raw file):
} // Assign each newly created asset group to its corresponding seedling.
Can we split out this new section into a new helper function? This method is already rather long as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 18 of 25 files reviewed, 14 unresolved discussions (waiting on @ffranr, @guggero, and @Roasbeef)
tapdb/asset_minting.go
line 1476 at r2 (raw file):
Previously, Roasbeef (Olaoluwa Osuntokun) wrote…
IIRC, we actually set things up s.t this key can change across minted batches. If this is the case, then we may need to update the logic here to return a slice, and factor in that the delegation key can change over time.
For ref, I understand the delegation key to serve two roles:
- The internal key used in the pre commitment output.
- The key used to sign the new blob for grouped assets that opt into an on-chain universe commitment.
My view when working on the pre-commit aspect of the supply commit feature was to keep things simple for now. Setting the delegation key isn’t possible via the existing CLI or RPC interface, but we’re not ruling out support for delegation key updates in the future.
In this version, the delegation key is assumed to be fixed for a given group key. Later on, we could extend the feature to allow delegation key changes. I'd like to focus on getting the supply commitment feature working end to end before making it any more complicated.
Does that strategy sound reasonable to you?
tapgarden/batch.go
line 427 at r2 (raw file):
Previously, Roasbeef (Olaoluwa Osuntokun) wrote…
So the prior logic here was essentially an
OR
, what prompted the change to instead assert the conjunction?'The error message here also seems to indicate that the block here was still intended to be an
OR
?
We only raise an error in this if-clause when the seedling is neither creating a new asset group nor adding an asset to an existing one. In other words, if the seedling does not relate to any asset group and the supply/uni commitment feature is enabled, we return an error.
!newSeedling.EnableEmission
-> does the seedling NOT generate a new asset group!newSeedling.HasGroupKey()
-> does the seedling NOT add an asset to an exiting asset group.
tapgarden/planter.go
line 584 at r2 (raw file):
Previously, Roasbeef (Olaoluwa Osuntokun) wrote…
Do we need to fold the update call below into this block? As either way we're updating the state of the batch, which is then passed into
newCaretakerForBatch
.
I've updated my code block as follows:
// If the sealBatch call returned a sealed
// batch, update the pending batch accordingly.
if sealedBatch != nil {
batch = sealedBatch
}
// Any pending batch that was funded and sealed
// can now be set as frozen. We are already not
// able to add new seedlings to the batch.
batch.UpdateState(BatchStateFrozen)
If the seal batch function returns ErrBatchAlreadySealed
then sealedBatch == nil
. That's the only edge case we need to avoid. Otherwise, batch = sealedBatch
should be fine.
tapgarden/planter.go
line 1986 at r2 (raw file):
Previously, Roasbeef (Olaoluwa Osuntokun) wrote…
Ah so then the main shifted here is returning a new batch vs mutating the param tha was passed in.
Before my changes, method ChainPlanter.sealBatch
did return batchWithGroupInfo
which was a copy of the working batch. So effectively, and it did not mutate the workingBatch
param with batchWithGroupInfo
.
tapgarden/planter.go
line 2453 at r2 (raw file):
Previously, Roasbeef (Olaoluwa Osuntokun) wrote…
Can we split out this new section into a new helper function? This method is already rather long as is.
Will do 👍
e36acd4
to
19239df
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great refactor work! Found at least one bug that we need to fix. The rest is mostly optimization suggestions
tapgarden/planter.go
Outdated
return err | ||
} | ||
|
||
preCommitDesc.GroupPubKey = fn.Some(groupKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we unpack this when we put it back as an option? If we just want to validate it's there, we can do if !groupKeyOpt.IsSome()
. Same for the preCommitDesc. Probably doesn't matter too much, but might visually simplify the logic a little bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone with:
// Formulate the pre-commitment output descriptor with the group key.
preCommitDesc := fn.MapOptionZ(
fundedAnchor.PreCommitmentOutput,
// nolint: lll
func(preCommit PreCommitmentOutput) fn.Option[PreCommitmentOutput] {
preCommit.GroupPubKey = fn.Some(groupKey)
return fn.Some(preCommit)
},
)
does that work for you?
19239df
to
4ec6d81
Compare
This commit introduces a breaking change by replacing the `AssetMeta` proto message with `FetchAssetMetaResponse` as the response type for the `FetchAssetMeta` RPC endpoint. This change allows us to decouple the setting of asset metadata during minting from the retrieval of metadata via `FetchAssetMeta`. The motivation is to avoid duplicating certain metadata fields in the minting interface and to prevent users from setting fields that should be system-controlled (e.g., delegation key for now). The new response message includes the default supported metadata fields, which can now be fetched via the `FetchAssetMeta` endpoint. This will aid in retrieving the delegation key during supply commitment testing.
Make clear that this field is either populated when the seedling is added into a batch or when the batch is sealed.
Replaced the extractGenesisOutpoint function with a FundedMintAnchorPsbt.GenesisOutpoint method. This change adds nil pointer dereference checks and directly associates the genesis outpoint with the genesis package, improving code clarity and self-documentation.
Updated pre-commitment output generation to clarify that the asset group key may not be available yet, as the batch may still be unsealed and no existing asset group key may have been specified.
Ensure the delegation key is added to the Seedling only when the universe commitment feature is active.
Update UpsertMintAnchorUniCommitment to accept the raw batch key instead of the batch database row ID. Likewise, update FetchMintAnchorUniCommitment to fetch records using the raw batch key. The raw batch key is more readily available than the database row ID, making these queries more practical and convenient to use.
Add delegation_key_id to asset_seedlings. Replace taproot_internal_key with taproot_internal_key_id foreign key in mint_anchor_uni_commitments. Update related queries accordingly. Safe to drop taproot_internal_key column as supply commitment pre-commitment feature was not functional. The `FetchMintAnchorUniCommitment` SQL query has been extended to support additional filter parameters and to return multiple rows.
Don't upsert into primary key ID column. This is auto-assigned.
Renamed the MintStore method AddSeedlingGroups to SealBatch to better reflect its broader purpose. This change prepares the codebase for future extensions involving other batch sealing operations, ensuring all write actions occur atomically during the seal step. The method now accepts the entire batch as an argument, requiring the batch to be consistent with the state written to the database. This also reduces the likelihood of needing future interface changes, as the entire batch context is now provided. In a future commit, we will extend the implementation of MintStore.SealBatch to update any existing pre-commit (supply commit feature) database entry with newly generated asset group key information.
The pre-commitment output links an asset group key to an output in the anchoring transaction. If the group key is generated during the batch seal step, we associate it with the pre-commitment output, if present, and update the corresponding database entry.
Revise the minting batch-level check. If the supply commitment feature is enabled, the seedling must either create a new asset group or issue into an existing one.
Ensure the batch caretaker state machine has access to the asset group key associated with the pre-commitment output. This is necessary because the caretaker inserts the anchor transaction along with the pre-commitment entry in AddSproutsToBatch. Without this, it would upsert a nil asset group key if the field is unset.
If a seedling mints an asset into an existing asset group, attempt to look up the supply commitment delegation key from the database instead of generating a new one.
Update the test assertion helper to skip metadata comparison when there is only one asset of a given name. The metadata check was likely used for unique asset selection, but it's unnecessary if there is only one asset for a given name. Skipping the comparison avoids false failures when the minting process modifies metadata (e.g. by adding a delegation key), making the original comparison invalid.
The test mints two tranches into the same asset group across two different minting batches. It verifies that a pre-commitment output is created for both anchoring transactions and that the same delegation key is used for both tranches.
4ec6d81
to
ae463aa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice, LGTM 🎉
Closes #1586
Problem
The entire pre-commitment process—both adding the output to the transaction and linking it to an asset group—was executed in the
funding
step. This approach was flawed because it incorrectly assumed the asset group already existed on disk. This caused a failure when handling universe commitments for newly created assets, as the required group data was not yet available.Solution
This PR refactors the logic to align with the minting batch lifecycle, separating transaction construction from data association.
During the
funding
step: The pre-commitment output is still created and added to the minting transaction, as before. This part of the logic, which deals with the transaction itself, in principle remains unchanged.During the
seal
step: We now introduce logic toupsert
the relationship between the pre-commitment output and the asset group. This association is intentionally deferred to theseal
step because this is the first point at which we can guarantee a new asset group has been successfully created and its data is available.This separation of concerns ensures the feature works reliably for both new and existing asset groups.
This change is