Skip to content
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

Part 1: Asset Minting with V1 Asset Group Key and Chantools Cold Storage Support #1272

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

Conversation

ffranr
Copy link
Contributor

@ffranr ffranr commented Jan 3, 2025

This PR builds on Oli's WIP to demonstrate asset minting using the new V1 asset group key formulation. It integrates chantools to mint assets with an external asset group signing key (taproot internal key), offering a viable option for users who wish to keep their asset group signing key in cold storage.

A new integration test is included to simulate user behavior by leveraging chantools via the command line. The test verifies that the minter can successfully sign an asset into the asset group using the external asset group signing key.


Notes for Reviewers

Work which will be included in a future PR(s), ordered by most important first:

  • Tweaked NUMS key asset group key formulation.
  • This TODO in the doc:
TODO(guggero/ffranr): Replace this step by allowing the CLI to take the signed
PSBT instead. We can list the batch to get the asset ID of each pending asset
and match the PSBT's input previous output to find out what signed PSBT belongs
to what asset (in case there are multiple).

@ffranr ffranr self-assigned this Jan 3, 2025
@dstadulis dstadulis requested a review from GeorgeTsagk January 6, 2025 15:32
@dstadulis dstadulis added this to the v0.6 milestone Jan 6, 2025
@ffranr ffranr force-pushed the cold-group-key branch 3 times, most recently from c766bbe to cabfd1d Compare January 8, 2025 15:30
@coveralls
Copy link

coveralls commented Jan 8, 2025

Pull Request Test Coverage Report for Build 12709993838

Details

  • 198 of 645 (30.7%) changed or added relevant lines in 10 files are covered.
  • 22 unchanged lines in 6 files lost coverage.
  • Overall coverage decreased (-0.2%) to 40.763%

Changes Missing Coverage Covered Lines Changed/Added Lines %
proof/mint.go 7 10 70.0%
tapdb/assets_common.go 26 35 74.29%
cmd/tapcli/assets.go 0 49 0.0%
rpcserver.go 0 68 0.0%
tapgarden/planter.go 105 192 54.69%
asset/asset.go 46 161 28.57%
itest/chantools_harness.go 0 116 0.0%
Files with Coverage Reduction New Missed Lines %
tappsbt/create.go 2 53.22%
commitment/tap.go 2 84.09%
asset/asset.go 2 75.34%
tapgarden/planter.go 3 71.13%
rpcserver.go 3 0.0%
universe/interface.go 10 52.81%
Totals Coverage Status
Change from base Build 12650018423: -0.2%
Covered Lines: 26495
Relevant Lines: 64997

💛 - Coveralls

ffranr and others added 17 commits January 9, 2025 01:58
Introduce a new method to support future usage in the tapgarden package.
Refactored the `FundBatch` method to use a general return type,
`FundBatchResp`. This change improves code health and allows for
easier extension by enabling the state machine to return additional
fields in the future.
Introduced a `newVerboseBatch` function to create a verbose mint
batch from a regular mint batch. This functionality is extracted
from the existing list batch logic as part of a refactor.

The function will be used in a future commit to include a verbose
batch in the response of the FundBatch RPC endpoint.
Added comments to the planter function `buildGroupReqs` to improve
code readability and explain the logic more clearly.
Introduce a guide outlining the workflow for using external group
keys. Future commits in this PR will add features and implement
changes to support the described workflow.
The FundBatch RPC endpoint now returns a verbose batch instead of a
regular batch. The verbose batch includes additional asset group
information required by external signers.
Added a new `ExternalKey` message type to represent an external key
used for deriving and managing HD wallet addresses per BIP-86.

A new field of type `ExternalKey` was added to the `MintAsset` message,
enabling the specification of an external key when a mint request is
added to the batch.
Introduce a new `ExternalKey` type and add external key fields of this
type to `Seedling` and `GroupKeyRequest`.
Add unmarshalling support for the new `Asset.ExternalGroupKey` field
in the `MintAsset` RPC endpoint. Also populate the external key
in the `Seedling` instance.
Add an `external_key` field to the `GroupKeyRequest` RPC message,
which will also make it available in the `UnsealedAsset` RPC message.

Include marshalling functionality for the new field.
Added the following command line minting flags:
- group_key_xpub
- group_key_derivation_path
- group_key_fingerprint
Updated the documentation for the `MintAsset.group_tapscript_root`
field to clarify its purpose: it now represents the custom tapscript
subtree root for V1 group key reveals and serves as the tapscript tree
root for V0 group key reveals.
Add the `CustomTapscriptRoot` field to both the `GroupKey` and
`GroupKeyRequest` types. This field stores the user-defined custom
tapscript subtree root, which is committed to by the asset group key.

