diff --git a/chain/client-primitives/src/types.rs b/chain/client-primitives/src/types.rs index c1e8b277cd7..0e7cff968af 100644 --- a/chain/client-primitives/src/types.rs +++ b/chain/client-primitives/src/types.rs @@ -339,16 +339,83 @@ pub struct Status { pub is_health_check: bool, } +#[derive(thiserror::Error, Debug)] +pub enum StatusError { + #[error("Node is syncing")] + NodeIsSyncing, + #[error("No blocks for {elapsed:?}")] + NoNewBlocks { elapsed: std::time::Duration }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: near_primitives::types::EpochId }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for StatusError { + fn from(error: near_chain_primitives::error::Error) -> Self { + match error.kind() { + near_chain_primitives::error::ErrorKind::DBNotFoundErr(error_message) + | near_chain_primitives::error::ErrorKind::IOErr(error_message) + | near_chain_primitives::error::ErrorKind::ValidatorError(error_message) => { + Self::InternalError { error_message } + } + near_chain_primitives::error::ErrorKind::EpochOutOfBounds(epoch_id) => { + Self::EpochOutOfBounds { epoch_id } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + impl Message for Status { - type Result = Result; + type Result = Result; } pub struct GetNextLightClientBlock { pub last_block_hash: CryptoHash, } +#[derive(thiserror::Error, Debug)] +pub enum GetNextLightClientBlockError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: near_primitives::types::EpochId }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetNextLightClientBlockError { + fn from(error: near_chain_primitives::error::Error) -> Self { + match error.kind() { + near_chain_primitives::error::ErrorKind::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + near_chain_primitives::error::ErrorKind::IOErr(error_message) => { + Self::InternalError { error_message } + } + near_chain_primitives::error::ErrorKind::EpochOutOfBounds(epoch_id) => { + Self::EpochOutOfBounds { epoch_id } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + impl Message for GetNextLightClientBlock { - type Result = Result, String>; + type Result = Result, GetNextLightClientBlockError>; } pub struct GetNetworkInfo {} @@ -482,7 +549,7 @@ pub struct GetValidatorOrdered { } impl Message for GetValidatorOrdered { - type Result = Result, String>; + type Result = Result, GetValidatorInfoError>; } pub struct GetStateChanges { @@ -490,8 +557,38 @@ pub struct GetStateChanges { pub state_changes_request: StateChangesRequestView, } +#[derive(thiserror::Error, Debug)] +pub enum GetStateChangesError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetStateChangesError { + fn from(error: near_chain_primitives::Error) -> Self { + match error.kind() { + near_chain_primitives::ErrorKind::IOErr(error_message) => { + Self::IOError { error_message } + } + near_chain_primitives::ErrorKind::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + impl Message for GetStateChanges { - type Result = Result; + type Result = Result; } pub struct GetStateChangesInBlock { @@ -499,7 +596,7 @@ pub struct GetStateChangesInBlock { } impl Message for GetStateChangesInBlock { - type Result = Result; + type Result = Result; } pub struct GetStateChangesWithCauseInBlock { @@ -507,20 +604,73 @@ pub struct GetStateChangesWithCauseInBlock { } impl Message for GetStateChangesWithCauseInBlock { - type Result = Result; + type Result = Result; } pub struct GetExecutionOutcome { pub id: TransactionOrReceiptId, } +#[derive(thiserror::Error, Debug)] +pub enum GetExecutionOutcomeError { + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Inconsistent state. Total number of shards is {number_or_shards} but the execution outcome is in shard {execution_outcome_shard_id}")] + InconsistentState { + number_or_shards: usize, + execution_outcome_shard_id: near_primitives::types::ShardId, + }, + #[error("{transaction_or_receipt_id} has not been confirmed")] + NotConfirmed { transaction_or_receipt_id: near_primitives::hash::CryptoHash }, + #[error("{transaction_or_receipt_id} does not exist")] + UnknownTransactionOrReceipt { transaction_or_receipt_id: near_primitives::hash::CryptoHash }, + #[error("Node doesn't track the shard where {transaction_or_receipt_id} is executed")] + UnavailableShard { + transaction_or_receipt_id: near_primitives::hash::CryptoHash, + shard_id: near_primitives::types::ShardId, + }, + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetExecutionOutcomeError { + fn from(error: TxStatusError) -> Self { + match error { + TxStatusError::ChainError(err) => { + Self::InternalError { error_message: err.to_string() } + } + _ => Self::Unreachable { error_message: format!("{:?}", error) }, + } + } +} + +impl From for GetExecutionOutcomeError { + fn from(error: near_chain_primitives::error::Error) -> Self { + match error.kind() { + near_chain_primitives::ErrorKind::IOErr(error_message) => { + Self::InternalError { error_message } + } + near_chain_primitives::ErrorKind::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + pub struct GetExecutionOutcomeResponse { pub outcome_proof: ExecutionOutcomeWithIdView, pub outcome_root_proof: MerklePath, } impl Message for GetExecutionOutcome { - type Result = Result; + type Result = Result; } pub struct GetExecutionOutcomesForBlock { @@ -541,8 +691,36 @@ pub struct GetBlockProofResponse { pub proof: MerklePath, } +#[derive(thiserror::Error, Debug)] +pub enum GetBlockProofError { + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetBlockProofError { + fn from(error: near_chain_primitives::error::Error) -> Self { + match error.kind() { + near_chain_primitives::error::ErrorKind::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + near_chain_primitives::error::ErrorKind::Other(error_message) => { + Self::InternalError { error_message } + } + err => Self::Unreachable { error_message: err.to_string() }, + } + } +} + impl Message for GetBlockProof { - type Result = Result; + type Result = Result; } pub struct GetReceipt { diff --git a/chain/client/src/client_actor.rs b/chain/client/src/client_actor.rs index 6843c7ca540..483fa3edabd 100644 --- a/chain/client/src/client_actor.rs +++ b/chain/client/src/client_actor.rs @@ -55,7 +55,7 @@ use crate::AdversarialControls; use crate::StatusResponse; use near_client_primitives::types::{ Error, GetNetworkInfo, NetworkInfoResponse, ShardSyncDownload, ShardSyncStatus, Status, - StatusSyncInfo, SyncStatus, + StatusError, StatusSyncInfo, SyncStatus, }; use near_primitives::block_header::ApprovalType; @@ -530,7 +530,7 @@ impl Handler for ClientActor { } impl Handler for ClientActor { - type Result = Result; + type Result = Result; #[perf] fn handle(&mut self, msg: Status, ctx: &mut Context) -> Self::Result { @@ -538,12 +538,8 @@ impl Handler for ClientActor { let _d = DelayDetector::new("client status".to_string().into()); self.check_triggers(ctx); - let head = self.client.chain.head().map_err(|err| err.to_string())?; - let header = self - .client - .chain - .get_block_header(&head.last_block_hash) - .map_err(|err| err.to_string())?; + let head = self.client.chain.head()?; + let header = self.client.chain.get_block_header(&head.last_block_hash)?; let latest_block_time = header.raw_timestamp().clone(); if msg.is_health_check { let now = Utc::now(); @@ -556,19 +552,18 @@ impl Handler for ClientActor { * STATUS_WAIT_TIME_MULTIPLIER, ) { - return Err(format!("No blocks for {:?}.", elapsed)); + return Err(StatusError::NoNewBlocks { elapsed }); } } if self.client.sync_status.is_syncing() { - return Err("Node is syncing.".to_string()); + return Err(StatusError::NodeIsSyncing); } } let validators = self .client .runtime_adapter - .get_epoch_block_producers_ordered(&head.epoch_id, &head.last_block_hash) - .map_err(|err| err.to_string())? + .get_epoch_block_producers_ordered(&head.epoch_id, &head.last_block_hash)? .into_iter() .map(|(validator_stake, is_slashed)| ValidatorInfo { account_id: validator_stake.take_account_id(), @@ -576,11 +571,8 @@ impl Handler for ClientActor { }) .collect(); - let protocol_version = self - .client - .runtime_adapter - .get_epoch_protocol_version(&head.epoch_id) - .map_err(|err| err.to_string())?; + let protocol_version = + self.client.runtime_adapter.get_epoch_protocol_version(&head.epoch_id)?; let validator_account_id = self.client.validator_signer.as_ref().map(|vs| vs.validator_id()).cloned(); diff --git a/chain/client/src/view_client.rs b/chain/client/src/view_client.rs index c18cea4b4b3..3e68c7f68dc 100644 --- a/chain/client/src/view_client.rs +++ b/chain/client/src/view_client.rs @@ -19,9 +19,10 @@ use near_chain::{ }; use near_chain_configs::{ClientConfig, ProtocolConfigView}; use near_client_primitives::types::{ - Error, GetBlock, GetBlockError, GetBlockProof, GetBlockProofResponse, GetBlockWithMerkleTree, - GetChunkError, GetExecutionOutcome, GetExecutionOutcomesForBlock, GetGasPrice, - GetGasPriceError, GetProtocolConfig, GetProtocolConfigError, GetReceipt, GetReceiptError, + Error, GetBlock, GetBlockError, GetBlockProof, GetBlockProofError, GetBlockProofResponse, + GetBlockWithMerkleTree, GetChunkError, GetExecutionOutcome, GetExecutionOutcomeError, + GetExecutionOutcomesForBlock, GetGasPrice, GetGasPriceError, GetNextLightClientBlockError, + GetProtocolConfig, GetProtocolConfigError, GetReceipt, GetReceiptError, GetStateChangesError, GetStateChangesWithCauseInBlock, GetValidatorInfoError, Query, QueryError, TxStatus, TxStatusError, }; @@ -658,11 +659,12 @@ impl Handler for ViewClientActor { } impl Handler for ViewClientActor { - type Result = Result, String>; + type Result = Result, GetValidatorInfoError>; #[perf] fn handle(&mut self, msg: GetValidatorOrdered, _: &mut Self::Context) -> Self::Result { - self.maybe_block_id_to_block_hash(msg.block_id) + Ok(self + .maybe_block_id_to_block_hash(msg.block_id) .and_then(|block_hash| self.chain.get_block_header(&block_hash).map(|h| h.clone())) .and_then(|header| { get_epoch_block_producers_view( @@ -670,41 +672,44 @@ impl Handler for ViewClientActor { header.prev_hash(), &*self.runtime_adapter, ) - }) - .map_err(|err| err.to_string()) + })?) } } /// Returns a list of change kinds per account in a store for a given block. impl Handler for ViewClientActor { - type Result = Result; + type Result = Result; #[perf] fn handle(&mut self, msg: GetStateChangesInBlock, _: &mut Self::Context) -> Self::Result { - self.chain + Ok(self + .chain .store() - .get_state_changes_in_block(&msg.block_hash) - .map(|state_changes_kinds| state_changes_kinds.into_iter().map(Into::into).collect()) - .map_err(|e| e.to_string()) + .get_state_changes_in_block(&msg.block_hash)? + .into_iter() + .map(Into::into) + .collect()) } } /// Returns a list of changes in a store for a given block filtering by the state changes request. impl Handler for ViewClientActor { - type Result = Result; + type Result = Result; #[perf] fn handle(&mut self, msg: GetStateChanges, _: &mut Self::Context) -> Self::Result { - self.chain + Ok(self + .chain .store() - .get_state_changes(&msg.block_hash, &msg.state_changes_request.into()) - .map(|state_changes| state_changes.into_iter().map(Into::into).collect()) - .map_err(|e| e.to_string()) + .get_state_changes(&msg.block_hash, &msg.state_changes_request.into())? + .into_iter() + .map(Into::into) + .collect()) } } /// Returns a list of changes in a store with causes for a given block. impl Handler for ViewClientActor { - type Result = Result; + type Result = Result; #[perf] fn handle( @@ -712,11 +717,13 @@ impl Handler for ViewClientActor { msg: GetStateChangesWithCauseInBlock, _: &mut Self::Context, ) -> Self::Result { - self.chain + Ok(self + .chain .store() - .get_state_changes_with_cause_in_block(&msg.block_hash) - .map(|state_changes| state_changes.into_iter().map(Into::into).collect()) - .map_err(|e| e.to_string()) + .get_state_changes_with_cause_in_block(&msg.block_hash)? + .into_iter() + .map(Into::into) + .collect()) } } @@ -729,28 +736,23 @@ impl Handler for ViewClientActor { /// 3. Otherwise, return the last final block in the epoch that follows that of the last block known /// to the light client impl Handler for ViewClientActor { - type Result = Result, String>; + type Result = Result, GetNextLightClientBlockError>; #[perf] fn handle(&mut self, msg: GetNextLightClientBlock, _: &mut Self::Context) -> Self::Result { - let last_block_header = - self.chain.get_block_header(&msg.last_block_hash).map_err(|err| err.to_string())?; + let last_block_header = self.chain.get_block_header(&msg.last_block_hash)?; let last_epoch_id = last_block_header.epoch_id().clone(); let last_next_epoch_id = last_block_header.next_epoch_id().clone(); let last_height = last_block_header.height(); - let head = self.chain.head().map_err(|err| err.to_string())?; + let head = self.chain.head()?; if last_epoch_id == head.epoch_id || last_next_epoch_id == head.epoch_id { - let head_header = self - .chain - .get_block_header(&head.last_block_hash) - .map_err(|err| err.to_string())?; + let head_header = self.chain.get_block_header(&head.last_block_hash)?; let ret = Chain::create_light_client_block( &head_header.clone(), &*self.runtime_adapter, self.chain.mut_store(), - ) - .map_err(|err| err.to_string())?; + )?; if ret.inner_lite.height <= last_height { Ok(None) @@ -764,7 +766,7 @@ impl Handler for ViewClientActor { if let ErrorKind::DBNotFoundErr(_) = e.kind() { Ok(None) } else { - Err(e.to_string()) + Err(e.into()) } } } @@ -773,7 +775,7 @@ impl Handler for ViewClientActor { } impl Handler for ViewClientActor { - type Result = Result; + type Result = Result; #[perf] fn handle(&mut self, msg: GetExecutionOutcome, _: &mut Self::Context) -> Self::Result { @@ -790,8 +792,7 @@ impl Handler for ViewClientActor { let mut outcome_proof = outcome.clone(); let next_block_hash = self .chain - .get_next_block_hash_with_new_chunk(&outcome_proof.block_hash, target_shard_id) - .map_err(|e| e.to_string())? + .get_next_block_hash_with_new_chunk(&outcome_proof.block_hash, target_shard_id)? .cloned(); match next_block_hash { Some(h) => { @@ -800,14 +801,16 @@ impl Handler for ViewClientActor { // should be fast let outcome_roots = self .chain - .get_block(&h) - .map_err(|e| e.to_string())? + .get_block(&h)? .chunks() .iter() .map(|header| header.outcome_root()) .collect::>(); if target_shard_id >= (outcome_roots.len() as u64) { - return Err(format!("Inconsistent state. Total number of shards is {} but the execution outcome is in shard {}", outcome_roots.len(), target_shard_id)); + return Err(GetExecutionOutcomeError::InconsistentState { + number_or_shards: outcome_roots.len(), + execution_outcome_shard_id: target_shard_id, + }); } Ok(GetExecutionOutcomeResponse { outcome_proof: outcome_proof.into(), @@ -816,7 +819,9 @@ impl Handler for ViewClientActor { .clone(), }) } - None => Err(format!("{} has not been confirmed", id)), + None => Err(GetExecutionOutcomeError::NotConfirmed { + transaction_or_receipt_id: id, + }), } } Err(e) => match e.kind() { @@ -828,12 +833,17 @@ impl Handler for ViewClientActor { target_shard_id, true, ) { - Err(format!("{} does not exist", id)) + Err(GetExecutionOutcomeError::UnknownTransactionOrReceipt { + transaction_or_receipt_id: id, + }) } else { - Err(format!("Node doesn't track the shard where {} is executed", id)) + Err(GetExecutionOutcomeError::UnavailableShard { + transaction_or_receipt_id: id, + shard_id: target_shard_id, + }) } } - _ => Err(e.to_string()), + _ => Err(e.into()), }, } } @@ -870,20 +880,14 @@ impl Handler for ViewClientActor { } impl Handler for ViewClientActor { - type Result = Result; + type Result = Result; #[perf] fn handle(&mut self, msg: GetBlockProof, _: &mut Self::Context) -> Self::Result { - self.chain.check_block_final_and_canonical(&msg.block_hash).map_err(|e| e.to_string())?; - self.chain - .check_block_final_and_canonical(&msg.head_block_hash) - .map_err(|e| e.to_string())?; - let block_header_lite = - self.chain.get_block_header(&msg.block_hash).map_err(|e| e.to_string())?.clone().into(); - let block_proof = self - .chain - .get_block_proof(&msg.block_hash, &msg.head_block_hash) - .map_err(|e| e.to_string())?; + self.chain.check_block_final_and_canonical(&msg.block_hash)?; + self.chain.check_block_final_and_canonical(&msg.head_block_hash)?; + let block_header_lite = self.chain.get_block_header(&msg.block_hash)?.clone().into(); + let block_proof = self.chain.get_block_proof(&msg.block_hash, &msg.head_block_hash)?; Ok(GetBlockProofResponse { block_header_lite, proof: block_proof }) } } diff --git a/chain/indexer/src/streamer/fetchers.rs b/chain/indexer/src/streamer/fetchers.rs index a018a940a84..7e200976718 100644 --- a/chain/indexer/src/streamer/fetchers.rs +++ b/chain/indexer/src/streamer/fetchers.rs @@ -19,7 +19,7 @@ pub(crate) async fn fetch_status( client .send(near_client::Status { is_health_check: false }) .await? - .map_err(FailedToFetchData::String) + .map_err(|err| FailedToFetchData::String(err.to_string())) } /// Fetches the status to retrieve `latest_block_height` to determine if we need to fetch @@ -64,7 +64,7 @@ pub(crate) async fn fetch_state_changes( client .send(near_client::GetStateChangesWithCauseInBlock { block_hash }) .await? - .map_err(FailedToFetchData::String) + .map_err(|err| FailedToFetchData::String(err.to_string())) } /// Fetches single chunk (as `near_primitives::views::ChunkView`) by provided `near_client::GetChunk` enum diff --git a/chain/jsonrpc-primitives/src/errors.rs b/chain/jsonrpc-primitives/src/errors.rs index 571e5fb3294..8729b9eff46 100644 --- a/chain/jsonrpc-primitives/src/errors.rs +++ b/chain/jsonrpc-primitives/src/errors.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt; use serde::{Deserialize, Serialize}; use serde_json::{to_value, Value}; @@ -65,6 +65,9 @@ impl RpcError { pub fn parse_error(e: String) -> Self { RpcError::new(-32_700, "Parse error".to_owned(), Some(Value::String(e))) } + pub fn serialization_error(e: String) -> Self { + RpcError::new(-32_000, "Server error".to_owned(), Some(Value::String(e))) + } /// Create a method not found error. pub fn method_not_found(method: String) -> Self { RpcError::new(-32_601, "Method not found".to_owned(), Some(Value::String(method))) @@ -89,8 +92,8 @@ impl From for RpcError { } } -impl Display for ServerError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { +impl fmt::Display for ServerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ServerError::TxExecutionError(e) => write!(f, "ServerError: {}", e), ServerError::Timeout => write!(f, "ServerError: Timeout"), diff --git a/chain/jsonrpc-primitives/src/lib.rs b/chain/jsonrpc-primitives/src/lib.rs index 00d46c3cc78..3cfd6d57947 100644 --- a/chain/jsonrpc-primitives/src/lib.rs +++ b/chain/jsonrpc-primitives/src/lib.rs @@ -1,6 +1,5 @@ pub mod errors; pub mod message; pub(crate) mod metrics; -pub mod rpc; pub mod types; pub(crate) mod utils; diff --git a/chain/jsonrpc-primitives/src/rpc.rs b/chain/jsonrpc-primitives/src/rpc.rs deleted file mode 100644 index 3ddd282e3d9..00000000000 --- a/chain/jsonrpc-primitives/src/rpc.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! This module defines RPC types to nearcore public APIs. -//! -//! NOTE: This module should be only used in RPC server and RPC client implementations, and -//! should not leak these types anywhere else. -use serde::{Deserialize, Serialize}; - -use near_primitives::hash::CryptoHash; -use near_primitives::merkle::MerklePath; -use near_primitives::types::{BlockReference, MaybeBlockId, TransactionOrReceiptId}; -use near_primitives::views::{ - ExecutionOutcomeWithIdView, LightClientBlockLiteView, QueryRequest, StateChangeWithCauseView, - StateChangesKindsView, StateChangesRequestView, -}; - -#[derive(Serialize, Deserialize)] -pub struct RpcQueryRequest { - #[serde(flatten)] - pub block_reference: BlockReference, - #[serde(flatten)] - pub request: QueryRequest, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcStateChangesRequest { - #[serde(flatten)] - pub block_reference: BlockReference, - #[serde(flatten)] - pub state_changes_request: StateChangesRequestView, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcStateChangesResponse { - pub block_hash: CryptoHash, - pub changes: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcStateChangesInBlockRequest { - #[serde(flatten)] - pub block_reference: BlockReference, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcStateChangesInBlockResponse { - pub block_hash: CryptoHash, - pub changes: StateChangesKindsView, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcLightClientExecutionProofRequest { - #[serde(flatten)] - pub id: TransactionOrReceiptId, - pub light_client_head: CryptoHash, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcLightClientExecutionProofResponse { - pub outcome_proof: ExecutionOutcomeWithIdView, - pub outcome_root_proof: MerklePath, - pub block_header_lite: LightClientBlockLiteView, - pub block_proof: MerklePath, -} - -#[derive(Serialize, Deserialize)] -pub struct RpcValidatorsOrderedRequest { - pub block_id: MaybeBlockId, -} diff --git a/chain/jsonrpc-primitives/src/types/blocks.rs b/chain/jsonrpc-primitives/src/types/blocks.rs index 78c3f6ea4ea..71da3120601 100644 --- a/chain/jsonrpc-primitives/src/types/blocks.rs +++ b/chain/jsonrpc-primitives/src/types/blocks.rs @@ -25,13 +25,13 @@ pub enum RpcBlockError { Unreachable { error_message: String }, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct RpcBlockRequest { #[serde(flatten)] pub block_reference: BlockReference, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct RpcBlockResponse { #[serde(flatten)] pub block_view: near_primitives::views::BlockView, diff --git a/chain/jsonrpc-primitives/src/types/changes.rs b/chain/jsonrpc-primitives/src/types/changes.rs new file mode 100644 index 00000000000..e9b8078396b --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/changes.rs @@ -0,0 +1,114 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcStateChangesRequest { + #[serde(flatten)] + pub block_reference: crate::types::blocks::BlockReference, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcStateChangesResponse { + pub block_hash: near_primitives::hash::CryptoHash, + pub changes: near_primitives::views::StateChangesView, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcStateChangesInBlockRequest { + #[serde(flatten)] + pub block_reference: crate::types::blocks::BlockReference, + #[serde(flatten)] + pub state_changes_request: near_primitives::views::StateChangesRequestView, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcStateChangesInBlockResponse { + pub block_hash: near_primitives::hash::CryptoHash, + pub changes: near_primitives::views::StateChangesKindsView, +} + +#[derive(thiserror::Error, Debug, Serialize, Deserialize)] +pub enum RpcStateChangesError { + #[error("Block not found: {error_message}")] + UnknownBlock { error_message: String }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl RpcStateChangesRequest { + pub fn parse(value: Option) -> Result { + Ok(crate::utils::parse_params::(value)?) + } +} + +impl RpcStateChangesInBlockRequest { + pub fn parse(value: Option) -> Result { + Ok(crate::utils::parse_params::(value)?) + } +} + +impl From for RpcStateChangesError { + fn 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 { error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); + near_metrics::inc_counter_vec( + &crate::metrics::RPC_UNREACHABLE_ERROR_COUNT, + &["RpcStateChangesError"], + ); + Self::Unreachable { error_message } + } + } + } +} + +impl From for RpcStateChangesError { + fn 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 { error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); + near_metrics::inc_counter_vec( + &crate::metrics::RPC_UNREACHABLE_ERROR_COUNT, + &["RpcStateChangesError"], + ); + Self::Unreachable { error_message } + } + } + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcStateChangesError) -> Self { + let error_data = Some(Value::String(error.to_string())); + + Self::new(-32_000, "Server error".to_string(), error_data) + } +} + +impl From for RpcStateChangesError { + fn from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} diff --git a/chain/jsonrpc-primitives/src/types/light_client.rs b/chain/jsonrpc-primitives/src/types/light_client.rs new file mode 100644 index 00000000000..c979d6f6861 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/light_client.rs @@ -0,0 +1,214 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcLightClientExecutionProofRequest { + #[serde(flatten)] + pub id: near_primitives::types::TransactionOrReceiptId, + pub light_client_head: near_primitives::hash::CryptoHash, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcLightClientNextBlockRequest { + pub last_block_hash: near_primitives::hash::CryptoHash, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcLightClientExecutionProofResponse { + pub outcome_proof: near_primitives::views::ExecutionOutcomeWithIdView, + pub outcome_root_proof: near_primitives::merkle::MerklePath, + pub block_header_lite: near_primitives::views::LightClientBlockLiteView, + pub block_proof: near_primitives::merkle::MerklePath, +} + +#[derive(Debug, Serialize)] +pub struct RpcLightClientNextBlockResponse { + #[serde(flatten)] + pub light_client_block: Option, +} + +#[derive(thiserror::Error, Debug, Serialize)] +pub enum RpcLightClientProofError { + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Inconsistent state. Total number of shards is {number_or_shards} but the execution outcome is in shard {execution_outcome_shard_id}")] + InconsistentState { + number_or_shards: usize, + execution_outcome_shard_id: near_primitives::types::ShardId, + }, + #[error("{transaction_or_receipt_id} has not been confirmed")] + NotConfirmed { transaction_or_receipt_id: near_primitives::hash::CryptoHash }, + #[error("{transaction_or_receipt_id} does not exist")] + UnknownTransactionOrReceipt { transaction_or_receipt_id: near_primitives::hash::CryptoHash }, + #[error("Node doesn't track the shard where {transaction_or_receipt_id} is executed")] + UnavailableShard { + transaction_or_receipt_id: near_primitives::hash::CryptoHash, + shard_id: near_primitives::types::ShardId, + }, + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +#[derive(thiserror::Error, Debug, Serialize)] +pub enum RpcLightClientNextBlockError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: near_primitives::types::EpochId }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl RpcLightClientExecutionProofRequest { + pub fn parse(value: Option) -> Result { + Ok(crate::utils::parse_params::(value)?) + } +} + +impl RpcLightClientNextBlockRequest { + pub fn parse(value: Option) -> Result { + if let Ok((last_block_hash,)) = + crate::utils::parse_params::<(near_primitives::hash::CryptoHash,)>(value.clone()) + { + Ok(Self { last_block_hash }) + } else { + Ok(crate::utils::parse_params::(value)?) + } + } +} + +impl From> + for RpcLightClientNextBlockResponse +{ + fn from(light_client_block: Option) -> Self { + Self { light_client_block } + } +} + +impl From for RpcLightClientProofError { + fn 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 { error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); + near_metrics::inc_counter_vec( + &crate::metrics::RPC_UNREACHABLE_ERROR_COUNT, + &["RpcLightClientProofError"], + ); + Self::Unreachable { error_message } + } + } + } +} + +impl From for RpcLightClientProofError { + fn 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 { error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); + near_metrics::inc_counter_vec( + &crate::metrics::RPC_UNREACHABLE_ERROR_COUNT, + &["RpcLightClientProofError"], + ); + Self::Unreachable { error_message } + } + } + } +} + +impl From + for RpcLightClientNextBlockError +{ + fn 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 { + error_message, + } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); + near_metrics::inc_counter_vec( + &crate::metrics::RPC_UNREACHABLE_ERROR_COUNT, + &["RpcLightClientNextBlockError"], + ); + Self::Unreachable { error_message } + } + } + } +} + +impl From for RpcLightClientProofError { + fn from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl From for RpcLightClientNextBlockError { + fn from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcLightClientProofError) -> Self { + let error_data = match error { + RpcLightClientProofError::UnknownBlock { error_message } => { + Some(Value::String(format!("DB Not Found Error: {}", error_message))) + } + _ => Some(Value::String(error.to_string())), + }; + + Self::new(-32_000, "Server error".to_string(), error_data) + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcLightClientNextBlockError) -> Self { + let error_data = Some(Value::String(error.to_string())); + + Self::new(-32_000, "Server error".to_string(), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/mod.rs b/chain/jsonrpc-primitives/src/types/mod.rs index c9dd15e7320..5be7f646316 100644 --- a/chain/jsonrpc-primitives/src/types/mod.rs +++ b/chain/jsonrpc-primitives/src/types/mod.rs @@ -1,8 +1,12 @@ pub mod blocks; +pub mod changes; pub mod chunks; pub mod config; pub mod gas_price; +pub mod light_client; +pub mod network_info; pub mod query; pub mod receipts; +pub mod status; pub mod transactions; pub mod validator; diff --git a/chain/jsonrpc-primitives/src/types/network_info.rs b/chain/jsonrpc-primitives/src/types/network_info.rs new file mode 100644 index 00000000000..0769b943ed3 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/network_info.rs @@ -0,0 +1,40 @@ +use serde::Serialize; +use serde_json::Value; + +#[derive(Serialize, Debug)] +pub struct RpcNetworkInfoResponse { + #[serde(flatten)] + pub network_info_response: near_client_primitives::types::NetworkInfoResponse, +} + +#[derive(thiserror::Error, Debug)] +pub enum RpcNetworkInfoError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, +} + +impl From for RpcNetworkInfoResponse { + fn from(network_info_response: near_client_primitives::types::NetworkInfoResponse) -> Self { + Self { network_info_response } + } +} + +impl From for RpcNetworkInfoError { + fn from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl From for RpcNetworkInfoError { + fn from(error_message: String) -> Self { + Self::InternalError { error_message } + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcNetworkInfoError) -> Self { + let error_data = Some(Value::String(error.to_string())); + + Self::new(-32_000, "Server error".to_string(), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/status.rs b/chain/jsonrpc-primitives/src/types/status.rs new file mode 100644 index 00000000000..b522908f3a2 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/status.rs @@ -0,0 +1,80 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Debug, Serialize, Deserialize)] +pub struct RpcStatusResponse { + #[serde(flatten)] + pub status_response: near_primitives::views::StatusResponse, +} + +#[derive(Debug, Serialize)] +pub struct RpcHealthResponse; + +#[derive(thiserror::Error, Debug)] +pub enum RpcStatusError { + #[error("Node is syncing")] + NodeIsSyncing, + #[error("No blocks for {elapsed:?}")] + NoNewBlocks { elapsed: std::time::Duration }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: near_primitives::types::EpochId }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for RpcStatusResponse { + fn from(status_response: near_primitives::views::StatusResponse) -> Self { + Self { status_response } + } +} + +impl From for RpcHealthResponse { + fn from(_status_response: near_primitives::views::StatusResponse) -> Self { + Self {} + } +} + +impl From for RpcStatusError { + fn 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 { error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", &error_message); + near_metrics::inc_counter_vec( + &crate::metrics::RPC_UNREACHABLE_ERROR_COUNT, + &["RpcStatusError"], + ); + Self::Unreachable { error_message } + } + } + } +} + +impl From for RpcStatusError { + fn from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcStatusError) -> Self { + let error_data = Some(Value::String(error.to_string())); + + Self::new(-32_000, "Server error".to_string(), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/transactions.rs b/chain/jsonrpc-primitives/src/types/transactions.rs index c728987ba0c..e10b2bf519e 100644 --- a/chain/jsonrpc-primitives/src/types/transactions.rs +++ b/chain/jsonrpc-primitives/src/types/transactions.rs @@ -27,7 +27,7 @@ pub enum RpcTransactionError { #[error("Node doesn't track this shard. Cannot determine whether the transaction is valid")] DoesNotTrackShard, #[error("Transaction with hash {transaction_hash} was routed")] - RequestRouted { transaction_hash: String }, + RequestRouted { transaction_hash: near_primitives::hash::CryptoHash }, #[error("Transaction {requested_transaction_hash} doesn't exist")] UnknownTransaction { requested_transaction_hash: near_primitives::hash::CryptoHash }, #[error("The node reached its limits. Try again later. More details: {debug_info}")] @@ -44,7 +44,7 @@ pub struct RpcTransactionResponse { #[derive(Serialize, Deserialize, Debug)] pub struct RpcBroadcastTxSyncResponse { - pub transaction_hash: String, + pub transaction_hash: near_primitives::hash::CryptoHash, } impl RpcBroadcastTransactionRequest { @@ -105,16 +105,21 @@ impl From for RpcTransact impl From for crate::errors::RpcError { fn from(error: RpcTransactionError) -> Self { - let error_data = match error { - RpcTransactionError::InvalidTransaction { ref context } => Some( - serde_json::to_value(crate::errors::ServerError::TxExecutionError( - near_primitives::errors::TxExecutionError::InvalidTxError(context.clone()), - )) - .unwrap_or(Value::String(error.to_string())), - ), - _ => Some(Value::String(error.to_string())), + let error_data = match &error { + RpcTransactionError::InvalidTransaction { context } => { + if let Ok(value) = + serde_json::to_value(crate::errors::ServerError::TxExecutionError( + near_primitives::errors::TxExecutionError::InvalidTxError(context.clone()), + )) + { + value + } else { + Value::String(error.to_string()) + } + } + _ => Value::String(error.to_string()), }; - Self::new(-32_000, "Server error".to_string(), error_data) + Self::new(-32_000, "Server error".to_string(), Some(error_data)) } } diff --git a/chain/jsonrpc-primitives/src/types/validator.rs b/chain/jsonrpc-primitives/src/types/validator.rs index 3f3a6d5c5f7..725e5d21fd1 100644 --- a/chain/jsonrpc-primitives/src/types/validator.rs +++ b/chain/jsonrpc-primitives/src/types/validator.rs @@ -1,8 +1,9 @@ -use near_primitives::types::EpochReference; -use near_primitives::views::EpochValidatorInfo; use serde::{Deserialize, Serialize}; use serde_json::Value; +pub type RpcValidatorsOrderedResponse = + Vec; + #[derive(thiserror::Error, Debug)] pub enum RpcValidatorError { #[error("Epoch not found")] @@ -19,16 +20,21 @@ pub enum RpcValidatorError { Unreachable(String), } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct RpcValidatorRequest { #[serde(flatten)] - pub epoch_reference: EpochReference, + pub epoch_reference: near_primitives::types::EpochReference, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RpcValidatorsOrderedRequest { + pub block_id: near_primitives::types::MaybeBlockId, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct RpcValidatorResponse { #[serde(flatten)] - pub validator_info: EpochValidatorInfo, + pub validator_info: near_primitives::views::EpochValidatorInfo, } impl From for RpcValidatorError { @@ -62,20 +68,24 @@ impl From for RpcValidatorError { } impl RpcValidatorRequest { - pub fn parse( - value: Option, - ) -> Result { + pub fn parse(value: Option) -> Result { let epoch_reference = if let Ok((block_id,)) = crate::utils::parse_params::<(near_primitives::types::MaybeBlockId,)>(value.clone()) { match block_id { - Some(id) => EpochReference::BlockId(id), - None => EpochReference::Latest, + Some(id) => near_primitives::types::EpochReference::BlockId(id), + None => near_primitives::types::EpochReference::Latest, } } else { - crate::utils::parse_params::(value)? + crate::utils::parse_params::(value)? }; - Ok(RpcValidatorRequest { epoch_reference }) + Ok(Self { epoch_reference }) + } +} + +impl RpcValidatorsOrderedRequest { + pub fn parse(value: Option) -> Result { + Ok(crate::utils::parse_params::(value)?) } } diff --git a/chain/jsonrpc-primitives/src/utils.rs b/chain/jsonrpc-primitives/src/utils.rs index 0bad7ec2451..f19b03e4715 100644 --- a/chain/jsonrpc-primitives/src/utils.rs +++ b/chain/jsonrpc-primitives/src/utils.rs @@ -14,16 +14,12 @@ pub(crate) fn parse_params( } } -fn from_base64_or_parse_err(encoded: String) -> Result, crate::errors::RpcParseError> { - near_primitives_core::serialize::from_base64(&encoded) - .map_err(|err| crate::errors::RpcParseError(err.to_string())) -} - pub(crate) fn parse_signed_transaction( value: Option, ) -> Result { let (encoded,) = crate::utils::parse_params::<(String,)>(value.clone())?; - let bytes = crate::utils::from_base64_or_parse_err(encoded)?; + let bytes = near_primitives_core::serialize::from_base64(&encoded) + .map_err(|err| crate::errors::RpcParseError(err.to_string()))?; Ok(near_primitives::transaction::SignedTransaction::try_from_slice(&bytes).map_err(|err| { crate::errors::RpcParseError(format!("Failed to decode transaction: {}", err)) })?) diff --git a/chain/jsonrpc/client/src/lib.rs b/chain/jsonrpc/client/src/lib.rs index 6ba0d04c0a9..823d929e166 100644 --- a/chain/jsonrpc/client/src/lib.rs +++ b/chain/jsonrpc/client/src/lib.rs @@ -7,9 +7,8 @@ use serde::Serialize; use near_jsonrpc_primitives::errors::RpcError; use near_jsonrpc_primitives::message::{from_slice, Message}; -use near_jsonrpc_primitives::rpc::{ - RpcStateChangesRequest, RpcStateChangesResponse, RpcValidatorsOrderedRequest, -}; +use near_jsonrpc_primitives::types::changes::{RpcStateChangesRequest, RpcStateChangesResponse}; +use near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest; use near_primitives::hash::CryptoHash; use near_primitives::types::{BlockId, BlockReference, MaybeBlockId, ShardId}; use near_primitives::views::validator_stake_view::ValidatorStakeView; diff --git a/chain/jsonrpc/src/lib.rs b/chain/jsonrpc/src/lib.rs index 1e17174ce28..8f5da67b74a 100644 --- a/chain/jsonrpc/src/lib.rs +++ b/chain/jsonrpc/src/lib.rs @@ -1,13 +1,12 @@ use std::string::FromUtf8Error; use std::time::Duration; -use actix::{Addr, MailboxError}; +use actix::Addr; use actix_cors::Cors; use actix_web::{http, middleware, web, App, Error as HttpError, HttpResponse, HttpServer}; use futures::Future; use futures::FutureExt; use prometheus; -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tokio::time::{sleep, timeout}; @@ -23,11 +22,6 @@ use near_client::{ pub use near_jsonrpc_client as client; use near_jsonrpc_primitives::errors::RpcError; use near_jsonrpc_primitives::message::{Message, Request}; -use near_jsonrpc_primitives::rpc::{ - RpcLightClientExecutionProofRequest, RpcLightClientExecutionProofResponse, - RpcStateChangesInBlockRequest, RpcStateChangesInBlockResponse, RpcStateChangesRequest, - RpcStateChangesResponse, RpcValidatorsOrderedRequest, -}; use near_jsonrpc_primitives::types::config::RpcProtocolConfigResponse; use near_metrics::{Encoder, TextEncoder}; #[cfg(feature = "adversarial")] @@ -94,7 +88,8 @@ impl RpcConfig { } } -fn parse_params(value: Option) -> Result { +#[cfg(feature = "adversarial")] +fn parse_params(value: Option) -> Result { if let Some(value) = value { serde_json::from_value(value) .map_err(|err| RpcError::invalid_params(format!("Failed parsing args: {}", err))) @@ -103,8 +98,9 @@ fn parse_params(value: Option) -> Result( - response: Result, MailboxError>, + response: Result, actix::MailboxError>, ) -> Result { response .map_err(|err| err.to_string()) @@ -240,7 +236,8 @@ impl JsonRpcHandler { let rpc_block_request = near_jsonrpc_primitives::types::blocks::RpcBlockRequest::parse(request.params)?; let block = self.block(rpc_block_request).await?; - serde_json::to_value(block).map_err(|err| RpcError::parse_error(err.to_string())) + serde_json::to_value(block) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "broadcast_tx_async" => { let rpc_transaction_request = @@ -249,7 +246,7 @@ impl JsonRpcHandler { )?; let transaction_hash = self.send_tx_async(rpc_transaction_request).await; serde_json::to_value((&transaction_hash).to_base()) - .map_err(|err| RpcError::parse_error(err.to_string())) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "broadcast_tx_commit" => { let rpc_transaction_request = @@ -258,13 +255,76 @@ impl JsonRpcHandler { )?; let send_tx_response = self.send_tx_commit(rpc_transaction_request).await?; serde_json::to_value(send_tx_response) - .map_err(|err| RpcError::parse_error(err.to_string())) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "chunk" => { let rpc_chunk_request = near_jsonrpc_primitives::types::chunks::RpcChunkRequest::parse(request.params)?; let chunk = self.chunk(rpc_chunk_request).await?; - serde_json::to_value(chunk).map_err(|err| RpcError::parse_error(err.to_string())) + serde_json::to_value(chunk) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "gas_price" => { + let rpc_gas_price_request = + near_jsonrpc_primitives::types::gas_price::RpcGasPriceRequest::parse( + request.params, + )?; + let gas_price = self.gas_price(rpc_gas_price_request).await?; + serde_json::to_value(gas_price) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "health" => { + let health_response = self.health().await?; + serde_json::to_value(health_response) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "light_client_proof" => { + let rpc_light_client_execution_proof_request = near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest::parse(request.params)?; + let rpc_light_client_execution_proof_response = self + .light_client_execution_outcome_proof(rpc_light_client_execution_proof_request) + .await?; + serde_json::to_value(rpc_light_client_execution_proof_response) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "next_light_client_block" => { + let rpc_light_client_next_block_request = near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockRequest::parse(request.params)?; + let next_light_client_block = + self.next_light_client_block(rpc_light_client_next_block_request).await?; + serde_json::to_value(next_light_client_block) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "network_info" => { + let network_info_response = self.network_info().await?; + serde_json::to_value(network_info_response) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "query" => { + let rpc_query_request = + near_jsonrpc_primitives::types::query::RpcQueryRequest::parse(request.params)?; + let query_response = self.query(rpc_query_request).await; + process_query_response(query_response) + } + "status" => { + let status_response = self.status().await?; + serde_json::to_value(status_response) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "tx" => { + let rpc_transaction_status_common_request = + near_jsonrpc_primitives::types::transactions::RpcTransactionStatusCommonRequest::parse(request.params)?; + let rpc_transaction_response = + self.tx_status_common(rpc_transaction_status_common_request, false).await?; + serde_json::to_value(rpc_transaction_response) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "validators" => { + let rpc_validator_request = + near_jsonrpc_primitives::types::validator::RpcValidatorRequest::parse( + request.params, + )?; + let validator_info = self.validators(rpc_validator_request).await?; + serde_json::to_value(validator_info) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "EXPERIMENTAL_broadcast_tx_sync" => { let rpc_transaction_request = @@ -273,10 +333,27 @@ impl JsonRpcHandler { )?; let broadcast_tx_sync_response = self.send_tx_sync(rpc_transaction_request).await?; serde_json::to_value(broadcast_tx_sync_response) - .map_err(|err| RpcError::parse_error(err.to_string())) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "EXPERIMENTAL_changes" => { + let rpc_state_changes_request = + near_jsonrpc_primitives::types::changes::RpcStateChangesInBlockRequest::parse( + request.params, + )?; + let state_changes = + self.changes_in_block_by_type(rpc_state_changes_request).await?; + serde_json::to_value(state_changes) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "EXPERIMENTAL_changes_in_block" => { + let rpc_state_changes_request = + near_jsonrpc_primitives::types::changes::RpcStateChangesRequest::parse( + request.params, + )?; + let state_changes = self.changes_in_block(rpc_state_changes_request).await?; + serde_json::to_value(state_changes) + .map_err(|err| RpcError::serialization_error(err.to_string())) } - "EXPERIMENTAL_changes" => self.changes_in_block_by_type(request.params).await, - "EXPERIMENTAL_changes_in_block" => self.changes_in_block(request.params).await, "EXPERIMENTAL_check_tx" => { let rpc_transaction_request = near_jsonrpc_primitives::types::transactions::RpcBroadcastTransactionRequest::parse( @@ -284,11 +361,20 @@ impl JsonRpcHandler { )?; let broadcast_tx_sync_response = self.check_tx(rpc_transaction_request).await?; serde_json::to_value(broadcast_tx_sync_response) - .map_err(|err| RpcError::parse_error(err.to_string())) + .map_err(|err| RpcError::serialization_error(err.to_string())) + } + "EXPERIMENTAL_genesis_config" => { + let genesis_config = self.genesis_config().await; + serde_json::to_value(genesis_config) + .map_err(|err| RpcError::serialization_error(err.to_string())) } - "EXPERIMENTAL_genesis_config" => self.genesis_config().await, "EXPERIMENTAL_light_client_proof" => { - self.light_client_execution_outcome_proof(request.params).await + let rpc_light_client_execution_proof_request = near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest::parse(request.params)?; + let rpc_light_client_execution_proof_response = self + .light_client_execution_outcome_proof(rpc_light_client_execution_proof_request) + .await?; + serde_json::to_value(rpc_light_client_execution_proof_response) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "EXPERIMENTAL_protocol_config" => { let rpc_protocol_config_request = @@ -296,7 +382,8 @@ impl JsonRpcHandler { request.params, )?; let config = self.protocol_config(rpc_protocol_config_request).await?; - serde_json::to_value(config).map_err(|err| RpcError::parse_error(err.to_string())) + serde_json::to_value(config) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "EXPERIMENTAL_receipt" => { let rpc_receipt_request = @@ -304,51 +391,24 @@ impl JsonRpcHandler { request.params, )?; let receipt = self.receipt(rpc_receipt_request).await?; - serde_json::to_value(receipt).map_err(|err| RpcError::parse_error(err.to_string())) + serde_json::to_value(receipt) + .map_err(|err| RpcError::serialization_error(err.to_string())) } "EXPERIMENTAL_tx_status" => { let rpc_transaction_status_common_request = near_jsonrpc_primitives::types::transactions::RpcTransactionStatusCommonRequest::parse(request.params)?; let rpc_transaction_response = self.tx_status_common(rpc_transaction_status_common_request, true).await?; serde_json::to_value(rpc_transaction_response) - .map_err(|err| RpcError::parse_error(err.to_string())) + .map_err(|err| RpcError::serialization_error(err.to_string())) } - "EXPERIMENTAL_validators_ordered" => self.validators_ordered(request.params).await, - "gas_price" => { - let rpc_gas_price_request = - near_jsonrpc_primitives::types::gas_price::RpcGasPriceRequest::parse( + "EXPERIMENTAL_validators_ordered" => { + let rpc_validators_ordered_request = + near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest::parse( request.params, )?; - let gas_price = self.gas_price(rpc_gas_price_request).await?; - serde_json::to_value(gas_price) - .map_err(|err| RpcError::parse_error(err.to_string())) - } - "health" => self.health().await, - "light_client_proof" => self.light_client_execution_outcome_proof(request.params).await, - "next_light_client_block" => self.next_light_client_block(request.params).await, - "network_info" => self.network_info().await, - "query" => { - let rpc_query_request = - near_jsonrpc_primitives::types::query::RpcQueryRequest::parse(request.params)?; - let query_response = self.query(rpc_query_request).await; - process_query_response(query_response) - } - "status" => self.status().await, - "tx" => { - let rpc_transaction_status_common_request = near_jsonrpc_primitives::types::transactions::RpcTransactionStatusCommonRequest::parse(request.params)?; - let rpc_transaction_response = - self.tx_status_common(rpc_transaction_status_common_request, false).await?; - serde_json::to_value(rpc_transaction_response) - .map_err(|err| RpcError::parse_error(err.to_string())) - } - "validators" => { - let rpc_validator_request = - near_jsonrpc_primitives::types::validator::RpcValidatorRequest::parse( - request.params, - )?; - let validator_info = self.validators(rpc_validator_request).await?; - serde_json::to_value(validator_info) - .map_err(|err| RpcError::parse_error(err.to_string())) + let validators = self.validators_ordered(rpc_validators_ordered_request).await?; + serde_json::to_value(validators) + .map_err(|err| RpcError::serialization_error(err.to_string())) } _ => Err(RpcError::method_not_found(request.method.clone())), }; @@ -561,12 +621,12 @@ impl JsonRpcHandler { match self.send_tx(request_data.clone().signed_transaction, false).await? { NetworkClientResponses::ValidTx => { Ok(near_jsonrpc_primitives::types::transactions::RpcBroadcastTxSyncResponse { - transaction_hash: (request_data.signed_transaction.get_hash()).to_string(), + transaction_hash: request_data.signed_transaction.get_hash(), }) } NetworkClientResponses::RequestRouted => { Err(near_jsonrpc_primitives::types::transactions::RpcTransactionError::RequestRouted { - transaction_hash: (request_data.signed_transaction.get_hash()).to_string(), + transaction_hash: request_data.signed_transaction.get_hash(), }) } network_client_responses=> Err( @@ -587,12 +647,12 @@ impl JsonRpcHandler { match self.send_tx(request_data.clone().signed_transaction, true).await? { NetworkClientResponses::ValidTx => { Ok(near_jsonrpc_primitives::types::transactions::RpcBroadcastTxSyncResponse { - transaction_hash: (request_data.signed_transaction.get_hash()).to_string(), + transaction_hash: request_data.signed_transaction.get_hash(), }) } NetworkClientResponses::RequestRouted => { Err(near_jsonrpc_primitives::types::transactions::RpcTransactionError::RequestRouted { - transaction_hash: (request_data.signed_transaction.get_hash()).to_string(), + transaction_hash: request_data.signed_transaction.get_hash(), }) } network_client_responses => Err( @@ -644,28 +704,30 @@ impl JsonRpcHandler { } } - async fn health(&self) -> Result { - match self.client_addr.send(Status { is_health_check: true }).await { - Ok(Ok(_)) => Ok(Value::Null), - Ok(Err(err)) => Err(RpcError::new(-32_001, err, None)), - Err(_) => Err(RpcError::server_error::<()>(None)), - } + async fn health( + &self, + ) -> Result< + near_jsonrpc_primitives::types::status::RpcHealthResponse, + near_jsonrpc_primitives::types::status::RpcStatusError, + > { + Ok(self.client_addr.send(Status { is_health_check: true }).await??.into()) } - pub async fn status(&self) -> Result { - match self.client_addr.send(Status { is_health_check: false }).await { - Ok(Ok(result)) => jsonify(Ok(Ok(result))), - Ok(Err(err)) => Err(RpcError::new(-32_001, err, None)), - Err(_) => Err(RpcError::server_error::<()>(None)), - } + pub async fn status( + &self, + ) -> Result< + near_jsonrpc_primitives::types::status::RpcStatusResponse, + near_jsonrpc_primitives::types::status::RpcStatusError, + > { + Ok(self.client_addr.send(Status { is_health_check: false }).await??.into()) } /// Expose Genesis Config (with internal Runtime Config) without state records to keep the /// output at a reasonable size. /// /// See also `genesis_records` API. - pub async fn genesis_config(&self) -> Result { - jsonify(Ok(Ok(&self.genesis_config))) + pub async fn genesis_config(&self) -> &GenesisConfig { + &self.genesis_config } pub async fn protocol_config( @@ -749,91 +811,100 @@ impl JsonRpcHandler { } } - async fn changes_in_block(&self, params: Option) -> Result { - let RpcStateChangesInBlockRequest { block_reference } = parse_params(params)?; - // TODO refactor it. Changed to keep it working before refactoring - let result = self.view_client_addr.send(GetBlock(block_reference)).await?; - let block = match result { - Ok(block) => block, - Err(err) => { - return Err(RpcError::from( - near_jsonrpc_primitives::types::blocks::RpcBlockError::from(err), - )) - } - }; + async fn changes_in_block( + &self, + request: near_jsonrpc_primitives::types::changes::RpcStateChangesRequest, + ) -> Result< + near_jsonrpc_primitives::types::changes::RpcStateChangesInBlockResponse, + near_jsonrpc_primitives::types::changes::RpcStateChangesError, + > { + let block = self.view_client_addr.send(GetBlock(request.block_reference.into())).await??; let block_hash = block.header.hash.clone(); - jsonify(self.view_client_addr.send(GetStateChangesInBlock { block_hash }).await.map(|v| { - v.map(|changes| RpcStateChangesInBlockResponse { - block_hash: block.header.hash, - changes, - }) - })) + let changes = self.view_client_addr.send(GetStateChangesInBlock { block_hash }).await??; + + Ok(near_jsonrpc_primitives::types::changes::RpcStateChangesInBlockResponse { + block_hash: block.header.hash, + changes, + }) } - async fn changes_in_block_by_type(&self, params: Option) -> Result { - let RpcStateChangesRequest { block_reference, state_changes_request } = - parse_params(params)?; - // TODO refactor it. Changed to keep it working before refactoring - let result = self.view_client_addr.send(GetBlock(block_reference)).await?; - let block = match result { - Ok(block) => block, - Err(err) => { - return Err(RpcError::from( - near_jsonrpc_primitives::types::blocks::RpcBlockError::from(err), - )) - } - }; + async fn changes_in_block_by_type( + &self, + request: near_jsonrpc_primitives::types::changes::RpcStateChangesInBlockRequest, + ) -> Result< + near_jsonrpc_primitives::types::changes::RpcStateChangesResponse, + near_jsonrpc_primitives::types::changes::RpcStateChangesError, + > { + let block = self.view_client_addr.send(GetBlock(request.block_reference.into())).await??; let block_hash = block.header.hash.clone(); - jsonify( - self.view_client_addr - .send(GetStateChanges { block_hash, state_changes_request }) - .await - .map(|v| { - v.map(|changes| RpcStateChangesResponse { - block_hash: block.header.hash, - changes, - }) - }), - ) + let changes = self + .view_client_addr + .send(GetStateChanges { + block_hash, + state_changes_request: request.state_changes_request, + }) + .await??; + + Ok(near_jsonrpc_primitives::types::changes::RpcStateChangesResponse { + block_hash: block.header.hash, + changes, + }) } - async fn next_light_client_block(&self, params: Option) -> Result { - let (last_block_hash,) = parse_params::<(CryptoHash,)>(params)?; - jsonify(self.view_client_addr.send(GetNextLightClientBlock { last_block_hash }).await) + async fn next_light_client_block( + &self, + request: near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockRequest, + ) -> Result< + near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockResponse, + near_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockError, + > { + Ok(self + .view_client_addr + .send(GetNextLightClientBlock { last_block_hash: request.last_block_hash }) + .await?? + .into()) } async fn light_client_execution_outcome_proof( &self, - params: Option, - ) -> Result { - let RpcLightClientExecutionProofRequest { id, light_client_head } = parse_params(params)?; - let execution_outcome_proof = self - .view_client_addr - .send(GetExecutionOutcome { id }) - .await - .map_err(|e| RpcError::from(near_jsonrpc_primitives::errors::ServerError::from(e)))? - .map_err(|e| RpcError::server_error(Some(e)))?; + request: near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest, + ) -> Result< + near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofResponse, + near_jsonrpc_primitives::types::light_client::RpcLightClientProofError, + > { + let near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest { + id, + light_client_head, + } = request; + + let execution_outcome_proof = + self.view_client_addr.send(GetExecutionOutcome { id }).await??; + let block_proof = self .view_client_addr .send(GetBlockProof { block_hash: execution_outcome_proof.outcome_proof.block_hash, head_block_hash: light_client_head, }) - .await - .map_err(|e| RpcError::from(near_jsonrpc_primitives::errors::ServerError::from(e)))?; - let res = block_proof.map(|block_proof| RpcLightClientExecutionProofResponse { + .await??; + + Ok(near_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofResponse { outcome_proof: execution_outcome_proof.outcome_proof, outcome_root_proof: execution_outcome_proof.outcome_root_proof, block_header_lite: block_proof.block_header_lite, block_proof: block_proof.proof, - }); - jsonify(Ok(res)) + }) } - async fn network_info(&self) -> Result { - jsonify(self.client_addr.send(GetNetworkInfo {}).await) + async fn network_info( + &self, + ) -> Result< + near_jsonrpc_primitives::types::network_info::RpcNetworkInfoResponse, + near_jsonrpc_primitives::types::network_info::RpcNetworkInfoError, + > { + Ok(self.client_addr.send(GetNetworkInfo {}).await??.into()) } async fn gas_price( @@ -874,10 +945,16 @@ impl JsonRpcHandler { /// Returns the current epoch validators ordered in the block producer order with repetition. /// This endpoint is solely used for bridge currently and is not intended for other external use /// cases. - async fn validators_ordered(&self, params: Option) -> Result { - let RpcValidatorsOrderedRequest { block_id } = - parse_params::(params)?; - jsonify(self.view_client_addr.send(GetValidatorOrdered { block_id }).await) + async fn validators_ordered( + &self, + request: near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest, + ) -> Result< + near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedResponse, + near_jsonrpc_primitives::types::validator::RpcValidatorError, + > { + let near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest { block_id } = + request; + Ok(self.view_client_addr.send(GetValidatorOrdered { block_id }).await??.into()) } } diff --git a/chain/jsonrpc/tests/rpc_query.rs b/chain/jsonrpc/tests/rpc_query.rs index d922a17f042..694e92da10d 100644 --- a/chain/jsonrpc/tests/rpc_query.rs +++ b/chain/jsonrpc/tests/rpc_query.rs @@ -8,8 +8,8 @@ use near_actix_test_utils::run_actix_until_stop; use near_crypto::{KeyType, PublicKey, Signature}; use near_jsonrpc::client::new_client; use near_jsonrpc_client::ChunkId; -use near_jsonrpc_primitives::rpc::RpcValidatorsOrderedRequest; use near_jsonrpc_primitives::types::query::QueryResponseKind; +use near_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest; use near_logger_utils::init_test_logger; use near_network::test_utils::WaitOrTimeout; use near_primitives::account::{AccessKey, AccessKeyPermission}; diff --git a/chain/rosetta-rpc/src/adapters/mod.rs b/chain/rosetta-rpc/src/adapters/mod.rs index 4af8f551237..ea90704976e 100644 --- a/chain/rosetta-rpc/src/adapters/mod.rs +++ b/chain/rosetta-rpc/src/adapters/mod.rs @@ -139,8 +139,7 @@ pub(crate) async fn convert_block_to_transactions( account_ids: touched_account_ids.into_iter().collect(), }, }) - .await? - .map_err(crate::errors::ErrorKind::InternalError)?; + .await??; let transactions = convert_block_changes_to_transactions( &genesis.config.runtime_config, diff --git a/chain/rosetta-rpc/src/errors.rs b/chain/rosetta-rpc/src/errors.rs index 06e6dfc1a96..1c7d981e0ee 100644 --- a/chain/rosetta-rpc/src/errors.rs +++ b/chain/rosetta-rpc/src/errors.rs @@ -50,3 +50,22 @@ impl std::convert::From for ErrorKind { } } } + +impl std::convert::From for ErrorKind { + fn from(err: near_client_primitives::types::GetStateChangesError) -> Self { + match err { + near_client_primitives::types::GetStateChangesError::IOError { error_message } => { + Self::InternalError(error_message) + } + near_client_primitives::types::GetStateChangesError::NotSyncedYet => { + Self::NotFound(err.to_string()) + } + near_client_primitives::types::GetStateChangesError::UnknownBlock { error_message } => { + Self::NotFound(error_message) + } + near_client_primitives::types::GetStateChangesError::Unreachable { error_message } => { + Self::InternalError(error_message) + } + } + } +} diff --git a/chain/rosetta-rpc/src/lib.rs b/chain/rosetta-rpc/src/lib.rs index d830153daac..d150dc8b79c 100644 --- a/chain/rosetta-rpc/src/lib.rs +++ b/chain/rosetta-rpc/src/lib.rs @@ -39,7 +39,7 @@ async fn network_list( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; Ok(Json(models::NetworkListResponse { network_identifiers: vec![models::NetworkIdentifier { blockchain: "nearprotocol".to_owned(), @@ -64,7 +64,7 @@ async fn network_status( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != body.network_identifier.network { return Err(models::Error { code: 2, @@ -136,7 +136,7 @@ async fn network_options( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != body.network_identifier.network { return Err(models::Error { code: 2, @@ -193,7 +193,7 @@ async fn block_details( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -284,7 +284,7 @@ async fn block_transaction_details( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -346,7 +346,7 @@ async fn account_balance( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -472,7 +472,7 @@ async fn construction_derive( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -504,7 +504,7 @@ async fn construction_preprocess( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -553,7 +553,7 @@ async fn construction_metadata( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -625,7 +625,7 @@ async fn construction_payloads( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -698,7 +698,7 @@ async fn construction_combine( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -732,7 +732,7 @@ async fn construction_parse( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -792,7 +792,7 @@ async fn construction_hash( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, @@ -827,7 +827,7 @@ async fn construction_submit( let status = client_addr .send(near_client::Status { is_health_check: false }) .await? - .map_err(errors::ErrorKind::InternalError)?; + .map_err(|err| errors::ErrorKind::InternalError(err.to_string()))?; if status.chain_id != network_identifier.network { return Err(models::Error { code: 2, diff --git a/core/primitives/src/types.rs b/core/primitives/src/types.rs index be2e93e319a..382abfbb76c 100644 --- a/core/primitives/src/types.rs +++ b/core/primitives/src/types.rs @@ -952,7 +952,7 @@ pub enum ValidatorKickoutReason { DidNotGetASeat, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(tag = "type", rename_all = "snake_case")] pub enum TransactionOrReceiptId { Transaction { transaction_hash: CryptoHash, sender_id: AccountId }, diff --git a/neard/tests/rpc_nodes.rs b/neard/tests/rpc_nodes.rs index c9b8e85c52b..a58f4ec3096 100644 --- a/neard/tests/rpc_nodes.rs +++ b/neard/tests/rpc_nodes.rs @@ -822,7 +822,7 @@ fn test_tx_not_enough_balance_must_return_error() { ); System::current().stop(); }) - .map_ok(move |_| panic!("Transaction must not succeed")) + .map_ok(|_| panic!("Transaction must not succeed")) .await; break; } @@ -833,6 +833,56 @@ fn test_tx_not_enough_balance_must_return_error() { }); } +#[test] +fn test_send_tx_sync_returns_transaction_hash() { + init_integration_logger(); + + let cluster = NodeCluster::new(1, |index| format!("tx_not_enough_balance{}", index)) + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = InMemorySigner::from_seed("near.0", KeyType::ED25519, "near.0"); + let transaction = SignedTransaction::send_money( + 1, + "near.0".to_string(), + "near.0".to_string(), + &signer, + 10000, + genesis_hash, + ); + + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let tx_hash = transaction.get_hash(); + let bytes = transaction.try_to_vec().unwrap(); + + spawn_interruptible(async move { + loop { + let res = view_client.send(GetBlock::latest()).await; + if let Ok(Ok(block)) = res { + if block.header.height > 10 { + let response = client + .EXPERIMENTAL_broadcast_tx_sync(to_base64(&bytes)) + .map_err(|err| panic_on_rpc_error!(err)) + .await + .unwrap(); + assert_eq!(response["transaction_hash"], tx_hash.to_string()); + System::current().stop(); + break; + } + } + sleep(std::time::Duration::from_millis(500)).await; + } + }); + }); +} + #[test] fn test_send_tx_sync_to_lightclient_must_be_routed() { init_integration_logger(); @@ -879,7 +929,7 @@ fn test_send_tx_sync_to_lightclient_must_be_routed() { ); System::current().stop(); }) - .map_ok(move |_| panic!("Transaction must not succeed")) + .map_ok(|_| panic!("Transaction must not succeed")) .await; break; } @@ -936,7 +986,7 @@ fn test_check_unknown_tx_must_return_error() { ); System::current().stop(); }) - .map_ok(move |_| panic!("Transaction must be unknown")) + .map_ok(|_| panic!("Transaction must be unknown")) .await; break; } @@ -989,7 +1039,7 @@ fn test_check_tx_on_lightclient_must_return_does_not_track_shard() { ); System::current().stop(); }) - .map_ok(move |_| panic!("Must not track shard")) + .map_ok(|_| panic!("Must not track shard")) .await; break; }