feat: use helios as a library (#85)

* add root helios package

* fix revm

* copy blocktag when passing to funcs

* run all tests

* update readme

* update readme

* update readme
This commit is contained in:
Noah Citron 2022-11-03 19:36:14 -04:00 committed by GitHub
parent f605f009a7
commit ba08cc1a3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 560 additions and 303 deletions

View File

@ -21,7 +21,7 @@ jobs:
run: rustup target add aarch64-apple-darwin run: rustup target add aarch64-apple-darwin
- name: build - name: build
run: cargo build --all --release --target aarch64-apple-darwin run: cargo build --package cli --release --target aarch64-apple-darwin
- name: archive - name: archive
run: gtar -czvf "helios_darwin_arm64.tar.gz" -C ./target/aarch64-apple-darwin/release helios run: gtar -czvf "helios_darwin_arm64.tar.gz" -C ./target/aarch64-apple-darwin/release helios
@ -58,7 +58,7 @@ jobs:
run: rustup target add x86_64-apple-darwin run: rustup target add x86_64-apple-darwin
- name: build - name: build
run: cargo build --all --release --target x86_64-apple-darwin run: cargo build --package cli --release --target x86_64-apple-darwin
- name: archive - name: archive
run: gtar -czvf "helios_darwin_amd64.tar.gz" -C ./target/x86_64-apple-darwin/release helios run: gtar -czvf "helios_darwin_amd64.tar.gz" -C ./target/x86_64-apple-darwin/release helios
@ -101,7 +101,7 @@ jobs:
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: build - name: build
run: cargo build --all --release --target aarch64-unknown-linux-gnu run: cargo build --package cli --release --target aarch64-unknown-linux-gnu
- name: archive - name: archive
run: tar -czvf "helios_linux_arm64.tar.gz" -C ./target/aarch64-unknown-linux-gnu/release helios run: tar -czvf "helios_linux_arm64.tar.gz" -C ./target/aarch64-unknown-linux-gnu/release helios
@ -138,7 +138,7 @@ jobs:
run: rustup target add x86_64-unknown-linux-gnu run: rustup target add x86_64-unknown-linux-gnu
- name: build - name: build
run: cargo build --all --release --target x86_64-unknown-linux-gnu run: cargo build --package cli --release --target x86_64-unknown-linux-gnu
- name: archive - name: archive
run: tar -czvf "helios_linux_amd64.tar.gz" -C ./target/x86_64-unknown-linux-gnu/release helios run: tar -czvf "helios_linux_amd64.tar.gz" -C ./target/x86_64-unknown-linux-gnu/release helios

View File

@ -34,6 +34,7 @@ jobs:
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all
fmt: fmt:
name: fmt name: fmt

600
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
[package]
name = "helios"
version = "0.0.1"
edition = "2021"
[workspace] [workspace]
members = [ members = [
@ -9,5 +14,13 @@ members = [
"execution", "execution",
] ]
[dependencies]
client = { path = "./client" }
config = { path = "./config" }
common = { path = "./common" }
consensus = { path = "./consensus" }
execution = { path = "./execution" }
[patch.crates-io] [patch.crates-io]
ethers = { git = "https://github.com/ncitron/ethers-rs", branch = "fix-retry" } ethers = { git = "https://github.com/ncitron/ethers-rs", branch = "fix-retry" }

View File

@ -28,6 +28,8 @@ Helios will now run a local RPC server at `http://127.0.0.1:8545`.
`--rpc-port` or `-p` sets the port that the local RPC should run on. The default value is `8545`. `--rpc-port` or `-p` sets the port that the local RPC should run on. The default value is `8545`.
`--data-dir` or `d` sets the directory that Helios should use to store cached weak subjectivity checkpoints in. Each network only stores the latest checkpoint, which is just 32 bytes.
### Configuration Files ### Configuration Files
All configuration options can be set on a per-network level in `~/.helios/helios.toml`. Here is an example config file: All configuration options can be set on a per-network level in `~/.helios/helios.toml`. Here is an example config file:
``` ```
@ -42,5 +44,39 @@ execution_rpc = "https://eth-goerli.g.alchemy.com/v2/XXXXX"
checkpoint = "0xb5c375696913865d7c0e166d87bc7c772b6210dc9edf149f4c7ddc6da0dd4495" checkpoint = "0xb5c375696913865d7c0e166d87bc7c772b6210dc9edf149f4c7ddc6da0dd4495"
``` ```
### Using Helios as a Library
Helios can be imported into any Rust project. Helios requires the Rust nightly toolchain to compile.
```rust
use std::{str::FromStr, env};
use helios::{client::ClientBuilder, config::networks::Network, types::BlockTag};
use ethers::{types::Address, utils};
use eyre::Result;
#[tokio::main]
async fn main() -> Result<()> {
let untrusted_rpc_url = env::var("UNTRUSTED_RPC_URL")?;
let mut client = ClientBuilder::new()
.network(Network::MAINNET)
.consensus_rpc("https://www.lightclientdata.org")
.execution_rpc(&untrusted_rpc_url)
.build()?;
client.start().await?;
let head_block_num = client.get_block_number().await?;
let addr = Address::from_str("0x00000000219ab540356cBB839Cbe05303d7705Fa")?;
let block = BlockTag::Latest;
let balance = client.get_balance(&addr, block).await?;
println!("synced up to block: {}", head_block_num);
println!("balance of deposit contract: {}", utils::format_ether(balance));
Ok(())
}
```
## Contributing ## Contributing
All contributions to Helios are welcome. Before opening a PR, please submit an issue detailing the bug or feature. When opening a PR, please ensure that your contribution builds on the nightly rust toolchain, has been linted with `cargo fmt`, and contains tests when applicable. All contributions to Helios are welcome. Before opening a PR, please submit an issue detailing the bug or feature. When opening a PR, please ensure that your contribution builds on the nightly rust toolchain, has been linted with `cargo fmt`, and contains tests when applicable.

View File

@ -1,8 +1,15 @@
cargo-features = ["different-binary-name"]
[package] [package]
name = "helios" name = "cli"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[[bin]]
name = "cli"
filename = "helios"
path = "src/main.rs"
[dependencies] [dependencies]
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
clap = { version = "3.2.18", features = ["derive", "env"] } clap = { version = "3.2.18", features = ["derive", "env"] }

View File

@ -15,7 +15,7 @@ ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "cb08f18ca919cc1
blst = "0.3.10" blst = "0.3.10"
ethers = "1.0.0" ethers = "1.0.0"
jsonrpsee = { version = "0.15.1", features = ["full"] } jsonrpsee = { version = "0.15.1", features = ["full"] }
revm = "1.9.0" revm = "2.1.0"
bytes = "1.2.1" bytes = "1.2.1"
futures = "0.3.23" futures = "0.3.23"
toml = "0.5.9" toml = "0.5.9"

View File

@ -22,7 +22,7 @@ use crate::rpc::Rpc;
pub struct Client<DB: Database> { pub struct Client<DB: Database> {
node: Arc<RwLock<Node>>, node: Arc<RwLock<Node>>,
rpc: Option<Rpc>, rpc: Option<Rpc>,
db: DB, db: Option<DB>,
} }
impl Client<FileDB> { impl Client<FileDB> {
@ -38,7 +38,11 @@ impl Client<FileDB> {
}; };
let data_dir = config.data_dir.clone(); let data_dir = config.data_dir.clone();
let db = FileDB::new(data_dir.ok_or(eyre!("data dir not found"))?); let db = if let Some(dir) = data_dir {
Some(FileDB::new(dir))
} else {
None
};
Ok(Client { node, rpc, db }) Ok(Client { node, rpc, db })
} }
@ -114,29 +118,29 @@ impl ClientBuilder {
config.to_base_config() config.to_base_config()
}; };
let consensus_rpc = self.consensus_rpc.unwrap_or( let consensus_rpc = self.consensus_rpc.unwrap_or_else(|| {
self.config self.config
.as_ref() .as_ref()
.ok_or(eyre!("missing consensus rpc"))? .expect("missing consensus rpc")
.consensus_rpc .consensus_rpc
.clone(), .clone()
); });
let execution_rpc = self.execution_rpc.unwrap_or( let execution_rpc = self.execution_rpc.unwrap_or_else(|| {
self.config self.config
.as_ref() .as_ref()
.ok_or(eyre!("missing execution rpc"))? .expect("missing execution rpc")
.execution_rpc .execution_rpc
.clone(), .clone()
); });
let checkpoint = self.checkpoint.unwrap_or( let checkpoint = if let Some(checkpoint) = self.checkpoint {
self.config checkpoint
.as_ref() } else if let Some(config) = &self.config {
.ok_or(eyre!("missing checkpoint"))? config.checkpoint.clone()
.checkpoint } else {
.clone(), base_config.checkpoint
); };
let rpc_port = if self.rpc_port.is_some() { let rpc_port = if self.rpc_port.is_some() {
self.rpc_port self.rpc_port
@ -170,15 +174,17 @@ impl ClientBuilder {
impl<DB: Database> Client<DB> { impl<DB: Database> Client<DB> {
pub async fn start(&mut self) -> Result<()> { pub async fn start(&mut self) -> Result<()> {
self.rpc.as_mut().unwrap().start().await?; if let Some(rpc) = &mut self.rpc {
rpc.start().await?;
}
let node = self.node.clone(); let res = self.node.write().await.sync().await;
spawn(async move {
let res = node.write().await.sync().await;
if let Err(err) = res { if let Err(err) = res {
warn!("consensus error: {}", err); warn!("consensus error: {}", err);
} }
let node = self.node.clone();
spawn(async move {
loop { loop {
let res = node.write().await.advance().await; let res = node.write().await.advance().await;
if let Err(err) = res { if let Err(err) = res {
@ -202,13 +208,13 @@ impl<DB: Database> Client<DB> {
}; };
info!("saving last checkpoint hash"); info!("saving last checkpoint hash");
let res = self.db.save_checkpoint(checkpoint); let res = self.db.as_ref().map(|db| db.save_checkpoint(checkpoint));
if res.is_err() { if res.is_some() && res.unwrap().is_err() {
warn!("checkpoint save failed"); warn!("checkpoint save failed");
} }
} }
pub async fn call(&self, opts: &CallOpts, block: &BlockTag) -> Result<Vec<u8>> { pub async fn call(&self, opts: &CallOpts, block: BlockTag) -> Result<Vec<u8>> {
self.node.read().await.call(opts, block).await self.node.read().await.call(opts, block).await
} }
@ -216,15 +222,15 @@ impl<DB: Database> Client<DB> {
self.node.read().await.estimate_gas(opts).await self.node.read().await.estimate_gas(opts).await
} }
pub async fn get_balance(&self, address: &Address, block: &BlockTag) -> Result<U256> { pub async fn get_balance(&self, address: &Address, block: BlockTag) -> Result<U256> {
self.node.read().await.get_balance(address, block).await self.node.read().await.get_balance(address, block).await
} }
pub async fn get_nonce(&self, address: &Address, block: &BlockTag) -> Result<u64> { pub async fn get_nonce(&self, address: &Address, block: BlockTag) -> Result<u64> {
self.node.read().await.get_nonce(address, block).await self.node.read().await.get_nonce(address, block).await
} }
pub async fn get_code(&self, address: &Address, block: &BlockTag) -> Result<Vec<u8>> { pub async fn get_code(&self, address: &Address, block: BlockTag) -> Result<Vec<u8>> {
self.node.read().await.get_code(address, block).await self.node.read().await.get_code(address, block).await
} }
@ -269,7 +275,7 @@ impl<DB: Database> Client<DB> {
pub async fn get_block_by_number( pub async fn get_block_by_number(
&self, &self,
block: &BlockTag, block: BlockTag,
full_tx: bool, full_tx: bool,
) -> Result<Option<ExecutionBlock>> { ) -> Result<Option<ExecutionBlock>> {
self.node self.node

View File

@ -98,8 +98,8 @@ impl Node {
Ok(()) Ok(())
} }
pub async fn call(&self, opts: &CallOpts, block: &BlockTag) -> Result<Vec<u8>> { pub async fn call(&self, opts: &CallOpts, block: BlockTag) -> Result<Vec<u8>> {
self.check_blocktag_age(block)?; self.check_blocktag_age(&block)?;
let payload = self.get_payload(block)?; let payload = self.get_payload(block)?;
let mut evm = Evm::new(self.execution.clone(), payload.clone(), self.chain_id()); let mut evm = Evm::new(self.execution.clone(), payload.clone(), self.chain_id());
@ -109,29 +109,29 @@ impl Node {
pub async fn estimate_gas(&self, opts: &CallOpts) -> Result<u64> { pub async fn estimate_gas(&self, opts: &CallOpts) -> Result<u64> {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(&BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
let mut evm = Evm::new(self.execution.clone(), payload.clone(), self.chain_id()); let mut evm = Evm::new(self.execution.clone(), payload.clone(), self.chain_id());
evm.estimate_gas(opts).await evm.estimate_gas(opts).await
} }
pub async fn get_balance(&self, address: &Address, block: &BlockTag) -> Result<U256> { pub async fn get_balance(&self, address: &Address, block: BlockTag) -> Result<U256> {
self.check_blocktag_age(block)?; self.check_blocktag_age(&block)?;
let payload = self.get_payload(block)?; let payload = self.get_payload(block)?;
let account = self.execution.get_account(&address, None, payload).await?; let account = self.execution.get_account(&address, None, payload).await?;
Ok(account.balance) Ok(account.balance)
} }
pub async fn get_nonce(&self, address: &Address, block: &BlockTag) -> Result<u64> { pub async fn get_nonce(&self, address: &Address, block: BlockTag) -> Result<u64> {
self.check_blocktag_age(block)?; self.check_blocktag_age(&block)?;
let payload = self.get_payload(block)?; let payload = self.get_payload(block)?;
let account = self.execution.get_account(&address, None, payload).await?; let account = self.execution.get_account(&address, None, payload).await?;
Ok(account.nonce) Ok(account.nonce)
} }
pub async fn get_code(&self, address: &Address, block: &BlockTag) -> Result<Vec<u8>> { pub async fn get_code(&self, address: &Address, block: BlockTag) -> Result<Vec<u8>> {
self.check_blocktag_age(block)?; self.check_blocktag_age(&block)?;
let payload = self.get_payload(block)?; let payload = self.get_payload(block)?;
let account = self.execution.get_account(&address, None, payload).await?; let account = self.execution.get_account(&address, None, payload).await?;
@ -141,7 +141,7 @@ impl Node {
pub async fn get_storage_at(&self, address: &Address, slot: H256) -> Result<U256> { pub async fn get_storage_at(&self, address: &Address, slot: H256) -> Result<U256> {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(&BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
let account = self let account = self
.execution .execution
.get_account(address, Some(&[slot]), payload) .get_account(address, Some(&[slot]), payload)
@ -177,7 +177,7 @@ impl Node {
pub fn get_gas_price(&self) -> Result<U256> { pub fn get_gas_price(&self) -> Result<U256> {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(&BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
let base_fee = U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()); let base_fee = U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le());
let tip = U256::from(10_u64.pow(9)); let tip = U256::from(10_u64.pow(9));
Ok(base_fee + tip) Ok(base_fee + tip)
@ -192,16 +192,16 @@ impl Node {
pub fn get_block_number(&self) -> Result<u64> { pub fn get_block_number(&self) -> Result<u64> {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(&BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
Ok(payload.block_number) Ok(payload.block_number)
} }
pub async fn get_block_by_number( pub async fn get_block_by_number(
&self, &self,
block: &BlockTag, block: BlockTag,
full_tx: bool, full_tx: bool,
) -> Result<Option<ExecutionBlock>> { ) -> Result<Option<ExecutionBlock>> {
self.check_blocktag_age(block)?; self.check_blocktag_age(&block)?;
match self.get_payload(block) { match self.get_payload(block) {
Ok(payload) => self Ok(payload) => self
@ -247,7 +247,7 @@ impl Node {
self.consensus.last_checkpoint.clone() self.consensus.last_checkpoint.clone()
} }
fn get_payload(&self, block: &BlockTag) -> Result<&ExecutionPayload> { fn get_payload(&self, block: BlockTag) -> Result<&ExecutionPayload> {
match block { match block {
BlockTag::Latest => { BlockTag::Latest => {
let payload = self.payloads.last_key_value(); let payload = self.payloads.last_key_value();
@ -260,8 +260,8 @@ impl Node {
.1) .1)
} }
BlockTag::Number(num) => { BlockTag::Number(num) => {
let payload = self.payloads.get(num); let payload = self.payloads.get(&num);
payload.ok_or(BlockNotFoundError::new(BlockTag::Number(*num)).into()) payload.ok_or(BlockNotFoundError::new(BlockTag::Number(num)).into())
} }
} }
} }

View File

@ -111,7 +111,7 @@ impl EthRpcServer for RpcInner {
async fn get_balance(&self, address: &str, block: BlockTag) -> Result<String, Error> { async fn get_balance(&self, address: &str, block: BlockTag) -> Result<String, Error> {
let address = convert_err(Address::from_str(address))?; let address = convert_err(Address::from_str(address))?;
let node = self.node.read().await; let node = self.node.read().await;
let balance = convert_err(node.get_balance(&address, &block).await)?; let balance = convert_err(node.get_balance(&address, block).await)?;
Ok(balance.encode_hex()) Ok(balance.encode_hex())
} }
@ -119,7 +119,7 @@ impl EthRpcServer for RpcInner {
async fn get_transaction_count(&self, address: &str, block: BlockTag) -> Result<String, Error> { async fn get_transaction_count(&self, address: &str, block: BlockTag) -> Result<String, Error> {
let address = convert_err(Address::from_str(address))?; let address = convert_err(Address::from_str(address))?;
let node = self.node.read().await; let node = self.node.read().await;
let nonce = convert_err(node.get_nonce(&address, &block).await)?; let nonce = convert_err(node.get_nonce(&address, block).await)?;
Ok(nonce.encode_hex()) Ok(nonce.encode_hex())
} }
@ -127,14 +127,14 @@ impl EthRpcServer for RpcInner {
async fn get_code(&self, address: &str, block: BlockTag) -> Result<String, Error> { async fn get_code(&self, address: &str, block: BlockTag) -> Result<String, Error> {
let address = convert_err(Address::from_str(address))?; let address = convert_err(Address::from_str(address))?;
let node = self.node.read().await; let node = self.node.read().await;
let code = convert_err(node.get_code(&address, &block).await)?; let code = convert_err(node.get_code(&address, block).await)?;
Ok(hex::encode(code)) Ok(hex::encode(code))
} }
async fn call(&self, opts: CallOpts, block: BlockTag) -> Result<String, Error> { async fn call(&self, opts: CallOpts, block: BlockTag) -> Result<String, Error> {
let node = self.node.read().await; let node = self.node.read().await;
let res = convert_err(node.call(&opts, &block).await)?; let res = convert_err(node.call(&opts, block).await)?;
Ok(format!("0x{}", hex::encode(res))) Ok(format!("0x{}", hex::encode(res)))
} }
@ -176,7 +176,7 @@ impl EthRpcServer for RpcInner {
full_tx: bool, full_tx: bool,
) -> Result<Option<ExecutionBlock>, Error> { ) -> Result<Option<ExecutionBlock>, Error> {
let node = self.node.read().await; let node = self.node.read().await;
let block = convert_err(node.get_block_by_number(&block, full_tx).await)?; let block = convert_err(node.get_block_by_number(block, full_tx).await)?;
Ok(block) Ok(block)
} }

View File

@ -5,7 +5,7 @@ use ssz_rs::Vector;
pub type Bytes32 = Vector<u8, 32>; pub type Bytes32 = Vector<u8, 32>;
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum BlockTag { pub enum BlockTag {
Latest, Latest,
Finalized, Finalized,

View File

@ -15,7 +15,7 @@ hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" } ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
blst = "0.3.10" blst = "0.3.10"
ethers = "1.0.0" ethers = "1.0.0"
revm = "1.9.0" revm = "2.1.0"
bytes = "1.2.1" bytes = "1.2.1"
futures = "0.3.23" futures = "0.3.23"
toml = "0.5.9" toml = "0.5.9"

View File

@ -1,4 +1,4 @@
use std::{cmp, collections::HashMap, fmt::Display, str::FromStr, sync::Arc, thread}; use std::{cmp, collections::HashMap, str::FromStr, sync::Arc, thread};
use bytes::Bytes; use bytes::Bytes;
use ethers::{ use ethers::{
@ -6,7 +6,7 @@ use ethers::{
prelude::{Address, H160, H256, U256}, prelude::{Address, H160, H256, U256},
types::transaction::eip2930::AccessListItem, types::transaction::eip2930::AccessListItem,
}; };
use eyre::Result; use eyre::{Report, Result};
use futures::future::join_all; use futures::future::join_all;
use log::trace; use log::trace;
use revm::{AccountInfo, Bytecode, Database, Env, TransactOut, TransactTo, EVM}; use revm::{AccountInfo, Bytecode, Database, Env, TransactOut, TransactTo, EVM};
@ -44,10 +44,9 @@ impl<R: ExecutionRpc> Evm<R> {
self.evm.db.as_mut().unwrap().set_accounts(account_map); self.evm.db.as_mut().unwrap().set_accounts(account_map);
self.evm.env = self.get_env(opts); self.evm.env = self.get_env(opts);
let tx = self.evm.transact(); let tx = self.evm.transact().0;
let output = tx.1;
match tx.0 { match tx.exit_reason {
revm::Return::Revert => Err(eyre::eyre!("execution reverted")), revm::Return::Revert => Err(eyre::eyre!("execution reverted")),
revm::Return::OutOfGas => Err(eyre::eyre!("execution reverted: out of gas")), revm::Return::OutOfGas => Err(eyre::eyre!("execution reverted: out of gas")),
revm::Return::OutOfFund => Err(eyre::eyre!("not enough funds")), revm::Return::OutOfFund => Err(eyre::eyre!("not enough funds")),
@ -63,7 +62,7 @@ impl<R: ExecutionRpc> Evm<R> {
return Err(eyre::eyre!(err.clone())); return Err(eyre::eyre!(err.clone()));
} }
match output { match tx.out {
TransactOut::None => Err(eyre::eyre!("Invalid Call")), TransactOut::None => Err(eyre::eyre!("Invalid Call")),
TransactOut::Create(..) => Err(eyre::eyre!("Invalid Call")), TransactOut::Create(..) => Err(eyre::eyre!("Invalid Call")),
TransactOut::Call(bytes) => Ok(bytes.to_vec()), TransactOut::Call(bytes) => Ok(bytes.to_vec()),
@ -77,10 +76,10 @@ impl<R: ExecutionRpc> Evm<R> {
self.evm.db.as_mut().unwrap().set_accounts(account_map); self.evm.db.as_mut().unwrap().set_accounts(account_map);
self.evm.env = self.get_env(opts); self.evm.env = self.get_env(opts);
let tx = self.evm.transact(); let tx = self.evm.transact().0;
let gas = tx.2; let gas = tx.gas_used;
match tx.0 { match tx.exit_reason {
revm::Return::Revert => Err(eyre::eyre!("execution reverted")), revm::Return::Revert => Err(eyre::eyre!("execution reverted")),
revm::Return::OutOfGas => Err(eyre::eyre!("execution reverted: out of gas")), revm::Return::OutOfGas => Err(eyre::eyre!("execution reverted: out of gas")),
revm::Return::OutOfFund => Err(eyre::eyre!("not enough funds")), revm::Return::OutOfFund => Err(eyre::eyre!("not enough funds")),
@ -211,21 +210,11 @@ impl<R: ExecutionRpc> ProofDB<R> {
} }
} }
pub fn safe_unwrap<T: Default, E: Display>(&mut self, res: Result<T, E>) -> T {
match res {
Ok(value) => value,
Err(err) => {
self.error = Some(err.to_string());
T::default()
}
}
}
pub fn set_accounts(&mut self, accounts: HashMap<Address, Account>) { pub fn set_accounts(&mut self, accounts: HashMap<Address, Account>) {
self.accounts = accounts; self.accounts = accounts;
} }
fn get_account(&mut self, address: Address, slots: &[H256]) -> Account { fn get_account(&mut self, address: Address, slots: &[H256]) -> Result<Account> {
let execution = self.execution.clone(); let execution = self.execution.clone();
let addr = address.clone(); let addr = address.clone();
let payload = self.payload.clone(); let payload = self.payload.clone();
@ -237,14 +226,16 @@ impl<R: ExecutionRpc> ProofDB<R> {
runtime.block_on(account_fut) runtime.block_on(account_fut)
}); });
self.safe_unwrap(handle.join().unwrap()) handle.join().unwrap()
} }
} }
impl<R: ExecutionRpc> Database for ProofDB<R> { impl<R: ExecutionRpc> Database for ProofDB<R> {
fn basic(&mut self, address: H160) -> AccountInfo { type Error = Report;
fn basic(&mut self, address: H160) -> Result<Option<AccountInfo>, Report> {
if is_precompile(&address) { if is_precompile(&address) {
return AccountInfo::default(); return Ok(Some(AccountInfo::default()));
} }
trace!( trace!(
@ -254,18 +245,22 @@ impl<R: ExecutionRpc> Database for ProofDB<R> {
let account = match self.accounts.get(&address) { let account = match self.accounts.get(&address) {
Some(account) => account.clone(), Some(account) => account.clone(),
None => self.get_account(address, &[]), None => self.get_account(address, &[])?,
}; };
let bytecode = Bytecode::new_raw(Bytes::from(account.code.clone())); let bytecode = Bytecode::new_raw(Bytes::from(account.code.clone()));
AccountInfo::new(account.balance, account.nonce, bytecode) Ok(Some(AccountInfo::new(
account.balance,
account.nonce,
bytecode,
)))
} }
fn block_hash(&mut self, _number: U256) -> H256 { fn block_hash(&mut self, _number: U256) -> Result<H256, Report> {
H256::default() Ok(H256::default())
} }
fn storage(&mut self, address: H160, slot: U256) -> U256 { fn storage(&mut self, address: H160, slot: U256) -> Result<U256, Report> {
trace!( trace!(
"fetch evm state for address=0x{}, slot={}", "fetch evm state for address=0x{}, slot={}",
hex::encode(address.as_bytes()), hex::encode(address.as_bytes()),
@ -274,27 +269,27 @@ impl<R: ExecutionRpc> Database for ProofDB<R> {
let slot = H256::from_uint(&slot); let slot = H256::from_uint(&slot);
match self.accounts.get(&address) { Ok(match self.accounts.get(&address) {
Some(account) => match account.slots.get(&slot) { Some(account) => match account.slots.get(&slot) {
Some(slot) => slot.clone(), Some(slot) => slot.clone(),
None => self None => self
.get_account(address, &[slot]) .get_account(address, &[slot])?
.slots .slots
.get(&slot) .get(&slot)
.unwrap() .unwrap()
.clone(), .clone(),
}, },
None => self None => self
.get_account(address, &[slot]) .get_account(address, &[slot])?
.slots .slots
.get(&slot) .get(&slot)
.unwrap() .unwrap()
.clone(), .clone(),
} })
} }
fn code_by_hash(&mut self, _code_hash: H256) -> Bytecode { fn code_by_hash(&mut self, _code_hash: H256) -> Result<Bytecode, Report> {
panic!("should never be called"); Err(eyre::eyre!("should never be called"))
} }
} }

17
src/lib.rs Normal file
View File

@ -0,0 +1,17 @@
pub mod client {
pub use client::{database::FileDB, Client, ClientBuilder};
}
pub mod config {
pub use config::{networks, Config};
}
pub mod types {
pub use common::types::BlockTag;
}
pub mod errors {
pub use common::errors::*;
pub use consensus::errors::*;
pub use execution::errors::*;
}