Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLV encoding: add forward-compatibility #1037

Merged
merged 11 commits into from
Aug 16, 2024
Merged

TLV encoding: add forward-compatibility #1037

merged 11 commits into from
Aug 16, 2024

Conversation

guggero
Copy link
Member

@guggero guggero commented Jul 19, 2024

Replaces #557.

Fixes #997.

With this PR we add forward-compatibility that will allow us in the future to add new optional (odd-numbered) TLV types to any of our TLV structs without breaking old clients.
This will allow us to implement #956 in the future, once everyone has upgraded to v0.4.1 (or whichever version this lands in).

I opted to not go with any version increases, since this doesn't really change anything in the current behavior yet...


This change is Reviewable

@guggero
Copy link
Member Author

guggero commented Jul 26, 2024

Rebased.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid approach!

No major comments, other than one re how we can de-risk enforcing strict decode in a short time horizon.

Also need to circle back re the interaction with the test vectors and the new type constants, as well as some of the forward compat unit tests.

asset/records.go Show resolved Hide resolved
asset/records.go Show resolved Hide resolved
asset/asset.go Outdated
@@ -2000,7 +2000,8 @@ func (a *Asset) Decode(r io.Reader) error {
if err != nil {
return err
}
return stream.Decode(r)

return TlvStrictDecode(stream, r, KnownAssetLeafTypes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think to be slightly more conservative, we should put this behind a flag that's disabled for now. This protects us in the off chain that someone is using a modified version of the daemon with their own custom flag, or is using a fresh implementation.

We could check for this in the wild by running the new decode method on the existing assets in proofs uploaded to our Universe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But wouldn't that break the intended forward compatibility?
Isn't the intention that when we ship v0.4.2 with this code enabled (e.g. no flag, allow new unknown odd types) that we could then with v0.5.0 add new fields with odd types, being backward compatible to v0.4.2?
With the intention that users can remain on v0.4.2 without their nodes complaining when syncing new proofs created with v0.5.0. If they first have to turn on a flag to allow that, doesn't that kind of defeat the purpose?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related, a useful itest could be that, if a proof is created and then extended with an unknown odd field, checking that a client could successfully store that proof (via local Universe, DB, or flat file), and then return the identical proof to another client.

asset/asset.go Outdated
return err
}

a.unknownOddTypes = unknownOddTypes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dig the implementation!

One gap (still getting through the PR) that may pop up is when we encode a protocol object within the database as a fully featured row vs encoding an encoded blob.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that what we do for most fields?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wouldn't actually store the information in the unknown types. Just keep it in memory so we can validate correctly (e.g. arrive at the correct leaf hash for assets or the correct meta hash for meta reveals).
Only when you upgrade to the version that implements the new value, would we store it, with the corresponding database migration. But then it would be a known type and the value wouldn't end up in unknownOddTypes anymore.

asset/records.go Show resolved Hide resolved
asset/asset.go Show resolved Hide resolved
Copy link
Contributor

@jharveyb jharveyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall! I think it could be useful to have some of this exercised in itest, especially for the composite / nested types like proofs.

asset/records.go Outdated Show resolved Hide resolved
asset/records.go Outdated Show resolved Hide resolved
asset/records.go Show resolved Hide resolved
asset/records.go Show resolved Hide resolved
asset/records.go Show resolved Hide resolved
asset/records.go Show resolved Hide resolved
address/address_test.go Outdated Show resolved Hide resolved
asset/records_test.go Show resolved Hide resolved
asset/records_test.go Outdated Show resolved Hide resolved
proof/records.go Show resolved Hide resolved
@lightninglabs-deploy
Copy link

@guggero, remember to re-request review from reviewers when ready

@guggero
Copy link
Member Author

guggero commented Aug 9, 2024

Will get back to this PR next week.

@guggero
Copy link
Member Author

guggero commented Aug 14, 2024

Also need to circle back re the interaction with the test vectors and the new type constants, as well as some of the forward compat unit tests.

Will look into that tomorrow, but I think for now this is ready for a re-review.

@coveralls
Copy link

coveralls commented Aug 14, 2024

Pull Request Test Coverage Report for Build 10421878251

Details

  • 284 of 294 (96.6%) changed or added relevant lines in 14 files are covered.
  • 15 unchanged lines in 4 files lost coverage.
  • Overall coverage increased (+0.3%) to 40.402%

Changes Missing Coverage Covered Lines Changed/Added Lines %
asset/asset.go 10 14 71.43%
asset/records.go 54 60 90.0%
Files with Coverage Reduction New Missed Lines %
tapdb/addrs.go 2 79.04%
commitment/tap.go 2 84.43%
asset/asset.go 3 81.35%
universe/interface.go 8 50.22%
Totals Coverage Status
Change from base Build 10408357404: 0.3%
Covered Lines: 23914
Relevant Lines: 59190

💛 - Coveralls

Copy link
Contributor

@jharveyb jharveyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Left some comment in existing threads on itest behavior we may want to get better assurances on forwards-compat.

Some of the unit tests already extend data from the BIP test vectors, so I'm not sure if we want/need to add data with unknown types as test vectors themselves.

internal/test/generic.go Outdated Show resolved Hide resolved
internal/test/generic.go Outdated Show resolved Hide resolved
asset/records_test.go Outdated Show resolved Hide resolved
address/address_test.go Outdated Show resolved Hide resolved
This commit makes it possible for the protocol to add new odd (optional)
types in the future without old clients breaking.
By parsing and keeping any left-over unknown odd types, then re-encoding
them when calculating the commitment leaf, old software can still
correctly use and validate an asset, even if it doesn't fully understand
all of its (optional) new fields.
@guggero
Copy link
Member Author

guggero commented Aug 16, 2024

I've added test vectors and an integration test.

@guggero guggero requested a review from jharveyb August 16, 2024 15:36
Copy link
Contributor

@jharveyb jharveyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! With the itest, I'm convinced that this should work out.

Since that includes proof decode with an unknown type, I don't think there's particular value on running this over the proofs of our testnet and mainnet universes. IIUC any unknown types should have been dropped when those servers were storing the proofs anyway.

567: []byte("it's committed"),
}
cp.TaprootAssetProof.UnknownOddTypes = tlv.TypeMap{
789: []byte("there's assets in here..."),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😆

// there.
require.Equal(t.t, modifiedBlob, transferProof2.RawProofFile)
require.True(t.t, bytes.Contains(modifiedBlob, []byte("it's included")))
require.True(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@Roasbeef Roasbeef requested a review from jharveyb August 16, 2024 23:23
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 18 of 18 files at r1, 28 of 28 files at r2, all commit messages.
Reviewable status: all files reviewed, 23 unresolved discussions (waiting on @guggero and @jharveyb)


commitment/commitment_test.go line 1707 at r2 (raw file):

	var knownProofBytes []byte
	test.RunUnknownOddTypeTest(

Nice generalization 👍

@Roasbeef Roasbeef merged commit 420f246 into main Aug 16, 2024
17 checks passed
@guggero guggero deleted the tlv-carve-out branch August 19, 2024 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

[feature]: Implement "Asset TLV Placeholder" Design for Decimal Display
6 participants