Skip to content

Commit 97697ed

Browse files
committed
proof: make forward-compatible by parsing unknown odd types
1 parent 4e78fb2 commit 97697ed

File tree

9 files changed

+718
-140
lines changed

9 files changed

+718
-140
lines changed

proof/meta.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ type MetaReveal struct {
9999

100100
// Data is the committed data being revealed.
101101
Data []byte
102+
103+
// UnknownOddTypes is a map of unknown odd types that were encountered
104+
// during decoding. This map is used to preserve unknown types that we
105+
// don't know of yet, so we can still encode them back when serializing.
106+
// This enables forward compatibility with future versions of the
107+
// protocol as it allows new odd (optional) types to be added without
108+
// breaking old clients that don't yet fully understand them.
109+
UnknownOddTypes tlv.TypeMap
102110
}
103111

104112
// SizableInteger is a subset of Integer that excludes int8, since we never use
@@ -337,10 +345,13 @@ func (m *MetaReveal) MetaHash() [asset.MetaHashLen]byte {
337345

338346
// EncodeRecords returns the TLV encode records for the meta reveal.
339347
func (m *MetaReveal) EncodeRecords() []tlv.Record {
340-
return []tlv.Record{
348+
records := []tlv.Record{
341349
MetaRevealTypeRecord(&m.Type),
342350
MetaRevealDataRecord(&m.Data),
343351
}
352+
353+
// Add any unknown odd types that were encountered during decoding.
354+
return asset.CombineRecords(records, m.UnknownOddTypes)
344355
}
345356

346357
// DecodeRecords returns the TLV decode records for the meta reveal.
@@ -370,8 +381,14 @@ func (m *MetaReveal) Decode(r io.Reader) error {
370381
// Note, we can't use the DecodeP2P method here, because the meta data
371382
// itself can be larger than 65k bytes. But we impose limits in the
372383
// individual decoding functions.
373-
//
374-
// TODO(guggero): Store unknown types (next commits).
375-
_, err = asset.TlvStrictDecode(stream, r, KnownMetaRevealTypes)
376-
return err
384+
unknownOddTypes, err := asset.TlvStrictDecode(
385+
stream, r, KnownMetaRevealTypes,
386+
)
387+
if err != nil {
388+
return err
389+
}
390+
391+
m.UnknownOddTypes = unknownOddTypes
392+
393+
return nil
377394
}

proof/meta_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88
"strings"
99
"testing"
1010

11+
"github.com/lightninglabs/taproot-assets/asset"
12+
"github.com/lightninglabs/taproot-assets/internal/test"
13+
"github.com/lightningnetwork/lnd/tlv"
1114
"github.com/stretchr/testify/require"
1215
)
1316

@@ -115,3 +118,39 @@ func TestProofInvalidJsonMetaReveal(t *testing.T) {
115118
require.ErrorIs(t, err, ErrInvalidJSON)
116119
require.Zero(t, decDisplay)
117120
}
121+
122+
// TestMetaRevealUnknownOddType tests that an unknown odd type is allowed in a
123+
// meta reveal and that we can still arrive at the correct meta hash with it.
124+
func TestMetaRevealUnknownOddType(t *testing.T) {
125+
knownMeta := &MetaReveal{
126+
Type: 123,
127+
Data: []byte("probably some JPEG or something"),
128+
}
129+
knownMetaHash := knownMeta.MetaHash()
130+
131+
test.RunUnknownOddTypeTest(
132+
t, knownMeta, &asset.ErrUnknownType{},
133+
func(buf *bytes.Buffer, meta *MetaReveal) error {
134+
return meta.Encode(buf)
135+
},
136+
func(buf *bytes.Buffer) (*MetaReveal, error) {
137+
var parsedMeta MetaReveal
138+
return &parsedMeta, parsedMeta.Decode(buf)
139+
},
140+
func(parsedMeta *MetaReveal, unknownTypes tlv.TypeMap) {
141+
require.Equal(
142+
t, unknownTypes, parsedMeta.UnknownOddTypes,
143+
)
144+
145+
// The meta should've changed, to make sure the unknown
146+
// value was taken into account when creating the
147+
// serialized meta.
148+
parsedMetaHash := parsedMeta.MetaHash()
149+
150+
require.NotEqual(t, knownMetaHash, parsedMetaHash)
151+
152+
parsedMeta.UnknownOddTypes = nil
153+
require.Equal(t, knownMeta, parsedMeta)
154+
},
155+
)
156+
}

proof/mock.go

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/lightningnetwork/lnd/keychain"
2424
"github.com/lightningnetwork/lnd/lnutils"
2525
"github.com/lightningnetwork/lnd/lnwire"
26+
"github.com/lightningnetwork/lnd/tlv"
2627
"github.com/stretchr/testify/require"
2728
)
2829

