From 7a0347592c978f0c9ce1be1560ba75a5e1836c3f Mon Sep 17 00:00:00 2001 From: Mykhailo Slyvka Date: Wed, 22 Jan 2025 21:54:47 +0200 Subject: [PATCH 1/3] add get_tx_count/get_code/get_storage_at/send_raw_transaction/get_balance calls --- crates/console/src/base.rs | 7 +- crates/modules/src/xcb.rs | 131 ++++++++++++++++++++++++++++++++++- crates/rpc/src/go_core.rs | 80 ++++++++++++++++++++- crates/rpc/src/lib.rs | 15 ++++ crates/rpc/src/mock.rs | 21 ++++++ crates/types/Cargo.toml | 3 +- crates/types/src/response.rs | 4 ++ 7 files changed, 257 insertions(+), 4 deletions(-) diff --git a/crates/console/src/base.rs b/crates/console/src/base.rs index 5f52316..2378893 100644 --- a/crates/console/src/base.rs +++ b/crates/console/src/base.rs @@ -19,10 +19,15 @@ fn list() { println!("'xcb' - XCB module commands:"); println!(" 'get_block_height()' - get the current block height"); - println!(" 'get_block(||'latest')' - get block information by hash or number. Use 'latest' to get the latest block"); println!(" 'get_energy_price()' - get the current energy price to allow a timely execution of a transaction"); println!(" 'get_network_id()' - get the nework ID of the current network"); println!(" 'syncing()' - get the syncing status of the node"); + println!(" 'get_block(||'latest')' - get block information by hash or number. Use 'latest' to get the latest block"); + println!(" 'get_balance(
, | 'latest')' - get the balance of an account at a specific block. Use 'latest' to get the latest balance"); + println!(" 'get_tx_count(
, | 'latest')' - get the transaction count of an account at a specific block. Use 'latest' to get the latest transaction count"); + println!(" 'get_code(
, | 'latest')' - get the code of an account at a specific block. Use 'latest' to get the latest code"); + println!(" 'get_storage_at(
, , | 'latest')' - get the storage at a specific key of an account at a specific block. Use 'latest' to get the latest storage"); + println!(" 'send_raw_transaction()' - send a raw transaction to the network"); println!("'xcbkey' - XCB Key module commands:"); println!(" 'list()' - list all accounts"); diff --git a/crates/modules/src/xcb.rs b/crates/modules/src/xcb.rs index a79a4e6..5b43b5d 100644 --- a/crates/modules/src/xcb.rs +++ b/crates/modules/src/xcb.rs @@ -87,6 +87,127 @@ impl XcbModule { } } + async fn get_balance(&self, args: Vec) -> Result { + if args.len() != 2 { + return Err(CliError::InvalidNumberOfArguments("1".to_string())); + } + let address = &args[0]; + let block = if args[1] == "latest" { + None + } else { + Some(args[1].parse::().map_err(|_| { + CliError::InvalidArgument(args[1].clone(), "number or `latest`".to_string()) + })?) + }; + let balance = self + .client() + .await + .lock() + .await + .get_balance(address.to_string(), block) + .await; + match balance { + Ok(balance) => Ok(Response::U256(balance)), + Err(e) => Err(e), + } + } + + async fn get_tx_count(&self, args: Vec) -> Result { + if args.len() != 2 { + return Err(CliError::InvalidNumberOfArguments("1".to_string())); + } + let address = &args[0]; + let block = if args[1] == "latest" { + None + } else { + Some(args[1].parse::().map_err(|_| { + CliError::InvalidArgument(args[1].clone(), "number or `latest`".to_string()) + })?) + }; + let tx_count = self + .client() + .await + .lock() + .await + .get_tx_count(address.to_string(), block) + .await; + match tx_count { + Ok(tx_count) => Ok(Response::U64(tx_count)), + Err(e) => Err(e), + } + } + + async fn get_code(&self, args: Vec) -> Result { + if args.len() != 2 { + return Err(CliError::InvalidNumberOfArguments("1".to_string())); + } + let address = &args[0]; + let block = if args[1] == "latest" { + None + } else { + Some(args[1].parse::().map_err(|_| { + CliError::InvalidArgument(args[1].clone(), "number or `latest`".to_string()) + })?) + }; + let code = self + .client() + .await + .lock() + .await + .get_code(address.to_string(), block) + .await; + match code { + Ok(code) => Ok(Response::String(code)), + Err(e) => Err(e), + } + } + + async fn send_raw_transaction(&self, args: Vec) -> Result { + if args.len() != 1 { + return Err(CliError::InvalidNumberOfArguments("1".to_string())); + } + let tx = &args[0]; + let tx_hash = self + .client() + .await + .lock() + .await + .send_raw_transaction(tx.to_string()) + .await; + match tx_hash { + Ok(tx_hash) => Ok(Response::String(tx_hash)), + Err(e) => Err(e), + } + } + + async fn get_storage_at(&self, args: Vec) -> Result { + if args.len() != 3 { + return Err(CliError::InvalidNumberOfArguments("1".to_string())); + } + let address = &args[0]; + let key = args[1].parse::().map_err(|_| { + CliError::InvalidArgument(args[1].clone(), "hex string".to_string()) + })?; + let block = if args[2] == "latest" { + None + } else { + Some(args[2].parse::().map_err(|_| { + CliError::InvalidArgument(args[2].clone(), "number or `latest`".to_string()) + })?) + }; + let storage = self + .client() + .await + .lock() + .await + .get_storage_at(address.to_string(), key, block) + .await; + match storage { + Ok(storage) => Ok(Response::String(storage)), + Err(e) => Err(e), + } + } + async fn syncing(&self) -> Result { let syncing = self.client().await.lock().await.syncing().await; match syncing { @@ -101,9 +222,17 @@ impl Module for XcbModule { async fn execute(&mut self, command: String, args: Vec) -> Result { match command.as_str() { "get_block_height" => self.block_height().await, - "get_block" => self.block(args).await, "get_energy_price" => self.get_energy_price().await, "get_network_id" => self.get_network_id().await, + + "get_block" => self.block(args).await, + "get_balance" => self.get_balance(args).await, + "get_tx_count" => self.get_tx_count(args).await, + "get_code" => self.get_code(args).await, + "get_storage_at" => self.get_storage_at(args).await, + + "send_raw_transaction" => self.send_raw_transaction(args).await, + "syncing" => self.syncing().await, _ => Err(CliError::UnknownCommand), } diff --git a/crates/rpc/src/go_core.rs b/crates/rpc/src/go_core.rs index 2836157..07318f9 100644 --- a/crates/rpc/src/go_core.rs +++ b/crates/rpc/src/go_core.rs @@ -4,7 +4,7 @@ use atoms_provider::{network::Ethereum, Provider, RootProvider}; use atoms_rpc_client::RpcClient as AtomsRpcClient; use atoms_rpc_types::{Block, BlockId, BlockNumberOrTag, RpcBlockHash, SyncStatus}; use atoms_transport_http::{Client, Http}; -use base_primitives::{hex::FromHex, FixedBytes}; +use base_primitives::{hex::FromHex, FixedBytes, IcanAddress, U256}; use cli_error::CliError; pub struct GoCoreClient { @@ -99,4 +99,82 @@ impl RpcClient for GoCoreClient { .map_err(|e| CliError::RpcError(e.to_string()))?; Ok(response) } + + async fn get_balance(&self, account: String, block: Option) -> Result { + let id = if let Some(number) = block { + BlockId::Number(BlockNumberOrTag::Number(number)) + } else { + BlockId::Number(BlockNumberOrTag::Latest) + }; + let hex = IcanAddress::from_hex(account) + .map_err(|e| CliError::InvalidHexArgument(e.to_string()))?; + let response = self + .provider + .get_balance(hex, id) + .await + .map_err(|e| CliError::RpcError(e.to_string()))?; + Ok(response) + } + + async fn get_tx_count(&self, account: String, block: Option) -> Result { + let id = if let Some(number) = block { + BlockId::Number(BlockNumberOrTag::Number(number)) + } else { + BlockId::Number(BlockNumberOrTag::Latest) + }; + let hex = IcanAddress::from_hex(account) + .map_err(|e| CliError::InvalidHexArgument(e.to_string()))?; + let response = self + .provider + .get_transaction_count(hex, id) + .await + .map_err(|e| CliError::RpcError(e.to_string()))?; + Ok(response) + } + + async fn get_code(&self, account: String, block: Option) -> Result { + let id = if let Some(number) = block { + BlockId::Number(BlockNumberOrTag::Number(number)) + } else { + BlockId::Number(BlockNumberOrTag::Latest) + }; + let hex = IcanAddress::from_hex(account) + .map_err(|e| CliError::InvalidHexArgument(e.to_string()))?; + let response = self + .provider + .get_code_at(hex, id) + .await + .map_err(|e| CliError::RpcError(e.to_string()))?; + Ok(response.to_string()) + } + + async fn send_raw_transaction(&self, tx: String) -> Result { + let response = self + .provider + .send_raw_transaction(tx.as_bytes()) + .await + .map_err(|e| CliError::RpcError(e.to_string()))?; + Ok(response.tx_hash().to_string()) + } + + async fn get_storage_at( + &self, + address: String, + key: u128, + block: Option, + ) -> Result { + let id = if let Some(number) = block { + BlockId::Number(BlockNumberOrTag::Number(number)) + } else { + BlockId::Number(BlockNumberOrTag::Latest) + }; + let hex = IcanAddress::from_hex(address) + .map_err(|e| CliError::InvalidHexArgument(e.to_string()))?; + let response = self + .provider + .get_storage_at(hex, U256::from(key), id) + .await + .map_err(|e| CliError::RpcError(e.to_string()))?; + Ok(response.to_string()) + } } diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 1987f2f..b6e2475 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use atoms_rpc_types::{Block, SyncStatus}; +use base_primitives::U256; use cli_error::CliError; pub mod go_core; @@ -7,6 +8,7 @@ pub use go_core::GoCoreClient; pub mod mock; pub use mock::MockRpcClient; +use types::account; #[async_trait] pub trait RpcClient { @@ -19,4 +21,17 @@ pub trait RpcClient { async fn get_network_id(&self) -> Result; async fn syncing(&self) -> Result; + + async fn get_balance(&self, account: String, block: Option) -> Result; + async fn get_tx_count(&self, account: String, block: Option) -> Result; + async fn get_code(&self, account: String, block: Option) -> Result; + + async fn get_storage_at( + &self, + account: String, + key: u128, + block: Option, + ) -> Result; + + async fn send_raw_transaction(&self, tx: String) -> Result; } diff --git a/crates/rpc/src/mock.rs b/crates/rpc/src/mock.rs index 3f466c8..da796e0 100644 --- a/crates/rpc/src/mock.rs +++ b/crates/rpc/src/mock.rs @@ -1,6 +1,7 @@ use crate::{CliError, RpcClient}; use async_trait::async_trait; use atoms_rpc_types::Block; +use base_primitives::U256; pub struct MockRpcClient { pub block_height: u64, @@ -96,4 +97,24 @@ impl RpcClient for MockRpcClient { async fn syncing(&self) -> Result { Ok(self.syncing) } + + async fn get_balance(&self, _account: String, _block: Option) -> Result { + Ok(U256::from(0)) + } + + async fn get_tx_count(&self, _account: String, _block: Option) -> Result { + Ok(0) + } + + async fn get_code(&self, _account: String, _block: Option) -> Result { + Ok("".to_string()) + } + + async fn send_raw_transaction(&self, _tx: String) -> Result { + Ok("".to_string()) + } + + async fn get_storage_at(&self, _account: String, _key: u128, _block: Option) -> Result { + Ok("".to_string()) + } } diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index f0edbe1..8746aee 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -17,4 +17,5 @@ chrono.workspace = true hex.workspace = true atoms-rpc-types.workspace = true -atoms-signer-wallet.workspace = true \ No newline at end of file +atoms-signer-wallet.workspace = true +base-primitives.workspace = true \ No newline at end of file diff --git a/crates/types/src/response.rs b/crates/types/src/response.rs index 284e746..ddb55b8 100644 --- a/crates/types/src/response.rs +++ b/crates/types/src/response.rs @@ -3,6 +3,7 @@ use serde::Serialize; use crate::{account::KeyFile, Account}; use atoms_rpc_types::SyncStatus; +use base_primitives::U256; use std::str::FromStr; /// ResponseView decided if response of call will be returned as a string, json object or human readable format @@ -34,6 +35,7 @@ impl FromStr for ResponseView { pub enum Response { U64(u64), U128(u128), + U256(U256), Bool(bool), String(String), @@ -51,6 +53,7 @@ impl std::fmt::Display for Response { match self { Response::U64(val) => write!(f, "{}", val), Response::U128(val) => write!(f, "{}", val), + Response::U256(val) => write!(f, "{}", val), Response::Bool(val) => write!(f, "{}", val), Response::String(val) => write!(f, "{}", val), Response::Block(val) => write!( @@ -101,6 +104,7 @@ impl Response { match self { Response::U64(val) => format!("u64 value: {:#?}", val), Response::U128(val) => format!("U128 value: {:#?}", val), + Response::U256(val) => format!("U256 value: {:#?}", val), Response::Bool(val) => format!("Boolean value: {:#?}", val), Response::String(val) => format!("String value: {:#?}", val), Response::Block(val) => format!("{:#?}", val), From 25d7fe679d2f7743237d68fda77b91763703578e Mon Sep 17 00:00:00 2001 From: Mykhailo Slyvka Date: Wed, 22 Jan 2025 21:56:06 +0200 Subject: [PATCH 2/3] update version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 779c6b7..14316f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ keywords = ["core blockchain", "xcb", "cli"] license = "MIT OR Apache-2.0" homepage = "https://github.com/core-coin/core-cli" repository = "https://github.com/core-coin/core-cli" -version = "0.0.4" +version = "0.0.5" [workspace.dependencies] # Workspace members From f7a9090b52dff5f3608075489f3cdf88afe0d1ae Mon Sep 17 00:00:00 2001 From: Mykhailo Slyvka Date: Wed, 22 Jan 2025 21:57:59 +0200 Subject: [PATCH 3/3] fix clippy --- crates/rpc/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index b6e2475..e80c2b6 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -8,7 +8,6 @@ pub use go_core::GoCoreClient; pub mod mock; pub use mock::MockRpcClient; -use types::account; #[async_trait] pub trait RpcClient {