-
Notifications
You must be signed in to change notification settings - Fork 224
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
Serialization improvements #248
Conversation
…ializers into module
timestamp: Time, | ||
/// Signature of vote | ||
signature: Signature, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Representing this as an enum is a nice idea 👍
tendermint/src/block/commit_sig.rs
Outdated
} | ||
} | ||
|
||
deserializer.deserialize_map(CommitSigVisitor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the enum idea is cool, it seems to come with the cost of a rather complicated Deserialize
impl for commitSig
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and that's the tradeoff @brapse was going to go for in an earlier discussion. (Feel free to engage Sean.) The idea is that serialization is not actually the core product here and during the usual development it's just annoying to do something some particular way just because serialization says so.
This approach puts the logic of making sure that the incoming data is deserialized properly (with some basic validation) and the outgoing data is serialized properly in the serialization "layer" and the internal workings of the application can become completely independent from it. For example it can make use of the additional features of Rust better.
Just to highlight it even more: if/when the Tendermint data model changes because the Go + Rust teams decide on some other implementation, the changes can be implemented in this layer without affecting the application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit there is one not-completely-Rust-native-best-practice feature in the code and it annoys me slightly. I deserialize the incoming CommitSig into an internal CommitSigSpec structure that is defined in ADR-025. I could have gone hard-core and implement complete deserialization while parsing the incoming text. But that would be even more convoluted and harder to change down the road. I feel this is a good compromise that allows us to easily update the deserialization code, if a new ADR comes by.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and that's the tradeoff @brapse was going to go for in an earlier discussion. (Feel free to engage Sean.) The idea is that serialization is not actually the core product here and during the usual development it's just annoying to do something some particular way just because serialization says so.
That is absolutely reasonable. Serialization isn't the core product and we shouldn't build our core types around it. All in favour of decoupling serialization concerns and core types (I like this approach for this: #197 (comment) by @tarcieri ).
What I was referring to was that the seemingly beautiful change made the part where the serialization actually happens difficult to read and quite complex. A simpler way might be to have a serialization type and a From
/ Into
for that and the actual type. This is very similar to what you are already doing with the CommitSigSpec
type but simpler to parse for a human :-)
Also, it would be cool if the CommitSigSpec
would use an enum instead of an u8
for the block_id_flag. Then one wouldn't need to annotate the match arms with meaning (e.g. 1 => {...}
with // BlockIDFlagAbsent
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done and done: Today's commit moved things into serde's "from" annotation. So now, the code looks a bit cleaner, although it's not less code:
In the serializers, a new RawCommitSig type was introduced that deserializes from JSON and it looks very much like the definition in the ADR (including the enum for BlockIDFlags). Deserialization automatically invokes basic type-checking of the struct (like an u8 has to be an u8).
The Rust-native CommitSig type converts from this RawCommitSig type through it's "From" trait which holds the validation for the struct. This logic is more about "if this variable is set, than that other variable also has to have a value".
So the deserializer does type-check and the From trait does validation. My only grief is that type-checking happens in the serializers library while validation happens in the struct's library (in this case at CommitSig). Interestingly, this is similar to how it used to be, so I'm guessing the developers will be fine with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the code looks cleaner that is already worth it 👍 And I think it is OK for the core type to defines when it's valid (s.t. you can only construct a valid type).
Co-authored-by: Ismail Khoffi <[email protected]>
I'm going to look into the clippy messages and fix them. While I do that I also wanted to call out for @Shivani912 and ask her about some of the test-stable tests that are failing. (single_skip_commit_tests_verify and commit_tests_verify). I feel they are failing because the used JSON input is not compliant with the slightly stricter implementation this new CommitSig has: according to ADR-025, if a signature has block_id_flag = 1 (no vote cast), then there should be no signature and timestamp fields. Some of the JSON files have them, hence the above two tests fail. Shivani, I'm happy to fix those in the JSON files, but I'm worried that I'm not editing the source of truth. Are you still copying these files from your own repository? Is it ok if I update them here? |
20cf2d6
to
d1b33ff
Compare
@@ -34,7 +34,7 @@ mod rpc { | |||
async fn abci_info() { | |||
let abci_info = localhost_rpc_client().abci_info().await.unwrap(); | |||
|
|||
assert_eq!(&abci_info.version, "0.16.2"); | |||
assert_eq!(&abci_info.version, "0.17.0"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good, this will also close: #249
Just a remark regarding the JSON tests: some might fail because now basic validation is done while decoding the type (e.g. the relation between BlockIdFlag and absence or presence of certain fields). This is a good move (as we can fail earlier on invalid input) but might require changes in the JSON test because that would mean that some test-files won't parse anymore. Currently, they would parse correctly but fail later (when validation is done). This is a sign that the JSON tests might be to narrowly built around the golang version: there the JSON in a few cases might parse correctly and the validation would fail later (and this is implicitly expected as an error in the JSON test description). |
I'll fix this on the generator code and update the tests in this repo. But, looks like this should be reported for the the Go code. A Thanks for finding this @greg-szabo ! |
Yes, I'm going to report that on the next Tendermint call. The Go code doesn't comply with the ADR. |
A general remark: I think this PR could have better been 2, 3 or even 4 PRs:
|
8bc39cd
to
2a032ab
Compare
The failing tests (on stable) is because #248 (comment)
|
.github/workflows/rust.yml
Outdated
@@ -94,7 +94,7 @@ jobs: | |||
args: --all-features --no-fail-fast | |||
env: | |||
CARGO_INCREMENTAL: "0" | |||
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads" | |||
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=unwind -Zpanic_abort_tests' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not fix this on master, get that PR merged in quickly and merge master into this PR? It would benefit all other opened PRs in the meantime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a "one-branch" guy :D
Sure, that makes sense.
@greg-szabo Another note on An empty |
I thought they'd serialize it as a |
Regarding managing the PR: I was thinking on doing that but all the PRs would have depended on each other (or at least in some cases it would have). Is that ok? Also, the work is not done yet, I just chose a bigger milestone instead of small changes (which I'll try next time). There is a "huge" TODO in the middle of the serializers code that will refactor at least the Header struct and possibly the Block too. |
Nope: #196 (comment)
|
OK, but please do not add more to it at least:
It quickly becomes painful to review and also the quality of the review decreases. Some of the changes do not depend on each other, e.g.: (smaller things like) #248 (comment) and https://github.com/informalsystems/tendermint-rs/pull/248/files#r419026955, |
); | ||
} | ||
// TODO: deal with Time | ||
// see https://github.com/informalsystems/tendermint-rs/pull/196#discussion_r401027989 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was also a reminder about the time peculiarities :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sh*t, I completely missed that. :( Thanks for the thorough review!
Should I add the fix on this PR?
The fix as I see is accepting Nil commits with timestamp and checkinf if the timestamp is 0. If yes, all's well (but don't put the timestamp in the enum), if no, raise an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened issue #259 about this.
_ => None, | ||
/// CommitSig implementation | ||
impl CommitSig { | ||
/// Helper: Extract validator address, since it's always present (according to ADR-025) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really should clarify this first: #246 (comment)
I can very well be that adr isn't representing the full decision anymore, or, that it is slightly confusing nil and absent votes. Also: #196 (comment)
cc @melekes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created tracking issue #260 about this.
I suggest adding symmetric assertions to the unit tests, something like:
Or even better the decoded one is the same as original one if it implements
|
This is a great suggestion @yihuang. There is a little function introduced by @romac that could be used here: tendermint-rs/tendermint/src/test.rs Lines 4 to 10 in 7df19f8
|
An interesting way to do those "round trip" tests is with a tool like |
Ok, so I've broken out this PR into three smaller PRs as we discussed with Ismail. This one will be closed soon, I just want to save the additional things that came up into the issue (#247) until they are implemented/decided on. |
All right. Closing this PR.
|
#247 - Serialization refactor
#246 - CommitSig bug fix