Skip to content

Commit 5cd9007

Browse files
committed
fix(rpcserver): Support named and null parameters, Review optionals and refact arg_parser
1 parent 49e77bc commit 5cd9007

4 files changed

Lines changed: 154 additions & 221 deletions

File tree

crates/floresta-node/src/json_rpc/blockchain.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,11 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> {
5959
pub fn get_rescan_interval(
6060
&self,
6161
use_timestamp: bool,
62-
start: Option<u32>,
63-
stop: Option<u32>,
64-
confidence: Option<RescanConfidence>,
62+
start: u32,
63+
stop: u32,
64+
confidence: RescanConfidence,
6565
) -> Result<(u32, u32), JsonRpcError> {
66-
let start = start.unwrap_or(0u32);
67-
let stop = stop.unwrap_or(0u32);
68-
6966
if use_timestamp {
70-
let confidence = confidence.unwrap_or(RescanConfidence::Medium);
71-
// `get_block_height_by_timestamp` already does the time validity checks.
72-
7367
let start_height = self.get_block_height_by_timestamp(start, &confidence)?;
7468

7569
let stop_height = self.get_block_height_by_timestamp(stop, &RescanConfidence::Exact)?;

crates/floresta-node/src/json_rpc/request.rs

Lines changed: 54 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub struct RpcRequest {
1717
pub method: String,
1818

1919
/// The parameters for the method, as an array of json values.
20-
pub params: Vec<Value>,
20+
pub params: Option<Value>,
2121

2222
/// An optional identifier for the request, which can be used to match responses.
2323
pub id: Value,
@@ -27,83 +27,34 @@ pub struct RpcRequest {
2727
/// methods already handle the case where the parameter is missing or has an
2828
/// unexpected type, returning an error if so.
2929
pub mod arg_parser {
30-
use std::str::FromStr;
3130

31+
use serde::Deserialize;
3232
use serde_json::Value;
3333

3434
use crate::json_rpc::res::jsonrpc_interface::JsonRpcError;
3535

36-
/// Extracts a u64 parameter from the request parameters at the specified index.
37-
///
38-
/// This function checks if the parameter exists, is of type u64 and can be converted to `T`.
39-
/// Returns an error otherwise.
40-
pub fn get_numeric<T: TryFrom<u64>>(
41-
params: &[Value],
42-
index: usize,
43-
opt_name: &str,
44-
) -> Result<T, JsonRpcError> {
45-
let v = params
46-
.get(index)
47-
.ok_or_else(|| JsonRpcError::MissingParameter(opt_name.to_string()))?;
48-
49-
let n = v.as_u64().ok_or_else(|| {
50-
JsonRpcError::InvalidParameterType(format!("{opt_name} must be a number"))
51-
})?;
52-
53-
T::try_from(n)
54-
.map_err(|_| JsonRpcError::InvalidParameterType(format!("{opt_name} is out-of-range")))
55-
}
56-
57-
/// Extracts a string parameter from the request parameters at the specified index.
58-
///
59-
/// This function checks if the parameter exists and is of type string. Returns an error
60-
/// otherwise.
61-
pub fn get_string(
62-
params: &[Value],
63-
index: usize,
64-
opt_name: &str,
65-
) -> Result<String, JsonRpcError> {
66-
let v = params
67-
.get(index)
68-
.ok_or_else(|| JsonRpcError::MissingParameter(opt_name.to_string()))?;
69-
70-
let str = v.as_str().ok_or_else(|| {
71-
JsonRpcError::InvalidParameterType(format!("{opt_name} must be a string"))
72-
})?;
73-
74-
Ok(str.to_string())
75-
}
76-
77-
/// Extracts a boolean parameter from the request parameters at the specified index.
78-
///
79-
/// This function checks if the parameter exists and is of type boolean. Returns an error
80-
/// otherwise.
81-
pub fn get_bool(params: &[Value], index: usize, opt_name: &str) -> Result<bool, JsonRpcError> {
82-
let v = params
83-
.get(index)
84-
.ok_or_else(|| JsonRpcError::MissingParameter(opt_name.to_string()))?;
85-
86-
v.as_bool().ok_or_else(|| {
87-
JsonRpcError::InvalidParameterType(format!("{opt_name} must be a boolean"))
88-
})
89-
}
90-
91-
/// Extracts a hash parameter from the request parameters at the specified index.
36+
/// Extracts a parameter from the request parameters at the specified index.
9237
///
9338
/// This function can extract any type that implements `FromStr`, such as `BlockHash` or
9439
/// `Txid`. It checks if the parameter exists and is a valid string representation of the type.
9540
/// Returns an error otherwise.
96-
pub fn get_hash<T: FromStr>(
97-
params: &[Value],
41+
pub fn get_at<T: Deserialize<'static>>(
42+
params: &Value,
9843
index: usize,
99-
opt_name: &str,
44+
field_name: &str,
10045
) -> Result<T, JsonRpcError> {
101-
let v = params
102-
.get(index)
103-
.ok_or_else(|| JsonRpcError::MissingParameter(opt_name.to_string()))?;
104-
105-
v.as_str().and_then(|s| s.parse().ok()).ok_or_else(|| {
106-
JsonRpcError::InvalidParameterType(format!("{opt_name} must be a valid hash"))
46+
let v = match (params.is_array(), params.is_object()) {
47+
(true, false) => params.get(index),
48+
(false, true) => params.get(field_name),
49+
_ => None,
50+
};
51+
52+
let unwrap = v
53+
.ok_or_else(|| JsonRpcError::MissingParameter(field_name.to_string()))?
54+
.clone();
55+
56+
T::deserialize(unwrap).map_err(|_| {
57+
JsonRpcError::InvalidParameterType(format!("{field_name} has an invalid type"))
10758
})
10859
}
10960

@@ -112,45 +63,52 @@ pub mod arg_parser {
11263
/// This function can extract an array of any type that implements `FromStr`, such as
11364
/// `BlockHash` or `Txid`. It checks if the parameter exists and is an array of valid string
11465
/// representations of the type. Returns an error otherwise.
115-
pub fn get_hashes_array<T: FromStr>(
116-
params: &[Value],
66+
pub fn get_arr_at<T: Deserialize<'static>>(
67+
params: &Value,
11768
index: usize,
118-
opt_name: &str,
69+
field_name: &str,
11970
) -> Result<Vec<T>, JsonRpcError> {
120-
let v = params
121-
.get(index)
122-
.ok_or_else(|| JsonRpcError::MissingParameter(opt_name.to_string()))?;
123-
124-
let array = v.as_array().ok_or_else(|| {
125-
JsonRpcError::InvalidParameterType(format!("{opt_name} must be an array of hashes"))
126-
})?;
127-
128-
array
71+
let v = match (params.is_array(), params.is_object()) {
72+
(true, false) => params.get(index),
73+
(false, true) => return Err(JsonRpcError::InvalidRequest),
74+
_ => None,
75+
};
76+
77+
let unwrap = v
78+
.ok_or_else(|| JsonRpcError::MissingParameter(field_name.to_string()))?
79+
.as_array()
80+
.unwrap(); // Safe unwrap, we checked if this is an array earlier.
81+
82+
unwrap
12983
.iter()
130-
.map(|v| {
131-
v.as_str().and_then(|s| s.parse().ok()).ok_or_else(|| {
132-
JsonRpcError::InvalidParameterType(format!("{opt_name} must be a valid hash"))
133-
})
134-
})
84+
.enumerate()
85+
.map(|(index, v)| get_at(v, index, field_name))
13586
.collect()
13687
}
13788

138-
/// Extracts an optional field from the request parameters at the specified index.
89+
pub fn optional<T>(result: Result<T, JsonRpcError>) -> Result<Option<T>, JsonRpcError> {
90+
match result {
91+
Ok(t) => Ok(Some(t)),
92+
Err(JsonRpcError::MissingParameter(_)) => Ok(None),
93+
Err(e) => Err(e),
94+
}
95+
}
96+
97+
/// Extracts a field from the request parameters at the specified index.
13998
///
14099
/// This function checks if the parameter exists and is of the expected type. If the parameter
141-
/// doesn't exist, it returns `None`. If it exists but is of an unexpected type, it returns an
100+
/// doesn't exist, it returns `default`. If it exists but is of an unexpected type, it returns an
142101
/// error.
143-
pub fn get_optional_field<T>(
144-
params: &[Value],
102+
pub fn get_with_default<T: Deserialize<'static>>(
103+
v: &Value,
145104
index: usize,
146-
opt_name: &str,
147-
extractor_fn: impl Fn(&[Value], usize, &str) -> Result<T, JsonRpcError>,
148-
) -> Result<Option<T>, JsonRpcError> {
149-
if params.len() <= index {
150-
return Ok(None);
105+
field_name: &str,
106+
default: T,
107+
) -> Result<T, JsonRpcError> {
108+
match get_at(v, index, field_name) {
109+
Ok(t) => Ok(t),
110+
Err(JsonRpcError::MissingParameter(_)) => Ok(default),
111+
Err(e) => Err(e),
151112
}
152-
153-
let value = extractor_fn(params, index, opt_name)?;
154-
Ok(Some(value))
155113
}
156114
}

crates/floresta-node/src/json_rpc/res.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
use corepc_types::v30::GetBlockVerboseOne;
2-
use floresta_chain::extensions::HeaderExtError;
3-
use floresta_common::impl_error_from;
4-
use floresta_mempool::mempool::AcceptToMempoolError;
52
use serde::Deserialize;
63
use serde::Serialize;
74

@@ -14,6 +11,7 @@ pub mod jsonrpc_interface {
1411
use axum::response::IntoResponse;
1512
use floresta_chain::extensions::HeaderExtError;
1613
use floresta_common::impl_error_from;
14+
use floresta_mempool::mempool::AcceptToMempoolError;
1715
use serde::Deserialize;
1816
use serde::Serialize;
1917
use serde_json::Value;
@@ -189,8 +187,11 @@ pub mod jsonrpc_interface {
189187

190188
/// Raised if when the rescanblockchain command, with the timestamp flag activated, contains some timestamp thats less than the genesis one and not zero which is the default value for this arg.
191189
InvalidTimestamp,
190+
/// Something went wrong when attempting to publish a transaction to mempool
191+
MempoolAccept(AcceptToMempoolError),
192192
}
193193

194+
impl_error_from!(JsonRpcError, AcceptToMempoolError, MempoolAccept);
194195
impl JsonRpcError {
195196
pub fn http_code(&self) -> u16 {
196197
use axum::http::StatusCode;
@@ -204,6 +205,7 @@ pub mod jsonrpc_interface {
204205
| JsonRpcError::InvalidDescriptor(_)
205206
| JsonRpcError::InvalidVerbosityLevel
206207
| JsonRpcError::Decode(_)
208+
| JsonRpcError::MempoolAccept(_)
207209
| JsonRpcError::InvalidMemInfoMode
208210
| JsonRpcError::InvalidAddnodeCommand
209211
| JsonRpcError::InvalidDisconnectNodeCommand
@@ -350,6 +352,11 @@ pub mod jsonrpc_interface {
350352
message: "Wallet error".into(),
351353
data: Some(Value::String(msg.clone())),
352354
},
355+
JsonRpcError::MempoolAccept(msg) => RpcError {
356+
code: SERVER_ERROR_MAX - 5, // -32095
357+
message: "Wallet error".into(),
358+
data: Some(Value::String(msg.to_string())),
359+
},
353360
JsonRpcError::InInitialBlockDownload => RpcError {
354361
code: SERVER_ERROR_MAX - 5, // -32094
355362
message: "Node is in initial block download".into(),

0 commit comments

Comments
 (0)