Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Port more tests, prep for differential optimizations, multi-threaded …
…fevm
  • Loading branch information
WilfredTA committed Feb 1, 2022
commit b50dfe06224abd76c56b2267304454afbd4c6b2d
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 7 additions & 41 deletions crates/analyzer/tests/snapshots/analysis__uniswap.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4159,7 +4159,7 @@ note:
311 │ ╭ pub fn create_pair(self, token_a: address, token_b: address) -> address:
312 │ │ assert token_a != token_b, "UniswapV2: IDENTICAL_ADDRESSES"
313 │ │
314 │ │ let token0: address = token_a if token_a < token_b else token_b
314 │ │ let token0: address = token_a
· │
328 │ │ emit PairCreated(token0, token1, pair=address(pair), index=self.pair_counter)
329 │ │ return address(pair)
Expand Down Expand Up @@ -4197,9 +4197,9 @@ note:
note:
┌─ demos/uniswap.fe:314:21
314 │ let token0: address = token_a if token_a < token_b else token_b
314 │ let token0: address = token_a
│ ^^^^^^^ address
315 │ let token1: address = token_a if token_a > token_b else token_b
315 │ let token1: address = token_b
│ ^^^^^^^ address
·
319 │ let salt: u256 = keccak256((token0, token1).abi_encode())
Expand All @@ -4223,44 +4223,10 @@ note:
│ │
│ bool: Value
313 │
314 │ let token0: address = token_a if token_a < token_b else token_b
│ ^^^^^^^ ^^^^^^^ address: Value
│ │
│ address: Value

note:
┌─ demos/uniswap.fe:314:31
314 │ let token0: address = token_a if token_a < token_b else token_b
│ ^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^ address: Value
│ │ │
│ │ bool: Value
│ address: Value

note:
┌─ demos/uniswap.fe:314:31
314 │ let token0: address = token_a if token_a < token_b else token_b
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ address: Value
315 │ let token1: address = token_a if token_a > token_b else token_b
│ ^^^^^^^ ^^^^^^^ address: Value
│ │
│ address: Value

note:
┌─ demos/uniswap.fe:315:31
315 │ let token1: address = token_a if token_a > token_b else token_b
│ ^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^ address: Value
│ │ │
│ │ bool: Value
│ address: Value

note:
┌─ demos/uniswap.fe:315:31
315 │ let token1: address = token_a if token_a > token_b else token_b
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ address: Value
314 │ let token0: address = token_a
│ ^^^^^^^ address: Value
315 │ let token1: address = token_b
│ ^^^^^^^ address: Value
316 │ assert token0 != address(0), "UniswapV2: ZERO_ADDRESS"
│ ^^^^^^ ^ u256: Value
│ │
Expand Down
59 changes: 50 additions & 9 deletions crates/fevm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,46 @@ pub use revm::{
};
pub use primitive_types::{self, H160, U256};
use std::cell::RefCell;
use std::sync::Mutex;
pub mod conversion;
pub use conversion::*;

pub type CallResult = (Return, TransactOut, u64, Vec<Log>);
pub type GasUsed = u64;
pub type CallResult = (Return, TransactOut, GasUsed, Vec<Log>);

// Impl eq
pub struct TransactionResult {
pub return_code: Return,
pub return_data: TransactOut,
pub gas_used: u64,
pub logs: Vec<Log>
}

impl TransactionResult {
pub fn ret_eq(&self, other: TransactionResult) -> bool {
todo!()
}

pub fn data_eq(&self, other: TransactionResult) -> bool {
todo!()
}

pub fn gas_lt(&self, other: TransactionResult) -> bool {
todo!()
}

pub fn gas_eq(&self, other: TransactionResult) -> bool {
todo!()
}

pub fn gas_gt(&self, other: TransactionResult) -> bool {
todo!()
}


}



pub trait ToBeBytes {
fn to_be_bytes(&self) -> [u8; 32];
Expand All @@ -49,7 +85,7 @@ pub trait AsToken {
}

pub struct Fevm<'a>{
inner: RefCell<EVM<InMemoryDB>>,
inner: Mutex<EVM<InMemoryDB>>,
contracts: HashMap<&'a ContractId, Contract<'a>>

}
Expand All @@ -62,12 +98,17 @@ impl Fevm<'_> {
let mut vm = revm::new();
vm.database(InMemoryDB::default());
Self {
inner: RefCell::new(vm),
inner: Mutex::new(vm),
contracts: HashMap::new()
}
}

pub fn reset(&mut self) {
self.inner = Mutex::new(revm::new());
self.contracts = HashMap::new();
}
pub fn call(&self, input: Vec<u8>, addr: &Address, caller: &Caller) -> CallResult {
let mut vm = self.inner.borrow_mut();
let mut vm = self.inner.lock().unwrap();
vm.env.tx.caller = caller.0;
vm.env.tx.transact_to = TransactTo::Call(addr.clone());
vm.env.tx.data = input.into();
Expand All @@ -82,7 +123,7 @@ impl Fevm<'_> {
bytecode = constructor.encode_input(bytecode, init_params).unwrap()
}

