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

RPC support for CometBFT 0.38 #1317

Merged
merged 30 commits into from
Jun 14, 2023
Merged

RPC support for CometBFT 0.38 #1317

merged 30 commits into from
Jun 14, 2023

Conversation

mzabaluev
Copy link
Contributor

@mzabaluev mzabaluev commented May 19, 2023

The RPC changes for #1301

  • Referenced an issue explaining the need for the change
  • Updated all relevant documentation in docs
  • Updated all code comments where relevant
  • Wrote tests
  • Added entry in .changelog/

In the v0_37+ dialect helper, support the new finalize_block_events
field if it is present. Also default the begin_block_events and
end_block_events fields to None if they are not present.
For abci::Event and abci::response::CheckTx, restore the Deserialize
implementation on domain types themselves.

The event serialization format as used in 0.37+ will be established as
the "canonical" serialization.
Add the new fields and types without static differentiation for 0.38.x.

Use the domain type ExecTxResult in deserializing the endpoint response types in the current (i.e. 0.37+) dialect for these endpoints:
- /block_results
- /broadcast_tx_commit
- /tx
- /tx_commit

The Deserialize impls are restored on the response types,
corresponding to the JSON schema for the current RPC dialect.

Simplify the 0.34 dialect support for these responses, no longer using
generics.

In endpoint::tx_commit::Response, rename the deliver_tx field to
tx_result to correspond to the current JSON schema. The Deserialize
impl falls back to deliver_tx to also be able to parse 0.37 responses.
@mzabaluev mzabaluev added rpc domain-types Anything relating to the creation, modification or removal of domain types labels May 19, 2023
@mzabaluev mzabaluev requested a review from romac May 19, 2023 17:03
Some serde attributes were not migrated onto the ExecTxResult
domain type when it has been tasked with deserializing the latest
RPC dialect.
On abci::response::CheckTx, add serde(default) on fields that are
no longer present in CometBFT 0.38.
If the finalize_block_events is not present, default to an empty vector.
Conversely, skip the field from serializing if it's set to an
empty vector.
BeginBlock, EndBlock, FinalizeBlock are used to deserialize
RPC responses.
Now that event data can have new fields, distinguish this with
a new NewBlock enum variant, renaming the old one to LegacyNewBlock.

The dialect serialization helpers for subscription events are
separated to v0_34::DialectEvent, that does not recognize the
0.38 fields at all and parses the event attributes as base64-encoded,
and latest::DialectEvent, which has provisions for 0.38 fields if they
are present, and in this case converts to the NewBlock variant.
@codecov-commenter
Copy link

codecov-commenter commented May 25, 2023

Codecov Report

Merging #1317 (e6e0285) into main (dba8242) will increase coverage by 1.6%.
The diff coverage is 88.2%.

❗ Current head e6e0285 differs from pull request most recent head 18ccbcc. Consider uploading reports for the commit 18ccbcc to get more accurate results

@@           Coverage Diff           @@
##            main   #1317     +/-   ##
=======================================
+ Coverage   58.1%   59.8%   +1.6%     
=======================================
  Files        281     283      +2     
  Lines      25478   26895   +1417     
=======================================
+ Hits       14824   16093   +1269     
- Misses     10654   10802    +148     
Impacted Files Coverage Δ
rpc/src/client/bin/main.rs 0.3% <0.0%> (-0.1%) ⬇️
rpc/src/dialect/v0_37.rs 0.0% <ø> (-71.8%) ⬇️
rpc/src/response.rs 59.5% <ø> (ø)
rpc/tests/kvstore_fixtures.rs 100.0% <ø> (ø)
rpc/src/endpoint/broadcast/tx_commit.rs 42.3% <55.5%> (-16.6%) ⬇️
rpc/src/event.rs 55.0% <59.7%> (+5.8%) ⬆️
rpc/tests/kvstore_fixtures/v0_37.rs 93.9% <85.0%> (+<0.1%) ⬆️
rpc/tests/kvstore_fixtures/v0_34.rs 98.2% <87.8%> (-0.1%) ⬇️
rpc/tests/kvstore_fixtures/v0_38.rs 93.8% <93.8%> (ø)
rpc/src/client/transport/websocket.rs 64.4% <98.7%> (+2.3%) ⬆️
... and 15 more

... and 5 files with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

For consistency, define event::v0_37 and event::v0_38, aliasing
the public definitions in event::latest.
@mzabaluev mzabaluev marked this pull request as ready for review May 29, 2023 14:09
Copy link
Member

@romac romac left a comment

Choose a reason for hiding this comment

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

Amazing work @mzabaluev! I just have some questions which I've left inline.

