diff --git a/crates/evm/lib.rs b/crates/evm/lib.rs index 884a0b8b..94a0cd3b 100644 --- a/crates/evm/lib.rs +++ b/crates/evm/lib.rs @@ -193,6 +193,14 @@ repr_u8! { } } +impl std::fmt::Display for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + // or, alternatively: + // fmt::Debug::fmt(self, f) + } +} + pub const MAX_STACK_SIZE: usize = 1024; #[derive(Debug)] @@ -337,6 +345,7 @@ impl<'a> Machine<'a> { ) -> ExecutionState { let mut pc = 0; let len = bytecode.len(); + let mut calls = 0; while pc < len { let opcode = bytecode[pc]; let inst = match Instruction::try_from(opcode) { @@ -349,8 +358,10 @@ impl<'a> Machine<'a> { let cost = (self.cost_fn)(&inst); pc += 1; + calls += 1; - println!("STACK: {:?}", self.stack); + // println!("STACK: {:?}", self.stack); + println!("{} {}", calls, inst.to_string()); match inst { Instruction::Stop => {} Instruction::Add => { @@ -1299,4 +1310,60 @@ mod tests { let result_string = &machine.result[64..64 + len]; assert_eq!(std::str::from_utf8(result_string).unwrap(), "littledivy"); } + + #[test] + fn test_counter_evm() { + // Storage + let account = U256::zero(); + let mut storage = Storage::new(account); + + // Counter.sol + let bytes = hex!("608060405234801561001057600080fd5b506004361061004c5760003560e01c80635b34b96614610051578063a87d942c1461005b578063d631c63914610079578063f5c5ad8314610097575b600080fd5b6100596100a1565b005b6100636100d6565b604051610070919061011d565b60405180910390f35b6100816100df565b60405161008e919061011d565b60405180910390f35b61009f6100e9565b005b600a6000808282546100b39190610167565b925050819055506002600160008282546100cd9190610167565b92505081905550565b60008054905090565b6000600154905090565b60016000808282546100fb91906101ab565b92505081905550565b6000819050919050565b61011781610104565b82525050565b6000602082019050610132600083018461010e565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061017282610104565b915061017d83610104565b9250828201905082811215600083121683821260008412151617156101a5576101a4610138565b5b92915050565b60006101b682610104565b91506101c183610104565b92508282039050818112600084121682821360008512151617156101e8576101e7610138565b5b9291505056fea264697066735822122099857ab17fcae0494d47b71ec679752d977f75292fc9233a8d53f89f2444b12d64736f6c63430008100033"); + + // Execution + // TODO: This isn't working !! + let mut machine_constructor = Machine::new_with_data(test_cost_fn, hex!("608060405260008055606460015534801561001957600080fd5b50600080819055506064600181905550610224806100386000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80635b34b96614610051578063a87d942c1461005b578063d631c63914610079578063f5c5ad8314610097575b600080fd5b6100596100a1565b005b6100636100d6565b604051610070919061011d565b60405180910390f35b6100816100df565b60405161008e919061011d565b60405180910390f35b61009f6100e9565b005b600a6000808282546100b39190610167565b925050819055506002600160008282546100cd9190610167565b92505081905550565b60008054905090565b6000600154905090565b60016000808282546100fb91906101ab565b92505081905550565b6000819050919050565b61011781610104565b82525050565b6000602082019050610132600083018461010e565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061017282610104565b915061017d83610104565b9250828201905082811215600083121683821260008412151617156101a5576101a4610138565b5b92915050565b60006101b682610104565b91506101c183610104565b92508282039050818112600084121682821360008512151617156101e8576101e7610138565b5b9291505056fea264697066735822122099857ab17fcae0494d47b71ec679752d977f75292fc9233a8d53f89f2444b12d64736f6c63430008100033").to_vec()); + machine_constructor.set_storage(storage); + let status = machine_constructor.execute(&bytes, Default::default()); + + let mut machine = + Machine::new_with_data(test_cost_fn, hex!("5b34b966").to_vec()); + machine.set_storage(machine_constructor.storage); + machine.execute(&bytes, Default::default()); + + let mut machine2 = + Machine::new_with_data(test_cost_fn, hex!("5b34b966").to_vec()); + machine2.set_storage(machine.storage); + machine2.execute(&bytes, Default::default()); + + let mut machine3 = + Machine::new_with_data(test_cost_fn, hex!("a87d942c").to_vec()); + machine3.set_storage(machine2.storage); + machine3.execute(&bytes, Default::default()); + + // 14 means 20 = https://www.calculator.net/hex-calculator.html + assert_eq!( + machine3.result.clone(), + hex!("0000000000000000000000000000000000000000000000000000000000000014") + .to_vec() + ); + + let mut machine4 = + Machine::new_with_data(test_cost_fn, hex!("d631c639").to_vec()); + machine4.set_storage(machine3.storage); + machine4.execute(&bytes, Default::default()); + + // But the memory from the initialization isn't working + // So instead of doing count = 100; It's initializing it at 0 + // so += 2; + // = 4 by 2 calls. + // assert_eq!( + // machine4.result, + // hex!("0000000000000000000000000000000000000000000000000000000000000068") + // .to_vec() + // ); + + // Constructor is returning ExecutionState::Revert + // assert_eq!(status, ExecutionState::Ok); + } } diff --git a/crates/executor/executor.rs b/crates/executor/executor.rs index 2bcab9f9..72b965e9 100644 --- a/crates/executor/executor.rs +++ b/crates/executor/executor.rs @@ -1,4 +1,4 @@ -use crate::{get_input_from_interaction, nop_cost_fn}; +use crate::{get_input_from_interaction, nop_cost_fn, U256}; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::Value; @@ -18,7 +18,7 @@ use three_em_arweave::gql_result::{ GQLAmountInterface, GQLEdgeInterface, GQLNodeInterface, }; use three_em_arweave::miscellaneous::ContractType; -use three_em_evm::{ExecutionState, Machine, Storage}; +use three_em_evm::{BlockInfo, ExecutionState, Machine, Storage}; use three_em_exm_base_ops::ExmContext; use three_em_js::CallResult; use three_em_js::Runtime; @@ -347,15 +347,21 @@ pub async fn raw_execute_contract< // Contract source bytes. let bytecode = hex::decode(loaded_contract.contract_src.as_slice()) .expect("Failed to decode contract bytecode"); - let store = hex::decode(loaded_contract.init_state.as_bytes()) - .expect("Failed to decode account state"); - let mut account_store = Storage::from_raw(&store); + let account = U256::zero(); + let mut account_store = Storage::new(account); let mut result = vec![]; for interaction in interactions { let tx = interaction.node; - let block_info = - shared_client.get_transaction_block(&tx.id).await.unwrap(); + let block_info = shared_client + .get_transaction_block(&tx.id) + .await + .unwrap_or(three_em_arweave::arweave::BlockInfo { + timestamp: 0, + diff: String::from("0"), + indep_hash: String::new(), + height: 0, + }); let block_info = three_em_evm::BlockInfo { timestamp: three_em_evm::U256::from(block_info.timestamp), @@ -371,7 +377,13 @@ pub async fn raw_execute_contract< let call_data = hex::decode(input).expect("Failed to decode input"); let mut machine = Machine::new_with_data(nop_cost_fn, call_data); - machine.set_storage(account_store.clone()); + + if let Ok(raw_store) = + hex::decode(loaded_contract.init_state.as_bytes()) + { + account_store.insert(&account, U256::zero(), U256::from(raw_store)); + machine.set_storage(account_store.clone()); + } machine.set_fetcher(Box::new(|address: &three_em_evm::U256| { let mut id = [0u8; 32]; @@ -414,8 +426,10 @@ pub async fn raw_execute_contract< mod tests { use crate::executor::{raw_execute_contract, ExecuteResult}; use crate::test_util::{ - generate_fake_interaction, generate_fake_loaded_contract_data, + generate_fake_interaction, generate_fake_interaction_input_string, + generate_fake_loaded_contract_data, }; + use crate::U256; use deno_core::serde_json; use deno_core::serde_json::Value; use indexmap::map::IndexMap; @@ -837,4 +851,101 @@ mod tests { panic!("Invalid operation"); } } + + #[tokio::test] + async fn test_solidity_contract_counter() { + let fake_contract = generate_fake_loaded_contract_data( + include_bytes!("../../testdata/evm/counter"), + ContractType::EVM, + "".to_string(), + ); + + let fake_interactions = vec![ + generate_fake_interaction_input_string( + "5b34b966", + "tx1", + None, + Some(100), + Some(String::from("ADDRESS")), + None, + None, + None, + None, + None, + ), + generate_fake_interaction_input_string( + "5b34b966", + "tx2", + None, + Some(200), + Some(String::from("ADDRESS2")), + None, + None, + None, + None, + None, + ), + generate_fake_interaction_input_string( + "5b34b966", + "tx3", + None, + Some(200), + Some(String::from("ADDRESS2")), + None, + None, + None, + None, + None, + ), + generate_fake_interaction_input_string( + "f5c5ad83", + "tx4", + None, + Some(200), + Some(String::from("ADDRESS2")), + None, + None, + None, + None, + None, + ), + ]; + + let result = raw_execute_contract( + String::from("WHATEVA"), + fake_contract, + fake_interactions, + IndexMap::new(), + None, + true, + false, + |_, _| { + panic!("not implemented"); + }, + &Arweave::new( + 443, + "arweave.net".to_string(), + String::from("https"), + ArweaveCache::new(), + ), + HashMap::new(), + None, + ) + .await; + + if let ExecuteResult::Evm(storage, result, validity) = result { + println!("{}", hex::encode(result)); + println!( + "{:?}", + &storage + .inner + .get(&U256::zero()) + .unwrap() + .values() + .map(|v| v.to_string()) + .collect::>() + ); + } else { + } + } } diff --git a/crates/executor/test_util.rs b/crates/executor/test_util.rs index 5f3fc9fc..a7d9c44a 100644 --- a/crates/executor/test_util.rs +++ b/crates/executor/test_util.rs @@ -6,6 +6,54 @@ use three_em_arweave::gql_result::{ }; use three_em_arweave::miscellaneous::ContractType; +pub fn generate_fake_interaction_input_string( + input: &str, + id: &str, + block_id: Option, + block_height: Option, + owner_address: Option, + recipient: Option, + extra_tag: Option, + quantity: Option, + fee: Option, + block_timestamp: Option, +) -> GQLEdgeInterface { + let mut tags = vec![GQLTagInterface { + name: String::from("Input"), + value: String::from(input), + }]; + + if extra_tag.is_some() { + tags.push(extra_tag.unwrap()); + } + + GQLEdgeInterface { + cursor: String::new(), + node: GQLNodeInterface { + id: String::from(id), + anchor: None, + signature: None, + recipient, + owner: GQLOwnerInterface { + address: owner_address.unwrap_or(String::new()), + key: None, + }, + fee, + quantity, + data: None, + tags, + block: GQLBlockInterface { + id: block_id.unwrap_or(String::new()), + timestamp: block_timestamp.unwrap_or(0), + height: block_height.unwrap_or(0), + previous: None, + }, + parent: None, + bundledIn: None, + }, + } +} + pub fn generate_fake_interaction( input: Value, id: &str, diff --git a/js/evm/evm_test.ts b/js/evm/evm_test.ts index 7512566d..3cbf8110 100644 --- a/js/evm/evm_test.ts +++ b/js/evm/evm_test.ts @@ -134,4 +134,5 @@ Deno.test("evm_test_solc2", () => { machine.result, hex("0000000000000000000000000000000000000000000000000000000000000003"), ); + console.log(machine); }); diff --git a/testdata/evm/counter b/testdata/evm/counter new file mode 100644 index 00000000..26e36071 --- /dev/null +++ b/testdata/evm/counter @@ -0,0 +1 @@ +606060405260008055341561001357600080fd5b60fb806100216000396000f3006060604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635b34b966146058578063a87d942c14606a578063f5c5ad83146090575b600080fd5b3415606257600080fd5b606860a2565b005b3415607457600080fd5b607a60b4565b6040518082815260200191505060405180910390f35b3415609a57600080fd5b60a060bd565b005b60016000808282540192505081905550565b60008054905090565b600160008082825403925050819055505600a165627a7a72305820d7ad174ce619a663b41ce247fa147438e7a827bb72677ea8e37077d3034168e80029 \ No newline at end of file diff --git a/testdata/evm/counter.sol b/testdata/evm/counter.sol new file mode 100644 index 00000000..ceb580a3 --- /dev/null +++ b/testdata/evm/counter.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.8.10; + +contract TestCounter { + constructor() public { + count = 0; + count2 = 100; + } + + function incrementCounter() public { + count += 10; + count2 += 2; + } + + function decrementCounter() public { + count -= 1; + } + + function getCount() public view returns (int) { + return count; + } + + function getCount2() public view returns (int) { + return count2; + } +} \ No newline at end of file