|
|
@@ -11,10 +11,19 @@ use solana_rbpf::{
|
|
|
user_error::UserError,
|
|
|
vm::{Config, DefaultInstructionMeter, EbpfVm, Executable, Syscall, SyscallObject},
|
|
|
};
|
|
|
-use solang::{compile, file_cache::FileCache, sema::diagnostics, Target};
|
|
|
+use solang::{
|
|
|
+ compile,
|
|
|
+ file_cache::FileCache,
|
|
|
+ sema::{ast, diagnostics},
|
|
|
+ Target,
|
|
|
+};
|
|
|
use std::alloc::Layout;
|
|
|
+use std::cell::RefCell;
|
|
|
+use std::collections::HashMap;
|
|
|
+use std::convert::TryInto;
|
|
|
use std::io::Write;
|
|
|
use std::mem::{align_of, size_of};
|
|
|
+use std::rc::Rc;
|
|
|
|
|
|
mod solana_tests;
|
|
|
|
|
|
@@ -30,7 +39,23 @@ fn account_new() -> Account {
|
|
|
a
|
|
|
}
|
|
|
|
|
|
-fn build_solidity(src: &str) -> Program {
|
|
|
+struct VirtualMachine {
|
|
|
+ account_data: HashMap<Account, (Vec<u8>, Option<Account>)>,
|
|
|
+ programs: Vec<Contract>,
|
|
|
+ stack: Vec<Contract>,
|
|
|
+ returndata: Account,
|
|
|
+ printbuf: String,
|
|
|
+ output: Vec<u8>,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+struct Contract {
|
|
|
+ program: Account,
|
|
|
+ abi: ethabi::Contract,
|
|
|
+ data: Account,
|
|
|
+}
|
|
|
+
|
|
|
+fn build_solidity(src: &str) -> VirtualMachine {
|
|
|
let mut cache = FileCache::new();
|
|
|
|
|
|
cache.set_file_contents("test.sol", src.to_string());
|
|
|
@@ -51,27 +76,46 @@ fn build_solidity(src: &str) -> Program {
|
|
|
|
|
|
assert_eq!(res.is_empty(), false);
|
|
|
|
|
|
+ let mut account_data = HashMap::new();
|
|
|
+ let mut programs = Vec::new();
|
|
|
+
|
|
|
// resolve
|
|
|
- let (code, abi) = res.last().unwrap().clone();
|
|
|
+ for (code, abi) in res {
|
|
|
+ let program = account_new();
|
|
|
+
|
|
|
+ account_data.insert(program, (code.clone(), None));
|
|
|
+
|
|
|
+ let abi = ethabi::Contract::load(abi.as_bytes()).unwrap();
|
|
|
+
|
|
|
+ let data = account_new();
|
|
|
+
|
|
|
+ account_data.insert(data, ([0u8; 4096].to_vec(), Some(program)));
|
|
|
+
|
|
|
+ programs.push(Contract { program, abi, data });
|
|
|
+ }
|
|
|
|
|
|
- Program {
|
|
|
- code,
|
|
|
- abi: ethabi::Contract::load(abi.as_bytes()).unwrap(),
|
|
|
- account: account_new(),
|
|
|
+ let returndata = account_new();
|
|
|
+
|
|
|
+ account_data.insert(returndata, ([0u8; 1024].to_vec(), None));
|
|
|
+
|
|
|
+ let cur = programs.last().unwrap().clone();
|
|
|
+
|
|
|
+ VirtualMachine {
|
|
|
+ account_data,
|
|
|
+ programs,
|
|
|
+ stack: vec![cur],
|
|
|
+ returndata,
|
|
|
printbuf: String::new(),
|
|
|
output: Vec::new(),
|
|
|
- data: Vec::new(),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const MAX_PERMITTED_DATA_INCREASE: usize = 10 * 1024;
|
|
|
|
|
|
-fn serialize_parameters(input: &[u8], account: &Account, data: &[u8]) -> Vec<u8> {
|
|
|
+fn serialize_parameters(input: &[u8], vm: &VirtualMachine) -> Vec<u8> {
|
|
|
let mut v: Vec<u8> = Vec::new();
|
|
|
|
|
|
- // ka_num
|
|
|
- v.write_u64::<LittleEndian>(2).unwrap();
|
|
|
- for account_no in 0..2 {
|
|
|
+ fn serialize_account(v: &mut Vec<u8>, key: &Account, acc: &(Vec<u8>, Option<Account>)) {
|
|
|
// dup_info
|
|
|
v.write_u8(0xff).unwrap();
|
|
|
// signer
|
|
|
@@ -83,27 +127,15 @@ fn serialize_parameters(input: &[u8], account: &Account, data: &[u8]) -> Vec<u8>
|
|
|
// padding
|
|
|
v.write_all(&[0u8; 4]).unwrap();
|
|
|
// key
|
|
|
- if account_no == 1 {
|
|
|
- v.write_all(&account[..]).unwrap();
|
|
|
- } else {
|
|
|
- v.write_all(&account_new()).unwrap();
|
|
|
- }
|
|
|
+ v.write_all(key).unwrap();
|
|
|
// owner
|
|
|
- v.write_all(&[0u8; 32]).unwrap();
|
|
|
+ v.write_all(&acc.1.unwrap_or([0u8; 32])).unwrap();
|
|
|
// lamports
|
|
|
v.write_u64::<LittleEndian>(0).unwrap();
|
|
|
|
|
|
// account data
|
|
|
- // data len
|
|
|
- if account_no == 1 {
|
|
|
- v.write_u64::<LittleEndian>(4096).unwrap();
|
|
|
- let mut data = data.to_vec();
|
|
|
- data.resize(4096, 0);
|
|
|
- v.write_all(&data).unwrap();
|
|
|
- } else {
|
|
|
- v.write_u64::<LittleEndian>(4096).unwrap();
|
|
|
- v.write_all(&[0u8; 4096]).unwrap();
|
|
|
- }
|
|
|
+ v.write_u64::<LittleEndian>(acc.0.len() as u64).unwrap();
|
|
|
+ v.write_all(&acc.0).unwrap();
|
|
|
v.write_all(&[0u8; MAX_PERMITTED_DATA_INCREASE]).unwrap();
|
|
|
|
|
|
let padding = v.len() % 8;
|
|
|
@@ -116,6 +148,18 @@ fn serialize_parameters(input: &[u8], account: &Account, data: &[u8]) -> Vec<u8>
|
|
|
v.write_u64::<LittleEndian>(0).unwrap();
|
|
|
}
|
|
|
|
|
|
+ // ka_num
|
|
|
+ v.write_u64::<LittleEndian>(vm.account_data.len() as u64)
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ serialize_account(&mut v, &vm.returndata, &vm.account_data[&vm.returndata]);
|
|
|
+
|
|
|
+ for (key, data) in &vm.account_data {
|
|
|
+ if key != &vm.returndata {
|
|
|
+ serialize_account(&mut v, key, data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// calldata
|
|
|
v.write_u64::<LittleEndian>(input.len() as u64).unwrap();
|
|
|
v.write_all(input).unwrap();
|
|
|
@@ -127,21 +171,29 @@ fn serialize_parameters(input: &[u8], account: &Account, data: &[u8]) -> Vec<u8>
|
|
|
}
|
|
|
|
|
|
// We want to extract the account data
|
|
|
-fn deserialize_parameters(input: &[u8]) -> Vec<Vec<u8>> {
|
|
|
+fn deserialize_parameters(
|
|
|
+ input: &[u8],
|
|
|
+ accounts_data: &mut HashMap<Account, (Vec<u8>, Option<Account>)>,
|
|
|
+) {
|
|
|
let mut start = 0;
|
|
|
|
|
|
let ka_num = LittleEndian::read_u64(&input[start..]);
|
|
|
start += size_of::<u64>();
|
|
|
|
|
|
- let mut res = Vec::new();
|
|
|
-
|
|
|
for _ in 0..ka_num {
|
|
|
- start += 8 + 32 + 32 + 8;
|
|
|
+ start += 8;
|
|
|
+
|
|
|
+ let account: Account = input[start..start + 32].try_into().unwrap();
|
|
|
+
|
|
|
+ start += 32 + 32 + 8;
|
|
|
|
|
|
let data_len = LittleEndian::read_u64(&input[start..]) as usize;
|
|
|
start += size_of::<u64>();
|
|
|
+ let data = input[start..start + data_len].to_vec();
|
|
|
|
|
|
- res.push(input[start..start + data_len].to_vec());
|
|
|
+ if let Some(entry) = accounts_data.get_mut(&account) {
|
|
|
+ entry.0 = data;
|
|
|
+ }
|
|
|
|
|
|
start += data_len + MAX_PERMITTED_DATA_INCREASE;
|
|
|
|
|
|
@@ -152,21 +204,10 @@ fn deserialize_parameters(input: &[u8]) -> Vec<Vec<u8>> {
|
|
|
|
|
|
start += size_of::<u64>();
|
|
|
}
|
|
|
-
|
|
|
- res
|
|
|
-}
|
|
|
-
|
|
|
-struct Program {
|
|
|
- code: Vec<u8>,
|
|
|
- abi: ethabi::Contract,
|
|
|
- account: Account,
|
|
|
- printbuf: String,
|
|
|
- data: Vec<u8>,
|
|
|
- output: Vec<u8>,
|
|
|
}
|
|
|
|
|
|
struct Printer<'a> {
|
|
|
- buf: &'a mut String,
|
|
|
+ context: Rc<RefCell<&'a mut VirtualMachine>>,
|
|
|
}
|
|
|
|
|
|
impl<'a> SyscallObject<UserError> for Printer<'a> {
|
|
|
@@ -194,7 +235,9 @@ impl<'a> SyscallObject<UserError> for Printer<'a> {
|
|
|
))
|
|
|
.unwrap();
|
|
|
println!("log: {}", message);
|
|
|
- self.buf.push_str(message);
|
|
|
+ if let Ok(mut context) = self.context.try_borrow_mut() {
|
|
|
+ context.printbuf.push_str(message);
|
|
|
+ }
|
|
|
Ok(0)
|
|
|
}
|
|
|
}
|
|
|
@@ -240,15 +283,220 @@ impl SyscallObject<UserError> for SyscallAllocFree {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Program {
|
|
|
- fn execute(&mut self, buf: &mut String, calldata: &[u8]) {
|
|
|
+/// Rust representation of C's SolInstruction
|
|
|
+#[derive(Debug)]
|
|
|
+struct SolInstruction {
|
|
|
+ program_id_addr: u64,
|
|
|
+ accounts_addr: u64,
|
|
|
+ accounts_len: usize,
|
|
|
+ data_addr: u64,
|
|
|
+ data_len: usize,
|
|
|
+}
|
|
|
+
|
|
|
+/// Rust representation of C's SolAccountMeta
|
|
|
+#[derive(Debug)]
|
|
|
+struct SolAccountMeta {
|
|
|
+ pubkey_addr: u64,
|
|
|
+ is_writable: bool,
|
|
|
+ is_signer: bool,
|
|
|
+}
|
|
|
+
|
|
|
+/// Rust representation of C's SolAccountInfo
|
|
|
+#[derive(Debug)]
|
|
|
+struct SolAccountInfo {
|
|
|
+ key_addr: u64,
|
|
|
+ lamports_addr: u64,
|
|
|
+ data_len: u64,
|
|
|
+ data_addr: u64,
|
|
|
+ owner_addr: u64,
|
|
|
+ rent_epoch: u64,
|
|
|
+ is_signer: bool,
|
|
|
+ is_writable: bool,
|
|
|
+ executable: bool,
|
|
|
+}
|
|
|
+
|
|
|
+/// Rust representation of C's SolSignerSeed
|
|
|
+#[derive(Debug)]
|
|
|
+struct SolSignerSeedC {
|
|
|
+ addr: u64,
|
|
|
+ len: u64,
|
|
|
+}
|
|
|
+
|
|
|
+/// Rust representation of C's SolSignerSeeds
|
|
|
+#[derive(Debug)]
|
|
|
+struct SolSignerSeedsC {
|
|
|
+ addr: u64,
|
|
|
+ len: u64,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct Instruction {
|
|
|
+ /// Pubkey of the instruction processor that executes this instruction
|
|
|
+ pub program_id: Pubkey,
|
|
|
+ /// Metadata for what accounts should be passed to the instruction processor
|
|
|
+ pub accounts: Vec<AccountMeta>,
|
|
|
+ /// Opaque data passed to the instruction processor
|
|
|
+ pub data: Vec<u8>,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug, PartialEq, Clone)]
|
|
|
+pub struct Pubkey([u8; 32]);
|
|
|
+
|
|
|
+#[derive(Debug, PartialEq, Clone)]
|
|
|
+pub struct AccountMeta {
|
|
|
+ /// An account's public key
|
|
|
+ pub pubkey: Pubkey,
|
|
|
+ /// True if an Instruction requires a Transaction signature matching `pubkey`.
|
|
|
+ pub is_signer: bool,
|
|
|
+ /// True if the `pubkey` can be loaded as a read-write account.
|
|
|
+ pub is_writable: bool,
|
|
|
+}
|
|
|
+
|
|
|
+fn translate(
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ access_type: AccessType,
|
|
|
+ vm_addr: u64,
|
|
|
+ len: u64,
|
|
|
+) -> Result<u64, EbpfError<UserError>> {
|
|
|
+ memory_mapping.map::<UserError>(access_type, vm_addr, len)
|
|
|
+}
|
|
|
+
|
|
|
+fn translate_type_inner<'a, T>(
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ access_type: AccessType,
|
|
|
+ vm_addr: u64,
|
|
|
+) -> Result<&'a mut T, EbpfError<UserError>> {
|
|
|
+ unsafe {
|
|
|
+ translate(memory_mapping, access_type, vm_addr, size_of::<T>() as u64)
|
|
|
+ .map(|value| &mut *(value as *mut T))
|
|
|
+ }
|
|
|
+}
|
|
|
+fn translate_type<'a, T>(
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ vm_addr: u64,
|
|
|
+) -> Result<&'a T, EbpfError<UserError>> {
|
|
|
+ translate_type_inner::<T>(memory_mapping, AccessType::Load, vm_addr).map(|value| &*value)
|
|
|
+}
|
|
|
+fn translate_slice<'a, T>(
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ vm_addr: u64,
|
|
|
+ len: u64,
|
|
|
+) -> Result<&'a [T], EbpfError<UserError>> {
|
|
|
+ translate_slice_inner::<T>(memory_mapping, AccessType::Load, vm_addr, len).map(|value| &*value)
|
|
|
+}
|
|
|
+fn translate_slice_inner<'a, T>(
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ access_type: AccessType,
|
|
|
+ vm_addr: u64,
|
|
|
+ len: u64,
|
|
|
+) -> Result<&'a mut [T], EbpfError<UserError>> {
|
|
|
+ if len == 0 {
|
|
|
+ Ok(&mut [])
|
|
|
+ } else {
|
|
|
+ match translate(
|
|
|
+ memory_mapping,
|
|
|
+ access_type,
|
|
|
+ vm_addr,
|
|
|
+ len.saturating_mul(size_of::<T>() as u64),
|
|
|
+ ) {
|
|
|
+ Ok(value) => {
|
|
|
+ Ok(unsafe { std::slice::from_raw_parts_mut(value as *mut T, len as usize) })
|
|
|
+ }
|
|
|
+ Err(e) => Err(e),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct SyscallInvokeSignedC<'a> {
|
|
|
+ context: Rc<RefCell<&'a mut VirtualMachine>>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a> SyscallInvokeSignedC<'a> {
|
|
|
+ fn translate_instruction(
|
|
|
+ &self,
|
|
|
+ addr: u64,
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ ) -> Result<Instruction, EbpfError<UserError>> {
|
|
|
+ let ix_c = translate_type::<SolInstruction>(memory_mapping, addr)?;
|
|
|
+
|
|
|
+ let program_id = translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr)?;
|
|
|
+ let meta_cs = translate_slice::<SolAccountMeta>(
|
|
|
+ memory_mapping,
|
|
|
+ ix_c.accounts_addr,
|
|
|
+ ix_c.accounts_len as u64,
|
|
|
+ )?;
|
|
|
+ let data =
|
|
|
+ translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len as u64)?.to_vec();
|
|
|
+ let accounts = meta_cs
|
|
|
+ .iter()
|
|
|
+ .map(|meta_c| {
|
|
|
+ let pubkey = translate_type::<Pubkey>(memory_mapping, meta_c.pubkey_addr)?;
|
|
|
+ Ok(AccountMeta {
|
|
|
+ pubkey: pubkey.clone(),
|
|
|
+ is_signer: meta_c.is_signer,
|
|
|
+ is_writable: meta_c.is_writable,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .collect::<Result<Vec<AccountMeta>, EbpfError<UserError>>>()?;
|
|
|
+
|
|
|
+ Ok(Instruction {
|
|
|
+ program_id: program_id.clone(),
|
|
|
+ accounts,
|
|
|
+ data,
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
|
|
|
+ fn call(
|
|
|
+ &mut self,
|
|
|
+ instruction_addr: u64,
|
|
|
+ _account_infos_addr: u64,
|
|
|
+ _account_infos_len: u64,
|
|
|
+ _signers_seeds_addr: u64,
|
|
|
+ _signers_seeds_len: u64,
|
|
|
+ memory_mapping: &MemoryMapping,
|
|
|
+ ) -> Result<u64, EbpfError<UserError>> {
|
|
|
+ let instruction = self
|
|
|
+ .translate_instruction(instruction_addr, memory_mapping)
|
|
|
+ .expect("instruction not valid");
|
|
|
+
|
|
|
+ let data_id: Account = instruction.data[..32].try_into().unwrap();
|
|
|
+
|
|
|
+ println!("instruction:{:?}", instruction);
|
|
|
+
|
|
|
+ if let Ok(mut context) = self.context.try_borrow_mut() {
|
|
|
+ let p = context
|
|
|
+ .programs
|
|
|
+ .iter()
|
|
|
+ .find(|p| p.program == instruction.program_id.0 && p.data == data_id)
|
|
|
+ .unwrap()
|
|
|
+ .clone();
|
|
|
+
|
|
|
+ context.stack.insert(0, p);
|
|
|
+
|
|
|
+ context.execute(&instruction.data);
|
|
|
+
|
|
|
+ context.stack.remove(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(0)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl VirtualMachine {
|
|
|
+ fn execute(&mut self, calldata: &[u8]) {
|
|
|
println!("running bpf with calldata:{}", hex::encode(calldata));
|
|
|
|
|
|
- let parameter_bytes = serialize_parameters(&calldata, &self.account, &self.data);
|
|
|
+ let parameter_bytes = serialize_parameters(&calldata, &self);
|
|
|
let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
|
|
|
let heap_region = MemoryRegion::new_from_slice(&heap, MM_HEAP_START, true);
|
|
|
|
|
|
- let executable = Executable::<UserError>::from_elf(&self.code, None).expect("should work");
|
|
|
+ let program = &self.stack[0];
|
|
|
+
|
|
|
+ let executable =
|
|
|
+ Executable::<UserError>::from_elf(&self.account_data[&program.program].0, None)
|
|
|
+ .expect("should work");
|
|
|
let mut vm = EbpfVm::<UserError, DefaultInstructionMeter>::new(
|
|
|
executable.as_ref(),
|
|
|
Config::default(),
|
|
|
@@ -257,9 +505,23 @@ impl Program {
|
|
|
)
|
|
|
.unwrap();
|
|
|
|
|
|
- vm.register_syscall_ex("sol_log_", Syscall::Object(Box::new(Printer { buf })))
|
|
|
- .unwrap();
|
|
|
+ let context = Rc::new(RefCell::new(self));
|
|
|
+
|
|
|
+ vm.register_syscall_ex(
|
|
|
+ "sol_log_",
|
|
|
+ Syscall::Object(Box::new(Printer {
|
|
|
+ context: context.clone(),
|
|
|
+ })),
|
|
|
+ )
|
|
|
+ .unwrap();
|
|
|
|
|
|
+ vm.register_syscall_ex(
|
|
|
+ "sol_invoke_signed_c",
|
|
|
+ Syscall::Object(Box::new(SyscallInvokeSignedC {
|
|
|
+ context: context.clone(),
|
|
|
+ })),
|
|
|
+ )
|
|
|
+ .unwrap();
|
|
|
vm.register_syscall_ex(
|
|
|
"sol_alloc_free_",
|
|
|
Syscall::Object(Box::new(SyscallAllocFree {
|
|
|
@@ -272,52 +534,81 @@ impl Program {
|
|
|
.execute_program_interpreted(&mut DefaultInstructionMeter {})
|
|
|
.unwrap();
|
|
|
|
|
|
- let mut accounts = deserialize_parameters(¶meter_bytes);
|
|
|
+ let mut elf = context.try_borrow_mut().unwrap();
|
|
|
|
|
|
- let output = accounts.remove(0);
|
|
|
- let data = accounts.remove(0);
|
|
|
+ deserialize_parameters(¶meter_bytes, &mut elf.account_data);
|
|
|
+
|
|
|
+ let output = &elf.account_data[&elf.returndata].0;
|
|
|
|
|
|
let len = LittleEndian::read_u64(&output);
|
|
|
- self.output = output[8..len as usize + 8].to_vec();
|
|
|
- self.data = data;
|
|
|
+ elf.output = output[8..len as usize + 8].to_vec();
|
|
|
|
|
|
- println!("account: {}", hex::encode(&self.output));
|
|
|
+ println!("return: {}", hex::encode(&elf.output));
|
|
|
|
|
|
assert_eq!(res, 0);
|
|
|
}
|
|
|
|
|
|
fn constructor(&mut self, args: &[Token]) {
|
|
|
- let calldata = if let Some(constructor) = &self.abi.constructor {
|
|
|
+ let program = &self.stack[0];
|
|
|
+
|
|
|
+ let calldata = if let Some(constructor) = &program.abi.constructor {
|
|
|
constructor
|
|
|
- .encode_input(self.account.to_vec(), args)
|
|
|
+ .encode_input(program.data.to_vec(), args)
|
|
|
.unwrap()
|
|
|
} else {
|
|
|
- self.account.to_vec()
|
|
|
+ program.data.to_vec()
|
|
|
};
|
|
|
|
|
|
- let mut buf = String::new();
|
|
|
- self.execute(&mut buf, &calldata);
|
|
|
- self.printbuf = buf;
|
|
|
+ self.execute(&calldata);
|
|
|
}
|
|
|
|
|
|
fn function(&mut self, name: &str, args: &[Token]) -> Vec<Token> {
|
|
|
- let mut calldata: Vec<u8> = self.account.to_vec();
|
|
|
+ let program = &self.stack[0];
|
|
|
|
|
|
- match self.abi.functions[name][0].encode_input(args) {
|
|
|
+ let mut calldata: Vec<u8> = program.data.to_vec();
|
|
|
+
|
|
|
+ match program.abi.functions[name][0].encode_input(args) {
|
|
|
Ok(n) => calldata.extend(&n),
|
|
|
Err(x) => panic!("{}", x),
|
|
|
};
|
|
|
|
|
|
println!("input: {}", hex::encode(&calldata));
|
|
|
|
|
|
- let mut buf = String::new();
|
|
|
- self.execute(&mut buf, &calldata);
|
|
|
- self.printbuf = buf;
|
|
|
+ self.execute(&calldata);
|
|
|
|
|
|
println!("output: {}", hex::encode(&self.output));
|
|
|
|
|
|
- self.abi.functions[name][0]
|
|
|
+ let program = &self.stack[0];
|
|
|
+
|
|
|
+ program.abi.functions[name][0]
|
|
|
.decode_output(&self.output)
|
|
|
.unwrap()
|
|
|
}
|
|
|
+
|
|
|
+ fn data(&self) -> &Vec<u8> {
|
|
|
+ let program = &self.stack[0];
|
|
|
+
|
|
|
+ &self.account_data[&program.data].0
|
|
|
+ }
|
|
|
+
|
|
|
+ fn set_program(&mut self, no: usize) {
|
|
|
+ let cur = self.programs[no].clone();
|
|
|
+
|
|
|
+ self.stack = vec![cur];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn parse_and_resolve(src: &'static str, target: Target) -> ast::Namespace {
|
|
|
+ let mut cache = FileCache::new();
|
|
|
+
|
|
|
+ cache.set_file_contents("test.sol", src.to_string());
|
|
|
+
|
|
|
+ solang::parse_and_resolve("test.sol", &mut cache, target)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn first_error(errors: Vec<ast::Diagnostic>) -> String {
|
|
|
+ match errors.iter().find(|m| m.level == ast::Level::Error) {
|
|
|
+ Some(m) => m.message.to_owned(),
|
|
|
+ None => panic!("no errors found"),
|
|
|
+ }
|
|
|
}
|