#[cfg(not(feature = "mutex"))]
mod impls {
use std::{
cell::RefCell, cmp::min, collections::HashMap, ptr::copy_nonoverlapping,
slice::from_raw_parts,
};
use keccak_const::Keccak256;
type U = [u8; 32];
type Address = [u8; 20];
type WordHashMap = HashMap<[u8; 32], [u8; 32]>;
thread_local! {
pub static STORAGE: RefCell<WordHashMap> = RefCell::default();
pub static TRANSIENT: RefCell<WordHashMap> = RefCell::default();
}
pub fn storage_clear() {
STORAGE.with(|s| s.borrow_mut().clear())
}
pub fn transient_clear() {
TRANSIENT.with(|s| s.borrow_mut().clear())
}
unsafe fn read_word(key: *const u8) -> [u8; 32] {
let mut r = [0u8; 32];
unsafe {
copy_nonoverlapping(key, r.as_mut_ptr(), 32);
}
r
}
unsafe fn write_word(key: *mut u8, val: [u8; 32]) {
unsafe {
copy_nonoverlapping(val.as_ptr(), key, 32);
}
}
pub unsafe fn storage_load_bytes32(key: *const u8, out: *mut u8) {
let k = unsafe { read_word(key) };
let value = STORAGE.with(|s| match s.borrow().get(&k) {
Some(v) => *v,
None => [0u8; 32],
});
unsafe { write_word(out, value) };
}
pub unsafe fn storage_cache_bytes32(key: *const u8, value: *const u8) {
let k = unsafe { read_word(key) };
let v = unsafe { read_word(value) };
STORAGE.with(|s| s.borrow_mut().insert(k, v));
}
pub unsafe fn transient_load_bytes32(key: *const u8, out: *mut u8) {
let k = unsafe { read_word(key) };
let value = TRANSIENT.with(|s| match s.borrow().get(&k) {
Some(v) => *v,
None => [0u8; 32],
});
unsafe { write_word(out, value) };
}
pub unsafe fn transient_store_bytes32(key: *const u8, value: *const u8) {
let k = unsafe { read_word(key) };
let v = unsafe { read_word(value) };
TRANSIENT.with(|s| s.borrow_mut().insert(k, v));
}
pub unsafe fn storage_flush_cache(clear: bool) {
if clear {
storage_clear()
}
}
thread_local! {
static ACCOUNT_BALANCE: RefCell<HashMap<Address, U>> = RefCell::default();
static ARGS: RefCell<Vec<u8>> = RefCell::default();
static MSG_SENDER: RefCell<[u8; 20]> = RefCell::default();
static CONTRACT_ADDRESS: RefCell<Address> = RefCell::default();
static MSG_VALUE: RefCell<U> = RefCell::default();
static CHAIN_ID: RefCell<u64> = RefCell::default();
static BLOCK_TIMESTAMP: RefCell<u64> = RefCell::default();
static ACCOUNT_CODE: RefCell<HashMap<Address, Vec<u8>>> = RefCell::default();
}
const EMPTY_HASH: U = [
0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03,
0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85,
0xa4, 0x70,
];
pub unsafe fn account_balance(addr_: *const u8, out: *mut u8) {
ACCOUNT_BALANCE.with(|s| {
let mut addr = [0u8; 20];
unsafe {
copy_nonoverlapping(addr_, addr.as_mut_ptr(), 32);
}
let h = s.borrow();
let amt = h.get(&addr).unwrap_or(&[0u8; 32]);
unsafe {
copy_nonoverlapping(amt.as_ptr(), out, 32);
}
})
}
#[allow(unused)]
pub unsafe fn pay_for_memory_grow(_: u16) {}
pub unsafe fn write_result(d: *const u8, l: usize) {
println!("{}", const_hex::encode(unsafe { from_raw_parts(d, l) }));
}
pub unsafe fn return_data_size() -> usize {
0
}
pub fn set_args(x: Vec<u8>) {
ARGS.with(|s| *s.borrow_mut() = x)
}
pub fn args_len() -> usize {
ARGS.with(|s| s.borrow().len())
}
pub unsafe fn read_args(out: *mut u8) {
ARGS.with(|s| {
let b = s.borrow();
unsafe {
copy_nonoverlapping(b.as_ptr(), out, b.len());
}
})
}
pub fn set_msg_sender(x: Address) {
MSG_SENDER.with(|s| *s.borrow_mut() = x)
}
pub unsafe fn msg_sender(out: *mut u8) {
MSG_SENDER.with(|s| {
let b = s.borrow();
unsafe {
copy_nonoverlapping(b.as_ptr(), out, 20);
}
})
}
pub fn set_contract_address(x: Address) {
CONTRACT_ADDRESS.with(|s| {
*s.borrow_mut() = x;
})
}
pub fn set_account_code(x: Address, code: Vec<u8>) {
ACCOUNT_CODE.with(|s| s.borrow_mut().insert(x, code));
}
pub unsafe fn contract_address(out: *mut u8) {
CONTRACT_ADDRESS.with(|s| {
let b = s.borrow();
unsafe {
copy_nonoverlapping(b.as_ptr(), out, 20);
}
})
}
pub unsafe fn msg_value(out: *mut u8) {
MSG_VALUE.with(|s| {
let b = s.borrow();
unsafe {
copy_nonoverlapping(b.as_ptr(), out, 32);
}
})
}
pub unsafe fn chainid() -> u64 {
CHAIN_ID.with(|s| s.borrow().clone())
}
pub unsafe fn account_code_size(addr_: *const u8) -> usize {
ACCOUNT_CODE.with(|s| {
let mut addr = [0u8; 20];
unsafe {
copy_nonoverlapping(addr_, addr.as_mut_ptr(), 20);
}
s.borrow().get(&addr).map(|s| s.len()).unwrap_or(0)
})
}
pub unsafe fn account_code(
addr_: *const u8,
offset: usize,
size: usize,
out: *mut u8,
) -> usize {
ACCOUNT_CODE.with(|s| {
let mut addr = [0u8; 20];
unsafe {
copy_nonoverlapping(addr_, addr.as_mut_ptr(), 20);
}
let b = s.borrow();
match b.get(&addr) {
Some(b) => {
if offset >= b.len() {
return 0;
}
let src = &b[offset..];
let len = min(size, src.len());
unsafe {
copy_nonoverlapping(src.as_ptr(), out, len);
}
len
}
None => 0,
}
})
}
fn keccak256(h: &[u8]) -> [u8; 32] {
Keccak256::new().update(h).finalize()
}
pub unsafe fn account_codehash(addr_: *const u8, out: *mut u8) {
ACCOUNT_CODE.with(|s| {
let mut addr = [0u8; 20];
unsafe {
copy_nonoverlapping(addr_, addr.as_mut_ptr(), 20);
}
let b = s.borrow();
let h = match b.get(&addr) {
None => EMPTY_HASH,
Some(b) if b.len() == 0 => EMPTY_HASH,
Some(b) => keccak256(&b),
};
unsafe {
copy_nonoverlapping(h.as_ptr(), out, 32);
}
})
}
pub fn set_block_timestamp(n: u64) {
BLOCK_TIMESTAMP.with(|s| *s.borrow_mut() = n)
}
pub unsafe fn block_timestamp() -> u64 {
BLOCK_TIMESTAMP.with(|s| *s.borrow())
}
pub unsafe fn block_basefee(_: *mut u8) {}
pub unsafe fn evm_gas_left() -> u64 {
0
}
pub unsafe fn evm_ink_left() -> u64 {
0
}
}
#[cfg(feature = "mutex")]
mod impls {
use std::{
collections::HashMap,
ptr::copy_nonoverlapping,
sync::{LazyLock, Mutex},
};
type WordHashMap = HashMap<[u8; 32], [u8; 32]>;
pub static STORAGE: LazyLock<Mutex<WordHashMap>> = LazyLock::new(|| Mutex::default());
pub static TRANSIENT: LazyLock<Mutex<WordHashMap>> = LazyLock::new(|| Mutex::default());
pub fn storage_clear() {
STORAGE.lock().unwrap().clear()
}
pub fn transient_clear() {
TRANSIENT.lock().unwrap().clear()
}
unsafe fn read_word(key: *const u8) -> [u8; 32] {
let mut r = [0u8; 32];
unsafe {
copy_nonoverlapping(key, r.as_mut_ptr(), 32);
}
r
}
unsafe fn write_word(key: *mut u8, val: [u8; 32]) {
unsafe {
copy_nonoverlapping(val.as_ptr(), key, 32);
}
}
pub unsafe fn storage_load_bytes32(key: *const u8, out: *mut u8) {
let k = unsafe { read_word(key) };
let value = match STORAGE.lock().unwrap().get(&k) {
Some(v) => *v,
None => [0u8; 32],
};
unsafe { write_word(out, value) };
}
pub unsafe fn storage_cache_bytes32(key: *const u8, value: *const u8) {
let k = unsafe { read_word(key) };
let v = unsafe { read_word(value) };
STORAGE.lock().unwrap().insert(k, v);
}
pub unsafe fn transient_load_bytes32(key: *const u8, out: *mut u8) {
let k = unsafe { read_word(key) };
let value = match TRANSIENT.lock().unwrap().get(&k) {
Some(v) => *v,
None => [0u8; 32],
};
unsafe { write_word(out, value) };
}
pub unsafe fn transient_store_bytes32(key: *const u8, value: *const u8) {
let k = unsafe { read_word(key) };
let v = unsafe { read_word(value) };
TRANSIENT.lock().unwrap().insert(k, v);
}
pub unsafe fn storage_flush_cache(clear: bool) {
if clear {
storage_clear()
}
}
}
pub use impls::*;
pub fn log_txt(_: *const u8, _: usize) {}
pub unsafe fn call_contract(
_contract: *const u8,
_calldata: *const u8,
_calldata_len: usize,
_value: *const u8,
_gas: u64,
_return_data_len: *mut usize,
) -> u8 {
0
}
pub unsafe fn static_call_contract(
_contract: *const u8,
_calldata: *const u8,
_calldata_len: usize,
_gas: u64,
_return_data_len: *mut usize,
) -> u8 {
0
}
pub unsafe fn delegate_call_contract(
_contract: *const u8,
_calldata: *const u8,
_calldata_len: usize,
_gas: u64,
_return_data_len: *mut usize,
) -> u8 {
0
}
pub unsafe fn read_return_data(_: *mut u8, _: usize, _: usize) -> usize {
0
}
unsafe extern "C" {
pub fn math_div(x: *mut u8, y: *const u8);
pub fn math_mod(x: *mut u8, y: *const u8);
pub fn math_add_mod(a: *mut u8, b: *const u8, c: *const u8);
pub fn math_mul_mod(a: *mut u8, b: *const u8, c: *const u8);
}
pub unsafe fn exit_early(_: i32) -> ! {
todo!("implement dispatch function");
}