| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391 |
- use ethabi::{decode, ethereum_types, RawLog, Token};
- use num_derive::FromPrimitive;
- use num_traits::FromPrimitive;
- use rand::Rng;
- use ripemd::Ripemd160;
- use sha2::{Digest, Sha256};
- use std::collections::HashMap;
- use std::ffi::OsStr;
- use std::fmt;
- use tiny_keccak::{Hasher, Keccak};
- use wasmi::memory_units::Pages;
- use wasmi::*;
- use solang::{compile, file_resolver::FileResolver, Target};
- mod ewasm_tests;
- type Address = [u8; 20];
- pub fn address_new() -> Address {
- let mut rng = rand::thread_rng();
- let mut a = [0u8; 20];
- rng.fill(&mut a[..]);
- a
- }
- struct VirtualMachine {
- memory: MemoryRef,
- cur: Address,
- code: Vec<u8>,
- input: Vec<u8>,
- output: Vec<u8>,
- returndata: Vec<u8>,
- }
- impl VirtualMachine {
- fn new(code: Vec<u8>, address: Address) -> Self {
- VirtualMachine {
- memory: MemoryInstance::alloc(Pages(2), Some(Pages(2))).unwrap(),
- input: Vec::new(),
- output: Vec::new(),
- returndata: Vec::new(),
- code,
- cur: address,
- }
- }
- }
- struct TestRuntime {
- abi: ethabi::Contract,
- contracts: Vec<Vec<u8>>,
- value: u128,
- caller: Address,
- accounts: HashMap<Address, (Vec<u8>, u128)>,
- store: HashMap<(Address, [u8; 32]), [u8; 32]>,
- vm: VirtualMachine,
- events: Vec<Event>,
- }
- struct Event {
- topics: Vec<[u8; 32]>,
- data: Vec<u8>,
- }
- #[derive(FromPrimitive)]
- #[allow(non_camel_case_types)]
- pub enum Extern {
- getCallDataSize = 1,
- callDataCopy,
- storageLoad,
- storageStore,
- finish,
- revert,
- printMem,
- getCodeSize,
- codeCopy,
- create,
- call,
- returnDataCopy,
- getReturnDataSize,
- getCallValue,
- getCaller,
- getAddress,
- getExternalBalance,
- selfDestruct,
- log,
- }
- #[derive(Debug, Clone, PartialEq)]
- struct HostCodeFinish {}
- impl HostError for HostCodeFinish {}
- impl fmt::Display for HostCodeFinish {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(f, "finish")
- }
- }
- #[derive(Debug, Clone, PartialEq)]
- struct HostCodeRevert {}
- impl fmt::Display for HostCodeRevert {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(f, "revert")
- }
- }
- impl HostError for HostCodeRevert {}
- impl Externals for TestRuntime {
- fn invoke_index(
- &mut self,
- index: usize,
- args: RuntimeArgs,
- ) -> Result<Option<RuntimeValue>, Trap> {
- match FromPrimitive::from_usize(index) {
- Some(Extern::getCallDataSize) => {
- Ok(Some(RuntimeValue::I32(self.vm.input.len() as i32)))
- }
- Some(Extern::getCodeSize) => Ok(Some(RuntimeValue::I32(self.vm.code.len() as i32))),
- Some(Extern::getReturnDataSize) => {
- Ok(Some(RuntimeValue::I32(self.vm.returndata.len() as i32)))
- }
- Some(Extern::callDataCopy) => {
- let dest = args.nth_checked::<u32>(0)?;
- let input_offset = args.nth_checked::<u32>(1)? as usize;
- let input_len = args.nth_checked::<u32>(2)? as usize;
- self.vm
- .memory
- .set(
- dest,
- &self.vm.input[input_offset as usize..input_offset + input_len],
- )
- .expect("calldatacopy should work");
- Ok(None)
- }
- Some(Extern::codeCopy) => {
- let dest = args.nth_checked::<u32>(0)?;
- let code_offset = args.nth_checked::<u32>(1)? as usize;
- let code_len = args.nth_checked::<u32>(2)? as usize;
- let data = &self.vm.code[code_offset as usize..code_offset + code_len];
- println!("codeCopy {} {}", code_len, hex::encode(data));
- self.vm
- .memory
- .set(dest, data)
- .expect("codeCopy should work");
- Ok(None)
- }
- Some(Extern::returnDataCopy) => {
- let dest = args.nth_checked::<u32>(0)?;
- let data_offset = args.nth_checked::<u32>(1)? as usize;
- let data_len = args.nth_checked::<u32>(2)? as usize;
- let data = &self.vm.returndata[data_offset as usize..data_offset + data_len];
- println!("returnDataCopy {} {}", data_len, hex::encode(data));
- self.vm
- .memory
- .set(dest, data)
- .expect("returnDataCopy should work");
- Ok(None)
- }
- Some(Extern::finish) => {
- let src: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let mut output = Vec::new();
- output.resize(len as usize, 0);
- self.vm.memory.get_into(src, &mut output).unwrap();
- println!("finish: {} {}", len, hex::encode(&output));
- self.vm.output = output;
- Err(Trap::new(TrapKind::Host(Box::new(HostCodeFinish {}))))
- }
- Some(Extern::revert) => {
- let src: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let mut output = Vec::new();
- output.resize(len as usize, 0);
- self.vm.memory.get_into(src, &mut output).unwrap();
- self.vm.output = output;
- println!(
- "revert {} {}",
- self.vm.output.len(),
- hex::encode(&self.vm.output)
- );
- Err(Trap::new(TrapKind::Host(Box::new(HostCodeRevert {}))))
- }
- Some(Extern::storageLoad) => {
- let key_ptr: u32 = args.nth_checked(0)?;
- let data_ptr: u32 = args.nth_checked(1)?;
- let mut key = [0u8; 32];
- self.vm
- .memory
- .get_into(key_ptr, &mut key)
- .expect("copy key from wasm memory");
- let res = if let Some(v) = self.store.get(&(self.vm.cur, key)) {
- v
- } else {
- &[0u8; 32]
- };
- println!("storageLoad {} -> {}", hex::encode(&key), hex::encode(&res));
- self.vm
- .memory
- .set(data_ptr, res)
- .expect("copy key from wasm memory");
- Ok(None)
- }
- Some(Extern::storageStore) => {
- let key_ptr: u32 = args.nth_checked(0)?;
- let data_ptr: u32 = args.nth_checked(1)?;
- let mut key = [0u8; 32];
- let mut data = [0u8; 32];
- self.vm
- .memory
- .get_into(key_ptr, &mut key)
- .expect("copy key from wasm memory");
- self.vm
- .memory
- .get_into(data_ptr, &mut data)
- .expect("copy key from wasm memory");
- println!(
- "storageStore {} <- {}",
- hex::encode(&key),
- hex::encode(&data)
- );
- if data.iter().any(|n| *n != 0) {
- self.store.insert((self.vm.cur, key), data);
- } else {
- self.store.remove(&(self.vm.cur, key));
- }
- Ok(None)
- }
- Some(Extern::printMem) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let mut buf = Vec::new();
- buf.resize(len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut buf) {
- panic!("printMem: {}", e);
- }
- println!("{}", String::from_utf8_lossy(&buf));
- Ok(None)
- }
- Some(Extern::create) => {
- //let balance_ptr: u32 = args.nth_checked(0)?;
- let input_ptr: u32 = args.nth_checked(1)?;
- let input_len: u32 = args.nth_checked(2)?;
- let address_ptr: u32 = args.nth_checked(3)?;
- let mut buf = Vec::new();
- buf.resize(input_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(input_ptr, &mut buf) {
- panic!("create: {}", e);
- }
- println!("create code: {}", hex::encode(&buf));
- let addr = address_new();
- println!("create address: {}", hex::encode(&addr));
- // when ewasm creates a contract, the abi encoded args are concatenated to the
- // code. So, find which code is was and use that instead. Otherwise, the
- // wasm validator will trip
- let code = self
- .contracts
- .iter()
- .find(|c| buf.starts_with(c))
- .unwrap()
- .clone();
- let mut vm = VirtualMachine::new(buf, addr);
- std::mem::swap(&mut self.vm, &mut vm);
- let module = self.create_module(&code);
- if let Some(ExternVal::Memory(memory_ref)) = module.export_by_name("memory") {
- self.vm.memory = memory_ref;
- }
- match module.invoke_export("main", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- assert!(
- host_error.downcast_ref::<HostCodeRevert>().is_none(),
- "revert executed"
- );
- }
- _ => panic!("fail to invoke main via create: {}", trap),
- },
- Ok(_) => {}
- Err(e) => panic!("fail to invoke main via create: {}", e),
- }
- let res = self.vm.output.clone();
- println!("create returns: {}", hex::encode(&res));
- std::mem::swap(&mut self.vm, &mut vm);
- self.accounts.insert(addr, (res, 0));
- self.vm
- .memory
- .set(address_ptr, &addr[..])
- .expect("copy key from wasm memory");
- Ok(Some(RuntimeValue::I32(0)))
- }
- Some(Extern::call) => {
- //let gas: u64 = args.nth_checked(0)?;
- let address_ptr: u32 = args.nth_checked(1)?;
- //let value_ptr: u32 = args.nth_checked(2)?;
- let input_ptr: u32 = args.nth_checked(3)?;
- let input_len: u32 = args.nth_checked(4)?;
- let mut buf = Vec::new();
- buf.resize(input_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(input_ptr, &mut buf) {
- panic!("call: {}", e);
- }
- let mut addr = [0u8; 20];
- if let Err(e) = self.vm.memory.get_into(address_ptr, &mut addr) {
- panic!("address: {}", e);
- }
- println!(
- "extern call address: {} data: {}",
- hex::encode(&addr),
- hex::encode(&buf)
- );
- // if the first 19 bytes are 0, it's a precompile
- if addr[0..19].iter().all(|v| *v == 0) {
- match addr[19] {
- 20 => {
- let mut hasher = Keccak::v256();
- let mut hash = [0u8; 32];
- hasher.update(&buf);
- hasher.finalize(&mut hash);
- self.vm.returndata = hash.to_vec();
- return Ok(Some(RuntimeValue::I32(0)));
- }
- 2 => {
- let mut hasher = Sha256::new();
- hasher.update(&buf);
- self.vm.returndata = hasher.finalize().to_vec();
- return Ok(Some(RuntimeValue::I32(0)));
- }
- 3 => {
- let mut hasher = Ripemd160::new();
- hasher.update(&buf);
- self.vm.returndata = hasher.finalize().to_vec();
- return Ok(Some(RuntimeValue::I32(0)));
- }
- n => {
- panic!("unknown precompile {}", n);
- }
- }
- }
- // when ewasm creates a contract, the abi encoded args are concatenated to the
- // code. So, find which code is was and use that instead. Otherwise, the
- // wasm validator will trip
- let (code, _) = self.accounts.get(&addr).unwrap().clone();
- let mut vm = VirtualMachine::new(code.to_vec(), addr);
- std::mem::swap(&mut self.vm, &mut vm);
- self.vm.input = buf;
- let module = self.create_module(&code);
- if let Some(ExternVal::Memory(memory_ref)) = module.export_by_name("memory") {
- self.vm.memory = memory_ref;
- }
- let ret = match module.invoke_export("main", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(kind) => {
- if format!("{}", kind) == "revert" {
- 1
- } else {
- 0
- }
- }
- _ => panic!("fail to invoke main via create: {}", trap),
- },
- Ok(_) => 0,
- Err(e) => panic!("fail to invoke main via create: {}", e),
- };
- let res = self.vm.output.clone();
- std::mem::swap(&mut self.vm, &mut vm);
- self.vm.returndata = res;
- self.vm
- .memory
- .set(address_ptr, &addr[..])
- .expect("copy key from wasm memory");
- Ok(Some(RuntimeValue::I32(ret)))
- }
- Some(Extern::getCallValue) => {
- let value_ptr: u32 = args.nth_checked(0)?;
- let value = self.value.to_le_bytes();
- println!("getCallValue: {}", hex::encode(&value));
- self.vm.memory.set(value_ptr, &value).expect("set value");
- Ok(None)
- }
- Some(Extern::getAddress) => {
- let address_ptr: u32 = args.nth_checked(0)?;
- println!("getAddress: {}", hex::encode(&self.vm.cur));
- self.vm
- .memory
- .set(address_ptr, &self.vm.cur[..])
- .expect("set address");
- Ok(None)
- }
- Some(Extern::getCaller) => {
- let address_ptr: u32 = args.nth_checked(0)?;
- println!("getCaller: {}", hex::encode(&self.caller));
- self.vm
- .memory
- .set(address_ptr, &self.caller[..])
- .expect("set address");
- Ok(None)
- }
- Some(Extern::getExternalBalance) => {
- let address_ptr: u32 = args.nth_checked(0)?;
- let balance_ptr: u32 = args.nth_checked(1)?;
- let mut addr = [0u8; 20];
- if let Err(e) = self.vm.memory.get_into(address_ptr, &mut addr) {
- panic!("call: {}", e);
- }
- let value = self.accounts.get(&addr).map(|a| a.1).unwrap_or(0);
- println!("getExternalBalance: {} {}", hex::encode(&addr), value);
- self.vm
- .memory
- .set(balance_ptr, &value.to_le_bytes()[..])
- .expect("set balance");
- Ok(None)
- }
- Some(Extern::selfDestruct) => {
- let address_ptr: u32 = args.nth_checked(0)?;
- let mut addr = [0u8; 20];
- if let Err(e) = self.vm.memory.get_into(address_ptr, &mut addr) {
- panic!("selfDestruct: {}", e);
- }
- let remaining = self.accounts[&self.vm.cur].1;
- self.accounts.get_mut(&addr).unwrap().1 += remaining;
- println!("selfDestruct: {} {}", hex::encode(&addr), remaining);
- self.accounts.remove(&self.vm.cur);
- Err(Trap::new(TrapKind::Host(Box::new(HostCodeFinish {}))))
- }
- Some(Extern::log) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let data_len: u32 = args.nth_checked(1)?;
- let mut data = Vec::new();
- data.resize(data_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("log: {}", e);
- }
- let topic_count: u32 = args.nth_checked(2)?;
- let mut event = Event {
- data,
- topics: Vec::new(),
- };
- assert!(topic_count <= 4, "log: wrong topic count {}", topic_count);
- for topic in 0..topic_count {
- let topic_ptr: u32 = args.nth_checked(3 + topic as usize)?;
- let mut topic_data = [0u8; 32];
- if let Err(e) = self.vm.memory.get_into(topic_ptr, &mut topic_data) {
- panic!("log: topic {} {}", topic, e);
- }
- event.topics.push(topic_data);
- }
- println!(
- "log: data: {} topics: {}",
- hex::encode(&event.data),
- event
- .topics
- .iter()
- .map(hex::encode)
- .collect::<Vec<String>>()
- .join(" ")
- );
- self.events.push(event);
- Ok(None)
- }
- _ => panic!("external {} unknown", index),
- }
- }
- }
- impl ModuleImportResolver for TestRuntime {
- fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
- let index = match field_name {
- "getCallDataSize" => Extern::getCallDataSize,
- "callDataCopy" => Extern::callDataCopy,
- "finish" => Extern::finish,
- "revert" => Extern::revert,
- "storageStore" => Extern::storageStore,
- "storageLoad" => Extern::storageLoad,
- "printMem" => Extern::printMem,
- "getCodeSize" => Extern::getCodeSize,
- "codeCopy" => Extern::codeCopy,
- "create" => Extern::create,
- "call" => Extern::call,
- "returnDataCopy" => Extern::returnDataCopy,
- "getReturnDataSize" => Extern::getReturnDataSize,
- "getCallValue" => Extern::getCallValue,
- "getCaller" => Extern::getCaller,
- "getAddress" => Extern::getAddress,
- "getExternalBalance" => Extern::getExternalBalance,
- "selfDestruct" => Extern::selfDestruct,
- "log" => Extern::log,
- _ => {
- panic!("{} not implemented", field_name);
- }
- };
- Ok(FuncInstance::alloc_host(signature.clone(), index as usize))
- }
- fn resolve_memory(
- &self,
- _field_name: &str,
- _memory_type: &MemoryDescriptor,
- ) -> Result<MemoryRef, Error> {
- Ok(self.vm.memory.clone())
- }
- }
- impl TestRuntime {
- fn create_module(&self, code: &[u8]) -> ModuleRef {
- let module = Module::from_buffer(&code).expect("parse wasm should work");
- ModuleInstance::new(
- &module,
- &ImportsBuilder::new().with_resolver("ethereum", self),
- )
- .expect("Failed to instantiate module")
- .run_start(&mut NopExternals)
- .expect("Failed to run start function in module")
- }
- fn function(&mut self, name: &str, args: &[Token]) -> Vec<Token> {
- let calldata = match self.abi.functions[name][0].encode_input(args) {
- Ok(n) => n,
- Err(x) => panic!("{}", x),
- };
- let module = self.create_module(&self.accounts[&self.vm.cur].0);
- println!("FUNCTION CALLDATA: {}", hex::encode(&calldata));
- self.vm.input = calldata;
- if let Some(ExternVal::Memory(memory_ref)) = module.export_by_name("memory") {
- self.vm.memory = memory_ref;
- }
- match module.invoke_export("main", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- assert!(
- host_error.downcast_ref::<HostCodeFinish>().is_some(),
- "fail to invoke main: {}",
- host_error
- );
- }
- _ => panic!("fail to invoke main: {}", trap),
- },
- Ok(Some(RuntimeValue::I32(0))) => {}
- Ok(Some(RuntimeValue::I32(ret))) => panic!("main returns: {}", ret),
- Err(e) => panic!("fail to invoke main: {}", e),
- Ok(None) => panic!("fail to invoke main"),
- _ => panic!("fail to invoke main, unknown"),
- }
- println!("RETURNDATA: {}", hex::encode(&self.vm.output));
- self.abi.functions[name][0]
- .decode_output(&self.vm.output)
- .unwrap()
- }
- fn function_abi_fail(&mut self, name: &str, args: &[Token], patch: fn(&mut Vec<u8>)) {
- let mut calldata = match self.abi.functions[name][0].encode_input(args) {
- Ok(n) => n,
- Err(x) => panic!("{}", x),
- };
- patch(&mut calldata);
- let module = self.create_module(&self.accounts[&self.vm.cur].0);
- println!("FUNCTION CALLDATA: {}", hex::encode(&calldata));
- if let Some(ExternVal::Memory(memory_ref)) = module.export_by_name("memory") {
- self.vm.memory = memory_ref;
- }
- self.vm.input = calldata;
- match module.invoke_export("main", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- assert!(
- host_error.downcast_ref::<HostCodeFinish>().is_some(),
- "fail to invoke main: {}",
- host_error
- );
- }
- _ => panic!("fail to invoke main: {}", trap),
- },
- Ok(Some(RuntimeValue::I32(3))) => {}
- Ok(Some(RuntimeValue::I32(ret))) => panic!("main returns: {}", ret),
- Err(e) => panic!("fail to invoke main: {}", e),
- Ok(None) => panic!("fail to invoke main"),
- _ => panic!("fail to invoke main, unknown"),
- }
- }
- fn function_revert(&mut self, name: &str, args: &[Token]) -> Option<String> {
- let calldata = match self.abi.functions[name][0].encode_input(args) {
- Ok(n) => n,
- Err(x) => panic!("{}", x),
- };
- let module = self.create_module(&self.accounts[&self.vm.cur].0);
- println!("FUNCTION CALLDATA: {}", hex::encode(&calldata));
- self.vm.input = calldata;
- if let Some(ExternVal::Memory(memory_ref)) = module.export_by_name("memory") {
- self.vm.memory = memory_ref;
- }
- match module.invoke_export("main", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- if host_error.downcast_ref::<HostCodeRevert>().is_none() {
- panic!("function was suppose to revert, not finish")
- }
- }
- _ => panic!("fail to invoke main: {}", trap),
- },
- Ok(Some(RuntimeValue::I32(1))) => {}
- Err(e) => panic!("fail to invoke main: {}", e),
- _ => panic!("fail to invoke main"),
- }
- println!("RETURNDATA: {}", hex::encode(&self.vm.output));
- if self.vm.output.is_empty() {
- return None;
- }
- assert_eq!(self.vm.output[..4], 0x08c3_79a0u32.to_be_bytes());
- if let Ok(v) = decode(&[ethabi::ParamType::String], &self.vm.output[4..]) {
- assert_eq!(v.len(), 1);
- if let ethabi::Token::String(r) = &v[0] {
- return Some(r.to_owned());
- }
- }
- panic!("failed to decode");
- }
- fn constructor_expect_revert(&mut self, args: &[Token]) {
- assert!(!self.do_constructor(args));
- }
- fn constructor(&mut self, args: &[Token]) {
- assert!(self.do_constructor(args));
- }
- fn do_constructor(&mut self, args: &[Token]) -> bool {
- let calldata = if let Some(constructor) = &self.abi.constructor {
- constructor.encode_input(Vec::new(), args).unwrap()
- } else {
- Vec::new()
- };
- let module = self.create_module(self.contracts.last().unwrap());
- println!("CONSTRUCTOR CALLDATA: {}", hex::encode(&calldata));
- if let Some(ExternVal::Memory(memory_ref)) = module.export_by_name("memory") {
- self.vm.memory = memory_ref;
- }
- self.vm.code.extend(calldata);
- self.vm.cur = address_new();
- match module.invoke_export("main", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- if host_error.downcast_ref::<HostCodeRevert>().is_some() {
- return false;
- }
- }
- _ => panic!("fail to invoke main: {}", trap),
- },
- Ok(_) => {}
- Err(e) => panic!("fail to invoke main: {}", e),
- }
- println!(
- "DEPLOYER RETURNS: {} {}",
- self.vm.output.len(),
- hex::encode(&self.vm.output)
- );
- self.accounts
- .insert(self.vm.cur, (self.vm.output.clone(), 0));
- true
- }
- fn events(&self) -> Vec<RawLog> {
- self.events
- .iter()
- .map(|e| RawLog {
- data: e.data.clone(),
- topics: e.topics.iter().map(ethereum_types::H256::from).collect(),
- })
- .collect()
- }
- }
- fn build_solidity(src: &str) -> TestRuntime {
- let mut cache = FileResolver::new();
- cache.set_file_contents("test.sol", src.to_string());
- let (res, ns) = compile(
- OsStr::new("test.sol"),
- &mut cache,
- inkwell::OptimizationLevel::Default,
- Target::Ewasm,
- false,
- );
- ns.print_diagnostics_in_plain(&cache, false);
- for v in &res {
- println!("contract size:{}", v.0.len());
- }
- assert!(!res.is_empty());
- // resolve
- let (bc, abi) = res.last().unwrap().clone();
- TestRuntime {
- accounts: HashMap::new(),
- vm: VirtualMachine::new(bc, [0u8; 20]),
- value: 0,
- store: HashMap::new(),
- abi: ethabi::Contract::load(abi.as_bytes()).unwrap(),
- contracts: res.into_iter().map(|v| v.0).collect(),
- events: Vec::new(),
- caller: address_new(),
- }
- }
- #[test]
- fn simple_solidiy_compile_and_run() {
- // parse
- let mut runtime = build_solidity(
- "
- contract test {
- function foo() public returns (uint32) {
- return 2;
- }
- }",
- );
- // call constructor
- runtime.constructor(&[]);
- let returns = runtime.function("foo", &[]);
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(2))]
- );
- }
- #[test]
- fn simple_loops() {
- let mut runtime = build_solidity(
- r##"
- contract test3 {
- function foo(uint32 a) public returns (uint32) {
- uint32 b = 50 - a;
- uint32 c;
- c = 100 * b;
- c += 5;
- return a * 1000 + c;
- }
- function bar(uint32 b, bool x) public returns (uint32) {
- uint32 i = 1;
- if (x) {
- do {
- i += 10;
- }
- while (b-- > 0);
- } else {
- uint32 j;
- for (j=2; j<10; j++) {
- i *= 3;
- }
- }
- return i;
- }
- function baz(uint32 x) public returns (uint32) {
- for (uint32 i = 0; i<100; i++) {
- x *= 7;
- if (x > 200) {
- break;
- }
- x++;
- }
- return x;
- }
- }"##,
- );
- // call constructor
- runtime.constructor(&[]);
- for i in 0..=50 {
- let res = ((50 - i) * 100 + 5) + i * 1000;
- let returns =
- runtime.function("foo", &[ethabi::Token::Uint(ethereum_types::U256::from(i))]);
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(res))]
- );
- }
- for i in 0..=50 {
- let res = (i + 1) * 10 + 1;
- let returns = runtime.function(
- "bar",
- &[
- ethabi::Token::Uint(ethereum_types::U256::from(i)),
- ethabi::Token::Bool(true),
- ],
- );
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(res))]
- );
- }
- for i in 0..=50 {
- let mut res = 1;
- for _ in 2..10 {
- res *= 3;
- }
- let returns = runtime.function(
- "bar",
- &[
- ethabi::Token::Uint(ethereum_types::U256::from(i)),
- ethabi::Token::Bool(false),
- ],
- );
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(res))]
- );
- }
- for i in 1..=50 {
- let mut res = i;
- for _ in 0..100 {
- res *= 7;
- if res > 200 {
- break;
- }
- res += 1;
- }
- let returns =
- runtime.function("baz", &[ethabi::Token::Uint(ethereum_types::U256::from(i))]);
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(res))]
- );
- }
- }
- #[test]
- fn stack_test() {
- let mut runtime = build_solidity(
- r##"
- contract test3 {
- function foo() public returns (bool) {
- uint b = 18446744073709551616;
- uint c = 36893488147419103232;
- return b * 2 == c;
- }
- }"##,
- );
- // call constructor
- runtime.constructor(&[]);
- let returns = runtime.function("foo", &[]);
- assert_eq!(returns, vec![ethabi::Token::Bool(true)]);
- }
- #[test]
- fn abi_call_return_test() {
- let mut runtime = build_solidity(
- r##"
- contract test {
- function foo() public returns (uint32) {
- return 102;
- }
- }"##,
- );
- // call constructor
- runtime.constructor(&[]);
- let returns = runtime.function("foo", &[]);
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(102))]
- );
- }
- #[test]
- fn abi_call_pass_return_test() {
- let mut runtime = build_solidity(
- r##"
- contract x {
- function test() public {
- }
- }
- contract bar {
- function foo(uint32 a) public returns (uint32) {
- return a;
- }
- }"##,
- );
- // call constructor
- runtime.constructor(&[]);
- for val in [102i32, 255, 256, 0x7fff_ffff].iter() {
- let returns = runtime.function(
- "foo",
- &[ethabi::Token::Uint(ethereum_types::U256::from(*val))],
- );
- assert_eq!(
- returns,
- vec![ethabi::Token::Uint(ethereum_types::U256::from(*val))]
- );
- }
- }
- #[test]
- fn contract_storage_test() {
- let mut runtime = build_solidity(
- r##"
- contract test {
- uint32 foo;
- constructor() public {
- foo = 102;
- }
- function getFoo() public returns (uint32) {
- return foo + 256;
- }
- function setFoo(uint32 a) public {
- foo = a - 256;
- }
- }"##,
- );
- // call constructor
- runtime.constructor(&[]);
- for val in [4096u32, 1000u32].iter() {
- let eval = ethabi::Token::Uint(ethereum_types::U256::from(*val));
- // create call for foo
- let returns = runtime.function("setFoo", &[eval]);
- assert_eq!(returns, vec![]);
- // create call for foo
- let returns = runtime.function("getFoo", &[]);
- let eval = ethabi::Token::Uint(ethereum_types::U256::from(*val));
- assert_eq!(returns, vec![eval]);
- }
- }
- #[test]
- fn large_ints_encoded() {
- let mut runtime = build_solidity(
- r##"
- contract test {
- uint foo;
- constructor() public {
- foo = 102;
- }
- function getFoo() public returns (uint) {
- return foo + 256;
- }
- function setFoo(uint a) public {
- foo = a - 256;
- }
- }"##,
- );
- // call constructor
- runtime.constructor(&[]);
- for val in [4096u32, 1000u32].iter() {
- let eval = ethabi::Token::Uint(ethereum_types::U256::from(*val));
- // create call for foo
- let returns = runtime.function("setFoo", &[eval]);
- assert_eq!(returns, vec![]);
- // create call for foo
- let returns = runtime.function("getFoo", &[]);
- let eval = ethabi::Token::Uint(ethereum_types::U256::from(*val));
- assert_eq!(returns, vec![eval]);
- }
- }
- #[test]
- fn address() {
- let mut runtime = build_solidity(
- "
- contract address_tester {
- function encode_const() public returns (address) {
- return 0x52908400098527886E0F7030069857D2E4169EE7;
- }
- function test_arg(address foo) public {
- assert(foo == 0x27b1fdb04752bbc536007a920d24acb045561c26);
- // this literal is a number
- int x = 0x27b1fdb047_52bbc536007a920d24acb045561C26;
- assert(int(foo) == x);
- }
- function allones() public returns (address) {
- return address(1);
- }
- }",
- );
- // call constructor
- runtime.constructor(&[]);
- let ret = runtime.function("encode_const", &[]);
- assert_eq!(
- ret,
- [ethabi::Token::Address(ethereum_types::Address::from_slice(
- &hex::decode("52908400098527886E0F7030069857D2E4169EE7").unwrap()
- ))]
- );
- runtime.function(
- "test_arg",
- &[ethabi::Token::Address(ethereum_types::Address::from_slice(
- &hex::decode("27b1fdb04752bbc536007a920d24acb045561c26").unwrap(),
- ))],
- );
- let ret = runtime.function("allones", &[]);
- assert_eq!(
- ret,
- [ethabi::Token::Address(ethereum_types::Address::from_slice(
- &hex::decode("0000000000000000000000000000000000000001").unwrap()
- ))]
- );
- // no arithmetic/bitwise allowed on address
- // no ordered comparison allowed
- // address 0x27b1fdb04752bbc536007a920d24acb045561C26 should be a warning
- }
- #[test]
- fn bytes() {
- let mut runtime = build_solidity(
- r##"
- contract bar {
- bytes4 constant foo = hex"11223344";
- function get_foo() public returns (bytes4) {
- return foo;
- }
- function bytes4asuint32() public view returns (uint32) {
- return uint32(foo);
- }
- function bytes4asuint64() public view returns (uint64) {
- return uint64(bytes8(foo));
- }
- function bytes4asbytes2() public view returns (bytes2) {
- return bytes2(foo);
- }
- function passthrough(bytes4 bar) public view returns (bytes4) {
- return bar;
- }
- function entry(uint index) public view returns (bytes1) {
- return foo[index];
- }
- function entry2(uint index) public pure returns (bytes1) {
- return hex"AABBCCDD"[index];
- }
- function shiftedleft() public view returns (bytes4) {
- return foo << 8;
- }
- function shiftedright() public view returns (bytes4) {
- return foo >> 8;
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("get_foo", &[]);
- assert_eq!(
- ret,
- [ethabi::Token::FixedBytes(vec!(0x11, 0x22, 0x33, 0x44))]
- );
- let ret = runtime.function("bytes4asuint32", &[]);
- assert_eq!(
- ret,
- [ethabi::Token::Uint(ethereum_types::U256::from(
- 0x11_22_33_44
- ))]
- );
- let ret = runtime.function("bytes4asuint64", &[]);
- assert_eq!(
- ret,
- [ethabi::Token::Uint(ethereum_types::U256::from(
- 0x1122_3344_0000_0000u64
- ))]
- );
- let ret = runtime.function("bytes4asbytes2", &[]);
- assert_eq!(ret, [ethabi::Token::FixedBytes(vec!(0x11, 0x22))]);
- let val = vec![ethabi::Token::FixedBytes(vec![0x41, 0x42, 0x43, 0x44])];
- assert_eq!(runtime.function("passthrough", &val), val);
- let val = vec![ethabi::Token::Uint(ethereum_types::U256::from(1))];
- let ret = runtime.function("entry", &val);
- assert_eq!(ret, [ethabi::Token::FixedBytes(vec!(0x22))]);
- let ret = runtime.function("entry2", &val);
- assert_eq!(ret, [ethabi::Token::FixedBytes(vec!(0xBB))]);
- }
- #[test]
- fn array() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f(uint i1) public returns (int) {
- int[8] bar = [ int(10), 20, 30, 4, 5, 6, 7, 8 ];
- bar[2] = 0x7_f;
- return bar[i1];
- }
- function bar() public returns (uint) {
- uint[2][3][4] array;
- return array.length;
- }
- }"##,
- );
- runtime.constructor(&[]);
- let val = vec![ethabi::Token::Uint(ethereum_types::U256::from(1))];
- let ret = runtime.function("f", &val);
- assert_eq!(ret, [ethabi::Token::Int(ethereum_types::U256::from(20))]);
- let val = vec![ethabi::Token::Uint(ethereum_types::U256::from(2))];
- let ret = runtime.function("f", &val);
- assert_eq!(ret, [ethabi::Token::Int(ethereum_types::U256::from(127))]);
- let ret = runtime.function("bar", &[]);
- assert_eq!(ret, [ethabi::Token::Uint(ethereum_types::U256::from(4))]);
- }
- #[test]
- fn encode_array() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f(int32[4] a, uint i) public returns (int32) {
- return a[i];
- }
- }"##,
- );
- runtime.constructor(&[]);
- let array = vec![
- ethabi::Token::Int(ethereum_types::U256::from(0x20)),
- ethabi::Token::Int(ethereum_types::U256::from(0x40)),
- ethabi::Token::Int(ethereum_types::U256::from(0x80)),
- ethabi::Token::Int(ethereum_types::U256::from(0x100)),
- ];
- for i in 0..4 {
- let ret = runtime.function(
- "f",
- &[
- ethabi::Token::FixedArray(array.clone()),
- ethabi::Token::Uint(ethereum_types::U256::from(i)),
- ],
- );
- assert_eq!(ret, [array[i].clone()]);
- }
- }
- #[test]
- #[should_panic]
- fn array_bounds_uint() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f(int32[4] a, uint i) public returns (int32) {
- return a[i];
- }
- }"##,
- );
- runtime.constructor(&[]);
- let array = vec![
- ethabi::Token::Int(ethereum_types::U256::from(0x20)),
- ethabi::Token::Int(ethereum_types::U256::from(0x40)),
- ethabi::Token::Int(ethereum_types::U256::from(0x80)),
- ethabi::Token::Int(ethereum_types::U256::from(0x100)),
- ];
- runtime.function(
- "f",
- &[
- ethabi::Token::FixedArray(array),
- ethabi::Token::Uint(ethereum_types::U256::from(4)),
- ],
- );
- }
- fn array_bounds_int(index: ethabi::Token) {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f(int32[4] a, int i) public returns (int32) {
- return a[i];
- }
- }"##,
- );
- runtime.constructor(&[]);
- let array = vec![
- ethabi::Token::Int(ethereum_types::U256::from(0x20)),
- ethabi::Token::Int(ethereum_types::U256::from(0x40)),
- ethabi::Token::Int(ethereum_types::U256::from(0x80)),
- ethabi::Token::Int(ethereum_types::U256::from(0x100)),
- ];
- runtime.function("f", &[ethabi::Token::FixedArray(array), index]);
- }
- #[test]
- #[should_panic]
- fn array_bounds_int_neg() {
- array_bounds_int(ethabi::Token::Int(ethereum_types::U256::from(-1)))
- }
- #[test]
- #[should_panic]
- fn array_bounds_int_pos() {
- array_bounds_int(ethabi::Token::Int(ethereum_types::U256::from(4)))
- }
- #[test]
- fn array_array() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f(int a, uint i1, uint i2) public returns (int) {
- int[4][2] memory bar = [ [ int(1), 2, 3, 4 ], [ 5, 6, 7, a ] ];
- return bar[i1][i2];
- }
- }"##,
- );
- runtime.constructor(&[]);
- for i1 in 0..2 {
- for i2 in 0..4 {
- let val = runtime.function(
- "f",
- &[
- ethabi::Token::Int(ethereum_types::U256::from(8)),
- ethabi::Token::Uint(ethereum_types::U256::from(i1)),
- ethabi::Token::Uint(ethereum_types::U256::from(i2)),
- ],
- );
- println!("i1:{} i2:{}: {:?}", i1, i2, val);
- assert_eq!(
- val,
- [ethabi::Token::Int(ethereum_types::U256::from(
- 1 + 4 * i1 + i2
- ))]
- );
- }
- }
- }
- #[test]
- fn arrays_are_refs() {
- // verified on remix
- let mut runtime = build_solidity(
- r##"
- pragma solidity >=0.4.22 <0.6.0;
- contract refs {
- function f2(int[4] memory foo) private {
- foo[2] = 2;
- }
- function f1(int[4] memory foo) private {
- foo[1] = 2;
- }
- function bar() public returns (int[4] memory) {
- int[4] memory x = [ int(0), 0, 0, 0 ];
- f1(x);
- f2(x);
- return x;
- }
- }
- "##,
- );
- runtime.constructor(&[]);
- let val = runtime.function("bar", &[]);
- assert_eq!(
- val,
- &[ethabi::Token::FixedArray(vec!(
- ethabi::Token::Int(ethereum_types::U256::from(0)),
- ethabi::Token::Int(ethereum_types::U256::from(2)),
- ethabi::Token::Int(ethereum_types::U256::from(2)),
- ethabi::Token::Int(ethereum_types::U256::from(0))
- ))],
- );
- }
- #[test]
- fn storage_structs() {
- // verified on remix
- let mut runtime = build_solidity(
- r##"
- /**
- * This is a doccomment
- */
- pragma solidity 0;
- pragma experimental ABIEncoderV2;
- contract test_struct_parsing {
- struct foo {
- bool x;
- uint32 y;
- }
- foo f;
- function test() public {
- f.x = true;
- f.y = 64;
- assert(f.x == true);
- assert(f.y == 64);
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.function("test", &[]);
- }
- #[test]
- fn struct_encode() {
- let mut runtime = build_solidity(
- r##"
- contract structs {
- struct foo {
- bool x;
- uint32 y;
- }
- function test(foo memory f) public {
- assert(f.x == true);
- assert(f.y == 64);
- }
- }
- "##,
- );
- runtime.constructor(&[]);
- runtime.function(
- "test",
- &[ethabi::Token::Tuple(vec![
- ethabi::Token::Bool(true),
- ethabi::Token::Uint(ethereum_types::U256::from(64)),
- ])],
- );
- }
- #[test]
- fn struct_dynamic_array_encode() {
- let mut runtime = build_solidity(
- r##"
- contract structs {
- struct foo {
- bool x;
- uint32 y;
- }
- function test() public returns (foo[]) {
- foo[] x = new foo[](3);
- x[0] = foo({x: true,y: 64});
- x[1] = foo({x: false,y: 102});
- x[2] = foo({x: true,y: 0x800});
- return x;
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("test", &[]);
- assert_eq!(
- ret,
- vec![ethabi::Token::Array(vec![
- ethabi::Token::Tuple(vec![
- ethabi::Token::Bool(true),
- ethabi::Token::Uint(ethereum_types::U256::from(64))
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Bool(false),
- ethabi::Token::Uint(ethereum_types::U256::from(102))
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Bool(true),
- ethabi::Token::Uint(ethereum_types::U256::from(0x800)),
- ])
- ])],
- );
- }
- #[test]
- fn struct_decode() {
- let mut runtime = build_solidity(
- r##"
- contract structs {
- struct foo {
- bool x;
- uint32 y;
- }
- function test() public returns (foo) {
- foo f;
- f.x = true;
- f.y = 64;
- return f;
- }
- }
- "##,
- );
- runtime.constructor(&[]);
- let val = runtime.function("test", &[]);
- assert_eq!(
- val,
- &[ethabi::Token::Tuple(vec![
- ethabi::Token::Bool(true),
- ethabi::Token::Uint(ethereum_types::U256::from(64)),
- ])],
- );
- }
- /* TODO: find out why this test fails.
- #[test]
- fn struct_in_struct_decode() {
- let mut runtime = build_solidity(
- r##"
- contract structs {
- enum suit { club, diamonds, hearts, spades }
- enum value { two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace }
- struct card {
- value v;
- suit s;
- }
- struct hand {
- card card1;
- card card2;
- card card3;
- card card4;
- card card5;
- }
- function test() public returns (hand) {
- hand h = hand({
- card1: card({ s: suit.hearts, v: value.two }),
- card2: card({ s: suit.diamonds, v: value.three }),
- card3: card({ s: suit.club, v: value.four }),
- card4: card({ s: suit.diamonds, v: value.ten }),
- card5: card({ s: suit.hearts, v: value.jack })
- });
- return h;
- }
- }
- "##,
- );
- runtime.constructor(&[]);
- let val = runtime.function("test", &[]);
- assert_eq!(
- val,
- &[ethabi::Token::Tuple(vec![
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(0)),
- ethabi::Token::Uint(ethereum_types::U256::from(2)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(1)),
- ethabi::Token::Uint(ethereum_types::U256::from(1)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(2)),
- ethabi::Token::Uint(ethereum_types::U256::from(0)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(8)),
- ethabi::Token::Uint(ethereum_types::U256::from(1)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(9)),
- ethabi::Token::Uint(ethereum_types::U256::from(2)),
- ]),
- ])],
- );
- }*/
- #[test]
- fn struct_in_struct_encode() {
- let mut runtime = build_solidity(
- r##"
- contract structs {
- enum suit { club, diamonds, hearts, spades }
- enum value { two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace }
- struct card {
- value v;
- suit s;
- }
- struct hand {
- card card1;
- card card2;
- card card3;
- card card4;
- card card5;
- }
- function test(hand h) public {
- assert(h.card1.s == suit.hearts);
- assert(h.card1.v == value.two);
- assert(h.card2.s == suit.diamonds);
- assert(h.card2.v == value.three);
- assert(h.card3.s == suit.club);
- assert(h.card3.v == value.four);
- assert(h.card4.s == suit.diamonds);
- assert(h.card4.v == value.ten);
- assert(h.card5.s == suit.hearts);
- assert(h.card5.v == value.jack);
- }
- }
- "##,
- );
- runtime.constructor(&[]);
- runtime.function(
- "test",
- &[ethabi::Token::Tuple(vec![
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(0)),
- ethabi::Token::Uint(ethereum_types::U256::from(2)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(1)),
- ethabi::Token::Uint(ethereum_types::U256::from(1)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(2)),
- ethabi::Token::Uint(ethereum_types::U256::from(0)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(8)),
- ethabi::Token::Uint(ethereum_types::U256::from(1)),
- ]),
- ethabi::Token::Tuple(vec![
- ethabi::Token::Uint(ethereum_types::U256::from(9)),
- ethabi::Token::Uint(ethereum_types::U256::from(2)),
- ]),
- ])],
- );
- }
- #[test]
- fn array_push_delete() {
- // ensure that structs and fixed arrays are wiped by delete
- let mut runtime = build_solidity(
- r#"
- pragma solidity 0;
- contract foo {
- uint32[] bar;
- function setup() public {
- for (uint32 i = 0; i < 105; i++) {
- bar.push(i + 0x8000);
- }
- }
- function clear() public {
- delete bar;
- }
- }"#,
- );
- runtime.constructor(&[]);
- runtime.function("setup", &[]);
- assert_eq!(runtime.store.len(), 106);
- runtime.function("clear", &[]);
- assert_eq!(runtime.store.len(), 0);
- }
- #[test]
- fn encode_string() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f() public returns (string) {
- return "Hello, World!";
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("f", &[]);
- assert_eq!(ret, vec!(ethabi::Token::String("Hello, World!".to_owned())));
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f() public returns (int32, string, int64) {
- return (105, "the quick brown dog jumps over the lazy fox", -563);
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("f", &[]);
- let n563 = ethereum_types::U256::from(0)
- .overflowing_sub(ethereum_types::U256::from(563))
- .0;
- assert_eq!(
- ret,
- vec!(
- ethabi::Token::Int(ethereum_types::U256::from(105)),
- ethabi::Token::String("the quick brown dog jumps over the lazy fox".to_owned()),
- ethabi::Token::Int(n563),
- )
- );
- }
- #[test]
- fn decode_string() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f(string a) public returns (string) {
- return a + " ";
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("f", &[ethabi::Token::String("Hello, World!".to_owned())]);
- assert_eq!(
- ret,
- vec!(ethabi::Token::String("Hello, World! ".to_owned()))
- );
- }
- #[test]
- fn revert() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- function f() public {
- revert("Hello, World!");
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function_revert("f", &[]);
- assert_eq!(ret, Some("Hello, World!".to_owned()));
- }
- #[test]
- fn constructor_args() {
- let mut runtime = build_solidity(
- r##"
- contract foo {
- int64 v;
- constructor(int64 a) public {
- v = a;
- }
- function f() public returns (int64) {
- return v;
- }
- }"##,
- );
- let v = ethabi::Token::Int(ethereum_types::U256::from(105));
- runtime.constructor(&[v.clone()]);
- let ret = runtime.function("f", &[]);
- assert_eq!(ret, vec!(v));
- }
- #[test]
- fn create() {
- let mut runtime = build_solidity(
- r##"
- contract a {
- int32 x;
- constructor() public {
- }
- function test() public {
- x = 102;
- }
- }
- contract b {
- function x() public {
- a r = new a();
- }
- }
- "##,
- );
- runtime.constructor(&[]);
- runtime.function("x", &[]);
- }
- #[test]
- fn decode_complexish() {
- let mut runtime = build_solidity(
- r##"
- pragma solidity 0;
- struct foo1 {
- int32 f1;
- string f2;
- int64[2] f3;
- int64[] f4;
- }
- contract c {
- function test(foo1[] a) public {
- assert(a.length == 1);
- assert(a[0].f2 == "Hello, World!");
- assert(a[0].f3[0] == 55);
- assert(a[0].f3[1] == 59);
- assert(a[0].f4.length == 1);
- assert(a[0].f4[0] == 102);
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.function(
- "test",
- &[ethabi::Token::Array(vec![ethabi::Token::Tuple(vec![
- ethabi::Token::Int(ethereum_types::U256::from(102)),
- ethabi::Token::String("Hello, World!".to_owned()),
- ethabi::Token::FixedArray(vec![
- ethabi::Token::Int(ethereum_types::U256::from(55)),
- ethabi::Token::Int(ethereum_types::U256::from(59)),
- ]),
- ethabi::Token::Array(vec![ethabi::Token::Int(ethereum_types::U256::from(102))]),
- ])])],
- );
- }
- #[test]
- fn decode_bad_abi() {
- let mut runtime = build_solidity(
- r##"
- contract c {
- function test(string a) public {
- }
- }"##,
- );
- runtime.constructor(&[]);
- // patch offset to be garbage
- runtime.function_abi_fail(
- "test",
- &[ethabi::Token::String("Hello, World!".to_owned())],
- |x| x[30] = 2,
- );
- // patch offset to overflow
- runtime.function_abi_fail(
- "test",
- &[ethabi::Token::String("Hello, World!".to_owned())],
- |x| {
- x[31] = 0xff;
- x[30] = 0xff;
- x[29] = 0xff;
- x[28] = 0xe0;
- },
- );
- // patch length to be garbage
- runtime.function_abi_fail(
- "test",
- &[ethabi::Token::String("Hello, World!".to_owned())],
- |x| x[62] = 2,
- );
- // patch length to overflow
- runtime.function_abi_fail(
- "test",
- &[ethabi::Token::String("Hello, World!".to_owned())],
- |x| {
- x[63] = 0xff;
- x[62] = 0xff;
- x[61] = 0xff;
- x[60] = 0xe0;
- },
- );
- }
- #[test]
- fn external_call() {
- let mut runtime = build_solidity(
- r##"
- contract b {
- int32 x;
- constructor(int32 a) public {
- x = a;
- }
- function get_x() public returns (int32) {
- return 200 * x;
- }
- }
- contract c {
- b x;
- constructor() public {
- x = new b(102);
- }
- function test() public returns (int32) {
- return x.get_x();
- }
- function enc() public returns (bytes) {
- return abi.encodeWithSignature("get_x()");
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("enc", &[]);
- assert_eq!(
- ret,
- vec!(ethabi::Token::Bytes(0x3829050au32.to_be_bytes().into()))
- );
- let ret = runtime.function("test", &[]);
- assert_eq!(
- ret,
- vec!(ethabi::Token::Int(ethereum_types::U256::from(20400)))
- );
- let mut runtime = build_solidity(
- r##"
- contract b {
- int32 x;
- constructor(int32 a) public {
- x = a;
- }
- function get_x(int32 t) public returns (int32) {
- return x * t;
- }
- }
- contract c {
- b x;
- constructor() public {
- x = new b(102);
- }
- function test() public returns (int32) {
- return x.get_x({ t: 10 });
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("test", &[]);
- assert_eq!(
- ret,
- vec!(ethabi::Token::Int(ethereum_types::U256::from(1020)))
- );
- }
- #[test]
- fn try_catch() {
- let mut runtime = build_solidity(
- r##"
- contract b {
- int32 x;
- constructor(int32 a) public {
- x = a;
- }
- function get_x(int32 t) public returns (int32) {
- if (t == 0) {
- revert("cannot be zero");
- }
- return x * t;
- }
- }
- contract c {
- b x;
- constructor() public {
- x = new b(102);
- }
- function test() public returns (int32) {
- int32 state = 0;
- try x.get_x(0) returns (int32 l) {
- state = 1;
- } catch Error(string err) {
- if (err == "cannot be zero") {
- state = 2;
- } else {
- state = 3;
- }
- } catch (bytes ) {
- state = 4;
- }
- return state;
- }
- }"##,
- );
- runtime.constructor(&[]);
- let ret = runtime.function("test", &[]);
- assert_eq!(ret, vec!(ethabi::Token::Int(ethereum_types::U256::from(2))));
- }
- #[test]
- fn payables() {
- // no contructors means can't send value
- let mut runtime = build_solidity(
- r##"
- contract c {
- function test(string a) public {
- }
- }"##,
- );
- runtime.value = 1;
- runtime.constructor_expect_revert(&[]);
- // contructors w/o payable means can't send value
- let mut runtime = build_solidity(
- r##"
- contract c {
- constructor() public {
- int32 a = 0;
- }
- function test(string a) public {
- }
- }"##,
- );
- runtime.value = 1;
- runtime.constructor_expect_revert(&[]);
- // contructors w/ payable means can send value
- let mut runtime = build_solidity(
- r##"
- contract c {
- constructor() public payable {
- int32 a = 0;
- }
- function test(string a) public {
- }
- }"##,
- );
- runtime.value = 1;
- runtime.constructor(&[]);
- // function w/o payable means can't send value
- let mut runtime = build_solidity(
- r##"
- contract c {
- function test() public {
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.value = 1;
- runtime.function_revert("test", &[]);
- // test both
- let mut runtime = build_solidity(
- r##"
- contract c {
- function test() payable public {
- }
- function test2() public {
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.value = 1;
- runtime.function_revert("test2", &[]);
- runtime.value = 1;
- runtime.function("test", &[]);
- }
- #[test]
- fn balance() {
- let mut runtime = build_solidity(
- r##"
- contract c {
- function test() public returns (uint128) {
- return address(this).balance;
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.accounts.get_mut(&runtime.vm.cur).unwrap().1 = 512;
- let ret = runtime.function("test", &[]);
- assert_eq!(
- ret,
- vec!(ethabi::Token::Uint(ethereum_types::U256::from(512)))
- );
- }
- #[test]
- fn selfdestruct() {
- let mut runtime = build_solidity(
- r##"
- contract other {
- function goaway(address payable recipient) public returns (bool) {
- selfdestruct(recipient);
- }
- }
- contract c {
- other o;
- function step1() public {
- o = new other{value: 511}();
- }
- function step2() public {
- bool foo = o.goaway(payable(address(this)));
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.function("step1", &[]);
- runtime.accounts.get_mut(&runtime.vm.cur).unwrap().1 = 0;
- runtime.function_revert("step2", &[]);
- runtime.accounts.get_mut(&runtime.vm.cur).unwrap().1 = 511;
- }
- #[test]
- fn event() {
- let mut runtime = build_solidity(
- r##"
- contract c {
- event foo (
- bool indexed y,
- int32 x
- ) anonymous;
- function func() public {
- emit foo(true, 102);
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.function("func", &[]);
- assert_eq!(runtime.events.len(), 1);
- let event = &runtime.abi.events_by_name("foo").unwrap()[0];
- for log in runtime.events() {
- let decoded = event.parse_log(log).unwrap();
- for log in &decoded.params {
- match log.name.as_str() {
- "y" => assert_eq!(log.value, ethabi::Token::Bool(true)),
- "x" => assert_eq!(
- log.value,
- ethabi::Token::Int(ethereum_types::U256::from(102))
- ),
- _ => panic!("unexpected field {}", log.name),
- }
- }
- }
- let mut runtime = build_solidity(
- r##"
- contract c {
- event foo (
- bool indexed y,
- string indexed x,
- int[2] indexed z
- );
- function func() public {
- emit foo(true, "foobar", [1, 2]);
- }
- }"##,
- );
- runtime.constructor(&[]);
- runtime.function("func", &[]);
- assert_eq!(runtime.events.len(), 1);
- let event = &runtime.abi.events_by_name("foo").unwrap()[0];
- for log in runtime.events() {
- let decoded = event.parse_log(log).unwrap();
- for log in &decoded.params {
- match log.name.as_str() {
- "y" => assert_eq!(log.value, ethabi::Token::Bool(true)),
- "x" => assert_eq!(
- log.value,
- ethabi::Token::FixedBytes(
- hex::decode(
- "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e"
- )
- .unwrap()
- )
- ),
- "z" => assert_eq!(
- log.value,
- ethabi::Token::FixedBytes(
- hex::decode(
- "e90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0"
- )
- .unwrap()
- )
- ),
- _ => panic!("unexpected field {}", log.name),
- }
- }
- }
- }
|