@@ -566,13 +567,14 @@ func NewTestFromProof(t testing.TB, p *Proof) *TestProof {
566567
t.Helper()
567568

568569
tp := &TestProof{
569-
PrevOut: p.PrevOut.String(),
570-
BlockHeader: NewTestFromBlockHeader(t, &p.BlockHeader),
571-
BlockHeight: p.BlockHeight,
572-
AnchorTx: test.HexTx(t, &p.AnchorTx),
573-
TxMerkleProof: NewTestFromTxMerkleProof(t, &p.TxMerkleProof),
574-
Asset: asset.NewTestFromAsset(t, &p.Asset),
575-
InclusionProof: NewTestFromTaprootProof(t, &p.InclusionProof),
570+
PrevOut: p.PrevOut.String(),
571+
BlockHeader: NewTestFromBlockHeader(t, &p.BlockHeader),
572+
BlockHeight: p.BlockHeight,
573+
AnchorTx: test.HexTx(t, &p.AnchorTx),
574+
TxMerkleProof: NewTestFromTxMerkleProof(t, &p.TxMerkleProof),
575+
Asset: asset.NewTestFromAsset(t, &p.Asset),
576+
InclusionProof: NewTestFromTaprootProof(t, &p.InclusionProof),
577+
UnknownOddTypes: p.UnknownOddTypes,
576578
}
577579

578580
for i := range p.ExclusionProofs {
@@ -637,19 +639,21 @@ type TestProof struct {
637639
ChallengeWitness []string `json:"challenge_witness"`
638640
GenesisReveal *asset.TestGenesisReveal `json:"genesis_reveal"`
639641
GroupKeyReveal *asset.TestGroupKeyReveal `json:"group_key_reveal"`
642+
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
640643
}
641644

642645
func (tp *TestProof) ToProof(t testing.TB) *Proof {
643646
t.Helper()
644647

645648
p := &Proof{
646-
PrevOut: test.ParseOutPoint(t, tp.PrevOut),
647-
BlockHeader: *tp.BlockHeader.ToBlockHeader(t),
648-
BlockHeight: tp.BlockHeight,
649-
AnchorTx: *test.ParseTx(t, tp.AnchorTx),
650-
TxMerkleProof: *tp.TxMerkleProof.ToTxMerkleProof(t),
651-
Asset: *tp.Asset.ToAsset(t),
652-
InclusionProof: *tp.InclusionProof.ToTaprootProof(t),
649+
PrevOut: test.ParseOutPoint(t, tp.PrevOut),
650+
BlockHeader: *tp.BlockHeader.ToBlockHeader(t),
651+
BlockHeight: tp.BlockHeight,
652+
AnchorTx: *test.ParseTx(t, tp.AnchorTx),
653+
TxMerkleProof: *tp.TxMerkleProof.ToTxMerkleProof(t),
654+
Asset: *tp.Asset.ToAsset(t),
655+
InclusionProof: *tp.InclusionProof.ToTaprootProof(t),
656+
UnknownOddTypes: tp.UnknownOddTypes,
653657
}
654658

655659
for i := range tp.ExclusionProofs {
@@ -774,8 +778,9 @@ func NewTestFromTaprootProof(t testing.TB,
774778
t.Helper()
775779

776780
ttp := &TestTaprootProof{
777-
OutputIndex: p.OutputIndex,
778-
InternalKey: test.HexPubKey(p.InternalKey),
781+
OutputIndex: p.OutputIndex,
782+
InternalKey: test.HexPubKey(p.InternalKey),
783+
UnknownOddTypes: p.UnknownOddTypes,
779784
}
780785

781786
if p.CommitmentProof != nil {
@@ -798,14 +803,16 @@ type TestTaprootProof struct {
798803
InternalKey string `json:"internal_key"`
799804
CommitmentProof *TestCommitmentProof `json:"commitment_proof"`
800805
TapscriptProof *TestTapscriptProof `json:"tapscript_proof"`
806+
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
801807
}
802808

803809
func (ttp *TestTaprootProof) ToTaprootProof(t testing.TB) *TaprootProof {
804810
t.Helper()
805811

806812
p := &TaprootProof{
807-
OutputIndex: ttp.OutputIndex,
808-
InternalKey: test.ParsePubKey(t, ttp.InternalKey),
813+
OutputIndex: ttp.OutputIndex,
814+
InternalKey: test.ParsePubKey(t, ttp.InternalKey),
815+
UnknownOddTypes: ttp.UnknownOddTypes,
809816
}
810817

811818
if ttp.CommitmentProof != nil {
@@ -829,12 +836,14 @@ func NewTestFromCommitmentProof(t testing.TB,
829836
TapscriptSibling: commitment.HexTapscriptSibling(
830837
t, p.TapSiblingPreimage,
831838
),
839+
UnknownOddTypes: p.UnknownOddTypes,
832840
}
833841
}
834842

835843
type TestCommitmentProof struct {
836844
Proof *commitment.TestProof `json:"proof"`
837845
TapscriptSibling string `json:"tapscript_sibling"`
846+
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
838847
}
839848

840849
func (tcp *TestCommitmentProof) ToCommitmentProof(
@@ -847,6 +856,7 @@ func (tcp *TestCommitmentProof) ToCommitmentProof(
847856
TapSiblingPreimage: commitment.ParseTapscriptSibling(
848857
t, tcp.TapscriptSibling,
849858
),
859+
UnknownOddTypes: tcp.UnknownOddTypes,
850860
}
851861
}
852862

@@ -856,16 +866,22 @@ func NewTestFromTapscriptProof(t testing.TB,
856866
t.Helper()
857867

858868
return &TestTapscriptProof{
859-
TapPreimage1: commitment.HexTapscriptSibling(t, p.TapPreimage1),
860-
TapPreimage2: commitment.HexTapscriptSibling(t, p.TapPreimage2),
861-
Bip86: p.Bip86,
869+
TapPreimage1: commitment.HexTapscriptSibling(
870+
t, p.TapPreimage1,
871+
),
872+
TapPreimage2: commitment.HexTapscriptSibling(
873+
t, p.TapPreimage2,
874+
),
875+
Bip86: p.Bip86,
876+
UnknownOddTypes: p.UnknownOddTypes,
862877
}
863878
}
864879

865880
type TestTapscriptProof struct {
866-
TapPreimage1 string `json:"tap_preimage_1"`
867-
TapPreimage2 string `json:"tap_preimage_2"`
868-
Bip86 bool `json:"bip86"`
881+
TapPreimage1 string `json:"tap_preimage_1"`
882+
TapPreimage2 string `json:"tap_preimage_2"`
883+
Bip86 bool `json:"bip86"`
884+
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
869885
}
870886

871887
func (ttp *TestTapscriptProof) ToTapscriptProof(t testing.TB) *TapscriptProof {
@@ -878,22 +894,25 @@ func (ttp *TestTapscriptProof) ToTapscriptProof(t testing.TB) *TapscriptProof {
878894
TapPreimage2: commitment.ParseTapscriptSibling(
879895
t, ttp.TapPreimage2,
880896
),
881-
Bip86: ttp.Bip86,
897+
Bip86: ttp.Bip86,
898+
UnknownOddTypes: ttp.UnknownOddTypes,
882899
}
883900
}
884901

885902
func NewTestFromMetaReveal(t testing.TB, m *MetaReveal) *TestMetaReveal {
886903
t.Helper()
887904

888905
return &TestMetaReveal{
889-
Type: uint8(m.Type),
890-
Data: hex.EncodeToString(m.Data),
906+
Type: uint8(m.Type),
907+
Data: hex.EncodeToString(m.Data),
908+
UnknownOddTypes: m.UnknownOddTypes,
891909
}
892910
}
893911

894912
type TestMetaReveal struct {
895-
Type uint8 `json:"type"`
896-
Data string `json:"data"`
913+
Type uint8 `json:"type"`
914+
Data string `json:"data"`
915+
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
897916
}
898917

899918
func (tmr *TestMetaReveal) ToMetaReveal(t testing.TB) *MetaReveal {
@@ -903,7 +922,8 @@ func (tmr *TestMetaReveal) ToMetaReveal(t testing.TB) *MetaReveal {
903922
require.NoError(t, err)
904923

905924
return &MetaReveal{
906-
Type: MetaType(tmr.Type),
907-
Data: data,
925+
Type: MetaType(tmr.Type),
926+
Data: data,
927+
UnknownOddTypes: tmr.UnknownOddTypes,
908928
}
909929
}

proof/proof.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,6 @@ type Proof struct {
277277
// MetaReveal is the set of bytes that were revealed to prove the
278278
// derivation of the meta data hash contained in the genesis asset.
279279
//
280-
// TODO(roasbeef): use even/odd framing here?
281-
//
282280
// NOTE: This field is optional, and can only be specified if the asset
283281
// above is a genesis asset. If specified, then verifiers _should_ also
284282
// verify the hashes match up.
@@ -300,14 +298,22 @@ type Proof struct {
300298
// provided for minting proofs, and must be empty for non-minting
301299
// proofs. This allows for derivation of the asset ID. If the asset is
302300
// part of an asset group, the Genesis information is also used for
303-
// rederivation of the asset group key.
301+
// re-derivation of the asset group key.
304302
GenesisReveal *asset.Genesis
305303

306304
// GroupKeyReveal is an optional set of bytes that represent the public
307305
// key and Tapscript root used to derive the final tweaked group key for
308306
// the asset group. This field must be provided for issuance proofs of
309307
// grouped assets.
310308
GroupKeyReveal *asset.GroupKeyReveal
309+
310+
// UnknownOddTypes is a map of unknown odd types that were encountered
311+
// during decoding. This map is used to preserve unknown types that we
312+
// don't know of yet, so we can still encode them back when serializing.
313+
// This enables forward compatibility with future versions of the
314+
// protocol as it allows new odd (optional) types to be added without
315+
// breaking old clients that don't yet fully understand them.
316+
UnknownOddTypes tlv.TypeMap
311317
}
312318

313319
// OutPoint returns the outpoint that commits to the asset associated with this
@@ -361,7 +367,9 @@ func (p *Proof) EncodeRecords() []tlv.Record {
361367
&p.GroupKeyReveal,
362368
))
363369
}
364-
return records
370+
371+
// Add any unknown odd types that were encountered during decoding.
372+
return asset.CombineRecords(records, p.UnknownOddTypes)
365373
}
366374

367375
// DecodeRecords returns the set of known TLV records to decode a Proof.
@@ -427,9 +435,16 @@ func (p *Proof) Decode(r io.Reader) error {
427435
// Note, we can't use the DecodeP2P method here, because the additional
428436
// inputs records might be larger than 64k each. Instead, we add
429437
// individual limits to each record.
430-
// TODO(guggero): Store unknown types (next commits).
431-
_, err = asset.TlvStrictDecode(stream, r, KnownProofTypes)
432-
return err
438+
unknownOddTypes, err := asset.TlvStrictDecode(
439+
stream, r, KnownProofTypes,
440+
)
441+
if err != nil {
442+
return err
443+
}
444+
445+
p.UnknownOddTypes = unknownOddTypes
446+
447+
return nil
433448
}
434449

435450
// Record returns a TLV record that can be used to encode/decode a Proof to/from

0 commit comments

Comments
 (0)