Skip to content

Commit

Permalink
feat(zint): use full evm for deployment tests
Browse files Browse the repository at this point in the history
  • Loading branch information
clearloop committed Dec 18, 2023
1 parent 55e30ac commit 8510a6c
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 2,016 deletions.
2,049 changes: 100 additions & 1,949 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ clap = "4.4.11"
color-eyre = "0.6.2"
colored = "2.1.0"
etc = "0.1.16"
ethers = "2.0.11"
hex = "0.4.3"
indexmap = "2.1.0"
paste = "1.0.14"
Expand All @@ -43,7 +42,6 @@ sha3 = "0.10.8"
smallvec = "1.11.2"
syn = { version = "2.0.41", features = [ "full" ] }
thiserror = "1.0.51"
tokio = "1.35.0"
toml = "0.8.8"
toml_edit = "0.21.0"
tracing = "0.1.40"
Expand Down Expand Up @@ -97,5 +95,4 @@ zink-codegen.workspace = true
anyhow.workspace = true
paste.workspace = true
filetests.workspace = true
zint = { workspace = true, features = [ "ethers" ] }
tokio = { workspace = true, features = [ "macros" ] }
zint.workspace = true
3 changes: 3 additions & 0 deletions codegen/src/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::{Buffer, CodeGen, Function, JumpTable, MacroAssembler, Result, ToLSBy
/// - `INIT_LOGIC`
/// - `RETURN RUNTIME_BYTECODE`
/// - `RUNTIME_BYTECODE`
///
/// TODO: introduce ABI for constructor
pub struct Constructor {
/// Code buffer.
pub masm: MacroAssembler,
Expand Down Expand Up @@ -46,6 +48,7 @@ impl Constructor {
})
}

/// Returns the length of instructions.
fn return_instr_length(init_code_length: usize, runtime_bytecode_length: usize) -> usize {
let mut expected_length =
runtime_bytecode_length.to_ls_bytes().len() + init_code_length.to_ls_bytes().len() + 3;
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Compiler {
return Err(Error::BufferOverflow(self.buffer.len()));
}

self.abi = dispatcher.abi;
self.abi.append(&mut dispatcher.abi);
Ok(())
}

