From 7838182144205ce63e3ea5386ccbf78fba871774 Mon Sep 17 00:00:00 2001 From: Miraculous Owonubi Date: Tue, 31 May 2022 15:26:50 +0100 Subject: [PATCH] chore(jsonrpc): split the `parser` and `errors` module (#6917) Tracking issue: https://github.com/near/nearcore/issues/6850 Requires: #6902 Splits up the `parser` and `errors` module into finer and more specialized modules, consolidating both parsing and type conversion logic. --- chain/jsonrpc/src/api/adversarial.rs | 12 + chain/jsonrpc/src/api/blocks.rs | 42 ++ chain/jsonrpc/src/api/changes.rs | 65 +++ chain/jsonrpc/src/api/chunks.rs | 62 +++ chain/jsonrpc/src/api/config.rs | 38 ++ chain/jsonrpc/src/api/gas_price.rs | 40 ++ chain/jsonrpc/src/api/light_client.rs | 126 +++++ chain/jsonrpc/src/api/mod.rs | 93 ++++ chain/jsonrpc/src/api/network_info.rs | 56 ++ chain/jsonrpc/src/api/query.rs | 157 ++++++ chain/jsonrpc/src/api/receipts.rs | 44 ++ chain/jsonrpc/src/api/sandbox.rs | 33 ++ chain/jsonrpc/src/api/status.rs | 51 ++ chain/jsonrpc/src/api/transactions.rs | 61 +++ chain/jsonrpc/src/api/validator.rs | 54 ++ chain/jsonrpc/src/errors.rs | 736 -------------------------- chain/jsonrpc/src/lib.rs | 7 +- chain/jsonrpc/src/parser.rs | 259 --------- 18 files changed, 937 insertions(+), 999 deletions(-) create mode 100644 chain/jsonrpc/src/api/adversarial.rs create mode 100644 chain/jsonrpc/src/api/blocks.rs create mode 100644 chain/jsonrpc/src/api/changes.rs create mode 100644 chain/jsonrpc/src/api/chunks.rs create mode 100644 chain/jsonrpc/src/api/config.rs create mode 100644 chain/jsonrpc/src/api/gas_price.rs create mode 100644 chain/jsonrpc/src/api/light_client.rs create mode 100644 chain/jsonrpc/src/api/mod.rs create mode 100644 chain/jsonrpc/src/api/network_info.rs create mode 100644 chain/jsonrpc/src/api/query.rs create mode 100644 chain/jsonrpc/src/api/receipts.rs create mode 100644 chain/jsonrpc/src/api/sandbox.rs create mode 100644 chain/jsonrpc/src/api/status.rs create mode 100644 chain/jsonrpc/src/api/transactions.rs create mode 100644 chain/jsonrpc/src/api/validator.rs delete mode 100644 chain/jsonrpc/src/errors.rs delete mode 100644 chain/jsonrpc/src/parser.rs diff --git a/chain/jsonrpc/src/api/adversarial.rs b/chain/jsonrpc/src/api/adversarial.rs new file mode 100644 index 00000000000..c4ace5779c0 --- /dev/null +++ b/chain/jsonrpc/src/api/adversarial.rs @@ -0,0 +1,12 @@ +use serde_json::Value; + +use near_jsonrpc_adversarial_primitives::SetRoutingTableRequest; +use near_jsonrpc_primitives::errors::RpcParseError; + +use super::{parse_params, RpcRequest}; + +impl RpcRequest for SetRoutingTableRequest { + fn parse(value: Option) -> Result { + parse_params::(value) + } +} diff --git a/chain/jsonrpc/src/api/blocks.rs b/chain/jsonrpc/src/api/blocks.rs new file mode 100644 index 00000000000..087f2e1303c --- /dev/null +++ b/chain/jsonrpc/src/api/blocks.rs @@ -0,0 +1,42 @@ +use serde_json::Value; + +use near_client_primitives::types::GetBlockError; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::blocks::{RpcBlockError, RpcBlockRequest}; +use near_primitives::types::{BlockId, BlockReference}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcBlockRequest { + fn parse(value: Option) -> Result { + let block_reference = if let Ok((block_id,)) = parse_params::<(BlockId,)>(value.clone()) { + BlockReference::BlockId(block_id) + } else { + parse_params::(value)? + }; + Ok(Self { block_reference }) + } +} + +impl RpcFrom for RpcBlockError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcBlockError { + fn rpc_from(error: GetBlockError) -> Self { + match error { + GetBlockError::UnknownBlock { error_message } => Self::UnknownBlock { error_message }, + GetBlockError::NotSyncedYet => Self::NotSyncedYet, + GetBlockError::IOError { error_message } => Self::InternalError { error_message }, + GetBlockError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcBlockError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/changes.rs b/chain/jsonrpc/src/api/changes.rs new file mode 100644 index 00000000000..632da4e0b18 --- /dev/null +++ b/chain/jsonrpc/src/api/changes.rs @@ -0,0 +1,65 @@ +use serde_json::Value; + +use near_client_primitives::types::{GetBlockError, GetStateChangesError}; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::changes::{ + RpcStateChangesError, RpcStateChangesInBlockByTypeRequest, RpcStateChangesInBlockRequest, +}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcStateChangesInBlockRequest { + fn parse(value: Option) -> Result { + parse_params::(value) + } +} + +impl RpcRequest for RpcStateChangesInBlockByTypeRequest { + fn parse(value: Option) -> Result { + parse_params::(value) + } +} + +impl RpcFrom for RpcStateChangesError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcStateChangesError { + fn rpc_from(error: GetBlockError) -> Self { + match error { + GetBlockError::UnknownBlock { error_message } => Self::UnknownBlock { error_message }, + GetBlockError::NotSyncedYet => Self::NotSyncedYet, + GetBlockError::IOError { error_message } => Self::InternalError { error_message }, + GetBlockError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcStateChangesError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcStateChangesError { + fn rpc_from(error: GetStateChangesError) -> Self { + match error { + GetStateChangesError::IOError { error_message } => { + Self::InternalError { error_message } + } + GetStateChangesError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetStateChangesError::NotSyncedYet => Self::NotSyncedYet, + GetStateChangesError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcStateChangesError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/chunks.rs b/chain/jsonrpc/src/api/chunks.rs new file mode 100644 index 00000000000..28a0c1be344 --- /dev/null +++ b/chain/jsonrpc/src/api/chunks.rs @@ -0,0 +1,62 @@ +use serde_json::Value; + +use near_client_primitives::types::{GetChunk, GetChunkError}; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::chunks::{ChunkReference, RpcChunkError, RpcChunkRequest}; +use near_primitives::hash::CryptoHash; +use near_primitives::types::{BlockId, ShardId}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcChunkRequest { + fn parse(value: Option) -> Result { + // Try to parse legacy positioned args and if it fails parse newer named args + let chunk_reference = if let Ok((chunk_id,)) = parse_params::<(CryptoHash,)>(value.clone()) + { + ChunkReference::ChunkHash { chunk_id } + } else if let Ok(((block_id, shard_id),)) = + parse_params::<((BlockId, ShardId),)>(value.clone()) + { + ChunkReference::BlockShardId { block_id, shard_id } + } else { + parse_params::(value)? + }; + Ok(Self { chunk_reference }) + } +} + +impl RpcFrom for RpcChunkError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for GetChunk { + fn rpc_from(chunk_reference: ChunkReference) -> Self { + match chunk_reference { + ChunkReference::BlockShardId { block_id, shard_id } => match block_id { + BlockId::Height(height) => Self::Height(height, shard_id), + BlockId::Hash(block_hash) => Self::BlockHash(block_hash, shard_id), + }, + ChunkReference::ChunkHash { chunk_id } => Self::ChunkHash(chunk_id.into()), + } + } +} + +impl RpcFrom for RpcChunkError { + fn rpc_from(error: GetChunkError) -> Self { + match error { + GetChunkError::IOError { error_message } => Self::InternalError { error_message }, + GetChunkError::UnknownBlock { error_message } => Self::UnknownBlock { error_message }, + GetChunkError::InvalidShardId { shard_id } => Self::InvalidShardId { shard_id }, + GetChunkError::UnknownChunk { chunk_hash } => Self::UnknownChunk { chunk_hash }, + GetChunkError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcChunkError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/config.rs b/chain/jsonrpc/src/api/config.rs new file mode 100644 index 00000000000..447f78ab2d1 --- /dev/null +++ b/chain/jsonrpc/src/api/config.rs @@ -0,0 +1,38 @@ +use serde_json::Value; + +use near_client_primitives::types::GetProtocolConfigError; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::config::{RpcProtocolConfigError, RpcProtocolConfigRequest}; +use near_primitives::types::BlockReference; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcProtocolConfigRequest { + fn parse(value: Option) -> Result { + parse_params::(value).map(|block_reference| Self { block_reference }) + } +} + +impl RpcFrom for RpcProtocolConfigError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcProtocolConfigError { + fn rpc_from(error: GetProtocolConfigError) -> Self { + match error { + GetProtocolConfigError::UnknownBlock(error_message) => { + Self::UnknownBlock { error_message } + } + GetProtocolConfigError::IOError(error_message) => Self::InternalError { error_message }, + GetProtocolConfigError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcProtocolConfigError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/gas_price.rs b/chain/jsonrpc/src/api/gas_price.rs new file mode 100644 index 00000000000..7263d4a041a --- /dev/null +++ b/chain/jsonrpc/src/api/gas_price.rs @@ -0,0 +1,40 @@ +use serde_json::Value; + +use near_client_primitives::types::GetGasPriceError; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::gas_price::{RpcGasPriceError, RpcGasPriceRequest}; +use near_primitives::types::MaybeBlockId; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcGasPriceRequest { + fn parse(value: Option) -> Result { + parse_params::<(MaybeBlockId,)>(value).map(|(block_id,)| Self { block_id }) + } +} + +impl RpcFrom for RpcGasPriceError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcGasPriceError { + fn rpc_from(error: GetGasPriceError) -> Self { + match error { + GetGasPriceError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetGasPriceError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetGasPriceError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcGasPriceError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/light_client.rs b/chain/jsonrpc/src/api/light_client.rs new file mode 100644 index 00000000000..baaa12a1be3 --- /dev/null +++ b/chain/jsonrpc/src/api/light_client.rs @@ -0,0 +1,126 @@ +use std::sync::Arc; + +use serde_json::Value; + +use near_client_primitives::types::{ + GetBlockProofError, GetExecutionOutcomeError, GetNextLightClientBlockError, +}; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::light_client::{ + RpcLightClientExecutionProofRequest, RpcLightClientNextBlockError, + RpcLightClientNextBlockRequest, RpcLightClientNextBlockResponse, RpcLightClientProofError, +}; +use near_primitives::hash::CryptoHash; +use near_primitives::views::LightClientBlockView; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcLightClientExecutionProofRequest { + fn parse(value: Option) -> Result { + Ok(parse_params::(value)?) + } +} + +impl RpcRequest for RpcLightClientNextBlockRequest { + fn parse(value: Option) -> Result { + if let Ok((last_block_hash,)) = parse_params::<(CryptoHash,)>(value.clone()) { + Ok(Self { last_block_hash }) + } else { + Ok(parse_params::(value)?) + } + } +} + +impl RpcFrom>> for RpcLightClientNextBlockResponse { + fn rpc_from(light_client_block: Option>) -> Self { + Self { light_client_block } + } +} + +impl RpcFrom for RpcLightClientProofError { + fn rpc_from(error: GetExecutionOutcomeError) -> Self { + match error { + GetExecutionOutcomeError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetExecutionOutcomeError::InconsistentState { + number_or_shards, + execution_outcome_shard_id, + } => Self::InconsistentState { number_or_shards, execution_outcome_shard_id }, + GetExecutionOutcomeError::NotConfirmed { transaction_or_receipt_id } => { + Self::NotConfirmed { transaction_or_receipt_id } + } + GetExecutionOutcomeError::UnknownTransactionOrReceipt { transaction_or_receipt_id } => { + Self::UnknownTransactionOrReceipt { transaction_or_receipt_id } + } + GetExecutionOutcomeError::UnavailableShard { transaction_or_receipt_id, shard_id } => { + Self::UnavailableShard { transaction_or_receipt_id, shard_id } + } + GetExecutionOutcomeError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetExecutionOutcomeError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcLightClientProofError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcLightClientProofError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcLightClientProofError { + fn rpc_from(error: GetBlockProofError) -> Self { + match error { + GetBlockProofError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetBlockProofError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetBlockProofError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcLightClientProofError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcLightClientNextBlockError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcLightClientNextBlockError { + fn rpc_from(error: GetNextLightClientBlockError) -> Self { + match error { + GetNextLightClientBlockError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetNextLightClientBlockError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetNextLightClientBlockError::EpochOutOfBounds { epoch_id } => { + Self::EpochOutOfBounds { epoch_id } + } + GetNextLightClientBlockError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcLightClientNextBlockError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/mod.rs b/chain/jsonrpc/src/api/mod.rs new file mode 100644 index 00000000000..c73785a7940 --- /dev/null +++ b/chain/jsonrpc/src/api/mod.rs @@ -0,0 +1,93 @@ +use serde::de::DeserializeOwned; +use serde_json::Value; + +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::errors::{RpcError, ServerError}; +use near_primitives::borsh::BorshDeserialize; + +#[cfg(feature = "test_features")] +mod adversarial; +mod blocks; +mod changes; +mod chunks; +mod config; +mod gas_price; +mod light_client; +mod network_info; +mod query; +mod receipts; +mod sandbox; +mod status; +mod transactions; +mod validator; + +pub(crate) trait RpcRequest: Sized { + fn parse(value: Option) -> Result; +} + +pub trait RpcFrom { + fn rpc_from(_: T) -> Self; +} + +pub trait RpcInto { + fn rpc_into(self) -> T; +} + +impl RpcFrom for T { + fn rpc_from(val: T) -> Self { + val + } +} + +impl RpcInto for T +where + X: RpcFrom, +{ + fn rpc_into(self) -> X { + X::rpc_from(self) + } +} + +impl RpcFrom for RpcError { + fn rpc_from(error: actix::MailboxError) -> Self { + RpcError::new( + -32_000, + "Server error".to_string(), + Some(serde_json::Value::String(error.to_string())), + ) + } +} + +impl RpcFrom for ServerError { + fn rpc_from(error: actix::MailboxError) -> Self { + match error { + actix::MailboxError::Closed => ServerError::Closed, + actix::MailboxError::Timeout => ServerError::Timeout, + } + } +} + +impl RpcFrom for ServerError { + fn rpc_from(e: near_primitives::errors::InvalidTxError) -> ServerError { + ServerError::TxExecutionError(near_primitives::errors::TxExecutionError::InvalidTxError(e)) + } +} + +fn parse_params(value: Option) -> Result { + if let Some(value) = value { + serde_json::from_value(value) + .map_err(|err| RpcParseError(format!("Failed parsing args: {}", err))) + } else { + Err(RpcParseError("Require at least one parameter".to_owned())) + } +} + +fn parse_signed_transaction( + value: Option, +) -> Result { + let (encoded,) = parse_params::<(String,)>(value)?; + let bytes = near_primitives::serialize::from_base64(&encoded) + .map_err(|err| RpcParseError(err.to_string()))?; + Ok(near_primitives::transaction::SignedTransaction::try_from_slice(&bytes) + .map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err)))?) +} diff --git a/chain/jsonrpc/src/api/network_info.rs b/chain/jsonrpc/src/api/network_info.rs new file mode 100644 index 00000000000..72cdabfd9b3 --- /dev/null +++ b/chain/jsonrpc/src/api/network_info.rs @@ -0,0 +1,56 @@ +use near_client_primitives::types::NetworkInfoResponse; +use near_jsonrpc_primitives::types::network_info::{ + RpcKnownProducer, RpcNetworkInfoError, RpcNetworkInfoResponse, RpcPeerInfo, +}; +use near_network_primitives::types::{KnownProducer, PeerInfo}; + +use super::{RpcFrom, RpcInto}; + +impl RpcFrom for RpcNetworkInfoError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcPeerInfo { + fn rpc_from(peer_info: PeerInfo) -> Self { + Self { id: peer_info.id, addr: peer_info.addr, account_id: peer_info.account_id } + } +} + +impl RpcFrom for RpcKnownProducer { + fn rpc_from(known_producer: KnownProducer) -> Self { + Self { + account_id: known_producer.account_id, + addr: known_producer.addr, + peer_id: known_producer.peer_id, + } + } +} + +impl RpcFrom for RpcNetworkInfoResponse { + fn rpc_from(network_info_response: NetworkInfoResponse) -> Self { + Self { + active_peers: network_info_response + .connected_peers + .iter() + .map(|pi| pi.clone().rpc_into()) + .collect(), + num_active_peers: network_info_response.num_connected_peers, + peer_max_count: network_info_response.peer_max_count, + sent_bytes_per_sec: network_info_response.sent_bytes_per_sec, + received_bytes_per_sec: network_info_response.received_bytes_per_sec, + known_producers: network_info_response + .known_producers + .iter() + .map(|kp| kp.clone().rpc_into()) + .collect(), + } + } +} + +impl RpcFrom for RpcNetworkInfoError { + fn rpc_from(error_message: String) -> Self { + Self::InternalError { error_message } + } +} diff --git a/chain/jsonrpc/src/api/query.rs b/chain/jsonrpc/src/api/query.rs new file mode 100644 index 00000000000..7de116e7db2 --- /dev/null +++ b/chain/jsonrpc/src/api/query.rs @@ -0,0 +1,157 @@ +use serde_json::Value; + +use near_client_primitives::types::QueryError; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::query::{RpcQueryError, RpcQueryRequest, RpcQueryResponse}; +use near_primitives::serialize; +use near_primitives::types::BlockReference; +use near_primitives::views::{QueryRequest, QueryResponse}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +/// Max size of the query path (soft-deprecated) +const QUERY_DATA_MAX_SIZE: usize = 10 * 1024; + +impl RpcRequest for RpcQueryRequest { + fn parse(value: Option) -> Result { + let query_request = if let Ok((path, data)) = + parse_params::<(String, String)>(value.clone()) + { + // Handle a soft-deprecated version of the query API, which is based on + // positional arguments with a "path"-style first argument. + // + // This whole block can be removed one day, when the new API is 100% adopted. + let data = serialize::from_base(&data).map_err(|err| RpcParseError(err.to_string()))?; + let query_data_size = path.len() + data.len(); + if query_data_size > QUERY_DATA_MAX_SIZE { + return Err(RpcParseError(format!( + "Query data size {} is too large", + query_data_size + ))); + } + + let mut path_parts = path.splitn(3, '/'); + let make_err = || RpcParseError("Not enough query parameters provided".to_string()); + let query_command = path_parts.next().ok_or_else(make_err)?; + let account_id = path_parts + .next() + .ok_or_else(make_err)? + .parse() + .map_err(|err| RpcParseError(format!("{}", err)))?; + let maybe_extra_arg = path_parts.next(); + + let request = match query_command { + "account" => QueryRequest::ViewAccount { account_id }, + "access_key" => match maybe_extra_arg { + None => QueryRequest::ViewAccessKeyList { account_id }, + Some(pk) => QueryRequest::ViewAccessKey { + account_id, + public_key: pk + .parse() + .map_err(|_| RpcParseError("Invalid public key".to_string()))?, + }, + }, + "code" => QueryRequest::ViewCode { account_id }, + "contract" => QueryRequest::ViewState { account_id, prefix: data.into() }, + "call" => match maybe_extra_arg { + Some(method_name) => QueryRequest::CallFunction { + account_id, + method_name: method_name.to_string(), + args: data.into(), + }, + None => return Err(RpcParseError("Method name is missing".to_string())), + }, + _ => return Err(RpcParseError(format!("Unknown path {}", query_command))), + }; + // Use Finality::None here to make backward compatibility tests work + Self { request, block_reference: BlockReference::latest() } + } else { + parse_params::(value)? + }; + Ok(query_request) + } +} + +impl RpcFrom for RpcQueryError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcQueryError { + fn rpc_from(error: QueryError) -> Self { + match error { + QueryError::InternalError { error_message } => Self::InternalError { error_message }, + QueryError::NoSyncedBlocks => Self::NoSyncedBlocks, + QueryError::UnavailableShard { requested_shard_id } => { + Self::UnavailableShard { requested_shard_id } + } + QueryError::UnknownBlock { block_reference } => Self::UnknownBlock { block_reference }, + QueryError::GarbageCollectedBlock { block_height, block_hash } => { + Self::GarbageCollectedBlock { block_height, block_hash } + } + QueryError::InvalidAccount { requested_account_id, block_height, block_hash } => { + Self::InvalidAccount { requested_account_id, block_height, block_hash } + } + QueryError::UnknownAccount { requested_account_id, block_height, block_hash } => { + Self::UnknownAccount { requested_account_id, block_height, block_hash } + } + QueryError::NoContractCode { contract_account_id, block_height, block_hash } => { + Self::NoContractCode { contract_account_id, block_height, block_hash } + } + QueryError::UnknownAccessKey { public_key, block_height, block_hash } => { + Self::UnknownAccessKey { public_key, block_height, block_hash } + } + QueryError::ContractExecutionError { vm_error, block_height, block_hash } => { + Self::ContractExecutionError { vm_error, block_height, block_hash } + } + QueryError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcQueryError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + QueryError::TooLargeContractState { contract_account_id, block_height, block_hash } => { + Self::TooLargeContractState { contract_account_id, block_height, block_hash } + } + } + } +} + +impl RpcFrom for RpcQueryResponse { + fn rpc_from(query_response: QueryResponse) -> Self { + Self { + kind: RpcFrom::rpc_from(query_response.kind), + block_hash: query_response.block_hash, + block_height: query_response.block_height, + } + } +} + +impl RpcFrom + for near_jsonrpc_primitives::types::query::QueryResponseKind +{ + fn rpc_from(query_response_kind: near_primitives::views::QueryResponseKind) -> Self { + match query_response_kind { + near_primitives::views::QueryResponseKind::ViewAccount(account_view) => { + Self::ViewAccount(account_view) + } + near_primitives::views::QueryResponseKind::ViewCode(contract_code_view) => { + Self::ViewCode(contract_code_view) + } + near_primitives::views::QueryResponseKind::ViewState(view_state_result) => { + Self::ViewState(view_state_result) + } + near_primitives::views::QueryResponseKind::CallResult(call_result) => { + Self::CallResult(call_result) + } + near_primitives::views::QueryResponseKind::AccessKey(access_key_view) => { + Self::AccessKey(access_key_view) + } + near_primitives::views::QueryResponseKind::AccessKeyList(access_key_list) => { + Self::AccessKeyList(access_key_list) + } + } + } +} diff --git a/chain/jsonrpc/src/api/receipts.rs b/chain/jsonrpc/src/api/receipts.rs new file mode 100644 index 00000000000..2a98b7c15ed --- /dev/null +++ b/chain/jsonrpc/src/api/receipts.rs @@ -0,0 +1,44 @@ +use serde_json::Value; + +use near_client_primitives::types::{GetReceipt, GetReceiptError}; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::receipts::{ + ReceiptReference, RpcReceiptError, RpcReceiptRequest, +}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcReceiptRequest { + fn parse(value: Option) -> Result { + let receipt_reference = parse_params::(value)?; + Ok(Self { receipt_reference }) + } +} + +impl RpcFrom for RpcReceiptError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for GetReceipt { + fn rpc_from(receipt_reference: ReceiptReference) -> Self { + Self { receipt_id: receipt_reference.receipt_id } + } +} + +impl RpcFrom for RpcReceiptError { + fn rpc_from(error: GetReceiptError) -> Self { + match error { + GetReceiptError::IOError(error_message) => Self::InternalError { error_message }, + GetReceiptError::UnknownReceipt(hash) => Self::UnknownReceipt { receipt_id: hash }, + GetReceiptError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcReceiptError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/sandbox.rs b/chain/jsonrpc/src/api/sandbox.rs new file mode 100644 index 00000000000..3991a98d57f --- /dev/null +++ b/chain/jsonrpc/src/api/sandbox.rs @@ -0,0 +1,33 @@ +use serde_json::Value; + +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::sandbox::{ + RpcSandboxFastForwardError, RpcSandboxFastForwardRequest, RpcSandboxPatchStateError, + RpcSandboxPatchStateRequest, +}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcSandboxPatchStateRequest { + fn parse(value: Option) -> Result { + parse_params::(value) + } +} + +impl RpcRequest for RpcSandboxFastForwardRequest { + fn parse(value: Option) -> Result { + parse_params::(value) + } +} + +impl RpcFrom for RpcSandboxPatchStateError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcSandboxFastForwardError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} diff --git a/chain/jsonrpc/src/api/status.rs b/chain/jsonrpc/src/api/status.rs new file mode 100644 index 00000000000..f70404a8f42 --- /dev/null +++ b/chain/jsonrpc/src/api/status.rs @@ -0,0 +1,51 @@ +use near_client_primitives::types::StatusError; +use near_jsonrpc_primitives::types::status::{ + RpcHealthResponse, RpcStatusError, RpcStatusResponse, +}; +use near_primitives::views::StatusResponse; + +use super::RpcFrom; + +impl RpcFrom for RpcStatusError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcStatusResponse { + fn rpc_from(status_response: StatusResponse) -> Self { + Self { status_response } + } +} + +impl RpcFrom + for near_jsonrpc_primitives::types::status::RpcDebugStatusResponse +{ + fn rpc_from(status_response: near_client_primitives::types::DebugStatusResponse) -> Self { + Self { status_response } + } +} + +impl RpcFrom for RpcHealthResponse { + fn rpc_from(_status_response: StatusResponse) -> Self { + Self {} + } +} + +impl RpcFrom for RpcStatusError { + fn rpc_from(error: StatusError) -> Self { + match error { + StatusError::InternalError { error_message } => Self::InternalError { error_message }, + StatusError::NodeIsSyncing => Self::NodeIsSyncing, + StatusError::NoNewBlocks { elapsed } => Self::NoNewBlocks { elapsed }, + StatusError::EpochOutOfBounds { epoch_id } => Self::EpochOutOfBounds { epoch_id }, + StatusError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcStatusError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/transactions.rs b/chain/jsonrpc/src/api/transactions.rs new file mode 100644 index 00000000000..b34825a99e8 --- /dev/null +++ b/chain/jsonrpc/src/api/transactions.rs @@ -0,0 +1,61 @@ +use serde_json::Value; + +use near_client_primitives::types::TxStatusError; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::transactions::{ + RpcBroadcastTransactionRequest, RpcTransactionError, RpcTransactionResponse, + RpcTransactionStatusCommonRequest, TransactionInfo, +}; +use near_primitives::hash::CryptoHash; +use near_primitives::types::AccountId; +use near_primitives::views::FinalExecutionOutcomeViewEnum; + +use super::{parse_params, parse_signed_transaction, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcBroadcastTransactionRequest { + fn parse(value: Option) -> Result { + let signed_transaction = parse_signed_transaction(value)?; + Ok(Self { signed_transaction }) + } +} + +impl RpcRequest for RpcTransactionStatusCommonRequest { + fn parse(value: Option) -> Result { + if let Ok((hash, account_id)) = parse_params::<(CryptoHash, AccountId)>(value.clone()) { + let transaction_info = TransactionInfo::TransactionId { hash, account_id }; + Ok(Self { transaction_info }) + } else { + let signed_transaction = parse_signed_transaction(value)?; + let transaction_info = TransactionInfo::Transaction(signed_transaction); + Ok(Self { transaction_info }) + } + } +} + +impl RpcFrom for RpcTransactionError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { debug_info: error.to_string() } + } +} + +impl RpcFrom for RpcTransactionError { + fn rpc_from(error: TxStatusError) -> Self { + match error { + TxStatusError::ChainError(err) => { + Self::InternalError { debug_info: format!("{:?}", err) } + } + TxStatusError::MissingTransaction(requested_transaction_hash) => { + Self::UnknownTransaction { requested_transaction_hash } + } + TxStatusError::InvalidTx(context) => Self::InvalidTransaction { context }, + TxStatusError::InternalError(debug_info) => Self::InternalError { debug_info }, + TxStatusError::TimeoutError => Self::TimeoutError, + } + } +} + +impl RpcFrom for RpcTransactionResponse { + fn rpc_from(final_execution_outcome: FinalExecutionOutcomeViewEnum) -> Self { + Self { final_execution_outcome } + } +} diff --git a/chain/jsonrpc/src/api/validator.rs b/chain/jsonrpc/src/api/validator.rs new file mode 100644 index 00000000000..49b965ff46c --- /dev/null +++ b/chain/jsonrpc/src/api/validator.rs @@ -0,0 +1,54 @@ +use serde_json::Value; + +use near_client_primitives::types::GetValidatorInfoError; +use near_jsonrpc_primitives::errors::RpcParseError; +use near_jsonrpc_primitives::types::validator::{ + RpcValidatorError, RpcValidatorRequest, RpcValidatorsOrderedRequest, +}; +use near_primitives::types::{EpochReference, MaybeBlockId}; + +use super::{parse_params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcValidatorRequest { + fn parse(value: Option) -> Result { + let epoch_reference = + if let Ok((block_id,)) = parse_params::<(MaybeBlockId,)>(value.clone()) { + match block_id { + Some(id) => EpochReference::BlockId(id), + None => EpochReference::Latest, + } + } else { + parse_params::(value)? + }; + Ok(Self { epoch_reference }) + } +} + +impl RpcRequest for RpcValidatorsOrderedRequest { + fn parse(value: Option) -> Result { + parse_params::(value) + } +} + +impl RpcFrom for RpcValidatorError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcValidatorError { + fn rpc_from(error: GetValidatorInfoError) -> Self { + match error { + GetValidatorInfoError::UnknownEpoch => Self::UnknownEpoch, + GetValidatorInfoError::ValidatorInfoUnavailable => Self::ValidatorInfoUnavailable, + GetValidatorInfoError::IOError(error_message) => Self::InternalError { error_message }, + GetValidatorInfoError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcValidatorError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/errors.rs b/chain/jsonrpc/src/errors.rs deleted file mode 100644 index dc97321842d..00000000000 --- a/chain/jsonrpc/src/errors.rs +++ /dev/null @@ -1,736 +0,0 @@ -use near_jsonrpc_primitives::errors::{RpcError, ServerError}; - -pub trait RpcFrom { - fn rpc_from(_: T) -> Self; -} - -pub trait RpcInto { - fn rpc_into(self) -> T; -} - -impl RpcFrom for T { - fn rpc_from(val: T) -> Self { - val - } -} - -impl RpcInto for T -where - X: RpcFrom, -{ - fn rpc_into(self) -> X { - X::rpc_from(self) - } -} - -// -- - -impl RpcFrom for RpcError { - fn rpc_from(error: actix::MailboxError) -> Self { - RpcError::new( - -32_000, - "Server error".to_string(), - Some(serde_json::Value::String(error.to_string())), - ) - } -} - -impl RpcFrom for ServerError { - fn rpc_from(error: actix::MailboxError) -> Self { - match error { - actix::MailboxError::Closed => ServerError::Closed, - actix::MailboxError::Timeout => ServerError::Timeout, - } - } -} - -impl RpcFrom for ServerError { - fn rpc_from(e: near_primitives::errors::InvalidTxError) -> ServerError { - ServerError::TxExecutionError(near_primitives::errors::TxExecutionError::InvalidTxError(e)) - } -} - -// -- block -- - -impl RpcFrom for near_jsonrpc_primitives::types::blocks::RpcBlockError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::blocks::RpcBlockError -{ - fn rpc_from(error: near_client_primitives::types::GetBlockError) -> Self { - match error { - near_client_primitives::types::GetBlockError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetBlockError::NotSyncedYet => Self::NotSyncedYet, - near_client_primitives::types::GetBlockError::IOError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetBlockError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcBlockError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- block / changes -- - -impl RpcFrom - for near_jsonrpc_primitives::types::changes::RpcStateChangesError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::changes::RpcStateChangesError -{ - fn rpc_from(error: near_client_primitives::types::GetBlockError) -> Self { - match error { - near_client_primitives::types::GetBlockError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetBlockError::NotSyncedYet => Self::NotSyncedYet, - near_client_primitives::types::GetBlockError::IOError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetBlockError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcStateChangesError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::changes::RpcStateChangesError -{ - fn rpc_from(error: near_client_primitives::types::GetStateChangesError) -> Self { - match error { - near_client_primitives::types::GetStateChangesError::IOError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetStateChangesError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetStateChangesError::NotSyncedYet => Self::NotSyncedYet, - near_client_primitives::types::GetStateChangesError::Unreachable { - ref error_message, - } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcStateChangesError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- changes / chunk -- - -impl RpcFrom for near_jsonrpc_primitives::types::chunks::RpcChunkError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_client_primitives::types::GetChunk -{ - fn rpc_from(chunk_reference: near_jsonrpc_primitives::types::chunks::ChunkReference) -> Self { - match chunk_reference { - near_jsonrpc_primitives::types::chunks::ChunkReference::BlockShardId { - block_id, - shard_id, - } => match block_id { - near_primitives::types::BlockId::Height(height) => Self::Height(height, shard_id), - near_primitives::types::BlockId::Hash(block_hash) => { - Self::BlockHash(block_hash, shard_id) - } - }, - near_jsonrpc_primitives::types::chunks::ChunkReference::ChunkHash { chunk_id } => { - Self::ChunkHash(chunk_id.into()) - } - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::chunks::RpcChunkError -{ - fn rpc_from(error: near_client_primitives::types::GetChunkError) -> Self { - match error { - near_client_primitives::types::GetChunkError::IOError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetChunkError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetChunkError::InvalidShardId { shard_id } => { - Self::InvalidShardId { shard_id } - } - near_client_primitives::types::GetChunkError::UnknownChunk { chunk_hash } => { - Self::UnknownChunk { chunk_hash } - } - near_client_primitives::types::GetChunkError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcChunkError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- chunk / config -- - -impl RpcFrom - for near_jsonrpc_primitives::types::config::RpcProtocolConfigError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::config::RpcProtocolConfigError -{ - fn rpc_from(error: near_client_primitives::types::GetProtocolConfigError) -> Self { - match error { - near_client_primitives::types::GetProtocolConfigError::UnknownBlock(error_message) => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetProtocolConfigError::IOError(error_message) => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetProtocolConfigError::Unreachable( - ref error_message, - ) => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcProtocolConfigError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- config / gas_price -- - -impl RpcFrom for near_jsonrpc_primitives::types::gas_price::RpcGasPriceError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::gas_price::RpcGasPriceError -{ - fn rpc_from(error: near_client_primitives::types::GetGasPriceError) -> Self { - match error { - near_client_primitives::types::GetGasPriceError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetGasPriceError::InternalError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetGasPriceError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcGasPriceError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- gas_price / light_client -- - -impl RpcFrom>> - for near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockResponse -{ - fn rpc_from( - light_client_block: Option>, - ) -> Self { - Self { light_client_block } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::light_client::RpcLightClientProofError -{ - fn rpc_from(error: near_client_primitives::types::GetExecutionOutcomeError) -> Self { - match error { - near_client_primitives::types::GetExecutionOutcomeError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - }, - near_client_primitives::types::GetExecutionOutcomeError::InconsistentState { - number_or_shards, execution_outcome_shard_id - } => Self::InconsistentState { number_or_shards, execution_outcome_shard_id }, - near_client_primitives::types::GetExecutionOutcomeError::NotConfirmed { - transaction_or_receipt_id - } => Self::NotConfirmed { transaction_or_receipt_id }, - near_client_primitives::types::GetExecutionOutcomeError::UnknownTransactionOrReceipt { - transaction_or_receipt_id - } => Self::UnknownTransactionOrReceipt { transaction_or_receipt_id }, - near_client_primitives::types::GetExecutionOutcomeError::UnavailableShard { - transaction_or_receipt_id, - shard_id - } => Self::UnavailableShard { transaction_or_receipt_id, shard_id }, - near_client_primitives::types::GetExecutionOutcomeError::InternalError { error_message } => { - Self::InternalError { error_message } - }, - near_client_primitives::types::GetExecutionOutcomeError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT.with_label_values( - &["RpcLightClientProofError"], - ).inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::light_client::RpcLightClientProofError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::light_client::RpcLightClientProofError -{ - fn rpc_from(error: near_client_primitives::types::GetBlockProofError) -> Self { - match error { - near_client_primitives::types::GetBlockProofError::UnknownBlock { error_message } => { - Self::UnknownBlock { error_message } - } - near_client_primitives::types::GetBlockProofError::InternalError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetBlockProofError::Unreachable { - ref error_message, - } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcLightClientProofError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockError -{ - fn rpc_from(error: near_client_primitives::types::GetNextLightClientBlockError) -> Self { - match error { - near_client_primitives::types::GetNextLightClientBlockError::InternalError { - error_message, - } => Self::InternalError { error_message }, - near_client_primitives::types::GetNextLightClientBlockError::UnknownBlock { - error_message, - } => Self::UnknownBlock { error_message }, - near_client_primitives::types::GetNextLightClientBlockError::EpochOutOfBounds { - epoch_id, - } => Self::EpochOutOfBounds { epoch_id }, - near_client_primitives::types::GetNextLightClientBlockError::Unreachable { - ref error_message, - } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcLightClientNextBlockError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- light_client / network_info -- - -impl RpcFrom - for near_jsonrpc_primitives::types::network_info::RpcNetworkInfoError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::network_info::RpcPeerInfo -{ - fn rpc_from(peer_info: near_network_primitives::types::PeerInfo) -> Self { - Self { id: peer_info.id, addr: peer_info.addr, account_id: peer_info.account_id } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::network_info::RpcKnownProducer -{ - fn rpc_from(known_producer: near_network_primitives::types::KnownProducer) -> Self { - Self { - account_id: known_producer.account_id, - addr: known_producer.addr, - peer_id: known_producer.peer_id, - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::network_info::RpcNetworkInfoResponse -{ - fn rpc_from(network_info_response: near_client_primitives::types::NetworkInfoResponse) -> Self { - Self { - active_peers: network_info_response - .connected_peers - .iter() - .map(|pi| pi.clone().rpc_into()) - .collect(), - num_active_peers: network_info_response.num_connected_peers, - peer_max_count: network_info_response.peer_max_count, - sent_bytes_per_sec: network_info_response.sent_bytes_per_sec, - received_bytes_per_sec: network_info_response.received_bytes_per_sec, - known_producers: network_info_response - .known_producers - .iter() - .map(|kp| kp.clone().rpc_into()) - .collect(), - } - } -} - -impl RpcFrom for near_jsonrpc_primitives::types::network_info::RpcNetworkInfoError { - fn rpc_from(error_message: String) -> Self { - Self::InternalError { error_message } - } -} - -// -- network_info / query -- - -impl RpcFrom for near_jsonrpc_primitives::types::query::RpcQueryError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::query::RpcQueryError -{ - fn rpc_from(error: near_client_primitives::types::QueryError) -> Self { - match error { - near_client_primitives::types::QueryError::InternalError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::QueryError::NoSyncedBlocks => Self::NoSyncedBlocks, - near_client_primitives::types::QueryError::UnavailableShard { requested_shard_id } => { - Self::UnavailableShard { requested_shard_id } - } - near_client_primitives::types::QueryError::UnknownBlock { block_reference } => { - Self::UnknownBlock { block_reference } - } - near_client_primitives::types::QueryError::GarbageCollectedBlock { - block_height, - block_hash, - } => Self::GarbageCollectedBlock { block_height, block_hash }, - near_client_primitives::types::QueryError::InvalidAccount { - requested_account_id, - block_height, - block_hash, - } => Self::InvalidAccount { requested_account_id, block_height, block_hash }, - near_client_primitives::types::QueryError::UnknownAccount { - requested_account_id, - block_height, - block_hash, - } => Self::UnknownAccount { requested_account_id, block_height, block_hash }, - near_client_primitives::types::QueryError::NoContractCode { - contract_account_id, - block_height, - block_hash, - } => Self::NoContractCode { contract_account_id, block_height, block_hash }, - near_client_primitives::types::QueryError::UnknownAccessKey { - public_key, - block_height, - block_hash, - } => Self::UnknownAccessKey { public_key, block_height, block_hash }, - near_client_primitives::types::QueryError::ContractExecutionError { - vm_error, - block_height, - block_hash, - } => Self::ContractExecutionError { vm_error, block_height, block_hash }, - near_client_primitives::types::QueryError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcQueryError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - near_client_primitives::types::QueryError::TooLargeContractState { - contract_account_id, - block_height, - block_hash, - } => Self::TooLargeContractState { contract_account_id, block_height, block_hash }, - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::query::RpcQueryResponse -{ - fn rpc_from(query_response: near_primitives::views::QueryResponse) -> Self { - Self { - kind: RpcFrom::rpc_from(query_response.kind), - block_hash: query_response.block_hash, - block_height: query_response.block_height, - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::query::QueryResponseKind -{ - fn rpc_from(query_response_kind: near_primitives::views::QueryResponseKind) -> Self { - match query_response_kind { - near_primitives::views::QueryResponseKind::ViewAccount(account_view) => { - Self::ViewAccount(account_view) - } - near_primitives::views::QueryResponseKind::ViewCode(contract_code_view) => { - Self::ViewCode(contract_code_view) - } - near_primitives::views::QueryResponseKind::ViewState(view_state_result) => { - Self::ViewState(view_state_result) - } - near_primitives::views::QueryResponseKind::CallResult(call_result) => { - Self::CallResult(call_result) - } - near_primitives::views::QueryResponseKind::AccessKey(access_key_view) => { - Self::AccessKey(access_key_view) - } - near_primitives::views::QueryResponseKind::AccessKeyList(access_key_list) => { - Self::AccessKeyList(access_key_list) - } - } - } -} - -// -- query / receipts -- - -impl RpcFrom for near_jsonrpc_primitives::types::receipts::RpcReceiptError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_client_primitives::types::GetReceipt -{ - fn rpc_from( - receipt_reference: near_jsonrpc_primitives::types::receipts::ReceiptReference, - ) -> Self { - Self { receipt_id: receipt_reference.receipt_id } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::receipts::RpcReceiptError -{ - fn rpc_from(error: near_client_primitives::types::GetReceiptError) -> Self { - match error { - near_client_primitives::types::GetReceiptError::IOError(error_message) => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetReceiptError::UnknownReceipt(hash) => { - Self::UnknownReceipt { receipt_id: hash } - } - near_client_primitives::types::GetReceiptError::Unreachable(ref error_message) => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcReceiptError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- receipts / sandbox -- - -impl RpcFrom - for near_jsonrpc_primitives::types::sandbox::RpcSandboxPatchStateError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -// -- sandbox / status -- - -impl RpcFrom for near_jsonrpc_primitives::types::status::RpcStatusError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::status::RpcStatusResponse -{ - fn rpc_from(status_response: near_primitives::views::StatusResponse) -> Self { - Self { status_response } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::status::RpcDebugStatusResponse -{ - fn rpc_from(status_response: near_client_primitives::types::DebugStatusResponse) -> Self { - Self { status_response } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::status::RpcHealthResponse -{ - fn rpc_from(_status_response: near_primitives::views::StatusResponse) -> Self { - Self {} - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::status::RpcStatusError -{ - fn rpc_from(error: near_client_primitives::types::StatusError) -> Self { - match error { - near_client_primitives::types::StatusError::InternalError { error_message } => { - Self::InternalError { error_message } - } - near_client_primitives::types::StatusError::NodeIsSyncing => Self::NodeIsSyncing, - near_client_primitives::types::StatusError::NoNewBlocks { elapsed } => { - Self::NoNewBlocks { elapsed } - } - near_client_primitives::types::StatusError::EpochOutOfBounds { epoch_id } => { - Self::EpochOutOfBounds { epoch_id } - } - near_client_primitives::types::StatusError::Unreachable { ref error_message } => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcStatusError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- status / transactions -- - -impl RpcFrom - for near_jsonrpc_primitives::types::transactions::RpcTransactionError -{ - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { debug_info: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::transactions::RpcTransactionError -{ - fn rpc_from(error: near_client_primitives::types::TxStatusError) -> Self { - match error { - near_client_primitives::types::TxStatusError::ChainError(err) => { - Self::InternalError { debug_info: format!("{:?}", err) } - } - near_client_primitives::types::TxStatusError::MissingTransaction( - requested_transaction_hash, - ) => Self::UnknownTransaction { requested_transaction_hash }, - near_client_primitives::types::TxStatusError::InvalidTx(context) => { - Self::InvalidTransaction { context } - } - near_client_primitives::types::TxStatusError::InternalError(debug_info) => { - Self::InternalError { debug_info } - } - near_client_primitives::types::TxStatusError::TimeoutError => Self::TimeoutError, - } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::transactions::RpcTransactionResponse -{ - fn rpc_from( - final_execution_outcome: near_primitives::views::FinalExecutionOutcomeViewEnum, - ) -> Self { - Self { final_execution_outcome } - } -} - -// -- transactions / validator -- - -impl RpcFrom for near_jsonrpc_primitives::types::validator::RpcValidatorError { - fn rpc_from(error: actix::MailboxError) -> Self { - Self::InternalError { error_message: error.to_string() } - } -} - -impl RpcFrom - for near_jsonrpc_primitives::types::validator::RpcValidatorError -{ - fn rpc_from(error: near_client_primitives::types::GetValidatorInfoError) -> Self { - match error { - near_client_primitives::types::GetValidatorInfoError::UnknownEpoch => { - Self::UnknownEpoch - } - near_client_primitives::types::GetValidatorInfoError::ValidatorInfoUnavailable => { - Self::ValidatorInfoUnavailable - } - near_client_primitives::types::GetValidatorInfoError::IOError(error_message) => { - Self::InternalError { error_message } - } - near_client_primitives::types::GetValidatorInfoError::Unreachable( - ref error_message, - ) => { - tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); - crate::metrics::RPC_UNREACHABLE_ERROR_COUNT - .with_label_values(&["RpcValidatorError"]) - .inc(); - Self::InternalError { error_message: error.to_string() } - } - } - } -} - -// -- validator diff --git a/chain/jsonrpc/src/lib.rs b/chain/jsonrpc/src/lib.rs index 8a78bdce015..976a2d95014 100644 --- a/chain/jsonrpc/src/lib.rs +++ b/chain/jsonrpc/src/lib.rs @@ -34,12 +34,11 @@ use near_primitives::transaction::SignedTransaction; use near_primitives::types::AccountId; use near_primitives::views::FinalExecutionOutcomeViewEnum; -mod errors; +mod api; mod metrics; -mod parser; -pub use errors::{RpcFrom, RpcInto}; -use parser::RpcRequest; +use api::RpcRequest; +pub use api::{RpcFrom, RpcInto}; #[derive(Serialize, Deserialize, Clone, Copy, Debug)] pub struct RpcPollingConfig { diff --git a/chain/jsonrpc/src/parser.rs b/chain/jsonrpc/src/parser.rs deleted file mode 100644 index 3b9167051f1..00000000000 --- a/chain/jsonrpc/src/parser.rs +++ /dev/null @@ -1,259 +0,0 @@ -use serde::de::DeserializeOwned; -use serde_json::Value; - -use near_jsonrpc_primitives::errors::RpcParseError; -use near_primitives::borsh::BorshDeserialize; - -pub(crate) trait RpcRequest: Sized { - fn parse(value: Option) -> Result; -} - -fn parse_params(value: Option) -> Result { - if let Some(value) = value { - serde_json::from_value(value) - .map_err(|err| RpcParseError(format!("Failed parsing args: {}", err))) - } else { - Err(RpcParseError("Require at least one parameter".to_owned())) - } -} - -fn parse_signed_transaction( - value: Option, -) -> Result { - let (encoded,) = parse_params::<(String,)>(value)?; - let bytes = near_primitives::serialize::from_base64(&encoded) - .map_err(|err| RpcParseError(err.to_string()))?; - Ok(near_primitives::transaction::SignedTransaction::try_from_slice(&bytes) - .map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err)))?) -} - -impl RpcRequest for near_jsonrpc_primitives::types::blocks::RpcBlockRequest { - fn parse(value: Option) -> Result { - let block_reference = if let Ok((block_id,)) = - parse_params::<(near_primitives::types::BlockId,)>(value.clone()) - { - near_primitives::types::BlockReference::BlockId(block_id) - } else { - parse_params::(value)? - }; - Ok(Self { block_reference }) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::changes::RpcStateChangesInBlockRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::changes::RpcStateChangesInBlockByTypeRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::chunks::RpcChunkRequest { - fn parse(value: Option) -> Result { - // Try to parse legacy positioned args and if it fails parse newer named args - let chunk_reference = if let Ok((chunk_id,)) = - parse_params::<(near_primitives::hash::CryptoHash,)>(value.clone()) - { - near_jsonrpc_primitives::types::chunks::ChunkReference::ChunkHash { chunk_id } - } else if let Ok(((block_id, shard_id),)) = parse_params::<(( - near_primitives::types::BlockId, - near_primitives::types::ShardId, - ),)>(value.clone()) - { - near_jsonrpc_primitives::types::chunks::ChunkReference::BlockShardId { - block_id, - shard_id, - } - } else { - parse_params::(value)? - }; - Ok(Self { chunk_reference }) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::config::RpcProtocolConfigRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - .map(|block_reference| Self { block_reference }) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::gas_price::RpcGasPriceRequest { - fn parse(value: Option) -> Result { - parse_params::<(near_primitives::types::MaybeBlockId,)>(value) - .map(|(block_id,)| Self { block_id }) - } -} - -impl RpcRequest - for near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest -{ - fn parse(value: Option) -> Result { - Ok(parse_params::(value)?) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockRequest { - fn parse(value: Option) -> Result { - if let Ok((last_block_hash,)) = - parse_params::<(near_primitives::hash::CryptoHash,)>(value.clone()) - { - Ok(Self { last_block_hash }) - } else { - Ok(parse_params::(value)?) - } - } -} - -/// Max size of the query path (soft-deprecated) -const QUERY_DATA_MAX_SIZE: usize = 10 * 1024; - -impl RpcRequest for near_jsonrpc_primitives::types::query::RpcQueryRequest { - fn parse(value: Option) -> Result { - let query_request = if let Ok((path, data)) = - parse_params::<(String, String)>(value.clone()) - { - // Handle a soft-deprecated version of the query API, which is based on - // positional arguments with a "path"-style first argument. - // - // This whole block can be removed one day, when the new API is 100% adopted. - let data = near_primitives::serialize::from_base(&data) - .map_err(|err| RpcParseError(err.to_string()))?; - let query_data_size = path.len() + data.len(); - if query_data_size > QUERY_DATA_MAX_SIZE { - return Err(RpcParseError(format!( - "Query data size {} is too large", - query_data_size - ))); - } - - let mut path_parts = path.splitn(3, '/'); - let make_err = || RpcParseError("Not enough query parameters provided".to_string()); - let query_command = path_parts.next().ok_or_else(make_err)?; - let account_id = path_parts - .next() - .ok_or_else(make_err)? - .parse() - .map_err(|err| RpcParseError(format!("{}", err)))?; - let maybe_extra_arg = path_parts.next(); - - let request = match query_command { - "account" => near_primitives::views::QueryRequest::ViewAccount { account_id }, - "access_key" => match maybe_extra_arg { - None => near_primitives::views::QueryRequest::ViewAccessKeyList { account_id }, - Some(pk) => near_primitives::views::QueryRequest::ViewAccessKey { - account_id, - public_key: pk - .parse() - .map_err(|_| RpcParseError("Invalid public key".to_string()))?, - }, - }, - "code" => near_primitives::views::QueryRequest::ViewCode { account_id }, - "contract" => near_primitives::views::QueryRequest::ViewState { - account_id, - prefix: data.into(), - }, - "call" => match maybe_extra_arg { - Some(method_name) => near_primitives::views::QueryRequest::CallFunction { - account_id, - method_name: method_name.to_string(), - args: data.into(), - }, - None => return Err(RpcParseError("Method name is missing".to_string())), - }, - _ => return Err(RpcParseError(format!("Unknown path {}", query_command))), - }; - // Use Finality::None here to make backward compatibility tests work - Self { request, block_reference: near_primitives::types::BlockReference::latest() } - } else { - parse_params::(value)? - }; - Ok(query_request) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::receipts::RpcReceiptRequest { - fn parse(value: Option) -> Result { - let receipt_reference = - parse_params::(value)?; - Ok(Self { receipt_reference }) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::sandbox::RpcSandboxPatchStateRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::transactions::RpcBroadcastTransactionRequest { - fn parse(value: Option) -> Result { - let signed_transaction = parse_signed_transaction(value)?; - Ok(Self { signed_transaction }) - } -} - -impl RpcRequest - for near_jsonrpc_primitives::types::transactions::RpcTransactionStatusCommonRequest -{ - fn parse(value: Option) -> Result { - if let Ok((hash, account_id)) = parse_params::<( - near_primitives::hash::CryptoHash, - near_primitives::types::AccountId, - )>(value.clone()) - { - let transaction_info = - near_jsonrpc_primitives::types::transactions::TransactionInfo::TransactionId { - hash, - account_id, - }; - Ok(Self { transaction_info }) - } else { - let signed_transaction = parse_signed_transaction(value)?; - let transaction_info = - near_jsonrpc_primitives::types::transactions::TransactionInfo::Transaction( - signed_transaction, - ); - Ok(Self { transaction_info }) - } - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::validator::RpcValidatorRequest { - fn parse(value: Option) -> Result { - let epoch_reference = if let Ok((block_id,)) = - parse_params::<(near_primitives::types::MaybeBlockId,)>(value.clone()) - { - match block_id { - Some(id) => near_primitives::types::EpochReference::BlockId(id), - None => near_primitives::types::EpochReference::Latest, - } - } else { - parse_params::(value)? - }; - Ok(Self { epoch_reference }) - } -} - -impl RpcRequest for near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - } -} - -#[cfg(feature = "test_features")] -impl RpcRequest for near_jsonrpc_adversarial_primitives::SetRoutingTableRequest { - fn parse(value: Option) -> Result { - parse_params::(value) - } -}