Skip to content

Commit

Permalink
multi: use Pedersen commitment for V1 group keys
Browse files Browse the repository at this point in the history
  • Loading branch information
guggero committed Jan 16, 2025
1 parent 6981896 commit 59985ca
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 19 deletions.
9 changes: 9 additions & 0 deletions asset/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ const (
// assets. The main contributing factor to this size are the previous
// witnesses which we currently allow to number up to 65k witnesses.
MaxAssetEncodeSizeBytes = blockchain.MaxBlockWeight

// PedersenXPubMasterKeyFingerprint is the master key fingerprint we use
// to identify the fake xPub we create from a Pedersen commitment public
// key (which is a special form of tweaked NUMS key). Master key
// fingerprints in PSBTs are always encoded as little-endian uint32s but
// displayed as 4 bytes of hex. Serialized as little-endian uint32, this
// spells "7a9a55e7" which can be read as "TAP asset" in leet speak
// (with a bit of imagination).
PedersenXPubMasterKeyFingerprint uint32 = 0xe7559a7a
)

// SerializedKey is a type for representing a public key, serialized in the
Expand Down
104 changes: 85 additions & 19 deletions tapgarden/planter.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,6 @@ func (p *PendingAssetGroup) PSBT(
p.GroupKeyRequest.RawKey.PubKey,
)

var (
bip32Derivation *psbt.Bip32Derivation
trBip32Derivation *psbt.TaprootBip32Derivation
)

switch {
case p.GroupKeyRequest.ExternalKey.IsSome():
externalKey := p.GroupKeyRequest.ExternalKey.UnwrapToPtr()
Expand All @@ -177,28 +172,98 @@ func (p *PendingAssetGroup) PSBT(
"from external key: %w", err)
}

bip32Derivation = &psbt.Bip32Derivation{
bip32Main := &psbt.Bip32Derivation{
PubKey: pubKey.SerializeCompressed(),
MasterKeyFingerprint: externalKey.MasterFingerprint,
Bip32Path: externalKey.DerivationPath,
}
trBip32Derivation = &psbt.TaprootBip32Derivation{
XOnlyPubKey: bip32Derivation.PubKey[1:],
trBip32Main := &psbt.TaprootBip32Derivation{
XOnlyPubKey: bip32Main.PubKey[1:],
MasterKeyFingerprint: externalKey.MasterFingerprint,
Bip32Path: externalKey.DerivationPath,
LeafHashes: make([][]byte, 0),
}

default:
bip32Derivation, trBip32Derivation =
tappsbt.Bip32DerivationFromKeyDesc(
p.GroupKeyRequest.RawKey, params.HDCoinType,
xPub := externalKey.XPub
xPubPath := externalKey.DerivationPath[:xPub.Depth()]
packet.XPubs = append(packet.XPubs, psbt.XPub{
ExtendedKey: psbt.EncodeExtendedKey(&xPub),
MasterKeyFingerprint: externalKey.MasterFingerprint,
Bip32Path: xPubPath,
})

vIn.Bip32Derivation = []*psbt.Bip32Derivation{
bip32Main,
}
vIn.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{
trBip32Main,
}

// TODO(guggero): Make this switch dependent on the non-spend
// leaf version, once we allow the user to configure that.
if true {
assetID := p.AnchorGen.ID()
numsXPub, numsKey, err := asset.TweakedNumsKey(assetID)
if err != nil {
return nil, fmt.Errorf("error deriving nums "+
"key: %w", err)
}

// For the fake/NUMS key, we use a specific static
// fingerprint, which will allow us to identify it in
// the HWI library in order to construct the correct
// miniscript policy for this type of spend.
numsFP := asset.PedersenXPubMasterKeyFingerprint
numsKeyBytes := numsKey.SerializeCompressed()
bip32Nums := &psbt.Bip32Derivation{
PubKey: numsKeyBytes,
MasterKeyFingerprint: numsFP,
// We use the same derivation path as for the
// "real" key, but it doesn't really matter,
// since it's a fake key anyway.
Bip32Path: externalKey.DerivationPath,
}
trBip32Nums := &psbt.TaprootBip32Derivation{
XOnlyPubKey: numsKeyBytes[1:],
MasterKeyFingerprint: numsFP,
// We use the same derivation path as for the
// "real" key, but it doesn't really matter,
// since it's a fake key anyway.
Bip32Path: externalKey.DerivationPath,
LeafHashes: make([][]byte, 0),
}

vIn.Bip32Derivation = append(
vIn.Bip32Derivation, bip32Nums,
)
vIn.TaprootBip32Derivation = append(
vIn.TaprootBip32Derivation, trBip32Nums,
)
}

vIn.Bip32Derivation = []*psbt.Bip32Derivation{bip32Derivation}
vIn.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{
trBip32Derivation,
numsXPub, err = numsXPub.CloneWithVersion(
params.HDPublicKeyID[:],
)
if err != nil {
return nil, fmt.Errorf("error cloning nums "+
"key: %w", err)
}
packet.XPubs = append(packet.XPubs, psbt.XPub{
ExtendedKey: psbt.EncodeExtendedKey(
numsXPub,
),
MasterKeyFingerprint: numsFP,
Bip32Path: xPubPath,
})
}

default:
bip32, trBip32 := tappsbt.Bip32DerivationFromKeyDesc(
p.GroupKeyRequest.RawKey, params.HDCoinType,
)
vIn.Bip32Derivation = []*psbt.Bip32Derivation{bip32}
vIn.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{
trBip32,
}
}

return packet, nil
Expand Down Expand Up @@ -902,9 +967,10 @@ func buildGroupReqs(genesisPoint wire.OutPoint, assetOutputIndex uint32,
genTXs = append(genTXs, *genTx)

newGroupKey := &asset.GroupKey{
Version: groupReq.Version,
RawKey: *seedling.GroupInternalKey,
TapscriptRoot: seedling.GroupTapscriptRoot,
Version: groupReq.Version,
RawKey: *seedling.GroupInternalKey,
TapscriptRoot: seedling.GroupTapscriptRoot,
CustomTapscriptRoot: customRootHash,
}

newGroups[seedlingName] = &asset.AssetGroup{
Expand Down

0 comments on commit 59985ca

Please sign in to comment.