@@ -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.
9311043type Asset struct {
9321044 // Version is the Taproot Asset version of the asset.
0 commit comments