diff --git a/chain/client-primitives/src/types.rs b/chain/client-primitives/src/types.rs index 9ad598f75..54b12f8f2 100644 --- a/chain/client-primitives/src/types.rs +++ b/chain/client-primitives/src/types.rs @@ -10,12 +10,7 @@ use unc_primitives::types::{ AccountId, BlockHeight, BlockReference, EpochId, EpochReference, MaybeBlockId, ShardId, TransactionOrReceiptId, }; -use unc_primitives::views::{ - BlockView, ChunkView, DownloadStatusView, EpochValidatorInfo, ExecutionOutcomeWithIdView, - GasPriceView, LightClientBlockLiteView, LightClientBlockView, MaintenanceWindowsView, - QueryRequest, QueryResponse, ReceiptView, ShardSyncDownloadView, SplitStorageInfoView, - StateChangesKindsView, StateChangesRequestView, StateChangesView, SyncStatusView, TxStatusView, -}; +use unc_primitives::views::{BlockView, ChunkView, DownloadStatusView, EpochValidatorInfo, ExecutionOutcomeWithIdView, GasPriceView, LightClientBlockLiteView, LightClientBlockView, MaintenanceWindowsView, MinerChipsListView, QueryRequest, QueryResponse, ReceiptView, ShardSyncDownloadView, SplitStorageInfoView, StateChangesKindsView, StateChangesRequestView, StateChangesView, SyncStatusView, TxStatusView}; pub use unc_primitives::views::{StatusResponse, StatusSyncInfo}; use std::collections::HashMap; use std::sync::atomic::{AtomicBool, Ordering}; @@ -369,7 +364,7 @@ impl From for SyncStatusView { } } -/// Actor message requesting block provider by block hash. +/// Actor message requesting block provider by EpochId and BlockHeight. #[derive(Debug)] pub struct GetProvider(pub EpochId, pub BlockHeight); @@ -414,6 +409,51 @@ impl Message for crate::types::GetProvider { type Result = Result; } +/// Actor message requesting all chips owned by AccountId +#[derive(Debug)] +pub struct MinerChipsList(pub AccountId); + +#[derive(thiserror::Error, Debug)] +pub enum MinerChipsListError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Account either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownAccount { error_message: String }, + #[error("Chips info unavailable")] + ChipsInfoUnavailable, + // 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/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for crate::types::MinerChipsListError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::IOError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownAccount { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl From for crate::types::MinerChipsListError { + fn from(error: EpochError) -> Self { + Self::IOError { error_message: error.to_string() } + } +} + + +impl Message for crate::types::MinerChipsList { + type Result = Result; +} + /// Actor message requesting block by id, hash or sync state. #[derive(Debug)] pub struct GetBlock(pub BlockReference); diff --git a/chain/client/src/view_client.rs b/chain/client/src/view_client.rs index 577ca22e5..0ccb3b056 100644 --- a/chain/client/src/view_client.rs +++ b/chain/client/src/view_client.rs @@ -14,7 +14,7 @@ use unc_chain::{ }; use unc_chain_configs::{ClientConfig, ProtocolConfigView}; use unc_chain_primitives::error::EpochErrorResultToChainError; -use unc_client_primitives::types::{Error, GetBlock, GetBlockError, GetBlockProof, GetBlockProofError, GetBlockProofResponse, GetBlockWithMerkleTree, GetChunkError, GetExecutionOutcome, GetExecutionOutcomeError, GetExecutionOutcomesForBlock, GetGasPrice, GetGasPriceError, GetMaintenanceWindows, GetMaintenanceWindowsError, GetNextLightClientBlockError, GetProtocolConfig, GetProtocolConfigError, GetProvider, GetProviderError, GetReceipt, GetReceiptError, GetSplitStorageInfo, GetSplitStorageInfoError, GetStateChangesError, GetStateChangesWithCauseInBlock, GetStateChangesWithCauseInBlockForTrackedShards, GetValidatorInfoError, Query, QueryError, TxStatus, TxStatusError}; +use unc_client_primitives::types::{Error, GetBlock, GetBlockError, GetBlockProof, GetBlockProofError, GetBlockProofResponse, GetBlockWithMerkleTree, GetChunkError, GetExecutionOutcome, GetExecutionOutcomeError, GetExecutionOutcomesForBlock, GetGasPrice, GetGasPriceError, GetMaintenanceWindows, GetMaintenanceWindowsError, GetNextLightClientBlockError, GetProtocolConfig, GetProtocolConfigError, GetProvider, GetProviderError, MinerChipsList, MinerChipsListError, GetReceipt, GetReceiptError, GetSplitStorageInfo, GetSplitStorageInfoError, GetStateChangesError, GetStateChangesWithCauseInBlock, GetStateChangesWithCauseInBlockForTrackedShards, GetValidatorInfoError, Query, QueryError, TxStatus, TxStatusError}; use unc_epoch_manager::shard_tracker::ShardTracker; use unc_epoch_manager::EpochManagerAdapter; use unc_network::types::{ @@ -34,16 +34,8 @@ use unc_primitives::state_sync::{ ShardStateSyncResponse, ShardStateSyncResponseHeader, ShardStateSyncResponseV3, }; use unc_primitives::static_clock::StaticClock; -use unc_primitives::types::{ - AccountId, BlockHeight, BlockId, BlockReference, EpochReference, Finality, MaybeBlockId, - ShardId, SyncCheckpoint, TransactionOrReceiptId, ValidatorInfoIdentifier, -}; -use unc_primitives::views::{ - BlockView, ChunkView, EpochValidatorInfo, ExecutionOutcomeWithIdView, ExecutionStatusView, - FinalExecutionOutcomeView, FinalExecutionOutcomeViewEnum, GasPriceView, LightClientBlockView, - MaintenanceWindowsView, QueryRequest, QueryResponse, ReceiptView, SplitStorageInfoView, - StateChangesKindsView, StateChangesView, TxExecutionStatus, TxStatusView, -}; +use unc_primitives::types::{AccountId, BlockHeight, BlockId, BlockReference, EpochReference, Finality, MaybeBlockId, ShardId, SyncCheckpoint, TransactionOrReceiptId, ValidatorInfoIdentifier}; +use unc_primitives::views::{BlockView, ChipView, ChunkView, EpochValidatorInfo, ExecutionOutcomeWithIdView, ExecutionStatusView, FinalExecutionOutcomeView, FinalExecutionOutcomeViewEnum, GasPriceView, LightClientBlockView, MaintenanceWindowsView, MinerChipsListView, QueryRequest, QueryResponse, ReceiptView, SplitStorageInfoView, StateChangesKindsView, StateChangesView, TxExecutionStatus, TxStatusView}; use unc_store::flat::{FlatStorageReadyStatus, FlatStorageStatus}; use unc_store::{DBCol, COLD_HEAD_KEY, FINAL_HEAD_KEY, HEAD_KEY}; use std::cmp::Ordering; @@ -632,6 +624,32 @@ impl Handler> for ViewClientActor { } } +/// Handles retrieving miner chips list from the chain. +impl Handler> for ViewClientActor { + type Result = Result; + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let account_id = msg.0; + let chip0 = ChipView { + power: 5000000000, + serial_number: String::from("serialnumberabc000"), + bus_id: String::from("busidabc000"), + p2: String::from("p2abc000"), + public_key: String::from("publickeyabc000"), + p2_size: 120, + public_key_size: 235, + }; + let mut chips = Vec::new(); + chips.push(chip0); + Ok(MinerChipsListView{ + account_id, + chips, + }) + } +} + /// Handles retrieving block from the chain. impl Handler> for ViewClientActor { type Result = Result; diff --git a/chain/jsonrpc-primitives/src/types/miner_chips_list.rs b/chain/jsonrpc-primitives/src/types/miner_chips_list.rs new file mode 100644 index 000000000..0e8d9f6d6 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/miner_chips_list.rs @@ -0,0 +1,49 @@ +use serde_json::Value; +use unc_primitives::types::{AccountId}; +use unc_primitives::views::ChipView; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcMinerChipsListError { + #[error("Account not found")] + UnknownAccount { error_message: String }, + #[error("Chips info unavailable")] + ChipsInfoUnavailable, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct RpcMinerChipsListRequest { + pub account_id: AccountId, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcMinerChipsListResponse { + pub account_id: AccountId, + pub chips: Vec, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcMinerChipsListError) -> Self { + let error_data = match &error { + RpcMinerChipsListError::UnknownAccount{ error_message } => Some(Value::String(error_message.to_string())), + RpcMinerChipsListError::ChipsInfoUnavailable => { + Some(Value::String("Chips info unavailable".to_string())) + } + RpcMinerChipsListError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcValidatorError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/mod.rs b/chain/jsonrpc-primitives/src/types/mod.rs index 8d40ff869..cc5543960 100644 --- a/chain/jsonrpc-primitives/src/types/mod.rs +++ b/chain/jsonrpc-primitives/src/types/mod.rs @@ -16,3 +16,4 @@ pub mod status; pub mod transactions; pub mod validator; pub mod provider; +pub mod miner_chips_list; diff --git a/chain/jsonrpc/src/api/miner_chips_list.rs b/chain/jsonrpc/src/api/miner_chips_list.rs new file mode 100644 index 000000000..255926ebc --- /dev/null +++ b/chain/jsonrpc/src/api/miner_chips_list.rs @@ -0,0 +1,58 @@ +use unc_primitives::types::{AccountId}; +use serde_json::Value; + +use unc_client_primitives::types::{MinerChipsListError}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::miner_chips_list::{ + RpcMinerChipsListError, RpcMinerChipsListRequest, +}; + +use super::{RpcFrom, RpcRequest}; + +impl RpcRequest for RpcMinerChipsListRequest { + // fn parse(value: Value) -> Result { + // let block_height = value + // .get("block_height") + // .and_then(|v| v.as_u64()) + // .ok_or_else(|| RpcParseError("block_height not found or not a u64".parse().unwrap()))?; + // Ok(Self { block_height }) + // } + fn parse(value: Value) -> Result { + // Extract block_hash_str from value + let account_id_str = value + .get("account_id") + .and_then(|v| v.as_str()) + .ok_or_else(|| RpcParseError("account_id not found or not a string".parse().unwrap()))?; + + // Construct the CryptoHash from the decoded bytes + let account_id : AccountId = account_id_str + .parse() + .map_err(|_| RpcParseError("Failed to parse epoch_id from base58".parse().unwrap()))?; + + Ok(Self { account_id }) + } + +} + +impl RpcFrom for RpcMinerChipsListError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcMinerChipsListError { + fn rpc_from(error: MinerChipsListError) -> Self { + match error { + MinerChipsListError::UnknownAccount{ error_message } => Self::UnknownAccount { error_message }, + MinerChipsListError::ChipsInfoUnavailable => Self::ChipsInfoUnavailable, + MinerChipsListError::IOError{ error_message } => Self::InternalError { error_message }, + MinerChipsListError::Unreachable{ ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["MinerChipsListError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/mod.rs b/chain/jsonrpc/src/api/mod.rs index fc669ffc3..7d2b3add5 100644 --- a/chain/jsonrpc/src/api/mod.rs +++ b/chain/jsonrpc/src/api/mod.rs @@ -21,6 +21,7 @@ mod transactions; mod validator; mod provider; +mod miner_chips_list; pub(crate) trait RpcRequest: Sized { fn parse(value: Value) -> Result; diff --git a/chain/jsonrpc/src/lib.rs b/chain/jsonrpc/src/lib.rs index 6c71d7a7a..0e633962d 100644 --- a/chain/jsonrpc/src/lib.rs +++ b/chain/jsonrpc/src/lib.rs @@ -17,7 +17,7 @@ use unc_client::{ GetStateChangesInBlock, GetValidatorInfo, GetValidatorOrdered, ProcessTxRequest, ProcessTxResponse, Query, Status, TxStatus, ViewClientActor, }; -use unc_client_primitives::types::{GetProvider, GetSplitStorageInfo}; +use unc_client_primitives::types::{MinerChipsList, GetProvider, GetSplitStorageInfo}; pub use unc_jsonrpc_client as client; use unc_jsonrpc_primitives::errors::RpcError; use unc_jsonrpc_primitives::message::{Message, Request}; @@ -395,6 +395,9 @@ impl JsonRpcHandler { "provider" => { process_method_call(request, |params | self.get_provider(params)).await } + "miner_chips_list" => { + process_method_call(request, |params | self.miner_chips_list(params)).await + } _ => return Err(request), }) } @@ -861,6 +864,18 @@ impl JsonRpcHandler { Ok(unc_jsonrpc_primitives::types::provider::RpcProviderResponse{ provider_account }) } + async fn miner_chips_list( + &self, + request_data: unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListRequest, + ) -> Result< + unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListResponse, + unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListError, + > { + let account_id = request_data.account_id; + let chips = self.view_client_send(MinerChipsList(account_id.clone())).await?; + Ok(unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListResponse{ account_id, chips: chips.chips }) + } + async fn block( &self, request_data: unc_jsonrpc_primitives::types::blocks::RpcBlockRequest, diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index 2e6f84de3..8dcbe5c6f 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -739,6 +739,16 @@ impl From for ChallengeView { } } +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ChipView { + pub power: Power, + pub serial_number: String, + pub bus_id: String, + pub p2: String, + pub public_key: String, + pub p2_size: usize, + pub public_key_size: usize, +} #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct BlockHeaderView { pub height: BlockHeight, @@ -1151,6 +1161,11 @@ impl From for ShardChunkHeader { } } +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct MinerChipsListView { + pub account_id: AccountId, + pub chips: Vec, +} #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct BlockView { pub author: AccountId,