Skip to content

Commit

Permalink
Merge pull request #1037 from lightninglabs/tlv-carve-out
Browse files Browse the repository at this point in the history
TLV encoding: add forward-compatibility
  • Loading branch information
Roasbeef authored Aug 16, 2024
2 parents 0a64116 + d06206e commit 420f246
Show file tree
Hide file tree
Showing 31 changed files with 2,436 additions and 452 deletions.
23 changes: 21 additions & 2 deletions address/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ type Tap struct {
// ProofCourierAddr is the address of the proof courier that will be
// used to distribute related proofs for this address.
ProofCourierAddr url.URL

// UnknownOddTypes is a map of unknown odd types that were encountered
// during decoding. This map is used to preserve unknown types that we
// don't know of yet, so we can still encode them back when serializing.
// This enables forward compatibility with future versions of the
// protocol as it allows new odd (optional) types to be added without
// breaking old clients that don't yet fully understand them.
UnknownOddTypes tlv.TypeMap
}

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

return records
// Add any unknown odd types that were encountered during decoding.
return asset.CombineRecords(records, a.UnknownOddTypes)
}

// DecodeRecords provides all records known for an address for proper
Expand Down Expand Up @@ -414,7 +423,17 @@ func (a *Tap) Decode(r io.Reader) error {
if err != nil {
return err
}
return stream.DecodeP2P(r)

unknownOddTypes, err := asset.TlvStrictDecodeP2P(
stream, r, KnownAddressTypes,
)
if err != nil {
return err
}

a.UnknownOddTypes = unknownOddTypes

return nil
}

