Skip to content

Commit 3d3b254

Browse files
author
ffranr
authored
Merge pull request #1235 from lightninglabs/groupkeyreveal-version-refactor
Refactor GroupKeyReveal for multi-version support
2 parents 651ab88 + 2fa1fb5 commit 3d3b254

File tree

14 files changed

+135
-79
lines changed

14 files changed

+135
-79
lines changed

asset/asset.go

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -918,49 +918,96 @@ type GroupVirtualTx struct {
918918
TweakedKey btcec.PublicKey
919919
}
920920

921-
// GroupKeyReveal is a type for representing the data used to derive the tweaked
922-
// key used to identify an asset group. The final tweaked key is the result of:
923-
// TapTweak(groupInternalKey, tapscriptRoot)
924-
type GroupKeyReveal struct {
921+
// GroupKeyReveal represents the data used to derive the adjusted key that
922+
// uniquely identifies an asset group.
923+
type GroupKeyReveal interface {
924+
// RawKey returns the raw key of the group key reveal.
925+
RawKey() SerializedKey
926+
927+
// SetRawKey sets the raw key of the group key reveal.
928+
SetRawKey(SerializedKey)
929+
930+
// TapscriptRoot returns the tapscript root of the group key reveal.
931+
TapscriptRoot() []byte
932+
933+
// SetTapscriptRoot sets the tapscript root of the group key reveal.
934+
SetTapscriptRoot([]byte)
935+
936+
// GroupPubKey returns the group public key derived from the group key
937+
// reveal.
938+
GroupPubKey(assetID ID) (*btcec.PublicKey, error)
939+
}
940+
941+
// GroupKeyRevealV0 is a version 0 group key reveal type for representing the
942+
// data used to derive the tweaked key used to identify an asset group. The
943+
// final tweaked key is the result of: TapTweak(groupInternalKey, tapscriptRoot)
944+
type GroupKeyRevealV0 struct {
925945
// RawKey is the public key that is tweaked twice to derive the final
926946
// tweaked group key. The final tweaked key is the result of:
927947
// internalKey = rawKey + singleTweak * G
928948
// tweakedGroupKey = TapTweak(internalKey, tapTweak)
929-
RawKey SerializedKey
949+
rawKey SerializedKey
930950

931951
// TapscriptRoot is the root of the Tapscript tree that commits to all
932952
// script spend conditions for the group key. Instead of spending an
933953
// asset, these scripts are used to define witnesses more complex than
934954
// a Schnorr signature for reissuing assets. This is either empty/nil or
935955
// a 32-byte hash.
936-
TapscriptRoot []byte
956+
tapscriptRoot []byte
937957
}
938958

939-
// PendingGroupWitness specifies the asset group witness for an asset seedling
940-
// in an unsealed minting batch.
941-
type PendingGroupWitness struct {
942-
GenID ID
943-
Witness wire.TxWitness
959+
// Ensure that GroupKeyRevealV0 implements the GroupKeyReveal interface.
960+
var _ GroupKeyReveal = (*GroupKeyRevealV0)(nil)
961+
962+
// NewGroupKeyRevealV0 creates a new version 0 group key reveal instance.
963+
func NewGroupKeyRevealV0(rawKey SerializedKey,
964+
tapscriptRoot []byte) GroupKeyReveal {
965+
966+
return &GroupKeyRevealV0{
967+
rawKey: rawKey,
968+
tapscriptRoot: tapscriptRoot,
969+
}
970+
}
971+
972+
// RawKey returns the raw key of the group key reveal.
973+
func (g *GroupKeyRevealV0) RawKey() SerializedKey {
974+
return g.rawKey
975+
}
976+
977+
// SetRawKey sets the raw key of the group key reveal.
978+
func (g *GroupKeyRevealV0) SetRawKey(rawKey SerializedKey) {
979+
g.rawKey = rawKey
980+
}
981+
982+
// TapscriptRoot returns the tapscript root of the group key reveal.
983+
func (g *GroupKeyRevealV0) TapscriptRoot() []byte {
984+
return g.tapscriptRoot
985+
}
986+
987+
// SetTapscriptRoot sets the tapscript root of the group key reveal.
988+
func (g *GroupKeyRevealV0) SetTapscriptRoot(tapscriptRoot []byte) {
989+
g.tapscriptRoot = tapscriptRoot
944990
}
945991

946992
// GroupPubKey returns the group public key derived from the group key reveal.
947-
func (g *GroupKeyReveal) GroupPubKey(assetID ID) (*btcec.PublicKey, error) {
948-
rawKey, err := g.RawKey.ToPubKey()
993+
func (g *GroupKeyRevealV0) GroupPubKey(assetID ID) (*btcec.PublicKey, error) {
994+
rawKey, err := g.RawKey().ToPubKey()
949995
if err != nil {
950996
return nil, fmt.Errorf("group reveal raw key invalid: %w", err)
951997
}
952998

953-
return GroupPubKey(rawKey, assetID[:], g.TapscriptRoot)
999+
return GroupPubKeyV0(rawKey, assetID[:], g.TapscriptRoot())
9541000
}
9551001

956-
// GroupPubKey derives a tweaked group key from a public key and two tweaks;
957-
// the single tweak is the asset ID of the group anchor asset, and the tapTweak
958-
// is the root of a tapscript tree that commits to script-based conditions for
959-
// reissuing assets as part of this asset group. The tweaked key is defined by:
1002+
// GroupPubKeyV0 derives a version 0 tweaked group key from a public key and two
1003+
// tweaks; the single tweak is the asset ID of the group anchor asset, and the
1004+
// tapTweak is the root of a tapscript tree that commits to script-based
1005+
// conditions for reissuing assets as part of this asset group. The tweaked key
1006+
// is defined by:
9601007
//
9611008
// internalKey = rawKey + singleTweak * G
9621009
// tweakedGroupKey = TapTweak(internalKey, tapTweak)
963-
func GroupPubKey(rawKey *btcec.PublicKey, singleTweak, tapTweak []byte) (
1010+
func GroupPubKeyV0(rawKey *btcec.PublicKey, singleTweak, tapTweak []byte) (
9641011
*btcec.PublicKey, error) {
9651012

9661013
if len(singleTweak) != sha256.Size {
@@ -1371,7 +1418,7 @@ func (req *GroupKeyRequest) BuildGroupVirtualTx(genBuilder GenesisTxBuilder) (
13711418
// Compute the tweaked group key and set it in the asset before
13721419
// creating the virtual minting transaction.
13731420
genesisTweak := req.AnchorGen.ID()
1374-
tweakedGroupKey, err := GroupPubKey(
1421+
tweakedGroupKey, err := GroupPubKeyV0(
13751422
req.RawKey.PubKey, genesisTweak[:], req.TapscriptRoot,
13761423
)
13771424
if err != nil {
@@ -1491,6 +1538,13 @@ func DeriveGroupKey(genSigner GenesisSigner, genTx GroupVirtualTx,
14911538
}, nil
14921539
}
14931540

1541+
// PendingGroupWitness specifies the asset group witness for an asset seedling
1542+
// in an unsealed minting batch.
1543+
type PendingGroupWitness struct {
1544+
GenID ID
1545+
Witness wire.TxWitness
1546+
}
1547+
14941548
// Asset represents a Taproot asset.
14951549
type Asset struct {
14961550
// Version is the Taproot Asset version of the asset.

asset/asset_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,10 +920,10 @@ func TestAssetGroupKey(t *testing.T) {
920920

921921
// Group key tweaking should fail when given invalid tweaks.
922922
badTweak := test.RandBytes(33)
923-
_, err = GroupPubKey(groupPub, badTweak, badTweak)
923+
_, err = GroupPubKeyV0(groupPub, badTweak, badTweak)
924924
require.Error(t, err)
925925

926-
_, err = GroupPubKey(groupPub, groupTweak[:], badTweak)
926+
_, err = GroupPubKeyV0(groupPub, groupTweak[:], badTweak)
927927
require.Error(t, err)
928928
}
929929

asset/mock.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -971,13 +971,14 @@ func (tgr *TestGenesisReveal) ToGenesisReveal(t testing.TB) *Genesis {
971971
}
972972

973973
func NewTestFromGroupKeyReveal(t testing.TB,
974-
gkr *GroupKeyReveal) *TestGroupKeyReveal {
974+
gkr GroupKeyReveal) *TestGroupKeyReveal {
975975

976976
t.Helper()
977977

978+
rawKey := gkr.RawKey()
978979
return &TestGroupKeyReveal{
979-
RawKey: hex.EncodeToString(gkr.RawKey[:]),
980-
TapscriptRoot: hex.EncodeToString(gkr.TapscriptRoot),
980+
RawKey: hex.EncodeToString(rawKey[:]),
981+
TapscriptRoot: hex.EncodeToString(gkr.TapscriptRoot()),
981982
}
982983
}
983984

@@ -986,15 +987,12 @@ type TestGroupKeyReveal struct {
986987
TapscriptRoot string `json:"tapscript_root"`
987988
}
988989

989-
func (tgkr *TestGroupKeyReveal) ToGroupKeyReveal(t testing.TB) *GroupKeyReveal {
990+
func (tgkr *TestGroupKeyReveal) ToGroupKeyReveal(t testing.TB) GroupKeyReveal {
990991
t.Helper()
991992

992993
rawKey := test.ParsePubKey(t, tgkr.RawKey)
993994
tapscriptRoot, err := hex.DecodeString(tgkr.TapscriptRoot)
994995
require.NoError(t, err)
995996

996-
return &GroupKeyReveal{
997-
RawKey: ToSerialized(rawKey),
998-
TapscriptRoot: tapscriptRoot,
999-
}
997+
return NewGroupKeyRevealV0(ToSerialized(rawKey), tapscriptRoot)
1000998
}

itest/assertions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,7 @@ func AssertGroupAnchor(t *testing.T, anchorGen *asset.Genesis,
14031403

14041404
// TODO(jhb): add tapscript root support
14051405
anchorTweak := anchorGen.ID()
1406-
computedGroupPubKey, err := asset.GroupPubKey(
1406+
computedGroupPubKey, err := asset.GroupPubKeyV0(
14071407
internalPubKey, anchorTweak[:], nil,
14081408
)
14091409
require.NoError(t, err)

proof/encoding.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -467,13 +467,13 @@ func GenesisRevealDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
467467
}
468468

469469
func GroupKeyRevealEncoder(w io.Writer, val any, buf *[8]byte) error {
470-
if t, ok := val.(**asset.GroupKeyReveal); ok {
471-
key := &(*t).RawKey
472-
if err := asset.SerializedKeyEncoder(w, key, buf); err != nil {
470+
if t, ok := val.(*asset.GroupKeyReveal); ok {
471+
key := (*t).RawKey()
472+
if err := asset.SerializedKeyEncoder(w, &key, buf); err != nil {
473473
return err
474474
}
475-
root := &(*t).TapscriptRoot
476-
return tlv.EVarBytes(w, root, buf)
475+
root := (*t).TapscriptRoot()
476+
return tlv.EVarBytes(w, &root, buf)
477477
}
478478

479479
return tlv.NewTypeForEncodingErr(val, "GroupKeyReveal")
@@ -489,20 +489,22 @@ func GroupKeyRevealDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
489489
ErrProofInvalid)
490490
}
491491

492-
if typ, ok := val.(**asset.GroupKeyReveal); ok {
493-
var reveal asset.GroupKeyReveal
492+
if typ, ok := val.(*asset.GroupKeyReveal); ok {
493+
var rawKey asset.SerializedKey
494494
err := asset.SerializedKeyDecoder(
495-
r, &reveal.RawKey, buf, btcec.PubKeyBytesLenCompressed,
495+
r, &rawKey, buf, btcec.PubKeyBytesLenCompressed,
496496
)
497497
if err != nil {
498498
return err
499499
}
500500
remaining := l - btcec.PubKeyBytesLenCompressed
501-
err = tlv.DVarBytes(r, &reveal.TapscriptRoot, buf, remaining)
501+
var tapscriptRoot []byte
502+
err = tlv.DVarBytes(r, &tapscriptRoot, buf, remaining)
502503
if err != nil {
503504
return err
504505
}
505-
*typ = &reveal
506+
507+
*typ = asset.NewGroupKeyRevealV0(rawKey, tapscriptRoot)
506508
return nil
507509
}
508510
return tlv.NewTypeForEncodingErr(val, "GroupKeyReveal")

proof/mint.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,11 @@ func committedProofs(baseProof *Proof, tapTreeRoot *commitment.TapCommitment,
388388

389389
err := groupAnchorVerifier(&newAsset.Genesis, groupKey)
390390
if err == nil {
391-
groupReveal := &asset.GroupKeyReveal{
392-
RawKey: asset.ToSerialized(
393-
groupKey.RawKey.PubKey,
394-
),
395-
TapscriptRoot: groupKey.TapscriptRoot,
396-
}
391+
rawKey := asset.ToSerialized(
392+
groupKey.RawKey.PubKey,
393+
)
394+
groupReveal := asset.NewGroupKeyRevealV0(
395+
rawKey, groupKey.TapscriptRoot)
397396
assetProof.GroupKeyReveal = groupReveal
398397
}
399398
}

proof/mock.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ func RandProof(t testing.TB, genesis asset.Genesis,
3939
t, genesis, 1, 0, 0, tweakedScriptKey, nil,
4040
)
4141
groupKey := asset.RandGroupKey(t, genesis, protoAsset)
42-
groupReveal := asset.GroupKeyReveal{
43-
RawKey: asset.ToSerialized(&groupKey.GroupPubKey),
44-
TapscriptRoot: test.RandBytes(32),
45-
}
42+
groupReveal := asset.NewGroupKeyRevealV0(
43+
asset.ToSerialized(&groupKey.GroupPubKey),
44+
test.RandBytes(32),
45+
)
4646

4747
amount := uint64(1)
4848
mintCommitment, assets, err := commitment.Mint(
@@ -146,7 +146,7 @@ func RandProof(t testing.TB, genesis asset.Genesis,
146146
},
147147
ChallengeWitness: wire.TxWitness{[]byte("foo"), []byte("bar")},
148148
GenesisReveal: &genesis,
149-
GroupKeyReveal: &groupReveal,
149+
GroupKeyReveal: groupReveal,
150150
}
151151
}
152152

proof/proof.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,13 @@ type Proof struct {
301301
// re-derivation of the asset group key.
302302
GenesisReveal *asset.Genesis
303303

304-
// GroupKeyReveal is an optional set of bytes that represent the public
305-
// key and Tapscript root used to derive the final tweaked group key for
306-
// the asset group. This field must be provided for issuance proofs of
307-
// grouped assets.
308-
GroupKeyReveal *asset.GroupKeyReveal
304+
// GroupKeyReveal contains the data required to derive the final tweaked
305+
// group key for an asset group.
306+
//
307+
// NOTE: This field is mandatory for the group anchor (i.e., the initial
308+
// minting tranche of an asset group). Subsequent minting tranches
309+
// require only a valid signature for the previously revealed group key.
310+
GroupKeyReveal asset.GroupKeyReveal
309311

310312
// UnknownOddTypes is a map of unknown odd types that were encountered
311313
// during decoding. This map is used to preserve unknown types that we

proof/proof_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func TestProofEncoding(t *testing.T) {
186186
require.False(t, IsSingleProof(nil))
187187

188188
// Test with a nil tapscript root in the group reveal.
189-
proof.GroupKeyReveal.TapscriptRoot = nil
189+
proof.GroupKeyReveal.SetTapscriptRoot(nil)
190190
file, err = NewFile(V0, proof, proof)
191191
require.NoError(t, err)
192192
proof.AdditionalInputs = []File{*file, *file}
@@ -262,12 +262,10 @@ func genRandomGenesisWithProof(t testing.TB, assetType asset.Type,
262262
asset.WithAssetVersion(assetVersion),
263263
)
264264
assetGroupKey := asset.RandGroupKey(t, assetGenesis, protoAsset)
265-
groupKeyReveal := &asset.GroupKeyReveal{
266-
RawKey: asset.ToSerialized(
267-
assetGroupKey.RawKey.PubKey,
268-
),
269-
TapscriptRoot: assetGroupKey.TapscriptRoot,
270-
}
265+
groupKeyReveal := asset.NewGroupKeyRevealV0(
266+
asset.ToSerialized(assetGroupKey.RawKey.PubKey),
267+
assetGroupKey.TapscriptRoot,
268+
)
271269

272270
if groupRevealMutator != nil {
273271
groupRevealMutator(groupKeyReveal)
@@ -362,7 +360,7 @@ func genRandomGenesisWithProof(t testing.TB, assetType asset.Type,
362360

363361
type genMutator func(*asset.Genesis)
364362

365-
type groupRevealMutator func(*asset.GroupKeyReveal)
363+
type groupRevealMutator func(asset.GroupKeyReveal)
366364

367365
type genRevealMutator func(*asset.Genesis) *asset.Genesis
368366

@@ -557,8 +555,10 @@ func TestGenesisProofVerification(t *testing.T) {
557555
name: "group key reveal invalid key",
558556
assetType: asset.Collectible,
559557
noMetaHash: true,
560-
groupRevealMutator: func(gkr *asset.GroupKeyReveal) {
561-
gkr.RawKey[0] = 0x01
558+
groupRevealMutator: func(gkr asset.GroupKeyReveal) {
559+
rawKey := gkr.RawKey()
560+
rawKey[0] = 0x01
561+
gkr.SetRawKey(rawKey)
562562
},
563563
expectedErr: secp256k1.ErrPubKeyInvalidFormat,
564564
},
@@ -567,8 +567,8 @@ func TestGenesisProofVerification(t *testing.T) {
567567
assetType: asset.Normal,
568568
amount: &amount,
569569
noMetaHash: true,
570-
groupRevealMutator: func(gkr *asset.GroupKeyReveal) {
571-
gkr.TapscriptRoot = test.RandBytes(32)
570+
groupRevealMutator: func(gkr asset.GroupKeyReveal) {
571+
gkr.SetTapscriptRoot(test.RandBytes(32))
572572
},
573573
expectedErr: ErrGroupKeyRevealMismatch,
574574
},

proof/records.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,14 +378,14 @@ func GenesisRevealRecord(genesis **asset.Genesis) tlv.Record {
378378
)
379379
}
380380

381-
func GroupKeyRevealRecord(reveal **asset.GroupKeyReveal) tlv.Record {
381+
func GroupKeyRevealRecord(reveal *asset.GroupKeyReveal) tlv.Record {
382382
recordSize := func() uint64 {
383383
if reveal == nil || *reveal == nil {
384384
return 0
385385
}
386386
r := *reveal
387387
return uint64(
388-
btcec.PubKeyBytesLenCompressed + len(r.TapscriptRoot),
388+
btcec.PubKeyBytesLenCompressed + len(r.TapscriptRoot()),
389389
)
390390
}
391391
return tlv.MakeDynamicRecord(

0 commit comments

Comments
 (0)