Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade EVM #632

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
543 changes: 516 additions & 27 deletions Cargo.lock

Large diffs are not rendered by default.

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
30 changes: 30 additions & 0 deletions crates/fevm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "fevm"
version = "0.1.0"
authors = ["The Fe Developers <[email protected]>"]
edition = "2021"
license = "GPL-3.0-or-later"
repository = "https://github.com/ethereum/fe"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bytes = { version = "1.1.0", default-features = false }
ethabi = "16.0.0"
hex = "0.4.3"
primitive-types = { version = "0.10.1", default-features = false, features = ["rlp"] }
serde_json = "1.0.74"
revm = {git = "https://github.com/bluealloy/revm"}
fe-common = {path = "../common", version = "^0.13.0-alpha"}
fe-driver = {path = "../driver", version = "^0.13.0-alpha"}
fe-yulgen = {path = "../yulgen", version = "^0.13.0-alpha"}
fe-yulc = {path = "../yulc", version = "^0.13.0-alpha", optional = true, features = ["solc-backend"]}
fe-analyzer = {path = "../analyzer", version = "^0.13.0-alpha"}
solc = {git = "https://github.com/g-r-a-n-t/solc-rust", rev = "da554b3", optional = true}
yultsur = {git = "https://github.com/g-r-a-n-t/yultsur", rev = "ae85470"}
indexmap = "1.6.2"
getrandom = { version = "0.2.3", features = ["js"] }
test-files = {path = "../test-files", package = "fe-test-files" }


[features]
solc-backend = ["fe-yulc", "solc", "fe-driver/solc-backend"]
75 changes: 75 additions & 0 deletions crates/fevm/src/conversion/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

use crate::{U256, H160};
use std::str::FromStr;


#[allow(dead_code)]
pub fn uint_token(n: u64) -> ethabi::Token {
ethabi::Token::Uint(U256::from(n))
}

#[allow(dead_code)]
pub fn uint_token_from_dec_str(val: &str) -> ethabi::Token {
ethabi::Token::Uint(U256::from_dec_str(val).expect("Not a valid dec string"))
}

#[allow(dead_code)]
pub fn address_token(addr: primitive_types::H160) -> ethabi::Token {
ethabi::Token::Address(addr.clone())
}

#[allow(dead_code)]
pub fn address_token_from_str(s: &str) -> ethabi::Token {
// left pads to 40 characters
ethabi::Token::Address(address(&format!("{:0>40}", s)))
}

#[allow(dead_code)]
pub fn string_token(s: &str) -> ethabi::Token {
ethabi::Token::String(s.to_string())
}

#[allow(dead_code)]
pub fn bool_token(val: bool) -> ethabi::Token {
ethabi::Token::Bool(val)
}

#[allow(dead_code)]
pub fn address(s: &str) -> primitive_types::H160 {
H160::from_str(s).unwrap_or_else(|_| panic!("couldn't create address from: {}", s))
}


#[allow(dead_code)]
pub fn tuple_token(tokens: &[ethabi::Token]) -> ethabi::Token {
ethabi::Token::Tuple(tokens.to_owned())
}


#[allow(dead_code)]
pub fn int_token(val: i64) -> ethabi::Token {
ethabi::Token::Int(to_2s_complement(val))
}

#[allow(dead_code)]
pub fn to_2s_complement(val: i64) -> U256 {
// Since this API takes an `i64` we can be sure that the min and max values
// will never be above what fits the `I256` type which has the same capacity
// as U256 but splits it so that one half covers numbers above 0 and the
// other half covers the numbers below 0.

// Conversion to Two's Complement: https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html

if val >= 0 {
U256::from(val)
} else {
let positive_val = -val;
get_2s_complement_for_negative(U256::from(positive_val))
}
}

#[allow(dead_code)]
pub fn get_2s_complement_for_negative(assume_negative: U256) -> U256 {
let (negated, _) = assume_negative.overflowing_neg();
negated + 1
}
179 changes: 179 additions & 0 deletions crates/fevm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
pub mod types;
use ethabi::Token;
use revm::Log;
pub use types::*;


use bytes::Bytes;
use fe_common::diagnostics::print_diagnostics;
use fe_common::files::FileStore;
use fe_common::utils::keccak;
use fe_yulgen::runtime::functions;
use std::borrow::{Borrow, BorrowMut};
use std::collections::HashMap;
use std::str::FromStr;
use yultsur::*;
pub use revm::{
self,
Return,
InMemoryDB,
EVM,
AccountInfo,
TransactTo,
TransactOut,
SpecId,
NoOpInspector,
Host
};
pub use primitive_types::{self, H160, U256};
use std::cell::RefCell;
use std::sync::Mutex;
pub mod conversion;
pub use conversion::*;
pub use ethabi;
pub type GasUsed = u64;
pub type CallResult = (Return, TransactOut, GasUsed, Vec<Log>);


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];
}

impl ToBeBytes for U256 {
fn to_be_bytes(&self) -> [u8; 32] {
let mut input_bytes: [u8; 32] = [0; 32];
self.to_big_endian(&mut input_bytes);
input_bytes
}
}

pub trait AsToken {
fn as_token(&self) -> Token;
}

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

}



impl Fevm<'_> {

pub fn new() -> Self {
let mut vm = revm::new();
vm.database(InMemoryDB::default());
Self {
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.lock().unwrap();
vm.env.tx.caller = caller.0;
vm.env.tx.transact_to = TransactTo::Call(addr.clone());
vm.env.tx.data = input.into();
vm.inspect_commit(NoOpInspector{})
}

pub fn deploy(&self, contract: &mut Contract, deployer: &Caller, init_params: &[ethabi::Token]) {
if let ContractCode::Bytes(bin) = &contract.code {
let mut bytecode = bin.clone();

if let Some(constructor) = &contract.abi.constructor {
bytecode = constructor.encode_input(bytecode, init_params).unwrap()
}

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();
vm.env.tx.transact_to = TransactTo::create();
vm.env.tx.data = Bytes::from(bytecode);
let (_, out, _, _) = vm.transact_commit();
let contract_address = match out {
TransactOut::Create(_, Some(contract)) => contract,
_ => panic!("Invalid create. This is a bug in the EVM"),
};
contract.code = ContractCode::Deployed;
contract.address = Some(contract_address);

},
None => panic!("Invalid caller. Please deploy with an existing address."),
}
} else {
panic!("Contract does not have bytecode. If you wish to redeploy, please re-instantiate with bytecode");
}
}

pub fn create_account(&self, address: impl AsRef<Address>, balance: impl Into<U256>) {
let acc = AccountInfo::from_balance(balance.into());
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.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())
.clone();
acc.balance += amt.into();
vm.db().unwrap().insert_cache(address.clone(), acc);
}

pub fn balance_of(&self, address: &Address) -> U256 {
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.lock().unwrap().db().unwrap().cache().get(address).cloned()
}

pub fn erase(&self, address: &Address) -> Address {
todo!()
}


}
Loading