// EncodeAddress returns a bech32m string encoding of a Taproot Asset address.
Expand Down
85 changes: 85 additions & 0 deletions address/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/internal/test"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -361,6 +362,29 @@ func TestAddressEncoding(t *testing.T) {
},
err: nil,
},
{
name: "simnet collectible with sibling and unknown " +
"TLV type",
f: func() (*Tap, string, error) {
addr, _, err := randEncodedAddress(
t, &SimNetTap, false, true,
asset.Collectible,
)

if err != nil {
return nil, "", err
}

foo := []byte("foo")
addr.UnknownOddTypes = tlv.TypeMap{
test.TestVectorAllowedUnknownType: foo,
}

str, err := addr.EncodeAddress()
return addr, str, err
},
err: nil,
},
{
name: "unsupported hrp",
f: func() (*Tap, string, error) {
Expand Down Expand Up @@ -492,6 +516,24 @@ func runBIPTestVector(t *testing.T, testVectors *TestVectors) {

areEqual := validCase.Expected == addrString

// Make sure the address in the test vectors doesn't use
// a record type we haven't marked as known/supported
// yet. If the following check fails, you need to update
// the KnownAddressTypes set.
for _, record := range a.EncodeRecords() {
// Test vectors may contain this one type to
// demonstrate that it is not rejected.
if record.Type() ==
test.TestVectorAllowedUnknownType {

continue
}

require.Contains(
tt, KnownAddressTypes, record.Type(),
)
}

// Create nice diff if things don't match.
if !areEqual {
chainParams, err := a.Net()
Expand Down Expand Up @@ -544,3 +586,46 @@ func FuzzAddressDecode(f *testing.F) {
_ = a.Decode(bytes.NewReader(data))
})
}

// TestAddressUnknownOddType tests that an unknown odd type is allowed in an
// address and that we can still arrive at the correct leaf hash with it.
func TestAddressUnknownOddType(t *testing.T) {
knownAddr, _, _ := RandAddr(t, &TestNet3Tap, RandProofCourierAddr(t))
knownAddrString, err := knownAddr.EncodeAddress()
require.NoError(t, err)

test.RunUnknownOddTypeTest(
t, knownAddr.Tap, &asset.ErrUnknownType{},
func(buf *bytes.Buffer, addr *Tap) error {
return addr.Encode(buf)
},
func(buf *bytes.Buffer) (*Tap, error) {
parsedAddr := &Tap{
ChainParams: &TestNet3Tap,
}
return parsedAddr, parsedAddr.Decode(buf)
},
func(parsedAddr *Tap, unknownTypes tlv.TypeMap) {
require.Equal(
t, unknownTypes, parsedAddr.UnknownOddTypes,
)

// The address should've changed, to make sure the
// unknown value was taken into account when creating
// the serialized address.
parsedAddrString, err := parsedAddr.EncodeAddress()
require.NoError(t, err)

require.NotEqual(t, knownAddrString, parsedAddrString)

parsedAddr.UnknownOddTypes = nil

// The genesis information isn't actually encoded in the
// address, so we need to clear that out before
// comparing.
knownAddr.Tap.assetGen = asset.Genesis{}

require.Equal(t, knownAddr.Tap, parsedAddr)
},
)
}
24 changes: 14 additions & 10 deletions address/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/internal/test"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -135,6 +136,7 @@ func NewTestFromAddress(t testing.TB, a *Tap) *TestAddress {
InternalKey: test.HexPubKey(&a.InternalKey),
Amount: a.Amount,
ProofCourierAddr: a.ProofCourierAddr.String(),
UnknownOddTypes: a.UnknownOddTypes,
}

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

type TestAddress struct {
Version uint8 `json:"version"`
ChainParamsHRP string `json:"chain_params_hrp"`
AssetVersion uint8 `json:"asset_version"`
AssetID string `json:"asset_id"`
GroupKey string `json:"group_key"`
ScriptKey string `json:"script_key"`
InternalKey string `json:"internal_key"`
TapscriptSibling string `json:"tapscript_sibling"`
Amount uint64 `json:"amount"`
ProofCourierAddr string `json:"proof_courier_addr"`
Version uint8 `json:"version"`
ChainParamsHRP string `json:"chain_params_hrp"`
AssetVersion uint8 `json:"asset_version"`
AssetID string `json:"asset_id"`
GroupKey string `json:"group_key"`
ScriptKey string `json:"script_key"`
InternalKey string `json:"internal_key"`
TapscriptSibling string `json:"tapscript_sibling"`
Amount uint64 `json:"amount"`
ProofCourierAddr string `json:"proof_courier_addr"`
UnknownOddTypes tlv.TypeMap `json:"unknown_odd_types"`
}

func (ta *TestAddress) ToAddress(t testing.TB) *Tap {
Expand Down Expand Up @@ -218,6 +221,7 @@ func (ta *TestAddress) ToAddress(t testing.TB) *Tap {
InternalKey: *test.ParsePubKey(t, ta.InternalKey),
Amount: ta.Amount,
ProofCourierAddr: *proofCourierAddr,
UnknownOddTypes: ta.UnknownOddTypes,
}

if ta.GroupKey != "" {
Expand Down
9 changes: 9 additions & 0 deletions address/records.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightningnetwork/lnd/tlv"
)

Expand Down Expand Up @@ -43,6 +44,14 @@ const (
addrProofCourierAddrType addressTLVType = 12
)

// KnownAddressTypes is a set of all known address TLV types. This set is
// asserted to be complete by a check in the BIP test vector unit tests.
var KnownAddressTypes = fn.NewSet(
addrVersionType, addrAssetVersionType, addrAssetIDType,
addrGroupKeyType, addrScriptKeyType, addrInternalKeyType,
addrTapscriptSiblingType, addrAmountType, addrProofCourierAddrType,
)

func newAddressVersionRecord(version *Version) tlv.Record {
return tlv.MakeStaticRecord(
addrVersionType, version, 1, VersionEncoder, VersionDecoder,
Expand Down
43 changes: 35 additions & 8 deletions address/testdata/address_tlv_encoding_generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 5577006791947779410,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "taprt1qqqsqqspqqzzq73cz93shve4q0r9xmp6yg7netktj0l9ta9ngw2j3m0j0vgd8r5nqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl7nt9sgss0l8a2gxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpn5zn5dz",
"comment": "valid regtest address"
Expand All @@ -27,7 +28,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 2933568871211445515,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "tapsb1qqqsqqspqqzzp4e0kxcehcfvha3z8k22htlltwylltzvlkd780vls92kchwd0suhqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl729ky9v8evadpvxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpn76z4sn",
"comment": "valid simnet address"
Expand All @@ -43,7 +45,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 1905388747193831650,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "taptb1qqqsqqspqqzzqny6jwx754yjgnnxwcaff3s9ey6fdztq7c6wrz3fagw0e4h9c9uhqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl7xn3fnuxhq7sugxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnfaa7x2",
"comment": "valid testnet address"
Expand All @@ -59,7 +62,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 1598098976185383115,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "tapbc1qqqszqspqqzzpwyapvrhaedr5cpufmfk0hzyxvyzy3a3wphzv8gu5g4z6hhqvcdpqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl793dj6xn7u0cevxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnyqawrf",
"comment": "valid mainnet address"
Expand All @@ -75,7 +79,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 898860202204764712,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "tapbc1qqqszqspqyzzp54v3a42v0nd8c2lwwqdhk7zd4sgqgg4a5lwmtf94aqcekqlmcyhqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgyl7rrevjtk7f569qxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnk2wtnl",
"comment": "valid addr, v1 asset version"
Expand All @@ -91,7 +96,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "taptb1qqqsqqspqqzzq7wzuajcy98j9tu0z46qqp36vv0vf78gtpv62s4jp5p39z7acx2zq5ssynpnh0gc9tp97pwz4ecljxc9u9xc8jeg4wuyt7c9gsg6kt5wuzjyqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgqszrp2dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xses8qwtmu",
"comment": "signet group collectible"
Expand All @@ -107,7 +113,8 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "tapsb1qqqsqqspqqzzpv59k6j6mjj4e27a5rcjq3p8uzmz8rq2xyvktj4g0nk2epym5akzqcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pgqszrp2dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xsesxc04ee",
"comment": "simnet collectible"
Expand All @@ -123,10 +130,30 @@
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "00c0126e6f7420612076616c696420736372697074",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": null
},
"expected": "tapsb1qqqsqqspqqzzqq2wt629x8jps985w5jaz69h9x22pqqstk4nctlgna8vuun7qej6qcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0py2spsqjdehhggrpypmxzmrfvss8xcmjd9c8gzspqyxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpn0dwlms",
"comment": "simnet collectible with sibling"
},
{
"address": {
"version": 1,
"chain_params_hrp": "tapsb",
"asset_version": 0,
"asset_id": "0e3551778a29c399050bd495e38fc1c13ff200777972e69db6c84e670b406985",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "00c0126e6f7420612076616c696420736372697074",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443",
"unknown_odd_types": {
"31337": "Zm9v"
}
},
"expected": "tapsb1qqqszqspqqzzqr3429mc52wrnyzsh4y4uw8ursfl7gq8w7tju6wmdjzwvu95q6v9qcss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0pqss9g90avt97rkrdzqtdrst427e4kwx9lg6dx4fnz7rp6drgcszupu0py2spsqjdehhggrpypmxzmrfvss8xcmjd9c8gzspqyxz56rpwd5x6ctfdsaz7tmjv9hxgtngv9eksmtpd9kzuurjdahkvtnrda6hy6t9wgargdpnl4axjqmxdahsazt7x4",
"comment": "simnet collectible with sibling and unknown TLV type"
}
],
"error_test_cases": null
Expand Down
Loading

0 comments on commit 420f246

Please sign in to comment.