diff --git a/client/src/client.rs b/client/src/client.rs index 353cc1c..6068987 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -340,6 +340,20 @@ impl Client { self.node.read().await.get_nonce(address, block).await } + pub async fn get_block_transaction_count_by_hash(&self, hash: &Vec) -> Result { + self.node + .read() + .await + .get_block_transaction_count_by_hash(hash) + } + + pub async fn get_block_transaction_count_by_number(&self, block: BlockTag) -> Result { + self.node + .read() + .await + .get_block_transaction_count_by_number(block) + } + pub async fn get_code(&self, address: &Address, block: BlockTag) -> Result> { self.node.read().await.get_code(address, block).await } diff --git a/client/src/node.rs b/client/src/node.rs index 02d02af..7db7416 100644 --- a/client/src/node.rs +++ b/client/src/node.rs @@ -155,6 +155,20 @@ impl Node { Ok(account.nonce) } + pub fn get_block_transaction_count_by_hash(&self, hash: &Vec) -> Result { + let payload = self.get_payload_by_hash(hash)?; + let transaction_count = payload.1.transactions.len(); + + Ok(transaction_count as u64) + } + + pub fn get_block_transaction_count_by_number(&self, block: BlockTag) -> Result { + let payload = self.get_payload(block)?; + let transaction_count = payload.transactions.len(); + + Ok(transaction_count as u64) + } + pub async fn get_code(&self, address: &Address, block: BlockTag) -> Result> { self.check_blocktag_age(&block)?; @@ -248,19 +262,11 @@ impl Node { hash: &Vec, full_tx: bool, ) -> Result> { - let payloads = self - .payloads - .iter() - .filter(|entry| &entry.1.block_hash.to_vec() == hash) - .collect::>(); + let payload = self.get_payload_by_hash(hash); - if let Some(payload_entry) = payloads.get(0) { - self.execution - .get_block(payload_entry.1, full_tx) - .await - .map(Some) - } else { - Ok(None) + match payload { + Ok(payload) => self.execution.get_block(payload.1, full_tx).await.map(Some), + Err(_) => Ok(None), } } @@ -296,6 +302,19 @@ impl Node { } } + fn get_payload_by_hash(&self, hash: &Vec) -> Result<(&u64, &ExecutionPayload)> { + let payloads = self + .payloads + .iter() + .filter(|entry| &entry.1.block_hash.to_vec() == hash) + .collect::>(); + + payloads + .get(0) + .cloned() + .ok_or(eyre!("Block not found by hash")) + } + fn check_head_age(&self) -> Result<(), NodeError> { let synced_slot = self.consensus.get_header().slot; let expected_slot = self.consensus.expected_current_slot(); diff --git a/client/src/rpc.rs b/client/src/rpc.rs index 3f72c7b..34e9295 100644 --- a/client/src/rpc.rs +++ b/client/src/rpc.rs @@ -57,6 +57,11 @@ trait EthRpc { async fn get_balance(&self, address: &str, block: BlockTag) -> Result; #[method(name = "getTransactionCount")] async fn get_transaction_count(&self, address: &str, block: BlockTag) -> Result; + #[method(name = "getBlockTransactionCountByHash")] + async fn get_block_transaction_count_by_hash(&self, hash: &str) -> Result; + #[method(name = "getBlockTransactionCountByNumber")] + async fn get_block_transaction_count_by_number(&self, block: BlockTag) + -> Result; #[method(name = "getCode")] async fn get_code(&self, address: &str, block: BlockTag) -> Result; #[method(name = "call")] @@ -133,6 +138,23 @@ impl EthRpcServer for RpcInner { Ok(format!("0x{nonce:x}")) } + async fn get_block_transaction_count_by_hash(&self, hash: &str) -> Result { + let hash = convert_err(hex_str_to_bytes(hash))?; + let node = self.node.read().await; + let transaction_count = convert_err(node.get_block_transaction_count_by_hash(&hash))?; + + Ok(u64_to_hex_string(transaction_count)) + } + + async fn get_block_transaction_count_by_number( + &self, + block: BlockTag, + ) -> Result { + let node = self.node.read().await; + let transaction_count = convert_err(node.get_block_transaction_count_by_number(block))?; + Ok(u64_to_hex_string(transaction_count)) + } + async fn get_code(&self, address: &str, block: BlockTag) -> Result { let address = convert_err(Address::from_str(address))?; let node = self.node.read().await; diff --git a/examples/readme.rs b/examples/readme.rs index 24e4ddc..1c4d5ce 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -9,7 +9,7 @@ use helios::{client::ClientBuilder, config::networks::Network, types::BlockTag}; async fn main() -> Result<()> { env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); - let untrusted_rpc_url = "https://mainnet.infura.io/v3/"; + let untrusted_rpc_url = "https://eth-mainnet.g.alchemy.com/v2/"; log::info!("Using untrusted RPC URL [REDACTED]"); let consensus_rpc = "https://www.lightclientdata.org"; diff --git a/rpc.md b/rpc.md index 987a21e..07c5097 100644 --- a/rpc.md +++ b/rpc.md @@ -7,7 +7,7 @@ Helios provides a variety of RPC methods for interacting with the Ethereum netwo | RPC Method | Client Function | Description | Example | | ---------- | --------------- | ----------- | ------- | | `eth_getBalance` | `get_balance` | Returns the balance of the account given an address. | `client.get_balance(&self, address: &str, block: BlockTag)` | -| `eth_getTransactionCount` | `get_transaction_count` | Returns the number of transactions sent from the given address. | `client.get_transaction_count(&self, address: &str, block: BlockTag)` | +| `eth_getTransactionCount` | `get_nonce` | Returns the number of transactions sent from the given address. | `client.get_nonce(&self, address: &str, block: BlockTag)` | | `eth_getCode` | `get_code` | Returns the code at a given address. | `client.get_code(&self, address: &str, block: BlockTag)` | | `eth_call` | `call` | Executes a new message call immediately without creating a transaction on the blockchain. | `client.call(&self, opts: CallOpts, block: BlockTag)` | | `eth_estimateGas` | `estimate_gas` | Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. | `client.estimate_gas(&self, opts: CallOpts)` | @@ -20,4 +20,6 @@ Helios provides a variety of RPC methods for interacting with the Ethereum netwo | `eth_sendRawTransaction` | `send_raw_transaction` | Submits a raw transaction to the network. | `client.send_raw_transaction(&self, bytes: &str)` | | `eth_getTransactionReceipt` | `get_transaction_receipt` | Returns the receipt of a transaction by transaction hash. | `client.get_transaction_receipt(&self, hash: &str)` | | `eth_getLogs` | `get_logs` | Returns an array of logs matching the filter. | `client.get_logs(&self, filter: Filter)` | -| `eth_getStorageAt` | `get_storage_at` | Returns the value from a storage position at a given address. | `client.get_storage_at(&self, address: &str, slot: H256, block: BlockTag)` | \ No newline at end of file +| `eth_getStorageAt` | `get_storage_at` | Returns the value from a storage position at a given address. | `client.get_storage_at(&self, address: &str, slot: H256, block: BlockTag)` | +| `eth_getBlockTransactionCountByHash` | `get_block_transaction_count_by_hash` | Returns the number of transactions in a block from a block matching the transaction hash. | `client.get_block_transaction_count_by_hash(&self, hash: &str)` | +| `eth_getBlockTransactionCountByNumber` | `get_block_transaction_count_by_number` | Returns the number of transactions in a block from a block matching the block number. | `client.get_block_transaction_count_by_number(&self, block: BlockTag)` | \ No newline at end of file