#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(non_snake_case)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(missing_docs)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::useless_vec)]
#[macro_use]
pub mod macros;
#[macro_use]
pub mod error;
#[macro_use]
pub mod bytecode;
pub mod evm_types;
pub mod geth_types;
pub mod keccak;
pub mod sign_types;
pub use keccak::{keccak256, Keccak};
pub use bytecode::Bytecode;
pub use error::Error;
use halo2_proofs::{
halo2curves::{
bn256::{Fq, Fr},
ff::{Field as Halo2Field, FromUniformBytes, PrimeField},
},
plonk::Expression,
};
use crate::evm_types::{memory::Memory, stack::Stack, storage::Storage, OpcodeId};
use ethers_core::types;
pub use ethers_core::{
abi::ethereum_types::{BigEndianHash, U512},
types::{
transaction::{
eip2930::{AccessList, AccessListItem},
response::Transaction,
},
Address, Block, Bytes, Signature, H160, H256, H64, U256, U64,
},
};
use serde::{de, Deserialize, Serialize};
use std::{collections::HashMap, fmt, str::FromStr};
pub trait OpsIdentity {
type Output;
fn zero() -> Self::Output;
fn one() -> Self::Output;
}
impl<F: Field> OpsIdentity for Expression<F> {
type Output = Expression<F>;
fn zero() -> Self::Output {
Expression::Constant(F::ZERO)
}
fn one() -> Self::Output {
Expression::Constant(F::ONE)
}
}
impl OpsIdentity for Fr {
type Output = Fr;
fn zero() -> Self::Output {
Fr::zero()
}
fn one() -> Self::Output {
Fr::one()
}
}
impl OpsIdentity for Fq {
type Output = Fq;
fn zero() -> Self::Output {
Fq::zero()
}
fn one() -> Self::Output {
Fq::one()
}
}
pub trait Field:
Halo2Field + PrimeField<Repr = [u8; 32]> + FromUniformBytes<64> + Ord + OpsIdentity<Output = Self>
{
fn get_lower_128(&self) -> u128 {
let bytes = self.to_repr();
bytes[..16]
.iter()
.rev()
.fold(0u128, |acc, value| acc * 256u128 + *value as u128)
}
fn get_lower_32(&self) -> u32 {
let bytes = self.to_repr();
bytes[..4]
.iter()
.rev()
.fold(0u32, |acc, value| acc * 256u32 + *value as u32)
}
}
impl Field for Fr {}
impl Field for Fq {}
pub trait ToScalar<F> {
fn to_scalar(&self) -> Option<F>;
}
pub trait ToWord {
fn to_word(&self) -> Word;
}
pub trait ToAddress {
fn to_address(&self) -> Address;
}
pub trait ToBigEndian {
fn to_be_bytes(&self) -> [u8; 32];
}
pub trait ToLittleEndian {
fn to_le_bytes(&self) -> [u8; 32];
}
#[allow(clippy::all)]
mod uint_types {
uint::construct_uint! {
pub struct DebugU256(4);
}
}
pub use uint_types::DebugU256;
impl<'de> Deserialize<'de> for DebugU256 {
fn deserialize<D>(deserializer: D) -> Result<DebugU256, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
DebugU256::from_str(&s).map_err(de::Error::custom)
}
}
impl<F: Field> ToScalar<F> for DebugU256 {
fn to_scalar(&self) -> Option<F> {
let mut bytes = [0u8; 32];
self.to_little_endian(&mut bytes);
F::from_repr(bytes).into()
}
}
impl ToBigEndian for DebugU256 {
fn to_be_bytes(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
self.to_big_endian(&mut bytes);
bytes
}
}
impl ToWord for DebugU256 {
fn to_word(&self) -> Word {
U256(self.0)
}
}
pub type Word = U256;
impl ToBigEndian for U256 {
fn to_be_bytes(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
self.to_big_endian(&mut bytes);
bytes
}
}
impl ToLittleEndian for U256 {
fn to_le_bytes(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
self.to_little_endian(&mut bytes);
bytes
}
}
impl<F: Field> ToScalar<F> for U256 {
fn to_scalar(&self) -> Option<F> {
let mut bytes = [0u8; 32];
self.to_little_endian(&mut bytes);
F::from_repr(bytes).into()
}
}
impl ToAddress for U256 {
fn to_address(&self) -> Address {
Address::from_slice(&self.to_be_bytes()[12..])
}
}
pub type Hash = types::H256;
impl ToWord for Hash {
fn to_word(&self) -> Word {
Word::from(self.as_bytes())
}
}
impl ToWord for Address {
fn to_word(&self) -> Word {
let mut bytes = [0u8; 32];
bytes[32 - Self::len_bytes()..].copy_from_slice(self.as_bytes());
Word::from(bytes)
}
}
impl ToWord for bool {
fn to_word(&self) -> Word {
if *self {
Word::one()
} else {
Word::zero()
}
}
}
impl ToWord for u64 {
fn to_word(&self) -> Word {
Word::from(*self)
}
}
impl ToWord for u128 {
fn to_word(&self) -> Word {
Word::from(*self)
}
}
impl ToWord for usize {
fn to_word(&self) -> Word {
u64::try_from(*self)
.expect("usize bigger than u64")
.to_word()
}
}
impl ToWord for i32 {
fn to_word(&self) -> Word {
let value = Word::from(self.unsigned_abs() as u64);
if self.is_negative() {
value.overflowing_neg().0
} else {
value
}
}
}
impl ToWord for U64 {
fn to_word(&self) -> Word {
self.as_u64().into()
}
}
impl ToWord for Word {
fn to_word(&self) -> Word {
*self
}
}
impl<F: Field> ToScalar<F> for Address {
fn to_scalar(&self) -> Option<F> {
let mut bytes = [0u8; 32];
bytes[32 - Self::len_bytes()..].copy_from_slice(self.as_bytes());
bytes.reverse();
F::from_repr(bytes).into()
}
}
impl<F: Field> ToScalar<F> for bool {
fn to_scalar(&self) -> Option<F> {
self.to_word().to_scalar()
}
}
impl<F: Field> ToScalar<F> for u64 {
fn to_scalar(&self) -> Option<F> {
Some(F::from(*self))
}
}
impl<F: Field> ToScalar<F> for usize {
fn to_scalar(&self) -> Option<F> {
u64::try_from(*self).ok().map(F::from)
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
pub struct StorageProof {
pub key: U256,
pub value: U256,
pub proof: Vec<Bytes>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EIP1186ProofResponse {
pub address: Address,
pub balance: U256,
pub code_hash: H256,
pub nonce: U64,
pub storage_hash: H256,
pub account_proof: Vec<Bytes>,
pub storage_proof: Vec<StorageProof>,
}
#[derive(Deserialize)]
#[doc(hidden)]
struct GethExecStepInternal {
pc: u64,
op: OpcodeId,
gas: u64,
#[serde(default)]
refund: u64,
#[serde(rename = "gasCost")]
gas_cost: u64,
depth: u16,
error: Option<String>,
stack: Vec<DebugU256>,
#[serde(default)]
memory: Vec<DebugU256>,
#[serde(default)]
storage: HashMap<DebugU256, DebugU256>,
}
#[derive(Clone, Eq, PartialEq, Serialize)]
#[doc(hidden)]
pub struct GethExecStep {
pub pc: u64,
pub op: OpcodeId,
pub gas: u64,
pub gas_cost: u64,
pub refund: u64,
pub depth: u16,
pub error: Option<String>,
pub stack: Stack,
pub memory: Memory,
pub storage: Storage,
}
pub(crate) struct DebugByte(pub(crate) u8);
impl fmt::Debug for DebugByte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:02x}", self.0))
}
}
pub(crate) struct DebugWord<'a>(pub(crate) &'a Word);
impl<'a> fmt::Debug for DebugWord<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("0x{:x}", self.0))
}
}
impl fmt::Debug for GethExecStep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Step")
.field("pc", &format_args!("0x{:04x}", self.pc))
.field("op", &self.op)
.field("gas", &format_args!("{}", self.gas))
.field("gas_cost", &format_args!("{}", self.gas_cost))
.field("depth", &self.depth)
.field("error", &self.error)
.field("stack", &self.stack)
.field("storage", &self.storage)
.finish()
}
}
impl<'de> Deserialize<'de> for GethExecStep {
fn deserialize<D>(deserializer: D) -> Result<GethExecStep, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = GethExecStepInternal::deserialize(deserializer)?;
Ok(Self {
pc: s.pc,
op: s.op,
gas: s.gas,
refund: s.refund,
gas_cost: s.gas_cost,
depth: s.depth,
error: s.error,
stack: Stack(s.stack.iter().map(|dw| dw.to_word()).collect::<Vec<Word>>()),
memory: Memory::from(
s.memory
.iter()
.map(|dw| dw.to_word())
.collect::<Vec<Word>>(),
),
storage: Storage(
s.storage
.iter()
.map(|(k, v)| (k.to_word(), v.to_word()))
.collect(),
),
})
}
}
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
#[doc(hidden)]
pub struct ResultGethExecTraces(pub Vec<ResultGethExecTrace>);
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
#[doc(hidden)]
pub struct ResultGethExecTrace {
pub result: GethExecTrace,
}
#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
pub struct GethExecTrace {
pub gas: u64,
pub failed: bool,
#[serde(default)]
pub invalid: bool,
#[serde(rename = "returnValue")]
pub return_value: String,
#[serde(rename = "structLogs")]
pub struct_logs: Vec<GethExecStep>,
}
#[macro_export]
macro_rules! address {
($addr_hex:expr) => {{
use std::str::FromStr;
$crate::Address::from_str(&$addr_hex).expect("invalid hex Address")
}};
}
#[macro_export]
macro_rules! word {
($word_hex:expr) => {
$crate::Word::from_str_radix(&$word_hex, 16).expect("invalid hex Word")
};
}
#[macro_export]
macro_rules! word_map {
() => {
std::collections::HashMap::new()
};
($($key_hex:expr => $value_hex:expr),*) => {
{
std::collections::HashMap::from_iter([(
$(word!($key_hex), word!($value_hex)),*
)])
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::evm_types::{memory::Memory, opcode_ids::OpcodeId, stack::Stack};
#[test]
fn deserialize_geth_exec_trace2() {
let trace_json = r#"
{
"gas": 26809,
"failed": false,
"returnValue": "",
"structLogs": [
{
"pc": 0,
"op": "PUSH1",
"gas": 22705,
"gasCost": 3,
"refund": 0,
"depth": 1,
"stack": []
},
{
"pc": 163,
"op": "SLOAD",
"gas": 5217,
"gasCost": 2100,
"refund": 0,
"depth": 1,
"stack": [
"0x1003e2d2",
"0x2a",
"0x0"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "000000000000000000000000000000000000000000000000000000000000006f"
},
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
]
},
{
"pc": 189,
"op": "KECCAK256",
"gas": 178805,
"gasCost": 42,
"refund": 0,
"depth": 1,
"stack": [
"0x3635c9adc5dea00000",
"0x40",
"0x0"
],
"memory": [
"000000000000000000000000b8f67472dcc25589672a61905f7fd63f09e5d470",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000000000000000000000a0",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000003635c9adc5dea00000",
"00000000000000000000000000000000000000000000003635c9adc5dea00000"
]
}
]
}
"#;
let trace: GethExecTrace =
serde_json::from_str(trace_json).expect("json-deserialize GethExecTrace");
assert_eq!(
trace,
GethExecTrace {
gas: 26809,
failed: false,
invalid: false,
return_value: "".to_owned(),
struct_logs: vec![
GethExecStep {
pc: 0,
op: OpcodeId::PUSH1,
gas: 22705,
refund: 0,
gas_cost: 3,
depth: 1,
error: None,
stack: Stack::new(),
storage: Storage(word_map!()),
memory: Memory::new(),
},
GethExecStep {
pc: 163,
op: OpcodeId::SLOAD,
gas: 5217,
refund: 0,
gas_cost: 2100,
depth: 1,
error: None,
stack: Stack(vec![word!("0x1003e2d2"), word!("0x2a"), word!("0x0")]),
storage: Storage(word_map!("0x0" => "0x6f")),
memory: Memory::from(vec![word!("0x0"), word!("0x0"), word!("0x080")]),
},
GethExecStep {
pc: 189,
op: OpcodeId::SHA3,
gas: 178805,
refund: 0,
gas_cost: 42,
depth: 1,
error: None,
stack: Stack(vec![
word!("0x3635c9adc5dea00000"),
word!("0x40"),
word!("0x0")
]),
storage: Storage(word_map!()),
memory: Memory::from(vec![
word!(
"000000000000000000000000b8f67472dcc25589672a61905f7fd63f09e5d470"
),
word!(
"0000000000000000000000000000000000000000000000000000000000000000"
),
word!(
"00000000000000000000000000000000000000000000000000000000000000a0"
),
word!(
"0000000000000000000000000000000000000000000000000000000000000000"
),
word!(
"00000000000000000000000000000000000000000000003635c9adc5dea00000"
),
word!(
"00000000000000000000000000000000000000000000003635c9adc5dea00000"
),
]),
}
],
}
);
}
}
#[cfg(test)]
mod eth_types_test {
use super::*;
use crate::{Error, Word};
use std::str::FromStr;
#[test]
fn address() {
assert_eq!(
Address::from_str("0x9a0C63EBb78B35D7c209aFbD299B056098b5439b").unwrap(),
Address::from([
154, 12, 99, 235, 183, 139, 53, 215, 194, 9, 175, 189, 41, 155, 5, 96, 152, 181,
67, 155
])
);
assert_eq!(
Address::from_str("9a0C63EBb78B35D7c209aFbD299B056098b5439b").unwrap(),
Address::from([
154, 12, 99, 235, 183, 139, 53, 215, 194, 9, 175, 189, 41, 155, 5, 96, 152, 181,
67, 155
])
);
assert_eq!(
&format!(
"{:?}",
Address::from_str("0x9a0C63EBb78B35D7c209aFbD299B056098b543")
),
"Err(Invalid input length)",
);
assert_eq!(
&format!(
"{:?}",
Address::from_str("0x9a0C63EBb78B35D7c209aFbD299B056098b543XY")
),
"Err(Invalid character 'X' at position 38)",
);
assert_eq!(
Address::from_str("0x0000000000000000000000000000000000000001")
.unwrap()
.to_word(),
Word::from(1u32),
)
}
#[test]
fn word_bytes_serialization_trip() -> Result<(), Error> {
let first_usize = 64536usize;
assert_eq!(
Word::from_little_endian(&first_usize.to_le_bytes()),
Word::from_big_endian(&first_usize.to_be_bytes())
);
let addr = Word::from_little_endian(&first_usize.to_le_bytes());
assert_eq!(addr, Word::from(first_usize));
let mut le_obtained_usize = [0u8; 32];
addr.to_little_endian(&mut le_obtained_usize);
let mut le_array = [0u8; 8];
le_array.copy_from_slice(&le_obtained_usize[0..8]);
let mut be_array = [0u8; 8];
let be_obtained_usize = addr.to_be_bytes();
be_array.copy_from_slice(&be_obtained_usize[24..32]);
assert_eq!(first_usize, usize::from_le_bytes(le_array));
assert_eq!(first_usize, usize::from_be_bytes(be_array));
Ok(())
}
#[test]
fn word_from_str() -> Result<(), Error> {
let word_str = "000000000000000000000000000000000000000000000000000c849c24f39248";
let word_from_u128 = Word::from(3523505890234952u128);
let word_from_str = Word::from_str(word_str).unwrap();
assert_eq!(word_from_u128, word_from_str);
Ok(())
}
#[test]
fn creation_tx_into_tx_req() -> Result<(), Error> {
let tx = &geth_types::Transaction {
to: None,
..Default::default()
};
let req: ethers_core::types::TransactionRequest = tx.into();
assert_eq!(req.to, None);
Ok(())
}
}