fix: cleanup call example (#212)

* fix: evm panic on slot not found (#208)

* cleanup and example

---------

Co-authored-by: refcell.eth <abigger87@gmail.com>
This commit is contained in:
Noah Citron 2023-03-11 01:58:42 -05:00 committed by GitHub
parent a73f9c648b
commit 3c471c2bef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 488 additions and 285 deletions

573
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -21,23 +21,21 @@ config = { path = "./config" }
common = { path = "./common" }
consensus = { path = "./consensus" }
execution = { path = "./execution" }
serde = { version = "1.0.154", features = ["derive"] }
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { version = "1", features = ["full"] }
eyre = "0.6.8"
home = "0.5.4"
ethers = "1.0.0"
dirs = "4.0.0"
ethers = { version = "1.0.2", features = [ "abigen" ] }
env_logger = "0.9.0"
log = "0.4.17"
tracing-test = "0.2.3"
tracing-test = "0.2.4"
criterion = { version = "0.4", features = [ "async_tokio", "plotters" ]}
plotters = "0.3.3"
tempfile = "3.3.0"
plotters = "0.3.4"
tempfile = "3.4.0"
hex = "0.4.3"
[patch.crates-io]
ethers = { git = "https://github.com/gakonst/ethers-rs", rev = "c17c0c3c956f12d205a5ede3176599d8a30ca739" }
[profile.release]
strip = true
opt-level = "z"
@ -65,6 +63,10 @@ path = "examples/client.rs"
name = "config"
path = "examples/config.rs"
[[example]]
name = "call"
path = "examples/call.rs"
######################################
# Benchmarks
######################################

View File

@ -1,3 +1,4 @@
use ethers::types::H256;
use thiserror::Error;
use crate::types::BlockTag;
@ -14,6 +15,18 @@ impl BlockNotFoundError {
}
}
#[derive(Debug, Error)]
#[error("slot not found: {slot:?}")]
pub struct SlotNotFoundError {
slot: H256,
}
impl SlotNotFoundError {
pub fn new(slot: H256) -> Self {
Self { slot }
}
}
#[derive(Debug, Error)]
#[error("rpc error on method: {method}, message: {error}")]
pub struct RpcError<E: ToString> {

93
examples/call.rs Normal file
View File

@ -0,0 +1,93 @@
#![allow(deprecated)]
use env_logger::Env;
use ethers::prelude::*;
use std::{path::PathBuf, sync::Arc};
use helios::{
client::{Client, ClientBuilder, FileDB},
config::networks::Network,
types::{BlockTag, CallOpts},
};
// Generate the type-safe contract bindings with an ABI
abigen!(
Renderer,
r#"[
function renderBroker(uint256) external view returns (string memory)
function renderBroker(uint256, uint256) external view returns (string memory)
]"#,
event_derives(serde::Deserialize, serde::Serialize)
);
#[tokio::main]
async fn main() -> eyre::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
// Load the rpc url using the `MAINNET_RPC_URL` environment variable
let eth_rpc_url = std::env::var("MAINNET_RPC_URL")?;
let consensus_rpc = "https://www.lightclientdata.org";
log::info!("Consensus RPC URL: {}", consensus_rpc);
// Construct the client
let data_dir = PathBuf::from("/tmp/helios");
let mut client: Client<FileDB> = ClientBuilder::new()
.network(Network::MAINNET)
.data_dir(data_dir)
.consensus_rpc(consensus_rpc)
.execution_rpc(&eth_rpc_url)
.load_external_fallback()
.build()?;
log::info!(
"[\"{}\"] Client built with external checkpoint fallbacks",
Network::MAINNET
);
// Start the client
client.start().await?;
// Call the erroneous account method
// The expected asset is: https://0x8bb9a8baeec177ae55ac410c429cbbbbb9198cac.w3eth.io/renderBroker/5
// Retrieved by calling `renderBroker(5)` on the contract: https://etherscan.io/address/0x8bb9a8baeec177ae55ac410c429cbbbbb9198cac#code
let account = "0x8bb9a8baeec177ae55ac410c429cbbbbb9198cac";
let method = "renderBroker(uint256)";
let method2 = "renderBroker(uint256, uint256)";
let argument = U256::from(5);
let address = account.parse::<Address>()?;
let block = BlockTag::Latest;
let provider = Provider::<Http>::try_from(eth_rpc_url)?;
let render = Renderer::new(address, Arc::new(provider.clone()));
log::debug!("Context: call @ {account}::{method} <{argument}>");
// Call using abigen
let result = render.render_broker_0(argument).call().await?;
log::info!(
"[ABIGEN] {account}::{method} -> Response Length: {:?}",
result.len()
);
let render = Renderer::new(address, Arc::new(provider.clone()));
let result = render
.render_broker_1(argument, U256::from(10))
.call()
.await?;
log::info!(
"[ABIGEN] {account}::{method2} -> Response Length: {:?}",
result.len()
);
// Call on helios client
let encoded_call = render.render_broker_0(argument).calldata().unwrap();
let call_opts = CallOpts {
from: Some("0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8".parse::<Address>()?),
to: Some(address),
gas: Some(U256::from(U64::MAX.as_u64())),
gas_price: None,
value: None,
data: Some(encoded_call.to_vec()),
};
log::debug!("Calling helios client on block: {block:?}");
let result = client.call(&call_opts, block).await?;
log::info!("[HELIOS] {account}::{method} ->{:?}", result.len());
Ok(())
}

