use dharitri_wasm::{Address, ArgBuffer, BoxedBytes, CodeMetadata, H256};
use crate::async_data::*;
use crate::big_int_mock::*;
use crate::big_uint_mock::*;
use crate::blockchain_mock::*;
use crate::display_util::*;
use dharitri_wasm::err_msg;
use dharitri_wasm::ContractHookApi;
use dharitri_wasm::{BigIntApi, BigUintApi};
use num_bigint::{BigInt, BigUint};
use num_traits::cast::ToPrimitive;
use alloc::vec::Vec;
use std::collections::HashMap;
use std::fmt;
use alloc::rc::Rc;
use core::cell::RefCell;
use sha3::{Digest, Keccak256, Sha3_256};
const ADDRESS_LENGTH: usize = 32;
const TOPIC_LENGTH: usize = 32;
pub struct TxPanic {
pub status: u64,
pub message: Vec<u8>,
}
#[derive(Clone, Debug)]
pub struct TxInput {
pub from: Address,
pub to: Address,
pub call_value: BigUint,
pub dct_value: BigUint,
pub dct_token_name: Vec<u8>,
pub func_name: Vec<u8>,
pub args: Vec<Vec<u8>>,
pub gas_limit: u64,
pub gas_price: u64,
pub tx_hash: H256,
}
impl fmt::Display for TxInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TxInput {{ func: {}, args: {:?}, call_value: {}, dct_token_name: {:?}, dct_value: {:?}, from: 0x{}, to: 0x{}\n}}",
String::from_utf8(self.func_name.clone()).unwrap(),
self.args,
self.call_value,
self.dct_token_name,
self.dct_value,
address_hex(&self.from),
address_hex(&self.to))
}
}
impl TxInput {
pub fn add_arg(&mut self, arg: Vec<u8>) {
self.args.push(arg);
}
}
#[derive(Clone, Debug)]
pub struct TxResult {
pub result_status: u64,
pub result_message: Vec<u8>,
pub result_values: Vec<Vec<u8>>,
}
impl fmt::Display for TxResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let results_hex: Vec<String> = self
.result_values
.iter()
.map(|r| format!("0x{}", hex::encode(r)))
.collect();
write!(
f,
"TxResult {{\n\tresult_status: {},\n\tresult_values:{:?}\n}}",
self.result_status, results_hex
)
}
}
impl TxResult {
pub fn empty() -> TxResult {
TxResult {
result_status: 0,
result_message: Vec::new(),
result_values: Vec::new(),
}
}
pub fn print(&self) {
println!("{}", self);
}
}
#[derive(Debug)]
pub struct SendBalance {
pub recipient: Address,
pub amount: BigUint,
}
#[derive(Debug)]
pub struct TxOutput {
pub contract_storage: HashMap<Vec<u8>, Vec<u8>>,
pub result: TxResult,
pub send_balance_list: Vec<SendBalance>,
pub async_call: Option<AsyncCallTxData>,
}
impl Default for TxOutput {
fn default() -> Self {
TxOutput {
contract_storage: HashMap::new(),
result: TxResult::empty(),
send_balance_list: Vec::new(),
async_call: None,
}
}
}
impl TxOutput {
pub fn from_panic_obj(panic_obj: &TxPanic) -> Self {
TxOutput {
contract_storage: HashMap::new(),
result: TxResult {
result_status: panic_obj.status,
result_message: panic_obj.message.clone(),
result_values: Vec::new(),
},
send_balance_list: Vec::new(),
async_call: None,
}
}
pub fn from_panic_string(_: &str) -> Self {
TxOutput {
contract_storage: HashMap::new(),
result: TxResult {
result_status: 4,
result_message: b"panic occurred".to_vec(),
result_values: Vec::new(),
},
send_balance_list: Vec::new(),
async_call: None,
}
}
}
#[derive(Debug)]
pub struct TxContext {
pub blockchain_info_box: Box<BlockchainTxInfo>,
pub tx_input_box: Box<TxInput>,
pub tx_output_cell: Rc<RefCell<TxOutput>>,
}
impl TxContext {
pub fn new(blockchain_info: BlockchainTxInfo, tx_input: TxInput, tx_output: TxOutput) -> Self {
TxContext {
blockchain_info_box: Box::new(blockchain_info),
tx_input_box: Box::new(tx_input),
tx_output_cell: Rc::new(RefCell::new(tx_output)),
}
}
pub fn into_output(self) -> TxOutput {
let ref_cell = Rc::try_unwrap(self.tx_output_cell).unwrap();
ref_cell.replace(TxOutput::default())
}
pub fn dummy() -> Self {
TxContext {
blockchain_info_box: Box::new(BlockchainTxInfo {
previous_block_info: BlockInfo::new(),
current_block_info: BlockInfo::new(),
contract_balance: 0u32.into(),
contract_owner: None,
}),
tx_input_box: Box::new(TxInput {
from: Address::zero(),
to: Address::zero(),
call_value: 0u32.into(),
dct_value: 0u32.into(),
dct_token_name: Vec::new(),
func_name: Vec::new(),
args: Vec::new(),
gas_limit: 0,
gas_price: 0,
tx_hash: b"dummy...........................".into(),
}),
tx_output_cell: Rc::new(RefCell::new(TxOutput::default())),
}
}
}
impl Clone for TxContext {
fn clone(&self) -> Self {
TxContext {
blockchain_info_box: self.blockchain_info_box.clone(),
tx_input_box: self.tx_input_box.clone(),
tx_output_cell: Rc::clone(&self.tx_output_cell),
}
}
}
impl dharitri_wasm::ContractHookApi<RustBigInt, RustBigUint> for TxContext {
fn get_sc_address(&self) -> Address {
self.tx_input_box.to.clone()
}
fn get_owner_address(&self) -> Address {
self.blockchain_info_box
.contract_owner
.clone()
.unwrap_or_else(|| panic!("contract owner address not set"))
}
fn get_caller(&self) -> Address {
self.tx_input_box.from.clone()
}
fn get_balance(&self, address: &Address) -> RustBigUint {
if address != &self.get_sc_address() {
panic!("get balance not yet implemented for accounts other than the contract itself");
}
self.blockchain_info_box.contract_balance.clone().into()
}
fn storage_store_slice_u8(&self, key: &[u8], value: &[u8]) {
if key.starts_with(&b"DHARITRI"[..]) {
panic!(TxPanic {
status: 10,
message: b"cannot write to storage under Dharitri reserved key".to_vec(),
});
}
let mut tx_output = self.tx_output_cell.borrow_mut();
tx_output
.contract_storage
.insert(key.to_vec(), value.to_vec());
}
fn storage_load_vec_u8(&self, key: &[u8]) -> Vec<u8> {
let tx_output = self.tx_output_cell.borrow();
match tx_output.contract_storage.get(&key.to_vec()) {
None => Vec::with_capacity(0),
Some(value) => value.clone(),
}
}
#[inline]
fn storage_load_len(&self, key: &[u8]) -> usize {
self.storage_load_vec_u8(key).len()
}
fn storage_store_bytes32(&self, key: &[u8], value: &[u8; 32]) {
self.storage_store_slice_u8(key, &value[..].to_vec());
}
fn storage_load_bytes32(&self, key: &[u8]) -> [u8; 32] {
let value = self.storage_load_vec_u8(key);
let mut res = [0u8; 32];
let offset = 32 - value.len();
if !value.is_empty() {
res[offset..].clone_from_slice(&value[..]);
}
res
}
fn storage_store_big_uint(&self, key: &[u8], value: &RustBigUint) {
self.storage_store_slice_u8(key, &value.to_bytes_be());
}
fn storage_load_big_uint(&self, key: &[u8]) -> RustBigUint {
let value = self.storage_load_vec_u8(key);
let bi = BigInt::from_bytes_be(num_bigint::Sign::Plus, value.as_slice());
bi.into()
}
fn storage_store_big_uint_raw(&self, _key: &[u8], _handle: i32) {
panic!("cannot call storage_store_big_uint_raw in debug mode");
}
fn storage_load_big_uint_raw(&self, _key: &[u8]) -> i32 {
panic!("cannot call storage_load_big_uint_raw in debug mode");
}
fn storage_store_big_int(&self, key: &[u8], value: &RustBigInt) {
self.storage_store_slice_u8(key, &value.to_signed_bytes_be());
}
fn storage_load_big_int(&self, key: &[u8]) -> RustBigInt {
let value = self.storage_load_vec_u8(key);
let bi = BigInt::from_signed_bytes_be(value.as_slice());
bi.into()
}
fn storage_store_i64(&self, key: &[u8], value: i64) {
self.storage_store_big_int(key, &RustBigInt::from(value));
}
fn storage_store_u64(&self, key: &[u8], value: u64) {
self.storage_store_big_uint(key, &RustBigUint::from(value));
}
fn storage_load_i64(&self, key: &[u8]) -> i64 {
let bi = self.storage_load_big_int(key);
if let Some(v) = bi.0.to_i64() {
v
} else {
panic!(TxPanic {
status: 10,
message: b"storage value out of range".to_vec(),
})
}
}
fn storage_load_u64(&self, key: &[u8]) -> u64 {
let bu = self.storage_load_big_uint(key);
if let Some(v) = bu.0.to_u64() {
v
} else {
panic!(TxPanic {
status: 10,
message: b"storage value out of range".to_vec(),
})
}
}
#[inline]
fn get_call_value_big_uint(&self) -> RustBigUint {
self.tx_input_box.call_value.clone().into()
}
#[inline]
fn get_dct_value_big_uint(&self) -> RustBigUint {
self.tx_input_box.dct_value.clone().into()
}
#[inline]
fn get_dct_token_name(&self) -> Vec<u8> {
self.tx_input_box.dct_token_name.clone()
}
fn send_tx(&self, to: &Address, amount: &RustBigUint, _data: &[u8]) {
let mut tx_output = self.tx_output_cell.borrow_mut();
tx_output.send_balance_list.push(SendBalance {
recipient: to.clone(),
amount: amount.value(),
})
}
fn async_call(&self, to: &Address, amount: &RustBigUint, data: &[u8]) {
let mut tx_output = self.tx_output_cell.borrow_mut();
tx_output.async_call = Some(AsyncCallTxData {
to: to.clone(),
call_value: amount.value(),
call_data: data.to_vec(),
tx_hash: self.get_tx_hash(),
});
}
fn deploy_contract(
&self,
_gas: u64,
_amount: &RustBigUint,
_code: &BoxedBytes,
_code_metadata: CodeMetadata,
_arg_buffer: &ArgBuffer,
) -> Address {
panic!("deploy_contract not yet implemented")
}
fn get_tx_hash(&self) -> H256 {
self.tx_input_box.tx_hash.clone()
}
fn get_gas_left(&self) -> u64 {
self.tx_input_box.gas_limit
}
fn get_block_timestamp(&self) -> u64 {
self.blockchain_info_box.current_block_info.block_timestamp
}
fn get_block_nonce(&self) -> u64 {
self.blockchain_info_box.current_block_info.block_nonce
}
fn get_block_round(&self) -> u64 {
self.blockchain_info_box.current_block_info.block_round
}
fn get_block_epoch(&self) -> u64 {
self.blockchain_info_box.current_block_info.block_epoch
}
fn get_block_random_seed(&self) -> Box<[u8; 48]> {
self.blockchain_info_box
.current_block_info
.block_random_seed
.clone()
}
fn get_prev_block_timestamp(&self) -> u64 {
self.blockchain_info_box.previous_block_info.block_timestamp
}
fn get_prev_block_nonce(&self) -> u64 {
self.blockchain_info_box.previous_block_info.block_nonce
}
fn get_prev_block_round(&self) -> u64 {
self.blockchain_info_box.previous_block_info.block_round
}
fn get_prev_block_epoch(&self) -> u64 {
self.blockchain_info_box.previous_block_info.block_epoch
}
fn get_prev_block_random_seed(&self) -> Box<[u8; 48]> {
self.blockchain_info_box
.previous_block_info
.block_random_seed
.clone()
}
fn sha256(&self, data: &[u8]) -> H256 {
let mut hasher = Sha3_256::new();
hasher.input(data);
let hash: [u8; 32] = hasher.result().into();
hash.into()
}
fn keccak256(&self, data: &[u8]) -> H256 {
let mut hasher = Keccak256::new();
hasher.input(data);
let hash: [u8; 32] = hasher.result().into();
hash.into()
}
}
impl dharitri_wasm::ContractIOApi<RustBigInt, RustBigUint> for TxContext {
fn get_num_arguments(&self) -> i32 {
self.tx_input_box.args.len() as i32
}
fn check_not_payable(&self) {
if self.get_call_value_big_uint() > 0 {
self.signal_error(err_msg::NON_PAYABLE);
}
}
fn get_argument_len(&self, arg_index: i32) -> usize {
let arg = self.get_argument_vec_u8(arg_index);
arg.len()
}
fn copy_argument_to_slice(&self, _arg_index: i32, _slice: &mut [u8]) {
panic!("copy_argument_to_slice not yet implemented")
}
fn get_argument_vec_u8(&self, arg_index: i32) -> Vec<u8> {
let arg_idx_usize = arg_index as usize;
if arg_idx_usize >= self.tx_input_box.args.len() {
panic!("Tx arg index out of range");
}
self.tx_input_box.args[arg_idx_usize].clone()
}
fn get_argument_boxed_bytes(&self, arg_index: i32) -> BoxedBytes {
self.get_argument_vec_u8(arg_index).into()
}
fn get_argument_big_uint(&self, arg_index: i32) -> RustBigUint {
let bytes = self.get_argument_vec_u8(arg_index);
RustBigUint::from_bytes_be(&bytes[..])
}
fn get_argument_big_int(&self, arg_index: i32) -> RustBigInt {
let bytes = self.get_argument_vec_u8(arg_index);
RustBigInt::from_signed_bytes_be(&bytes)
}
fn get_argument_big_uint_raw(&self, _arg_index: i32) -> i32 {
panic!("cannot call get_argument_big_uint_raw in debug mode");
}
fn get_argument_big_int_raw(&self, _arg_index: i32) -> i32 {
panic!("cannot call get_argument_big_int_raw in debug mode");
}
fn get_argument_i64(&self, arg_index: i32) -> i64 {
let bytes = self.get_argument_vec_u8(arg_index);
let bi = BigInt::from_signed_bytes_be(&bytes);
if let Some(v) = bi.to_i64() {
v
} else {
panic!(TxPanic {
status: 10,
message: b"argument out of range".to_vec(),
})
}
}
fn get_argument_u64(&self, arg_index: i32) -> u64 {
let bytes = self.get_argument_vec_u8(arg_index);
let bu = BigUint::from_bytes_be(&bytes);
if let Some(v) = bu.to_u64() {
v
} else {
panic!(TxPanic {
status: 10,
message: b"argument out of range".to_vec(),
})
}
}
fn finish_slice_u8(&self, slice: &[u8]) {
let mut v = vec![0u8; slice.len()];
v.copy_from_slice(slice);
let mut tx_output = self.tx_output_cell.borrow_mut();
tx_output.result.result_values.push(v)
}
fn finish_big_int(&self, bi: &RustBigInt) {
self.finish_slice_u8(bi.to_signed_bytes_be().as_slice());
}
fn finish_big_uint(&self, bu: &RustBigUint) {
self.finish_slice_u8(bu.to_bytes_be().as_slice());
}
fn finish_big_int_raw(&self, _handle: i32) {
panic!("cannot call finish_big_int_raw in debug mode");
}
fn finish_big_uint_raw(&self, _handle: i32) {
panic!("cannot call finish_big_uint_raw in debug mode");
}
fn finish_i64(&self, value: i64) {
self.finish_big_int(&value.into());
}
fn finish_u64(&self, value: u64) {
self.finish_big_uint(&value.into());
}
fn signal_error(&self, message: &[u8]) -> ! {
panic!(TxPanic {
status: 4,
message: message.to_vec()
})
}
fn write_log(&self, _topics: &[[u8; 32]], _data: &[u8]) {
}
}