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

Split RPCs into a separate crate #1910

Merged
merged 36 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c4cdedb
WIP extract RPCs into separate crate
jsdw Jan 27, 2025
467a5ec
fmt
jsdw Jan 27, 2025
d0943e6
Fix test
jsdw Jan 27, 2025
7b67618
Remove unused deps
jsdw Jan 27, 2025
4576c47
fix import
jsdw Jan 28, 2025
b0f9c9d
WIP: Fix up errors and most tests. Start extracintg some tests/code t…
jsdw Jan 29, 2025
bb0096e
MockRpcClient sync or async
jsdw Jan 29, 2025
a371df4
MockRpcClient only async but better type inference
jsdw Jan 29, 2025
432a3e6
Merge branch 'master' into jsdw-subxt-rpcs
jsdw Jan 30, 2025
a92ebcd
WIP MockRpcClient FnMuts and some test updates to use it
jsdw Feb 3, 2025
6e641dd
Get all but one test working with new MockRpcClient
jsdw Feb 3, 2025
d1c243d
WIP trying to debug failure
jsdw Feb 4, 2025
a78005b
Merge branch 'master' into jsdw-subxt-rpcs
jsdw Feb 6, 2025
141608f
WIP, Tests mostly fixed, need to add back oen more
jsdw Feb 6, 2025
afc7048
Get mock RPC tests working
jsdw Feb 7, 2025
ed1a054
fmt
jsdw Feb 7, 2025
6c335c9
fmt
jsdw Feb 7, 2025
35276da
Clippy and comment tweak
jsdw Feb 7, 2025
7d79297
update CI to explicitly check subxt-rpc features
jsdw Feb 7, 2025
5d79b0d
clippy
jsdw Feb 7, 2025
a139312
small tweaks after pass over
jsdw Feb 7, 2025
4813af3
feature flag rename
jsdw Feb 7, 2025
52f6bf6
update some docs
jsdw Feb 7, 2025
353c997
Fix some examples
jsdw Feb 7, 2025
b09fe2a
fmt
jsdw Feb 7, 2025
3da4138
Fix features flags to work with web/wasm32
jsdw Feb 10, 2025
ff74d25
Fix unused dep warning
jsdw Feb 10, 2025
ad5549a
explicit targets in wasm CI
jsdw Feb 10, 2025
5afe631
Add better crate level docs
jsdw Feb 10, 2025
74e2d18
fmt
jsdw Feb 10, 2025
541720c
Address review comments
jsdw Feb 14, 2025
97c87f2
Merge branch 'master' into jsdw-subxt-rpcs
jsdw Feb 14, 2025
4c67947
Merge branch 'master' into jsdw-subxt-rpcs
jsdw Feb 17, 2025
5add5c1
Comment out flaky test for now and make more obvious how similar POlk…
jsdw Feb 18, 2025
fb7ceb7
Not a doc comment
jsdw Feb 18, 2025
8e0c2eb
Remove unused imports
jsdw Feb 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,24 @@ jobs:
cargo check -p subxt-signer --no-default-features --features ecdsa
cargo check -p subxt-signer --no-default-features --features unstable-eth

# Subxt-rpcs has a bunch of clients that can be exposed. Check that they all stand on their own.
- name: Cargo check subxt-rpcs
run: |
cargo check -p subxt-rpcs
cargo check -p subxt-rpcs --no-default-features --features native
cargo check -p subxt-rpcs --no-default-features --features native,subxt
cargo check -p subxt-rpcs --no-default-features --features native,jsonrpsee
cargo check -p subxt-rpcs --no-default-features --features native,reconnecting-rpc-client
cargo check -p subxt-rpcs --no-default-features --features native,mock-rpc-client
cargo check -p subxt-rpcs --no-default-features --features native,unstable-light-client

# We can't enable web features here, so no cargo hack.
- name: Cargo check subxt-lightclient
run: cargo check -p subxt-lightclient

# Next, check each other package in isolation.
- name: Cargo hack; check each feature/crate on its own
run: cargo hack --exclude subxt --exclude subxt-signer --exclude subxt-lightclient --exclude-all-features --each-feature check --workspace
run: cargo hack --exclude subxt --exclude subxt-signer --exclude subxt-lightclient --exclude subxt-rpcs --exclude-all-features --each-feature check --workspace

# Check the parachain-example code, which isn't a part of the workspace so is otherwise ignored.
- name: Cargo check parachain-example
Expand Down Expand Up @@ -225,6 +236,11 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7

- name: Cargo check web features which require wasm32 target.
run: |
cargo check -p subxt-rpcs --target wasm32-unknown-unknown --no-default-features --features web
cargo check -p subxt-rpcs --target wasm32-unknown-unknown --no-default-features --features web,reconnecting-rpc-client