Update the `buildGroupReqs` function to leverage this new field when
constructing group key requests for generating asset group keys.
Introduce a new `GroupKeyVersion` type to represent the group key
version in `asset.GroupKey` and `asset.GroupKeyRequest`.

Add a `version` field to both `asset.GroupKey` and
`asset.GroupKeyRequest`, with logic to populate it. Enhance the
verification process by adding version-specific checks.
Update `GroupKeyRequest` methods to support the generation of
version 1 (V1) group keys.
Add a new PSBT field to the `UnsealedAssets` RPC message type, which
contains the byte-serialized PSBT equivalent of the group virtual
transaction for unsealed assets. As a result, the `FundBatch` and
`ListBatches` RPC endpoints now return group virtual PSBTs.

Include logic to generate the group virtual PSBT. The PSBT is unsigned
and is provided to allow signing with an external cold private key.
@ffranr ffranr force-pushed the cold-group-key branch 2 times, most recently from 8af293f to 4666b23 Compare January 9, 2025 02:19
@ffranr ffranr marked this pull request as ready for review January 9, 2025 02:19
@ffranr ffranr requested a review from gijswijs January 9, 2025 02:19
@ffranr ffranr force-pushed the cold-group-key branch 2 times, most recently from 1e543e2 to e61d638 Compare January 9, 2025 02:29
@ffranr
Copy link
Contributor Author

ffranr commented Jan 9, 2025

I think these are the necessary changes. This PR can't really be made smaller in any meaningful way.

@ffranr ffranr changed the title Asset Minting with V1 Asset Group Key and Chantools Cold Storage Support Part 1: Asset Minting with V1 Asset Group Key and Chantools Cold Storage Support Jan 9, 2025
ffranr and others added 8 commits January 10, 2025 12:59
Extended the `asset_groups` database table with two new columns:
`rows_version` and `custom_subtree_root`. These fields are required
to store and retrieve new GroupKey data for use during mint proof
generation, where the group key reveal is formulated.
Ensured the `version` and `custom_subtree_root` columns are
populated with the appropriate GroupKey data when storing a
group key in the database.
Implemented parsing logic for the `version` and `custom_subtree_root`
fields when reading from the database.
Added support for group key reveal V1 alongside the existing
group key reveal V0 when generating mint proofs.
Adds a test to ensure the new PSBT field introduced in the previous
commit can be used to derive a transaction identical to the group
virtual transaction.
Update the `build-itest` Makefile target, used as a subcommand during
`make itest`, to ensure `chantools` is properly set up for integration
tests.

Additionally, update `.gitignore` to exclude the `chantools` build
directory.
Add a test harness to execute the chantools binary via the command line
and parse its output.
Added a new integration test, `testMintExternalGroupKeyChantools`,
to verify the ability to mint an asset and generate an asset group
signature using chantools with an externally managed signing key.
@ffranr
Copy link
Contributor Author

ffranr commented Jan 10, 2025

As mentioned during our last call, I’ve updated taproot-assets/tapdb/sqlc/migrations/000026_asset_group_version_customsubtree.down.sql to include the recreation of the previous version of key_group_info_view as part of the down migration. While we’re not actively using down migrations, it’s still beneficial to have this in place as a precaution.

This key enables signing operations to be performed externally, outside
the daemon.
*/
taprpc.ExternalKey external_group_key = 14;
Copy link
Member

Choose a reason for hiding this comment

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

Is this field mutually exclusive with the existing group_key field above?

@@ -883,6 +966,10 @@ type GroupKeyRequest struct {
// has been applied.
RawKey keychain.KeyDescriptor

// ExternalKey specifies a public key that, when provided, is used to
// externally sign the group virtual transaction outside of tapd.
ExternalKey fn.Option[ExternalKey]
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here re the proto: can RawKey and ExternalKey be specified at the same time?

@@ -138,6 +139,21 @@ var mintAssetCommand = cli.Command{
"in order to avoid printing a large amount " +
"of data in case of large batches",
},
cli.StringFlag{
Name: "group_key_xpub",
Copy link
Member

Choose a reason for hiding this comment

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

Do we also need to be able to specify optional leaves for the custom tapscript root?

})
}

// TODO(guggero): Actually just ask for the signed PSBT back and extract
Copy link
Member

Choose a reason for hiding this comment

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

Will this be addressed in this PR?

So accepting the signed PSBT to extract the signatures manually ourselves.