let mut vm = self.inner.borrow_mut();
let mut vm = self.inner.lock().unwrap();
match vm.db().expect("DB not found").cache().get_key_value(deployer.as_ref()) {
Some(_) => {
vm.env.tx.caller = deployer.as_ref().clone();
Expand All @@ -106,11 +147,11 @@ impl Fevm<'_> {

pub fn create_account(&self, address: impl AsRef<Address>, balance: impl Into<U256>) {
let acc = AccountInfo::from_balance(balance.into());
self.inner.borrow_mut().db().unwrap().insert_cache(address.as_ref().clone(), acc);
self.inner.lock().unwrap().db().unwrap().insert_cache(address.as_ref().clone(), acc);
}

pub fn fund_account(&self, address: &Address, amt: impl Into<U256>) {
let mut vm = self.inner.borrow_mut();
let mut vm = self.inner.lock().unwrap();

let mut acc = vm.db().unwrap().cache().get(address)
.expect(format!("Cannot find account for address {:?}. Please create account first", address).as_str())
Expand All @@ -120,14 +161,14 @@ impl Fevm<'_> {
}

pub fn balance_of(&self, address: &Address) -> U256 {
match self.inner.borrow_mut().db().unwrap().cache().get(address) {
match self.inner.lock().unwrap().db().unwrap().cache().get(address) {
Some(acc) => acc.balance,
None => 0.into()
}
}

pub fn get_account(&self, address: &Address) -> Option<AccountInfo> {
self.inner.borrow_mut().db().unwrap().cache().get(address).cloned()
self.inner.lock().unwrap().db().unwrap().cache().get(address).cloned()
}

pub fn erase(&self, address: &Address) -> Address {
Expand Down
113 changes: 113 additions & 0 deletions crates/fevm/src/types/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ use bytes::Bytes;
use fe_common::files::FileStore;
use primitive_types::{H160, U256};
use revm::{TransactOut, Return};
use serde_json;
use crate::{Fevm, Caller, CallResult, Address};


#[derive(Debug)]
pub struct SolidityCompileError(Vec<serde_json::Value>);

#[derive(Hash, PartialEq, Eq, Clone, Debug)]
pub enum ContractId {
Name(String),
Expand Down Expand Up @@ -60,6 +65,24 @@ impl<'a> ContractBuilder<'a> {
self.address = Some(addr);
self
}

#[cfg(feature = "solc-backend")]
pub fn sol_fixture(mut self, fixture: &str, contract_name: &str) -> Contract<'a> {
let src = test_files::fixture(fixture)
.replace("\n", "")
.replace("\"", "\\\"");

let (bytecode, abi) = compile_solidity_contract(contract_name, &src, true)
.expect("Could not compile contract");

Contract {
vm: self.vm.unwrap(),
abi: ethabi::Contract::load(abi.as_bytes()).expect("Unable to generate solidity contract abi"),
address: self.address,
code: ContractCode::Bytes(hex::decode(bytecode).expect("Failed to decode Solidity bytecode"))
}

}
#[cfg(feature = "solc-backend")]
pub fn fixture(
mut self,
Expand Down Expand Up @@ -112,6 +135,18 @@ pub struct Contract<'a> {


impl Contract<'_> {

pub fn capture_call(&self, name: &str, input: &[ethabi::Token], caller: &Caller) -> CallResult {
if self.address.is_none() {
panic!("Please deploy contract prior to making calls!");
}
let function = &self.abi.functions[name][0];
let input = self.build_calldata(name, input);


self.vm.call(input, self.address.as_ref().unwrap(), caller)

}
pub fn call(&self, name: &str, input: &[ethabi::Token], caller: &Caller) -> Option<ethabi::Token> {
if self.address.is_none() {
panic!("Please deploy contract prior to making calls!");
Expand Down Expand Up @@ -164,3 +199,81 @@ impl Contract<'_> {
}


#[cfg(feature = "solc-backend")]
pub fn compile_solidity_contract(
name: &str,
solidity_src: &str,
optimized: bool,
) -> Result<(String, String), SolidityCompileError> {
let solc_config = r#"
{
"language": "Solidity",
"sources": { "input.sol": { "content": "{src}" } },
"settings": {
"optimizer": { "enabled": {optimizer_enabled} },
"outputSelection": { "*": { "*": ["*"], "": [ "*" ] } }
}
}
"#;
let solc_config = solc_config
.replace("{src}", solidity_src)
.replace("{optimizer_enabled}", &optimized.to_string());

let raw_output = solc::compile(&solc_config);

let output: serde_json::Value =
serde_json::from_str(&raw_output).expect("Unable to compile contract");

if output["errors"].is_array() {
let severity: serde_json::Value =
serde_json::to_value("error").expect("Unable to convert into serde value type");
let errors: serde_json::Value = output["errors"]
.as_array()
.unwrap()
.iter()
.cloned()
.filter_map(|err| {
if err["severity"] == severity {
Some(err["formattedMessage"].clone())
} else {
None
}
})
.collect();

let errors_list = errors
.as_array()
.unwrap_or_else(|| panic!("Unable to parse error properly"));
if !errors_list.is_empty() {
return Err(SolidityCompileError(errors_list.clone()));
}
}

let bytecode = output["contracts"]["input.sol"][name]["evm"]["bytecode"]["object"]
.to_string()
.replace("\"", "");

let abi = if let serde_json::Value::Array(data) = &output["contracts"]["input.sol"][name]["abi"]
{
data.iter()
.cloned()
.filter(|val| {
// ethabi doesn't yet support error types so we just filter them out for now
// https://github.com/rust-ethereum/ethabi/issues/225
val["type"] != "error"
})
.collect::<Vec<_>>()
} else {
vec![]
};

let abi = serde_json::Value::Array(abi).to_string();

if [&bytecode, &abi].iter().any(|val| val == &"null") {
return Err(SolidityCompileError(vec![serde_json::Value::String(
String::from("Bytecode not found"),
)]));
}

Ok((bytecode, abi))
}
Loading