From 7b6fd6ee160299514fee30b315a2a53fbfb434d7 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 22 Feb 2024 17:37:32 -0300 Subject: [PATCH 1/8] eth: add chain id to types --- Cargo.lock | 47 ++++--------- src/eth.rs | 160 +++++++++++++++++++++++++++++++++++--------- src/kernel_types.rs | 11 +++ 3 files changed, 153 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a3dfb4..60c0706 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1757,15 +1757,6 @@ version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" -[[package]] -name = "wasm-encoder" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" -dependencies = [ - "leb128", -] - [[package]] name = "wasm-encoder" version = "0.41.0" @@ -1787,18 +1778,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.41.0", - "wasmparser 0.121.0", -] - -[[package]] -name = "wasmparser" -version = "0.118.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" -dependencies = [ - "indexmap", - "semver 1.0.21", + "wasm-encoder", + "wasmparser", ] [[package]] @@ -1899,8 +1880,8 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.16.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +version = "0.17.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=21a46c7#21a46c774532da99384f7a1877c1fcfb7a4c72d3" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -1908,8 +1889,8 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.16.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +version = "0.17.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=21a46c7#21a46c774532da99384f7a1877c1fcfb7a4c72d3" dependencies = [ "anyhow", "wit-component", @@ -1918,8 +1899,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.16.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +version = "0.17.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=21a46c7#21a46c774532da99384f7a1877c1fcfb7a4c72d3" dependencies = [ "anyhow", "heck", @@ -1930,8 +1911,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.16.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322" +version = "0.17.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=21a46c7#21a46c774532da99384f7a1877c1fcfb7a4c72d3" dependencies = [ "anyhow", "proc-macro2", @@ -1944,9 +1925,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" +checksum = "331de496d439010797c17637d8002712b9b69110f1669164c09dfa226ad277bb" dependencies = [ "anyhow", "bitflags", @@ -1955,9 +1936,9 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.38.1", + "wasm-encoder", "wasm-metadata", - "wasmparser 0.118.1", + "wasmparser", "wit-parser", ] diff --git a/src/eth.rs b/src/eth.rs index 7fb89db..2c0c6cb 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -8,7 +8,9 @@ pub use alloy_rpc_types::{ }; use serde::{Deserialize, Serialize}; -/// The Action and Request type that can be made to eth:distro:sys. +/// The Action and Request type that can be made to eth:distro:sys. Any process with messaging +/// capabilities can send this action to the eth provider. +/// /// Will be serialized and deserialized using `serde_json::to_vec` and `serde_json::from_slice`. #[derive(Debug, Serialize, Deserialize)] pub enum EthAction { @@ -16,6 +18,7 @@ pub enum EthAction { /// Logs come in as alloy_rpc_types::pubsub::SubscriptionResults SubscribeLogs { sub_id: u64, + chain_id: u64, kind: SubscriptionKind, params: Params, }, @@ -23,22 +26,25 @@ pub enum EthAction { UnsubscribeLogs(u64), /// Raw request. Used by kinode_process_lib. Request { + chain_id: u64, method: String, params: serde_json::Value, }, } /// Incoming Result type for subscription updates or errors that processes will receive. +/// Can deserialize all incoming requests from eth:distro:sys to this type. +/// +/// Will be serialized and deserialized using `serde_json::to_vec` and `serde_json::from_slice`. pub type EthSubResult = Result; -/// Incoming Request type for subscription updates. +/// Incoming Request type for successful subscription updates. #[derive(Debug, Serialize, Deserialize)] pub struct EthSub { pub id: u64, pub result: SubscriptionResult, } -/// Incoming Request for subscription errors that processes will receive. /// If your subscription is closed unexpectedly, you will receive this. #[derive(Debug, Serialize, Deserialize)] pub struct EthSubError { @@ -58,6 +64,8 @@ pub enum EthResponse { #[derive(Debug, Serialize, Deserialize)] pub enum EthError { + /// No RPC provider for the chain + NoRpcForChain, /// Underlying transport error TransportError(String), /// Subscription closed @@ -67,11 +75,68 @@ pub enum EthError { /// Invalid method InvalidMethod(String), /// Permission denied - PermissionDenied(String), + PermissionDenied, /// Internal RPC error RpcError(String), } +/// The action type used for configuring eth:distro:sys. Only processes which have the "root" +/// capability from eth:distro:sys can successfully send this action. +/// +/// NOTE: changes to config will not be persisted between boots, they must be saved in .env +/// to be reflected between boots. TODO: can change this +#[derive(Debug, Serialize, Deserialize)] +pub enum EthConfigAction { + /// Add a new provider to the list of providers. + AddProvider(ProviderConfig), + /// Remove a provider from the list of providers. + /// The tuple is (chain_id, node_id/rpc_url). + RemoveProvider((u64, String)), + /// make our provider public + SetPublic, + /// make our provider not-public + SetPrivate, + /// add node to whitelist on a provider + AllowNode(String), + /// remove node from whitelist on a provider + UnallowNode(String), + /// add node to blacklist on a provider + DenyNode(String), + /// remove node from blacklist on a provider + UndenyNode(String), + /// Set the list of providers to a new list. + /// Replaces all existing saved provider configs. + SetProviders(SavedConfigs), + /// Get the list of as a [`SavedConfigs`] object. + GetProviders, +} + +/// Response type from an [`EthConfigAction`] request. +#[derive(Debug, Serialize, Deserialize)] +pub enum EthConfigResponse { + Ok, + /// Response from a GetProviders request. + Providers(SavedConfigs), + /// Permission denied due to missing capability + PermissionDenied, +} + +pub type SavedConfigs = Vec; + +/// Provider config. Can currently be a node or a ws provider instance. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ProviderConfig { + pub chain_id: u64, + pub trusted: bool, + pub provider: NodeOrRpcUrl, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum NodeOrRpcUrl { + Node(crate::kernel_types::KnsUpdate), + RpcUrl(String), +} + /// Sends a request based on the specified `EthAction` and parses the response. /// /// This function constructs a request targeting the Ethereum distribution system, serializes the provided `EthAction`, @@ -104,8 +169,9 @@ pub fn send_request_and_parse_response( /// /// # Returns /// An `anyhow::Result` representing the current block number. -pub fn get_block_number() -> anyhow::Result { +pub fn get_block_number(chain_id: u64) -> anyhow::Result { let action = EthAction::Request { + chain_id, method: "eth_blockNumber".to_string(), params: ().into(), }; @@ -122,12 +188,13 @@ pub fn get_block_number() -> anyhow::Result { /// /// # Returns /// An `anyhow::Result` representing the balance of the address. -pub fn get_balance(address: Address, tag: Option) -> anyhow::Result { +pub fn get_balance(chain_id: u64, address: Address, tag: Option) -> anyhow::Result { let params = serde_json::to_value(( address, tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), ))?; let action = EthAction::Request { + chain_id, method: "eth_getBalance".to_string(), params, }; @@ -142,8 +209,9 @@ pub fn get_balance(address: Address, tag: Option) -> anyhow::Result>` containing the logs that match the filter. -pub fn get_logs(filter: &Filter) -> anyhow::Result> { +pub fn get_logs(chain_id: u64, filter: &Filter) -> anyhow::Result> { let action = EthAction::Request { + chain_id, method: "eth_getLogs".to_string(), params: serde_json::to_value((filter,))?, }; @@ -155,8 +223,9 @@ pub fn get_logs(filter: &Filter) -> anyhow::Result> { /// /// # Returns /// An `anyhow::Result` representing the current gas price. -pub fn get_gas_price() -> anyhow::Result { +pub fn get_gas_price(chain_id: u64) -> anyhow::Result { let action = EthAction::Request { + chain_id, method: "eth_gasPrice".to_string(), params: ().into(), }; @@ -164,19 +233,6 @@ pub fn get_gas_price() -> anyhow::Result { send_request_and_parse_response::(action) } -/// Retrieves the chain ID. -/// -/// # Returns -/// An `anyhow::Result` representing the chain ID. -pub fn get_chain_id() -> anyhow::Result { - let action = EthAction::Request { - method: "eth_chainId".to_string(), - params: ().into(), - }; - - send_request_and_parse_response::(action) -} - /// Retrieves the number of transactions sent from the given address. /// /// # Parameters @@ -185,9 +241,14 @@ pub fn get_chain_id() -> anyhow::Result { /// /// # Returns /// An `anyhow::Result` representing the number of transactions sent from the address. -pub fn get_transaction_count(address: Address, tag: Option) -> anyhow::Result { +pub fn get_transaction_count( + chain_id: u64, + address: Address, + tag: Option, +) -> anyhow::Result { let params = serde_json::to_value((address, tag.unwrap_or_default()))?; let action = EthAction::Request { + chain_id, method: "eth_getTransactionCount".to_string(), params, }; @@ -203,9 +264,14 @@ pub fn get_transaction_count(address: Address, tag: Option) -> anyhow:: /// /// # Returns /// An `anyhow::Result>` representing the block, if found. -pub fn get_block_by_hash(hash: BlockHash, full_tx: bool) -> anyhow::Result> { +pub fn get_block_by_hash( + chain_id: u64, + hash: BlockHash, + full_tx: bool, +) -> anyhow::Result> { let params = serde_json::to_value((hash, full_tx))?; let action = EthAction::Request { + chain_id, method: "eth_getBlockByHash".to_string(), params, }; @@ -221,11 +287,13 @@ pub fn get_block_by_hash(hash: BlockHash, full_tx: bool) -> anyhow::Result>` representing the block, if found. pub fn get_block_by_number( + chain_id: u64, number: BlockNumberOrTag, full_tx: bool, ) -> anyhow::Result> { let params = serde_json::to_value((number, full_tx))?; let action = EthAction::Request { + chain_id, method: "eth_getBlockByNumber".to_string(), params, }; @@ -242,9 +310,15 @@ pub fn get_block_by_number( /// /// # Returns /// An `anyhow::Result` representing the data stored at the given address and key. -pub fn get_storage_at(address: Address, key: U256, tag: Option) -> anyhow::Result { +pub fn get_storage_at( + chain_id: u64, + address: Address, + key: U256, + tag: Option, +) -> anyhow::Result { let params = serde_json::to_value((address, key, tag.unwrap_or_default()))?; let action = EthAction::Request { + chain_id, method: "eth_getStorageAt".to_string(), params, }; @@ -260,9 +334,10 @@ pub fn get_storage_at(address: Address, key: U256, tag: Option) -> anyh /// /// # Returns /// An `anyhow::Result` representing the code stored at the given address. -pub fn get_code_at(address: Address, tag: BlockId) -> anyhow::Result { +pub fn get_code_at(chain_id: u64, address: Address, tag: BlockId) -> anyhow::Result { let params = serde_json::to_value((address, tag))?; let action = EthAction::Request { + chain_id, method: "eth_getCode".to_string(), params, }; @@ -277,9 +352,10 @@ pub fn get_code_at(address: Address, tag: BlockId) -> anyhow::Result { /// /// # Returns /// An `anyhow::Result>` representing the transaction, if found. -pub fn get_transaction_by_hash(hash: TxHash) -> anyhow::Result> { +pub fn get_transaction_by_hash(chain_id: u64, hash: TxHash) -> anyhow::Result> { let params = serde_json::to_value((hash,))?; let action = EthAction::Request { + chain_id, method: "eth_getTransactionByHash".to_string(), params, }; @@ -294,9 +370,13 @@ pub fn get_transaction_by_hash(hash: TxHash) -> anyhow::Result>` representing the transaction receipt, if found. -pub fn get_transaction_receipt(hash: TxHash) -> anyhow::Result> { +pub fn get_transaction_receipt( + chain_id: u64, + hash: TxHash, +) -> anyhow::Result> { let params = serde_json::to_value((hash,))?; let action = EthAction::Request { + chain_id, method: "eth_getTransactionReceipt".to_string(), params, }; @@ -312,9 +392,14 @@ pub fn get_transaction_receipt(hash: TxHash) -> anyhow::Result` representing the estimated gas amount. -pub fn estimate_gas(tx: TransactionRequest, block: Option) -> anyhow::Result { +pub fn estimate_gas( + chain_id: u64, + tx: TransactionRequest, + block: Option, +) -> anyhow::Result { let params = serde_json::to_value((tx, block.unwrap_or_default()))?; let action = EthAction::Request { + chain_id, method: "eth_estimateGas".to_string(), params, }; @@ -327,8 +412,9 @@ pub fn estimate_gas(tx: TransactionRequest, block: Option) -> anyhow::R /// # Returns /// An `anyhow::Result>` representing the list of accounts. /// Note: This function may return an empty list depending on the node's configuration and capabilities. -pub fn get_accounts() -> anyhow::Result> { +pub fn get_accounts(chain_id: u64) -> anyhow::Result> { let action = EthAction::Request { + chain_id, method: "eth_accounts".to_string(), params: serde_json::Value::Array(vec![]), }; @@ -346,12 +432,14 @@ pub fn get_accounts() -> anyhow::Result> { /// # Returns /// An `anyhow::Result` representing the fee history for the specified range. pub fn get_fee_history( + chain_id: u64, block_count: U256, last_block: BlockNumberOrTag, reward_percentiles: Vec, ) -> anyhow::Result { let params = serde_json::to_value((block_count, last_block, reward_percentiles))?; let action = EthAction::Request { + chain_id, method: "eth_feeHistory".to_string(), params, }; @@ -367,9 +455,14 @@ pub fn get_fee_history( /// /// # Returns /// An `anyhow::Result` representing the result of the call. -pub fn call(tx: TransactionRequest, block: Option) -> anyhow::Result { +pub fn call( + chain_id: u64, + tx: TransactionRequest, + block: Option, +) -> anyhow::Result { let params = serde_json::to_value((tx, block.unwrap_or_default()))?; let action = EthAction::Request { + chain_id, method: "eth_call".to_string(), params, }; @@ -384,8 +477,9 @@ pub fn call(tx: TransactionRequest, block: Option) -> anyhow::Result` representing the hash of the transaction once it has been sent. -pub fn send_raw_transaction(tx: Bytes) -> anyhow::Result { +pub fn send_raw_transaction(chain_id: u64, tx: Bytes) -> anyhow::Result { let action = EthAction::Request { + chain_id, method: "eth_sendRawTransaction".to_string(), params: serde_json::to_value((tx,))?, }; @@ -401,9 +495,10 @@ pub fn send_raw_transaction(tx: Bytes) -> anyhow::Result { /// /// # Returns /// An `anyhow::Result<()>` indicating the operation was dispatched. -pub fn subscribe(sub_id: u64, filter: Filter) -> anyhow::Result<()> { +pub fn subscribe(sub_id: u64, chain_id: u64, filter: Filter) -> anyhow::Result<()> { let action = EthAction::SubscribeLogs { sub_id, + chain_id, kind: SubscriptionKind::Logs, params: Params::Logs(Box::new(filter)), }; @@ -413,6 +508,7 @@ pub fn subscribe(sub_id: u64, filter: Filter) -> anyhow::Result<()> { .body(serde_json::to_vec(&action)?) .send() } + /// Unsubscribes from a previously created subscription. /// /// # Parameters diff --git a/src/kernel_types.rs b/src/kernel_types.rs index e79aef6..258e9f0 100644 --- a/src/kernel_types.rs +++ b/src/kernel_types.rs @@ -432,3 +432,14 @@ pub enum MessageType { Request, Response, } + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KnsUpdate { + pub name: String, // actual username / domain name + pub owner: String, + pub node: String, // hex namehash of node + pub public_key: String, + pub ip: String, + pub port: u16, + pub routers: Vec, +} From 71580670f9648f8137ab85a1bd5ccd99a7d06b2a Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 22 Feb 2024 18:55:36 -0300 Subject: [PATCH 2/8] eth: refactor into Provider struct --- src/eth.rs | 767 ++++++++++++++++++++++++++++------------------------- 1 file changed, 401 insertions(+), 366 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 2c0c6cb..4fea29e 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -137,390 +137,425 @@ pub enum NodeOrRpcUrl { RpcUrl(String), } -/// Sends a request based on the specified `EthAction` and parses the response. -/// -/// This function constructs a request targeting the Ethereum distribution system, serializes the provided `EthAction`, -/// and sends it. It awaits a response with a specified timeout, then attempts to parse the response into the expected -/// type `T`. This method is generic and can be used for various Ethereum actions by specifying the appropriate `EthAction` -/// and return type `T`. -/// Note the timeout of 5s. -pub fn send_request_and_parse_response( - action: EthAction, -) -> anyhow::Result { - let resp = KiRequest::new() - .target(("our", "eth", "distro", "sys")) - .body(serde_json::to_vec(&action)?) - .send_and_await_response(10)??; - - match resp { - Message::Response { body, .. } => { - let response = serde_json::from_slice::(&body)?; - match response { - EthResponse::Response { value } => serde_json::from_value::(value) - .map_err(|e| anyhow::anyhow!("failed to parse response: {}", e)), - _ => Err(anyhow::anyhow!("unexpected response: {:?}", response)), +/// An EVM chain provider. Create this object to start making RPC calls. +/// Set the chain_id to determine which chain to call: requests will fail +/// unless the node this process is running on has access to a provider +/// for that chain. +pub struct Provider { + chain_id: u64, + request_timeout: u64, +} + +impl Provider { + /// Sends a request based on the specified `EthAction` and parses the response. + /// + /// This function constructs a request targeting the Ethereum distribution system, serializes the provided `EthAction`, + /// and sends it. It awaits a response with a specified timeout, then attempts to parse the response into the expected + /// type `T`. This method is generic and can be used for various Ethereum actions by specifying the appropriate `EthAction` + /// and return type `T`. + pub fn send_request_and_parse_response( + &self, + action: EthAction, + ) -> anyhow::Result { + let resp = KiRequest::new() + .target(("our", "eth", "distro", "sys")) + .body(serde_json::to_vec(&action)?) + .send_and_await_response(self.request_timeout)??; + + match resp { + Message::Response { body, .. } => { + let response = serde_json::from_slice::(&body)?; + match response { + EthResponse::Response { value } => serde_json::from_value::(value) + .map_err(|e| anyhow::anyhow!("failed to parse response: {}", e)), + _ => Err(anyhow::anyhow!("unexpected response: {:?}", response)), + } } + _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), } - _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), } -} -/// Retrieves the current block number. -/// -/// # Returns -/// An `anyhow::Result` representing the current block number. -pub fn get_block_number(chain_id: u64) -> anyhow::Result { - let action = EthAction::Request { - chain_id, - method: "eth_blockNumber".to_string(), - params: ().into(), - }; - - let res = send_request_and_parse_response::(action)?; - Ok(res.to::()) -} + /// Retrieves the current block number. + /// + /// # Returns + /// An `anyhow::Result` representing the current block number. + pub fn get_block_number(&self) -> anyhow::Result { + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_blockNumber".to_string(), + params: ().into(), + }; + + let res = self.send_request_and_parse_response::(action)?; + Ok(res.to::()) + } -/// Retrieves the balance of the given address at the specified block. -/// -/// # Parameters -/// - `address`: The address to query the balance for. -/// - `tag`: Optional block ID to specify the block at which the balance is queried. -/// -/// # Returns -/// An `anyhow::Result` representing the balance of the address. -pub fn get_balance(chain_id: u64, address: Address, tag: Option) -> anyhow::Result { - let params = serde_json::to_value(( - address, - tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - ))?; - let action = EthAction::Request { - chain_id, - method: "eth_getBalance".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Retrieves the balance of the given address at the specified block. + /// + /// # Parameters + /// - `address`: The address to query the balance for. + /// - `tag`: Optional block ID to specify the block at which the balance is queried. + /// + /// # Returns + /// An `anyhow::Result` representing the balance of the address. + pub fn get_balance(&self, address: Address, tag: Option) -> anyhow::Result { + let params = serde_json::to_value(( + address, + tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), + ))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getBalance".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Retrieves logs based on a filter. -/// -/// # Parameters -/// - `filter`: The filter criteria for the logs. -/// -/// # Returns -/// An `anyhow::Result>` containing the logs that match the filter. -pub fn get_logs(chain_id: u64, filter: &Filter) -> anyhow::Result> { - let action = EthAction::Request { - chain_id, - method: "eth_getLogs".to_string(), - params: serde_json::to_value((filter,))?, - }; - - send_request_and_parse_response::>(action) -} + /// Retrieves logs based on a filter. + /// + /// # Parameters + /// - `filter`: The filter criteria for the logs. + /// + /// # Returns + /// An `anyhow::Result>` containing the logs that match the filter. + pub fn get_logs(&self, filter: &Filter) -> anyhow::Result> { + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getLogs".to_string(), + params: serde_json::to_value(filter)?, + }; + + self.send_request_and_parse_response::>(action) + } -/// Retrieves the current gas price. -/// -/// # Returns -/// An `anyhow::Result` representing the current gas price. -pub fn get_gas_price(chain_id: u64) -> anyhow::Result { - let action = EthAction::Request { - chain_id, - method: "eth_gasPrice".to_string(), - params: ().into(), - }; - - send_request_and_parse_response::(action) -} + /// Retrieves the current gas price. + /// + /// # Returns + /// An `anyhow::Result` representing the current gas price. + pub fn get_gas_price(&self) -> anyhow::Result { + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_gasPrice".to_string(), + params: ().into(), + }; + + self.send_request_and_parse_response::(action) + } -/// Retrieves the number of transactions sent from the given address. -/// -/// # Parameters -/// - `address`: The address to query the transaction count for. -/// - `tag`: Optional block ID to specify the block at which the count is queried. -/// -/// # Returns -/// An `anyhow::Result` representing the number of transactions sent from the address. -pub fn get_transaction_count( - chain_id: u64, - address: Address, - tag: Option, -) -> anyhow::Result { - let params = serde_json::to_value((address, tag.unwrap_or_default()))?; - let action = EthAction::Request { - chain_id, - method: "eth_getTransactionCount".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Retrieves the number of transactions sent from the given address. + /// + /// # Parameters + /// - `address`: The address to query the transaction count for. + /// - `tag`: Optional block ID to specify the block at which the count is queried. + /// + /// # Returns + /// An `anyhow::Result` representing the number of transactions sent from the address. + pub fn get_transaction_count( + &self, + address: Address, + tag: Option, + ) -> anyhow::Result { + let params = serde_json::to_value((address, tag.unwrap_or_default()))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getTransactionCount".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Retrieves a block by its hash. -/// -/// # Parameters -/// - `hash`: The hash of the block to retrieve. -/// - `full_tx`: Whether to return full transaction objects or just their hashes. -/// -/// # Returns -/// An `anyhow::Result>` representing the block, if found. -pub fn get_block_by_hash( - chain_id: u64, - hash: BlockHash, - full_tx: bool, -) -> anyhow::Result> { - let params = serde_json::to_value((hash, full_tx))?; - let action = EthAction::Request { - chain_id, - method: "eth_getBlockByHash".to_string(), - params, - }; - - send_request_and_parse_response::>(action) -} -/// Retrieves a block by its number or tag. -/// -/// # Parameters -/// - `number`: The number or tag of the block to retrieve. -/// - `full_tx`: Whether to return full transaction objects or just their hashes. -/// -/// # Returns -/// An `anyhow::Result>` representing the block, if found. -pub fn get_block_by_number( - chain_id: u64, - number: BlockNumberOrTag, - full_tx: bool, -) -> anyhow::Result> { - let params = serde_json::to_value((number, full_tx))?; - let action = EthAction::Request { - chain_id, - method: "eth_getBlockByNumber".to_string(), - params, - }; - - send_request_and_parse_response::>(action) -} + /// Retrieves a block by its hash. + /// + /// # Parameters + /// - `hash`: The hash of the block to retrieve. + /// - `full_tx`: Whether to return full transaction objects or just their hashes. + /// + /// # Returns + /// An `anyhow::Result>` representing the block, if found. + pub fn get_block_by_hash( + &self, + hash: BlockHash, + full_tx: bool, + ) -> anyhow::Result> { + let params = serde_json::to_value((hash, full_tx))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getBlockByHash".to_string(), + params, + }; + + self.send_request_and_parse_response::>(action) + } + /// Retrieves a block by its number or tag. + /// + /// # Parameters + /// - `number`: The number or tag of the block to retrieve. + /// - `full_tx`: Whether to return full transaction objects or just their hashes. + /// + /// # Returns + /// An `anyhow::Result>` representing the block, if found. + pub fn get_block_by_number( + &self, + number: BlockNumberOrTag, + full_tx: bool, + ) -> anyhow::Result> { + let params = serde_json::to_value((number, full_tx))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getBlockByNumber".to_string(), + params, + }; + + self.send_request_and_parse_response::>(action) + } -/// Retrieves the storage at a given address and key. -/// -/// # Parameters -/// - `address`: The address of the storage to query. -/// - `key`: The key of the storage slot to retrieve. -/// - `tag`: Optional block ID to specify the block at which the storage is queried. -/// -/// # Returns -/// An `anyhow::Result` representing the data stored at the given address and key. -pub fn get_storage_at( - chain_id: u64, - address: Address, - key: U256, - tag: Option, -) -> anyhow::Result { - let params = serde_json::to_value((address, key, tag.unwrap_or_default()))?; - let action = EthAction::Request { - chain_id, - method: "eth_getStorageAt".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Retrieves the storage at a given address and key. + /// + /// # Parameters + /// - `address`: The address of the storage to query. + /// - `key`: The key of the storage slot to retrieve. + /// - `tag`: Optional block ID to specify the block at which the storage is queried. + /// + /// # Returns + /// An `anyhow::Result` representing the data stored at the given address and key. + pub fn get_storage_at( + &self, + address: Address, + key: U256, + tag: Option, + ) -> anyhow::Result { + let params = serde_json::to_value((address, key, tag.unwrap_or_default()))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getStorageAt".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Retrieves the code at a given address. -/// -/// # Parameters -/// - `address`: The address of the code to query. -/// - `tag`: The block ID to specify the block at which the code is queried. -/// -/// # Returns -/// An `anyhow::Result` representing the code stored at the given address. -pub fn get_code_at(chain_id: u64, address: Address, tag: BlockId) -> anyhow::Result { - let params = serde_json::to_value((address, tag))?; - let action = EthAction::Request { - chain_id, - method: "eth_getCode".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Retrieves the code at a given address. + /// + /// # Parameters + /// - `address`: The address of the code to query. + /// - `tag`: The block ID to specify the block at which the code is queried. + /// + /// # Returns + /// An `anyhow::Result` representing the code stored at the given address. + pub fn get_code_at(&self, address: Address, tag: BlockId) -> anyhow::Result { + let params = serde_json::to_value((address, tag))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getCode".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Retrieves a transaction by its hash. -/// -/// # Parameters -/// - `hash`: The hash of the transaction to retrieve. -/// -/// # Returns -/// An `anyhow::Result>` representing the transaction, if found. -pub fn get_transaction_by_hash(chain_id: u64, hash: TxHash) -> anyhow::Result> { - let params = serde_json::to_value((hash,))?; - let action = EthAction::Request { - chain_id, - method: "eth_getTransactionByHash".to_string(), - params, - }; - - send_request_and_parse_response::>(action) -} + /// Retrieves a transaction by its hash. + /// + /// # Parameters + /// - `hash`: The hash of the transaction to retrieve. + /// + /// # Returns + /// An `anyhow::Result>` representing the transaction, if found. + pub fn get_transaction_by_hash(&self, hash: TxHash) -> anyhow::Result> { + let params = serde_json::to_value(hash)?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getTransactionByHash".to_string(), + params, + }; + + self.send_request_and_parse_response::>(action) + } -/// Retrieves the receipt of a transaction by its hash. -/// -/// # Parameters -/// - `hash`: The hash of the transaction for which the receipt is requested. -/// -/// # Returns -/// An `anyhow::Result>` representing the transaction receipt, if found. -pub fn get_transaction_receipt( - chain_id: u64, - hash: TxHash, -) -> anyhow::Result> { - let params = serde_json::to_value((hash,))?; - let action = EthAction::Request { - chain_id, - method: "eth_getTransactionReceipt".to_string(), - params, - }; - - send_request_and_parse_response::>(action) -} + /// Retrieves the receipt of a transaction by its hash. + /// + /// # Parameters + /// - `hash`: The hash of the transaction for which the receipt is requested. + /// + /// # Returns + /// An `anyhow::Result>` representing the transaction receipt, if found. + pub fn get_transaction_receipt( + &self, + hash: TxHash, + ) -> anyhow::Result> { + let params = serde_json::to_value(hash)?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_getTransactionReceipt".to_string(), + params, + }; + + self.send_request_and_parse_response::>(action) + } -/// Estimates the amount of gas that a transaction will consume. -/// -/// # Parameters -/// - `tx`: The transaction request object containing the details of the transaction to estimate gas for. -/// - `block`: Optional block ID to specify the block at which the gas estimate should be made. -/// -/// # Returns -/// An `anyhow::Result` representing the estimated gas amount. -pub fn estimate_gas( - chain_id: u64, - tx: TransactionRequest, - block: Option, -) -> anyhow::Result { - let params = serde_json::to_value((tx, block.unwrap_or_default()))?; - let action = EthAction::Request { - chain_id, - method: "eth_estimateGas".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Estimates the amount of gas that a transaction will consume. + /// + /// # Parameters + /// - `tx`: The transaction request object containing the details of the transaction to estimate gas for. + /// - `block`: Optional block ID to specify the block at which the gas estimate should be made. + /// + /// # Returns + /// An `anyhow::Result` representing the estimated gas amount. + pub fn estimate_gas( + &self, + tx: TransactionRequest, + block: Option, + ) -> anyhow::Result { + let params = serde_json::to_value((tx, block.unwrap_or_default()))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_estimateGas".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Retrieves the list of accounts controlled by the node. -/// -/// # Returns -/// An `anyhow::Result>` representing the list of accounts. -/// Note: This function may return an empty list depending on the node's configuration and capabilities. -pub fn get_accounts(chain_id: u64) -> anyhow::Result> { - let action = EthAction::Request { - chain_id, - method: "eth_accounts".to_string(), - params: serde_json::Value::Array(vec![]), - }; - - send_request_and_parse_response::>(action) -} + /// Retrieves the list of accounts controlled by the node. + /// + /// # Returns + /// An `anyhow::Result>` representing the list of accounts. + /// Note: This function may return an empty list depending on the node's configuration and capabilities. + pub fn get_accounts(&self) -> anyhow::Result> { + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_accounts".to_string(), + params: serde_json::Value::Array(vec![]), + }; + + self.send_request_and_parse_response::>(action) + } -/// Retrieves the fee history for a given range of blocks. -/// -/// # Parameters -/// - `block_count`: The number of blocks to include in the history. -/// - `last_block`: The ending block number or tag for the history range. -/// - `reward_percentiles`: A list of percentiles to report fee rewards for. -/// -/// # Returns -/// An `anyhow::Result` representing the fee history for the specified range. -pub fn get_fee_history( - chain_id: u64, - block_count: U256, - last_block: BlockNumberOrTag, - reward_percentiles: Vec, -) -> anyhow::Result { - let params = serde_json::to_value((block_count, last_block, reward_percentiles))?; - let action = EthAction::Request { - chain_id, - method: "eth_feeHistory".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Retrieves the fee history for a given range of blocks. + /// + /// # Parameters + /// - `block_count`: The number of blocks to include in the history. + /// - `last_block`: The ending block number or tag for the history range. + /// - `reward_percentiles`: A list of percentiles to report fee rewards for. + /// + /// # Returns + /// An `anyhow::Result` representing the fee history for the specified range. + pub fn get_fee_history( + &self, + block_count: U256, + last_block: BlockNumberOrTag, + reward_percentiles: Vec, + ) -> anyhow::Result { + let params = serde_json::to_value((block_count, last_block, reward_percentiles))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_feeHistory".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Executes a call transaction, which is directly executed in the VM of the node, but never mined into the blockchain. -/// -/// # Parameters -/// - `tx`: The transaction request object containing the details of the call. -/// - `block`: Optional block ID to specify the block at which the call should be made. -/// -/// # Returns -/// An `anyhow::Result` representing the result of the call. -pub fn call( - chain_id: u64, - tx: TransactionRequest, - block: Option, -) -> anyhow::Result { - let params = serde_json::to_value((tx, block.unwrap_or_default()))?; - let action = EthAction::Request { - chain_id, - method: "eth_call".to_string(), - params, - }; - - send_request_and_parse_response::(action) -} + /// Executes a call transaction, which is directly executed in the VM of the node, but never mined into the blockchain. + /// + /// # Parameters + /// - `tx`: The transaction request object containing the details of the call. + /// - `block`: Optional block ID to specify the block at which the call should be made. + /// + /// # Returns + /// An `anyhow::Result` representing the result of the call. + pub fn call(&self, tx: TransactionRequest, block: Option) -> anyhow::Result { + let params = serde_json::to_value((tx, block.unwrap_or_default()))?; + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_call".to_string(), + params, + }; + + self.send_request_and_parse_response::(action) + } -/// Sends a raw transaction to the network. -/// -/// # Parameters -/// - `tx`: The raw transaction data. -/// -/// # Returns -/// An `anyhow::Result` representing the hash of the transaction once it has been sent. -pub fn send_raw_transaction(chain_id: u64, tx: Bytes) -> anyhow::Result { - let action = EthAction::Request { - chain_id, - method: "eth_sendRawTransaction".to_string(), - params: serde_json::to_value((tx,))?, - }; - - send_request_and_parse_response::(action) -} + /// Sends a raw transaction to the network. + /// + /// # Parameters + /// - `tx`: The raw transaction data. + /// + /// # Returns + /// An `anyhow::Result` representing the hash of the transaction once it has been sent. + pub fn send_raw_transaction(&self, tx: Bytes) -> anyhow::Result { + let action = EthAction::Request { + chain_id: self.chain_id, + method: "eth_sendRawTransaction".to_string(), + params: serde_json::to_value(tx)?, + }; + + self.send_request_and_parse_response::(action) + } -/// Subscribes to logs without waiting for a confirmation. -/// -/// # Parameters -/// - `sub_id`: The subscription ID to be used for unsubscribing. -/// - `filter`: The filter criteria for the logs. -/// -/// # Returns -/// An `anyhow::Result<()>` indicating the operation was dispatched. -pub fn subscribe(sub_id: u64, chain_id: u64, filter: Filter) -> anyhow::Result<()> { - let action = EthAction::SubscribeLogs { - sub_id, - chain_id, - kind: SubscriptionKind::Logs, - params: Params::Logs(Box::new(filter)), - }; - - KiRequest::new() - .target(("our", "eth", "distro", "sys")) - .body(serde_json::to_vec(&action)?) - .send() -} + /// Subscribes to logs without waiting for a confirmation. + /// + /// # Parameters + /// - `sub_id`: The subscription ID to be used for unsubscribing. + /// - `filter`: The filter criteria for the logs. + /// + /// # Returns + /// An `anyhow::Result<()>` indicating whether the subscription was created. + pub fn subscribe(&self, sub_id: u64, filter: Filter) -> anyhow::Result<()> { + let action = EthAction::SubscribeLogs { + sub_id, + chain_id: self.chain_id, + kind: SubscriptionKind::Logs, + params: Params::Logs(Box::new(filter)), + }; + + let resp = KiRequest::new() + .target(("our", "eth", "distro", "sys")) + .body(serde_json::to_vec(&action)?) + .send_and_await_response(self.request_timeout)??; + + match resp { + Message::Response { body, .. } => { + let response = serde_json::from_slice::(&body)?; + match response { + EthResponse::Ok => Ok(()), + EthResponse::Response { .. } => { + Err(anyhow::anyhow!("unexpected response: {:?}", response)) + } + EthResponse::Err(e) => Err(anyhow::anyhow!("{e:?}")), + } + } + _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), + } + } -/// Unsubscribes from a previously created subscription. -/// -/// # Parameters -/// - `sub_id`: The subscription ID to unsubscribe from. -/// -/// # Returns -/// An `anyhow::Result<()>` indicating the success or failure of the unsubscription request. -pub fn unsubscribe(sub_id: u64) -> anyhow::Result<()> { - let action = EthAction::UnsubscribeLogs(sub_id); - - KiRequest::new() - .target(("our", "eth", "distro", "sys")) - .body(serde_json::to_vec(&action)?) - .send() + /// Unsubscribes from a previously created subscription. + /// + /// # Parameters + /// - `sub_id`: The subscription ID to unsubscribe from. + /// + /// # Returns + /// An `anyhow::Result<()>` indicating whether the subscription was cancelled. + pub fn unsubscribe(&self, sub_id: u64) -> anyhow::Result<()> { + let action = EthAction::UnsubscribeLogs(sub_id); + + let resp = KiRequest::new() + .target(("our", "eth", "distro", "sys")) + .body(serde_json::to_vec(&action)?) + .send_and_await_response(self.request_timeout)??; + + match resp { + Message::Response { body, .. } => { + let response = serde_json::from_slice::(&body)?; + match response { + EthResponse::Ok => Ok(()), + EthResponse::Response { .. } => { + Err(anyhow::anyhow!("unexpected response: {:?}", response)) + } + EthResponse::Err(e) => Err(anyhow::anyhow!("{e:?}")), + } + } + _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), + } + } } From 8e4d015404c3803981b05d2e64e3e100c5b782fc Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 22 Feb 2024 19:03:45 -0300 Subject: [PATCH 3/8] add provider::new() --- src/eth.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/eth.rs b/src/eth.rs index 4fea29e..fa5f4f7 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -147,6 +147,13 @@ pub struct Provider { } impl Provider { + /// Instantiate a new provider. + pub fn new(chain_id: u64, request_timeout: u64) -> Self { + Self { + chain_id, + request_timeout, + } + } /// Sends a request based on the specified `EthAction` and parses the response. /// /// This function constructs a request targeting the Ethereum distribution system, serializes the provided `EthAction`, From 567ba7830ba387625e668c61f252f98ff849d6eb Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Fri, 23 Feb 2024 20:25:36 -0300 Subject: [PATCH 4/8] eth: add `EthError` variants, make all fns return EthError instead of anyhow --- src/eth.rs | 165 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 100 insertions(+), 65 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index fa5f4f7..2796428 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -53,7 +53,7 @@ pub struct EthSubError { } /// The Response type which a process will get from requesting with an [`EthAction`] will be -/// of the form `Result<(), EthError>`, serialized and deserialized using `serde_json::to_vec` +/// of this type, serialized and deserialized using `serde_json::to_vec` /// and `serde_json::from_slice`. #[derive(Debug, Serialize, Deserialize)] pub enum EthResponse { @@ -74,10 +74,14 @@ pub enum EthError { SubscriptionNotFound, /// Invalid method InvalidMethod(String), + /// Invalid params + InvalidParams, /// Permission denied PermissionDenied, /// Internal RPC error RpcError(String), + /// RPC timed out + RpcTimeout, } /// The action type used for configuring eth:distro:sys. Only processes which have the "root" @@ -163,30 +167,33 @@ impl Provider { pub fn send_request_and_parse_response( &self, action: EthAction, - ) -> anyhow::Result { + ) -> Result { let resp = KiRequest::new() .target(("our", "eth", "distro", "sys")) - .body(serde_json::to_vec(&action)?) - .send_and_await_response(self.request_timeout)??; + .body(serde_json::to_vec(&action).unwrap()) + .send_and_await_response(self.request_timeout) + .unwrap() + .map_err(|_| EthError::RpcTimeout)?; match resp { Message::Response { body, .. } => { - let response = serde_json::from_slice::(&body)?; + let response = serde_json::from_slice::(&body); match response { - EthResponse::Response { value } => serde_json::from_value::(value) - .map_err(|e| anyhow::anyhow!("failed to parse response: {}", e)), - _ => Err(anyhow::anyhow!("unexpected response: {:?}", response)), + Ok(EthResponse::Response { value }) => serde_json::from_value::(value) + .map_err(|e| EthError::RpcError(format!("{e:?}"))), + Ok(EthResponse::Err(e)) => Err(e), + _ => Err(EthError::RpcError("unexpected response".to_string())), } } - _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), + _ => Err(EthError::RpcError("unexpected response".to_string())), } } /// Retrieves the current block number. /// /// # Returns - /// An `anyhow::Result` representing the current block number. - pub fn get_block_number(&self) -> anyhow::Result { + /// A `Result` representing the current block number. + pub fn get_block_number(&self) -> Result { let action = EthAction::Request { chain_id: self.chain_id, method: "eth_blockNumber".to_string(), @@ -204,12 +211,13 @@ impl Provider { /// - `tag`: Optional block ID to specify the block at which the balance is queried. /// /// # Returns - /// An `anyhow::Result` representing the balance of the address. - pub fn get_balance(&self, address: Address, tag: Option) -> anyhow::Result { + /// A `Result` representing the balance of the address. + pub fn get_balance(&self, address: Address, tag: Option) -> Result { let params = serde_json::to_value(( address, tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - ))?; + )) + .unwrap(); let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getBalance".to_string(), @@ -225,12 +233,15 @@ impl Provider { /// - `filter`: The filter criteria for the logs. /// /// # Returns - /// An `anyhow::Result>` containing the logs that match the filter. - pub fn get_logs(&self, filter: &Filter) -> anyhow::Result> { + /// A `Result, EthError>` containing the logs that match the filter. + pub fn get_logs(&self, filter: &Filter) -> Result, EthError> { + let Ok(params) = serde_json::to_value(filter) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getLogs".to_string(), - params: serde_json::to_value(filter)?, + params, }; self.send_request_and_parse_response::>(action) @@ -239,8 +250,8 @@ impl Provider { /// Retrieves the current gas price. /// /// # Returns - /// An `anyhow::Result` representing the current gas price. - pub fn get_gas_price(&self) -> anyhow::Result { + /// A `Result` representing the current gas price. + pub fn get_gas_price(&self) -> Result { let action = EthAction::Request { chain_id: self.chain_id, method: "eth_gasPrice".to_string(), @@ -257,13 +268,15 @@ impl Provider { /// - `tag`: Optional block ID to specify the block at which the count is queried. /// /// # Returns - /// An `anyhow::Result` representing the number of transactions sent from the address. + /// A `Result` representing the number of transactions sent from the address. pub fn get_transaction_count( &self, address: Address, tag: Option, - ) -> anyhow::Result { - let params = serde_json::to_value((address, tag.unwrap_or_default()))?; + ) -> Result { + let Ok(params) = serde_json::to_value((address, tag.unwrap_or_default())) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getTransactionCount".to_string(), @@ -280,13 +293,15 @@ impl Provider { /// - `full_tx`: Whether to return full transaction objects or just their hashes. /// /// # Returns - /// An `anyhow::Result>` representing the block, if found. + /// A `Result, EthError>` representing the block, if found. pub fn get_block_by_hash( &self, hash: BlockHash, full_tx: bool, - ) -> anyhow::Result> { - let params = serde_json::to_value((hash, full_tx))?; + ) -> Result, EthError> { + let Ok(params) = serde_json::to_value((hash, full_tx)) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getBlockByHash".to_string(), @@ -302,13 +317,15 @@ impl Provider { /// - `full_tx`: Whether to return full transaction objects or just their hashes. /// /// # Returns - /// An `anyhow::Result>` representing the block, if found. + /// A `Result, EthError>` representing the block, if found. pub fn get_block_by_number( &self, number: BlockNumberOrTag, full_tx: bool, - ) -> anyhow::Result> { - let params = serde_json::to_value((number, full_tx))?; + ) -> Result, EthError> { + let Ok(params) = serde_json::to_value((number, full_tx)) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getBlockByNumber".to_string(), @@ -326,14 +343,16 @@ impl Provider { /// - `tag`: Optional block ID to specify the block at which the storage is queried. /// /// # Returns - /// An `anyhow::Result` representing the data stored at the given address and key. + /// A `Result` representing the data stored at the given address and key. pub fn get_storage_at( &self, address: Address, key: U256, tag: Option, - ) -> anyhow::Result { - let params = serde_json::to_value((address, key, tag.unwrap_or_default()))?; + ) -> Result { + let Ok(params) = serde_json::to_value((address, key, tag.unwrap_or_default())) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getStorageAt".to_string(), @@ -350,9 +369,11 @@ impl Provider { /// - `tag`: The block ID to specify the block at which the code is queried. /// /// # Returns - /// An `anyhow::Result` representing the code stored at the given address. - pub fn get_code_at(&self, address: Address, tag: BlockId) -> anyhow::Result { - let params = serde_json::to_value((address, tag))?; + /// A `Result` representing the code stored at the given address. + pub fn get_code_at(&self, address: Address, tag: BlockId) -> Result { + let Ok(params) = serde_json::to_value((address, tag)) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getCode".to_string(), @@ -368,9 +389,11 @@ impl Provider { /// - `hash`: The hash of the transaction to retrieve. /// /// # Returns - /// An `anyhow::Result>` representing the transaction, if found. - pub fn get_transaction_by_hash(&self, hash: TxHash) -> anyhow::Result> { - let params = serde_json::to_value(hash)?; + /// A `Result, EthError>` representing the transaction, if found. + pub fn get_transaction_by_hash(&self, hash: TxHash) -> Result, EthError> { + let Ok(params) = serde_json::to_value(hash) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getTransactionByHash".to_string(), @@ -386,12 +409,14 @@ impl Provider { /// - `hash`: The hash of the transaction for which the receipt is requested. /// /// # Returns - /// An `anyhow::Result>` representing the transaction receipt, if found. + /// A `Result, EthError>` representing the transaction receipt, if found. pub fn get_transaction_receipt( &self, hash: TxHash, - ) -> anyhow::Result> { - let params = serde_json::to_value(hash)?; + ) -> Result, EthError> { + let Ok(params) = serde_json::to_value(hash) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_getTransactionReceipt".to_string(), @@ -408,13 +433,15 @@ impl Provider { /// - `block`: Optional block ID to specify the block at which the gas estimate should be made. /// /// # Returns - /// An `anyhow::Result` representing the estimated gas amount. + /// A `Result` representing the estimated gas amount. pub fn estimate_gas( &self, tx: TransactionRequest, block: Option, - ) -> anyhow::Result { - let params = serde_json::to_value((tx, block.unwrap_or_default()))?; + ) -> Result { + let Ok(params) = serde_json::to_value((tx, block.unwrap_or_default())) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_estimateGas".to_string(), @@ -427,9 +454,9 @@ impl Provider { /// Retrieves the list of accounts controlled by the node. /// /// # Returns - /// An `anyhow::Result>` representing the list of accounts. + /// A `Result, EthError>` representing the list of accounts. /// Note: This function may return an empty list depending on the node's configuration and capabilities. - pub fn get_accounts(&self) -> anyhow::Result> { + pub fn get_accounts(&self) -> Result, EthError> { let action = EthAction::Request { chain_id: self.chain_id, method: "eth_accounts".to_string(), @@ -447,14 +474,16 @@ impl Provider { /// - `reward_percentiles`: A list of percentiles to report fee rewards for. /// /// # Returns - /// An `anyhow::Result` representing the fee history for the specified range. + /// A `Result` representing the fee history for the specified range. pub fn get_fee_history( &self, block_count: U256, last_block: BlockNumberOrTag, reward_percentiles: Vec, - ) -> anyhow::Result { - let params = serde_json::to_value((block_count, last_block, reward_percentiles))?; + ) -> Result { + let Ok(params) = serde_json::to_value((block_count, last_block, reward_percentiles)) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_feeHistory".to_string(), @@ -471,9 +500,11 @@ impl Provider { /// - `block`: Optional block ID to specify the block at which the call should be made. /// /// # Returns - /// An `anyhow::Result` representing the result of the call. - pub fn call(&self, tx: TransactionRequest, block: Option) -> anyhow::Result { - let params = serde_json::to_value((tx, block.unwrap_or_default()))?; + /// A `Result` representing the result of the call. + pub fn call(&self, tx: TransactionRequest, block: Option) -> Result { + let Ok(params) = serde_json::to_value((tx, block.unwrap_or_default())) else { + return Err(EthError::InvalidParams); + }; let action = EthAction::Request { chain_id: self.chain_id, method: "eth_call".to_string(), @@ -489,12 +520,12 @@ impl Provider { /// - `tx`: The raw transaction data. /// /// # Returns - /// An `anyhow::Result` representing the hash of the transaction once it has been sent. - pub fn send_raw_transaction(&self, tx: Bytes) -> anyhow::Result { + /// A `Result` representing the hash of the transaction once it has been sent. + pub fn send_raw_transaction(&self, tx: Bytes) -> Result { let action = EthAction::Request { chain_id: self.chain_id, method: "eth_sendRawTransaction".to_string(), - params: serde_json::to_value(tx)?, + params: serde_json::to_value(tx).unwrap(), }; self.send_request_and_parse_response::(action) @@ -507,8 +538,8 @@ impl Provider { /// - `filter`: The filter criteria for the logs. /// /// # Returns - /// An `anyhow::Result<()>` indicating whether the subscription was created. - pub fn subscribe(&self, sub_id: u64, filter: Filter) -> anyhow::Result<()> { + /// A `Result<(), EthError>` indicating whether the subscription was created. + pub fn subscribe(&self, sub_id: u64, filter: Filter) -> Result<(), EthError> { let action = EthAction::SubscribeLogs { sub_id, chain_id: self.chain_id, @@ -516,23 +547,27 @@ impl Provider { params: Params::Logs(Box::new(filter)), }; + let Ok(body) = serde_json::to_vec(&action) else { + return Err(EthError::InvalidParams); + }; + let resp = KiRequest::new() .target(("our", "eth", "distro", "sys")) - .body(serde_json::to_vec(&action)?) - .send_and_await_response(self.request_timeout)??; + .body(body) + .send_and_await_response(self.request_timeout) + .unwrap() + .map_err(|_| EthError::RpcTimeout)?; match resp { Message::Response { body, .. } => { - let response = serde_json::from_slice::(&body)?; + let response = serde_json::from_slice::(&body); match response { - EthResponse::Ok => Ok(()), - EthResponse::Response { .. } => { - Err(anyhow::anyhow!("unexpected response: {:?}", response)) - } - EthResponse::Err(e) => Err(anyhow::anyhow!("{e:?}")), + Ok(EthResponse::Ok) => Ok(()), + Ok(EthResponse::Err(e)) => Err(e), + _ => Err(EthError::RpcError("unexpected response".to_string())), } } - _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), + _ => Err(EthError::RpcError("unexpected response".to_string())), } } From d1c29c20ab7be0e1cf98ee96711b719e147cfc56 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 26 Feb 2024 02:04:29 -0300 Subject: [PATCH 5/8] fix: add magic tuples, update alloy versions --- Cargo.lock | 33 ++++++++++++++++++++++++++++----- Cargo.toml | 8 ++++---- src/eth.rs | 12 ++++++++---- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60c0706..060b678 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy.git?rev=098ad56#098ad5657d55bbc5fe9469ede2a9ca79def738f2" +source = "git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4#6f8ebb45afca1a201a11d421ec46db0f7a1d8d08" dependencies = [ "alloy-primitives", "serde", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4b6fb2b432ff223d513db7f908937f63c252bee0af9b82bfd25b0a5dd1eb0d8" +checksum = "ef197eb250c64962003cb08b90b17f0882c192f4a6f2f544809d424fd7cb0e7d" dependencies = [ "alloy-rlp", "bytes", @@ -75,7 +75,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy.git?rev=098ad56#098ad5657d55bbc5fe9469ede2a9ca79def738f2" +source = "git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4#6f8ebb45afca1a201a11d421ec46db0f7a1d8d08" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -88,10 +88,11 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy.git?rev=098ad56#098ad5657d55bbc5fe9469ede2a9ca79def738f2" +source = "git+https://github.com/alloy-rs/alloy.git?rev=6f8ebb4#6f8ebb45afca1a201a11d421ec46db0f7a1d8d08" dependencies = [ "alloy-json-rpc", "base64", + "futures-util", "serde", "serde_json", "thiserror", @@ -618,6 +619,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "futures-task" version = "0.3.30" @@ -631,9 +643,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1386,6 +1400,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.1" diff --git a/Cargo.toml b/Cargo.toml index 2a8a97f..ed4c7e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,10 @@ homepage = "https://kinode.org" repository = "https://github.com/kinode-dao/process_lib" [dependencies] -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "098ad56" } -alloy-primitives = "0.6.2" -alloy-transport = { git = "https://github.com/alloy-rs/alloy.git", rev = "098ad56" } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy.git", rev = "098ad56" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "6f8ebb4" } +alloy-primitives = "0.6.3" +alloy-transport = { git = "https://github.com/alloy-rs/alloy.git", rev = "6f8ebb4" } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy.git", rev = "6f8ebb4" } anyhow = "1.0" bincode = "1.3.3" http = "1.0.0" diff --git a/src/eth.rs b/src/eth.rs index 2796428..8d9ddd9 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -235,7 +235,8 @@ impl Provider { /// # Returns /// A `Result, EthError>` containing the logs that match the filter. pub fn get_logs(&self, filter: &Filter) -> Result, EthError> { - let Ok(params) = serde_json::to_value(filter) else { + // NOTE: filter must be encased by a tuple to be serialized correctly + let Ok(params) = serde_json::to_value((filter,)) else { return Err(EthError::InvalidParams); }; let action = EthAction::Request { @@ -391,7 +392,8 @@ impl Provider { /// # Returns /// A `Result, EthError>` representing the transaction, if found. pub fn get_transaction_by_hash(&self, hash: TxHash) -> Result, EthError> { - let Ok(params) = serde_json::to_value(hash) else { + // NOTE: hash must be encased by a tuple to be serialized correctly + let Ok(params) = serde_json::to_value((hash,)) else { return Err(EthError::InvalidParams); }; let action = EthAction::Request { @@ -414,7 +416,8 @@ impl Provider { &self, hash: TxHash, ) -> Result, EthError> { - let Ok(params) = serde_json::to_value(hash) else { + // NOTE: hash must be encased by a tuple to be serialized correctly + let Ok(params) = serde_json::to_value((hash,)) else { return Err(EthError::InvalidParams); }; let action = EthAction::Request { @@ -525,7 +528,8 @@ impl Provider { let action = EthAction::Request { chain_id: self.chain_id, method: "eth_sendRawTransaction".to_string(), - params: serde_json::to_value(tx).unwrap(), + // NOTE: tx must be encased by a tuple to be serialized correctly + params: serde_json::to_value((tx,)).unwrap(), }; self.send_request_and_parse_response::(action) From bc24fd0ad76d133d694605a89b6cf65138e58500 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 26 Feb 2024 17:29:39 -0300 Subject: [PATCH 6/8] eth error refactor out unused --- src/eth.rs | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 8d9ddd9..035de5c 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -7,6 +7,11 @@ pub use alloy_rpc_types::{ TransactionReceipt, }; use serde::{Deserialize, Serialize}; +use std::collections::HashSet; + +// +// types mirrored from runtime module +// /// The Action and Request type that can be made to eth:distro:sys. Any process with messaging /// capabilities can send this action to the eth provider. @@ -32,13 +37,13 @@ pub enum EthAction { }, } -/// Incoming Result type for subscription updates or errors that processes will receive. +/// Incoming `Request` containing subscription updates or errors that processes will receive. /// Can deserialize all incoming requests from eth:distro:sys to this type. /// /// Will be serialized and deserialized using `serde_json::to_vec` and `serde_json::from_slice`. pub type EthSubResult = Result; -/// Incoming Request type for successful subscription updates. +/// Incoming type for successful subscription updates. #[derive(Debug, Serialize, Deserialize)] pub struct EthSub { pub id: u64, @@ -55,6 +60,9 @@ pub struct EthSubError { /// The Response type which a process will get from requesting with an [`EthAction`] will be /// of this type, serialized and deserialized using `serde_json::to_vec` /// and `serde_json::from_slice`. +/// +/// In the case of an [`EthAction::SubscribeLogs`] request, the response will indicate if +/// the subscription was successfully created or not. #[derive(Debug, Serialize, Deserialize)] pub enum EthResponse { Ok, @@ -64,22 +72,16 @@ pub enum EthResponse { #[derive(Debug, Serialize, Deserialize)] pub enum EthError { + /// provider module cannot parse message + MalformedRequest, /// No RPC provider for the chain NoRpcForChain, - /// Underlying transport error - TransportError(String), /// Subscription closed SubscriptionClosed(u64), - /// The subscription ID was not found, so we couldn't unsubscribe. - SubscriptionNotFound, /// Invalid method InvalidMethod(String), - /// Invalid params - InvalidParams, /// Permission denied PermissionDenied, - /// Internal RPC error - RpcError(String), /// RPC timed out RpcTimeout, } @@ -111,8 +113,10 @@ pub enum EthConfigAction { /// Set the list of providers to a new list. /// Replaces all existing saved provider configs. SetProviders(SavedConfigs), - /// Get the list of as a [`SavedConfigs`] object. + /// Get the list of current providers as a [`SavedConfigs`] object. GetProviders, + /// Get the current access settings. + GetAccessSettings, } /// Response type from an [`EthConfigAction`] request. @@ -120,11 +124,23 @@ pub enum EthConfigAction { pub enum EthConfigResponse { Ok, /// Response from a GetProviders request. + /// Note the [`crate::kernel_types::KnsUpdate`] will only have the correct `name` field. + /// The rest of the Update is not saved in this module. Providers(SavedConfigs), + /// Response from a GetAccessSettings request. + AccessSettings(AccessSettings), /// Permission denied due to missing capability PermissionDenied, } +/// Settings for our ETH provider +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AccessSettings { + pub public: bool, // whether or not other nodes can access through us + pub allow: HashSet, // whitelist for access (only used if public == false) + pub deny: HashSet, // blacklist for access (always used) +} + pub type SavedConfigs = Vec; /// Provider config. Can currently be a node or a ws provider instance. @@ -141,6 +157,15 @@ pub enum NodeOrRpcUrl { RpcUrl(String), } +impl std::cmp::PartialEq for NodeOrRpcUrl { + fn eq(&self, other: &str) -> bool { + match self { + NodeOrRpcUrl::Node(kns) => kns.name == other, + NodeOrRpcUrl::RpcUrl(url) => url == other, + } + } +} + /// An EVM chain provider. Create this object to start making RPC calls. /// Set the chain_id to determine which chain to call: requests will fail /// unless the node this process is running on has access to a provider From 9838f5d1cb0b2b6d63ad4d82c9628ff3e8d33dff Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 26 Feb 2024 17:41:26 -0300 Subject: [PATCH 7/8] re-add missing errors --- src/eth.rs | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 035de5c..62ad109 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -80,10 +80,14 @@ pub enum EthError { SubscriptionClosed(u64), /// Invalid method InvalidMethod(String), + /// Invalid parameters + InvalidParams, /// Permission denied PermissionDenied, /// RPC timed out RpcTimeout, + /// RPC gave garbage back + RpcMalformedResponse, } /// The action type used for configuring eth:distro:sys. Only processes which have the "root" @@ -201,16 +205,14 @@ impl Provider { .map_err(|_| EthError::RpcTimeout)?; match resp { - Message::Response { body, .. } => { - let response = serde_json::from_slice::(&body); - match response { - Ok(EthResponse::Response { value }) => serde_json::from_value::(value) - .map_err(|e| EthError::RpcError(format!("{e:?}"))), - Ok(EthResponse::Err(e)) => Err(e), - _ => Err(EthError::RpcError("unexpected response".to_string())), + Message::Response { body, .. } => match serde_json::from_slice::(&body) { + Ok(EthResponse::Response { value }) => { + serde_json::from_value::(value).map_err(|_| EthError::RpcMalformedResponse) } - } - _ => Err(EthError::RpcError("unexpected response".to_string())), + Ok(EthResponse::Err(e)) => Err(e), + _ => Err(EthError::RpcMalformedResponse), + }, + _ => Err(EthError::RpcMalformedResponse), } } @@ -593,10 +595,10 @@ impl Provider { match response { Ok(EthResponse::Ok) => Ok(()), Ok(EthResponse::Err(e)) => Err(e), - _ => Err(EthError::RpcError("unexpected response".to_string())), + _ => Err(EthError::RpcMalformedResponse), } } - _ => Err(EthError::RpcError("unexpected response".to_string())), + _ => Err(EthError::RpcMalformedResponse), } } @@ -606,27 +608,23 @@ impl Provider { /// - `sub_id`: The subscription ID to unsubscribe from. /// /// # Returns - /// An `anyhow::Result<()>` indicating whether the subscription was cancelled. - pub fn unsubscribe(&self, sub_id: u64) -> anyhow::Result<()> { + /// A `Result<(), EthError>` indicating whether the subscription was cancelled. + pub fn unsubscribe(&self, sub_id: u64) -> Result<(), EthError> { let action = EthAction::UnsubscribeLogs(sub_id); let resp = KiRequest::new() .target(("our", "eth", "distro", "sys")) - .body(serde_json::to_vec(&action)?) - .send_and_await_response(self.request_timeout)??; + .body(serde_json::to_vec(&action).map_err(|_| EthError::MalformedRequest)?) + .send_and_await_response(self.request_timeout) + .unwrap() + .map_err(|_| EthError::RpcTimeout)?; match resp { - Message::Response { body, .. } => { - let response = serde_json::from_slice::(&body)?; - match response { - EthResponse::Ok => Ok(()), - EthResponse::Response { .. } => { - Err(anyhow::anyhow!("unexpected response: {:?}", response)) - } - EthResponse::Err(e) => Err(anyhow::anyhow!("{e:?}")), - } - } - _ => Err(anyhow::anyhow!("unexpected message type: {:?}", resp)), + Message::Response { body, .. } => match serde_json::from_slice::(&body) { + Ok(EthResponse::Ok) => Ok(()), + _ => Err(EthError::RpcMalformedResponse), + }, + _ => Err(EthError::RpcMalformedResponse), } } } From 86ae506b03dccf0265f5994967ced1871009c76b Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 4 Mar 2024 15:41:23 -0300 Subject: [PATCH 8/8] match types to runtime --- src/eth.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 62ad109..07f27fb 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -17,7 +17,7 @@ use std::collections::HashSet; /// capabilities can send this action to the eth provider. /// /// Will be serialized and deserialized using `serde_json::to_vec` and `serde_json::from_slice`. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum EthAction { /// Subscribe to logs with a custom filter. ID is to be used to unsubscribe. /// Logs come in as alloy_rpc_types::pubsub::SubscriptionResults @@ -70,7 +70,7 @@ pub enum EthResponse { Err(EthError), } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, PartialEq)] pub enum EthError { /// provider module cannot parse message MalformedRequest, @@ -157,14 +157,17 @@ pub struct ProviderConfig { #[derive(Clone, Debug, Deserialize, Serialize)] pub enum NodeOrRpcUrl { - Node(crate::kernel_types::KnsUpdate), + Node { + kns_update: crate::kernel_types::KnsUpdate, + use_as_provider: bool, // for routers inside saved config + }, RpcUrl(String), } impl std::cmp::PartialEq for NodeOrRpcUrl { fn eq(&self, other: &str) -> bool { match self { - NodeOrRpcUrl::Node(kns) => kns.name == other, + NodeOrRpcUrl::Node { kns_update, .. } => kns_update.name == other, NodeOrRpcUrl::RpcUrl(url) => url == other, } }