Copy link
Member

Choose a reason for hiding this comment

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

One other related thing is that we want to add some basic meta data to the group key minting PSBT to allow external software to display information such as the asset ID, name, and units to be minted.

group key. This allows for future asset issuance authorized using a
script witness.
If an external group key is provided, the V1 scheme for group key script
Copy link
Member

Choose a reason for hiding this comment

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

Why this assumption vs a version + default?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if I understand @Roasbeef's suggestion here, but at least mention the default V0 scheme so that it is clear what we the change in behavior entails.

return nil, fmt.Errorf("error producing group virtual PSBT "+
"from tx: %w", err)
}

Copy link
Member

Choose a reason for hiding this comment

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

Re my other comment, this is where we'd add the new custom global fields to allow external signers/coordinates to display meta data w.r.t what it actually being minted. We can also provide the information needed to derive the virtual tx in the first place (for additional validation).

@@ -124,6 +124,9 @@ message UnsealedAsset {

// The group virtual transaction for the asset.
taprpc.GroupVirtualTx group_virtual_tx = 3;

// The byte serialized PSBT equivalent of the group virtual transaction.
string group_virtual_psbt = 4;
Copy link
Member

Choose a reason for hiding this comment

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

SealBatch can also optionally accept this.

-- is used to store the root of the custom subtree for the asset group. The
-- custom subtree, if provided, represents a subtree of the final tapscript
-- tree.
ALTER TABLE asset_groups ADD COLUMN custom_subtree_root BLOB;
Copy link
Member

Choose a reason for hiding this comment

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

Why isn't the existing field sufficient?

Copy link
Member

Choose a reason for hiding this comment

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

I think we only need the version here added for this migration.

GroupPubKey: *tweakedGroupKey,
TapscriptRoot: tapscriptRoot,
Witness: groupWitness,
CustomTapscriptRoot: customSubtreeRootHash,
Copy link
Member

Choose a reason for hiding this comment

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

Should expand assertions for db tests to make sure these new fields are read/write properly.

Although I don't think we need the new custom tapscript root at all. We can get away with just one field, the existing one, instead of having to lug around two fields where one is a transformation of the other.

@@ -501,6 +505,188 @@ func testMintFundSealAssets(t *harnessTest) {
})
}

// testMintExternalGroupKeyChantools tests that we're able to mint an asset
// using an external asset signing group key derived and managed by chantools.
func testMintExternalGroupKeyChantools(t *harnessTest) {
Copy link
Member

Choose a reason for hiding this comment

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

😎

Copy link
Contributor

@gijswijs gijswijs left a comment

Choose a reason for hiding this comment

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

Great work. I have added some small comments throughout.

nit: the commit message of commit a993070 claims the commit populates the external key in the seedling instance, but I don't think that happens in that commit.

I would love to see a itest that tests the re-issuance of an asset with cold minting.

Would also like to see a unit test that tests both the V0 and the V1 flow re GroupTapscriptRoot and a custom tapscript root. Whatever approach we take regarding implicit or explicit flow @Roasbeef suggested, we will end up with two separate flows that are currently not fully covered by unit tests.


if len(e.DerivationPath) != 5 {
return fmt.Errorf("derivation path must have exactly 5 " +
"componenets")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"componenets")
"components")

group key. This allows for future asset issuance authorized using a
script witness.
If an external group key is provided, the V1 scheme for group key script
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if I understand @Roasbeef's suggestion here, but at least mention the default V0 scheme so that it is clear what we the change in behavior entails.

)
// Construct an asset group pub key.
genesisAssetID := req.AnchorGen.ID()
groupPubKey, err := req.NewGroupPubKey(genesisAssetID)
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 I don't mind these small commits. Your commit narrative is very clear to me.

assets.asset_id AS asset_primary_key, assets.genesis_id, version, spent,
assets.asset_id AS asset_primary_key,
assets.genesis_id, assets.version,
spent,
Copy link
Contributor

Choose a reason for hiding this comment

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

ultra-nit: What are our coding style guidelines re line breaks and such for SQL?

Comment on lines +18 to +19
-- assets we care about. We obtain only the assets found in the batch
-- above, with the WHERE query at the bottom.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is copy pasta from the original migration file. The comment assets found in the batch above doesn't make sense here.

Suggested change
-- assets we care about. We obtain only the assets found in the batch
-- above, with the WHERE query at the bottom.
-- assets we care about. We obtain the assets with the WHERE query at the
-- bottom.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 👀 In review
Development

Successfully merging this pull request may close these issues.

6 participants