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

[feature]: custom Group Key API for minting #722

Closed
3 tasks done
jharveyb opened this issue Dec 5, 2023 · 7 comments · Fixed by #866
Closed
3 tasks done

[feature]: custom Group Key API for minting #722

jharveyb opened this issue Dec 5, 2023 · 7 comments · Fixed by #866
Assignees
Labels
enhancement New feature or request group_key
Milestone

Comments

@jharveyb
Copy link
Contributor

jharveyb commented Dec 5, 2023

Related to #343 .

We have support for group keys that include tapscript trees, but no way for users to actually provide a tapscript tree root during the minting process. Specifically:

For the first point, we'll need to add a new field to the Seedling and MintAssetRequest structs for custom group keys. We likely also want to mark these seedlings as using a custom group key, so that we require an external witness when finalizing the batch.

For the second point, we'll need to extend the FinalizeBatch call to accept a list of witnesses for each seedling. Otherwise we shouldn't have many changes wrt. creating the minting proofs.

The last point is being tracked in #343.

@jharveyb jharveyb added the enhancement New feature or request label Dec 5, 2023
@jharveyb jharveyb added this to the v0.4 milestone Dec 5, 2023
@jharveyb jharveyb self-assigned this Dec 5, 2023
@Roasbeef Roasbeef moved this from 🆕 New to 💇‍♂️Needs Shaping in Taproot-Assets Project Board Dec 5, 2023
@jharveyb
Copy link
Contributor Author

jharveyb commented Feb 22, 2024

Depends on #768.

There are a few points in the minting flow where this feature could be exposed:

  • AddSeedling call, when providing other details about the asset that will become the group anchor
  • Finalize call, when specifying other batch-wide parameters like feerate
  • A new call, Sign, to provide a witness for a group key in the batch

I'm in favor of the first option, as otherwise we have to specify the mapping between the provided witness and the asset seedlings.

Separately, there is the option to mandate that a tapscript tree is stored with tapd before minting begins (or least, with an RPC unrelated to minting). Then the tree could be referenced by root hash in minting-related calls.

More generally, right now we don't support an externally-managed key as the internal key for an asset group key. However, we also can't create a group key without an anchor asset. So we likely want to expand the AddSeedling call to be able to specify a custom internal key.

We'll also need more validation logic around group witnesses, to fail early for incorrect user-provided witnesses, but also to reject batch finalization if tapd can't produce a group witness directly (because a group internal key is externally managed).

@jharveyb jharveyb moved this from 💇‍♂️Needs Shaping to 🏗 In progress in Taproot-Assets Project Board Feb 28, 2024
@dstadulis
Copy link
Collaborator

dstadulis commented Mar 1, 2024

During initial implementation investigation, @jharveyb uncovered, a few areas which necessary changes will need to be implemented. An corresponding issue to describe the necessary prerequisites before this issue is started is currently being written.
Roughly the prerequisite are:

  • Reordering minter logic
  • Exposing group key creation phases

@Roasbeef
Copy link
Member

Roasbeef commented Mar 1, 2024

We'll also need more validation logic around group witnesses, to fail early for incorrect user-provided witnesses, but also to reject batch finalization if tapd can't produce a group witness directly (because a group internal key is externally managed).

IIRC, we can only actually validate the group witness once we know the true asset/ID, which right now is when we go to finalize (do the coin selection, etc) and obtain the genesis prev out. As a result, I think we're forced to either just accept all the witnesses in finalize, or accept it in AddSeedling, then only validate later on once we've done the necessary in selection. With the latter option, we could start to lock that genesis prev out sooner in the process, but need to make sure the lease is properly renwed.

@jharveyb
Copy link
Contributor Author

jharveyb commented Mar 1, 2024

Moved discussion on this requirement to #820.

@dstadulis dstadulis moved this from 🏗 In progress to 🔖 Ready in Taproot-Assets Project Board Mar 19, 2024
@jharveyb
Copy link
Contributor Author

jharveyb commented Mar 25, 2024

From discussion in #820, user flow would be:

  • Submit 1+ assets via MintAsset. Some assets may specify an internal key their asset group, or a tapscript root to be included in their asset group key.
  • Explicitly fund the batch via FundBatch, with an optional feerate and anchor tapscript tree sibling.
  • Call ListBatches to display the group virtual TXs associated with each seedling that will be a group anchor or grouped asset.
  • Select seedlings to produce custom witnesses for and generate tapscript leaves that satisfy the tapscript tree referenced in a seedling submitted earlier.
  • Construct the correct signing descriptor and pass that, along with the relevant script leaves, trees, and group virtual TXs, to an external signer that satisfies the lnd SignOutputRaw RPC (or equivalent).
  • Submit the produced witnesses, along with the assetID of the associated seedling, to the minter via SealBatch.
  • If all witnesses are validated successfully, call Finalize to finish the minting process.

