Skip to content

Commit 71d90c9

Browse files
committed
rpc: add config for max size of json rendered move value
1 parent 10662f5 commit 71d90c9

File tree

4 files changed

+22
-55
lines changed

4 files changed

+22
-55
lines changed

crates/sui-rpc-api/src/config.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ pub struct Config {
2929
/// If not provided then the node will not create an https service.
3030
#[serde(skip_serializing_if = "Option::is_none")]
3131
pub tls: Option<TlsConfig>,
32+
33+
/// Maxumum budget for rendering a Move value into JSON.
34+
///
35+
/// This sets the numbers of bytes that we are willing to spend on rendering field names and
36+
/// values when rendering a Move value into a JSON value.
37+
///
38+
/// Defaults to `1MiB` if not specified.
39+
#[serde(skip_serializing_if = "Option::is_none")]
40+
pub max_json_move_value_size: Option<usize>,
3241
}
3342

3443
impl Config {
@@ -44,6 +53,10 @@ impl Config {
4453
pub fn tls_config(&self) -> Option<&TlsConfig> {
4554
self.tls.as_ref()
4655
}
56+
57+
pub fn max_json_move_value_size(&self) -> usize {
58+
self.max_json_move_value_size.unwrap_or(1024 * 1024)
59+
}
4760
}
4861

4962
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]

crates/sui-rpc-api/src/grpc/v2beta/ledger_service/get_object.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ fn get_object_impl(
130130
Some((layout, s.contents()))
131131
})
132132
.and_then(|(layout, contents)| {
133-
sui_types::proto_value::ProtoVisitor::default()
133+
sui_types::proto_value::ProtoVisitor::new(service.config.max_json_move_value_size())
134134
.deserialize_value(contents, &layout)
135135
.map_err(|e| tracing::debug!("unable to convert to JSON: {e}"))
136136
.ok()

crates/sui-rpc-api/src/grpc/v2beta/ledger_service/get_transaction.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,13 @@ fn transaction_to_response(
190190
Some((layout, &event.contents))
191191
})
192192
.and_then(|(layout, contents)| {
193-
sui_types::proto_value::ProtoVisitor::default()
194-
.deserialize_value(contents, &layout)
195-
.map_err(|e| tracing::debug!("unable to convert to JSON: {e}"))
196-
.ok()
197-
.map(Box::new)
193+
sui_types::proto_value::ProtoVisitor::new(
194+
service.config.max_json_move_value_size(),
195+
)
196+
.deserialize_value(contents, &layout)
197+
.map_err(|e| tracing::debug!("unable to convert to JSON: {e}"))
198+
.ok()
199+
.map(Box::new)
198200
});
199201
}
200202
}

crates/sui-types/src/proto_value.rs

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,54 +20,12 @@ use move_core_types::{
2020
use prost_types::value::Kind;
2121
use prost_types::Struct;
2222
use prost_types::Value;
23-
use std::sync::LazyLock;
24-
use tracing::info;
2523

2624
/// This is the maximum depth of a proto message
2725
/// The maximum depth of a proto message is 100. Given this value may be nested itself somewhere
2826
/// we'll conservitively cap this to ~80% of that.
2927
const MAX_DEPTH: usize = 80;
3028

31-
/// Environment variable to override the default budget for deserialization. This can be set at
32-
/// runtime to change the maximum size of values that can be deserialized.
33-
const MAX_BOUND_VAR_NAME: &str = "MAX_JSON_MOVE_VALUE_SIZE";
34-
35-
/// Default budget for deserialization -- we're okay to spend 1MiB on rendering a move value to
36-
/// JSON.
37-
const DEFAULT_MAX_BOUND: usize = 1024 * 1024;
38-
39-
/// Budget for rendering a Move value into JSON. This sets the numbers of bytes that we
40-
/// are willing to spend on rendering field names and values when rendering a Move value into a
41-
/// JSON value.
42-
///
43-
/// Bounded deserialization is intended for use outside of the validator, and so uses a fixed bound
44-
/// that needs to be set at startup rather than one that is configured as part of the protocol.
45-
///
46-
/// If the environment variable `MAX_JSON_MOVE_VALUE_SIZE` is unset we default to
47-
/// `DEFAULT_MAX_BOUND` which allows 1MiB space usage on rendering a value to JSON.
48-
///
49-
/// This is read only once and after that the value is cached. To change this value you will need
50-
/// to restart the process with the new value set (or the value unset if you wish to use the
51-
/// `DEFAULT_MAX_BOUND` value).
52-
static MAX_BOUND: LazyLock<usize> = LazyLock::new(|| {
53-
let max_bound_opt = std::env::var(MAX_BOUND_VAR_NAME)
54-
.ok()
55-
.and_then(|s| s.parse().ok());
56-
if let Some(max_bound) = max_bound_opt {
57-
info!(
58-
"Using custom value for '{}' max bound: {}",
59-
MAX_BOUND_VAR_NAME, max_bound
60-
);
61-
max_bound
62-
} else {
63-
info!(
64-
"Using default value for '{}' -- max bound: {}",
65-
MAX_BOUND_VAR_NAME, DEFAULT_MAX_BOUND
66-
);
67-
DEFAULT_MAX_BOUND
68-
}
69-
});
70-
7129
pub struct ProtoVisitor {
7230
/// Budget left to spend on visiting.
7331
bound: usize,
@@ -89,12 +47,6 @@ pub enum Error {
8947
UnexpectedType,
9048
}
9149

92-
impl Default for ProtoVisitor {
93-
fn default() -> Self {
94-
Self::new(*MAX_BOUND)
95-
}
96-
}
97-
9850
impl ProtoVisitor {
9951
pub fn new(bound: usize) -> Self {
10052
Self { bound, depth: 0 }
@@ -698,7 +650,7 @@ pub(crate) mod tests {
698650

699651
fn json<T: serde::Serialize>(layout: A::MoveTypeLayout, data: T) -> serde_json::Value {
700652
let bcs = bcs::to_bytes(&data).unwrap();
701-
let proto_value = ProtoVisitor::default()
653+
let proto_value = ProtoVisitor::new(1024 * 1024)
702654
.deserialize_value(&bcs, &layout)
703655
.unwrap();
704656
proto_value_to_json_value(proto_value)

0 commit comments

Comments
 (0)