Expand Down
41 changes: 17 additions & 24 deletions examples/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,21 @@ pub fn constructor(value: i32) {
#[cfg(not(target_arch = "wasm32"))]
fn main() {}

#[cfg(test)]
mod tests {
use zint::{ethers::abi::Abi, Contract, Ethers};

#[tokio::test]
#[allow(deprecated)]
async fn constructor() -> zint::Result<()> {
let api = Ethers::anvil()?;
let contract = Contract::search("constructor")?
.constructor(true)
.compile()?;

// let info = contract.execute([])?;
// println!("{:?}", info);

let abi: Abi = Abi::load(&*contract.json_abi()?.as_bytes())
.map_err(|e| anyhow::anyhow!("Failed to load abi {e}"))?;
let factory = api.factory(abi, contract.bytecode)?;
let contract = factory.deploy(())?.legacy().send().await?;
let r = contract.method::<(), i32>("get", ())?.call().await?;

assert_eq!(r, 1);
Ok(())
}
#[test]
fn deploy() -> anyhow::Result<()> {
use zint::{Bytes32, Contract, EVM};

let contract = Contract::search("constructor")?
.without_dispatcher()
.constructor(true)
.compile()?;

let mut evm = EVM::default();
let mut info = evm.deploy(&contract.bytecode)?;
info = evm
.calldata(&contract.encode(&["get()"])?)
.call(info.address)?;

assert_eq!(info.ret, 1.to_bytes32());
Ok(())
}
1 change: 0 additions & 1 deletion zint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ repository.workspace = true
anyhow.workspace = true
cargo_metadata.workspace = true
etc.workspace = true
ethers = { workspace = true, optional = true }
hex.workspace = true
revm.workspace = true
serde = { workspace = true, features = [ "derive" ] }
Expand Down
14 changes: 11 additions & 3 deletions zint/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ impl Contract {
Ok(Self::new(fs::read(wasm)?))
}

/// Execute the contract.
pub fn execute<Param>(&mut self, inputs: impl AsRef<[Param]>) -> Result<Info>
/// Encode call data
pub fn encode<Param>(&self, inputs: impl AsRef<[Param]>) -> Result<Vec<u8>>
where
Param: Bytes32,
{
Expand All @@ -152,6 +152,14 @@ impl Contract {
calldata.extend_from_slice(&input.to_bytes32());
}

EVM::interp(&self.bytecode, &calldata)
Ok(calldata)
}

/// Execute the contract.
pub fn execute<Param>(&mut self, inputs: impl AsRef<[Param]>) -> Result<Info>
where
Param: Bytes32,
{
EVM::interp(&self.bytecode, &self.encode(inputs)?)
}
}
96 changes: 67 additions & 29 deletions zint/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use anyhow::{anyhow, Result};
use revm::{
primitives::{
AccountInfo, Bytecode, Bytes, Eval, ExecutionResult, Halt, Log, ResultAndState, TransactTo,
U256,
AccountInfo, Bytecode, Bytes, CreateScheme, Eval, ExecutionResult, Halt, Log, Output,
ResultAndState, TransactTo, U256,
},
InMemoryDB, EVM as REVM,
};
Expand Down Expand Up @@ -38,21 +38,31 @@ impl Default for EVM {
}

impl EVM {
fn db(&mut self) -> &mut InMemoryDB {
self.inner
.db()
.unwrap_or_else(|| unreachable!("provided on initialization"))
/// Interpret runtime bytecode with provided arguments
pub fn interp(runtime_bytecode: &[u8], input: &[u8]) -> Result<Info> {
Self::default()
.contract(runtime_bytecode)
.calldata(input)
.call(CONTRACT)
}

/// Send transaction to the provided address.
pub fn call(&mut self, to: [u8; 20]) -> Result<Info> {
self.inner.env.tx.transact_to = TransactTo::Call(to.into());
let to = TransactTo::Call(to.into());
self.inner.env.tx.transact_to = to.clone();
let result = self.inner.transact_ref().map_err(|e| anyhow!(e))?;
(result, to).try_into()
}

/// Interpret runtime bytecode with provided arguments
pub fn deploy(&mut self, bytecode: &[u8]) -> Result<Info> {
self.calldata(bytecode);
self.inner.env.tx.transact_to = TransactTo::Create(CreateScheme::Create);
self.inner.transact_commit()?.try_into()
}

/// Fill the calldata of the present transaction.
pub fn calldata(mut self, input: &[u8]) -> Self {
pub fn calldata(&mut self, input: &[u8]) -> &mut Self {
self.inner.env.tx.data = Bytes::copy_from_slice(input);
self
}
Expand All @@ -72,18 +82,18 @@ impl EVM {
self
}

/// Interpret runtime bytecode with provided arguments
pub fn interp(runtime_bytecode: &[u8], input: &[u8]) -> Result<Info> {
Self::default()
.contract(runtime_bytecode)
.calldata(input)
.call(CONTRACT)
fn db(&mut self) -> &mut InMemoryDB {
self.inner
.db()
.unwrap_or_else(|| unreachable!("provided on initialization"))
}
}

/// Interp execution result info.
#[derive(Debug, Default)]
pub struct Info {
/// the created contract address if any.
pub address: [u8; 20],
/// Gas spent.
pub gas: u64,
/// Return value.
Expand All @@ -96,13 +106,14 @@ pub struct Info {
pub halt: Option<Halt>,
}

impl TryFrom<(ResultAndState, [u8; 20])> for Info {
impl TryFrom<ExecutionResult> for Info {
type Error = anyhow::Error;

fn try_from((res, address): (ResultAndState, [u8; 20])) -> Result<Self> {
let ResultAndState { result, state } = res;
let mut info: Self = Default::default();
info.gas = result.gas_used();
fn try_from(result: ExecutionResult) -> Result<Self> {
let mut info = Info {
gas: result.gas_used(),
..Default::default()
};

match result {
ExecutionResult::Success {
Expand All @@ -112,25 +123,52 @@ impl TryFrom<(ResultAndState, [u8; 20])> for Info {
..
} => {
if reason != Eval::Return {
return Err(anyhow!("Transaction not returned: {reason:?}."));
return Err(anyhow!("Transaction is not returned: {reason:?}"));
}

info.logs = logs;
info.ret = output.into_data().to_vec();

let ret = match output {
Output::Call(bytes) => bytes,
Output::Create(bytes, maybe_address) => {
let Some(address) = maybe_address else {
return Err(anyhow!(
"No contract created after the creation transaction."
));
};

info.address = *address.as_ref();
bytes
}
};

info.ret = ret.into();
}
ExecutionResult::Halt { reason, .. } => {
info.halt = Some(reason);
}
_ => unreachable!("This should never happen"),
}

info.storage = state
.get(&address)
.ok_or_else(|| anyhow!("no state found for account 0x{}", hex::encode(&address)))?
.storage
.iter()
.map(|(k, v)| (*k, v.present_value))
.collect();
Ok(info)
}
}

impl TryFrom<(ResultAndState, TransactTo)> for Info {
type Error = anyhow::Error;

fn try_from((res, to): (ResultAndState, TransactTo)) -> Result<Self> {
let ResultAndState { result, state } = res;
let mut info = Self::try_from(result)?;

if let TransactTo::Call(address) = to {
info.storage = state
.get(&address)
.ok_or_else(|| anyhow!("no state found for account 0x{}", hex::encode(address)))?
.storage
.iter()
.map(|(k, v)| (*k, v.present_value))
.collect();
}

Ok(info)
}
Expand Down
9 changes: 4 additions & 5 deletions zint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@ pub use self::{
evm::{Info, EVM},
result::Result,
};
pub use hex;
pub use revm::primitives::{Halt, OutOfGasError, U256};
use tracing_subscriber::EnvFilter;

#[cfg(feature = "ethers")]
pub use api::*;
pub use tracing as log;
pub use zabi::selector::keccak256;

/// Set up the logger.
pub fn setup_logger() {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.without_time()
.compact()
.try_init()
Expand Down

0 comments on commit 8510a6c

Please sign in to comment.