Skip to content

Commit

Permalink
feat: implement auto-mining (#4622)
Browse files Browse the repository at this point in the history
Co-authored-by: Agost Biro <[email protected]>
  • Loading branch information
Wodann and agostbiro authored Dec 6, 2023
1 parent f61c25a commit 8debd54
Show file tree
Hide file tree
Showing 43 changed files with 1,861 additions and 394 deletions.
388 changes: 376 additions & 12 deletions Cargo.lock

Large diffs are not rendered by default.

22 changes: 10 additions & 12 deletions crates/edr_eth/src/remote.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
use std::{
fmt,
fmt::{Display, Formatter},
};

mod cacheable_method_invocation;
/// an Ethereum JSON-RPC client
pub mod client;

/// ethereum objects as specifically used in the JSON-RPC interface
pub mod eth;

/// data types for use with filter-based RPC methods
pub mod filter;

/// data types specific to JSON-RPC but not specific to Ethereum.
pub mod jsonrpc;

/// RPC methods
pub mod methods;
mod r#override;

mod cacheable_method_invocation;

pub use client::{RpcClient, RpcClientError};
use std::{
fmt,
fmt::{Display, Formatter},
};

pub use self::{
client::{RpcClient, RpcClientError},
r#override::*,
};
use crate::B256;

/// for representing block specifications per EIP-1898
Expand Down
2 changes: 1 addition & 1 deletion crates/edr_eth/src/remote/cacheable_method_invocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl<'a> TryFrom<&'a MethodInvocation> for CacheableMethodInvocation<'a> {
// Explicit to make sure if a new method is added, it is not forgotten here.
MethodInvocation::Accounts(_)
| MethodInvocation::BlockNumber(_)
| MethodInvocation::Call(_, _)
| MethodInvocation::Call(_, _, _)
| MethodInvocation::Coinbase(_)
| MethodInvocation::EstimateGas(_, _)
| MethodInvocation::FeeHistory(_, _, _)
Expand Down
87 changes: 56 additions & 31 deletions crates/edr_eth/src/remote/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,33 +669,53 @@ impl RpcClient {
address: &Address,
block: Option<BlockSpec>,
) -> Result<AccountInfo, RpcClientError> {
let inputs = &[
MethodInvocation::GetBalance(*address, block.clone()),
MethodInvocation::GetTransactionCount(*address, block.clone()),
MethodInvocation::GetCode(*address, block),
];

let responses = self.batch_call(inputs).await?;
let (balance, nonce, code) = responses
.into_iter()
.collect_tuple()
.expect("batch call checks responses");

let balance = balance.parse::<U256>().await?;
let nonce: u64 = nonce.parse::<U256>().await?.to();
let code: Bytes = code.parse::<ZeroXPrefixedBytes>().await?.into();
let code = if code.is_empty() {
None
} else {
Some(Bytecode::new_raw(code))
};
Ok(self
.get_account_infos(&[*address], block)
.await?
.pop()
.expect("batch call returns as many results as inputs if there was no error"))
}

Ok(AccountInfo {
balance,
code_hash: code.as_ref().map_or(KECCAK_EMPTY, Bytecode::hash_slow),
code,
nonce,
})
/// Fetch account infos for multiple addresses in a batch call.
pub async fn get_account_infos(
&self,
addresses: &[Address],
block: Option<BlockSpec>,
) -> Result<Vec<AccountInfo>, RpcClientError> {
let inputs: Vec<MethodInvocation> = addresses
.iter()
.flat_map(|address| {
[
MethodInvocation::GetBalance(*address, block.clone()),
MethodInvocation::GetTransactionCount(*address, block.clone()),
MethodInvocation::GetCode(*address, block.clone()),
]
})
.collect();

let responses = self.batch_call(inputs.as_slice()).await?;
let mut results = Vec::with_capacity(inputs.len() / 3);
for (balance, nonce, code) in responses.into_iter().tuples() {
let balance = balance.parse::<U256>().await?;
let nonce: u64 = nonce.parse::<U256>().await?.to();
let code: Bytes = code.parse::<ZeroXPrefixedBytes>().await?.into();
let code = if code.is_empty() {
None
} else {
Some(Bytecode::new_raw(code))
};

let account_info = AccountInfo {
balance,
code_hash: code.as_ref().map_or(KECCAK_EMPTY, Bytecode::hash_slow),
code,
nonce,
};

results.push(account_info);
}

Ok(results)
}

/// Calls `eth_getBlockByHash` and returns the transaction's hash.
Expand Down Expand Up @@ -1295,19 +1315,24 @@ mod tests {
}

#[tokio::test]
async fn get_account_info_latest_contract() {
async fn get_account_infos() {
let alchemy_url = get_alchemy_url();

let dai_address = Address::from_str("0x6b175474e89094c44da98b954eedeac495271d0f")
.expect("failed to parse address");
let hardhat_default_address =
Address::from_str("0xbe862ad9abfe6f22bcb087716c7d89a26051f74c")
.expect("failed to parse address");

let account_info = TestRpcClient::new(&alchemy_url)
.get_account_info(&dai_address, Some(BlockSpec::latest()))
let account_infos = TestRpcClient::new(&alchemy_url)
.get_account_infos(
&[dai_address, hardhat_default_address],
Some(BlockSpec::latest()),
)
.await
.expect("should have succeeded");

assert_ne!(account_info.code_hash, KECCAK_EMPTY);
assert!(account_info.code.is_some());
assert_eq!(account_infos.len(), 2);
}

#[tokio::test]
Expand Down
13 changes: 13 additions & 0 deletions crates/edr_eth/src/remote/jsonrpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ impl<T> ResponseData<T> {
}
}

impl<SuccessT: Serialize, ErrorT: Into<Error>> From<Result<SuccessT, ErrorT>>
for ResponseData<SuccessT>
{
fn from(result: Result<SuccessT, ErrorT>) -> Self {
match result {
Ok(result) => ResponseData::Success { result },
Err(error) => ResponseData::Error {
error: error.into(),
},
}
}
}

/// Represents JSON-RPC request/response id.
///
/// An identifier established by the Client that MUST contain a String, Number,
Expand Down
35 changes: 23 additions & 12 deletions crates/edr_eth/src/remote/methods.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use revm_primitives::ruint::aliases::U64;

use super::StateOverrideOptions;
use crate::{
access_list::AccessListItem,
remote::{
eth::eip712,
filter::{FilterOptions, SubscriptionType},
Expand All @@ -14,23 +16,31 @@ use crate::{
/// for specifying input to methods requiring a transaction object, like
/// `eth_call`, `eth_sendTransaction` and `eth_estimateGas`
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct TransactionInput {
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct CallRequest {
/// the address from which the transaction should be sent
pub from: Option<Address>,
/// the address to which the transaction should be sent
pub to: Option<Address>,
#[cfg_attr(feature = "serde", serde(default, with = "crate::serde::optional_u64"))]
/// gas
pub gas: Option<U256>,
pub gas: Option<u64>,
/// gas price
#[serde(rename = "gasPrice")]
pub gas_price: Option<U256>,
/// max base fee per gas sender is willing to pay
pub max_fee_per_gas: Option<U256>,
/// miner tip
pub max_priority_fee_per_gas: Option<U256>,
/// transaction value
pub value: Option<U256>,
/// transaction data
pub data: Option<ZeroXPrefixedBytes>,
/// warm storage access pre-payment
pub access_list: Option<Vec<AccessListItem>>,
}

mod optional_block_spec_resolved {
mod optional_block_spec {
use super::BlockSpec;

pub fn latest() -> Option<BlockSpec> {
Expand All @@ -55,12 +65,13 @@ pub enum MethodInvocation {
/// eth_call
#[serde(rename = "eth_call")]
Call(
TransactionInput,
CallRequest,
#[serde(
skip_serializing_if = "Option::is_none",
default = "optional_block_spec_resolved::latest"
default = "optional_block_spec::latest"
)]
Option<BlockSpec>,
#[serde(default, skip_serializing_if = "Option::is_none")] Option<StateOverrideOptions>,
),
/// eth_chainId
#[serde(rename = "eth_chainId", with = "crate::serde::empty_params")]
Expand All @@ -71,10 +82,10 @@ pub enum MethodInvocation {
/// eth_estimateGas
#[serde(rename = "eth_estimateGas")]
EstimateGas(
TransactionInput,
CallRequest,
#[serde(
skip_serializing_if = "Option::is_none",
default = "optional_block_spec_resolved::pending"
default = "optional_block_spec::pending"
)]
Option<BlockSpec>,
),
Expand All @@ -97,7 +108,7 @@ pub enum MethodInvocation {
Address,
#[serde(
skip_serializing_if = "Option::is_none",
default = "optional_block_spec_resolved::latest"
default = "optional_block_spec::latest"
)]
Option<BlockSpec>,
),
Expand Down Expand Up @@ -134,7 +145,7 @@ pub enum MethodInvocation {
Address,
#[serde(
skip_serializing_if = "Option::is_none",
default = "optional_block_spec_resolved::latest"
default = "optional_block_spec::latest"
)]
Option<BlockSpec>,
),
Expand All @@ -155,7 +166,7 @@ pub enum MethodInvocation {
U256,
#[serde(
skip_serializing_if = "Option::is_none",
default = "optional_block_spec_resolved::latest"
default = "optional_block_spec::latest"
)]
Option<BlockSpec>,
),
Expand All @@ -176,7 +187,7 @@ pub enum MethodInvocation {
Address,
#[serde(
skip_serializing_if = "Option::is_none",
default = "optional_block_spec_resolved::latest"
default = "optional_block_spec::latest"
)]
Option<BlockSpec>,
),
Expand Down
29 changes: 29 additions & 0 deletions crates/edr_eth/src/remote/override.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use revm_primitives::{Address, HashMap, U256};

use crate::{serde::ZeroXPrefixedBytes, state::Storage};

/// Options for overriding account information.
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountOverrideOptions {
/// Account balance override.
pub balance: Option<U256>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::serde::optional_u64"
)]
/// Account nonce override.
pub nonce: Option<u64>,
/// Account code override.
pub code: Option<ZeroXPrefixedBytes>,
/// Account storage override. Mutually exclusive with `storage_diff`.
#[serde(rename = "state")]
pub storage: Option<Storage>,
/// Account storage diff override. Mutually exclusive with `storage`.
#[serde(rename = "stateDiff")]
pub storage_diff: Option<Storage>,
}

/// Type representing a full set of overrides for account information.
pub type StateOverrideOptions = HashMap<Address, AccountOverrideOptions>;
2 changes: 1 addition & 1 deletion crates/edr_eth/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::{

/// Type for specifying a byte string that will have a 0x prefix when serialized
/// and deserialized
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ZeroXPrefixedBytes {
inner: Bytes,
}
Expand Down
Loading

0 comments on commit 8debd54

Please sign in to comment.