near / nearcore

Reference client for NEAR Protocol
https://near.org
GNU General Public License v3.0
2.32k stars 623 forks source link

feat: Separate `jsonrpc` from rest of codebase, to prevent `rpc`s to breaking due to refactoring. #5516

Open pmnoxx opened 2 years ago

pmnoxx commented 2 years ago

We should refactor the code to separate jsonrpc from rest of the code base.

Steps 1: remove usage of serde from crates other than jsonrpc. serde is used mainly for Serialize/Deserialize of messages. serde is used in following creates, simply because jsonrpc needs to serialize to json, to read/write messages to network. By removing serde from those crates, we will be one step closer to cleaning jsonrpc

Step 2:

See https://github.com/near/nearcore/pull/5428#pullrequestreview-813563252

matklad commented 2 years ago

I'd make this even more fine-grained: conceptually, jsonrpc consists of two parts:

It's important to separate data type definitions into a separate crate. That way, if someone wants use the API using a different networking stack (eg, based on blocking IO), they get to re-use definitions of the JSON API iteslf, without brining in tokio and actix ecosystems.

I think the end state here is that we publish near_rpc_types crate which:

My suggested path there:

matklad commented 2 years ago

Also, from me personally: this is a hugely important bit of work which has multiplicative implications for both how effective are folks working at nearcore itself, as well as how robust is the ecosystem which builds on top of us. Kudos for spearheading the effort @pmnoxx !

matklad commented 2 years ago

cc @ChaoticTempest as one of the consures of RPC which cares a lot about Rust-level semver stability.

frol commented 2 years ago

removing serde from near-primitives

@pmnoxx That might be not that clear of a win since actually some other projects might use and serialize those. Given that we publish near-primitives now with a 0.x semver-breaking releases, it is fine if we introduce breaking change since users will still be able to use whatever version they used before and rarely they actually need to upgrade near-primitives.

I think the end state here is that we publish near_rpc_types crate...

Well, near-jsonrpc-primitives is exactly that crate, and indeed it should not depend on other nearcore crates by default and only optionally depend on nearcore crates to implement From trait for those.

frol commented 2 years ago

@miraclx FYI, I had a chat with @matklad and suggested that he collaborate with you on this near-jsonrpc-primitives refactoring. This has been on my radar for quite a while, and I feel we can make these small steps toward better granularity of nearcore project and finally get to the point where we are comfortable releasing near-jsonrpc-client-rs 1.0

matklad commented 2 years ago

Had a design discussion with @miraclx, the write up is here:

https://hackmd.io/WeCthH5LQqGrHiO1dxv6WA

The most important bit is next steps:

Not that "remove nearcore dependencies from jsonrpc-primitives" is not there -- we can do that later, after we just concentrate serialize impls there.

pmnoxx commented 2 years ago

removing serde from near-primitives

@pmnoxx That might be not that clear of a win since actually some other projects might use and serialize those. Given that we publish near-primitives now with a 0.x semver-breaking releases, it is fine if we introduce breaking change since users will still be able to use whatever version they used before and rarely they actually need to upgrade near-primitives.

I think the end state here is that we publish near_rpc_types crate...

Well, near-jsonrpc-primitives is exactly that crate, and indeed it should not depend on other nearcore crates by default and only optionally depend on nearcore crates to implement From trait for those.

To clarify, removing serde from near-primitives is not done for the purpose of improving compile time. As you noticed It's going to be needed to compile dependencies. However. those libraries use serde just, because jsonrpc needs them for purpose of doing message serialization/deserialization of rpc queries and responses. Removing of serde::Serialize / serde::Deserialize shows us that the types in those creates are not used by jsonrpc anymore.

pmnoxx commented 2 years ago

@matklad @frol @miraclx Ok, the part 1 is done. near-network only uses serde with test_features, which doesn't happen in production. near-network-primitives / near-client-primitives doesn't use serde.

There may be some other crates that do.

matklad commented 2 years ago

Some extra observation about view types here: https://github.com/near/nearcore/pull/6068#discussion_r786613250. TL;DR at least one *View type is stored in the database, which feels suspect:

https://github.com/near/nearcore/blob/dd8b28a7bdfdf035f3714a2edb7467881e431192/core/store/src/db.rs#L160-L164

bowenwang1996 commented 2 years ago

Some extra observation about view types here: #6068 (comment). TL;DR at least one *View type is stored in the database, which feels suspect:

https://github.com/near/nearcore/blob/dd8b28a7bdfdf035f3714a2edb7467881e431192/core/store/src/db.rs#L160-L164

@matklad #4159 :(

frol commented 2 years ago

This was not fully addressed, see https://github.com/near/nearcore/pull/6587#discussion_r876113324

matklad commented 2 years ago

Another approach we should consider is defining the API in language-agnostic way and generating Rust-side of the API from the spec, rather than defining the spec in terms of rust impl.

Here's a good example:

https://www.twilio.com/docs/openapi/generating-a-rust-client-for-twilios-api#setup

openapi-generator generate -g rust \
  -i https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_api_v2010.json \
  -o twilio-rust \

This pulls language-agnostic JSON API spec from github and generates a bunch Rust structs with serde from it.

This is going to be very labor intensive (we'd have to write the API spec essentially from scratch, reverse-engineering from current Rust structs), but should give us (and 3rd party developers!!) the best results. This also probably won't be incremental with respect to tactical refactors suggested above, so, if we do want to pursue this approach at some point, it makes sense to start that today.

I am not familiar with the API specification languages landscape, so I can't vouch for openapi in particular, though I have to admit that the above one-command snippet looks great!

I ... don't really understand why we didn't cover openapi-like approaches in https://hackmd.io/WeCthH5LQqGrHiO1dxv6WA

frol commented 2 years ago

OpenAPI gravitates towards REST-like APIs and it does not support sum-types (enums). We would need to redesign the API in order to make it OpenAPI-friendly (and developers-friendly as well), and while it has been a long-standing wish, we never had time to sit down and address it.