use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use sha3::Digest;
use fvm_std::prelude::{Vec, Address, H256};
use fvm_std::types::LogLevel;
#[derive(Default)]
pub struct Runtime {
pub inner: Rc<RefCell<RuntimeInner>>,
}
#[derive(Default)]
pub struct RuntimeInner {
pub storage: HashMap<Vec<u8>, Vec<u8>>,
pub block_time: u64,
pub block_height: u64,
pub caller_addr: Address,
pub origin_addr: Address,
pub self_addr: Address,
pub block_hash: H256,
pub tx_hash: H256,
pub log: Vec<Vec<u8>>,
pub call_return: Vec<Vec<u8>>,
pub debug: HashMap<u32, Vec<Vec<u8>>>,
pub input: Vec<u8>,
pub ret: Vec<u8>,
pub hyper_counter: HashMap<Vec<u8>, i64>,
pub hyper_list: HashMap<Vec<u8>, Vec<i64>>,
}
impl RuntimeInner {
fn call_contract(&mut self) -> u32 {
self.call_return.last().expect("cross call set is not enough.").len() as u32
}
}
impl Runtime {
fn storage_write(&self, key: &[u8], prefix: &[u8], val: &[u8]) {
let mut p = prefix.to_vec();
let mut s = key.to_vec();
p.append(&mut s);
self.inner.borrow_mut().storage.insert(p, val.to_vec());
}
fn storage_read(&self, key: &[u8], prefix: &[u8]) -> Option<Vec<u8>> {
let mut p = prefix.to_vec();
let mut s = key.to_vec();
p.append(&mut s);
self.inner.borrow().storage.get(&p).map(|val| val.to_vec())
}
fn storage_delete(&self, key: &[u8], prefix: &[u8]) {
let mut p = prefix.to_vec();
let mut s = key.to_vec();
p.append(&mut s);
self.inner.borrow_mut().storage.remove(&p);
}
fn block_time(&self) -> u64 {
self.inner.borrow().block_time
}
fn block_height(&self) -> u64 {
self.inner.borrow().block_height
}
fn self_address(&self) -> Address {
self.inner.borrow().self_addr.clone()
}
fn caller_address(&self) -> Address {
self.inner.borrow().caller_addr.clone()
}
fn origin_address(&self) -> Address {
self.inner.borrow().origin_addr.clone()
}
fn tx_hash(&self) -> H256 {
self.inner.borrow().tx_hash.clone()
}
fn event(&self, msg: &[u8]) {
self.inner.borrow_mut().log.push(msg.to_vec());
}
fn sha256(&self, data: &[u8]) -> H256 {
let mut hasher = sha3::Keccak256::new();
hasher.update(data);
let hash = hasher.finalize();
H256::from_slice(hash.as_slice())
}
fn call_contract(&self, _: &Address, _: &[u8]) -> u32 {
self.inner.borrow_mut().call_contract()
}
fn cns_call_contract(&self, _: &[u8], _: &[u8]) -> u32 {
self.inner.borrow_mut().call_contract()
}
fn call_return(&self) -> Vec<u8> {
self.inner.borrow_mut().call_return.pop().expect("cross call set is not enough.")
}
fn ret(&self, _data: &[u8]) {
self.inner.borrow_mut().ret = _data.to_vec()
}
fn debug(&self, class_name: &[u8], data: &[u8], level: LogLevel) {
let mut c = String::from("[");
c.push_str(String::from_utf8(class_name.to_vec()).unwrap().as_str());
c.push_str("]: ");
c.push_str(String::from_utf8(data.to_vec()).unwrap().as_str());
if self.inner.borrow_mut().debug.contains_key(&level.to_int()) {
self.inner.borrow_mut().debug.get_mut(&level.to_int()).unwrap().push(c.into_bytes());
} else {
let mut temp = Vec::new();
temp.push(c.into_bytes());
self.inner.borrow_mut().debug.entry(level.to_int()).or_insert(temp);
}
}
pub fn revert(&self, _msg: &str) -> ! {
panic!("{}", _msg)
}
pub fn input(&self) -> Vec<u8> {
self.inner.borrow_mut().input.to_vec()
}
pub fn input_length(&self) -> u32 {
self.inner.borrow().input.len() as u32
}
pub fn add_list_key(&self, list_name: &[u8], index: u32) -> Option<i64> {
let list_name = list_name.to_vec();
let inner = &mut self.inner.borrow_mut();
let key =
match inner.hyper_counter.get(&list_name) {
None => {
inner.hyper_counter.insert(list_name.clone(), 0);
0
}
Some(v) => v.clone()
};
match inner.hyper_list.get_mut(&list_name) {
None => {
if index == 0 {
let mut new_vec = Vec::<i64>::new();
new_vec.insert(index as usize, key);
inner.hyper_list.insert(list_name.clone(), new_vec);
} else {
panic!("invalid index when add");
}
inner.hyper_counter.insert(list_name, key + 1);
Some(key)
}
Some(v) => {
if index as usize > v.len() {
return None;
}
v.insert(index as usize, key);
inner.hyper_counter.insert(list_name, key + 1);
Some(key)
}
}
}
pub fn get_list_key(&self, list_name: &[u8], index: u32) -> Option<i64> {
let list_name = list_name.to_vec();
let hyper_list = &self.inner.borrow().hyper_list;
let list = match hyper_list.get(&list_name) {
None => {
panic!("no list according to list_name when try to get key");
}
Some(v) => v
};
if index as usize >= list.len() {
None
} else {
Some(list[index as usize])
}
}
pub fn remove_list_key(&self, list_name: &[u8], index: u32) -> Option<i64> {
let list_name = list_name.to_vec();
let hyper_list = &mut self.inner.borrow_mut().hyper_list;
let list = match hyper_list.get_mut(&list_name) {
None => {
panic!("no list according to list_name when try to remove key");
}
Some(v) => v
};
if index as usize >= list.len() {
None
} else {
Some(list.remove(index as usize))
}
}
pub fn write_list_keys_back(&mut self, old_list_name: &[u8], list_name: &[u8], size: u32) {
let old_list_name = old_list_name.to_vec();
let new_list_name = list_name.to_vec();
let list_name = if old_list_name.len() > 0 {
old_list_name.clone()
} else {
new_list_name.clone()
};
let inner = &mut self.inner.borrow_mut();
let is_contain = inner.hyper_list.contains_key(&list_name);
match is_contain {
false => {
if size == 0 {
inner.hyper_counter.insert(new_list_name.clone(), 0);
let new_list = Vec::<i64>::new();
inner.hyper_list.insert(new_list_name.clone(), new_list);
} else {
panic!("size is not 0 but no list when writing keys back");
}
}
true => {
if old_list_name.len() > 0 {
let counter = inner.hyper_counter.remove(&old_list_name).unwrap();
let list = inner.hyper_list.remove(&old_list_name).unwrap();
inner.hyper_counter.insert(new_list_name.clone(), counter);
inner.hyper_list.insert(new_list_name.clone(), list);
}
let list = inner.hyper_list.get(&list_name).unwrap();
if list.len() != size as usize {
panic!("size not equal when writing keys back");
}
}
};
inner.storage.insert(["@HyperList".as_bytes(), new_list_name.as_slice()].concat().to_vec(), vec![0; 28]);
}
}
thread_local!(static RUNTIME: RefCell<Runtime> = RefCell::new(Runtime::default()));
pub fn setup_runtime(runtime: Runtime) {
RUNTIME.with(|r| *r.borrow_mut() = runtime);
}
mod env {
use super::*;
use std::cmp;
use std::ptr;
use std::slice;
use std::u32;
use fvm_std::types::Address;
use fvm_std::prelude::H256;
#[no_mangle]
pub unsafe extern "C" fn fvm_block_time() -> u64 {
RUNTIME.with(|r| r.borrow().block_time())
}
#[no_mangle]
pub unsafe extern "C" fn fvm_block_height() -> u64 {
RUNTIME.with(|r| r.borrow().block_height())
}
#[no_mangle]
pub unsafe extern "C" fn fvm_self_address(dest: *mut u8) {
RUNTIME.with(|r| {
let addr = r.borrow().self_address();
ptr::copy(addr.as_ptr(), dest, Address::len_bytes());
})
}
#[no_mangle]
pub unsafe extern "C" fn fvm_caller_address(dest: *mut u8) {
RUNTIME.with(|r| {
let caller = r.borrow().caller_address();
ptr::copy(caller.as_ptr(), dest, Address::len_bytes());
})
}
#[no_mangle]
pub unsafe extern "C" fn fvm_origin_address(dest: *mut u8) {
RUNTIME.with(|r| {
let origin = r.borrow().origin_address();
ptr::copy(origin.as_ptr(), dest, Address::len_bytes());
})
}
#[no_mangle]
pub unsafe extern "C" fn fvm_tx_hash(dest: *const u8) {
RUNTIME.with(|r| {
let tx_hash = r.borrow().tx_hash();
ptr::copy(tx_hash.as_ptr(), dest as *mut u8, H256::len_bytes());
})
}
#[no_mangle]
pub unsafe extern "C" fn fvm_storage_read(
key_ptr: *const u8, key_len: u32, prefix_ptr: *const u8, prefix_len: u32,
val: *mut u8, vlen: u32, offset: u32,
) -> u32 {
let key = core::slice::from_raw_parts(key_ptr, key_len as usize);
let prefix = core::slice::from_raw_parts(prefix_ptr, prefix_len as usize);
let offset = offset as usize;
let v = RUNTIME.with(|r| r.borrow().storage_read(key, prefix));
match v {
None => 0,
Some(v) => {
ptr::copy(
v.as_slice()[offset..].as_ptr(),
val,
cmp::min(vlen as usize, v.len() - offset),
);
v.len() as u32
}
}
}
#[no_mangle]
pub unsafe extern "C" fn fvm_storage_write(
key_ptr: *const u8, key_len: u32, prefix_ptr: *const u8, prefix_len: u32, val: *const u8, vlen: u32,
) {
let key = core::slice::from_raw_parts(key_ptr, key_len as usize);
let prefix = core::slice::from_raw_parts(prefix_ptr, prefix_len as usize);
let val = core::slice::from_raw_parts(val, vlen as usize);
RUNTIME.with(|r| r.borrow().storage_write(key, prefix, val));
}
#[no_mangle]
pub unsafe extern "C" fn fvm_storage_delete(key_ptr: *const u8, key_len: u32, prefix_ptr: *const u8, prefix_len: u32) {
let key = core::slice::from_raw_parts(key_ptr, key_len as usize);
let prefix = core::slice::from_raw_parts(prefix_ptr, prefix_len as usize);
RUNTIME.with(|r| r.borrow().storage_delete(key, prefix));
}
#[no_mangle]
pub unsafe extern "C" fn fvm_log(ptr: *const u8, len: u32) {
let data = core::slice::from_raw_parts(ptr, len as usize);
RUNTIME.with(|r| r.borrow().event(data));
}
#[no_mangle]
pub unsafe extern "C" fn fvm_sha256(data_ptr: *const u8, len: u32, val: *mut u8) {
let data = core::slice::from_raw_parts(data_ptr, len as usize);
RUNTIME.with(|r| {
let mut hash = r.borrow_mut().sha256(data);
ptr::copy(hash.as_mut_ptr(), val, 32);
});
}
#[no_mangle]
pub unsafe extern "C" fn fvm_call_contract(
addr: *const u8, input_ptr: *const u8, input_len: u32,
) -> u32 {
let input = core::slice::from_raw_parts(input_ptr, input_len as usize);
let addr = Address::from_slice(slice::from_raw_parts(addr, 20));
RUNTIME.with(|r| r.borrow().call_contract(&addr, input))
}
#[no_mangle]
pub unsafe extern "C" fn fvm_cns_call_contract(
cns: *const u8, cns_len: u32, input_ptr: *const u8, input_len: u32,
) -> u32 {
let input = core::slice::from_raw_parts(input_ptr, input_len as usize);
let addr = core::slice::from_raw_parts(cns, cns_len as usize);
RUNTIME.with(|r| r.borrow().cns_call_contract(&addr, input))
}
#[no_mangle]
pub unsafe extern "C" fn fvm_revert(ptr: *const u8, len: u32) -> ! {
let temp = core::slice::from_raw_parts(ptr, len as usize);
RUNTIME.with(|r| r.borrow().revert(std::str::from_utf8(temp).unwrap()))
}
#[no_mangle]
pub unsafe extern "C" fn fvm_call_return(dst: *mut u8) {
let output = RUNTIME.with(|r| r.borrow().call_return());
std::ptr::copy(output.as_ptr(), dst, output.len());
}
#[no_mangle]
pub unsafe extern "C" fn fvm_input_length() -> u32 {
RUNTIME.with(|r| r.borrow().input_length())
}
#[no_mangle]
pub unsafe extern "C" fn fvm_fetch_input(_dst: *mut u8) {
let output = RUNTIME.with(|r| r.borrow().input());
ptr::copy(output.as_ptr(), _dst, output.len())
}
#[no_mangle]
pub unsafe extern "C" fn fvm_return(ptr: *const u8, len: u32) {
let ret = core::slice::from_raw_parts(ptr, len as usize);
RUNTIME.with(|r| r.borrow().ret(ret))
}
#[no_mangle]
pub unsafe fn fvm_debug(class_prt: *const u8, class_len: u32, ptr: *const u8, len: u32, level: u32) {
let class_name = core::slice::from_raw_parts(class_prt, class_len as usize);
let data = core::slice::from_raw_parts(ptr, len as usize);
let l = match level {
0 => LogLevel::CRITICAL,
1 => LogLevel::ERROR,
2 => LogLevel::WARNING,
3 => LogLevel::NOTICE,
4 => LogLevel::INFO,
5 => LogLevel::DEBUG,
_ => panic!("invalid log level type")
};
RUNTIME.with(|r| r.borrow_mut().debug(class_name, data, l))
}
#[no_mangle]
pub unsafe fn fvm_add_list_key(key_ptr: *const u8, key_len: u32, index: u32) -> i64 {
let list_name = core::slice::from_raw_parts(key_ptr, key_len as usize);
let v = RUNTIME.with(|r| r.borrow_mut().add_list_key(list_name, index));
match v {
None => {
-1
}
Some(v) => v
}
}
#[no_mangle]
pub unsafe fn fvm_get_list_key(key_ptr: *const u8, key_len: u32, index: u32) -> i64 {
let list_name = core::slice::from_raw_parts(key_ptr, key_len as usize);
let v = RUNTIME.with(|r| r.borrow().get_list_key(list_name, index));
match v {
None => -1,
Some(v) => v
}
}
#[no_mangle]
pub fn fvm_remove_list_key(key_ptr: *const u8, key_len: u32, index: u32) -> i64 {
unsafe {
let list_name = core::slice::from_raw_parts(key_ptr, key_len as usize);
let v = RUNTIME.with(|r| r.borrow().remove_list_key(list_name, index));
match v {
None => -1,
Some(v) => v
}
}
}
#[no_mangle]
pub fn fvm_write_list_keys(old_list_name_ptr: *const u8, old_list_name_len: u32, new_list_name_ptr: *const u8, new_list_name_len: u32, size: u32) {
unsafe {
let old_list_name = core::slice::from_raw_parts(old_list_name_ptr, old_list_name_len as usize);
let list_name = core::slice::from_raw_parts(new_list_name_ptr, new_list_name_len as usize);
RUNTIME.with(|r| r.borrow_mut().write_list_keys_back(old_list_name, list_name, size))
}
}
}