Skip to content

Commit 420f246

Browse files
authored
Merge pull request #1037 from lightninglabs/tlv-carve-out
TLV encoding: add forward-compatibility
2 parents 0a64116 + d06206e commit 420f246

31 files changed

+2436
-452
lines changed

address/address.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ type Tap struct {
130130
// ProofCourierAddr is the address of the proof courier that will be
131131
// used to distribute related proofs for this address.
132132
ProofCourierAddr url.URL
133+
134+
// UnknownOddTypes is a map of unknown odd types that were encountered
135+
// during decoding. This map is used to preserve unknown types that we
136+
// don't know of yet, so we can still encode them back when serializing.
137+
// This enables forward compatibility with future versions of the
138+
// protocol as it allows new odd (optional) types to be added without
139+
// breaking old clients that don't yet fully understand them.
140+
UnknownOddTypes tlv.TypeMap
133141
}
134142

135143
// newAddrOptions are a set of options that can modified how a new address is
@@ -380,7 +388,8 @@ func (a *Tap) EncodeRecords() []tlv.Record {
380388
records, newProofCourierAddrRecord(&a.ProofCourierAddr),
381389
)
382390

383-
return records
391+
// Add any unknown odd types that were encountered during decoding.
392+
return asset.CombineRecords(records, a.UnknownOddTypes)
384393
}
385394

386395
// DecodeRecords provides all records known for an address for proper
@@ -414,7 +423,17 @@ func (a *Tap) Decode(r io.Reader) error {
414423
if err != nil {
415424
return err
416425
}
417-
return stream.DecodeP2P(r)
426+
427+
unknownOddTypes, err := asset.TlvStrictDecodeP2P(
428+
stream, r, KnownAddressTypes,
429+
)
430+
if err != nil {
431+
return err
432+
}
433+
434+
a.UnknownOddTypes = unknownOddTypes
435+
436+
return nil
418437
}
419438

420439
// EncodeAddress returns a bech32m string encoding of a Taproot Asset address.

address/address_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/lightninglabs/taproot-assets/commitment"
1515
"github.com/lightninglabs/taproot-assets/fn"
1616
"github.com/lightninglabs/taproot-assets/internal/test"
17+
"github.com/lightningnetwork/lnd/tlv"
1718
"github.com/stretchr/testify/require"
1819
)
1920

