| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238 |
- // 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,
- instantiation_nonce,
- 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: {key:?} = nil");
- 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 {flags} not valid"),
- }
- }
- 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::instantiation_nonce) => {
- Ok(Some(RuntimeValue::I64(self.accounts.len() as i64)))
- }
- 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 {index} unknown"),
- }
- }
- }
- 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,
- "instantiation_nonce" => SubstrateExternal::instantiation_nonce,
- "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!("{field_name} not implemented");
- }
- };
- 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 {ret} expected {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:{next:08x} prev:{prev:08x} length:{length} allocated:{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, " {b:02x}").unwrap();
- if b.is_ascii() && !b.is_ascii_control() {
- write!(chars, " {}", b as char).unwrap();
- } else {
- chars.push_str(" ");
- }
- }
- println!("{hex}\n{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, true)
- }
- pub fn build_solidity_with_options(
- src: &str,
- log_api_return_codes: bool,
- log_runtime_errors: 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(),
- log_api_return_codes,
- log_runtime_errors,
- );
- 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()
- }
|