View File

@ -1,4 +1,5 @@
use config::CliConfig;
use dirs::home_dir;
use eyre::Result;
use helios::prelude::*;
@ -6,7 +7,7 @@ use helios::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Load the config from the global config file
let config_path = home::home_dir().unwrap().join(".helios/helios.toml");
let config_path = home_dir().unwrap().join(".helios/helios.toml");
let config = Config::from_file(&config_path, "mainnet", &CliConfig::default());
println!("Constructed config: {config:#?}");

View File

@ -6,7 +6,10 @@ use std::{
};
use bytes::Bytes;
use common::{errors::BlockNotFoundError, types::BlockTag};
use common::{
errors::{BlockNotFoundError, SlotNotFoundError},
types::BlockTag,
};
use ethers::{
abi::ethereum_types::BigEndianHash,
types::transaction::eip2930::AccessListItem,
@ -224,8 +227,6 @@ impl<'a, R: ExecutionRpc> ProofDB<'a, R> {
let handle = thread::spawn(move || {
let account_fut = execution.get_account(&address, Some(&slots), &payload);
// let runtime = Runtime::new()?;
// runtime.block_on(account_fut)
block_on(account_fut)
});
@ -269,11 +270,7 @@ impl<'a, R: ExecutionRpc> Database for ProofDB<'a, R> {
}
fn storage(&mut self, address: H160, slot: U256) -> Result<U256, Report> {
trace!(
"fetch evm state for address=0x{}, slot={}",
hex::encode(address.as_bytes()),
slot
);
trace!("fetch evm state for address={:?}, slot={}", address, slot);
let slot = H256::from_uint(&slot);
@ -284,13 +281,13 @@ impl<'a, R: ExecutionRpc> Database for ProofDB<'a, R> {
.get_account(address, &[slot])?
.slots
.get(&slot)
.unwrap(),
.ok_or(SlotNotFoundError::new(slot))?,
},
None => *self
.get_account(address, &[slot])?
.slots
.get(&slot)
.unwrap(),
.ok_or(SlotNotFoundError::new(slot))?,
})
}
@ -303,3 +300,59 @@ fn is_precompile(address: &Address) -> bool {
address.le(&Address::from_str("0x0000000000000000000000000000000000000009").unwrap())
&& address.gt(&Address::zero())
}
#[cfg(test)]
mod tests {
use common::utils::hex_str_to_bytes;
use ssz_rs::Vector;
use crate::rpc::mock_rpc::MockRpc;
use super::*;
fn get_client() -> ExecutionClient<MockRpc> {
ExecutionClient::new("testdata/").unwrap()
}
#[test]
fn test_proof_db() {
// Construct proofdb params
let execution = get_client();
let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap();
let payload = ExecutionPayload {
state_root: Vector::from_iter(
hex_str_to_bytes(
"0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d",
)
.unwrap(),
),
..ExecutionPayload::default()
};
let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload.clone());
// Construct the proof database with the given client and payloads
let mut proof_db = ProofDB::new(Arc::new(execution), &payload, &payloads);
// Set the proof db accounts
let slot = U256::from(1337);
let mut accounts = HashMap::new();
let account = Account {
balance: U256::from(100),
code: hex_str_to_bytes("0x").unwrap(),
..Default::default()
};
accounts.insert(address, account);
proof_db.set_accounts(accounts);
// Get the account from the proof database
let storage_proof = proof_db.storage(address, slot);
// Check that the storage proof correctly returns a slot not found error
let expected_err: eyre::Report = SlotNotFoundError::new(H256::from_uint(&slot)).into();
assert_eq!(
expected_err.to_string(),
storage_proof.unwrap_err().to_string()
);
}
}