# Check WASM examples, which aren't a part of the workspace and so are otherwise missed:
- name: Cargo check WASM examples
run: |
Expand Down
34 changes: 31 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"testing/generate-custom-metadata",
"macro",
"metadata",
"rpcs",
"signer",
"subxt",
"scripts/artifacts",
Expand Down Expand Up @@ -82,7 +83,7 @@ frame-metadata = { version = "18.0.0", default-features = false }
futures = { version = "0.3.31", default-features = false, features = ["std"] }
getrandom = { version = "0.2", default-features = false }
hashbrown = "0.14.5"
hex = { version = "0.4.3", default-features = false }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
heck = "0.5.0"
impl-serde = { version = "0.5.0", default-features = false }
indoc = "2"
Expand Down Expand Up @@ -116,6 +117,9 @@ which = "6.0.3"
strip-ansi-escapes = "0.2.0"
proptest = "1.5.0"
hex-literal = "0.4.1"
tower = "0.4"
hyper = "1"
http-body = "1"

# Light client support:
smoldot = { version = "0.18.0", default-features = false }
Expand Down Expand Up @@ -145,6 +149,7 @@ subxt-macro = { version = "0.39.0", path = "macro" }
subxt-metadata = { version = "0.39.0", path = "metadata", default-features = false }
subxt-codegen = { version = "0.39.0", path = "codegen" }
subxt-signer = { version = "0.39.0", path = "signer", default-features = false }
subxt-rpcs = { version = "0.39.0", path = "rpcs", default-features = false }
subxt-lightclient = { version = "0.39.0", path = "lightclient", default-features = false }
subxt-utils-fetchmetadata = { version = "0.39.0", path = "utils/fetch-metadata", default-features = false }
test-runtime = { path = "testing/test-runtime" }
Expand Down
1 change: 1 addition & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ We also assume that ongoing work done is being merged directly to the `master` b

