Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Make it possible to override maximum payload of RPC #9019

Merged
14 commits merged into from
Jun 17, 2021
32 changes: 21 additions & 11 deletions client/cli/src/commands/run_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@ pub struct RunCmd {
/// The node will be started with the authority role and actively
/// participate in any consensus task that it can (e.g. depending on
/// availability of local keys).
#[structopt(
long = "validator"
)]
#[structopt(long)]
pub validator: bool,

/// Disable GRANDPA voter when running in validator mode, otherwise disable the GRANDPA observer.
/// Disable GRANDPA voter when running in validator mode, otherwise disable the GRANDPA
/// observer.
#[structopt(long)]
pub no_grandpa: bool,

Expand All @@ -57,8 +56,8 @@ pub struct RunCmd {

/// Listen to all RPC interfaces.
///
/// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC proxy
/// server to filter out dangerous methods. More details:
/// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC
/// proxy server to filter out dangerous methods. More details:
/// <https://github.com/paritytech/substrate/wiki/Public-RPC>.
/// Use `--unsafe-rpc-external` to suppress the warning if you understand the risks.
#[structopt(long = "rpc-external")]
Expand All @@ -74,8 +73,8 @@ pub struct RunCmd {
///
/// - `Unsafe`: Exposes every RPC method.
/// - `Safe`: Exposes only a safe subset of RPC methods, denying unsafe RPC methods.
/// - `Auto`: Acts as `Safe` if RPC is served externally, e.g. when `--{rpc,ws}-external` is passed,
/// otherwise acts as `Unsafe`.
/// - `Auto`: Acts as `Safe` if RPC is served externally, e.g. when `--{rpc,ws}-external` is
/// passed, otherwise acts as `Unsafe`.
#[structopt(
long,
value_name = "METHOD SET",
Expand All @@ -88,8 +87,9 @@ pub struct RunCmd {

/// Listen to all Websocket interfaces.
///
/// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC proxy
/// server to filter out dangerous methods. More details: <https://github.com/paritytech/substrate/wiki/Public-RPC>.
/// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC
/// proxy server to filter out dangerous methods. More details:
/// <https://github.com/paritytech/substrate/wiki/Public-RPC>.
/// Use `--unsafe-ws-external` to suppress the warning if you understand the risks.
#[structopt(long = "ws-external")]
pub ws_external: bool,
Expand All @@ -100,6 +100,11 @@ pub struct RunCmd {
#[structopt(long = "unsafe-ws-external")]
pub unsafe_ws_external: bool,

/// Set the the maximum RPC payload size for both requests and responses (both http and ws), in
/// megabytes. Default is 15MiB.
#[structopt(long = "rpc-max-payload")]
pub rpc_max_payload: Option<usize>,

/// Listen to all Prometheus data source interfaces.
///
/// Default is local.
Expand Down Expand Up @@ -194,7 +199,8 @@ pub struct RunCmd {
#[structopt(long, conflicts_with_all = &["alice", "charlie", "dave", "eve", "ferdie", "one", "two"])]
pub bob: bool,

/// Shortcut for `--name Charlie --validator` with session keys for `Charlie` added to keystore.
/// Shortcut for `--name Charlie --validator` with session keys for `Charlie` added to
/// keystore.
#[structopt(long, conflicts_with_all = &["alice", "bob", "dave", "eve", "ferdie", "one", "two"])]
pub charlie: bool,

Expand Down Expand Up @@ -435,6 +441,10 @@ impl CliConfiguration for RunCmd {
Ok(self.rpc_methods.into())
}

fn rpc_max_payload(&self) -> Result<Option<usize>> {
Ok(self.rpc_max_payload)
}

fn transaction_pool(&self) -> Result<TransactionPoolOptions> {
Ok(self.pool_config.transaction_pool())
}
Expand Down
6 changes: 6 additions & 0 deletions client/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
Ok(Some(Vec::new()))
}

/// Get maximum RPC payload.
fn rpc_max_payload(&self) -> Result<Option<usize>> {
Ok(None)
}

/// Get the prometheus configuration (`None` if disabled)
///
/// By default this is `None`.
Expand Down Expand Up @@ -535,6 +540,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
rpc_ws_max_connections: self.rpc_ws_max_connections()?,
rpc_http_threads: self.rpc_http_threads()?,
rpc_cors: self.rpc_cors(is_dev)?,
rpc_max_payload: self.rpc_max_payload()?,
prometheus_config: self.prometheus_config(DCV::prometheus_listen_port())?,
telemetry_endpoints,
telemetry_external_transport: self.telemetry_external_transport()?,
Expand Down
2 changes: 1 addition & 1 deletion client/executor/src/native_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
default_heap_pages: Option<u64>,
max_runtime_instances: usize,
) -> Self {
let extended = D::ExtendHostFunctions::host_functions();
let extended = D::ExtendHostFunctions::host_functions();
let mut host_functions = sp_io::SubstrateHostFunctions::host_functions()
.into_iter()
// filter out any host function overrides provided.
Expand Down
18 changes: 14 additions & 4 deletions client/rpc-servers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ use jsonrpc_core::{IoHandlerExtension, MetaIoHandler};
use log::error;
use pubsub::PubSubMetadata;

const MEGABYTE: usize = 1024 * 1024;

/// Maximal payload accepted by RPC servers.
pub const MAX_PAYLOAD: usize = 15 * 1024 * 1024;
pub const RPC_MAX_PAYLOAD_DEFAULT: usize = 15 * MEGABYTE;

/// Default maximum number of connections for WS RPC servers.
const WS_MAX_CONNECTIONS: usize = 100;
Expand Down Expand Up @@ -85,7 +87,10 @@ mod inner {
thread_pool_size: Option<usize>,
cors: Option<&Vec<String>>,
io: RpcHandler<M>,
maybe_max_payload_mb: Option<usize>,
) -> io::Result<http::Server> {
let max_request_body_size = maybe_max_payload_mb.map(|mb| mb.saturating_mul(MEGABYTE))
.unwrap_or(RPC_MAX_PAYLOAD_DEFAULT);
http::ServerBuilder::new(io)
.threads(thread_pool_size.unwrap_or(HTTP_THREADS))
.health_api(("/health", "system_health"))
Expand All @@ -96,7 +101,7 @@ mod inner {
http::RestApi::Unsecure
})
.cors(map_cors::<http::AccessControlAllowOrigin>(cors))
.max_request_body_size(MAX_PAYLOAD)
.max_request_body_size(max_request_body_size)
.start_http(addr)
}

Expand All @@ -120,14 +125,19 @@ mod inner {
/// Start WS server listening on given address.
///
/// **Note**: Only available if `not(target_os = "unknown")`.
pub fn start_ws<M: pubsub::PubSubMetadata + From<jsonrpc_core::futures::sync::mpsc::Sender<String>>> (
pub fn start_ws<
M: pubsub::PubSubMetadata + From<jsonrpc_core::futures::sync::mpsc::Sender<String>>,
>(
addr: &std::net::SocketAddr,
max_connections: Option<usize>,
cors: Option<&Vec<String>>,
io: RpcHandler<M>,
maybe_max_payload_mb: Option<usize>,
) -> io::Result<ws::Server> {
let rpc_max_payload = maybe_max_payload_mb.map(|mb| mb.saturating_mul(MEGABYTE))
.unwrap_or(RPC_MAX_PAYLOAD_DEFAULT);
ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| context.sender().into())
.max_payload(MAX_PAYLOAD)
.max_payload(rpc_max_payload)
.max_connections(max_connections.unwrap_or(WS_MAX_CONNECTIONS))
.allowed_origins(map_cors(cors))
.allowed_hosts(hosts_filtering(cors.is_some()))
Expand Down
7 changes: 5 additions & 2 deletions client/rpc/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ pub fn new_full<BE, Block: BlockT, Client>(
client: Arc<Client>,
subscriptions: SubscriptionManager,
deny_unsafe: DenyUnsafe,
rpc_max_payload: Option<usize>,
) -> (State<Block, Client>, ChildState<Block, Client>)
where
Block: BlockT + 'static,
Expand All @@ -193,9 +194,11 @@ pub fn new_full<BE, Block: BlockT, Client>(
Client::Api: Metadata<Block>,
{
let child_backend = Box::new(
self::state_full::FullState::new(client.clone(), subscriptions.clone())
self::state_full::FullState::new(
client.clone(), subscriptions.clone(), rpc_max_payload
)
);
let backend = Box::new(self::state_full::FullState::new(client, subscriptions));
let backend = Box::new(self::state_full::FullState::new(client, subscriptions, rpc_max_payload));
(State { backend, deny_unsafe }, ChildState { backend: child_backend })
}

Expand Down
21 changes: 16 additions & 5 deletions client/rpc/src/state/state_full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ struct QueryStorageRange<Block: BlockT> {
pub struct FullState<BE, Block: BlockT, Client> {
client: Arc<Client>,
subscriptions: SubscriptionManager,
_phantom: PhantomData<(BE, Block)>
_phantom: PhantomData<(BE, Block)>,
rpc_max_payload: Option<usize>
emostov marked this conversation as resolved.
Show resolved Hide resolved
}

impl<BE, Block: BlockT, Client> FullState<BE, Block, Client>
Expand All @@ -78,8 +79,12 @@ impl<BE, Block: BlockT, Client> FullState<BE, Block, Client>
Block: BlockT + 'static,
{
/// Create new state API backend for full nodes.
pub fn new(client: Arc<Client>, subscriptions: SubscriptionManager) -> Self {
Self { client, subscriptions, _phantom: PhantomData }
pub fn new(
client: Arc<Client>,
subscriptions: SubscriptionManager,
rpc_max_payload: Option<usize>
emostov marked this conversation as resolved.
Show resolved Hide resolved
) -> Self {
Self { client, subscriptions, _phantom: PhantomData, rpc_max_payload }
}

/// Returns given block hash or best block hash if None is passed.
Expand Down Expand Up @@ -540,9 +545,15 @@ impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Cli
targets: Option<String>,
storage_keys: Option<String>,
) -> FutureResult<sp_rpc::tracing::TraceBlockResponse> {
let block_executor = sc_tracing::block::BlockExecutor::new(
self.client.clone(),
block,
targets,
storage_keys,
self.rpc_max_payload,
);
Box::new(result(
sc_tracing::block::BlockExecutor::new(self.client.clone(), block, targets, storage_keys)
.trace_block()
block_executor.trace_block()
.map_err(|e| invalid_block::<Block>(block, None, e.to_string()))
))
}
Expand Down
8 changes: 8 additions & 0 deletions client/rpc/src/state/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ fn should_return_storage() {
Arc::new(client),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);
let key = StorageKey(KEY.to_vec());

Expand Down Expand Up @@ -105,6 +106,7 @@ fn should_return_child_storage() {
client,
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);
let child_key = prefixed_storage_key();
let key = StorageKey(b"key".to_vec());
Expand Down Expand Up @@ -144,6 +146,7 @@ fn should_call_contract() {
client,
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

assert_matches!(
Expand All @@ -162,6 +165,7 @@ fn should_notify_about_storage_changes() {
client.clone(),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

api.subscribe_storage(Default::default(), subscriber, None.into());
Expand Down Expand Up @@ -200,6 +204,7 @@ fn should_send_initial_storage_changes_and_notifications() {
client.clone(),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

let alice_balance_key = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into()));
Expand Down Expand Up @@ -242,6 +247,7 @@ fn should_query_storage() {
client.clone(),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

let mut add_block = |nonce| {
Expand Down Expand Up @@ -463,6 +469,7 @@ fn should_return_runtime_version() {
client.clone(),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\
Expand Down Expand Up @@ -490,6 +497,7 @@ fn should_notify_on_runtime_version_initially() {
client.clone(),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

api.subscribe_runtime_version(Default::default(), subscriber);
Expand Down
1 change: 1 addition & 0 deletions client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ fn gen_handler<TBl, TBackend, TExPool, TRpc, TCl>(
client.clone(),
subscriptions.clone(),
deny_unsafe,
config.rpc_max_payload,
);
(chain, state, child_state)
};
Expand Down
2 changes: 2 additions & 0 deletions client/service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub struct Configuration {
pub rpc_cors: Option<Vec<String>>,
/// RPC methods to expose (by default only a safe subset or all of them).
pub rpc_methods: RpcMethods,
/// Maximum payload of rpc request/responses.
pub rpc_max_payload: Option<usize>,
/// Prometheus endpoint configuration. `None` if disabled.
pub prometheus_config: Option<PrometheusConfig>,
/// Telemetry service URL. `None` if disabled.
Expand Down
2 changes: 2 additions & 0 deletions client/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ fn start_rpc_servers<
deny_unsafe(&address, &config.rpc_methods),
sc_rpc_server::RpcMiddleware::new(rpc_metrics.clone(), "http")
),
config.rpc_max_payload
),
)?.map(|s| waiting::HttpServer(Some(s))),
maybe_start_server(
Expand All @@ -399,6 +400,7 @@ fn start_rpc_servers<
deny_unsafe(&address, &config.rpc_methods),
sc_rpc_server::RpcMiddleware::new(rpc_metrics.clone(), "ws")
),
config.rpc_max_payload
),
)?.map(|s| waiting::WsServer(Some(s))),
)))
Expand Down
1 change: 1 addition & 0 deletions client/service/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'sta
rpc_http_threads: None,
rpc_cors: None,
rpc_methods: Default::default(),
rpc_max_payload: None,
prometheus_config: None,
telemetry_endpoints: None,
telemetry_external_transport: None,
Expand Down
12 changes: 9 additions & 3 deletions client/tracing/src/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use parking_lot::Mutex;
use tracing::{Dispatch, dispatcher, Subscriber, Level, span::{Attributes, Record, Id}};

use sc_client_api::BlockBackend;
use sc_rpc_server::MAX_PAYLOAD;
use sc_rpc_server::RPC_MAX_PAYLOAD_DEFAULT;
use sp_api::{Core, Metadata, ProvideRuntimeApi, Encode};
use sp_blockchain::HeaderBackend;
use sp_runtime::{
Expand Down Expand Up @@ -54,6 +54,7 @@ const DEFAULT_TARGETS: &str = "pallet,frame,state";
const TRACE_TARGET: &str = "block_trace";
// The name of a field required for all events.
const REQUIRED_EVENT_FIELD: &str = "method";
const MEGABYTE: usize = 1024 * 1024;

/// Tracing Block Result type alias
pub type TraceBlockResult<T> = Result<T, Error>;
Expand Down Expand Up @@ -174,6 +175,7 @@ pub struct BlockExecutor<Block: BlockT, Client> {
block: Block::Hash,
targets: Option<String>,
storage_keys: Option<String>,
rpc_max_payload: usize,
}

impl<Block, Client> BlockExecutor<Block, Client>
Expand All @@ -189,8 +191,12 @@ impl<Block, Client> BlockExecutor<Block, Client>
block: Block::Hash,
targets: Option<String>,
storage_keys: Option<String>,
rpc_max_payload: Option<usize>,
) -> Self {
Self { client, block, targets, storage_keys }
let rpc_max_payload = rpc_max_payload.map(|mb| mb.saturating_mul(MEGABYTE))
.unwrap_or(RPC_MAX_PAYLOAD_DEFAULT);

emostov marked this conversation as resolved.
Show resolved Hide resolved
Self { client, block, targets, storage_keys, rpc_max_payload }
}

/// Execute block, record all spans and events belonging to `Self::targets`
Expand Down Expand Up @@ -260,7 +266,7 @@ impl<Block, Client> BlockExecutor<Block, Client>
tracing::debug!(target: "state_tracing", "Captured {} spans and {} events", spans.len(), events.len());

let approx_payload_size = BASE_PAYLOAD + events.len() * AVG_EVENT + spans.len() * AVG_SPAN;
let response = if approx_payload_size > MAX_PAYLOAD {
let response = if approx_payload_size > self.rpc_max_payload {
TraceBlockResponse::TraceError(TraceError {
error:
"Payload likely exceeds max payload size of RPC server.".to_string()
Expand Down
1 change: 1 addition & 0 deletions test-utils/test-runner/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub fn default_config(task_executor: TaskExecutor, mut chain_spec: Box<dyn Chain
rpc_http_threads: None,
rpc_cors: None,
rpc_methods: Default::default(),
rpc_max_payload: None,
prometheus_config: None,
telemetry_endpoints: None,
telemetry_external_transport: None,
Expand Down
1 change: 1 addition & 0 deletions utils/browser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ where
rpc_ws_max_connections: Default::default(),
rpc_http_threads: Default::default(),
rpc_methods: Default::default(),
rpc_max_payload: Default::default(),
state_cache_child_ratio: Default::default(),
state_cache_size: Default::default(),
tracing_receiver: Default::default(),
Expand Down