Skip to content

Commit

Permalink
feat(examples): custom precompile
Browse files Browse the repository at this point in the history
chore(docs): Update top-level examples readme with custom precompile command
  • Loading branch information
refcell committed Dec 4, 2023
1 parent 9464ae6 commit b610de1
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will
cargo run -p revm --features ethersdb --example fork_ref_transact
```

```shell
cargo run -p revm --features std,serde,optimism --example custom_precompile
```

# Used by:

* [Foundry](https://github.com/foundry-rs/foundry) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
Expand Down
5 changes: 5 additions & 0 deletions crates/revm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ name = "fork_ref_transact"
path = "../../examples/fork_ref_transact.rs"
required-features = ["ethersdb"]

[[example]]
name = "custom_precompile"
path = "../../examples/custom_precompile.rs"
required-features = ["optimism", "std", "serde"]

[[bench]]
name = "bench"
path = "benches/bench.rs"
Expand Down
132 changes: 132 additions & 0 deletions examples/custom_precompile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//! Custom Precompile Example
//!
//! This example walks through the process of creating a custom precompile and
//! executing it with an optimism deposit transaction.
use revm::inspectors::TracerEip3155;
use revm::primitives::{
address, b256, AccountInfo, Bytes, CanyonSpec, Env, OptimismFields, SpecId, TransactTo, U256,
};
use revm::{EVMImpl, InMemoryDB, JournaledState, Transact};
use revm_precompile::{Precompile, PrecompileResult, Precompiles};
use std::io::stdout;

const MINIMUM_GAS_LIMIT: u8 = 0xFF;

// A standard precompile function that returns opcodes to send 100 wei to a
// specific address.
fn precompile_func(_input: &[u8], _gas_limit: u64) -> PrecompileResult {
// CALL Stack Input
// [gas, to, value, arg_offset, arg_size, ret_offset, ret_size]
// [0xFF, caller, 100, 0, 0, 0, 0 ]
println!("---> Inside the precompile function!");

let zero = 0x00 as u8;
let push1 = 0x60 as u8;
let hundred = 0x64 as u8;
let push20 = 0x73 as u8;
let to = address!("deadca11deadca11deadca11deadca11deadca11");
let call = 0xF1 as u8;

let gas_used = 0;
let ret_bytes = [
&[
push1, zero, push1, zero, push1, zero, push1, zero, push1, hundred, push20,
],
&to.as_slice()[..],
&[push1, MINIMUM_GAS_LIMIT, call],
]
.concat();

println!("---> Precompile function returning: {:x?}", ret_bytes);
Ok((gas_used, ret_bytes))
}

fn main() -> anyhow::Result<()> {
// Build the custom precompile
let precompile_addr = address!("0000000000000000000000000000000000000420");
let precompile = revm_precompile::PrecompileWithAddress(
precompile_addr,
Precompile::Standard(precompile_func),
);
let precompiles = Precompiles {
inner: vec![precompile],
};

// Build the evm environment {block, cfg, tx}
let mut env = Env::default();
let caller = address!("deadca11deadca11deadca11deadca11deadca11");

env.block.number = U256::from(113055114);
env.block.coinbase = address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001");
env.block.timestamp = U256::from(1_629_814_800);
env.block.gas_limit = U256::from(30_000_000);
env.block.basefee = U256::from(1);
env.block.difficulty = U256::from(1);

env.cfg.chain_id = 10;
env.cfg.spec_id = SpecId::CANYON;
env.cfg.optimism = true;

env.tx.caller = caller;
env.tx.gas_limit = 30000000;
env.tx.gas_price = U256::from(50);

env.tx.transact_to = TransactTo::call(precompile_addr);
env.tx.value = U256::from(100);
env.tx.data = Bytes::from_static(&[]);
env.tx.nonce = Some(0);
env.tx.chain_id = Some(10);
env.tx.gas_priority_fee = Some(U256::from(1));

env.tx.optimism = OptimismFields {
source_hash: None,
mint: Some(100_u128),
is_system_transaction: Some(false),
enveloped_tx: Some(Bytes::from_static(&[])),
};

// Insert the calling account into the database
let mut db = InMemoryDB::default();
db.insert_account_info(
caller,
AccountInfo {
nonce: 0,
balance: U256::from_str_radix("FFFFFFFFFFFF", 16).unwrap(),
code_hash: b256!("0000000000000000000000000000000000000000000000000000000000000000"),
code: None,
},
);
let mut journal = JournaledState::new(SpecId::CANYON, vec![]);
journal
.initial_account_load(caller, &[U256::from(100)], &mut db)
.unwrap();

let mut inspector = TracerEip3155::new(Box::new(stdout()), true, true);

let mut evm = Box::new(EVMImpl::<CanyonSpec, InMemoryDB>::new_with_spec(
&mut db,
&mut env,
Some(&mut inspector),
precompiles,
));

// Preverify for sanity
if let Err(e) = evm.preverify_transaction_inner() {
println!("Preverification error: {:?}", e);
return Err(anyhow::anyhow!("Preverification failed"));
}
println!("Preverification succeeded. Transacting...\n");

// Transact
let result = evm.transact();
match result {
Ok(r) => println!("\nTransact result\n{:?}", r),
Err(e) => {
println!("Transact error: {:?}", e);
return Err(anyhow::anyhow!("Transact failed"));
}
}

Ok(())
}

0 comments on commit b610de1

Please sign in to comment.