@@ -361,6 +362,29 @@ func TestAddressEncoding(t *testing.T) {
361362
},
362363
err: nil,
363364
},
365+
{
366+
name: "simnet collectible with sibling and unknown " +
367+
"TLV type",
368+
f: func() (*Tap, string, error) {
369+
addr, _, err := randEncodedAddress(
370+
t, &SimNetTap, false, true,
371+
asset.Collectible,
372+
)
373+
374+
if err != nil {
375+
return nil, "", err
376+
}
377+
378+
foo := []byte("foo")
379+
addr.UnknownOddTypes = tlv.TypeMap{
380+
test.TestVectorAllowedUnknownType: foo,
381+
}
382+
383+
str, err := addr.EncodeAddress()
384+
return addr, str, err
385+
},
386+
err: nil,
387+
},
364388
{
365389
name: "unsupported hrp",
366390
f: func() (*Tap, string, error) {
@@ -492,6 +516,24 @@ func runBIPTestVector(t *testing.T, testVectors *TestVectors) {
492516

493517
areEqual := validCase.Expected == addrString
494518

519+
// Make sure the address in the test vectors doesn't use
520+
// a record type we haven't marked as known/supported
521+
// yet. If the following check fails, you need to update
522+
// the KnownAddressTypes set.
523+
for _, record := range a.EncodeRecords() {
524+
// Test vectors may contain this one type to
525+
// demonstrate that it is not rejected.
526+
if record.Type() ==
527+
test.TestVectorAllowedUnknownType {
528+
529+
continue
530+
}
531+
532+
require.Contains(
533+
tt, KnownAddressTypes, record.Type(),
534+
)
535+
}
536+
495537
// Create nice diff if things don't match.
496538
if !areEqual {
497539
chainParams, err := a.Net()
@@ -544,3 +586,46 @@ func FuzzAddressDecode(f *testing.F) {
544586
_ = a.Decode(bytes.NewReader(data))
545587
})
546588
}
589+
590+
// TestAddressUnknownOddType tests that an unknown odd type is allowed in an
591+
// address and that we can still arrive at the correct leaf hash with it.
592+
func TestAddressUnknownOddType(t *testing.T) {
593+
knownAddr, _, _ := RandAddr(t, &TestNet3Tap, RandProofCourierAddr(t))
594+
knownAddrString, err := knownAddr.EncodeAddress()
595+
require.NoError(t, err)
596+
597+
test.RunUnknownOddTypeTest(
598+
t, knownAddr.Tap, &asset.ErrUnknownType{},
599+
func(buf *bytes.Buffer, addr *Tap) error {
600+
return addr.Encode(buf)
601+
},
602+
func(buf *bytes.Buffer) (*Tap, error) {
603+
parsedAddr := &Tap{
604+
ChainParams: &TestNet3Tap,
605+
}
606+
return parsedAddr, parsedAddr.Decode(buf)
607+
},
608+
func(parsedAddr *Tap, unknownTypes tlv.TypeMap) {
609+
require.Equal(
610+
t, unknownTypes, parsedAddr.UnknownOddTypes,
611+
)
612+
613+
// The address should've changed, to make sure the
614+
// unknown value was taken into account when creating
615+
// the serialized address.
616+
parsedAddrString, err := parsedAddr.EncodeAddress()
617+
require.NoError(t, err)
618+
619+
require.NotEqual(t, knownAddrString, parsedAddrString)
620+
621+
parsedAddr.UnknownOddTypes = nil
622+
623+
// The genesis information isn't actually encoded in the
624+
// address, so we need to clear that out before
625+
// comparing.
626+
knownAddr.Tap.assetGen = asset.Genesis{}
627+
628+
require.Equal(t, knownAddr.Tap, parsedAddr)
629+
},
630+
)
631+
}

address/mock.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/lightninglabs/taproot-assets/commitment"
1313
"github.com/lightninglabs/taproot-assets/internal/test"
1414
"github.com/lightningnetwork/lnd/keychain"
15+
"github.com/lightningnetwork/lnd/tlv"
1516
"github.com/stretchr/testify/require"
1617
)
1718

@@ -135,6 +136,7 @@ func NewTestFromAddress(t testing.TB, a *Tap) *TestAddress {
135136
InternalKey: test.HexPubKey(&a.InternalKey),
136137
Amount: a.Amount,
137138
ProofCourierAddr: a.ProofCourierAddr.String(),
139+
UnknownOddTypes: a.UnknownOddTypes,
138140
}
139141

140142
if a.GroupKey != nil {
@@ -151,16 +153,17 @@ func NewTestFromAddress(t testing.TB, a *Tap) *TestAddress {
151153
}
152154

153155
type TestAddress struct {
154-
Version uint8 `json:"version"`
155-
ChainParamsHRP string `json:"chain_params_hrp"`
156-
AssetVersion uint8 `json:"asset_version"`
157-
AssetID string `json:"asset_id"`
158-
GroupKey string `json:"group_key"`
159-
ScriptKey string `json:"script_key"`
160-
InternalKey string `json:"internal_key"`
161-
TapscriptSibling string `json:"tapscript_sibling"`
162-
Amount uint64 `json:"amount"`
163-
ProofCourierAddr string `json:"proof_courier_addr"`
156+
Version uint8 `json:"version"`
157+
ChainParamsHRP string `json:"chain_params_hrp"`
158+
AssetVersion uint8 `json:"asset_version"`
159+
AssetID string `json:"asset_id"`
160+
GroupKey string `json:"group_key"`
161+
ScriptKey string `json:"script_key"`
162+
InternalKey string `json:"internal_key"`
163+
TapscriptSibling string `json:"tapscript_sibling"`
164+
Amount uint64 `json:"amount"`
165+
ProofCourierAddr string `json:"proof_courier_addr"`
166+
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
164167
}
165168

166169
func (ta *TestAddress) ToAddress(t testing.TB) *Tap {
@@ -218,6 +221,7 @@ func (ta *TestAddress) ToAddress(t testing.TB) *Tap {
218221
InternalKey: *test.ParsePubKey(t, ta.InternalKey),
219222
Amount: ta.Amount,
220223
ProofCourierAddr: *proofCourierAddr,
224+
UnknownOddTypes: ta.UnknownOddTypes,
221225
}
222226

223227
if ta.GroupKey != "" {

address/records.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/btcsuite/btcd/btcec/v2"
88
"github.com/lightninglabs/taproot-assets/asset"
99
"github.com/lightninglabs/taproot-assets/commitment"
10+
"github.com/lightninglabs/taproot-assets/fn"
1011
"github.com/lightningnetwork/lnd/tlv"
1112
)
1213

@@ -43,6 +44,14 @@ const (
4344
addrProofCourierAddrType addressTLVType = 12
4445
)
4546

47+
// KnownAddressTypes is a set of all known address TLV types. This set is
48+
// asserted to be complete by a check in the BIP test vector unit tests.
49+
var KnownAddressTypes = fn.NewSet(
50+
addrVersionType, addrAssetVersionType, addrAssetIDType,
51+
addrGroupKeyType, addrScriptKeyType, addrInternalKeyType,
52+
addrTapscriptSiblingType, addrAmountType, addrProofCourierAddrType,
53+
)
54+
4655
func newAddressVersionRecord(version *Version) tlv.Record {
4756
return tlv.MakeStaticRecord(
4857
addrVersionType, version, 1, VersionEncoder, VersionDecoder,

address/testdata/address_tlv_encoding_generated.json

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
1212
"tapscript_sibling": "",
1313
"amount": 5577006791947779410,
14-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
14+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
15+
"unknown_odd_types": null
1516
},
1617
"expected": "taprt1qqqsqqspqqzzq73cz93shve4q0r9xmp6yg7netktj0l9ta9ngw2j3m0j0vgd8r5nqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl7nt9sgss0l8a2gxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpn5zn5dz",
1718
"comment": "valid regtest address"
@@ -27,7 +28,8 @@
2728
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
2829
"tapscript_sibling": "",
2930
"amount": 2933568871211445515,
30-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
31+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
32+
"unknown_odd_types": null
3133
},
3234
"expected": "tapsb1qqqsqqspqqzzp4e0kxcehcfvha3z8k22htlltwylltzvlkd780vls92kchwd0suhqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl729ky9v8evadpvxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpn76z4sn",
3335
"comment": "valid simnet address"
@@ -43,7 +45,8 @@
4345
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
4446
"tapscript_sibling": "",
4547
"amount": 1905388747193831650,
46-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
48+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
49+
"unknown_odd_types": null
4750
},
4851
"expected": "taptb1qqqsqqspqqzzqny6jwx754yjgnnxwcaff3s9ey6fdztq7c6wrz3fagw0e4h9c9uhqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl7xn3fnuxhq7sugxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnfaa7x2",
4952
"comment": "valid testnet address"
@@ -59,7 +62,8 @@
5962
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
6063
"tapscript_sibling": "",
6164
"amount": 1598098976185383115,
62-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
65+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
66+
"unknown_odd_types": null
6367
},
6468
"expected": "tapbc1qqqszqspqqzzpwyapvrhaedr5cpufmfk0hzyxvyzy3a3wphzv8gu5g4z6hhqvcdpqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl793dj6xn7u0cevxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnyqawrf",
6569
"comment": "valid mainnet address"
@@ -75,7 +79,8 @@
7579
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
7680
"tapscript_sibling": "",
7781
"amount": 898860202204764712,
78-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
82+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
83+
"unknown_odd_types": null
7984
},
8085
"expected": "tapbc1qqqszqspqyzzp54v3a42v0nd8c2lwwqdhk7zd4sgqgg4a5lwmtf94aqcekqlmcyhqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl7rrevjtk7f569qxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnk2wtnl",
8186
"comment": "valid addr, v1 asset version"
@@ -91,7 +96,8 @@
9196
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
9297
"tapscript_sibling": "",
9398
"amount": 1,
94-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
99+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
100+
"unknown_odd_types": null
95101
},
96102
"expected": "taptb1qqqsqqspqqzzq7wzuajcy98j9tu0z46qqp36vv0vf78gtpv62s4jp5p39z7acx2zq5ssynpnh0gc9tp97pwz4ecljxc9u9xc8jeg4wuyt7c9gsg6kt5wuzjyqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgqszrp2dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xses8qwtmu",
97103
"comment": "signet group collectible"
@@ -107,7 +113,8 @@
107113
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
108114
"tapscript_sibling": "",
109115
"amount": 1,
110-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
116+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
117+
"unknown_odd_types": null
111118
},
112119
"expected": "tapsb1qqqsqqspqqzzpv59k6j6mjj4e27a5rcjq3p8uzmz8rq2xyvktj4g0nk2epym5akzqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgqszrp2dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xsesxc04ee",
113120
"comment": "simnet collectible"
@@ -123,10 +130,30 @@
123130
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
124131
"tapscript_sibling": "00c0126e6f7420612076616c696420736372697074",
125132
"amount": 1,
126-
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
133+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
134+
"unknown_odd_types": null
127135
},
128136
"expected": "tapsb1qqqsqqspqqzzqq2wt629x8jps985w5jaz69h9x22pqqstk4nctlgna8vuun7qej6qcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0py2spsqjdehhggrpypmxzmrfvss8xcmjd9c8gzspqyxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpn0dwlms",
129137
"comment": "simnet collectible with sibling"
138+
},
139+
{
140+
"address": {
141+
"version": 1,
142+
"chain_params_hrp": "tapsb",
143+
"asset_version": 0,
144+
"asset_id": "0e3551778a29c399050bd495e38fc1c13ff200777972e69db6c84e670b406985",
145+
"group_key": "",
146+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
147+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
148+
"tapscript_sibling": "00c0126e6f7420612076616c696420736372697074",
149+
"amount": 1,
150+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
151+
"unknown_odd_types": {
152+
"31337": "Zm9v"
153+
}
154+
},
155+
"expected": "tapsb1qqqszqspqqzzqr3429mc52wrnyzsh4y4uw8ursfl7gq8w7tju6wmdjzwvu95q6v9qcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0py2spsqjdehhggrpypmxzmrfvss8xcmjd9c8gzspqyxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnl4axjqmxdahsazt7x4",
156+
"comment": "simnet collectible with sibling and unknown TLV type"
130157
}
131158
],
132159
"error_test_cases": null

0 commit comments

Comments
 (0)