Skip to content

Commit d01237d

Browse files
committed
asset: support group key creation with tapscripts
1 parent 35e7225 commit d01237d

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

asset/asset.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/btcsuite/btcd/blockchain"
1717
"github.com/btcsuite/btcd/btcec/v2"
1818
"github.com/btcsuite/btcd/btcec/v2/schnorr"
19+
"github.com/btcsuite/btcd/btcutil/psbt"
1920
"github.com/btcsuite/btcd/txscript"
2021
"github.com/btcsuite/btcd/wire"
2122
"github.com/lightninglabs/lndclient"
@@ -927,6 +928,117 @@ func DeriveGroupKey(genSigner GenesisSigner, genBuilder GenesisTxBuilder,
927928
}, nil
928929
}
929930

931+
// DeriveCustomGroupKey derives an asset's group key based on a signing
932+
// descriptor, the original group asset genesis, and the asset's genesis.
933+
func DeriveCustomGroupKey(genSigner GenesisSigner, genBuilder GenesisTxBuilder,
934+
req GroupKeyRequest, tapLeaf *psbt.TaprootTapLeafScript,
935+
scriptWitness []byte) (*GroupKey, error) {
936+
937+
// First, perform the final checks on the asset being authorized for
938+
// group membership.
939+
err := req.Validate()
940+
if err != nil {
941+
return nil, err
942+
}
943+
944+
// Compute the tweaked group key and set it in the asset before
945+
// creating the virtual minting transaction.
946+
genesisTweak := req.AnchorGen.ID()
947+
tweakedGroupKey, err := GroupPubKey(
948+
req.RawKey.PubKey, genesisTweak[:], req.TapscriptRoot,
949+
)
950+
if err != nil {
951+
return nil, fmt.Errorf("cannot tweak group key: %w", err)
952+
}
953+
954+
assetWithGroup := req.NewAsset.Copy()
955+
assetWithGroup.GroupKey = &GroupKey{
956+
GroupPubKey: *tweakedGroupKey,
957+
}
958+
959+
// Exit early if a group witness is already given, since we don't need
960+
// to construct a virtual TX nor produce a signature.
961+
if scriptWitness != nil {
962+
if tapLeaf == nil {
963+
return nil, fmt.Errorf("need tap leaf with group " +
964+
"script witness")
965+
}
966+
967+
witness := wire.TxWitness{
968+
scriptWitness, tapLeaf.Script, tapLeaf.ControlBlock,
969+
}
970+
971+
return &GroupKey{
972+
RawKey: req.RawKey,
973+
GroupPubKey: *tweakedGroupKey,
974+
TapscriptRoot: req.TapscriptRoot,
975+
Witness: witness,
976+
}, nil
977+
}
978+
979+
// Build the virtual transaction that represents the minting of the new
980+
// asset, which will be signed to generate the group witness.
981+
genesisTx, prevOut, err := genBuilder.BuildGenesisTx(assetWithGroup)
982+
if err != nil {
983+
return nil, fmt.Errorf("cannot build virtual tx: %w", err)
984+
}
985+
986+
// Populate the signing descriptor needed to sign the virtual minting
987+
// transaction.
988+
signDesc := &lndclient.SignDescriptor{
989+
KeyDesc: req.RawKey,
990+
SingleTweak: genesisTweak[:],
991+
TapTweak: req.TapscriptRoot,
992+
Output: prevOut,
993+
HashType: txscript.SigHashDefault,
994+
InputIndex: 0,
995+
}
996+
997+
// There are three possible signing cases: BIP-0086 key spend path, key
998+
// spend path with a script root, and script spend path.
999+
switch {
1000+
// If there is no tapscript root, we're doing a BIP-0086 key spend.
1001+
case len(signDesc.TapTweak) == 0:
1002+
signDesc.SignMethod = input.TaprootKeySpendBIP0086SignMethod
1003+
1004+
// No leaf means we're not signing a specific script, so this is the key
1005+
// spend path with a tapscript root.
1006+
case len(signDesc.TapTweak) != 0 && tapLeaf == nil:
1007+
signDesc.SignMethod = input.TaprootKeySpendSignMethod
1008+
1009+
// One leaf hash and a merkle root means we're signing a specific
1010+
// script.
1011+
case len(signDesc.TapTweak) != 0 && tapLeaf != nil:
1012+
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
1013+
signDesc.WitnessScript = tapLeaf.Script
1014+
1015+
default:
1016+
return nil, fmt.Errorf("bad sign descriptor for group key")
1017+
}
1018+
1019+
sig, err := genSigner.SignVirtualTx(signDesc, genesisTx, prevOut)
1020+
if err != nil {
1021+
return nil, err
1022+
}
1023+
1024+
witness := wire.TxWitness{sig.Serialize()}
1025+
1026+
// If this was a script spend, we also have to add the script itself and
1027+
// the control block to the witness, otherwise the verifier will reject
1028+
// the generated witness.
1029+
if signDesc.SignMethod == input.TaprootScriptSpendSignMethod {
1030+
witness = append(witness, signDesc.WitnessScript)
1031+
witness = append(witness, tapLeaf.ControlBlock)
1032+
}
1033+
1034+
return &GroupKey{
1035+
RawKey: signDesc.KeyDesc,
1036+
GroupPubKey: *tweakedGroupKey,
1037+
TapscriptRoot: signDesc.TapTweak,
1038+
Witness: witness,
1039+
}, nil
1040+
}
1041+
9301042
// Asset represents a Taproot asset.
9311043
type Asset struct {
9321044
// Version is the Taproot Asset version of the asset.

0 commit comments

Comments
 (0)