rpc: backpressured RPC server (bump jsonrpsee 0.20) (#1313)

This is a rather big change in jsonrpsee, the major things in this bump
are:
- Server backpressure (the subscription impls are modified to deal with
that)
- Allow custom error types / return types (remove jsonrpsee::core::Error
and jsonrpee::core::CallError)
- Bug fixes (graceful shutdown in particular not used by substrate
anyway)
   - Less dependencies for the clients in particular
   - Return type requires Clone in method call responses
   - Moved to tokio channels
   - Async subscription API (not used in this PR)

Major changes in this PR:
- The subscriptions are now bounded and if subscription can't keep up
with the server it is dropped
- CLI: add parameter to configure the jsonrpc server bounded message
buffer (default is 64)
- Add our own subscription helper to deal with the unbounded streams in
substrate

The most important things in this PR to review is the added helpers
functions in `substrate/client/rpc/src/utils.rs` and the rest is pretty
much chore.

Regarding the "bounded buffer limit" it may cause the server to handle
the JSON-RPC calls
slower than before.

The message size limit is bounded by "--rpc-response-size" thus "by
default 10MB * 64 = 640MB"
but the subscription message size is not covered by this limit and could
be capped as well.

Hopefully the last release prior to 1.0, sorry in advance for a big PR

Previous attempt: https://github.com/paritytech/substrate/pull/13992

Resolves https://github.com/paritytech/polkadot-sdk/issues/748, resolves
https://github.com/paritytech/polkadot-sdk/issues/627
This commit is contained in:
Niklas Adolfsson
2024-01-23 09:55:13 +01:00
committed by GitHub
parent 76c37c930b
commit e16ef0861f
117 changed files with 1245 additions and 1090 deletions
Generated
+98 -65
View File
@@ -6158,19 +6158,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "glutton-westend-runtime"
version = "1.0.0"
@@ -6508,7 +6495,6 @@ dependencies = [
"rustls-native-certs",
"tokio",
"tokio-rustls",
"webpki-roots 0.23.1",
]
[[package]]
@@ -6847,9 +6833,9 @@ dependencies = [
[[package]]
name = "jsonrpsee"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "367a292944c07385839818bb71c8d76611138e2dedb0677d035b8da21d29c78b"
checksum = "affdc52f7596ccb2d7645231fc6163bb314630c989b64998f3699a28b4d5d4dc"
dependencies = [
"jsonrpsee-core",
"jsonrpsee-http-client",
@@ -6857,19 +6843,19 @@ dependencies = [
"jsonrpsee-server",
"jsonrpsee-types",
"jsonrpsee-ws-client",
"tokio",
"tracing",
]
[[package]]
name = "jsonrpsee-client-transport"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8b3815d9f5d5de348e5f162b316dc9cdf4548305ebb15b4eb9328e66cf27d7a"
checksum = "b5b005c793122d03217da09af68ba9383363caa950b90d3436106df8cabce935"
dependencies = [
"futures-util",
"http",
"jsonrpsee-core",
"jsonrpsee-types",
"pin-project",
"rustls-native-certs",
"soketto",
@@ -6878,24 +6864,21 @@ dependencies = [
"tokio-rustls",
"tokio-util",
"tracing",
"webpki-roots 0.25.2",
"url",
]
[[package]]
name = "jsonrpsee-core"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b5dde66c53d6dcdc8caea1874a45632ec0fcf5b437789f1e45766a1512ce803"
checksum = "da2327ba8df2fdbd5e897e2b5ed25ce7f299d345b9736b6828814c3dbd1fd47b"
dependencies = [
"anyhow",
"arrayvec 0.7.4",
"async-lock",
"async-trait",
"beef",
"futures-channel",
"futures-timer",
"futures-util",
"globset",
"hyper",
"jsonrpsee-types",
"parking_lot 0.12.1",
@@ -6911,28 +6894,29 @@ dependencies = [
[[package]]
name = "jsonrpsee-http-client"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5f9fabdd5d79344728521bb65e3106b49ec405a78b66fbff073b72b389fa43"
checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20"
dependencies = [
"async-trait",
"hyper",
"hyper-rustls",
"jsonrpsee-core",
"jsonrpsee-types",
"rustc-hash",
"serde",
"serde_json",
"thiserror",
"tokio",
"tower",
"tracing",
"url",
]
[[package]]
name = "jsonrpsee-proc-macros"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a"
checksum = "29110019693a4fa2dbda04876499d098fa16d70eba06b1e6e2b3f1b251419515"
dependencies = [
"heck",
"proc-macro-crate 1.3.1",
@@ -6943,19 +6927,20 @@ dependencies = [
[[package]]
name = "jsonrpsee-server"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4d945a6008c9b03db3354fb3c83ee02d2faa9f2e755ec1dfb69c3551b8f4ba"
checksum = "82c39a00449c9ef3f50b84fc00fc4acba20ef8f559f07902244abf4c15c5ab9c"
dependencies = [
"futures-channel",
"futures-util",
"http",
"hyper",
"jsonrpsee-core",
"jsonrpsee-types",
"route-recognizer",
"serde",
"serde_json",
"soketto",
"thiserror",
"tokio",
"tokio-stream",
"tokio-util",
@@ -6965,9 +6950,9 @@ dependencies = [
[[package]]
name = "jsonrpsee-types"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245ba8e5aa633dd1c1e4fae72bce06e71f42d34c14a2767c6b4d173b57bee5e5"
checksum = "5be0be325642e850ed0bdff426674d2e66b2b7117c9be23a7caef68a2902b7d9"
dependencies = [
"anyhow",
"beef",
@@ -6979,14 +6964,15 @@ dependencies = [
[[package]]
name = "jsonrpsee-ws-client"
version = "0.16.3"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e1b3975ed5d73f456478681a417128597acd6a2487855fdb7b4a3d4d195bf5e"
checksum = "bca9cb3933ccae417eb6b08c3448eb1cb46e39834e5b503e395e5e5bd08546c0"
dependencies = [
"http",
"jsonrpsee-client-transport",
"jsonrpsee-core",
"jsonrpsee-types",
"url",
]
[[package]]
@@ -7972,6 +7958,15 @@ dependencies = [
"regex-automata 0.1.10",
]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]]
name = "matches"
version = "0.1.10"
@@ -8242,7 +8237,6 @@ dependencies = [
name = "mmr-rpc"
version = "4.0.0-dev"
dependencies = [
"anyhow",
"jsonrpsee",
"parity-scale-codec",
"serde",
@@ -8831,6 +8825,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num"
version = "0.4.1"
@@ -9048,6 +9052,12 @@ version = "6.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owo-colors"
version = "3.5.0"
@@ -15046,6 +15056,12 @@ dependencies = [
"westend-emulated-chain",
]
[[package]]
name = "route-recognizer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
[[package]]
name = "rpassword"
version = "7.2.0"
@@ -15227,7 +15243,7 @@ checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb"
dependencies = [
"log",
"ring 0.16.20",
"rustls-webpki 0.101.4",
"rustls-webpki",
"sct",
]
@@ -15252,16 +15268,6 @@ dependencies = [
"base64 0.21.2",
]
[[package]]
name = "rustls-webpki"
version = "0.100.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab"
dependencies = [
"ring 0.16.20",
"untrusted 0.7.1",
]
[[package]]
name = "rustls-webpki"
version = "0.101.4"
@@ -15973,7 +15979,7 @@ dependencies = [
"substrate-test-runtime",
"tempfile",
"tracing",
"tracing-subscriber",
"tracing-subscriber 0.2.25",
"wat",
]
@@ -16403,6 +16409,7 @@ dependencies = [
"sp-version",
"substrate-test-runtime-client",
"tokio",
"tracing-subscriber 0.3.18",
]
[[package]]
@@ -16455,6 +16462,7 @@ dependencies = [
"sc-block-builder",
"sc-chain-spec",
"sc-client-api",
"sc-rpc",
"sc-service",
"sc-transaction-pool-api",
"sc-utils",
@@ -16713,8 +16721,8 @@ dependencies = [
"sp-tracing 10.0.0",
"thiserror",
"tracing",
"tracing-log",
"tracing-subscriber",
"tracing-log 0.1.3",
"tracing-subscriber 0.2.25",
]
[[package]]
@@ -18899,7 +18907,7 @@ dependencies = [
"sp-std 8.0.0",
"tracing",
"tracing-core",
"tracing-subscriber",
"tracing-subscriber 0.2.25",
]
[[package]]
@@ -18911,7 +18919,7 @@ dependencies = [
"sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)",
"tracing",
"tracing-core",
"tracing-subscriber",
"tracing-subscriber 0.2.25",
]
[[package]]
@@ -19614,6 +19622,7 @@ dependencies = [
"sp-keystore",
"sp-runtime",
"sp-state-machine",
"tokio",
]
[[package]]
@@ -20428,6 +20437,10 @@ version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"pin-project",
"pin-project-lite 0.2.12",
"tower-layer",
"tower-service",
"tracing",
@@ -20540,6 +20553,17 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.1.3"
@@ -20559,7 +20583,7 @@ dependencies = [
"ansi_term",
"chrono",
"lazy_static",
"matchers",
"matchers 0.0.1",
"parking_lot 0.11.2",
"regex",
"serde",
@@ -20569,10 +20593,28 @@ dependencies = [
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-log 0.1.3",
"tracing-serde",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers 0.1.0",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log 0.2.0",
]
[[package]]
name = "trie-bench"
version = "0.38.0"
@@ -21499,15 +21541,6 @@ dependencies = [
"webpki",
]
[[package]]
name = "webpki-roots"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338"
dependencies = [
"rustls-webpki 0.100.2",
]
[[package]]
name = "webpki-roots"
version = "0.25.2"
@@ -22,5 +22,5 @@ sc-client-api = { path = "../../../substrate/client/api" }
futures = "0.3.28"
async-trait = "0.1.74"
thiserror = "1.0.48"
jsonrpsee-core = "0.16.2"
jsonrpsee-core = "0.20.3"
parity-scale-codec = "3.6.4"
@@ -33,7 +33,7 @@ tokio-util = { version = "0.7.8", features = ["compat"] }
futures = "0.3.28"
futures-timer = "3.0.2"
parity-scale-codec = "3.6.4"
jsonrpsee = { version = "0.16.2", features = ["ws-client"] }
jsonrpsee = { version = "0.20.3", features = ["ws-client"] }
tracing = "0.1.37"
async-trait = "0.1.74"
url = "2.4.0"
+1 -1
View File
@@ -18,7 +18,7 @@ clap = { version = "4.4.18", features = ["derive"] }
log = "0.4.20"
codec = { package = "parity-scale-codec", version = "3.0.0" }
serde = { version = "1.0.195", features = ["derive"] }
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
futures = "0.3.28"
serde_json = "1.0.111"
@@ -236,20 +236,14 @@ impl MatchesLocalAndForeignAssetsLocation<xcm::v3::Location>
{
fn is_local(location: &xcm::v3::Location) -> bool {
use assets_common::fungible_conversion::MatchesLocation;
let latest_location: Location = if let Ok(location) = (*location).try_into() {
location
} else {
return false;
};
let latest_location: Location =
if let Ok(location) = (*location).try_into() { location } else { return false };
TrustBackedAssetsConvertedConcreteId::contains(&latest_location)
}
fn is_foreign(location: &xcm::v3::Location) -> bool {
use assets_common::fungible_conversion::MatchesLocation;
let latest_location: Location = if let Ok(location) = (*location).try_into() {
location
} else {
return false;
};
let latest_location: Location =
if let Ok(location) = (*location).try_into() { location } else { return false };
ForeignAssetsConvertedConcreteId::contains(&latest_location)
}
}
@@ -231,21 +231,15 @@ impl MatchesLocalAndForeignAssetsLocation<xcm::v3::Location>
{
fn is_local(location: &xcm::v3::Location) -> bool {
use assets_common::fungible_conversion::MatchesLocation;
let latest_location: Location = if let Ok(location) = (*location).try_into() {
location
} else {
return false;
};
let latest_location: Location =
if let Ok(location) = (*location).try_into() { location } else { return false };
TrustBackedAssetsConvertedConcreteId::contains(&latest_location)
}
fn is_foreign(location: &xcm::v3::Location) -> bool {
use assets_common::fungible_conversion::MatchesLocation;
let latest_location: Location = if let Ok(location) = (*location).try_into() {
location
} else {
return false;
};
let latest_location: Location =
if let Ok(location) = (*location).try_into() { location } else { return false };
ForeignAssetsConvertedConcreteId::contains(&latest_location)
}
}
+1 -1
View File
@@ -38,9 +38,9 @@ coretime-rococo-runtime = { path = "../parachains/runtimes/coretime/coretime-roc
coretime-westend-runtime = { path = "../parachains/runtimes/coretime/coretime-westend" }
bridge-hub-westend-runtime = { path = "../parachains/runtimes/bridge-hubs/bridge-hub-westend" }
penpal-runtime = { path = "../parachains/runtimes/testing/penpal" }
jsonrpsee = { version = "0.20.3", features = ["server"] }
people-rococo-runtime = { path = "../parachains/runtimes/people/people-rococo" }
people-westend-runtime = { path = "../parachains/runtimes/people/people-westend" }
jsonrpsee = { version = "0.16.2", features = ["server"] }
parachains-common = { path = "../parachains/common" }
# Substrate
+1 -1
View File
@@ -17,7 +17,7 @@ async-trait = "0.1.74"
clap = { version = "4.4.18", features = ["derive"] }
codec = { package = "parity-scale-codec", version = "3.0.0" }
criterion = { version = "0.5.1", features = ["async_tokio"] }
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
rand = "0.8.5"
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
+1
View File
@@ -799,6 +799,7 @@ pub fn node_config(
rpc_id_provider: None,
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9945,
rpc_message_buffer_capacity: Default::default(),
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
+1 -1
View File
@@ -365,7 +365,7 @@ pub mod pallet {
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
if sender_balance < amount {
return Err("InsufficientBalance".into());
return Err("InsufficientBalance".into())
}
let reminder = sender_balance - amount;
+1
View File
@@ -182,6 +182,7 @@ pub fn node_config(
rpc_id_provider: None,
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
+1 -1
View File
@@ -10,7 +10,7 @@ description = "Polkadot specific RPC functionality."
workspace = true
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
polkadot-primitives = { path = "../primitives" }
sc-client-api = { path = "../../substrate/client/api" }
sp-blockchain = { path = "../../substrate/primitives/blockchain" }
+1 -1
View File
@@ -189,7 +189,7 @@ pub mod junctions {
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"));
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
let from_slice_syntax = generate_conversion_from_slice_syntax();
@@ -25,16 +25,9 @@ use xcm::latest::{InteriorLocation, Location, NetworkId};
pub struct StartsWith<T, L = Location>(sp_std::marker::PhantomData<(T, L)>);
impl<T: Get<L>, L: TryInto<Location> + Clone> Contains<L> for StartsWith<T, L> {
fn contains(location: &L) -> bool {
let latest_location: Location = if let Ok(location) = (*location).clone().try_into() {
location
} else {
return false;
};
let latest_t = if let Ok(location) = T::get().try_into() {
location
} else {
return false;
};
let latest_location: Location =
if let Ok(location) = (*location).clone().try_into() { location } else { return false };
let latest_t = if let Ok(location) = T::get().try_into() { location } else { return false };
latest_location.starts_with(&latest_t)
}
}
+18
View File
@@ -0,0 +1,18 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
title: backpressured JSON-RPC server (upgrade jsonrpsee)
doc:
- audience: Node Operator
description: |
Modifies the jsonrpc server to be "backpressured" and it's possible to configure
how many messages can be "buffered" via the CLI `--rpc_message_buffer_capacity`.
Major changes in this PR:
- The subscriptions are now bounded and if subscription can't keep up with the server it is dropped
- CLI: add parameter to configure the jsonrpc server bounded message buffer (default is 64)
- Add our own subscription helper to deal with the unbounded streams in substrate
crates:
- name: sc-rpc-server
+1 -1
View File
@@ -23,7 +23,7 @@ name = "minimal-node"
clap = { version = "4.4.18", features = ["derive"] }
futures = { version = "0.3.21", features = ["thread-pool"] }
futures-timer = "3.0.1"
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
serde_json = "1.0.111"
sc-cli = { path = "../../../client/cli" }
+1 -1
View File
@@ -48,7 +48,7 @@ frame-system = { path = "../../../frame/system" }
pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false }
# These dependencies are used for the node template's RPCs
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
sp-api = { path = "../../../primitives/api" }
sc-rpc-api = { path = "../../../client/rpc-api" }
sp-blockchain = { path = "../../../primitives/blockchain" }
+1 -1
View File
@@ -44,7 +44,7 @@ array-bytes = "6.1"
clap = { version = "4.4.18", features = ["derive"], optional = true }
codec = { package = "parity-scale-codec", version = "3.6.1" }
serde = { version = "1.0.195", features = ["derive"] }
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
futures = "0.3.21"
log = "0.4.17"
rand = "0.8"
@@ -83,6 +83,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
rpc_id_provider: Default::default(),
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
@@ -79,6 +79,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
rpc_id_provider: Default::default(),
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
+1 -1
View File
@@ -16,7 +16,7 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
node-primitives = { path = "../primitives" }
pallet-transaction-payment-rpc = { path = "../../../frame/transaction-payment/rpc" }
mmr-rpc = { path = "../../../client/merkle-mountain-range/rpc" }
+14 -3
View File
@@ -25,7 +25,7 @@ use crate::{
},
CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams,
RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB,
RPC_DEFAULT_MAX_SUBS_PER_CONN,
RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN,
};
use clap::Parser;
use regex::Regex;
@@ -102,9 +102,20 @@ pub struct RunCmd {
#[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)]
pub rpc_max_connections: u32,
/// Specify browser *origins* allowed to access the HTTP and WS RPC servers.
/// The number of messages the RPC server is allowed to keep in memory.
///
/// A comma-separated list of origins (`protocol://domain` or special `null`
/// If the buffer becomes full then the server will not process
/// new messages until the connected client start reading the
/// underlying messages.
///
/// This applies per connection which includes both
/// JSON-RPC methods calls and subscriptions.
#[arg(long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)]
pub rpc_message_buffer_capacity_per_connection: u32,
/// Specify browser *origins* allowed to access the HTTP & WS RPC servers.
///
/// A comma-separated list of origins (protocol://domain or special `null`
/// value). Value of `all` will disable origin validation. Default is to
/// allow localhost and <https://polkadot.js.org> origins. When running in
/// `--dev` mode the default is to allow all origins.
+10 -1
View File
@@ -52,8 +52,11 @@ pub const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
pub const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
/// The default max response size in MB.
pub const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 15;
/// The default number of connection..
/// The default concurrent connection limit.
pub const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 100;
/// The default number of messages the RPC server
/// is allowed to keep in memory per connection.
pub const RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN: u32 = 64;
/// Default configuration values used by Substrate
///
@@ -330,6 +333,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
Ok(RPC_DEFAULT_MAX_SUBS_PER_CONN)
}
/// The number of messages the RPC server is allowed to keep in memory per connection.
fn rpc_buffer_capacity_per_connection(&self) -> Result<u32> {
Ok(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)
}
/// Get the prometheus configuration (`None` if disabled)
///
/// By default this is `None`.
@@ -501,6 +509,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
rpc_id_provider: None,
rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?,
rpc_port: DCV::rpc_listen_port(),
rpc_message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?,
prometheus_config: self
.prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?,
telemetry_endpoints,
+1
View File
@@ -269,6 +269,7 @@ mod tests {
rpc_max_response_size: Default::default(),
rpc_id_provider: Default::default(),
rpc_max_subs_per_conn: Default::default(),
rpc_message_buffer_capacity: Default::default(),
rpc_port: 9944,
prometheus_config: None,
telemetry_endpoints: None,
@@ -16,7 +16,7 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
futures = "0.3.21"
serde = { version = "1.0.195", features = ["derive"] }
thiserror = "1.0"
+25 -19
View File
@@ -22,15 +22,15 @@ use std::{collections::HashMap, sync::Arc};
use futures::TryFutureExt;
use jsonrpsee::{
core::{async_trait, Error as JsonRpseeError, RpcResult},
core::async_trait,
proc_macros::rpc,
types::{error::CallError, ErrorObject},
types::{ErrorObject, ErrorObjectOwned},
};
use serde::{Deserialize, Serialize};
use sc_consensus_babe::{authorship, BabeWorkerHandle};
use sc_consensus_epochs::Epoch as EpochT;
use sc_rpc_api::DenyUnsafe;
use sc_rpc_api::{DenyUnsafe, UnsafeRpcError};
use sp_api::ProvideRuntimeApi;
use sp_application_crypto::AppCrypto;
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
@@ -48,7 +48,7 @@ pub trait BabeApi {
/// Returns data about which slots (primary or secondary) can be claimed in the current epoch
/// with the keys in the keystore.
#[method(name = "babe_epochAuthorship")]
async fn epoch_authorship(&self) -> RpcResult<HashMap<AuthorityId, EpochAuthorship>>;
async fn epoch_authorship(&self) -> Result<HashMap<AuthorityId, EpochAuthorship>, Error>;
}
/// Provides RPC methods for interacting with Babe.
@@ -89,7 +89,7 @@ where
C::Api: BabeRuntimeApi<B>,
SC: SelectChain<B> + Clone + 'static,
{
async fn epoch_authorship(&self) -> RpcResult<HashMap<AuthorityId, EpochAuthorship>> {
async fn epoch_authorship(&self) -> Result<HashMap<AuthorityId, EpochAuthorship>, Error> {
self.deny_unsafe.check_if_safe()?;
let best_header = self.select_chain.best_chain().map_err(Error::SelectChain).await?;
@@ -147,7 +147,7 @@ where
}
/// Holds information about the `slot`'s that can be claimed by a given key.
#[derive(Default, Debug, Deserialize, Serialize)]
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct EpochAuthorship {
/// the array of primary slots that can be claimed
primary: Vec<u64>,
@@ -166,20 +166,26 @@ pub enum Error {
/// Failed to fetch epoch data.
#[error("Failed to fetch epoch data")]
FetchEpoch,
/// Consensus error
#[error(transparent)]
Consensus(#[from] ConsensusError),
/// Errors that can be formatted as a String
#[error("{0}")]
StringError(String),
/// Call to an unsafe RPC was denied.
#[error(transparent)]
UnsafeRpcCalled(#[from] UnsafeRpcError),
}
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(error: Error) -> Self {
let error_code = match error {
Error::SelectChain(_) => 1,
Error::FetchEpoch => 2,
};
JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
BABE_ERROR + error_code,
error.to_string(),
Some(format!("{:?}", error)),
)))
match error {
Error::SelectChain(e) => ErrorObject::owned(BABE_ERROR + 1, e.to_string(), None::<()>),
Error::FetchEpoch => ErrorObject::owned(BABE_ERROR + 2, error.to_string(), None::<()>),
Error::Consensus(e) => ErrorObject::owned(BABE_ERROR + 3, e.to_string(), None::<()>),
Error::StringError(e) => ErrorObject::owned(BABE_ERROR + 4, e, None::<()>),
Error::UnsafeRpcCalled(e) => e.into(),
}
}
}
@@ -251,7 +257,7 @@ mod tests {
let api = babe_rpc.into_rpc();
let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#;
let (response, _) = api.raw_json_request(request).await.unwrap();
let (response, _) = api.raw_json_request(request, 1).await.unwrap();
let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4],"secondary_vrf":[]}},"id":1}"#;
assert_eq!(&response.result, expected);
@@ -263,7 +269,7 @@ mod tests {
let api = babe_rpc.into_rpc();
let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params":[],"id":1}"#;
let (response, _) = api.raw_json_request(request).await.unwrap();
let (response, _) = api.raw_json_request(request, 1).await.unwrap();
let expected = r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"#;
assert_eq!(&response.result, expected);
@@ -14,7 +14,7 @@ workspace = true
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] }
futures = "0.3.21"
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
log = "0.4"
parking_lot = "0.12.1"
serde = { version = "1.0.195", features = ["derive"] }
+18 -31
View File
@@ -23,15 +23,15 @@
use parking_lot::RwLock;
use std::sync::Arc;
use sc_rpc::SubscriptionTaskExecutor;
use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor};
use sp_runtime::traits::Block as BlockT;
use futures::{task::SpawnError, FutureExt, StreamExt};
use jsonrpsee::{
core::{async_trait, Error as JsonRpseeError, RpcResult},
core::async_trait,
proc_macros::rpc,
types::{error::CallError, ErrorObject, SubscriptionResult},
SubscriptionSink,
types::{ErrorObject, ErrorObjectOwned},
PendingSubscriptionSink,
};
use log::warn;
@@ -69,15 +69,11 @@ impl From<Error> for ErrorCode {
}
}
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(error: Error) -> Self {
let message = error.to_string();
let code = ErrorCode::from(error);
JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
code as i32,
message,
None::<()>,
)))
ErrorObject::owned(code as i32, message, None::<()>)
}
}
@@ -98,7 +94,7 @@ pub trait BeefyApi<Notification, Hash> {
/// in the network or if the client is still initializing or syncing with the network.
/// In such case an error would be returned.
#[method(name = "beefy_getFinalizedHead")]
async fn latest_finalized(&self) -> RpcResult<Hash>;
async fn latest_finalized(&self) -> Result<Hash, Error>;
}
/// Implements the BeefyApi RPC trait for interacting with BEEFY.
@@ -138,27 +134,17 @@ impl<Block> BeefyApiServer<notification::EncodedVersionedFinalityProof, Block::H
where
Block: BlockT,
{
fn subscribe_justifications(&self, mut sink: SubscriptionSink) -> SubscriptionResult {
fn subscribe_justifications(&self, pending: PendingSubscriptionSink) {
let stream = self
.finality_proof_stream
.subscribe(100_000)
.map(|vfp| notification::EncodedVersionedFinalityProof::new::<Block>(vfp));
let fut = async move {
sink.pipe_from_stream(stream).await;
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
Ok(())
sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream));
}
async fn latest_finalized(&self) -> RpcResult<Block::Hash> {
self.beefy_best_block
.read()
.as_ref()
.cloned()
.ok_or(Error::EndpointNotReady)
.map_err(Into::into)
async fn latest_finalized(&self) -> Result<Block::Hash, Error> {
self.beefy_best_block.read().as_ref().cloned().ok_or(Error::EndpointNotReady)
}
}
@@ -167,7 +153,7 @@ mod tests {
use super::*;
use codec::{Decode, Encode};
use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule};
use jsonrpsee::{core::EmptyServerParams as EmptyParams, RpcModule};
use sc_consensus_beefy::{
communication::notification::BeefyVersionedFinalityProofSender,
justification::BeefyVersionedFinalityProof,
@@ -199,7 +185,7 @@ mod tests {
let (rpc, _) = setup_io_handler();
let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#;
let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#.to_string();
let (response, _) = rpc.raw_json_request(&request).await.unwrap();
let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
assert_eq!(expected_response, response.result);
}
@@ -230,13 +216,13 @@ mod tests {
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2);
while std::time::Instant::now() < deadline {
let (response, _) = io.raw_json_request(request).await.expect("RPC requests work");
let (response, _) = io.raw_json_request(request, 1).await.expect("RPC requests work");
if response.result != not_ready {
assert_eq!(response.result, expected);
// Success
return
}
std::thread::sleep(std::time::Duration::from_millis(50))
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
panic!(
@@ -249,7 +235,7 @@ mod tests {
let (rpc, _) = setup_io_handler();
// Subscribe call.
let _sub = rpc
.subscribe("beefy_subscribeJustifications", EmptyParams::new())
.subscribe_unbounded("beefy_subscribeJustifications", EmptyParams::new())
.await
.unwrap();
@@ -257,6 +243,7 @@ mod tests {
let (response, _) = rpc
.raw_json_request(
r#"{"jsonrpc":"2.0","method":"beefy_unsubscribeJustifications","params":["FOO"],"id":1}"#,
1,
)
.await
.unwrap();
@@ -284,7 +271,7 @@ mod tests {
// Subscribe
let mut sub = rpc
.subscribe("beefy_subscribeJustifications", EmptyParams::new())
.subscribe_unbounded("beefy_subscribeJustifications", EmptyParams::new())
.await
.unwrap();
@@ -43,7 +43,7 @@ pub enum ImportResult {
}
/// Auxiliary data associated with an imported block result.
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ImportedAux {
/// Only the header has been imported. Block body verification was skipped.
pub header_only: bool,
@@ -15,7 +15,7 @@ workspace = true
[dependencies]
finality-grandpa = { version = "0.16.2", features = ["derive-codec"] }
futures = "0.3.16"
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
log = "0.4.8"
parity-scale-codec = { version = "3.6.1", features = ["derive"] }
serde = { version = "1.0.195", features = ["derive"] }
@@ -16,10 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
#[derive(Debug, thiserror::Error)]
/// Top-level error type for the RPC handler
@@ -61,15 +58,11 @@ impl From<Error> for ErrorCode {
}
}
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(error: Error) -> Self {
let message = error.to_string();
let code = ErrorCode::from(error);
JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
code as i32,
message,
None::<()>,
)))
ErrorObject::owned(code as i32, message, None::<()>)
}
}
@@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize};
use sc_consensus_grandpa::FinalityProofProvider;
use sp_runtime::traits::{Block as BlockT, NumberFor};
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub struct EncodedFinalityProof(pub sp_core::Bytes);
/// Local trait mainly to allow mocking in tests.
@@ -19,15 +19,13 @@
//! RPC API for GRANDPA.
#![warn(missing_docs)]
use futures::{FutureExt, StreamExt};
use futures::StreamExt;
use log::warn;
use std::sync::Arc;
use jsonrpsee::{
core::{async_trait, RpcResult},
core::{async_trait, server::PendingSubscriptionSink},
proc_macros::rpc,
types::SubscriptionResult,
SubscriptionSink,
};
mod error;
@@ -35,13 +33,13 @@ mod finality;
mod notification;
mod report;
use sc_consensus_grandpa::GrandpaJustificationStream;
use sc_rpc::SubscriptionTaskExecutor;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use error::Error;
use finality::{EncodedFinalityProof, RpcFinalityProofProvider};
use notification::JustificationNotification;
use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
use sc_consensus_grandpa::GrandpaJustificationStream;
use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor};
use sp_runtime::traits::{Block as BlockT, NumberFor};
/// Provides RPC methods for interacting with GRANDPA.
#[rpc(client, server)]
@@ -49,7 +47,7 @@ pub trait GrandpaApi<Notification, Hash, Number> {
/// Returns the state of the current best round state as well as the
/// ongoing background rounds.
#[method(name = "grandpa_roundState")]
async fn round_state(&self) -> RpcResult<ReportedRoundStates>;
async fn round_state(&self) -> Result<ReportedRoundStates, Error>;
/// Returns the block most recently finalized by Grandpa, alongside
/// side its justification.
@@ -63,7 +61,7 @@ pub trait GrandpaApi<Notification, Hash, Number> {
/// Prove finality for the given block number by returning the Justification for the last block
/// in the set and all the intermediary headers to link them together.
#[method(name = "grandpa_proveFinality")]
async fn prove_finality(&self, block: Number) -> RpcResult<Option<EncodedFinalityProof>>;
async fn prove_finality(&self, block: Number) -> Result<Option<EncodedFinalityProof>, Error>;
}
/// Provides RPC methods for interacting with GRANDPA.
@@ -99,36 +97,28 @@ where
Block: BlockT,
ProofProvider: RpcFinalityProofProvider<Block> + Send + Sync + 'static,
{
async fn round_state(&self) -> RpcResult<ReportedRoundStates> {
ReportedRoundStates::from(&self.authority_set, &self.voter_state).map_err(Into::into)
async fn round_state(&self) -> Result<ReportedRoundStates, Error> {
ReportedRoundStates::from(&self.authority_set, &self.voter_state)
}
fn subscribe_justifications(&self, mut sink: SubscriptionSink) -> SubscriptionResult {
fn subscribe_justifications(&self, pending: PendingSubscriptionSink) {
let stream = self.justification_stream.subscribe(100_000).map(
|x: sc_consensus_grandpa::GrandpaJustification<Block>| {
JustificationNotification::from(x)
},
);
let fut = async move {
sink.pipe_from_stream(stream).await;
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
Ok(())
sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream));
}
async fn prove_finality(
&self,
block: NumberFor<Block>,
) -> RpcResult<Option<EncodedFinalityProof>> {
self.finality_proof_provider
.rpc_prove_finality(block)
.map_err(|e| {
warn!("Error proving finality: {}", e);
error::Error::ProveFinalityFailed(e)
})
.map_err(Into::into)
) -> Result<Option<EncodedFinalityProof>, Error> {
self.finality_proof_provider.rpc_prove_finality(block).map_err(|e| {
warn!("Error proving finality: {}", e);
error::Error::ProveFinalityFailed(e)
})
}
}
@@ -137,17 +127,15 @@ mod tests {
use super::*;
use std::{collections::HashSet, convert::TryInto, sync::Arc};
use jsonrpsee::{
types::{EmptyServerParams as EmptyParams, SubscriptionId},
RpcModule,
};
use jsonrpsee::{core::EmptyServerParams as EmptyParams, types::SubscriptionId, RpcModule};
use parity_scale_codec::{Decode, Encode};
use sc_block_builder::BlockBuilderBuilder;
use sc_consensus_grandpa::{
report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender,
};
use sc_rpc::testing::test_executor;
use sp_blockchain::HeaderBackend;
use sp_core::{crypto::ByteArray, testing::TaskExecutor};
use sp_core::crypto::ByteArray;
use sp_keyring::Ed25519Keyring;
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use substrate_test_runtime_client::{
@@ -264,7 +252,7 @@ mod tests {
{
let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
let finality_proof_provider = Arc::new(TestFinalityProofProvider { finality_proof });
let executor = Arc::new(TaskExecutor::default());
let executor = test_executor();
let rpc = Grandpa::new(
executor,
@@ -283,7 +271,7 @@ mod tests {
let (rpc, _) = setup_io_handler(EmptyVoterState);
let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"GRANDPA RPC endpoint not ready"},"id":0}"#.to_string();
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
let (response, _) = rpc.raw_json_request(&request).await.unwrap();
let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
assert_eq!(expected_response, response.result);
}
@@ -306,7 +294,7 @@ mod tests {
},\"id\":0}".to_string();
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
let (response, _) = rpc.raw_json_request(&request).await.unwrap();
let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
assert_eq!(expected_response, response.result);
}
@@ -315,7 +303,7 @@ mod tests {
let (rpc, _) = setup_io_handler(TestVoterState);
// Subscribe call.
let _sub = rpc
.subscribe("grandpa_subscribeJustifications", EmptyParams::new())
.subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new())
.await
.unwrap();
@@ -323,6 +311,7 @@ mod tests {
let (response, _) = rpc
.raw_json_request(
r#"{"jsonrpc":"2.0","method":"grandpa_unsubscribeJustifications","params":["FOO"],"id":1}"#,
1,
)
.await
.unwrap();
@@ -385,7 +374,7 @@ mod tests {
let (rpc, justification_sender) = setup_io_handler(TestVoterState);
let mut sub = rpc
.subscribe("grandpa_subscribeJustifications", EmptyParams::new())
.subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new())
.await
.unwrap();
@@ -57,21 +57,21 @@ impl ReportVoterState for SharedVoterState {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Prevotes {
current_weight: u32,
missing: BTreeSet<AuthorityId>,
}
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Precommits {
current_weight: u32,
missing: BTreeSet<AuthorityId>,
}
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
round: u32,
@@ -111,7 +111,7 @@ impl RoundState {
/// The state of the current best round, as well as the background rounds in a
/// form suitable for serialization.
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReportedRoundStates {
set_id: u32,
@@ -16,7 +16,7 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
assert_matches = "1.3.0"
async-trait = "0.1.74"
codec = { package = "parity-scale-codec", version = "3.6.1" }
@@ -20,10 +20,7 @@
//! This is suitable for a testing environment.
use futures::channel::{mpsc::SendError, oneshot};
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
use sc_consensus::ImportResult;
use sp_blockchain::Error as BlockchainError;
use sp_consensus::Error as ConsensusError;
@@ -106,8 +103,8 @@ impl Error {
}
}
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(err: Error) -> Self {
CallError::Custom(ErrorObject::owned(err.to_code(), err.to_string(), None::<()>)).into()
ErrorObject::owned(err.to_code(), err.to_string(), None::<()>)
}
}
@@ -23,10 +23,7 @@ use futures::{
channel::{mpsc, oneshot},
SinkExt,
};
use jsonrpsee::{
core::{async_trait, Error as JsonRpseeError, RpcResult},
proc_macros::rpc,
};
use jsonrpsee::{core::async_trait, proc_macros::rpc};
use sc_consensus::ImportedAux;
use serde::{Deserialize, Serialize};
use sp_runtime::EncodedJustification;
@@ -74,7 +71,7 @@ pub trait ManualSealApi<Hash> {
create_empty: bool,
finalize: bool,
parent_hash: Option<Hash>,
) -> RpcResult<CreatedBlock<Hash>>;
) -> Result<CreatedBlock<Hash>, Error>;
/// Instructs the manual-seal authorship task to finalize a block
#[method(name = "engine_finalizeBlock")]
@@ -82,7 +79,7 @@ pub trait ManualSealApi<Hash> {
&self,
hash: Hash,
justification: Option<EncodedJustification>,
) -> RpcResult<bool>;
) -> Result<bool, Error>;
}
/// A struct that implements the [`ManualSealApiServer`].
@@ -91,7 +88,7 @@ pub struct ManualSeal<Hash> {
}
/// return type of `engine_createBlock`
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct CreatedBlock<Hash> {
/// hash of the created block.
pub hash: Hash,
@@ -115,7 +112,7 @@ impl<Hash: Send + 'static> ManualSealApiServer<Hash> for ManualSeal<Hash> {
create_empty: bool,
finalize: bool,
parent_hash: Option<Hash>,
) -> RpcResult<CreatedBlock<Hash>> {
) -> Result<CreatedBlock<Hash>, Error> {
let mut sink = self.import_block_channel.clone();
let (sender, receiver) = oneshot::channel();
// NOTE: this sends a Result over the channel.
@@ -131,7 +128,7 @@ impl<Hash: Send + 'static> ManualSealApiServer<Hash> for ManualSeal<Hash> {
match receiver.await {
Ok(Ok(rx)) => Ok(rx),
Ok(Err(e)) => Err(e.into()),
Err(e) => Err(JsonRpseeError::to_call_error(e)),
Err(e) => Err(e.into()),
}
}
@@ -139,12 +136,12 @@ impl<Hash: Send + 'static> ManualSealApiServer<Hash> for ManualSeal<Hash> {
&self,
hash: Hash,
justification: Option<EncodedJustification>,
) -> RpcResult<bool> {
) -> Result<bool, Error> {
let mut sink = self.import_block_channel.clone();
let (sender, receiver) = oneshot::channel();
let command = EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification };
sink.send(command).await?;
receiver.await.map(|_| true).map_err(|e| JsonRpseeError::to_call_error(e))
receiver.await.map(|_| true).map_err(Into::into)
}
}
@@ -16,14 +16,13 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1" }
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
serde = { version = "1.0.195", features = ["derive"] }
sp-api = { path = "../../../primitives/api" }
sp-blockchain = { path = "../../../primitives/blockchain" }
sp-core = { path = "../../../primitives/core" }
sp-mmr-primitives = { path = "../../../primitives/merkle-mountain-range" }
sp-runtime = { path = "../../../primitives/runtime" }
anyhow = "1"
[dev-dependencies]
serde_json = "1.0.111"
@@ -26,7 +26,7 @@ use codec::{Codec, Decode, Encode};
use jsonrpsee::{
core::{async_trait, RpcResult},
proc_macros::rpc,
types::error::{CallError, ErrorObject},
types::{error::ErrorObject, ErrorObjectOwned},
};
use serde::{Deserialize, Serialize};
@@ -189,11 +189,9 @@ where
fn verify_proof(&self, proof: LeavesProof<<Block as BlockT>::Hash>) -> RpcResult<bool> {
let mut api = self.client.runtime_api();
let leaves = Decode::decode(&mut &proof.leaves.0[..])
.map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?;
let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?;
let decoded_proof = Decode::decode(&mut &proof.proof.0[..])
.map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?;
let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?;
api.register_extension(OffchainDbExt::new(self.offchain_db.clone()));
@@ -211,11 +209,9 @@ where
) -> RpcResult<bool> {
let api = self.client.runtime_api();
let leaves = Decode::decode(&mut &proof.leaves.0[..])
.map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?;
let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?;
let decoded_proof = Decode::decode(&mut &proof.proof.0[..])
.map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?;
let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?;
api.verify_proof_stateless(proof.block_hash, mmr_root, leaves, decoded_proof)
.map_err(runtime_error_into_rpc_error)?
@@ -226,7 +222,7 @@ where
}
/// Converts an mmr-specific error into a [`CallError`].
fn mmr_error_into_rpc_error(err: MmrError) -> CallError {
fn mmr_error_into_rpc_error(err: MmrError) -> ErrorObjectOwned {
let error_code = MMR_ERROR +
match err {
MmrError::LeafNotFound => 1,
@@ -237,16 +233,20 @@ fn mmr_error_into_rpc_error(err: MmrError) -> CallError {
_ => 0,
};
CallError::Custom(ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err))))
ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err)))
}
/// Converts a runtime trap into a [`CallError`].
fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> CallError {
CallError::Custom(ErrorObject::owned(
RUNTIME_ERROR,
"Runtime trapped",
Some(format!("{:?}", err)),
))
fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> ErrorObjectOwned {
ErrorObject::owned(RUNTIME_ERROR, "Runtime trapped", Some(format!("{:?}", err)))
}
fn invalid_params(e: impl std::error::Error) -> ErrorObjectOwned {
ErrorObject::owned(
jsonrpsee::types::error::ErrorCode::InvalidParams.code(),
e.to_string(),
None::<()>,
)
}
#[cfg(test)]
@@ -469,7 +469,7 @@ where
Ok(chain_sync) => chain_sync,
Err(e) => {
error!(target: LOG_TARGET, "Failed to start `ChainSync`.");
return Err(e);
return Err(e)
},
};
// Let `ChainSync` know about connected peers.
+1 -1
View File
@@ -28,4 +28,4 @@ sp-core = { path = "../../primitives/core" }
sp-rpc = { path = "../../primitives/rpc" }
sp-runtime = { path = "../../primitives/runtime" }
sp-version = { path = "../../primitives/version" }
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
+39 -36
View File
@@ -18,10 +18,7 @@
//! Authoring RPC module errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
use sp_runtime::transaction_validity::InvalidTransaction;
/// Author RPC Result type.
@@ -86,98 +83,104 @@ const POOL_NO_TAGS: i32 = POOL_INVALID_TX + 9;
const POOL_INVALID_BLOCK_ID: i32 = POOL_INVALID_TX + 10;
/// The pool is not accepting future transactions.
const POOL_FUTURE_TX: i32 = POOL_INVALID_TX + 11;
/// Other error.
const OTHER_ERR: i32 = BASE_ERROR + 40;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
use sc_transaction_pool_api::error::Error as PoolError;
match e {
Error::BadFormat(e) => CallError::Custom(ErrorObject::owned(
Error::BadFormat(e) => ErrorObject::owned(
BAD_FORMAT,
format!("Extrinsic has invalid format: {}", e),
None::<()>,
)),
Error::Verification(e) => CallError::Custom(ErrorObject::owned(
),
Error::Verification(e) => ErrorObject::owned(
VERIFICATION_ERROR,
format!("Verification Error: {}", e),
Some(format!("{:?}", e)),
)),
),
Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => {
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_INVALID_TX,
"Invalid Transaction",
Some(format!("Custom error: {}", e)),
))
)
},
Error::Pool(PoolError::InvalidTransaction(e)) => {
let msg: &str = e.into();
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_INVALID_TX,
"Invalid Transaction",
Some(msg),
))
)
},
Error::Pool(PoolError::UnknownTransaction(e)) => {
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_UNKNOWN_VALIDITY,
"Unknown Transaction Validity",
Some(format!("{:?}", e)),
))
)
},
Error::Pool(PoolError::TemporarilyBanned) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_TEMPORARILY_BANNED,
"Transaction is temporarily banned",
None::<()>,
)),
),
Error::Pool(PoolError::AlreadyImported(hash)) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_ALREADY_IMPORTED,
"Transaction Already Imported",
Some(format!("{:?}", hash)),
)),
Error::Pool(PoolError::TooLowPriority { old, new }) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::TooLowPriority { old, new }) => ErrorObject::owned(
POOL_TOO_LOW_PRIORITY,
format!("Priority is too low: ({} vs {})", old, new),
Some("The transaction has too low priority to replace another transaction already in the pool.")
)),
),
Error::Pool(PoolError::CycleDetected) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_CYCLE_DETECTED,
"Cycle Detected",
None::<()>
)),
Error::Pool(PoolError::ImmediatelyDropped) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::ImmediatelyDropped) => ErrorObject::owned(
POOL_IMMEDIATELY_DROPPED,
"Immediately Dropped",
Some("The transaction couldn't enter the pool because of the limit"),
)),
Error::Pool(PoolError::Unactionable) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::Unactionable) => ErrorObject::owned(
POOL_UNACTIONABLE,
"Unactionable",
Some("The transaction is unactionable since it is not propagable and \
the local node does not author blocks")
)),
Error::Pool(PoolError::NoTagsProvided) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::NoTagsProvided) => ErrorObject::owned(
POOL_NO_TAGS,
"No tags provided",
Some("Transaction does not provide any tags, so the pool can't identify it")
)),
),
Error::Pool(PoolError::InvalidBlockId(_)) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_INVALID_BLOCK_ID,
"The provided block ID is not valid",
None::<()>
)),
),
Error::Pool(PoolError::RejectedFutureTransaction) => {
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_FUTURE_TX,
"The pool is not accepting future transactions",
None::<()>,
))
)
},
Error::UnsafeRpcCalled(e) => e.into(),
e => CallError::Failed(e.into()),
}.into()
other => ErrorObject::owned(
OTHER_ERR,
other.to_string(),
None::<()>,
)
}
}
}
+12 -11
View File
@@ -18,27 +18,28 @@
//! Substrate block-author/full-node API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sc_transaction_pool_api::TransactionStatus;
use sp_core::Bytes;
pub mod error;
pub mod hash;
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sc_transaction_pool_api::TransactionStatus;
use sp_core::Bytes;
/// Substrate authoring RPC API
#[rpc(client, server)]
pub trait AuthorApi<Hash, BlockHash> {
/// Submit hex-encoded extrinsic for inclusion in block.
#[method(name = "author_submitExtrinsic")]
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>;
async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<Hash, Error>;
/// Insert a key into the keystore.
#[method(name = "author_insertKey")]
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()>;
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<(), Error>;
/// Generate new session keys and returns the corresponding public keys.
#[method(name = "author_rotateKeys")]
fn rotate_keys(&self) -> RpcResult<Bytes>;
fn rotate_keys(&self) -> Result<Bytes, Error>;
/// Checks if the keystore has private keys for the given session public keys.
///
@@ -46,24 +47,24 @@ pub trait AuthorApi<Hash, BlockHash> {
///
/// Returns `true` iff all private keys could be found.
#[method(name = "author_hasSessionKeys")]
fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool>;
fn has_session_keys(&self, session_keys: Bytes) -> Result<bool, Error>;
/// Checks if the keystore has private keys for the given public key and key type.
///
/// Returns `true` if a private key could be found.
#[method(name = "author_hasKey")]
fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool>;
fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool, Error>;
/// Returns all pending extrinsics, potentially grouped by sender.
#[method(name = "author_pendingExtrinsics")]
fn pending_extrinsics(&self) -> RpcResult<Vec<Bytes>>;
fn pending_extrinsics(&self) -> Result<Vec<Bytes>, Error>;
/// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting.
#[method(name = "author_removeExtrinsic")]
fn remove_extrinsic(
&self,
bytes_or_hash: Vec<hash::ExtrinsicOrHash<Hash>>,
) -> RpcResult<Vec<Hash>>;
) -> Result<Vec<Hash>, Error>;
/// Submit an extrinsic to watch.
///
+5 -9
View File
@@ -18,10 +18,7 @@
//! Error helpers for Chain RPC module.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::{error::ErrorObject, ErrorObjectOwned};
/// Chain RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -39,12 +36,11 @@ pub enum Error {
/// Base error code for all chain errors.
const BASE_ERROR: i32 = crate::error::base::CHAIN;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
match e {
Error::Other(message) =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, message, None::<()>)).into(),
e => e.into(),
Error::Other(message) => ErrorObject::owned(BASE_ERROR + 1, message, None::<()>),
e => ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>),
}
}
}
+8 -7
View File
@@ -18,20 +18,21 @@
//! Substrate blockchain API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
pub mod error;
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
#[rpc(client, server)]
pub trait ChainApi<Number, Hash, Header, SignedBlock> {
/// Get header.
#[method(name = "chain_getHeader", blocking)]
fn header(&self, hash: Option<Hash>) -> RpcResult<Option<Header>>;
fn header(&self, hash: Option<Hash>) -> Result<Option<Header>, Error>;
/// Get header and body of a block.
#[method(name = "chain_getBlock", blocking)]
fn block(&self, hash: Option<Hash>) -> RpcResult<Option<SignedBlock>>;
fn block(&self, hash: Option<Hash>) -> Result<Option<SignedBlock>, Error>;
/// Get hash of the n-th block in the canon chain.
///
@@ -40,11 +41,11 @@ pub trait ChainApi<Number, Hash, Header, SignedBlock> {
fn block_hash(
&self,
hash: Option<ListOrValue<NumberOrHex>>,
) -> RpcResult<ListOrValue<Option<Hash>>>;
) -> Result<ListOrValue<Option<Hash>>, Error>;
/// Get hash of the last finalized block in the canon chain.
#[method(name = "chain_getFinalizedHead", aliases = ["chain_getFinalisedHead"], blocking)]
fn finalized_head(&self) -> RpcResult<Hash>;
fn finalized_head(&self) -> Result<Hash, Error>;
/// All head subscription.
#[subscription(
@@ -17,8 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Substrate child state API
use crate::state::ReadProof;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use crate::state::{Error, ReadProof};
use jsonrpsee::proc_macros::rpc;
use sp_core::storage::{PrefixedStorageKey, StorageData, StorageKey};
/// Substrate child state API
@@ -35,7 +35,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
prefix: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
) -> Result<Vec<StorageKey>, Error>;
/// Returns the keys with prefix from a child storage with pagination support.
/// Up to `count` keys will be returned.
@@ -48,7 +48,7 @@ pub trait ChildStateApi<Hash> {
count: u32,
start_key: Option<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
) -> Result<Vec<StorageKey>, Error>;
/// Returns a child storage entry at a specific block's state.
#[method(name = "childstate_getStorage", blocking)]
@@ -57,7 +57,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
key: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<StorageData>>;
) -> Result<Option<StorageData>, Error>;
/// Returns child storage entries for multiple keys at a specific block's state.
#[method(name = "childstate_getStorageEntries", blocking)]
@@ -66,7 +66,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<Option<StorageData>>>;
) -> Result<Vec<Option<StorageData>>, Error>;
/// Returns the hash of a child storage entry at a block's state.
#[method(name = "childstate_getStorageHash", blocking)]
@@ -75,7 +75,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
key: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<Hash>>;
) -> Result<Option<Hash>, Error>;
/// Returns the size of a child storage entry at a block's state.
#[method(name = "childstate_getStorageSize", blocking)]
@@ -84,7 +84,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
key: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<u64>>;
) -> Result<Option<u64>, Error>;
/// Returns proof of storage for child key entries at a specific block's state.
#[method(name = "state_getChildReadProof", blocking)]
@@ -93,5 +93,5 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<ReadProof<Hash>>;
) -> Result<ReadProof<Hash>, Error>;
}
+9 -14
View File
@@ -18,10 +18,10 @@
//! Error helpers for Dev RPC module.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// Dev RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
/// Dev RPC errors.
#[derive(Debug, thiserror::Error)]
@@ -46,21 +46,16 @@ pub enum Error {
/// Base error code for all dev errors.
const BASE_ERROR: i32 = crate::error::base::DEV;
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
let msg = e.to_string();
match e {
Error::BlockQueryError(_) =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>)),
Error::BlockExecutionFailed =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>)),
Error::WitnessCompactionFailed =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>)),
Error::ProofExtractionFailed =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 5, msg, None::<()>)),
Error::BlockQueryError(_) => ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>),
Error::BlockExecutionFailed => ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>),
Error::WitnessCompactionFailed => ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>),
Error::ProofExtractionFailed => ErrorObject::owned(BASE_ERROR + 5, msg, None::<()>),
Error::UnsafeRpcCalled(e) => e.into(),
}
.into()
}
}
+3 -2
View File
@@ -23,7 +23,8 @@
pub mod error;
use codec::{Decode, Encode};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use error::Error;
use jsonrpsee::proc_macros::rpc;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
@@ -59,5 +60,5 @@ pub trait DevApi<Hash> {
/// at the queried node. If either the specified block or the parent is pruned,
/// this function will return `None`.
#[method(name = "dev_getBlockStats")]
fn block_stats(&self, block_hash: Hash) -> RpcResult<Option<BlockStats>>;
fn block_stats(&self, block_hash: Hash) -> Result<Option<BlockStats>, Error>;
}
+1 -1
View File
@@ -25,7 +25,7 @@
mod error;
mod policy;
pub use policy::DenyUnsafe;
pub use policy::{DenyUnsafe, UnsafeRpcError};
pub mod author;
pub mod chain;
+3 -3
View File
@@ -18,7 +18,7 @@
//! Mixnet RPC module errors.
use jsonrpsee::types::error::{CallError, ErrorObject};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
use sc_mixnet::{PostErr, RemoteErr, TopologyErr};
/// Mixnet RPC error type.
@@ -27,7 +27,7 @@ pub struct Error(pub sc_mixnet::Error);
/// Base code for all mixnet errors.
const BASE_ERROR: i32 = crate::error::base::MIXNET;
impl From<Error> for jsonrpsee::core::Error {
impl From<Error> for ErrorObjectOwned {
fn from(err: Error) -> Self {
let code = match err.0 {
sc_mixnet::Error::ServiceUnavailable => BASE_ERROR + 1,
@@ -43,6 +43,6 @@ impl From<Error> for jsonrpsee::core::Error {
sc_mixnet::Error::Remote(RemoteErr::Other(_)) => BASE_ERROR + 200,
sc_mixnet::Error::Remote(RemoteErr::Decode(_)) => BASE_ERROR + 201,
};
CallError::Custom(ErrorObject::owned(code, err.0.to_string(), None::<()>)).into()
ErrorObject::owned(code, err.0.to_string(), None::<()>)
}
}
+3 -2
View File
@@ -20,12 +20,13 @@
pub mod error;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sp_core::Bytes;
#[rpc(client, server)]
pub trait MixnetApi {
/// Submit encoded extrinsic over the mixnet for inclusion in block.
#[method(name = "mixnet_submitExtrinsic")]
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<()>;
async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<(), Error>;
}
@@ -18,10 +18,7 @@
//! Offchain RPC errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// Offchain RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -40,15 +37,14 @@ pub enum Error {
/// Base error code for all offchain errors.
const BASE_ERROR: i32 = crate::error::base::OFFCHAIN;
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
match e {
Error::UnavailableStorageKind => CallError::Custom(ErrorObject::owned(
Error::UnavailableStorageKind => ErrorObject::owned(
BASE_ERROR + 1,
"This storage kind is not available yet",
None::<()>,
))
.into(),
),
Error::UnsafeRpcCalled(e) => e.into(),
}
}
+6 -5
View File
@@ -18,19 +18,20 @@
//! Substrate offchain API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sp_core::{offchain::StorageKind, Bytes};
pub mod error;
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sp_core::{offchain::StorageKind, Bytes};
/// Substrate offchain RPC API
#[rpc(client, server)]
pub trait OffchainApi {
/// Set offchain local storage under given key and prefix.
#[method(name = "offchain_localStorageSet")]
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()>;
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error>;
/// Get offchain local storage under given key and prefix.
#[method(name = "offchain_localStorageGet")]
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult<Option<Bytes>>;
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>, Error>;
}
+4 -20
View File
@@ -21,13 +21,7 @@
//! Contains a `DenyUnsafe` type that can be used to deny potentially unsafe
//! RPC when accessed externally.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::{
error::{CallError, ErrorCode},
ErrorObject,
},
};
use jsonrpsee::types::{error::ErrorCode, ErrorObject, ErrorObjectOwned};
/// Signifies whether a potentially unsafe RPC should be denied.
#[derive(Clone, Copy, Debug)]
@@ -61,18 +55,8 @@ impl std::fmt::Display for UnsafeRpcError {
impl std::error::Error for UnsafeRpcError {}
impl From<UnsafeRpcError> for CallError {
fn from(e: UnsafeRpcError) -> CallError {
CallError::Custom(ErrorObject::owned(
ErrorCode::MethodNotFound.code(),
e.to_string(),
None::<()>,
))
}
}
impl From<UnsafeRpcError> for JsonRpseeError {
fn from(e: UnsafeRpcError) -> JsonRpseeError {
JsonRpseeError::Call(e.into())
impl From<UnsafeRpcError> for ErrorObjectOwned {
fn from(e: UnsafeRpcError) -> ErrorObjectOwned {
ErrorObject::owned(ErrorCode::MethodNotFound.code(), e.to_string(), None::<()>)
}
}
+7 -11
View File
@@ -18,10 +18,8 @@
//! State RPC errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// State RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -57,16 +55,14 @@ pub enum Error {
/// Base code for all state errors.
const BASE_ERROR: i32 = crate::error::base::STATE;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
match e {
Error::InvalidBlockRange { .. } =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>))
.into(),
ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>),
Error::InvalidCount { .. } =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>))
.into(),
e => Self::to_call_error(e),
ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>),
e => ErrorObject::owned(BASE_ERROR + 3, e.to_string(), None::<()>),
}
}
}
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use sp_core::Bytes;
/// ReadProof struct returned by the RPC
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadProof<Hash> {
/// Block hash used to generate the proof
+24 -14
View File
@@ -18,7 +18,7 @@
//! Substrate state API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use jsonrpsee::proc_macros::rpc;
use sp_core::{
storage::{StorageChangeSet, StorageData, StorageKey},
Bytes,
@@ -29,18 +29,23 @@ pub mod error;
pub mod helpers;
pub use self::helpers::ReadProof;
pub use error::Error;
/// Substrate state API
#[rpc(client, server)]
pub trait StateApi<Hash> {
/// Call a method from the runtime API at a block's state.
#[method(name = "state_call", aliases = ["state_callAt"], blocking)]
fn call(&self, name: String, bytes: Bytes, hash: Option<Hash>) -> RpcResult<Bytes>;
fn call(&self, name: String, bytes: Bytes, hash: Option<Hash>) -> Result<Bytes, Error>;
/// Returns the keys with prefix, leave empty to get all the keys.
#[method(name = "state_getKeys", blocking)]
#[deprecated(since = "2.0.0", note = "Please use `getKeysPaged` with proper paging support")]
fn storage_keys(&self, prefix: StorageKey, hash: Option<Hash>) -> RpcResult<Vec<StorageKey>>;
fn storage_keys(
&self,
prefix: StorageKey,
hash: Option<Hash>,
) -> Result<Vec<StorageKey>, Error>;
/// Returns the keys with prefix, leave empty to get all the keys
#[method(name = "state_getPairs", blocking)]
@@ -48,7 +53,7 @@ pub trait StateApi<Hash> {
&self,
prefix: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Vec<(StorageKey, StorageData)>>;
) -> Result<Vec<(StorageKey, StorageData)>, Error>;
/// Returns the keys with prefix with pagination support.
/// Up to `count` keys will be returned.
@@ -60,27 +65,28 @@ pub trait StateApi<Hash> {
count: u32,
start_key: Option<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
) -> Result<Vec<StorageKey>, Error>;
/// Returns a storage entry at a specific block's state.
#[method(name = "state_getStorage", aliases = ["state_getStorageAt"], blocking)]
fn storage(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<StorageData>>;
fn storage(&self, key: StorageKey, hash: Option<Hash>) -> Result<Option<StorageData>, Error>;
/// Returns the hash of a storage entry at a block's state.
#[method(name = "state_getStorageHash", aliases = ["state_getStorageHashAt"], blocking)]
fn storage_hash(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<Hash>>;
fn storage_hash(&self, key: StorageKey, hash: Option<Hash>) -> Result<Option<Hash>, Error>;
/// Returns the size of a storage entry at a block's state.
#[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])]
async fn storage_size(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<u64>>;
async fn storage_size(&self, key: StorageKey, hash: Option<Hash>)
-> Result<Option<u64>, Error>;
/// Returns the runtime metadata as an opaque blob.
#[method(name = "state_getMetadata", blocking)]
fn metadata(&self, hash: Option<Hash>) -> RpcResult<Bytes>;
fn metadata(&self, hash: Option<Hash>) -> Result<Bytes, Error>;
/// Get the runtime version.
#[method(name = "state_getRuntimeVersion", aliases = ["chain_getRuntimeVersion"], blocking)]
fn runtime_version(&self, hash: Option<Hash>) -> RpcResult<RuntimeVersion>;
fn runtime_version(&self, hash: Option<Hash>) -> Result<RuntimeVersion, Error>;
/// Query historical storage entries (by key) starting from a block given as the second
/// parameter.
@@ -95,7 +101,7 @@ pub trait StateApi<Hash> {
keys: Vec<StorageKey>,
block: Hash,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
) -> Result<Vec<StorageChangeSet<Hash>>, Error>;
/// Query storage entries (by key) at a block hash given as the second parameter.
/// NOTE: Each StorageChangeSet in the result corresponds to exactly one element --
@@ -105,11 +111,15 @@ pub trait StateApi<Hash> {
&self,
keys: Vec<StorageKey>,
at: Option<Hash>,
) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
) -> Result<Vec<StorageChangeSet<Hash>>, Error>;
/// Returns proof of storage entries at a specific block's state.
#[method(name = "state_getReadProof", blocking)]
fn read_proof(&self, keys: Vec<StorageKey>, hash: Option<Hash>) -> RpcResult<ReadProof<Hash>>;
fn read_proof(
&self,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> Result<ReadProof<Hash>, Error>;
/// New runtime version subscription
#[subscription(
@@ -288,5 +298,5 @@ pub trait StateApi<Hash> {
targets: Option<String>,
storage_keys: Option<String>,
methods: Option<String>,
) -> RpcResult<sp_rpc::tracing::TraceBlockResponse>;
) -> Result<sp_rpc::tracing::TraceBlockResponse, Error>;
}
@@ -18,10 +18,7 @@
//! Statement RPC errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// Statement RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -40,15 +37,14 @@ pub enum Error {
/// Base error code for all statement errors.
const BASE_ERROR: i32 = crate::error::base::STATEMENT;
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
match e {
Error::StatementStore(message) => CallError::Custom(ErrorObject::owned(
Error::StatementStore(message) => ErrorObject::owned(
BASE_ERROR + 1,
format!("Statement store error: {message}"),
None::<()>,
))
.into(),
),
Error::UnsafeRpcCalled(e) => e.into(),
}
}
+17 -12
View File
@@ -19,9 +19,9 @@
//! System RPC module errors.
use crate::system::helpers::Health;
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
use jsonrpsee::types::{
error::{ErrorCode, ErrorObject},
ErrorObjectOwned,
};
/// System RPC Result type.
@@ -36,6 +36,12 @@ pub enum Error {
/// Peer argument is malformatted.
#[error("{0}")]
MalformattedPeerArg(String),
/// Call to an unsafe RPC was denied.
#[error(transparent)]
UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError),
/// Internal error.
#[error("{0}")]
Internal(String),
}
// Base code for all system errors.
@@ -45,17 +51,16 @@ const NOT_HEALTHY_ERROR: i32 = BASE_ERROR + 1;
// Peer argument is malformatted.
const MALFORMATTED_PEER_ARG_ERROR: i32 = BASE_ERROR + 2;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
match e {
Error::NotHealthy(ref h) =>
CallError::Custom(ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h))),
Error::MalformattedPeerArg(e) => CallError::Custom(ErrorObject::owned(
MALFORMATTED_PEER_ARG_ERROR + 2,
e,
None::<()>,
)),
ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h)),
Error::MalformattedPeerArg(e) =>
ErrorObject::owned(MALFORMATTED_PEER_ARG_ERROR, e, None::<()>),
Error::UnsafeRpcCalled(e) => e.into(),
Error::Internal(e) =>
ErrorObjectOwned::owned(ErrorCode::InternalError.code(), e, None::<()>),
}
.into()
}
}
@@ -38,7 +38,7 @@ pub struct SystemInfo {
}
/// Health struct returned by the RPC
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Health {
/// Number of connected peers
@@ -58,7 +58,7 @@ impl fmt::Display for Health {
}
/// Network Peer information
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PeerInfo<Hash, Number> {
/// Peer ID
@@ -72,7 +72,7 @@ pub struct PeerInfo<Hash, Number> {
}
/// The role the node is running as
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum NodeRole {
/// The node is a full node
Full,
@@ -81,7 +81,7 @@ pub enum NodeRole {
}
/// The state of the syncing of the node.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncState<Number> {
/// Height of the block at which syncing started.
+22 -24
View File
@@ -18,38 +18,36 @@
//! Substrate system API.
use jsonrpsee::{
core::{JsonValue, RpcResult},
proc_macros::rpc,
};
pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
pub mod error;
pub mod helpers;
use jsonrpsee::{core::JsonValue, proc_macros::rpc};
pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
pub use error::Error;
/// Substrate system RPC API
#[rpc(client, server)]
pub trait SystemApi<Hash, Number> {
/// Get the node's implementation name. Plain old string.
#[method(name = "system_name")]
fn system_name(&self) -> RpcResult<String>;
fn system_name(&self) -> Result<String, Error>;
/// Get the node implementation's version. Should be a semver string.
#[method(name = "system_version")]
fn system_version(&self) -> RpcResult<String>;
fn system_version(&self) -> Result<String, Error>;
/// Get the chain's name. Given as a string identifier.
#[method(name = "system_chain")]
fn system_chain(&self) -> RpcResult<String>;
fn system_chain(&self) -> Result<String, Error>;
/// Get the chain's type.
#[method(name = "system_chainType")]
fn system_type(&self) -> RpcResult<sc_chain_spec::ChainType>;
fn system_type(&self) -> Result<sc_chain_spec::ChainType, Error>;
/// Get a custom set of properties as a JSON object, defined in the chain spec.
#[method(name = "system_properties")]
fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties>;
fn system_properties(&self) -> Result<sc_chain_spec::Properties, Error>;
/// Return health status of the node.
///
@@ -57,22 +55,22 @@ pub trait SystemApi<Hash, Number> {
/// - connected to some peers (unless running in dev mode)
/// - not performing a major sync
#[method(name = "system_health")]
async fn system_health(&self) -> RpcResult<Health>;
async fn system_health(&self) -> Result<Health, Error>;
/// Returns the base58-encoded PeerId of the node.
#[method(name = "system_localPeerId")]
async fn system_local_peer_id(&self) -> RpcResult<String>;
async fn system_local_peer_id(&self) -> Result<String, Error>;
/// Returns the multi-addresses that the local node is listening on
///
/// The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to
/// be passed to `addReservedPeer` or as a bootnode address for example.
#[method(name = "system_localListenAddresses")]
async fn system_local_listen_addresses(&self) -> RpcResult<Vec<String>>;
async fn system_local_listen_addresses(&self) -> Result<Vec<String>, Error>;
/// Returns currently connected peers
#[method(name = "system_peers")]
async fn system_peers(&self) -> RpcResult<Vec<PeerInfo<Hash, Number>>>;
async fn system_peers(&self) -> Result<Vec<PeerInfo<Hash, Number>>, Error>;
/// Returns current state of the network.
///
@@ -81,7 +79,7 @@ pub trait SystemApi<Hash, Number> {
// TODO: the future of this call is uncertain: https://github.com/paritytech/substrate/issues/1890
// https://github.com/paritytech/substrate/issues/5541
#[method(name = "system_unstable_networkState")]
async fn system_network_state(&self) -> RpcResult<JsonValue>;
async fn system_network_state(&self) -> Result<JsonValue, Error>;
/// Adds a reserved peer. Returns the empty string or an error. The string
/// parameter should encode a `p2p` multiaddr.
@@ -89,25 +87,25 @@ pub trait SystemApi<Hash, Number> {
/// `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`
/// is an example of a valid, passing multiaddr with PeerId attached.
#[method(name = "system_addReservedPeer")]
async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()>;
async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error>;
/// Remove a reserved peer. Returns the empty string or an error. The string
/// should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`.
#[method(name = "system_removeReservedPeer")]
async fn system_remove_reserved_peer(&self, peer_id: String) -> RpcResult<()>;
async fn system_remove_reserved_peer(&self, peer_id: String) -> Result<(), Error>;
/// Returns the list of reserved peers
#[method(name = "system_reservedPeers")]
async fn system_reserved_peers(&self) -> RpcResult<Vec<String>>;
async fn system_reserved_peers(&self) -> Result<Vec<String>, Error>;
/// Returns the roles the node is running as.
#[method(name = "system_nodeRoles")]
async fn system_node_roles(&self) -> RpcResult<Vec<NodeRole>>;
async fn system_node_roles(&self) -> Result<Vec<NodeRole>, Error>;
/// Returns the state of the syncing of the node: starting block, current best block, highest
/// known block.
#[method(name = "system_syncState")]
async fn system_sync_state(&self) -> RpcResult<SyncState<Number>>;
async fn system_sync_state(&self) -> Result<SyncState<Number>, Error>;
/// Adds the supplied directives to the current log filter
///
@@ -115,9 +113,9 @@ pub trait SystemApi<Hash, Number> {
///
/// `sync=debug,state=trace`
#[method(name = "system_addLogFilter")]
fn system_add_log_filter(&self, directives: String) -> RpcResult<()>;
fn system_add_log_filter(&self, directives: String) -> Result<(), Error>;
/// Resets the log filter to Substrate defaults
#[method(name = "system_resetLogFilter")]
fn system_reset_log_filter(&self) -> RpcResult<()>;
fn system_reset_log_filter(&self) -> Result<(), Error>;
}
+2 -2
View File
@@ -16,11 +16,11 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
log = "0.4.17"
serde_json = "1.0.111"
tokio = { version = "1.22.0", features = ["parking_lot"] }
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" }
tower-http = { version = "0.4.0", features = ["cors"] }
tower = "0.4.13"
tower = { version = "0.4.13", features = ["util"] }
http = "0.2.8"
+33 -30
View File
@@ -22,15 +22,14 @@
pub mod middleware;
use std::{error::Error as StdError, net::SocketAddr, time::Duration};
use http::header::HeaderValue;
use jsonrpsee::{
server::{
middleware::proxy_get_request::ProxyGetRequestLayer, AllowHosts, ServerBuilder,
ServerHandle,
},
server::middleware::{HostFilterLayer, ProxyGetRequestLayer},
RpcModule,
};
use std::{error::Error as StdError, net::SocketAddr};
use tokio::net::TcpListener;
use tower_http::cors::{AllowOrigin, CorsLayer};
pub use crate::middleware::RpcMetrics;
@@ -42,7 +41,7 @@ pub use jsonrpsee::core::{
const MEGABYTE: u32 = 1024 * 1024;
/// Type alias for the JSON-RPC server.
pub type Server = ServerHandle;
pub type Server = jsonrpsee::server::ServerHandle;
/// RPC server configuration.
#[derive(Debug)]
@@ -61,6 +60,8 @@ pub struct Config<'a, M: Send + Sync + 'static> {
pub max_payload_out_mb: u32,
/// Metrics.
pub metrics: Option<RpcMetrics>,
/// Message buffer size
pub message_buffer_capacity: u32,
/// RPC API.
pub rpc_api: RpcModule<M>,
/// Subscription ID provider.
@@ -72,7 +73,7 @@ pub struct Config<'a, M: Send + Sync + 'static> {
/// Start RPC server listening on given address.
pub async fn start_server<M: Send + Sync + 'static>(
config: Config<'_, M>,
) -> Result<ServerHandle, Box<dyn StdError + Send + Sync>> {
) -> Result<Server, Box<dyn StdError + Send + Sync>> {
let Config {
addrs,
cors,
@@ -81,26 +82,30 @@ pub async fn start_server<M: Send + Sync + 'static>(
max_connections,
max_subs_per_conn,
metrics,
message_buffer_capacity,
id_provider,
tokio_handle,
rpc_api,
} = config;
let host_filter = hosts_filtering(cors.is_some(), &addrs);
let std_listener = TcpListener::bind(addrs.as_slice()).await?.into_std()?;
let local_addr = std_listener.local_addr().ok();
let host_filter = hosts_filtering(cors.is_some(), local_addr);
let middleware = tower::ServiceBuilder::new()
.option_layer(host_filter)
// Proxy `GET /health` requests to internal `system_health` method.
.layer(ProxyGetRequestLayer::new("/health", "system_health")?)
.layer(try_into_cors(cors)?);
let mut builder = ServerBuilder::new()
let mut builder = jsonrpsee::server::Server::builder()
.max_request_body_size(max_payload_in_mb.saturating_mul(MEGABYTE))
.max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE))
.max_connections(max_connections)
.max_subscriptions_per_connection(max_subs_per_conn)
.ping_interval(std::time::Duration::from_secs(30))
.set_host_filtering(host_filter)
.ping_interval(Duration::from_secs(30))
.set_middleware(middleware)
.set_message_buffer_capacity(message_buffer_capacity)
.custom_tokio_runtime(tokio_handle);
if let Some(provider) = id_provider {
@@ -110,36 +115,34 @@ pub async fn start_server<M: Send + Sync + 'static>(
};
let rpc_api = build_rpc_api(rpc_api);
let (handle, addr) = if let Some(metrics) = metrics {
let server = builder.set_logger(metrics).build(&addrs[..]).await?;
let addr = server.local_addr();
(server.start(rpc_api)?, addr)
let handle = if let Some(metrics) = metrics {
let server = builder.set_logger(metrics).build_from_tcp(std_listener)?;
server.start(rpc_api)
} else {
let server = builder.build(&addrs[..]).await?;
let addr = server.local_addr();
(server.start(rpc_api)?, addr)
let server = builder.build_from_tcp(std_listener)?;
server.start(rpc_api)
};
log::info!(
"Running JSON-RPC server: addr={}, allowed origins={}",
addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()),
local_addr.map_or_else(|| "unknown".to_string(), |a| a.to_string()),
format_cors(cors)
);
Ok(handle)
}
fn hosts_filtering(enabled: bool, addrs: &[SocketAddr]) -> AllowHosts {
fn hosts_filtering(enabled: bool, addr: Option<SocketAddr>) -> Option<HostFilterLayer> {
// If the local_addr failed, fallback to wildcard.
let port = addr.map_or("*".to_string(), |p| p.port().to_string());
if enabled {
// NOTE The listening addresses are whitelisted by default.
let mut hosts = Vec::with_capacity(addrs.len() * 2);
for addr in addrs {
hosts.push(format!("localhost:{}", addr.port()).into());
hosts.push(format!("127.0.0.1:{}", addr.port()).into());
}
AllowHosts::Only(hosts)
// NOTE: The listening addresses are whitelisted by default.
let hosts =
[format!("localhost:{port}"), format!("127.0.0.1:{port}"), format!("[::1]:{port}")];
Some(HostFilterLayer::new(hosts).expect("Valid hosts; qed"))
} else {
AllowHosts::Any
None
}
}
@@ -151,9 +154,9 @@ fn build_rpc_api<M: Send + Sync + 'static>(mut rpc_api: RpcModule<M>) -> RpcModu
rpc_api
.register_method("rpc_methods", move |_, _| {
Ok(serde_json::json!({
serde_json::json!({
"methods": available_methods,
}))
})
})
.expect("infallible all other methods have their own address space; qed");
@@ -18,7 +18,9 @@
//! RPC middleware to collect prometheus metrics on RPC calls.
use jsonrpsee::server::logger::{HttpRequest, Logger, MethodKind, Params, TransportProtocol};
use jsonrpsee::server::logger::{
HttpRequest, Logger, MethodKind, Params, SuccessOrError, TransportProtocol,
};
use prometheus_endpoint::{
register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry,
U64,
@@ -176,7 +178,7 @@ impl Logger for RpcMetrics {
fn on_result(
&self,
name: &str,
success: bool,
success_or_error: SuccessOrError,
started_at: Self::Instant,
transport: TransportProtocol,
) {
@@ -197,7 +199,7 @@ impl Logger for RpcMetrics {
name,
// the label "is_error", so `success` should be regarded as false
// and vice-versa to be registrered correctly.
if success { "false" } else { "true" },
if success_or_error.is_success() { "false" } else { "true" },
])
.inc();
}
+2 -1
View File
@@ -16,7 +16,7 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
# Internal chain structures for "chain_spec".
sc-chain-spec = { path = "../chain-spec" }
# Pool for submitting extrinsics required by "transaction"
@@ -29,6 +29,7 @@ sp-blockchain = { path = "../../primitives/blockchain" }
sp-version = { path = "../../primitives/version" }
sc-client-api = { path = "../api" }
sc-utils = { path = "../utils" }
sc-rpc = { path = "../rpc" }
codec = { package = "parity-scale-codec", version = "3.6.1" }
thiserror = "1.0"
serde = "1.0"
@@ -18,10 +18,7 @@
//! Error helpers for `archive` RPC module.
use jsonrpsee::{
core::Error as RpcError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::ErrorObject;
/// ChainHead RPC errors.
#[derive(Debug, thiserror::Error)]
@@ -58,9 +55,3 @@ impl From<Error> for ErrorObject<'static> {
.into()
}
}
impl From<Error> for RpcError {
fn from(e: Error) -> Self {
CallError::Custom(e.into()).into()
}
}
@@ -29,10 +29,8 @@ use super::{archive::Archive, *};
use assert_matches::assert_matches;
use codec::{Decode, Encode};
use jsonrpsee::{
core::error::Error,
rpc_params,
types::{error::CallError, EmptyServerParams as EmptyParams},
RpcModule,
core::{EmptyServerParams as EmptyParams, Error},
rpc_params, RpcModule,
};
use sc_block_builder::BlockBuilderBuilder;
use sc_client_api::ChildInfo;
@@ -289,7 +287,7 @@ async fn archive_call() {
)
.await
.unwrap_err();
assert_matches!(err, Error::Call(CallError::Custom(ref err)) if err.code() == 3001 && err.message().contains("Invalid parameter"));
assert_matches!(err, Error::Call(err) if err.code() == 3001 && err.message().contains("Invalid parameter"));
// Pass an invalid parameters that cannot be decode.
let err = api
@@ -300,7 +298,7 @@ async fn archive_call() {
)
.await
.unwrap_err();
assert_matches!(err, Error::Call(CallError::Custom(ref err)) if err.code() == 3001 && err.message().contains("Invalid parameter"));
assert_matches!(err, Error::Call(err) if err.code() == 3001 && err.message().contains("Invalid parameter"));
// Invalid hash.
let result: MethodResult = api
@@ -20,10 +20,13 @@
//! API trait of the chain head.
use crate::{
chain_head::event::{FollowEvent, MethodResponse},
chain_head::{
error::Error,
event::{FollowEvent, MethodResponse},
},
common::events::StorageQuery,
};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use jsonrpsee::proc_macros::rpc;
use sp_rpc::list::ListOrValue;
#[rpc(client, server)]
@@ -56,7 +59,7 @@ pub trait ChainHeadApi<Hash> {
&self,
follow_subscription: String,
hash: Hash,
) -> RpcResult<MethodResponse>;
) -> Result<MethodResponse, Error>;
/// Retrieves the header of a pinned block.
///
@@ -75,7 +78,7 @@ pub trait ChainHeadApi<Hash> {
&self,
follow_subscription: String,
hash: Hash,
) -> RpcResult<Option<String>>;
) -> Result<Option<String>, Error>;
/// Returns storage entries at a specific block's state.
///
@@ -89,7 +92,7 @@ pub trait ChainHeadApi<Hash> {
hash: Hash,
items: Vec<StorageQuery<String>>,
child_trie: Option<String>,
) -> RpcResult<MethodResponse>;
) -> Result<MethodResponse, Error>;
/// Call into the Runtime API at a specified block's state.
///
@@ -103,7 +106,7 @@ pub trait ChainHeadApi<Hash> {
hash: Hash,
function: String,
call_parameters: String,
) -> RpcResult<MethodResponse>;
) -> Result<MethodResponse, Error>;
/// Unpin a block or multiple blocks reported by the `follow` method.
///
@@ -120,7 +123,7 @@ pub trait ChainHeadApi<Hash> {
&self,
follow_subscription: String,
hash_or_hashes: ListOrValue<Hash>,
) -> RpcResult<()>;
) -> Result<(), Error>;
/// Resumes a storage fetch started with `chainHead_storage` after it has generated an
/// `operationWaitingForContinue` event.
@@ -133,7 +136,7 @@ pub trait ChainHeadApi<Hash> {
&self,
follow_subscription: String,
operation_id: String,
) -> RpcResult<()>;
) -> Result<(), Error>;
/// Stops an operation started with chainHead_unstable_body, chainHead_unstable_call, or
/// chainHead_unstable_storage. If the operation was still in progress, this interrupts it. If
@@ -147,5 +150,5 @@ pub trait ChainHeadApi<Hash> {
&self,
follow_subscription: String,
operation_id: String,
) -> RpcResult<()>;
) -> Result<(), Error>;
}
@@ -36,15 +36,14 @@ use crate::{
use codec::Encode;
use futures::future::FutureExt;
use jsonrpsee::{
core::{async_trait, RpcResult},
types::{SubscriptionEmptyError, SubscriptionId, SubscriptionResult},
SubscriptionSink,
core::async_trait, types::SubscriptionId, PendingSubscriptionSink, SubscriptionSink,
};
use log::debug;
use sc_client_api::{
Backend, BlockBackend, BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, StorageKey,
StorageProvider,
};
use sc_rpc::utils::to_sub_message;
use sp_api::CallApiAt;
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
use sp_core::{traits::CallContext, Bytes};
@@ -136,27 +135,13 @@ impl<BE: Backend<Block>, Block: BlockT, Client> ChainHead<BE, Block, Client> {
_phantom: PhantomData,
}
}
}
/// Accept the subscription and return the subscription ID on success.
fn accept_subscription(
&self,
sink: &mut SubscriptionSink,
) -> Result<String, SubscriptionEmptyError> {
// The subscription must be accepted before it can provide a valid subscription ID.
sink.accept()?;
let Some(sub_id) = sink.subscription_id() else {
// This can only happen if the subscription was not accepted.
return Err(SubscriptionEmptyError)
};
// Get the string representation for the subscription.
let sub_id = match sub_id {
SubscriptionId::Num(num) => num.to_string(),
SubscriptionId::Str(id) => id.into_owned().into(),
};
Ok(sub_id)
/// Helper to convert the `subscription ID` to a string.
pub fn read_subscription_id_as_string(sink: &SubscriptionSink) -> String {
match sink.subscription_id() {
SubscriptionId::Num(n) => n.to_string(),
SubscriptionId::Str(s) => s.into_owned().into(),
}
}
@@ -190,35 +175,28 @@ where
+ StorageProvider<Block, BE>
+ 'static,
{
fn chain_head_unstable_follow(
&self,
mut sink: SubscriptionSink,
with_runtime: bool,
) -> SubscriptionResult {
let sub_id = match self.accept_subscription(&mut sink) {
Ok(sub_id) => sub_id,
Err(err) => {
sink.close(ChainHeadRpcError::InternalError(
"Cannot generate subscription ID".into(),
));
return Err(err)
},
};
// Keep track of the subscription.
let Some(sub_data) = self.subscriptions.insert_subscription(sub_id.clone(), with_runtime)
else {
// Inserting the subscription can only fail if the JsonRPSee
// generated a duplicate subscription ID.
debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id);
let _ = sink.send(&FollowEvent::<Block::Hash>::Stop);
return Ok(())
};
debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id);
fn chain_head_unstable_follow(&self, pending: PendingSubscriptionSink, with_runtime: bool) {
let subscriptions = self.subscriptions.clone();
let backend = self.backend.clone();
let client = self.client.clone();
let fut = async move {
let Ok(sink) = pending.accept().await else { return };
let sub_id = read_subscription_id_as_string(&sink);
// Keep track of the subscription.
let Some(sub_data) = subscriptions.insert_subscription(sub_id.clone(), with_runtime)
else {
// Inserting the subscription can only fail if the JsonRPSee
// generated a duplicate subscription ID.
debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id);
let msg = to_sub_message(&sink, &FollowEvent::<String>::Stop);
let _ = sink.send(msg).await;
return
};
debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id);
let mut chain_head_follow = ChainHeadFollower::new(
client,
backend,
@@ -234,14 +212,13 @@ where
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
Ok(())
}
fn chain_head_unstable_body(
&self,
follow_subscription: String,
hash: Block::Hash,
) -> RpcResult<MethodResponse> {
) -> Result<MethodResponse, ChainHeadRpcError> {
let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) {
Ok(block) => block,
Err(SubscriptionManagementError::SubscriptionAbsent) |
@@ -293,7 +270,7 @@ where
&self,
follow_subscription: String,
hash: Block::Hash,
) -> RpcResult<Option<String>> {
) -> Result<Option<String>, ChainHeadRpcError> {
let _block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) {
Ok(block) => block,
Err(SubscriptionManagementError::SubscriptionAbsent) |
@@ -309,7 +286,6 @@ where
.header(hash)
.map(|opt_header| opt_header.map(|h| hex_string(&h.encode())))
.map_err(|err| ChainHeadRpcError::InternalError(err.to_string()))
.map_err(Into::into)
}
fn chain_head_unstable_storage(
@@ -318,7 +294,7 @@ where
hash: Block::Hash,
items: Vec<StorageQuery<String>>,
child_trie: Option<String>,
) -> RpcResult<MethodResponse> {
) -> Result<MethodResponse, ChainHeadRpcError> {
// Gain control over parameter parsing and returned error.
let items = items
.into_iter()
@@ -376,7 +352,7 @@ where
hash: Block::Hash,
function: String,
call_parameters: String,
) -> RpcResult<MethodResponse> {
) -> Result<MethodResponse, ChainHeadRpcError> {
let call_parameters = Bytes::from(parse_hex_param(call_parameters)?);
let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) {
@@ -420,14 +396,17 @@ where
});
let _ = block_guard.response_sender().unbounded_send(event);
Ok(MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items: None }))
Ok(MethodResponse::Started(MethodResponseStarted {
operation_id: operation_id.clone(),
discarded_items: None,
}))
}
fn chain_head_unstable_unpin(
&self,
follow_subscription: String,
hash_or_hashes: ListOrValue<Block::Hash>,
) -> RpcResult<()> {
) -> Result<(), ChainHeadRpcError> {
let result = match hash_or_hashes {
ListOrValue::Value(hash) =>
self.subscriptions.unpin_blocks(&follow_subscription, [hash]),
@@ -443,9 +422,9 @@ where
},
Err(SubscriptionManagementError::BlockHashAbsent) => {
// Block is not part of the subscription.
Err(ChainHeadRpcError::InvalidBlock.into())
Err(ChainHeadRpcError::InvalidBlock)
},
Err(_) => Err(ChainHeadRpcError::InvalidBlock.into()),
Err(_) => Err(ChainHeadRpcError::InvalidBlock),
}
}
@@ -453,7 +432,7 @@ where
&self,
follow_subscription: String,
operation_id: String,
) -> RpcResult<()> {
) -> Result<(), ChainHeadRpcError> {
let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id)
else {
return Ok(())
@@ -471,7 +450,7 @@ where
&self,
follow_subscription: String,
operation_id: String,
) -> RpcResult<()> {
) -> Result<(), ChainHeadRpcError> {
let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id)
else {
return Ok(())
@@ -24,7 +24,7 @@ use crate::chain_head::{
BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock, RuntimeEvent,
RuntimeVersionEvent,
},
subscription::{InsertedSubscriptionData, SubscriptionManagement, SubscriptionManagementError},
subscription::{SubscriptionManagement, SubscriptionManagementError},
};
use futures::{
channel::oneshot,
@@ -36,6 +36,7 @@ use log::{debug, error};
use sc_client_api::{
Backend, BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification,
};
use sc_rpc::utils::to_sub_message;
use sp_api::CallApiAt;
use sp_blockchain::{
Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, Info,
@@ -43,6 +44,8 @@ use sp_blockchain::{
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use std::{collections::HashSet, sync::Arc};
use super::subscription::InsertedSubscriptionData;
/// Generates the events of the `chainHead_follow` method.
pub struct ChainHeadFollower<BE: Backend<Block>, Block: BlockT, Client> {
/// Substrate client.
@@ -500,7 +503,7 @@ where
startup_point: &StartupPoint<Block>,
mut stream: EventStream,
mut to_ignore: HashSet<Block::Hash>,
mut sink: SubscriptionSink,
sink: SubscriptionSink,
rx_stop: oneshot::Receiver<()>,
) where
EventStream: Stream<Item = NotificationType<Block>> + Unpin,
@@ -529,35 +532,23 @@ where
self.sub_id,
err
);
let _ = sink.send(&FollowEvent::<String>::Stop);
let msg = to_sub_message(&sink, &FollowEvent::<String>::Stop);
let _ = sink.send(msg).await;
return
},
};
for event in events {
let result = sink.send(&event);
// Migration note: the new version of jsonrpsee returns Result<(), DisconnectError>
// The logic from `Err(err)` should be moved when building the new
// `SubscriptionMessage`.
// For now, jsonrpsee returns:
// Ok(true): message sent
// Ok(false): client disconnected or subscription closed
// Err(err): serder serialization error of the event
if let Err(err) = result {
let msg = to_sub_message(&sink, &event);
if let Err(err) = sink.send(msg).await {
// Failed to submit event.
debug!(
target: LOG_TARGET,
"[follow][id={:?}] Failed to send event {:?}", self.sub_id, err
);
let _ = sink.send(&FollowEvent::<String>::Stop);
return
}
if let Ok(false) = result {
// Client disconnected or subscription was closed.
let msg = to_sub_message(&sink, &FollowEvent::<String>::Stop);
let _ = sink.send(msg).await;
return
}
}
@@ -568,13 +559,14 @@ where
// If we got here either the substrate streams have closed
// or the `Stop` receiver was triggered.
let _ = sink.send(&FollowEvent::<String>::Stop);
let msg = to_sub_message(&sink, &FollowEvent::<String>::Stop);
let _ = sink.send(msg).await;
}
/// Generate the block events for the `chainHead_follow` method.
pub async fn generate_events(
&mut self,
mut sink: SubscriptionSink,
sink: SubscriptionSink,
sub_data: InsertedSubscriptionData<Block>,
) {
// Register for the new block and finalized notifications.
@@ -602,7 +594,8 @@ where
self.sub_id,
err
);
let _ = sink.send(&FollowEvent::<Block::Hash>::Stop);
let msg = to_sub_message(&sink, &FollowEvent::<String>::Stop);
let _ = sink.send(msg).await;
return
},
};
@@ -18,10 +18,7 @@
//! Error helpers for `chainHead` RPC module.
use jsonrpsee::{
core::Error as RpcError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::ErrorObject;
/// ChainHead RPC errors.
#[derive(Debug, thiserror::Error)]
@@ -81,9 +78,3 @@ impl From<Error> for ErrorObject<'static> {
}
}
}
impl From<Error> for RpcError {
fn from(e: Error) -> Self {
CallError::Custom(e.into()).into()
}
}
@@ -27,10 +27,8 @@ use assert_matches::assert_matches;
use codec::{Decode, Encode};
use futures::Future;
use jsonrpsee::{
core::{error::Error, server::rpc_module::Subscription as RpcSubscription},
rpc_params,
types::error::CallError,
RpcModule,
core::{error::Error, server::Subscription as RpcSubscription},
rpc_params, RpcModule,
};
use sc_block_builder::BlockBuilderBuilder;
use sc_client_api::ChildInfo;
@@ -120,7 +118,7 @@ async fn setup_api() -> (
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -171,7 +169,7 @@ async fn follow_subscription_produces_blocks() {
.into_rpc();
let finalized_hash = client.info().finalized_hash;
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -239,7 +237,7 @@ async fn follow_with_runtime() {
.into_rpc();
let finalized_hash = client.info().finalized_hash;
let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -361,7 +359,7 @@ async fn get_header() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// Obtain the valid header.
@@ -390,7 +388,7 @@ async fn get_body() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// Valid call.
@@ -475,7 +473,7 @@ async fn call_runtime() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// Pass an invalid parameters that cannot be decode.
@@ -488,7 +486,7 @@ async fn call_runtime() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter")
Error::Call(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter")
);
// Valid call.
@@ -550,7 +548,7 @@ async fn call_runtime_without_flag() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -591,7 +589,7 @@ async fn call_runtime_without_flag() {
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`")
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`")
);
}
@@ -629,7 +627,7 @@ async fn get_storage_hash() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// Valid call without storage at the key.
@@ -897,7 +895,7 @@ async fn get_storage_value() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// Valid call without storage at the key.
@@ -1209,11 +1207,12 @@ async fn separate_operation_ids_for_subscriptions() {
.into_rpc();
// Create two separate subscriptions.
let mut sub_first = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub_first = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
let sub_id_first = sub_first.subscription_id();
let sub_id_first = serde_json::to_string(&sub_id_first).unwrap();
let mut sub_second = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub_second =
api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
let sub_id_second = sub_second.subscription_id();
let sub_id_second = serde_json::to_string(&sub_id_second).unwrap();
@@ -1341,7 +1340,7 @@ async fn follow_generates_initial_blocks() {
let block_2_f_hash = block_2_f.header.hash();
client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -1450,7 +1449,7 @@ async fn follow_exceeding_pinned_blocks() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
let block = BlockBuilderBuilder::new(&*client)
.on_parent_block(client.chain_info().genesis_hash)
@@ -1526,7 +1525,7 @@ async fn follow_with_unpin() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -1572,7 +1571,7 @@ async fn follow_with_unpin() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// To not exceed the number of pinned blocks, we need to unpin before the next import.
@@ -1637,7 +1636,7 @@ async fn follow_with_multiple_unpin_hashes() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -1721,7 +1720,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
let _res: () = api
@@ -1738,7 +1737,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
// Unpin multiple blocks.
@@ -1756,7 +1755,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
let err = api
@@ -1767,7 +1766,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
}
@@ -1791,7 +1790,7 @@ async fn follow_prune_best_block() {
.into_rpc();
let finalized_hash = client.info().finalized_hash;
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -2051,7 +2050,7 @@ async fn follow_forks_pruned_block() {
// Block 2_f and 3_f are not pruned, pruning happens at height (N - 1).
client.finalize_block(block_3_hash, None).unwrap();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -2205,7 +2204,7 @@ async fn follow_report_multiple_pruned_block() {
let block_3_f = block_builder.build().unwrap().block;
let block_3_f_hash = block_3_f.hash();
client.import(BlockOrigin::Own, block_3_f.clone()).await.unwrap();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -2388,7 +2387,7 @@ async fn pin_block_references() {
}
}
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -2520,7 +2519,7 @@ async fn follow_finalized_before_new_block() {
let block_1_hash = block_1.header.hash();
client.import(BlockOrigin::Own, block_1.clone()).await.unwrap();
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
// Trigger the `FinalizedNotification` for block 1 before the `BlockImportNotification`, and
// expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events.
@@ -2622,7 +2621,7 @@ async fn ensure_operation_limits_works() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -2726,7 +2725,7 @@ async fn check_continue_operation() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -2908,7 +2907,7 @@ async fn stop_storage_operation() {
)
.into_rpc();
let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap();
let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
@@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule};
use jsonrpsee::{core::EmptyServerParams as EmptyParams, RpcModule};
use sc_chain_spec::Properties;
const CHAIN_NAME: &'static str = "TEST_CHAIN_NAME";
+1 -1
View File
@@ -37,7 +37,7 @@ pub mod transaction;
pub type SubscriptionTaskExecutor = std::sync::Arc<dyn sp_core::traits::SpawnNamed>;
/// The result of an RPC method.
#[derive(Debug, Deserialize, Serialize, PartialEq)]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum MethodResult {
/// Method generated a result.
@@ -29,27 +29,21 @@ use crate::{
},
SubscriptionTaskExecutor,
};
use jsonrpsee::{
core::async_trait,
types::{
error::{CallError, ErrorObject},
SubscriptionResult,
},
SubscriptionSink,
};
use jsonrpsee::{core::async_trait, types::error::ErrorObject, PendingSubscriptionSink};
use sc_transaction_pool_api::{
error::IntoPoolError, BlockHash, TransactionFor, TransactionPool, TransactionSource,
TransactionStatus,
};
use std::sync::Arc;
use sc_rpc::utils::pipe_from_stream;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_core::Bytes;
use sp_runtime::traits::Block as BlockT;
use codec::Decode;
use futures::{FutureExt, StreamExt, TryFutureExt};
use futures::{StreamExt, TryFutureExt};
/// An API for transaction RPC calls.
pub struct Transaction<Pool, Client> {
@@ -90,52 +84,53 @@ where
<Pool::Block as BlockT>::Hash: Unpin,
Client: HeaderBackend<Pool::Block> + ProvideRuntimeApi<Pool::Block> + Send + Sync + 'static,
{
fn submit_and_watch(&self, mut sink: SubscriptionSink, xt: Bytes) -> SubscriptionResult {
// This is the only place where the RPC server can return an error for this
// subscription. Other defects must be signaled as events to the sink.
let decoded_extrinsic = match TransactionFor::<Pool>::decode(&mut &xt[..]) {
Ok(decoded_extrinsic) => decoded_extrinsic,
Err(e) => {
let err = CallError::Custom(ErrorObject::owned(
BAD_FORMAT,
format!("Extrinsic has invalid format: {}", e),
None::<()>,
));
let _ = sink.reject(err);
return Ok(())
},
};
let best_block_hash = self.client.info().best_hash;
let submit = self
.pool
.submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic)
.map_err(|e| {
e.into_pool_error()
.map(Error::from)
.unwrap_or_else(|e| Error::Verification(Box::new(e)))
});
fn submit_and_watch(&self, pending: PendingSubscriptionSink, xt: Bytes) {
let client = self.client.clone();
let pool = self.pool.clone();
let fut = async move {
// This is the only place where the RPC server can return an error for this
// subscription. Other defects must be signaled as events to the sink.
let decoded_extrinsic = match TransactionFor::<Pool>::decode(&mut &xt[..]) {
Ok(decoded_extrinsic) => decoded_extrinsic,
Err(e) => {
let err = ErrorObject::owned(
BAD_FORMAT,
format!("Extrinsic has invalid format: {}", e),
None::<()>,
);
let _ = pending.reject(err).await;
return
},
};
let best_block_hash = client.info().best_hash;
let submit = pool
.submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic)
.map_err(|e| {
e.into_pool_error()
.map(Error::from)
.unwrap_or_else(|e| Error::Verification(Box::new(e)))
});
match submit.await {
Ok(stream) => {
let mut state = TransactionState::new();
let stream =
stream.filter_map(|event| async move { state.handle_event(event) });
sink.pipe_from_stream(stream.boxed()).await;
stream.filter_map(move |event| async move { state.handle_event(event) });
pipe_from_stream(pending, stream.boxed()).await;
},
Err(err) => {
// We have not created an `Watcher` for the tx. Make sure the
// error is still propagated as an event.
let event: TransactionEvent<<Pool::Block as BlockT>::Hash> = err.into();
sink.pipe_from_stream(futures::stream::once(async { event }).boxed()).await;
pipe_from_stream(pending, futures::stream::once(async { event }).boxed()).await;
},
};
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
Ok(())
sc_rpc::utils::spawn_subscription_task(&self.executor, fut);
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1" }
futures = "0.3.21"
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
log = "0.4.17"
parking_lot = "0.12.1"
serde_json = "1.0.111"
@@ -40,7 +40,6 @@ sp-runtime = { path = "../../primitives/runtime" }
sp-session = { path = "../../primitives/session" }
sp-version = { path = "../../primitives/version" }
sp-statement-store = { path = "../../primitives/statement-store" }
tokio = "1.22.0"
[dev-dependencies]
@@ -56,6 +55,7 @@ tokio = "1.22.0"
sp-io = { path = "../../primitives/io" }
substrate-test-runtime-client = { path = "../../test-utils/runtime/client" }
pretty_assertions = "1.2.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
[features]
test-helpers = []
+19 -21
View File
@@ -23,15 +23,14 @@ mod tests;
use std::sync::Arc;
use crate::SubscriptionTaskExecutor;
use crate::{
utils::{pipe_from_stream, spawn_subscription_task},
SubscriptionTaskExecutor,
};
use codec::{Decode, Encode};
use futures::{FutureExt, TryFutureExt};
use jsonrpsee::{
core::{async_trait, Error as JsonRpseeError, RpcResult},
types::SubscriptionResult,
SubscriptionSink,
};
use futures::TryFutureExt;
use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink};
use sc_rpc_api::DenyUnsafe;
use sc_transaction_pool_api::{
error::IntoPoolError, BlockHash, InPoolTransaction, TransactionFor, TransactionPool,
@@ -91,7 +90,7 @@ where
P::Hash: Unpin,
<P::Block as BlockT>::Hash: Unpin,
{
async fn submit_extrinsic(&self, ext: Bytes) -> RpcResult<TxHash<P>> {
async fn submit_extrinsic(&self, ext: Bytes) -> Result<TxHash<P>> {
let xt = match Decode::decode(&mut &ext[..]) {
Ok(xt) => xt,
Err(err) => return Err(Error::Client(Box::new(err)).into()),
@@ -105,7 +104,7 @@ where
})
}
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()> {
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()> {
self.deny_unsafe.check_if_safe()?;
let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
@@ -115,7 +114,7 @@ where
Ok(())
}
fn rotate_keys(&self) -> RpcResult<Bytes> {
fn rotate_keys(&self) -> Result<Bytes> {
self.deny_unsafe.check_if_safe()?;
let best_block_hash = self.client.info().best_hash;
@@ -129,7 +128,7 @@ where
.map_err(|api_err| Error::Client(Box::new(api_err)).into())
}
fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool> {
fn has_session_keys(&self, session_keys: Bytes) -> Result<bool> {
self.deny_unsafe.check_if_safe()?;
let best_block_hash = self.client.info().best_hash;
@@ -143,21 +142,21 @@ where
Ok(self.keystore.has_keys(&keys))
}
fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool> {
fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool> {
self.deny_unsafe.check_if_safe()?;
let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
Ok(self.keystore.has_keys(&[(public_key.to_vec(), key_type)]))
}
fn pending_extrinsics(&self) -> RpcResult<Vec<Bytes>> {
fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
Ok(self.pool.ready().map(|tx| tx.data().encode().into()).collect())
}
fn remove_extrinsic(
&self,
bytes_or_hash: Vec<hash::ExtrinsicOrHash<TxHash<P>>>,
) -> RpcResult<Vec<TxHash<P>>> {
) -> Result<Vec<TxHash<P>>> {
self.deny_unsafe.check_if_safe()?;
let hashes = bytes_or_hash
.into_iter()
@@ -178,13 +177,13 @@ where
.collect())
}
fn watch_extrinsic(&self, mut sink: SubscriptionSink, xt: Bytes) -> SubscriptionResult {
fn watch_extrinsic(&self, pending: PendingSubscriptionSink, xt: Bytes) {
let best_block_hash = self.client.info().best_hash;
let dxt = match TransactionFor::<P>::decode(&mut &xt[..]).map_err(|e| Error::from(e)) {
Ok(dxt) => dxt,
Err(e) => {
let _ = sink.reject(JsonRpseeError::from(e));
return Ok(())
spawn_subscription_task(&self.executor, pending.reject(e));
return
},
};
@@ -198,15 +197,14 @@ where
let stream = match submit.await {
Ok(stream) => stream,
Err(err) => {
let _ = sink.reject(JsonRpseeError::from(err));
let _ = pending.reject(ErrorObject::from(err)).await;
return
},
};
sink.pipe_from_stream(stream).await;
pipe_from_stream(pending, stream).await;
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
Ok(())
spawn_subscription_task(&self.executor, fut);
}
}
+6 -7
View File
@@ -22,8 +22,7 @@ use crate::testing::{test_executor, timeout_secs};
use assert_matches::assert_matches;
use codec::Encode;
use jsonrpsee::{
core::Error as RpcError,
types::{error::CallError, EmptyServerParams as EmptyParams},
core::{EmptyServerParams as EmptyParams, Error as RpcError},
RpcModule,
};
use sc_transaction_pool::{BasicPool, FullChainApi};
@@ -104,7 +103,7 @@ async fn author_submit_transaction_should_not_cause_error() {
assert_matches!(
api.call::<_, H256>("author_submitExtrinsic", [xt]).await,
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Already Imported") && err.code() == 1013
Err(RpcError::Call(err)) if err.message().contains("Already Imported") && err.code() == 1013
);
}
@@ -119,7 +118,7 @@ async fn author_should_watch_extrinsic() {
true,
);
let mut sub = api.subscribe("author_submitAndWatchExtrinsic", [xt]).await.unwrap();
let mut sub = api.subscribe_unbounded("author_submitAndWatchExtrinsic", [xt]).await.unwrap();
let (tx, sub_id) = timeout_secs(10, sub.next::<TransactionStatus<H256, Block>>())
.await
.unwrap()
@@ -157,11 +156,11 @@ async fn author_should_return_watch_validation_error() {
let invalid_xt = ExtrinsicBuilder::new_fill_block(Perbill::from_percent(100)).build();
let api = TestSetup::into_rpc();
let failed_sub = api.subscribe(METHOD, [to_hex(&invalid_xt.encode(), true)]).await;
let failed_sub = api.subscribe_unbounded(METHOD, [to_hex(&invalid_xt.encode(), true)]).await;
assert_matches!(
failed_sub,
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Invalid Transaction") && err.code() == 1010
Err(RpcError::Call(err)) if err.message().contains("Invalid Transaction") && err.code() == 1010
);
}
@@ -277,7 +276,7 @@ async fn author_has_session_keys() {
assert_matches!(
api.call::<_, bool>("author_hasSessionKeys", vec![Bytes::from(vec![1, 2, 3])]).await,
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Session keys are not encoded correctly")
Err(RpcError::Call(err)) if err.message().contains("Session keys are not encoded correctly")
);
}
+15 -15
View File
@@ -19,14 +19,17 @@
//! Blockchain API backend for full nodes.
use super::{client_err, ChainBackend, Error};
use crate::SubscriptionTaskExecutor;
use crate::{
utils::{pipe_from_stream, spawn_subscription_task},
SubscriptionTaskExecutor,
};
use std::{marker::PhantomData, sync::Arc};
use futures::{
future::{self, FutureExt},
future::{self},
stream::{self, Stream, StreamExt},
};
use jsonrpsee::SubscriptionSink;
use jsonrpsee::{core::async_trait, PendingSubscriptionSink};
use sc_client_api::{BlockBackend, BlockchainEvents};
use sp_blockchain::HeaderBackend;
use sp_runtime::{generic::SignedBlock, traits::Block as BlockT};
@@ -48,6 +51,7 @@ impl<Block: BlockT, Client> FullChain<Block, Client> {
}
}
#[async_trait]
impl<Block, Client> ChainBackend<Client, Block> for FullChain<Block, Client>
where
Block: BlockT + 'static,
@@ -66,11 +70,11 @@ where
self.client.block(self.unwrap_or_best(hash)).map_err(client_err)
}
fn subscribe_all_heads(&self, sink: SubscriptionSink) {
fn subscribe_all_heads(&self, pending: PendingSubscriptionSink) {
subscribe_headers(
&self.client,
&self.executor,
sink,
pending,
|| self.client().info().best_hash,
|| {
self.client()
@@ -80,11 +84,11 @@ where
)
}
fn subscribe_new_heads(&self, sink: SubscriptionSink) {
fn subscribe_new_heads(&self, pending: PendingSubscriptionSink) {
subscribe_headers(
&self.client,
&self.executor,
sink,
pending,
|| self.client().info().best_hash,
|| {
self.client()
@@ -95,11 +99,11 @@ where
)
}
fn subscribe_finalized_heads(&self, sink: SubscriptionSink) {
fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink) {
subscribe_headers(
&self.client,
&self.executor,
sink,
pending,
|| self.client().info().finalized_hash,
|| {
self.client()
@@ -114,7 +118,7 @@ where
fn subscribe_headers<Block, Client, F, G, S>(
client: &Arc<Client>,
executor: &SubscriptionTaskExecutor,
mut sink: SubscriptionSink,
pending: PendingSubscriptionSink,
best_block_hash: G,
stream: F,
) where
@@ -139,9 +143,5 @@ fn subscribe_headers<Block, Client, F, G, S>(
// duplicates at the beginning of the stream though.
let stream = stream::iter(maybe_header).chain(stream());
let fut = async move {
sink.pipe_from_stream(stream).await;
};
executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
spawn_subscription_task(executor, pipe_from_stream(pending, stream));
}
+20 -21
View File
@@ -27,7 +27,7 @@ use std::sync::Arc;
use crate::SubscriptionTaskExecutor;
use jsonrpsee::{core::RpcResult, types::SubscriptionResult, SubscriptionSink};
use jsonrpsee::{core::async_trait, PendingSubscriptionSink};
use sc_client_api::BlockchainEvents;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
use sp_runtime::{
@@ -42,6 +42,7 @@ pub use sc_rpc_api::chain::*;
use sp_blockchain::HeaderBackend;
/// Blockchain backend API
#[async_trait]
trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
where
Block: BlockT + 'static,
@@ -91,13 +92,13 @@ where
}
/// All new head subscription
fn subscribe_all_heads(&self, sink: SubscriptionSink);
fn subscribe_all_heads(&self, pending: PendingSubscriptionSink);
/// New best head subscription
fn subscribe_new_heads(&self, sink: SubscriptionSink);
fn subscribe_new_heads(&self, pending: PendingSubscriptionSink);
/// Finalized head subscription
fn subscribe_finalized_heads(&self, sink: SubscriptionSink);
fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink);
}
/// Create new state API that works on full node.
@@ -118,6 +119,7 @@ pub struct Chain<Block: BlockT, Client> {
backend: Box<dyn ChainBackend<Client, Block>>,
}
#[async_trait]
impl<Block, Client> ChainApiServer<NumberFor<Block>, Block::Hash, Block::Header, SignedBlock<Block>>
for Chain<Block, Client>
where
@@ -125,20 +127,20 @@ where
Block::Header: Unpin,
Client: HeaderBackend<Block> + BlockchainEvents<Block> + 'static,
{
fn header(&self, hash: Option<Block::Hash>) -> RpcResult<Option<Block::Header>> {
self.backend.header(hash).map_err(Into::into)
fn header(&self, hash: Option<Block::Hash>) -> Result<Option<Block::Header>, Error> {
self.backend.header(hash)
}
fn block(&self, hash: Option<Block::Hash>) -> RpcResult<Option<SignedBlock<Block>>> {
self.backend.block(hash).map_err(Into::into)
fn block(&self, hash: Option<Block::Hash>) -> Result<Option<SignedBlock<Block>>, Error> {
self.backend.block(hash)
}
fn block_hash(
&self,
number: Option<ListOrValue<NumberOrHex>>,
) -> RpcResult<ListOrValue<Option<Block::Hash>>> {
) -> Result<ListOrValue<Option<Block::Hash>>, Error> {
match number {
None => self.backend.block_hash(None).map(ListOrValue::Value).map_err(Into::into),
None => self.backend.block_hash(None).map(ListOrValue::Value),
Some(ListOrValue::Value(number)) => self
.backend
.block_hash(Some(number))
@@ -152,23 +154,20 @@ where
}
}
fn finalized_head(&self) -> RpcResult<Block::Hash> {
self.backend.finalized_head().map_err(Into::into)
fn finalized_head(&self) -> Result<Block::Hash, Error> {
self.backend.finalized_head()
}
fn subscribe_all_heads(&self, sink: SubscriptionSink) -> SubscriptionResult {
self.backend.subscribe_all_heads(sink);
Ok(())
fn subscribe_all_heads(&self, pending: PendingSubscriptionSink) {
self.backend.subscribe_all_heads(pending);
}
fn subscribe_new_heads(&self, sink: SubscriptionSink) -> SubscriptionResult {
self.backend.subscribe_new_heads(sink);
Ok(())
fn subscribe_new_heads(&self, pending: PendingSubscriptionSink) {
self.backend.subscribe_new_heads(pending)
}
fn subscribe_finalized_heads(&self, sink: SubscriptionSink) -> SubscriptionResult {
self.backend.subscribe_finalized_heads(sink);
Ok(())
fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink) {
self.backend.subscribe_finalized_heads(pending)
}
}
+2 -2
View File
@@ -19,7 +19,7 @@
use super::*;
use crate::testing::{test_executor, timeout_secs};
use assert_matches::assert_matches;
use jsonrpsee::types::EmptyServerParams as EmptyParams;
use jsonrpsee::core::EmptyServerParams as EmptyParams;
use sc_block_builder::BlockBuilderBuilder;
use sp_consensus::BlockOrigin;
use sp_rpc::list::ListOrValue;
@@ -252,7 +252,7 @@ async fn test_head_subscription(method: &str) {
let mut sub = {
let api = new_full(client.clone(), test_executor()).into_rpc();
let sub = api.subscribe(method, EmptyParams::new()).await.unwrap();
let sub = api.subscribe_unbounded(method, EmptyParams::new()).await.unwrap();
let block = BlockBuilderBuilder::new(&*client)
.on_parent_block(client.chain_info().best_hash)
.with_parent_block_number(client.chain_info().best_number)
+1 -2
View File
@@ -22,7 +22,6 @@
#[cfg(test)]
mod tests;
use jsonrpsee::core::RpcResult;
use sc_client_api::{BlockBackend, HeaderBackend};
use sc_rpc_api::{dev::error::Error, DenyUnsafe};
use sp_api::{ApiExt, Core, ProvideRuntimeApi};
@@ -65,7 +64,7 @@ where
+ 'static,
Client::Api: Core<Block>,
{
fn block_stats(&self, hash: Block::Hash) -> RpcResult<Option<BlockStats>> {
fn block_stats(&self, hash: Block::Hash) -> Result<Option<BlockStats>, Error> {
self.deny_unsafe.check_if_safe()?;
let block = {
+1 -1
View File
@@ -97,7 +97,7 @@ async fn deny_unsafe_works() {
"{{\"jsonrpc\":\"2.0\",\"method\":\"dev_getBlockStats\",\"params\":[{}],\"id\":1}}",
best_hash_param
);
let (resp, _) = api.raw_json_request(&request).await.expect("Raw calls should succeed");
let (resp, _) = api.raw_json_request(&request, 1).await.expect("Raw calls should succeed");
assert_eq!(
resp.result,
+1
View File
@@ -39,6 +39,7 @@ pub mod offchain;
pub mod state;
pub mod statement;
pub mod system;
pub mod utils;
#[cfg(any(test, feature = "test-helpers"))]
pub mod testing;
+2 -2
View File
@@ -18,7 +18,7 @@
//! Substrate mixnet API.
use jsonrpsee::core::{async_trait, RpcResult};
use jsonrpsee::core::async_trait;
use sc_mixnet::Api;
use sc_rpc_api::mixnet::error::Error;
pub use sc_rpc_api::mixnet::MixnetApiServer;
@@ -36,7 +36,7 @@ impl Mixnet {
#[async_trait]
impl MixnetApiServer for Mixnet {
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<()> {
async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<(), Error> {
// We only hold the lock while pushing the request into the requests channel
let fut = {
let mut api = self.0.lock().await;
+5 -5
View File
@@ -22,7 +22,7 @@
mod tests;
use self::error::Error;
use jsonrpsee::core::{async_trait, Error as JsonRpseeError, RpcResult};
use jsonrpsee::core::async_trait;
use parking_lot::RwLock;
/// Re-export the API for backward compatibility.
pub use sc_rpc_api::offchain::*;
@@ -50,23 +50,23 @@ impl<T: OffchainStorage> Offchain<T> {
#[async_trait]
impl<T: OffchainStorage + 'static> OffchainApiServer for Offchain<T> {
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()> {
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error> {
self.deny_unsafe.check_if_safe()?;
let prefix = match kind {
StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX,
StorageKind::LOCAL => return Err(JsonRpseeError::from(Error::UnavailableStorageKind)),
StorageKind::LOCAL => return Err(Error::UnavailableStorageKind),
};
self.storage.write().set(prefix, &key, &value);
Ok(())
}
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult<Option<Bytes>> {
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>, Error> {
self.deny_unsafe.check_if_safe()?;
let prefix = match kind {
StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX,
StorageKind::LOCAL => return Err(JsonRpseeError::from(Error::UnavailableStorageKind)),
StorageKind::LOCAL => return Err(Error::UnavailableStorageKind),
};
Ok(self.storage.read().get(prefix, &key).map(Into::into))
+4 -5
View File
@@ -39,7 +39,6 @@ fn local_storage_should_work() {
#[test]
fn offchain_calls_considered_unsafe() {
use jsonrpsee::types::error::CallError;
let storage = InMemOffchainStorage::default();
let offchain = Offchain::new(storage, DenyUnsafe::Yes);
let key = Bytes(b"offchain_storage".to_vec());
@@ -47,14 +46,14 @@ fn offchain_calls_considered_unsafe() {
assert_matches!(
offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()),
Err(JsonRpseeError::Call(CallError::Custom(err))) => {
assert_eq!(err.message(), "RPC call is unsafe to be called externally")
Err(Error::UnsafeRpcCalled(e)) => {
assert_eq!(e.to_string(), "RPC call is unsafe to be called externally")
}
);
assert_matches!(
offchain.get_local_storage(StorageKind::PERSISTENT, key),
Err(JsonRpseeError::Call(CallError::Custom(err))) => {
assert_eq!(err.message(), "RPC call is unsafe to be called externally")
Err(Error::UnsafeRpcCalled(e)) => {
assert_eq!(e.to_string(), "RPC call is unsafe to be called externally")
}
);
}
+43 -58
View File
@@ -24,32 +24,23 @@ mod utils;
#[cfg(test)]
mod tests;
use std::sync::Arc;
use crate::SubscriptionTaskExecutor;
use jsonrpsee::{
core::{async_trait, server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult},
types::SubscriptionResult,
use jsonrpsee::{core::async_trait, PendingSubscriptionSink};
use sc_client_api::{
Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider,
};
use sc_rpc_api::DenyUnsafe;
use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
use sp_blockchain::{HeaderBackend, HeaderMetadata};
use sp_core::{
storage::{PrefixedStorageKey, StorageChangeSet, StorageData, StorageKey},
Bytes,
};
use sp_runtime::traits::Block as BlockT;
use sp_version::RuntimeVersion;
use std::sync::Arc;
use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
use self::error::Error;
use sc_client_api::{
Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider,
};
pub use sc_rpc_api::{child_state::*, state::*};
use sp_blockchain::{HeaderBackend, HeaderMetadata};
const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000;
@@ -158,10 +149,15 @@ where
) -> Result<sp_rpc::tracing::TraceBlockResponse, Error>;
/// New runtime version subscription
fn subscribe_runtime_version(&self, sink: SubscriptionSink);
fn subscribe_runtime_version(&self, pending: PendingSubscriptionSink);
/// New storage subscription
fn subscribe_storage(&self, sink: SubscriptionSink, keys: Option<Vec<StorageKey>>);
fn subscribe_storage(
&self,
pending: PendingSubscriptionSink,
keys: Option<Vec<StorageKey>>,
deny_unsafe: DenyUnsafe,
);
}
/// Create new state API that works on full node.
@@ -207,7 +203,12 @@ where
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
fn call(&self, method: String, data: Bytes, block: Option<Block::Hash>) -> RpcResult<Bytes> {
fn call(
&self,
method: String,
data: Bytes,
block: Option<Block::Hash>,
) -> Result<Bytes, Error> {
self.backend.call(block, method, data).map_err(Into::into)
}
@@ -215,7 +216,7 @@ where
&self,
key_prefix: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
) -> Result<Vec<StorageKey>, Error> {
self.backend.storage_keys(block, key_prefix).map_err(Into::into)
}
@@ -223,7 +224,7 @@ where
&self,
key_prefix: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Vec<(StorageKey, StorageData)>> {
) -> Result<Vec<(StorageKey, StorageData)>, Error> {
self.deny_unsafe.check_if_safe()?;
self.backend.storage_pairs(block, key_prefix).map_err(Into::into)
}
@@ -234,12 +235,9 @@ where
count: u32,
start_key: Option<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
) -> Result<Vec<StorageKey>, Error> {
if count > STORAGE_KEYS_PAGED_MAX_COUNT {
return Err(JsonRpseeError::from(Error::InvalidCount {
value: count,
max: STORAGE_KEYS_PAGED_MAX_COUNT,
}))
return Err(Error::InvalidCount { value: count, max: STORAGE_KEYS_PAGED_MAX_COUNT })
}
self.backend
.storage_keys_paged(block, prefix, count, start_key)
@@ -250,7 +248,7 @@ where
&self,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<StorageData>> {
) -> Result<Option<StorageData>, Error> {
self.backend.storage(block, key).map_err(Into::into)
}
@@ -258,7 +256,7 @@ where
&self,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<Block::Hash>> {
) -> Result<Option<Block::Hash>, Error> {
self.backend.storage_hash(block, key).map_err(Into::into)
}
@@ -266,18 +264,18 @@ where
&self,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<u64>> {
) -> Result<Option<u64>, Error> {
self.backend
.storage_size(block, key, self.deny_unsafe)
.await
.map_err(Into::into)
}
fn metadata(&self, block: Option<Block::Hash>) -> RpcResult<Bytes> {
fn metadata(&self, block: Option<Block::Hash>) -> Result<Bytes, Error> {
self.backend.metadata(block).map_err(Into::into)
}
fn runtime_version(&self, at: Option<Block::Hash>) -> RpcResult<RuntimeVersion> {
fn runtime_version(&self, at: Option<Block::Hash>) -> Result<RuntimeVersion, Error> {
self.backend.runtime_version(at).map_err(Into::into)
}
@@ -286,7 +284,7 @@ where
keys: Vec<StorageKey>,
from: Block::Hash,
to: Option<Block::Hash>,
) -> RpcResult<Vec<StorageChangeSet<Block::Hash>>> {
) -> Result<Vec<StorageChangeSet<Block::Hash>>, Error> {
self.deny_unsafe.check_if_safe()?;
self.backend.query_storage(from, to, keys).map_err(Into::into)
}
@@ -295,7 +293,7 @@ where
&self,
keys: Vec<StorageKey>,
at: Option<Block::Hash>,
) -> RpcResult<Vec<StorageChangeSet<Block::Hash>>> {
) -> Result<Vec<StorageChangeSet<Block::Hash>>, Error> {
self.backend.query_storage_at(keys, at).map_err(Into::into)
}
@@ -303,7 +301,7 @@ where
&self,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<ReadProof<Block::Hash>> {
) -> Result<ReadProof<Block::Hash>, Error> {
self.backend.read_proof(block, keys).map_err(Into::into)
}
@@ -318,32 +316,19 @@ where
targets: Option<String>,
storage_keys: Option<String>,
methods: Option<String>,
) -> RpcResult<sp_rpc::tracing::TraceBlockResponse> {
) -> Result<sp_rpc::tracing::TraceBlockResponse, Error> {
self.deny_unsafe.check_if_safe()?;
self.backend
.trace_block(block, targets, storage_keys, methods)
.map_err(Into::into)
}
fn subscribe_runtime_version(&self, sink: SubscriptionSink) -> SubscriptionResult {
self.backend.subscribe_runtime_version(sink);
Ok(())
fn subscribe_runtime_version(&self, pending: PendingSubscriptionSink) {
self.backend.subscribe_runtime_version(pending)
}
fn subscribe_storage(
&self,
mut sink: SubscriptionSink,
keys: Option<Vec<StorageKey>>,
) -> SubscriptionResult {
if keys.is_none() {
if let Err(err) = self.deny_unsafe.check_if_safe() {
let _ = sink.reject(JsonRpseeError::from(err));
return Ok(())
}
}
self.backend.subscribe_storage(sink, keys);
Ok(())
fn subscribe_storage(&self, pending: PendingSubscriptionSink, keys: Option<Vec<StorageKey>>) {
self.backend.subscribe_storage(pending, keys, self.deny_unsafe)
}
}
@@ -430,7 +415,7 @@ where
storage_key: PrefixedStorageKey,
key_prefix: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
) -> Result<Vec<StorageKey>, Error> {
self.backend.storage_keys(block, storage_key, key_prefix).map_err(Into::into)
}
@@ -441,7 +426,7 @@ where
count: u32,
start_key: Option<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
) -> Result<Vec<StorageKey>, Error> {
self.backend
.storage_keys_paged(block, storage_key, prefix, count, start_key)
.map_err(Into::into)
@@ -452,7 +437,7 @@ where
storage_key: PrefixedStorageKey,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<StorageData>> {
) -> Result<Option<StorageData>, Error> {
self.backend.storage(block, storage_key, key).map_err(Into::into)
}
@@ -461,7 +446,7 @@ where
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<Vec<Option<StorageData>>> {
) -> Result<Vec<Option<StorageData>>, Error> {
self.backend.storage_entries(block, storage_key, keys).map_err(Into::into)
}
@@ -470,7 +455,7 @@ where
storage_key: PrefixedStorageKey,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<Block::Hash>> {
) -> Result<Option<Block::Hash>, Error> {
self.backend.storage_hash(block, storage_key, key).map_err(Into::into)
}
@@ -479,7 +464,7 @@ where
storage_key: PrefixedStorageKey,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<u64>> {
) -> Result<Option<u64>, Error> {
self.backend.storage_size(block, storage_key, key).map_err(Into::into)
}
@@ -488,7 +473,7 @@ where
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<ReadProof<Block::Hash>> {
) -> Result<ReadProof<Block::Hash>, Error> {
self.backend
.read_child_proof(block, child_storage_key, keys)
.map_err(Into::into)
+28 -25
View File
@@ -25,13 +25,13 @@ use super::{
error::{Error, Result},
ChildStateBackend, StateBackend,
};
use crate::{DenyUnsafe, SubscriptionTaskExecutor};
use futures::{future, stream, FutureExt, StreamExt};
use jsonrpsee::{
core::{async_trait, Error as JsonRpseeError},
SubscriptionSink,
use crate::{
utils::{pipe_from_stream, spawn_subscription_task},
DenyUnsafe, SubscriptionTaskExecutor,
};
use futures::{future, stream, StreamExt};
use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink};
use sc_client_api::{
Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider,
StorageProvider,
@@ -371,9 +371,7 @@ where
.map_err(client_err)
}
fn subscribe_runtime_version(&self, mut sink: SubscriptionSink) {
let client = self.client.clone();
fn subscribe_runtime_version(&self, pending: PendingSubscriptionSink) {
let initial = match self
.block_or_best(None)
.and_then(|block| self.client.runtime_version_at(block).map_err(Into::into))
@@ -381,12 +379,13 @@ where
{
Ok(initial) => initial,
Err(e) => {
let _ = sink.reject(JsonRpseeError::from(e));
spawn_subscription_task(&self.executor, pending.reject(e));
return
},
};
let mut previous_version = initial.clone();
let client = self.client.clone();
// A stream of new versions
let version_stream = client
@@ -406,24 +405,33 @@ where
});
let stream = futures::stream::once(future::ready(initial)).chain(version_stream);
let fut = async move {
sink.pipe_from_stream(stream).await;
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream));
}
fn subscribe_storage(&self, mut sink: SubscriptionSink, keys: Option<Vec<StorageKey>>) {
fn subscribe_storage(
&self,
pending: PendingSubscriptionSink,
keys: Option<Vec<StorageKey>>,
deny_unsafe: DenyUnsafe,
) {
if keys.is_none() {
if let Err(err) = deny_unsafe.check_if_safe() {
spawn_subscription_task(&self.executor, pending.reject(ErrorObject::from(err)));
return
}
}
let stream = match self.client.storage_changes_notification_stream(keys.as_deref(), None) {
Ok(stream) => stream,
Err(blockchain_err) => {
let _ = sink.reject(JsonRpseeError::from(Error::Client(Box::new(blockchain_err))));
spawn_subscription_task(
&self.executor,
pending.reject(Error::Client(Box::new(blockchain_err))),
);
return
},
};
// initial values
let initial = stream::iter(keys.map(|keys| {
let block = self.client.info().best_hash;
let changes = keys
@@ -436,7 +444,6 @@ where
StorageChangeSet { block, changes }
}));
// let storage_stream = stream.map(|(block, changes)| StorageChangeSet {
let storage_stream = stream.map(|storage_notif| StorageChangeSet {
block: storage_notif.block,
changes: storage_notif
@@ -450,11 +457,7 @@ where
.chain(storage_stream)
.filter(|storage| future::ready(!storage.changes.is_empty()));
let fut = async move {
sink.pipe_from_stream(stream).await;
};
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream));
}
fn trace_block(
+55 -107
View File
@@ -21,10 +21,7 @@ use super::*;
use crate::testing::{test_executor, timeout_secs};
use assert_matches::assert_matches;
use futures::executor;
use jsonrpsee::{
core::Error as RpcError,
types::{error::CallError as RpcCallError, EmptyServerParams as EmptyParams, ErrorObject},
};
use jsonrpsee::core::{EmptyServerParams as EmptyParams, Error as RpcError};
use sc_block_builder::BlockBuilderBuilder;
use sc_rpc_api::DenyUnsafe;
use sp_consensus::BlockOrigin;
@@ -42,6 +39,14 @@ fn prefixed_storage_key() -> PrefixedStorageKey {
child_info.prefixed_storage_key()
}
fn init_logger() {
use tracing_subscriber::{EnvFilter, FmtSubscriber};
let _ = FmtSubscriber::builder()
.with_env_filter(EnvFilter::from_default_env())
.try_init();
}
#[tokio::test]
async fn should_return_storage() {
const KEY: &[u8] = b":mock";
@@ -200,22 +205,25 @@ async fn should_call_contract() {
let genesis_hash = client.genesis_hash();
let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No);
use jsonrpsee::{core::Error, types::error::CallError};
assert_matches!(
client.call("balanceOf".into(), Bytes(vec![1, 2, 3]), Some(genesis_hash).into()),
Err(Error::Call(CallError::Failed(_)))
Err(Error::Client(_))
)
}
#[tokio::test]
async fn should_notify_about_storage_changes() {
init_logger();
let mut sub = {
let mut client = Arc::new(substrate_test_runtime_client::new());
let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No);
let api_rpc = api.into_rpc();
let sub = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await.unwrap();
let sub = api_rpc
.subscribe_unbounded("state_subscribeStorage", EmptyParams::new())
.await
.unwrap();
// Cause a change:
let mut builder = BlockBuilderBuilder::new(&*client)
@@ -241,11 +249,12 @@ async fn should_notify_about_storage_changes() {
// NOTE: previous versions of the subscription code used to return an empty value for the
// "initial" storage change here
assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(Some(_)));
assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(None));
}
#[tokio::test]
async fn should_send_initial_storage_changes_and_notifications() {
init_logger();
let mut sub = {
let mut client = Arc::new(substrate_test_runtime_client::new());
let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No);
@@ -263,7 +272,10 @@ async fn should_send_initial_storage_changes_and_notifications() {
let api_rpc = api.into_rpc();
let sub = api_rpc
.subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key)]])
.subscribe_unbounded(
"state_subscribeStorage",
[[StorageKey(alice_balance_key.to_vec())]],
)
.await
.unwrap();
@@ -288,9 +300,6 @@ async fn should_send_initial_storage_changes_and_notifications() {
assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(Some(_)));
assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(Some(_)));
// No more messages to follow
assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(None));
}
#[tokio::test]
@@ -393,108 +402,48 @@ async fn should_query_storage() {
assert_eq!(result.unwrap(), expected);
// Inverted range.
let result = api.query_storage(keys.clone(), block1_hash, Some(genesis_hash));
assert_eq!(
result.map_err(|e| e.to_string()),
Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
4001,
Error::InvalidBlockRange {
from: format!("1 ({:?})", block1_hash),
to: format!("0 ({:?})", genesis_hash),
details: "from number > to number".to_owned(),
}
.to_string(),
None::<()>,
))))
.map_err(|e| e.to_string())
assert_matches!(
api.query_storage(keys.clone(), block1_hash, Some(genesis_hash)),
Err(Error::InvalidBlockRange { from, to, details }) if from == format!("1 ({:?})", block1_hash) && to == format!("0 ({:?})", genesis_hash) && details == "from number > to number".to_owned()
);
let random_hash1 = H256::random();
let random_hash2 = H256::random();
// Invalid second hash.
let result = api.query_storage(keys.clone(), genesis_hash, Some(random_hash1));
assert_eq!(
result.map_err(|e| e.to_string()),
Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
4001,
Error::InvalidBlockRange {
from: format!("{:?}", genesis_hash),
to: format!("{:?}", Some(random_hash1)),
details: format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
),
}
.to_string(),
None::<()>,
))))
.map_err(|e| e.to_string())
assert_matches!(
api.query_storage(keys.clone(), genesis_hash, Some(random_hash1)),
Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", genesis_hash) && to == format!("{:?}", Some(random_hash1)) && details == format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
)
);
// Invalid first hash with Some other hash.
let result = api.query_storage(keys.clone(), random_hash1, Some(genesis_hash));
assert_eq!(
result.map_err(|e| e.to_string()),
Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
4001,
Error::InvalidBlockRange {
from: format!("{:?}", random_hash1),
to: format!("{:?}", Some(genesis_hash)),
details: format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
),
}
.to_string(),
None::<()>,
))))
.map_err(|e| e.to_string()),
assert_matches!(
api.query_storage(keys.clone(), random_hash1, Some(genesis_hash)),
Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(genesis_hash)) && details == format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
)
);
// Invalid first hash with None.
let result = api.query_storage(keys.clone(), random_hash1, None);
assert_eq!(
result.map_err(|e| e.to_string()),
Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
4001,
Error::InvalidBlockRange {
from: format!("{:?}", random_hash1),
to: format!("{:?}", Some(block2_hash)), // Best block hash.
details: format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
),
}
.to_string(),
None::<()>,
))))
.map_err(|e| e.to_string()),
assert_matches!(
api.query_storage(keys.clone(), random_hash1, None),
Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(block2_hash)) && details == format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
)
);
// Both hashes invalid.
let result = api.query_storage(keys.clone(), random_hash1, Some(random_hash2));
assert_eq!(
result.map_err(|e| e.to_string()),
Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
4001,
Error::InvalidBlockRange {
from: format!("{:?}", random_hash1), // First hash not found.
to: format!("{:?}", Some(random_hash2)),
details: format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
),
}
.to_string(),
None::<()>
))))
.map_err(|e| e.to_string()),
assert_matches!(
api.query_storage(keys.clone(), random_hash1, Some(random_hash2)),
Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(random_hash2)) && details == format!(
"UnknownBlock: Header was not found in the database: {:?}",
random_hash1
)
);
// single block range
@@ -548,7 +497,7 @@ async fn should_notify_on_runtime_version_initially() {
let api_rpc = api.into_rpc();
let sub = api_rpc
.subscribe("state_subscribeRuntimeVersion", EmptyParams::new())
.subscribe_unbounded("state_subscribeRuntimeVersion", EmptyParams::new())
.await
.unwrap();
@@ -557,9 +506,6 @@ async fn should_notify_on_runtime_version_initially() {
// assert initial version sent.
assert_matches!(timeout_secs(10, sub.next::<RuntimeVersion>()).await, Ok(Some(_)));
sub.close();
assert_matches!(timeout_secs(10, sub.next::<RuntimeVersion>()).await, Ok(None));
}
#[test]
@@ -572,12 +518,14 @@ fn should_deserialize_storage_key() {
#[tokio::test]
async fn wildcard_storage_subscriptions_are_rpc_unsafe() {
init_logger();
let client = Arc::new(substrate_test_runtime_client::new());
let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes);
let api_rpc = api.into_rpc();
let err = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await;
assert_matches!(err, Err(RpcError::Call(RpcCallError::Custom(e))) if e.message() == "RPC call is unsafe to be called externally");
let err = api_rpc.subscribe_unbounded("state_subscribeStorage", EmptyParams::new()).await;
assert_matches!(err, Err(RpcError::Call(e)) if e.message() == "RPC call is unsafe to be called externally");
}
#[tokio::test]
@@ -587,7 +535,7 @@ async fn concrete_storage_subscriptions_are_rpc_safe() {
let api_rpc = api.into_rpc();
let key = StorageKey(STORAGE_KEY.to_vec());
let sub = api_rpc.subscribe("state_subscribeStorage", [[key]]).await;
let sub = api_rpc.subscribe_unbounded("state_subscribeStorage", [[key]]).await;
assert!(sub.is_ok());
}
+34 -51
View File
@@ -22,17 +22,12 @@
mod tests;
use futures::channel::oneshot;
use jsonrpsee::{
core::{async_trait, error::Error as JsonRpseeError, JsonValue, RpcResult},
types::error::{CallError, ErrorCode, ErrorObject},
};
use jsonrpsee::core::{async_trait, JsonValue};
use sc_rpc_api::DenyUnsafe;
use sc_tracing::logging;
use sc_utils::mpsc::TracingUnboundedSender;
use sp_runtime::traits::{self, Header as HeaderT};
use self::error::Result;
pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
pub use sc_rpc_api::system::*;
@@ -57,9 +52,9 @@ pub enum Request<B: traits::Block> {
/// Must return the state of the network.
NetworkState(oneshot::Sender<serde_json::Value>),
/// Must return any potential parse error.
NetworkAddReservedPeer(String, oneshot::Sender<Result<()>>),
NetworkAddReservedPeer(String, oneshot::Sender<error::Result<()>>),
/// Must return any potential parse error.
NetworkRemoveReservedPeer(String, oneshot::Sender<Result<()>>),
NetworkRemoveReservedPeer(String, oneshot::Sender<error::Result<()>>),
/// Must return the list of reserved peers
NetworkReservedPeers(oneshot::Sender<Vec<String>>),
/// Must return the node role.
@@ -84,121 +79,109 @@ impl<B: traits::Block> System<B> {
#[async_trait]
impl<B: traits::Block> SystemApiServer<B::Hash, <B::Header as HeaderT>::Number> for System<B> {
fn system_name(&self) -> RpcResult<String> {
fn system_name(&self) -> Result<String, Error> {
Ok(self.info.impl_name.clone())
}
fn system_version(&self) -> RpcResult<String> {
fn system_version(&self) -> Result<String, Error> {
Ok(self.info.impl_version.clone())
}
fn system_chain(&self) -> RpcResult<String> {
fn system_chain(&self) -> Result<String, Error> {
Ok(self.info.chain_name.clone())
}
fn system_type(&self) -> RpcResult<sc_chain_spec::ChainType> {
fn system_type(&self) -> Result<sc_chain_spec::ChainType, Error> {
Ok(self.info.chain_type.clone())
}
fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties> {
fn system_properties(&self) -> Result<sc_chain_spec::Properties, Error> {
Ok(self.info.properties.clone())
}
async fn system_health(&self) -> RpcResult<Health> {
async fn system_health(&self) -> Result<Health, Error> {
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::Health(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_local_peer_id(&self) -> RpcResult<String> {
async fn system_local_peer_id(&self) -> Result<String, Error> {
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::LocalPeerId(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_local_listen_addresses(&self) -> RpcResult<Vec<String>> {
async fn system_local_listen_addresses(&self) -> Result<Vec<String>, Error> {
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::LocalListenAddresses(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_peers(
&self,
) -> RpcResult<Vec<PeerInfo<B::Hash, <B::Header as HeaderT>::Number>>> {
) -> Result<Vec<PeerInfo<B::Hash, <B::Header as HeaderT>::Number>>, Error> {
self.deny_unsafe.check_if_safe()?;
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::Peers(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_network_state(&self) -> RpcResult<JsonValue> {
async fn system_network_state(&self) -> Result<JsonValue, Error> {
self.deny_unsafe.check_if_safe()?;
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::NetworkState(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()> {
async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error> {
self.deny_unsafe.check_if_safe()?;
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::NetworkAddReservedPeer(peer, tx));
match rx.await {
Ok(Ok(())) => Ok(()),
Ok(Err(e)) => Err(JsonRpseeError::from(e)),
Err(e) => Err(JsonRpseeError::to_call_error(e)),
Ok(Err(e)) => Err(e),
Err(e) => Err(Error::Internal(e.to_string())),
}
}
async fn system_remove_reserved_peer(&self, peer: String) -> RpcResult<()> {
async fn system_remove_reserved_peer(&self, peer: String) -> Result<(), Error> {
self.deny_unsafe.check_if_safe()?;
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::NetworkRemoveReservedPeer(peer, tx));
match rx.await {
Ok(Ok(())) => Ok(()),
Ok(Err(e)) => Err(JsonRpseeError::from(e)),
Err(e) => Err(JsonRpseeError::to_call_error(e)),
Ok(Err(e)) => Err(e),
Err(e) => Err(Error::Internal(e.to_string())),
}
}
async fn system_reserved_peers(&self) -> RpcResult<Vec<String>> {
async fn system_reserved_peers(&self) -> Result<Vec<String>, Error> {
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::NetworkReservedPeers(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_node_roles(&self) -> RpcResult<Vec<NodeRole>> {
async fn system_node_roles(&self) -> Result<Vec<NodeRole>, Error> {
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::NodeRoles(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
async fn system_sync_state(&self) -> RpcResult<SyncState<<B::Header as HeaderT>::Number>> {
async fn system_sync_state(&self) -> Result<SyncState<<B::Header as HeaderT>::Number>, Error> {
let (tx, rx) = oneshot::channel();
let _ = self.send_back.unbounded_send(Request::SyncState(tx));
rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
rx.await.map_err(|e| Error::Internal(e.to_string()))
}
fn system_add_log_filter(&self, directives: String) -> RpcResult<()> {
fn system_add_log_filter(&self, directives: String) -> Result<(), Error> {
self.deny_unsafe.check_if_safe()?;
logging::add_directives(&directives);
logging::reload_filter().map_err(|e| {
JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
ErrorCode::InternalError.code(),
e,
None::<()>,
)))
})
logging::reload_filter().map_err(|e| Error::Internal(e))
}
fn system_reset_log_filter(&self) -> RpcResult<()> {
fn system_reset_log_filter(&self) -> Result<(), Error> {
self.deny_unsafe.check_if_safe()?;
logging::reset_log_filter().map_err(|e| {
JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
ErrorCode::InternalError.code(),
e,
None::<()>,
)))
})
logging::reset_log_filter().map_err(|e| Error::Internal(e))
}
}
+3 -4
View File
@@ -20,8 +20,7 @@ use super::{helpers::SyncState, *};
use assert_matches::assert_matches;
use futures::prelude::*;
use jsonrpsee::{
core::Error as RpcError,
types::{error::CallError, EmptyServerParams as EmptyParams},
core::{EmptyServerParams as EmptyParams, Error as RpcError},
RpcModule,
};
use sc_network::{self, config::Role, PeerId};
@@ -312,7 +311,7 @@ async fn system_network_add_reserved() {
let bad_peer_id = ["/ip4/198.51.100.19/tcp/30333"];
assert_matches!(
api(None).call::<_, ()>("system_addReservedPeer", bad_peer_id).await,
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Peer id is missing from the address")
Err(RpcError::Call(err)) if err.message().contains("Peer id is missing from the address")
);
}
@@ -328,7 +327,7 @@ async fn system_network_remove_reserved() {
assert_matches!(
api(None).call::<_, String>("system_removeReservedPeer", bad_peer_id).await,
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("base-58 decode error: provided string contained invalid character '/' at byte 0")
Err(RpcError::Call(err)) if err.message().contains("base-58 decode error: provided string contained invalid character '/' at byte 0")
);
}
#[tokio::test]
+42 -3
View File
@@ -20,11 +20,50 @@
use std::{future::Future, sync::Arc};
use sp_core::testing::TaskExecutor;
/// A task executor that can be used for running RPC tests.
///
/// Warning: the tokio runtime must be initialized before calling this.
#[derive(Clone)]
pub struct TokioTestExecutor(tokio::runtime::Handle);
impl TokioTestExecutor {
/// Create a new instance of `Self`.
pub fn new() -> Self {
Self(tokio::runtime::Handle::current())
}
}
impl Default for TokioTestExecutor {
fn default() -> Self {
Self::new()
}
}
impl sp_core::traits::SpawnNamed for TokioTestExecutor {
fn spawn_blocking(
&self,
_name: &'static str,
_group: Option<&'static str>,
future: futures::future::BoxFuture<'static, ()>,
) {
let handle = self.0.clone();
self.0.spawn_blocking(move || {
handle.block_on(future);
});
}
fn spawn(
&self,
_name: &'static str,
_group: Option<&'static str>,
future: futures::future::BoxFuture<'static, ()>,
) {
self.0.spawn(future);
}
}
/// Executor for testing.
pub fn test_executor() -> Arc<sp_core::testing::TaskExecutor> {
Arc::new(TaskExecutor::default())
pub fn test_executor() -> Arc<TokioTestExecutor> {
Arc::new(TokioTestExecutor::default())
}
/// Wrap a future in a timeout a little more concisely
+228
View File
@@ -0,0 +1,228 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! JSON-RPC helpers.
use crate::SubscriptionTaskExecutor;
use futures::{
future::{self, Either, Fuse, FusedFuture},
Future, FutureExt, Stream, StreamExt,
};
use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage, SubscriptionSink};
use sp_runtime::Serialize;
use std::collections::VecDeque;
const DEFAULT_BUF_SIZE: usize = 16;
/// A simple bounded VecDeque.
struct BoundedVecDeque<T> {
inner: VecDeque<T>,
max_cap: usize,
}
impl<T> BoundedVecDeque<T> {
/// Create a new bounded VecDeque.
fn new() -> Self {
Self { inner: VecDeque::with_capacity(DEFAULT_BUF_SIZE), max_cap: DEFAULT_BUF_SIZE }
}
fn push_back(&mut self, item: T) -> Result<(), ()> {
if self.inner.len() >= self.max_cap {
Err(())
} else {
self.inner.push_back(item);
Ok(())
}
}
fn pop_front(&mut self) -> Option<T> {
self.inner.pop_front()
}
}
/// Feed items to the subscription from the underlying stream.
///
/// This is bounded because the underlying streams in substrate are
/// unbounded and if the subscription can't keep with stream it can
/// cause the buffer to become very large and consume lots of memory.
///
/// In such cases the subscription is dropped.
pub async fn pipe_from_stream<S, T>(pending: PendingSubscriptionSink, mut stream: S)
where
S: Stream<Item = T> + Unpin + Send + 'static,
T: Serialize + Send + 'static,
{
let mut buf = BoundedVecDeque::new();
let accept_fut = pending.accept();
futures::pin_mut!(accept_fut);
// Poll the stream while waiting for the subscription to be accepted
//
// If the `max_cap` is exceeded then the subscription is dropped.
let sink = loop {
match future::select(accept_fut, stream.next()).await {
Either::Left((Ok(sink), _)) => break sink,
Either::Right((Some(msg), f)) => {
if buf.push_back(msg).is_err() {
log::warn!(target: "rpc", "Subscription::accept failed buffer limit={} exceed; dropping subscription", buf.max_cap);
return
}
accept_fut = f;
},
// The connection was closed or the stream was closed.
_ => return,
}
};
inner_pipe_from_stream(sink, stream, buf).await
}
async fn inner_pipe_from_stream<S, T>(
sink: SubscriptionSink,
mut stream: S,
mut buf: BoundedVecDeque<T>,
) where
S: Stream<Item = T> + Unpin + Send + 'static,
T: Serialize + Send + 'static,
{
let mut next_fut = Box::pin(Fuse::terminated());
let mut next_item = stream.next();
let closed = sink.closed();
futures::pin_mut!(closed);
loop {
if next_fut.is_terminated() {
if let Some(v) = buf.pop_front() {
let val = to_sub_message(&sink, &v);
next_fut.set(async { sink.send(val).await }.fuse());
}
}
match future::select(closed, future::select(next_fut, next_item)).await {
// Send operation finished.
Either::Right((Either::Left((_, n)), c)) => {
next_item = n;
closed = c;
next_fut = Box::pin(Fuse::terminated());
},
// New item from the stream
Either::Right((Either::Right((Some(v), n)), c)) => {
if buf.push_back(v).is_err() {
log::warn!(target: "rpc", "Subscription buffer limit={} exceed; dropping subscription", buf.max_cap);
return
}
next_fut = n;
closed = c;
next_item = stream.next();
},
// Stream "finished".
//
// Process remaining items and terminate.
Either::Right((Either::Right((None, pending_fut)), _)) => {
if pending_fut.await.is_err() {
return;
}
while let Some(v) = buf.pop_front() {
let val = to_sub_message(&sink, &v);
if sink.send(val).await.is_err() {
return;
}
}
return;
},
// Subscription was closed.
Either::Left(_) => return,
}
}
}
/// Builds a subscription message.
///
/// # Panics
///
/// This function panics `Serialize` fails and it is treated a bug.
pub fn to_sub_message(sink: &SubscriptionSink, result: &impl Serialize) -> SubscriptionMessage {
SubscriptionMessage::new(sink.method_name(), sink.subscription_id(), result)
.expect("Serialize infallible; qed")
}
/// Helper for spawning non-blocking rpc subscription task.
pub fn spawn_subscription_task(
executor: &SubscriptionTaskExecutor,
fut: impl Future<Output = ()> + Send + 'static,
) {
executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
}
#[cfg(test)]
mod tests {
use super::pipe_from_stream;
use futures::StreamExt;
use jsonrpsee::{core::EmptyServerParams, RpcModule, Subscription};
async fn subscribe() -> Subscription {
let mut module = RpcModule::new(());
module
.register_subscription("sub", "my_sub", "unsub", |_, pending, _| async move {
let stream = futures::stream::iter([0; 16]);
pipe_from_stream(pending, stream).await;
Ok(())
})
.unwrap();
module.subscribe("sub", EmptyServerParams::new(), 1).await.unwrap()
}
#[tokio::test]
async fn pipe_from_stream_works() {
let mut sub = subscribe().await;
let mut rx = 0;
while let Some(Ok(_)) = sub.next::<usize>().await {
rx += 1;
}
assert_eq!(rx, 16);
}
#[tokio::test]
async fn pipe_from_stream_is_bounded() {
let (tx, mut rx) = futures::channel::mpsc::unbounded::<()>();
let mut module = RpcModule::new(tx);
module
.register_subscription("sub", "my_sub", "unsub", |_, pending, ctx| async move {
let stream = futures::stream::iter([0; 32]);
pipe_from_stream(pending, stream).await;
_ = ctx.unbounded_send(());
Ok(())
})
.unwrap();
let mut sub = module.subscribe("sub", EmptyServerParams::new(), 1).await.unwrap();
// When the 17th item arrives the subscription is dropped
_ = rx.next().await.unwrap();
assert!(sub.next::<usize>().await.is_none());
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ runtime-benchmarks = [
]
[dependencies]
jsonrpsee = { version = "0.16.2", features = ["server"] }
jsonrpsee = { version = "0.20.3", features = ["server"] }
thiserror = "1.0.48"
futures = "0.3.21"
rand = "0.8.5"
+2
View File
@@ -100,6 +100,8 @@ pub struct Configuration {
pub rpc_max_subs_per_conn: u32,
/// JSON-RPC server default port.
pub rpc_port: u16,
/// The number of messages the JSON-RPC server is allowed to keep in memory.
pub rpc_message_buffer_capacity: u32,
/// Prometheus endpoint configuration. `None` if disabled.
pub prometheus_config: Option<PrometheusConfig>,
/// Telemetry service URL. `None` if disabled.
+10 -3
View File
@@ -37,7 +37,7 @@ mod task_manager;
use std::{collections::HashMap, net::SocketAddr};
use codec::{Decode, Encode};
use futures::{channel::mpsc, pin_mut, FutureExt, StreamExt};
use futures::{pin_mut, FutureExt, StreamExt};
use jsonrpsee::{core::Error as JsonRpseeError, RpcModule};
use log::{debug, error, warn};
use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider};
@@ -109,9 +109,15 @@ impl RpcHandlers {
pub async fn rpc_query(
&self,
json_query: &str,
) -> Result<(String, mpsc::UnboundedReceiver<String>), JsonRpseeError> {
) -> Result<(String, tokio::sync::mpsc::Receiver<String>), JsonRpseeError> {
// Because `tokio::sync::mpsc::channel` is used under the hood
// it will panic if it's set to usize::MAX.
//
// This limit is used to prevent panics and is large enough.
const TOKIO_MPSC_MAX_SIZE: usize = tokio::sync::Semaphore::MAX_PERMITS;
self.0
.raw_json_request(json_query)
.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE)
.await
.map(|(method_res, recv)| (method_res.result, recv))
}
@@ -394,6 +400,7 @@ where
max_payload_in_mb: config.rpc_max_request_size,
max_payload_out_mb: config.rpc_max_response_size,
max_subs_per_conn: config.rpc_max_subs_per_conn,
message_buffer_capacity: config.rpc_message_buffer_capacity,
rpc_api: gen_rpc_module(deny_unsafe(addr, &config.rpc_methods))?,
metrics,
id_provider: rpc_id_provider,
+1
View File
@@ -253,6 +253,7 @@ fn node_config<
rpc_id_provider: Default::default(),
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
+1 -1
View File
@@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1" }
jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] }
jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] }
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
thiserror = "1.0.48"
+11 -10
View File
@@ -44,9 +44,9 @@
use std::sync::Arc;
use jsonrpsee::{
core::{async_trait, Error as JsonRpseeError, RpcResult},
core::async_trait,
proc_macros::rpc,
types::{error::CallError, ErrorObject},
types::{ErrorObject, ErrorObjectOwned},
};
use sc_client_api::StorageData;
@@ -80,13 +80,13 @@ pub enum Error<Block: BlockT> {
LightSyncStateExtensionNotFound,
}
impl<Block: BlockT> From<Error<Block>> for JsonRpseeError {
impl<Block: BlockT> From<Error<Block>> for ErrorObjectOwned {
fn from(error: Error<Block>) -> Self {
let message = match error {
Error::JsonRpc(s) => s,
_ => error.to_string(),
};
CallError::Custom(ErrorObject::owned(1, message, None::<()>)).into()
ErrorObject::owned(1, message, None::<()>)
}
}
@@ -126,10 +126,10 @@ pub struct LightSyncState<Block: BlockT> {
/// An api for sync state RPC calls.
#[rpc(client, server)]
pub trait SyncStateApi {
pub trait SyncStateApi<B: BlockT> {
/// Returns the JSON serialized chainspec running the node, with a sync state.
#[method(name = "sync_state_genSyncSpec")]
async fn system_gen_sync_spec(&self, raw: bool) -> RpcResult<serde_json::Value>;
async fn system_gen_sync_spec(&self, raw: bool) -> Result<serde_json::Value, Error<B>>;
}
/// An api for sync state RPC calls.
@@ -188,12 +188,12 @@ where
}
#[async_trait]
impl<Block, Backend> SyncStateApiServer for SyncState<Block, Backend>
impl<Block, Backend> SyncStateApiServer<Block> for SyncState<Block, Backend>
where
Block: BlockT,
Backend: HeaderBackend<Block> + sc_client_api::AuxStore + 'static,
{
async fn system_gen_sync_spec(&self, raw: bool) -> RpcResult<serde_json::Value> {
async fn system_gen_sync_spec(&self, raw: bool) -> Result<serde_json::Value, Error<Block>> {
let current_sync_state = self.build_sync_state().await?;
let mut chain_spec = self.chain_spec.cloned_box();
@@ -202,10 +202,11 @@ where
)
.ok_or(Error::<Block>::LightSyncStateExtensionNotFound)?;
let val = serde_json::to_value(&current_sync_state)?;
let val = serde_json::to_value(&current_sync_state)
.map_err(|e| Error::<Block>::JsonRpc(e.to_string()))?;
*extension = Some(val);
let json_str = chain_spec.as_json(raw).map_err(|e| Error::<Block>::JsonRpc(e))?;
serde_json::from_str(&json_str).map_err(Into::into)
serde_json::from_str(&json_str).map_err(|e| Error::<Block>::JsonRpc(e.to_string()))
}
}

Some files were not shown because too many files have changed in this diff Show More