```
(cd core && cargo publish) && \
(cd rpcs && cargo publish) && \
(cd subxt && cargo publish) && \
(cd signer && cargo publish) && \
(cd cli && cargo publish);
Expand Down
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ scale-encode = { workspace = true, default-features = false, features = ["derive
frame-metadata = { workspace = true, default-features = false }
subxt-metadata = { workspace = true, default-features = false }
derive-where = { workspace = true }
hex = { workspace = true, default-features = false, features = ["alloc"] }
hex = { workspace = true }
serde = { workspace = true, default-features = false, features = ["derive"] }
serde_json = { workspace = true, default-features = false, features = ["raw_value", "alloc"] }
tracing = { workspace = true, default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub trait Config: Sized + Send + Sync + 'static {
type Hash: BlockHash;

/// The account ID type.
type AccountId: Debug + Clone + Encode;
type AccountId: Debug + Clone + Encode + Serialize;

/// The address type.
type Address: Debug + Encode + From<Self::AccountId>;
Expand Down
10 changes: 8 additions & 2 deletions core/src/config/polkadot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ pub enum PolkadotConfig {}
impl Config for PolkadotConfig {
type Hash = <SubstrateConfig as Config>::Hash;
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = MultiAddress<Self::AccountId, ()>;
type Signature = <SubstrateConfig as Config>::Signature;
type Hasher = <SubstrateConfig as Config>::Hasher;
type Header = <SubstrateConfig as Config>::Header;
type AssetId = <SubstrateConfig as Config>::AssetId;

// Address on Polkadot has no account index, whereas it's u32 on
// the default substrate dev node.
type Address = MultiAddress<Self::AccountId, ()>;

// These are the same as the default substrate node, but redefined
// because we need to pass the PolkadotConfig trait as a param.
type ExtrinsicParams = PolkadotExtrinsicParams<Self>;
type AssetId = u32;
}

/// A struct representing the signed extra and additional parameters required
Expand Down
9 changes: 9 additions & 0 deletions lightclient/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ pub enum LightClientRpcError {
#[error("RPC Error: {0}.")]
pub struct JsonRpcError(Box<RawValue>);

impl JsonRpcError {
/// Attempt to deserialize this error into some type.
pub fn try_deserialize<'a, T: serde::de::Deserialize<'a>>(
&'a self,
) -> Result<T, serde_json::Error> {
serde_json::from_str(self.0.get())
}
}

/// This represents a single light client connection to the network. Instantiate
/// it with [`LightClient::relay_chain()`] to communicate with a relay chain, and
/// then call [`LightClient::parachain()`] to establish connections to parachains.
Expand Down
98 changes: 98 additions & 0 deletions rpcs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
[package]
name = "subxt-rpcs"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
publish = true

license.workspace = true
readme = "README.md"
repository.workspace = true
documentation.workspace = true
homepage.workspace = true
description = "Make RPC calls to Substrate based nodes"
keywords = ["parity", "subxt", "rpcs"]

[features]
default = ["jsonrpsee", "native"]

subxt = ["dep:subxt-core"]
jsonrpsee = ["dep:jsonrpsee", "dep:tokio-util"]

unstable-light-client = [
"dep:subxt-lightclient"
]

reconnecting-rpc-client = [
"jsonrpsee",
"dep:finito",
"dep:tokio",
"tokio/sync",
]

mock-rpc-client = [
"dep:tokio",
"tokio/sync",
]

# Enable this for native (ie non web/wasm builds).
# Exactly 1 of "web" and "native" is expected.
native = [
"jsonrpsee?/async-client",
"jsonrpsee?/client-ws-transport-tls",
"jsonrpsee?/ws-client",
"subxt-lightclient?/native",
]

# Enable this for web/wasm builds.
# Exactly 1 of "web" and "native" is expected.
web = [
"jsonrpsee?/async-wasm-client",
"jsonrpsee?/client-web-transport",
"jsonrpsee?/wasm-client",
"subxt-lightclient?/web",
"finito?/wasm-bindgen",
"dep:wasm-bindgen-futures",
"getrandom/js",
]

[dependencies]
codec = { workspace = true }
derive-where = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
impl-serde = { workspace = true }
primitive-types = { workspace = true, features = ["serde"] }
serde = { workspace = true }
serde_json = { workspace = true, features = ["default", "raw_value"] }
thiserror = { workspace = true }
frame-metadata = { workspace = true, features = ["decode"] }
url = { workspace = true }
tracing = { workspace = true }
getrandom = { workspace = true, optional = true }

# Included with the jsonrpsee feature
jsonrpsee = { workspace = true, optional = true }
tokio-util = { workspace = true, features = ["compat"], optional = true }

# Included with the reconnecting-rpc-client feature
finito = { workspace = true, optional = true }
tokio = { workspace = true, optional = true }

# Included with the lightclient feature
subxt-lightclient = { workspace = true, optional = true, default-features = false }

# Included with the subxt-core feature to impl Config for RpcConfig
subxt-core = { workspace = true, optional = true }

# Included with WASM feature
wasm-bindgen-futures = { workspace = true, optional = true }

[dev-dependencies]
tower = { workspace = true }
hyper = { workspace = true }
http-body = { workspace = true }

[lints]
workspace = true
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use super::{RawRpcFuture, RawRpcSubscription, RpcClientT};
use crate::error::RpcError;
use crate::Error;
use futures::stream::{StreamExt, TryStreamExt};
use jsonrpsee::{
core::{
client::{Client, ClientT, SubscriptionClientT, SubscriptionKind},
client::{Error as JsonrpseeError, Client, ClientT, SubscriptionClientT, SubscriptionKind},
traits::ToRpcParams,
},
types::SubscriptionId,
Expand All @@ -29,9 +29,7 @@ impl RpcClientT for Client {
params: Option<Box<RawValue>>,
) -> RawRpcFuture<'a, Box<RawValue>> {
Box::pin(async move {
let res = ClientT::request(self, method, Params(params))
.await
.map_err(|e| RpcError::ClientError(Box::new(e)))?;
let res = ClientT::request(self, method, Params(params)).await?;
Ok(res)
})
}
Expand All @@ -48,9 +46,7 @@ impl RpcClientT for Client {
sub,
Params(params),
unsub,
)
.await
.map_err(|e| RpcError::ClientError(Box::new(e)))?;
).await?;

let id = match stream.kind() {
SubscriptionKind::Subscription(SubscriptionId::Str(id)) => {
Expand All @@ -60,9 +56,29 @@ impl RpcClientT for Client {
};

let stream = stream
.map_err(|e| RpcError::ClientError(Box::new(e)))
.map_err(|e| Error::Client(Box::new(e)))
.boxed();
Ok(RawRpcSubscription { stream, id })
})
}
}

// Convert a JsonrpseeError into the RPC error in this crate.
// The main reason for this is to capture user errors so that
// they can be represented/handled without casting.
impl From<JsonrpseeError> for Error {
fn from(error: JsonrpseeError) -> Self {
match error {
JsonrpseeError::Call(e) => {
Error::User(crate::UserError {
code: e.code(),
message: e.message().to_owned(),
data: e.data().map(|d| d.to_owned())
})
},
e => {
Error::Client(Box::new(e))
}
}
}
}
Loading
Loading