| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282 |
- // SPDX-License-Identifier: Apache-2.0
- use contract_metadata::ContractMetadata;
- use ink::metadata::InkProject;
- // Create WASM virtual machine like substrate
- use num_derive::FromPrimitive;
- use num_traits::FromPrimitive;
- use rand::Rng;
- use sha2::{Digest, Sha256};
- use std::{collections::HashMap, ffi::OsStr, fmt, fmt::Write};
- use tiny_keccak::{Hasher, Keccak};
- use wasmi::memory_units::Pages;
- use wasmi::*;
- use solang::file_resolver::FileResolver;
- use solang::{compile, Target};
- mod substrate_tests;
- type StorageKey = [u8; 32];
- type Account = [u8; 32];
- /// In `ink!`, u32::MAX (which is -1 in 2s complement) represents a `None` value
- const NONE_SENTINEL: RuntimeValue = RuntimeValue::I32(-1);
- fn account_new() -> Account {
- let mut rng = rand::thread_rng();
- let mut a = [0u8; 32];
- rng.fill(&mut a[..]);
- a
- }
- #[derive(Debug, Clone, PartialEq, Eq)]
- struct HostCodeTerminate {}
- impl HostError for HostCodeTerminate {}
- impl fmt::Display for HostCodeTerminate {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(f, "seal_terminate")
- }
- }
- #[derive(Debug, Clone, PartialEq, Eq)]
- struct HostCodeReturn(i32);
- impl fmt::Display for HostCodeReturn {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(f, "return {}", self.0)
- }
- }
- impl HostError for HostCodeReturn {}
- #[derive(FromPrimitive)]
- #[allow(non_camel_case_types)]
- enum SubstrateExternal {
- seal_input = 0,
- seal_set_storage,
- seal_clear_storage,
- seal_get_storage,
- seal_return,
- seal_hash_keccak_256,
- seal_debug_message,
- seal_call,
- seal_instantiate,
- seal_value_transferred,
- seal_minimum_balance,
- seal_random,
- seal_address,
- seal_balance,
- seal_terminate,
- seal_hash_sha2_256,
- seal_hash_blake2_128,
- seal_hash_blake2_256,
- seal_block_number,
- seal_now,
- seal_weight_to_fee,
- seal_gas_left,
- seal_caller,
- seal_deposit_event,
- seal_transfer,
- }
- pub struct Event {
- topics: Vec<[u8; 32]>,
- data: Vec<u8>,
- }
- pub struct VirtualMachine {
- account: Account,
- caller: Account,
- memory: MemoryRef,
- input: Vec<u8>,
- pub output: Vec<u8>,
- pub value: u128,
- }
- impl VirtualMachine {
- fn new(account: Account, caller: Account, value: u128) -> Self {
- VirtualMachine {
- memory: MemoryInstance::alloc(Pages(16), Some(Pages(16))).unwrap(),
- input: Vec::new(),
- output: Vec::new(),
- account,
- caller,
- value,
- }
- }
- }
- pub struct Program {
- abi: InkProject,
- code: Vec<u8>,
- }
- pub struct MockSubstrate {
- pub store: HashMap<(Account, StorageKey), Vec<u8>>,
- pub programs: Vec<Program>,
- pub printbuf: String,
- pub accounts: HashMap<Account, (Vec<u8>, u128)>,
- pub current_program: usize,
- pub vm: VirtualMachine,
- pub events: Vec<Event>,
- }
- impl Externals for MockSubstrate {
- #[allow(clippy::cognitive_complexity)]
- fn invoke_index(
- &mut self,
- index: usize,
- args: RuntimeArgs,
- ) -> Result<Option<RuntimeValue>, Trap> {
- macro_rules! set_seal_value {
- ($name:literal, $dest_ptr:expr, $len_ptr:expr, $buf:expr) => {{
- println!("{}: {}", $name, hex::encode($buf));
- let len = self
- .vm
- .memory
- .get_value::<u32>($len_ptr)
- .expect(&format!("{} len_ptr should be valid", $name));
- assert!(
- (len as usize) >= $buf.len(),
- "{} input is {} buffer is {}",
- $name,
- $buf.len(),
- len
- );
- if let Err(e) = self.vm.memory.set($dest_ptr, $buf) {
- panic!("{}: {}", $name, e);
- }
- self.vm
- .memory
- .set_value($len_ptr, $buf.len() as u32)
- .expect(&format!("{} len_ptr should be valid", $name));
- }};
- }
- match FromPrimitive::from_usize(index) {
- Some(SubstrateExternal::seal_input) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let len = self
- .vm
- .memory
- .get_value::<u32>(len_ptr)
- .expect("seal_input len_ptr should be valid");
- assert!(
- (len as usize) >= self.vm.input.len(),
- "input is {} seal_input buffer {}",
- self.vm.input.len(),
- len
- );
- if let Err(e) = self.vm.memory.set(dest_ptr, &self.vm.input) {
- panic!("seal_input: {}", e);
- }
- self.vm
- .memory
- .set_value(len_ptr, self.vm.input.len() as u32)
- .expect("seal_input len_ptr should be valid");
- Ok(None)
- }
- Some(SubstrateExternal::seal_get_storage) => {
- assert_eq!(args.len(), 4);
- let key_ptr: u32 = args.nth_checked(0)?;
- let key_len: u32 = args.nth_checked(1)?;
- let dest_ptr: u32 = args.nth_checked(2)?;
- let len_ptr: u32 = args.nth_checked(3)?;
- assert_eq!(key_len, 32);
- let mut key: StorageKey = [0; 32];
- if let Err(e) = self.vm.memory.get_into(key_ptr, &mut key) {
- panic!("seal_get_storage: {}", e);
- }
- if let Some(value) = self.store.get(&(self.vm.account, key)) {
- println!("seal_get_storage: {:?} = {:?}", key, value);
- let len = self
- .vm
- .memory
- .get_value::<u32>(len_ptr)
- .expect("seal_get_storage len_ptr should be valid");
- assert!(
- (len as usize) >= value.len(),
- "seal_get_storage buffer is too small"
- );
- if let Err(e) = self.vm.memory.set(dest_ptr, value) {
- panic!("seal_get_storage: {}", e);
- }
- self.vm
- .memory
- .set_value(len_ptr, value.len() as u32)
- .expect("seal_get_storage len_ptr should be valid");
- Ok(Some(RuntimeValue::I32(0)))
- } else {
- println!("seal_get_storage: {:?} = nil", key);
- Ok(Some(RuntimeValue::I32(1)))
- }
- }
- Some(SubstrateExternal::seal_clear_storage) => {
- let key_ptr: u32 = args.nth_checked(0)?;
- let key_len: u32 = args.nth_checked(1)?;
- assert_eq!(key_len, 32);
- let mut key: StorageKey = [0; 32];
- if let Err(e) = self.vm.memory.get_into(key_ptr, &mut key) {
- panic!("seal_clear_storage: {}", e);
- }
- println!("seal_clear_storage: {:?}", key);
- let pre_existing_len = self
- .store
- .remove(&(self.vm.account, key))
- .map(|e| RuntimeValue::I32(e.len() as i32))
- .or(Some(NONE_SENTINEL));
- Ok(pre_existing_len)
- }
- Some(SubstrateExternal::seal_set_storage) => {
- assert_eq!(args.len(), 4);
- let key_ptr: u32 = args.nth_checked(0)?;
- let key_len: u32 = args.nth_checked(1)?;
- let data_ptr: u32 = args.nth_checked(2)?;
- let len: u32 = args.nth_checked(3)?;
- assert_eq!(key_len, 32);
- let mut key: StorageKey = [0; 32];
- if let Err(e) = self.vm.memory.get_into(key_ptr, &mut key[..]) {
- panic!("seal_set_storage: {}", e);
- }
- let mut data = Vec::new();
- data.resize(len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("seal_set_storage: {}", e);
- }
- println!("seal_set_storage: {:?} = {:?}", key, data);
- let pre_existing_len = self
- .store
- .insert((self.vm.account, key), data)
- .map(|e| RuntimeValue::I32(e.len() as i32))
- .or(Some(NONE_SENTINEL));
- Ok(pre_existing_len)
- }
- Some(SubstrateExternal::seal_hash_keccak_256) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let out_ptr: u32 = args.nth_checked(2)?;
- let mut data = Vec::new();
- data.resize(len as usize, 0);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("seal_hash_keccak_256: {}", e);
- }
- let mut hasher = Keccak::v256();
- let mut hash = [0u8; 32];
- hasher.update(&data);
- hasher.finalize(&mut hash);
- println!(
- "seal_hash_keccak_256: {} = {}",
- hex::encode(data),
- hex::encode(hash)
- );
- if let Err(e) = self.vm.memory.set(out_ptr, &hash) {
- panic!("seal_hash_keccak_256: {}", e);
- }
- Ok(None)
- }
- Some(SubstrateExternal::seal_hash_sha2_256) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let out_ptr: u32 = args.nth_checked(2)?;
- let mut data = Vec::new();
- data.resize(len as usize, 0);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("seal_hash_sha2_256: {}", e);
- }
- let mut hasher = Sha256::new();
- hasher.update(&data);
- let hash = hasher.finalize();
- println!(
- "seal_hash_sha2_256: {} = {}",
- hex::encode(data),
- hex::encode(hash)
- );
- if let Err(e) = self.vm.memory.set(out_ptr, &hash) {
- panic!("seal_hash_sha2_256: {}", e);
- }
- Ok(None)
- }
- Some(SubstrateExternal::seal_hash_blake2_128) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let out_ptr: u32 = args.nth_checked(2)?;
- let mut data = Vec::new();
- data.resize(len as usize, 0);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("seal_hash_blake2_128: {}", e);
- }
- let hash = blake2_rfc::blake2b::blake2b(16, &[], &data);
- println!(
- "seal_hash_blake2_128: {} = {}",
- hex::encode(data),
- hex::encode(hash)
- );
- if let Err(e) = self.vm.memory.set(out_ptr, hash.as_bytes()) {
- panic!("seal_hash_blake2_128: {}", e);
- }
- Ok(None)
- }
- Some(SubstrateExternal::seal_hash_blake2_256) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let out_ptr: u32 = args.nth_checked(2)?;
- let mut data = Vec::new();
- data.resize(len as usize, 0);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("seal_hash_blake2_256: {}", e);
- }
- let hash = blake2_rfc::blake2b::blake2b(32, &[], &data);
- println!(
- "seal_hash_blake2_256: {} = {}",
- hex::encode(data),
- hex::encode(hash)
- );
- if let Err(e) = self.vm.memory.set(out_ptr, hash.as_bytes()) {
- panic!("seal_hash_blake2_256: {}", e);
- }
- Ok(None)
- }
- Some(SubstrateExternal::seal_return) => {
- let flags: i32 = args.nth_checked(0)?;
- let data_ptr: u32 = args.nth_checked(1)?;
- let len: u32 = args.nth_checked(2)?;
- self.vm.output.resize(len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut self.vm.output) {
- panic!("seal_return: {}", e);
- }
- match flags {
- 0 | 1 => Err(Trap::new(TrapKind::Host(Box::new(HostCodeReturn(flags))))),
- _ => panic!("seal_return flag {} not valid", flags),
- }
- }
- Some(SubstrateExternal::seal_debug_message) => {
- 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!("seal_debug_message: {}", e);
- }
- let s = String::from_utf8(buf).expect("seal_debug_message: Invalid UFT8");
- println!("seal_debug_message: {}", s);
- self.printbuf.push_str(&s);
- Ok(Some(RuntimeValue::I32(0)))
- }
- Some(SubstrateExternal::seal_random) => {
- let data_ptr: u32 = args.nth_checked(0)?;
- let len: u32 = args.nth_checked(1)?;
- let dest_ptr: u32 = args.nth_checked(2)?;
- let len_ptr: u32 = args.nth_checked(3)?;
- 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!("seal_random: {}", e);
- }
- let mut hash = [0u8; 32];
- hash.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], &buf).as_bytes());
- println!("seal_random: {} {}", hex::encode(buf), hex::encode(hash));
- let len = self
- .vm
- .memory
- .get_value::<u32>(len_ptr)
- .expect("seal_random len_ptr should be valid");
- assert!(
- (len as usize) >= hash.len(),
- "seal_random dest buffer is too small"
- );
- if let Err(e) = self.vm.memory.set(dest_ptr, &hash) {
- panic!("seal_random: {}", e);
- }
- self.vm
- .memory
- .set_value(len_ptr, hash.len() as u32)
- .expect("seal_random len_ptr should be valid");
- Ok(None)
- }
- Some(SubstrateExternal::seal_call) => {
- let flags: u32 = args.nth_checked(0)?;
- let account_ptr: u32 = args.nth_checked(1)?;
- // Gas usage is ignored in the mock VM
- let value_ptr: u32 = args.nth_checked(3)?;
- let input_ptr: u32 = args.nth_checked(4)?;
- let input_len: u32 = args.nth_checked(5)?;
- let output_ptr: u32 = args.nth_checked(6)?;
- let output_len_ptr: u32 = args.nth_checked(7)?;
- assert_eq!(flags, 0); //TODO: Call flags are not yet implemented
- let mut account = [0u8; 32];
- if let Err(e) = self.vm.memory.get_into(account_ptr, &mut account) {
- panic!("seal_call: {}", e);
- }
- let mut value = [0u8; 16];
- if let Err(e) = self.vm.memory.get_into(value_ptr, &mut value) {
- panic!("seal_call: {}", e);
- }
- let value = u128::from_le_bytes(value);
- if !self.accounts.contains_key(&account) {
- // substrate would return NotCallable
- return Ok(Some(RuntimeValue::I32(0x8)));
- }
- let mut input = Vec::new();
- input.resize(input_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(input_ptr, &mut input) {
- panic!("seal_call: {}", e);
- }
- println!(
- "seal_call: account={} input={}",
- hex::encode(account),
- hex::encode(&input)
- );
- let mut vm = VirtualMachine::new(account, self.vm.account, value);
- std::mem::swap(&mut self.vm, &mut vm);
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = input;
- let ret = module.invoke_export("call", &[], self);
- let ret = match ret {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- if let Some(ret) = host_error.downcast_ref::<HostCodeReturn>() {
- Some(RuntimeValue::I32(ret.0))
- } else if host_error.downcast_ref::<HostCodeTerminate>().is_some() {
- Some(RuntimeValue::I32(1))
- } else {
- return Err(trap);
- }
- }
- _ => {
- return Err(trap);
- }
- },
- Ok(v) => v,
- Err(e) => panic!("fail to invoke call: {}", e),
- };
- let output = self.vm.output.clone();
- std::mem::swap(&mut self.vm, &mut vm);
- println!("seal_call ret={:?} buf={}", ret, hex::encode(&output));
- if let Some(acc) = self.accounts.get_mut(&vm.account) {
- acc.1 += vm.value;
- }
- set_seal_value!("seal_call return buf", output_ptr, output_len_ptr, &output);
- Ok(ret)
- }
- Some(SubstrateExternal::seal_transfer) => {
- let account_ptr: u32 = args.nth_checked(0)?;
- let account_len: u32 = args.nth_checked(1)?;
- let value_ptr: u32 = args.nth_checked(2)?;
- let value_len: u32 = args.nth_checked(3)?;
- let mut account = [0u8; 32];
- assert!(account_len == 32, "seal_transfer: len = {}", account_len);
- if let Err(e) = self.vm.memory.get_into(account_ptr, &mut account) {
- panic!("seal_transfer: {}", e);
- }
- let mut value = [0u8; 16];
- assert!(value_len == 16, "seal_transfer: len = {}", value_len);
- if let Err(e) = self.vm.memory.get_into(value_ptr, &mut value) {
- panic!("seal_transfer: {}", e);
- }
- let value = u128::from_le_bytes(value);
- if !self.accounts.contains_key(&account) {
- // substrate would return TransferFailed
- return Ok(Some(RuntimeValue::I32(0x5)));
- }
- if let Some(acc) = self.accounts.get_mut(&account) {
- acc.1 += value;
- }
- Ok(Some(RuntimeValue::I32(0)))
- }
- Some(SubstrateExternal::seal_instantiate) => {
- let codehash_ptr: u32 = args.nth_checked(0)?;
- // Gas usage is ignored in the mock VM
- 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 account_ptr: u32 = args.nth_checked(5)?;
- let account_len_ptr: u32 = args.nth_checked(6)?;
- let output_ptr: u32 = args.nth_checked(7)?;
- let output_len_ptr: u32 = args.nth_checked(8)?;
- let salt_ptr: u32 = args.nth_checked(9)?;
- let salt_len: u32 = args.nth_checked(10)?;
- let mut codehash = [0u8; 32];
- if let Err(e) = self.vm.memory.get_into(codehash_ptr, &mut codehash) {
- panic!("seal_instantiate: {}", e);
- }
- let mut value = [0u8; 16];
- if let Err(e) = self.vm.memory.get_into(value_ptr, &mut value) {
- panic!("seal_instantiate: {}", e);
- }
- let value = u128::from_le_bytes(value);
- let mut input = Vec::new();
- input.resize(input_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(input_ptr, &mut input) {
- panic!("seal_instantiate: {}", e);
- }
- let mut salt = Vec::new();
- salt.resize(salt_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(salt_ptr, &mut salt) {
- panic!("seal_instantiate: {}", e);
- }
- println!(
- "seal_instantiate value:{} input={} salt={}",
- value,
- hex::encode(&input),
- hex::encode(&salt),
- );
- let mut account = [0u8; 32];
- let hash_data: Vec<u8> = input.iter().chain(salt.iter()).cloned().collect();
- account
- .copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], &hash_data).as_bytes());
- if self.accounts.contains_key(&account) {
- // substrate would return TRAP_RETURN_CODE (0x0100)
- return Ok(Some(RuntimeValue::I32(0x100)));
- }
- let program = self
- .programs
- .iter()
- .find(|program| {
- blake2_rfc::blake2b::blake2b(32, &[], &program.code).as_bytes() == codehash
- })
- .expect("codehash not found");
- self.accounts.insert(account, (program.code.clone(), 0));
- let mut input = Vec::new();
- input.resize(input_len as usize, 0u8);
- if let Err(e) = self.vm.memory.get_into(input_ptr, &mut input) {
- panic!("seal_instantiate: {}", e);
- }
- let mut vm = VirtualMachine::new(account, self.vm.account, value);
- std::mem::swap(&mut self.vm, &mut vm);
- let module = self.create_module(&program.code);
- self.vm.input = input;
- let ret = match module.invoke_export("deploy", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- if let Some(ret) = host_error.downcast_ref::<HostCodeReturn>() {
- Some(RuntimeValue::I32(ret.0))
- } else {
- return Err(trap);
- }
- }
- _ => {
- return Err(trap);
- }
- },
- Ok(v) => v,
- Err(e) => panic!("fail to invoke deploy: {}", e),
- };
- let output = self.vm.output.clone();
- std::mem::swap(&mut self.vm, &mut vm);
- set_seal_value!(
- "seal_instantiate output",
- output_ptr,
- output_len_ptr,
- &output
- );
- if let Some(RuntimeValue::I32(0)) = ret {
- self.accounts.get_mut(&vm.account).unwrap().1 += vm.value;
- set_seal_value!(
- "seal_instantiate account",
- account_ptr,
- account_len_ptr,
- &account
- );
- }
- println!("seal_instantiate ret:{:?}", ret);
- Ok(ret)
- }
- Some(SubstrateExternal::seal_value_transferred) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = self.vm.value.to_le_bytes();
- set_seal_value!("seal_value_transferred", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_address) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = self.vm.account;
- set_seal_value!("seal_address", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_caller) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = self.vm.caller;
- set_seal_value!("seal_caller", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_balance) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = self.accounts[&self.vm.account].1.to_le_bytes();
- set_seal_value!("seal_balance", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_minimum_balance) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = 500u128.to_le_bytes();
- set_seal_value!("seal_minimum_balance", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_block_number) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = 950_119_597u32.to_le_bytes();
- set_seal_value!("seal_block_number", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_now) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = 1594035638000u64.to_le_bytes();
- set_seal_value!("seal_now", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_gas_left) => {
- let dest_ptr: u32 = args.nth_checked(0)?;
- let len_ptr: u32 = args.nth_checked(1)?;
- let scratch = 2_224_097_461u64.to_le_bytes();
- set_seal_value!("seal_gas_left", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_weight_to_fee) => {
- let units: u64 = args.nth_checked(0)?;
- let dest_ptr: u32 = args.nth_checked(1)?;
- let len_ptr: u32 = args.nth_checked(2)?;
- let scratch = (59_541_253_813_967u128 * units as u128).to_le_bytes();
- set_seal_value!("seal_weight_to_fee", dest_ptr, len_ptr, &scratch);
- Ok(None)
- }
- Some(SubstrateExternal::seal_terminate) => {
- let account_ptr: u32 = args.nth_checked(0)?;
- let mut account = [0u8; 32];
- if let Err(e) = self.vm.memory.get_into(account_ptr, &mut account) {
- panic!("seal_terminate: {}", e);
- }
- let remaining = self.accounts[&self.vm.account].1;
- self.accounts.get_mut(&account).unwrap().1 += remaining;
- println!("seal_terminate: {} {}", hex::encode(account), remaining);
- self.accounts.remove(&self.vm.account);
- Err(Trap::new(TrapKind::Host(Box::new(HostCodeTerminate {}))))
- }
- Some(SubstrateExternal::seal_deposit_event) => {
- let mut topic_ptr: u32 = args.nth_checked(0)?;
- let topic_len: u32 = args.nth_checked(1)?;
- let data_ptr: u32 = args.nth_checked(2)?;
- let data_len: u32 = args.nth_checked(3)?;
- let mut topics = Vec::new();
- if topic_len != 0 {
- assert_eq!(topic_len % 32, 1);
- assert_eq!((topic_len - 1) % 32, 0);
- let mut vec_length = [0u8];
- if let Err(e) = self.vm.memory.get_into(topic_ptr, &mut vec_length) {
- panic!("seal_deposit_event: topic: {}", e);
- }
- println!("topic_len: {} first byte: {}", topic_len, vec_length[0]);
- assert_eq!(vec_length[0] as u32, (topic_len - 1) / 8);
- topic_ptr += 1;
- }
- for _ in 0..topic_len / 32 {
- let mut topic = [0u8; 32];
- if let Err(e) = self.vm.memory.get_into(topic_ptr, &mut topic) {
- panic!("seal_deposit_event: topic: {}", e);
- }
- topics.push(topic);
- topic_ptr += 32;
- }
- let mut data = Vec::new();
- data.resize(data_len as usize, 0);
- if let Err(e) = self.vm.memory.get_into(data_ptr, &mut data) {
- panic!("seal_deposit_event: data: {}", e);
- }
- println!(
- "seal_deposit_event: topic: {} data: {}",
- topics
- .iter()
- .map(hex::encode)
- .collect::<Vec<String>>()
- .join(" "),
- hex::encode(&data)
- );
- self.events.push(Event { topics, data });
- Ok(None)
- }
- _ => panic!("external {} unknown", index),
- }
- }
- }
- impl ModuleImportResolver for MockSubstrate {
- fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
- let index = match field_name {
- "seal_input" => SubstrateExternal::seal_input,
- "seal_get_storage" => SubstrateExternal::seal_get_storage,
- "seal_set_storage" => SubstrateExternal::seal_set_storage,
- "seal_clear_storage" => SubstrateExternal::seal_clear_storage,
- "seal_return" => SubstrateExternal::seal_return,
- "seal_hash_sha2_256" => SubstrateExternal::seal_hash_sha2_256,
- "seal_hash_keccak_256" => SubstrateExternal::seal_hash_keccak_256,
- "seal_hash_blake2_128" => SubstrateExternal::seal_hash_blake2_128,
- "seal_hash_blake2_256" => SubstrateExternal::seal_hash_blake2_256,
- "seal_debug_message" => SubstrateExternal::seal_debug_message,
- "seal_call" => SubstrateExternal::seal_call,
- "seal_instantiate" => SubstrateExternal::seal_instantiate,
- "seal_value_transferred" => SubstrateExternal::seal_value_transferred,
- "seal_minimum_balance" => SubstrateExternal::seal_minimum_balance,
- "seal_random" => SubstrateExternal::seal_random,
- "seal_address" => SubstrateExternal::seal_address,
- "seal_balance" => SubstrateExternal::seal_balance,
- "seal_terminate" => SubstrateExternal::seal_terminate,
- "seal_block_number" => SubstrateExternal::seal_block_number,
- "seal_now" => SubstrateExternal::seal_now,
- "seal_weight_to_fee" => SubstrateExternal::seal_weight_to_fee,
- "seal_gas_left" => SubstrateExternal::seal_gas_left,
- "seal_caller" => SubstrateExternal::seal_caller,
- "seal_deposit_event" => SubstrateExternal::seal_deposit_event,
- "seal_transfer" => SubstrateExternal::seal_transfer,
- _ => {
- 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 MockSubstrate {
- 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("env", self)
- .with_resolver("seal0", self)
- .with_resolver("seal1", self)
- .with_resolver("seal2", self),
- )
- .expect("Failed to instantiate module")
- .run_start(&mut NopExternals)
- .expect("Failed to run start function in module")
- }
- fn invoke_deploy(&mut self, module: ModuleRef) -> Option<RuntimeValue> {
- match module.invoke_export("deploy", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- if let Some(ret) = host_error.downcast_ref::<HostCodeReturn>() {
- Some(RuntimeValue::I32(ret.0))
- } else {
- panic!("did not go as planned");
- }
- }
- _ => panic!("fail to invoke deploy: {}", trap),
- },
- Ok(v) => v,
- Err(e) => panic!("fail to invoke deploy: {}", e),
- }
- }
- fn invoke_call(&mut self, module: ModuleRef) -> Option<RuntimeValue> {
- match module.invoke_export("call", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Host(host_error) => {
- if let Some(ret) = host_error.downcast_ref::<HostCodeReturn>() {
- Some(RuntimeValue::I32(ret.0))
- } else if host_error.downcast_ref::<HostCodeTerminate>().is_some() {
- Some(RuntimeValue::I32(1))
- } else {
- panic!("did not go as planned");
- }
- }
- _ => panic!("fail to invoke call: {}", trap),
- },
- Ok(v) => v,
- Err(e) => panic!("fail to invoke call: {}", e),
- }
- }
- pub fn set_program(&mut self, index: usize) {
- let account = account_new();
- let code = self.programs[index].code.clone();
- self.accounts.insert(account, (code, 0));
- self.vm = VirtualMachine::new(account, account_new(), 0);
- self.current_program = index;
- }
- pub fn constructor(&mut self, index: usize, args: Vec<u8>) {
- let m = &self.programs[self.current_program]
- .abi
- .spec()
- .constructors()[index];
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = m
- .selector()
- .to_bytes()
- .iter()
- .copied()
- .chain(args)
- .collect();
- let ret = self.invoke_deploy(module);
- if let Some(RuntimeValue::I32(ret)) = ret {
- if ret != 0 {
- panic!("non zero return")
- }
- }
- }
- pub fn constructor_expect_return(&mut self, index: usize, expected_ret: i32, args: Vec<u8>) {
- let m = &self.programs[self.current_program]
- .abi
- .spec()
- .constructors()[index];
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = m
- .selector()
- .to_bytes()
- .iter()
- .copied()
- .chain(args)
- .collect();
- let ret = self.invoke_deploy(module);
- if let Some(RuntimeValue::I32(ret)) = ret {
- println!(
- "function_expected_return: got {} expected {}",
- ret, expected_ret
- );
- if expected_ret != ret {
- panic!("non one return")
- }
- }
- }
- pub fn function(&mut self, name: &str, args: Vec<u8>) {
- let m = self.programs[self.current_program]
- .abi
- .spec()
- .messages()
- .iter()
- .find(|f| f.label() == name)
- .unwrap();
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = m
- .selector()
- .to_bytes()
- .iter()
- .copied()
- .chain(args)
- .collect();
- println!("input:{}", hex::encode(&self.vm.input));
- if let Some(RuntimeValue::I32(ret)) = self.invoke_call(module) {
- assert!(ret == 0, "non zero return: {}", ret);
- }
- }
- pub fn function_expect_failure(&mut self, name: &str, args: Vec<u8>) {
- let m = self.programs[self.current_program]
- .abi
- .spec()
- .messages()
- .iter()
- .find(|m| m.label() == name)
- .unwrap();
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = m
- .selector()
- .to_bytes()
- .iter()
- .copied()
- .chain(args)
- .collect();
- match module.invoke_export("call", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Unreachable => (),
- _ => panic!("trap: {:?}", trap),
- },
- Err(err) => {
- panic!("unexpected error: {:?}", err);
- }
- Ok(v) => {
- panic!("unexpected return value: {:?}", v);
- }
- }
- }
- pub fn raw_function(&mut self, input: Vec<u8>) {
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = input;
- if let Some(RuntimeValue::I32(ret)) = self.invoke_call(module) {
- if ret != 0 {
- panic!("non zero return")
- }
- }
- }
- pub fn raw_function_failure(&mut self, input: Vec<u8>) {
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = input;
- match module.invoke_export("call", &[], self) {
- Err(wasmi::Error::Trap(trap)) => match trap.kind() {
- TrapKind::Unreachable => (),
- _ => panic!("trap: {:?}", trap),
- },
- Err(err) => {
- panic!("unexpected error: {:?}", err);
- }
- Ok(v) => {
- panic!("unexpected return value: {:?}", v);
- }
- }
- }
- pub fn raw_constructor(&mut self, input: Vec<u8>) {
- let module = self.create_module(&self.accounts.get(&self.vm.account).unwrap().0);
- self.vm.input = input;
- if let Some(RuntimeValue::I32(ret)) = self.invoke_deploy(module) {
- if ret != 0 {
- panic!("non zero return")
- }
- }
- }
- pub fn heap_verify(&self) {
- let memsize = self.vm.memory.current_size().0 * 0x10000;
- println!("memory size:{}", memsize);
- let mut buf = Vec::new();
- buf.resize(memsize, 0);
- let mut current_elem = 0x10000;
- let mut last_elem = 0u32;
- loop {
- let next: u32 = self.vm.memory.get_value(current_elem).unwrap();
- let prev: u32 = self.vm.memory.get_value(current_elem + 4).unwrap();
- let length: u32 = self.vm.memory.get_value(current_elem + 8).unwrap();
- let allocated: u32 = self.vm.memory.get_value(current_elem + 12).unwrap();
- println!(
- "next:{:08x} prev:{:08x} length:{} allocated:{}",
- next, prev, length, allocated
- );
- let mut buf = vec![0u8; length as usize];
- self.vm
- .memory
- .get_into(current_elem + 16, &mut buf)
- .unwrap();
- if allocated == 0 {
- println!("{:08x} {} not allocated", current_elem + 16, length);
- } else {
- println!("{:08x} {} allocated", current_elem + 16, length);
- assert_eq!(allocated & 0xffff, 1);
- for offset in (0..buf.len()).step_by(16) {
- let mut hex = "\t".to_string();
- let mut chars = "\t".to_string();
- for i in 0..16 {
- if offset + i >= buf.len() {
- break;
- }
- let b = buf[offset + i];
- write!(hex, " {:02x}", b).unwrap();
- if b.is_ascii() && !b.is_ascii_control() {
- write!(chars, " {}", b as char).unwrap();
- } else {
- chars.push_str(" ");
- }
- }
- println!("{}\n{}", hex, chars);
- }
- }
- assert_eq!(last_elem, prev);
- if next == 0 {
- break;
- }
- last_elem = current_elem;
- current_elem = next;
- }
- }
- }
- pub fn build_solidity(src: &str) -> MockSubstrate {
- build_solidity_with_options(src, false, false)
- }
- pub fn build_solidity_with_options(
- src: &str,
- math_overflow_flag: bool,
- log_api_return_codes: bool,
- ) -> MockSubstrate {
- 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::default_substrate(),
- math_overflow_flag,
- log_api_return_codes,
- );
- ns.print_diagnostics_in_plain(&cache, false);
- assert!(!res.is_empty());
- let programs: Vec<Program> = res
- .iter()
- .map(|res| Program {
- code: res.0.clone(),
- abi: load_abi(&res.1),
- })
- .collect();
- let mut accounts = HashMap::new();
- let account = account_new();
- accounts.insert(account, (programs[0].code.clone(), 0));
- let vm = VirtualMachine::new(account, account_new(), 0);
- MockSubstrate {
- accounts,
- printbuf: String::new(),
- store: HashMap::new(),
- programs,
- vm,
- current_program: 0,
- events: Vec::new(),
- }
- }
- fn load_abi(s: &str) -> InkProject {
- let bundle = serde_json::from_str::<ContractMetadata>(s).unwrap();
- serde_json::from_value::<InkProject>(serde_json::to_value(bundle.abi).unwrap()).unwrap()
- }
|