1use numbat_wasm::{Address, ArgBuffer, BoxedBytes, CodeMetadata, H256};
2
3use crate::async_data::*;
4use crate::big_int_mock::*;
5use crate::big_uint_mock::*;
6use crate::blockchain_mock::*;
7use crate::display_util::*;
8
9use numbat_wasm::err_msg;
10use numbat_wasm::ContractHookApi;
11use numbat_wasm::{BigIntApi, BigUintApi};
12
13use num_bigint::{BigInt, BigUint};
14use num_traits::cast::ToPrimitive;
15
16use alloc::vec::Vec;
17
18use std::collections::HashMap;
19use std::fmt;
20
21use alloc::rc::Rc;
22use core::cell::RefCell;
23
24use sha3::{Digest, Keccak256, Sha3_256};
25
26const ADDRESS_LENGTH: usize = 32;
27const TOPIC_LENGTH: usize = 32;
28
29pub struct TxPanic {
30 pub status: u64,
31 pub message: Vec<u8>,
32}
33
34#[derive(Clone, Debug)]
35pub struct TxInput {
36 pub from: Address,
37 pub to: Address,
38 pub call_value: BigUint,
39 pub dcdt_value: BigUint,
40 pub dcdt_token_name: Vec<u8>,
41 pub func_name: Vec<u8>,
42 pub args: Vec<Vec<u8>>,
43 pub gas_limit: u64,
44 pub gas_price: u64,
45 pub tx_hash: H256,
46}
47
48impl fmt::Display for TxInput {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "TxInput {{ func: {}, args: {:?}, call_value: {}, dcdt_token_name: {:?}, dcdt_value: {:?}, from: 0x{}, to: 0x{}\n}}",
51 String::from_utf8(self.func_name.clone()).unwrap(),
52 self.args,
53 self.call_value,
54 self.dcdt_token_name,
55 self.dcdt_value,
56 address_hex(&self.from),
57 address_hex(&self.to))
58 }
59}
60
61impl TxInput {
62 pub fn add_arg(&mut self, arg: Vec<u8>) {
63 self.args.push(arg);
64 }
65}
66
67#[derive(Clone, Debug)]
68pub struct TxResult {
69 pub result_status: u64,
70 pub result_message: Vec<u8>,
71 pub result_values: Vec<Vec<u8>>,
72}
73
74impl fmt::Display for TxResult {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let results_hex: Vec<String> = self
77 .result_values
78 .iter()
79 .map(|r| format!("0x{}", hex::encode(r)))
80 .collect();
81 write!(
82 f,
83 "TxResult {{\n\tresult_status: {},\n\tresult_values:{:?}\n}}",
84 self.result_status, results_hex
85 )
86 }
87}
88
89impl TxResult {
90 pub fn empty() -> TxResult {
91 TxResult {
92 result_status: 0,
93 result_message: Vec::new(),
94 result_values: Vec::new(),
95 }
96 }
97 pub fn print(&self) {
98 println!("{}", self);
99 }
100}
101
102#[derive(Debug)]
103pub struct SendBalance {
104 pub recipient: Address,
105 pub amount: BigUint,
106}
107
108#[derive(Debug)]
109pub struct TxOutput {
110 pub contract_storage: HashMap<Vec<u8>, Vec<u8>>,
111 pub result: TxResult,
112 pub send_balance_list: Vec<SendBalance>,
113 pub async_call: Option<AsyncCallTxData>,
114}
115
116impl Default for TxOutput {
117 fn default() -> Self {
118 TxOutput {
119 contract_storage: HashMap::new(),
120 result: TxResult::empty(),
121 send_balance_list: Vec::new(),
122 async_call: None,
123 }
124 }
125}
126
127impl TxOutput {
128 pub fn from_panic_obj(panic_obj: &TxPanic) -> Self {
129 TxOutput {
130 contract_storage: HashMap::new(),
131 result: TxResult {
132 result_status: panic_obj.status,
133 result_message: panic_obj.message.clone(),
134 result_values: Vec::new(),
135 },
136 send_balance_list: Vec::new(),
137 async_call: None,
138 }
139 }
140
141 pub fn from_panic_string(_: &str) -> Self {
142 TxOutput {
143 contract_storage: HashMap::new(),
144 result: TxResult {
145 result_status: 4,
146 result_message: b"panic occurred".to_vec(),
147 result_values: Vec::new(),
148 },
149 send_balance_list: Vec::new(),
150 async_call: None,
151 }
152 }
153}
154
155#[derive(Debug)]
156pub struct TxContext {
157 pub blockchain_info_box: Box<BlockchainTxInfo>,
158 pub tx_input_box: Box<TxInput>,
159 pub tx_output_cell: Rc<RefCell<TxOutput>>,
160}
161
162impl TxContext {
163 pub fn new(blockchain_info: BlockchainTxInfo, tx_input: TxInput, tx_output: TxOutput) -> Self {
164 TxContext {
165 blockchain_info_box: Box::new(blockchain_info),
166 tx_input_box: Box::new(tx_input),
167 tx_output_cell: Rc::new(RefCell::new(tx_output)),
168 }
169 }
170
171 pub fn into_output(self) -> TxOutput {
172 let ref_cell = Rc::try_unwrap(self.tx_output_cell).unwrap();
173 ref_cell.replace(TxOutput::default())
174 }
175
176 pub fn dummy() -> Self {
177 TxContext {
178 blockchain_info_box: Box::new(BlockchainTxInfo {
179 previous_block_info: BlockInfo::new(),
180 current_block_info: BlockInfo::new(),
181 contract_balance: 0u32.into(),
182 contract_owner: None,
183 }),
184 tx_input_box: Box::new(TxInput {
185 from: Address::zero(),
186 to: Address::zero(),
187 call_value: 0u32.into(),
188 dcdt_value: 0u32.into(),
189 dcdt_token_name: Vec::new(),
190 func_name: Vec::new(),
191 args: Vec::new(),
192 gas_limit: 0,
193 gas_price: 0,
194 tx_hash: b"dummy...........................".into(),
195 }),
196 tx_output_cell: Rc::new(RefCell::new(TxOutput::default())),
197 }
198 }
199}
200
201impl Clone for TxContext {
202 fn clone(&self) -> Self {
203 TxContext {
204 blockchain_info_box: self.blockchain_info_box.clone(),
205 tx_input_box: self.tx_input_box.clone(),
206 tx_output_cell: Rc::clone(&self.tx_output_cell),
207 }
208 }
209}
210
211impl numbat_wasm::ContractHookApi<RustBigInt, RustBigUint> for TxContext {
212 fn get_sc_address(&self) -> Address {
213 self.tx_input_box.to.clone()
214 }
215
216 fn get_owner_address(&self) -> Address {
217 self.blockchain_info_box
218 .contract_owner
219 .clone()
220 .unwrap_or_else(|| panic!("contract owner address not set"))
221 }
222
223 fn get_caller(&self) -> Address {
224 self.tx_input_box.from.clone()
225 }
226
227 fn get_balance(&self, address: &Address) -> RustBigUint {
228 if address != &self.get_sc_address() {
229 panic!("get balance not yet implemented for accounts other than the contract itself");
230 }
231 self.blockchain_info_box.contract_balance.clone().into()
232 }
233
234 fn storage_store_slice_u8(&self, key: &[u8], value: &[u8]) {
235 if key.starts_with(&b"NUMBAT"[..]) {
237 panic!(TxPanic {
238 status: 10,
239 message: b"cannot write to storage under Numbat reserved key".to_vec(),
240 });
241 }
242
243 let mut tx_output = self.tx_output_cell.borrow_mut();
244 tx_output
245 .contract_storage
246 .insert(key.to_vec(), value.to_vec());
247 }
248
249 fn storage_load_vec_u8(&self, key: &[u8]) -> Vec<u8> {
250 let tx_output = self.tx_output_cell.borrow();
251 match tx_output.contract_storage.get(&key.to_vec()) {
252 None => Vec::with_capacity(0),
253 Some(value) => value.clone(),
254 }
255 }
256
257 #[inline]
258 fn storage_load_len(&self, key: &[u8]) -> usize {
259 self.storage_load_vec_u8(key).len()
260 }
261
262 fn storage_store_bytes32(&self, key: &[u8], value: &[u8; 32]) {
263 self.storage_store_slice_u8(key, &value[..].to_vec());
264 }
265
266 fn storage_load_bytes32(&self, key: &[u8]) -> [u8; 32] {
267 let value = self.storage_load_vec_u8(key);
268 let mut res = [0u8; 32];
269 let offset = 32 - value.len();
270 if !value.is_empty() {
271 res[offset..].clone_from_slice(&value[..]);
272 }
273 res
274 }
275
276 fn storage_store_big_uint(&self, key: &[u8], value: &RustBigUint) {
277 self.storage_store_slice_u8(key, &value.to_bytes_be());
278 }
279
280 fn storage_load_big_uint(&self, key: &[u8]) -> RustBigUint {
281 let value = self.storage_load_vec_u8(key);
282 let bi = BigInt::from_bytes_be(num_bigint::Sign::Plus, value.as_slice());
283 bi.into()
284 }
285
286 fn storage_store_big_uint_raw(&self, _key: &[u8], _handle: i32) {
287 panic!("cannot call storage_store_big_uint_raw in debug mode");
288 }
289
290 fn storage_load_big_uint_raw(&self, _key: &[u8]) -> i32 {
291 panic!("cannot call storage_load_big_uint_raw in debug mode");
292 }
293
294 fn storage_store_big_int(&self, key: &[u8], value: &RustBigInt) {
295 self.storage_store_slice_u8(key, &value.to_signed_bytes_be());
296 }
297
298 fn storage_load_big_int(&self, key: &[u8]) -> RustBigInt {
299 let value = self.storage_load_vec_u8(key);
300 let bi = BigInt::from_signed_bytes_be(value.as_slice());
301 bi.into()
302 }
303
304 fn storage_store_i64(&self, key: &[u8], value: i64) {
305 self.storage_store_big_int(key, &RustBigInt::from(value));
306 }
307
308 fn storage_store_u64(&self, key: &[u8], value: u64) {
309 self.storage_store_big_uint(key, &RustBigUint::from(value));
310 }
311
312 fn storage_load_i64(&self, key: &[u8]) -> i64 {
313 let bi = self.storage_load_big_int(key);
314 if let Some(v) = bi.0.to_i64() {
315 v
316 } else {
317 panic!(TxPanic {
318 status: 10,
319 message: b"storage value out of range".to_vec(),
320 })
321 }
322 }
323
324 fn storage_load_u64(&self, key: &[u8]) -> u64 {
325 let bu = self.storage_load_big_uint(key);
326 if let Some(v) = bu.0.to_u64() {
327 v
328 } else {
329 panic!(TxPanic {
330 status: 10,
331 message: b"storage value out of range".to_vec(),
332 })
333 }
334 }
335
336 #[inline]
337 fn get_call_value_big_uint(&self) -> RustBigUint {
338 self.tx_input_box.call_value.clone().into()
339 }
340
341 #[inline]
342 fn get_dcdt_value_big_uint(&self) -> RustBigUint {
343 self.tx_input_box.dcdt_value.clone().into()
344 }
345
346 #[inline]
347 fn get_dcdt_token_name(&self) -> Vec<u8> {
348 self.tx_input_box.dcdt_token_name.clone()
349 }
350
351 fn send_tx(&self, to: &Address, amount: &RustBigUint, _data: &[u8]) {
352 let mut tx_output = self.tx_output_cell.borrow_mut();
353 tx_output.send_balance_list.push(SendBalance {
354 recipient: to.clone(),
355 amount: amount.value(),
356 })
357 }
358
359 fn async_call(&self, to: &Address, amount: &RustBigUint, data: &[u8]) {
360 let mut tx_output = self.tx_output_cell.borrow_mut();
361 tx_output.async_call = Some(AsyncCallTxData {
362 to: to.clone(),
363 call_value: amount.value(),
364 call_data: data.to_vec(),
365 tx_hash: self.get_tx_hash(),
366 });
367 }
368
369 fn deploy_contract(
370 &self,
371 _gas: u64,
372 _amount: &RustBigUint,
373 _code: &BoxedBytes,
374 _code_metadata: CodeMetadata,
375 _arg_buffer: &ArgBuffer,
376 ) -> Address {
377 panic!("deploy_contract not yet implemented")
378 }
379
380 fn get_tx_hash(&self) -> H256 {
381 self.tx_input_box.tx_hash.clone()
382 }
383
384 fn get_gas_left(&self) -> u64 {
385 self.tx_input_box.gas_limit
386 }
387
388 fn get_block_timestamp(&self) -> u64 {
389 self.blockchain_info_box.current_block_info.block_timestamp
390 }
391
392 fn get_block_nonce(&self) -> u64 {
393 self.blockchain_info_box.current_block_info.block_nonce
394 }
395
396 fn get_block_round(&self) -> u64 {
397 self.blockchain_info_box.current_block_info.block_round
398 }
399
400 fn get_block_epoch(&self) -> u64 {
401 self.blockchain_info_box.current_block_info.block_epoch
402 }
403
404 fn get_block_random_seed(&self) -> Box<[u8; 48]> {
405 self.blockchain_info_box
406 .current_block_info
407 .block_random_seed
408 .clone()
409 }
410
411 fn get_prev_block_timestamp(&self) -> u64 {
412 self.blockchain_info_box.previous_block_info.block_timestamp
413 }
414
415 fn get_prev_block_nonce(&self) -> u64 {
416 self.blockchain_info_box.previous_block_info.block_nonce
417 }
418
419 fn get_prev_block_round(&self) -> u64 {
420 self.blockchain_info_box.previous_block_info.block_round
421 }
422
423 fn get_prev_block_epoch(&self) -> u64 {
424 self.blockchain_info_box.previous_block_info.block_epoch
425 }
426
427 fn get_prev_block_random_seed(&self) -> Box<[u8; 48]> {
428 self.blockchain_info_box
429 .previous_block_info
430 .block_random_seed
431 .clone()
432 }
433
434 fn sha256(&self, data: &[u8]) -> H256 {
435 let mut hasher = Sha3_256::new();
436 hasher.input(data);
437 let hash: [u8; 32] = hasher.result().into();
438 hash.into()
439 }
440
441 fn keccak256(&self, data: &[u8]) -> H256 {
442 let mut hasher = Keccak256::new();
443 hasher.input(data);
444 let hash: [u8; 32] = hasher.result().into();
445 hash.into()
446 }
447}
448
449impl numbat_wasm::ContractIOApi<RustBigInt, RustBigUint> for TxContext {
450 fn get_num_arguments(&self) -> i32 {
451 self.tx_input_box.args.len() as i32
452 }
453
454 fn check_not_payable(&self) {
455 if self.get_call_value_big_uint() > 0 {
456 self.signal_error(err_msg::NON_PAYABLE);
457 }
458 }
459
460 fn get_argument_len(&self, arg_index: i32) -> usize {
461 let arg = self.get_argument_vec_u8(arg_index);
462 arg.len()
463 }
464
465 fn copy_argument_to_slice(&self, _arg_index: i32, _slice: &mut [u8]) {
466 panic!("copy_argument_to_slice not yet implemented")
467 }
468
469 fn get_argument_vec_u8(&self, arg_index: i32) -> Vec<u8> {
470 let arg_idx_usize = arg_index as usize;
471 if arg_idx_usize >= self.tx_input_box.args.len() {
472 panic!("Tx arg index out of range");
473 }
474 self.tx_input_box.args[arg_idx_usize].clone()
475 }
476
477 fn get_argument_boxed_bytes(&self, arg_index: i32) -> BoxedBytes {
478 self.get_argument_vec_u8(arg_index).into()
479 }
480
481 fn get_argument_big_uint(&self, arg_index: i32) -> RustBigUint {
482 let bytes = self.get_argument_vec_u8(arg_index);
483 RustBigUint::from_bytes_be(&bytes[..])
484 }
485
486 fn get_argument_big_int(&self, arg_index: i32) -> RustBigInt {
487 let bytes = self.get_argument_vec_u8(arg_index);
488 RustBigInt::from_signed_bytes_be(&bytes)
489 }
490
491 fn get_argument_big_uint_raw(&self, _arg_index: i32) -> i32 {
492 panic!("cannot call get_argument_big_uint_raw in debug mode");
493 }
494
495 fn get_argument_big_int_raw(&self, _arg_index: i32) -> i32 {
496 panic!("cannot call get_argument_big_int_raw in debug mode");
497 }
498
499 fn get_argument_i64(&self, arg_index: i32) -> i64 {
500 let bytes = self.get_argument_vec_u8(arg_index);
501 let bi = BigInt::from_signed_bytes_be(&bytes);
502 if let Some(v) = bi.to_i64() {
503 v
504 } else {
505 panic!(TxPanic {
506 status: 10,
507 message: b"argument out of range".to_vec(),
508 })
509 }
510 }
511
512 fn get_argument_u64(&self, arg_index: i32) -> u64 {
513 let bytes = self.get_argument_vec_u8(arg_index);
514 let bu = BigUint::from_bytes_be(&bytes);
515 if let Some(v) = bu.to_u64() {
516 v
517 } else {
518 panic!(TxPanic {
519 status: 10,
520 message: b"argument out of range".to_vec(),
521 })
522 }
523 }
524
525 fn finish_slice_u8(&self, slice: &[u8]) {
526 let mut v = vec![0u8; slice.len()];
527 v.copy_from_slice(slice);
528 let mut tx_output = self.tx_output_cell.borrow_mut();
529 tx_output.result.result_values.push(v)
530 }
531
532 fn finish_big_int(&self, bi: &RustBigInt) {
533 self.finish_slice_u8(bi.to_signed_bytes_be().as_slice());
534 }
535
536 fn finish_big_uint(&self, bu: &RustBigUint) {
537 self.finish_slice_u8(bu.to_bytes_be().as_slice());
538 }
539
540 fn finish_big_int_raw(&self, _handle: i32) {
541 panic!("cannot call finish_big_int_raw in debug mode");
542 }
543
544 fn finish_big_uint_raw(&self, _handle: i32) {
545 panic!("cannot call finish_big_uint_raw in debug mode");
546 }
547
548 fn finish_i64(&self, value: i64) {
549 self.finish_big_int(&value.into());
550 }
551
552 fn finish_u64(&self, value: u64) {
553 self.finish_big_uint(&value.into());
554 }
555
556 fn signal_error(&self, message: &[u8]) -> ! {
557 panic!(TxPanic {
558 status: 4,
559 message: message.to_vec()
560 })
561 }
562
563 fn write_log(&self, _topics: &[[u8; 32]], _data: &[u8]) {
564 }
567}