@dstadulis
Copy link
Collaborator

dstadulis commented Mar 25, 2024

Construct the correct signing descriptor and pass that, along with the relevant script leaves, trees, and group virtual TXs, to an external signer that satisfies the lnd SignOutputRaw RPC (or equivalent).

#722 (comment)

taproot-assets/asset/asset.go

Lines 1108 to 1217 in 6494258

// DeriveCustomGroupKey derives an asset's group key based on a signing
// descriptor, the original group asset genesis, and the asset's genesis.
func DeriveCustomGroupKey(genSigner GenesisSigner, genBuilder GenesisTxBuilder,
req GroupKeyRequest, tapLeaf *psbt.TaprootTapLeafScript,
scriptWitness []byte) (*GroupKey, error) {
// First, perform the final checks on the asset being authorized for
// group membership.
err := req.Validate()
if err != nil {
return nil, err
}
// Compute the tweaked group key and set it in the asset before
// creating the virtual minting transaction.
genesisTweak := req.AnchorGen.ID()
tweakedGroupKey, err := GroupPubKey(
req.RawKey.PubKey, genesisTweak[:], req.TapscriptRoot,
)
if err != nil {
return nil, fmt.Errorf("cannot tweak group key: %w", err)
}
assetWithGroup := req.NewAsset.Copy()
assetWithGroup.GroupKey = &GroupKey{
GroupPubKey: *tweakedGroupKey,
}
// Exit early if a group witness is already given, since we don't need
// to construct a virtual TX nor produce a signature.
if scriptWitness != nil {
if tapLeaf == nil {
return nil, fmt.Errorf("need tap leaf with group " +
"script witness")
}
witness := wire.TxWitness{
scriptWitness, tapLeaf.Script, tapLeaf.ControlBlock,
}
return &GroupKey{
RawKey: req.RawKey,
GroupPubKey: *tweakedGroupKey,
TapscriptRoot: req.TapscriptRoot,
Witness: witness,
}, nil
}
// Build the virtual transaction that represents the minting of the new
// asset, which will be signed to generate the group witness.
genesisTx, prevOut, err := genBuilder.BuildGenesisTx(assetWithGroup)
if err != nil {
return nil, fmt.Errorf("cannot build virtual tx: %w", err)
}
// Populate the signing descriptor needed to sign the virtual minting
// transaction.
signDesc := &lndclient.SignDescriptor{
KeyDesc: req.RawKey,
SingleTweak: genesisTweak[:],
TapTweak: req.TapscriptRoot,
Output: prevOut,
HashType: txscript.SigHashDefault,
InputIndex: 0,
}
// There are three possible signing cases: BIP-0086 key spend path, key
// spend path with a script root, and script spend path.
switch {
// If there is no tapscript root, we're doing a BIP-0086 key spend.
case len(signDesc.TapTweak) == 0:
signDesc.SignMethod = input.TaprootKeySpendBIP0086SignMethod
// No leaf means we're not signing a specific script, so this is the key
// spend path with a tapscript root.
case len(signDesc.TapTweak) != 0 && tapLeaf == nil:
signDesc.SignMethod = input.TaprootKeySpendSignMethod
// One leaf hash and a merkle root means we're signing a specific
// script.
case len(signDesc.TapTweak) != 0 && tapLeaf != nil:
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
signDesc.WitnessScript = tapLeaf.Script
default:
return nil, fmt.Errorf("bad sign descriptor for group key")
}
sig, err := genSigner.SignVirtualTx(signDesc, genesisTx, prevOut)
if err != nil {
return nil, err
}
witness := wire.TxWitness{sig.Serialize()}
// If this was a script spend, we also have to add the script itself and
// the control block to the witness, otherwise the verifier will reject
// the generated witness.
if signDesc.SignMethod == input.TaprootScriptSpendSignMethod {
witness = append(witness, signDesc.WitnessScript)
witness = append(witness, tapLeaf.ControlBlock)
}
return &GroupKey{
RawKey: signDesc.KeyDesc,
GroupPubKey: *tweakedGroupKey,
TapscriptRoot: signDesc.TapTweak,
Witness: witness,
}, nil
}
stipulates the properties of the fields that a user, who is creating an external group key, will need to satisfy. Previously this would have been handled by lnd but now user would need to ensure conformance.

@jharveyb jharveyb moved this from 🔖 Ready to 🏗 In progress in Taproot-Assets Project Board Apr 11, 2024
@jharveyb
Copy link
Contributor Author

jharveyb commented Apr 16, 2024

Implemented in #827 , being exposed in #866 .

#883 is related.

@jharveyb jharveyb linked a pull request May 1, 2024 that will close this issue
7 tasks
@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in Taproot-Assets Project Board May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request group_key
Projects
Status: ✅ Done
Development

Successfully merging a pull request may close this issue.

3 participants