@@ -49,6 +49,7 @@ impl CompatMode {
match (version.major, version.minor) {
(0, 34) => Ok(CompatMode::V0_34),
(0, 37) => Ok(CompatMode::V0_37),
(0, 38) => Ok(CompatMode::V0_37),
Copy link
Member

Choose a reason for hiding this comment

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

This seems a bit counterintuitive, though I guess if the RPC client does not differentiate between 0.37 and 0.38 it makes sense.

I wonder though if we could rather add a CompatMode variant for 0.38 and then change the logic at use site to check whether the compat mode is set to 0.3X or higher on case-per-case basis, eg. with compat_mode >= CompatMode::v0_37 for when we want to check if the version is higher or equal to v0.37.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, the RPC "dialect" does not change enough between 0.37 and 0.38 that we have to specially switch anything in order to talk to any node built on either of these versions. Naming could be improved; I tried to play with the Latest compact mode name in the past, perhaps anticipating this ambiguity.

///
/// The JSON field carrying this data is named `deliver_tx` in
/// CometBFT versions before 0.38.
#[serde(alias = "deliver_tx")]
Copy link
Member

Choose a reason for hiding this comment

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

Let's perhaps add a comment to the whole struct to specify why we cannot derive Serialize here (ie. because of the field name change between <0.38 and >=0.38.

Copy link
Contributor Author

@mzabaluev mzabaluev Jun 1, 2023

Choose a reason for hiding this comment

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

But we do derive Serialize on the struct, and it will serialize to the latest RPC schema, 0.38.
The DialectResponse from the submodule can be used to serialize in the 0.34 schema.

And thanks for bringing this up: this means we have nothing to serialize with for 0.37 JSON-RPC if anyone wants to. The RPC client does not use Serialize for responses, so I've been treating it as an afterthought. Maybe there should be another compatibility submodule just to enable this.

rpc/src/event.rs Outdated
gas_used: msg.gas_used,
events: msg.events.into_iter().map(Into::into).collect(),
/// Serialization helpers for the RPC protocol used since CometBFT 0.37
pub mod latest {
Copy link
Member

Choose a reason for hiding this comment

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

Rather than have the latest module define directly items for the latest supported release, can we instead name it with the version for that release and have the latest module re-exports everything from that module?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The helpers in this module are actually a grab bag for deserializing anything that might be present in 0.37 or 0.38.
On the other hand, the Serialize impl can also emit any fields that are not set to None, which is rather sloppy.

I'll think on how to improve this in a more principled way.

pub consensus_param_updates: Option<consensus::Params>,
// FIXME: decide if base64 is the canonical serialization for AppHash,
Copy link
Member

Choose a reason for hiding this comment

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

Just a note that there is a remaining FIXME here

Copy link
Contributor Author

@mzabaluev mzabaluev Jun 1, 2023

Choose a reason for hiding this comment

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

Herein lies a question: do we want a "normal" way to serialize AppHash, even though in the current protocol there are instances of both hex and base64 encoding? I've asked this on Slack, but nobody provided an answer.

#[derive(Debug, Clone, PartialEq, Eq)]
// To be fixed in 0.24
#[allow(clippy::large_enum_variant)]
pub enum EventData {
Copy link
Member

Choose a reason for hiding this comment

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

Have we considered making EventData part of the dialect rather than introduce a legacy variant for NewBlock?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought of making the NewBlock variant in this API type carry both 0.37 and 0.38 fields as optionals just like in latest::DialectEventData, and decoding to a different set of fields getting Some values for each respective version, but this feels like we'd be ramifying the enum matching that typically needs to be done by the consumer across multiple levels of the data structure.
So I decided a more clean split of the old and the new data is needed, also because we should eventually deprecate and remove LegacyNewBlock when everything up to 0.37 is EOLd.

Or do you have another idea about the API for this? Consider the dialect stuff in submodules an implementation detail for decoding JSON-RPC; I think the whole dialect business should be made private, the only reason I haven't made it private is the "integration" tests that decode fixtures, but those more properly belong in unit tests.

NB: Hermes currently does not care about the changed fields at all, it just needs block as can be seen in informalsystems/hermes#3372.

The Serialize impl needs to be implemented differently for
0.37 and 0.38 in order to emit old or new fields for NewBlock.
The Deserialize impl, however, flexibly handles both formats to avoid
introducing another dialect where most other helpers would be
identical. This means that Event gets two different helpers for each
protocol version: DeEvent for Deserialize and SerEvent for Serialize.
Provide v0_37::DialectResponse as a way to serialize responses
in the 0.37 JSON format.
Use the exact protocol version to serialize test server responses as
appropriate. No new variant is added to CompatMode, as that is a
client-side setting and the client is able to cope with both versions
dynamically.
Comment on lines +1040 to +1044
enum TestRpcVersion {
V0_34,
V0_37,
V0_38,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's almost like CompatMode could be given the new V0_38 variant as well, but it's avoidable on the client side and I think it would result in lots of unnecessary code duplication.

No opinions have been expressed about any canonical way to serialize
app hashes, so the explicit serializer helper should be there for now.
@mzabaluev mzabaluev merged commit 52f3d8d into main Jun 14, 2023
@mzabaluev mzabaluev deleted the mikhail/cometbft-rpc-0.38 branch June 14, 2023 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain-types Anything relating to the creation, modification or removal of domain types rpc
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants