1use std::fmt;
2use std::panic;
3
4use std::{fs};
5use std::{thread, time::Duration};
6use std::ops::{Deref, DerefMut};
7use std::collections::{HashMap};
8use serde_json::{Value};
9
10use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol};
11use thrift::transport::{
12 ReadHalf, TBufferedReadTransport, TBufferedWriteTransport, TIoChannel, TTcpChannel, WriteHalf,
13};
14
15use crate::interfaces::{
16 IPCChainTesterSyncClient,
17 TIPCChainTesterSyncClient,
18 ApplySyncClient,
19 Action,
20 ActionArguments,
21};
22
23type ClientInputProtocol = TBinaryInputProtocol<TBufferedReadTransport<ReadHalf<TTcpChannel>>>;
24type ClientOutputProtocol = TBinaryOutputProtocol<TBufferedWriteTransport<WriteHalf<TTcpChannel>>>;
25
26
27use std::convert::{From, Into, TryInto};
28
29use lazy_static::lazy_static; use std::sync::{
31 Mutex,
32 MutexGuard
33};
34
35
36pub struct ChainTesterError {
37 pub json: Option<Value>,
38 pub error_string: Option<String>,
39}
40
41pub enum JsonKeyType {
42 ArrayIndex(usize),
43 MapKey(String)
44}
45
46impl From<usize> for JsonKeyType {
47 fn from(value: usize) -> Self {
48 JsonKeyType::ArrayIndex(value)
49 }
50}
51
52impl From<String> for JsonKeyType {
53 fn from(value: String) -> Self {
54 JsonKeyType::MapKey(value)
55 }
56}
57
58impl ChainTesterError {
59 pub fn get_err(&self) -> Option<String> {
60 let value = &self.json.as_ref().unwrap()["except"]["stack"][0]["data"]["s"];
61 if let Value::String(s) = value {
62 return Some(s.clone())
63 }
64 return None;
65 }
66
67 pub fn check_err(&self, err: &str) {
68 let err2 = &self.get_err().unwrap() ;
69 if err2 != err {
70 panic!("invalid error, expect {}, got {}", err, err2);
71 }
72 }
73}
74
75impl fmt::Display for ChainTesterError {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 if let Some(ref value) = self.json {
78 write!(f, "{}", serde_json::to_string_pretty(value).unwrap())
79 } else {
80 if let Some(ref err) = self.error_string {
81 write!(f, "{}", err)
82 } else {
83 write!(f, "{}", "Unknown error")
84 }
85 }
86 }
87}
88
89impl fmt::Debug for ChainTesterError {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 if let Some(ref value) = self.json {
92 write!(f, "{}", serde_json::to_string_pretty(&value).unwrap())
93 } else {
94 if let Some(ref err) = self.error_string {
95 write!(f, "{}", err)
96 } else {
97 write!(f, "{}", "Unknown error")
98 }
99 }
100 }
101}
102
103pub struct TransactionReturn {
104 pub value: Value
105}
106
107impl fmt::Display for TransactionReturn {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 write!(f, "{}", serde_json::to_string_pretty(&self.value).unwrap())
110 }
111}
112
113impl fmt::Debug for TransactionReturn {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 write!(f, "{}", serde_json::to_string_pretty(&self.value).unwrap())
116 }
117}
118
119pub type Result<T> = core::result::Result<T, ChainTesterError>;
120
121pub struct GetTableRowsPrams<'a> {
122 pub json: bool,
123 pub code: &'a str,
124 pub scope: &'a str,
125 pub table: &'a str,
126 pub lower_bound: &'a str,
127 pub upper_bound: &'a str,
128 pub limit: i64,
129 pub key_type: &'a str,
130 pub index_position: &'a str,
131 pub reverse: bool,
132 pub show_payer: bool,
133}
134
135impl<'a> Default for GetTableRowsPrams<'a> {
136 fn default() -> Self {
137 Self {
138 json: true,
139 code: "",
140 scope: "",
141 table: "",
142 lower_bound: "",
143 upper_bound: "",
144 limit: 10,
145 key_type: "",
146 index_position: "",
147 reverse: false,
148 show_payer: false
149 }
150 }
151}
152
153pub struct VMAPIClient {
154 vm_api_client: Option<ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>>,
155 in_apply: bool,
156}
157
158pub struct ChainTesterClient {
159 client: Option<IPCChainTesterSyncClient<ClientInputProtocol, ClientOutputProtocol>>,
160}
161
162lazy_static! {
163 static ref VM_API_CLIENT: Mutex<VMAPIClient> = Mutex::new(VMAPIClient::new());
164}
165
166lazy_static! {
167 static ref CHAIN_TESTER_CLIENT: Mutex<ChainTesterClient> = Mutex::new(ChainTesterClient::new());
168}
169
170lazy_static! {
171 static ref TEST_MUTEX: Mutex<i32> = Mutex::new(0);
172}
173
174type FnApply = fn(u64, u64, u64);
175
176lazy_static! {
177 static ref CHAIN_TESTER_APPLYS: Mutex<HashMap<i32, HashMap<String, FnApply>>> = Mutex::new(HashMap::new());
178}
179
180pub fn get_test_mutex() -> MutexGuard<'static, i32> {
181 let ret = TEST_MUTEX.lock().unwrap();
182 return ret;
183}
184
185pub fn get_apply_map_mutex() -> MutexGuard<'static, HashMap<i32, HashMap<String, FnApply>>> {
186 let ret = CHAIN_TESTER_APPLYS.lock().unwrap();
187 return ret;
188}
189
190pub struct GlobalVariables {
191 pub current_test_case: String,
192 pub debug_mode: bool,
193}
194
195impl GlobalVariables {
196 pub fn new() -> Self {
197 GlobalVariables{current_test_case: "".into(), debug_mode: false}
198 }
199
200 pub fn get_current_test_case(&self) -> String {
201 return self.current_test_case.clone();
202 }
203
204 pub fn set_current_test_case(&mut self, test_case: &str) {
205 self.current_test_case = test_case.into();
206 }
207
208 pub fn set_debug_mode(&mut self, enable: bool) {
209 self.debug_mode = enable;
210 }
211
212 pub fn get_debug_mode(&self) -> bool {
213 return self.debug_mode;
214 }
215}
216
217lazy_static! {
218 static ref GLOBAL_VARIABLES: Mutex<GlobalVariables> = Mutex::new(GlobalVariables::new());
219}
220
221pub fn get_globals() -> MutexGuard<'static, GlobalVariables> {
222 return GLOBAL_VARIABLES.lock().unwrap();
223}
224
225pub fn init_vm_api_client() {
226 let mut ret = VM_API_CLIENT.lock().unwrap();
227 if ret.vm_api_client.is_none() {
228 ret.init();
229 }
230}
231
232pub fn get_vm_api_client() -> MutexGuard<'static, VMAPIClient> {
233 let mut ret = VM_API_CLIENT.lock().unwrap();
234 if ret.vm_api_client.is_none() {
235 ret.init();
236 }
237 return ret;
238}
239
240pub fn close_vm_api_client() {
241 let mut ret = VM_API_CLIENT.lock().unwrap();
242 ret.close();
243}
244
245impl VMAPIClient {
246 fn new() -> Self {
247 VMAPIClient{vm_api_client: None, in_apply: false}
248 }
249
250 pub fn init(&mut self) {
251 if self.vm_api_client.is_none() {
252 let host = crate::get_debugger_config().vm_api_server_address.clone();
253 let port = crate::get_debugger_config().vm_api_server_port;
254 let client = new_vm_api_client(&host, port).unwrap();
255 self.vm_api_client = Some(client);
256 }
257 }
258
259 pub fn set_in_apply(&mut self, in_apply: bool) {
260 self.in_apply = in_apply;
261 }
262
263 pub fn is_in_apply(&mut self) -> bool {
264 return self.in_apply;
265 }
266
267 pub fn close(&mut self) {
268 if self.vm_api_client.is_some() {
269 self.vm_api_client = None;
270 }
271 }
272
273 }
277
278impl Deref for VMAPIClient {
279 type Target = ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>;
280
281 fn deref(&self) -> &ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>
282 {
283 self.vm_api_client.as_ref().unwrap()
284 }
285}
286
287impl DerefMut for VMAPIClient {
288 fn deref_mut(&mut self) -> &mut Self::Target {
289 if !self.is_in_apply() {
290 panic!("error: vm api function has been called out of apply context!");
291 }
292 let client = self.vm_api_client.as_mut().unwrap();
293 return client;
294 }
295}
296impl ChainTesterClient {
299 fn new() -> Self {
300 ChainTesterClient{client: None}
302 }
303
304 fn init(&mut self) {
305 if self.client.is_some() {
306 return;
307 }
308
309 let host = crate::get_debugger_config().debugger_server_address.clone();
310 let port = crate::get_debugger_config().debugger_server_port;
311
312 let mut c = TTcpChannel::new();
313
314 println!("connecting to debugger server on {}:{}", host, port);
316 c.open(&format!("{}:{}", host, port)).unwrap();
317 println!("debugger server connected");
318
319 let (i_chan, o_chan) = c.split().unwrap();
322
323 let i_tran = TBufferedReadTransport::new(i_chan);
325 let o_tran = TBufferedWriteTransport::new(o_chan);
326
327 let i_prot = TBinaryInputProtocol::new(i_tran, false);
329 let o_prot = TBinaryOutputProtocol::new(o_tran, true);
330
331 let mut client = IPCChainTesterSyncClient::new(i_prot, o_prot);
332 client.init_vm_api().unwrap();
333 init_vm_api_client(); client.init_apply_request().unwrap();
336 crate::server::init_apply_request_server(); self.client = Some(client);
339
340 }
341
342 pub fn close(&mut self) {
343 if self.client.is_some() {
344 self.client = None;
345 }
346 }
347}
348
349impl Deref for ChainTesterClient {
350 type Target = IPCChainTesterSyncClient<ClientInputProtocol, ClientOutputProtocol>;
351
352 fn deref(&self) -> &IPCChainTesterSyncClient<ClientInputProtocol, ClientOutputProtocol>
353 {
354 self.client.as_ref().unwrap()
355 }
356}
357
358impl DerefMut for ChainTesterClient {
359 fn deref_mut(&mut self) -> &mut Self::Target {
360 self.client.as_mut().unwrap()
361 }
362}
363
364pub fn get_chain_tester_client() -> MutexGuard<'static, ChainTesterClient> {
365 let mut ret = CHAIN_TESTER_CLIENT.lock().unwrap();
366 if ret.client.is_none() {
367 ret.init();
368 }
369 return ret;
370}
371
372pub fn close_chain_tester_client() {
373 let mut ret = CHAIN_TESTER_CLIENT.lock().unwrap();
374 ret.close();
375}
376
377pub struct ChainTester {
378 id: i32,
379}
380
381fn parse_ret(ret: &thrift::Result<String>) -> Result<Value> {
382 match ret {
383 Ok(ret) => {
384 let tx: Value = serde_json::from_str(&ret).map_err(|err| {
386 ChainTesterError{json: None, error_string: Some(err.to_string())}
387 })?;
388
389 if tx.get("except").is_some() {
390 Err(ChainTesterError{json: Some(tx), error_string: None})
391 } else {
392 Ok(tx)
393 }
394 }
395 Err(err) => {
396 Err(ChainTesterError{
397 json: None, error_string: Some(format!("{:?}", err)),
398 })
399 }
400 }
401}
402
403fn parse_ret2(ret: &thrift::Result<Vec<u8>>) -> Result<Value> {
404 match ret {
405 Ok(ret) => {
406 let tx: Value = serde_json::from_slice(ret).map_err(|err| {
407 ChainTesterError{json: None, error_string: Some(err.to_string())}
408 })?;
409
410 if tx.get("except").is_some() {
411 Err(ChainTesterError{json: Some(tx), error_string: None})
412 } else {
413 Ok(tx)
414 }
415 }
416 Err(err) => {
417 Err(ChainTesterError{
418 json: None, error_string: Some(format!("{:?}", err)),
419 })
420 }
421 }
422}
423
424impl ChainTester {
425 pub fn new() -> Self {
426 let id = get_chain_tester_client().new_chain(true).unwrap();
427 get_apply_map_mutex().insert(id, HashMap::new());
428 Self { id }
429 }
430
431 pub fn new_ex(initialize: bool) -> Self {
432 Self { id: get_chain_tester_client().new_chain(initialize).unwrap() }
433 }
434
435 fn client(&mut self) -> MutexGuard<'static, ChainTesterClient> {
436 get_chain_tester_client()
437 }
438
439 pub fn free(&mut self) {
440 get_apply_map_mutex().remove(&self.id);
441 self.client().free_chain(self.id).unwrap();
442 }
443
444 pub fn produce_block(&mut self) {
445 self.client().produce_block(self.id, 0).unwrap()
446 }
447
448 pub fn produce_block_ex(&mut self, next_block_skip_seconds: i64) {
449 self.client().produce_block(self.id, next_block_skip_seconds).unwrap()
450 }
451
452 pub fn enable_debugging(&mut self, enable: bool) -> thrift::Result<()> {
453 self.client().enable_debugging(enable)
454 }
455
456 pub fn set_native_contract(&mut self, contract: &str, dylib: &str) -> thrift::Result<bool> {
457 self.client().set_native_contract(self.id, contract.into(), dylib.into())
458 }
459
460 pub fn set_native_apply(&mut self, contract: &str, apply: Option<FnApply>) -> thrift::Result<()> {
461 let tester_apply_map = &mut get_apply_map_mutex();
462 let apply_map = tester_apply_map.get_mut(&self.id).unwrap();
463 if let Some(_apply) = apply {
464 apply_map.insert(contract.into(), _apply);
465 self.enable_debug_contract(contract, true)?;
466 } else {
467 let _contract: String = contract.into();
468 apply_map.remove(&_contract);
469 self.enable_debug_contract(contract, false)?;
470 }
471 Ok(())
472 }
473
474 pub fn enable_debug_contract(&mut self, contract: &str, enable: bool) -> thrift::Result<()> {
475 self.client().enable_debug_contract(self.id, contract.into(), enable)
476 }
477
478 pub fn is_debug_contract_enabled(&mut self, contract: &str) -> thrift::Result<bool> {
479 self.client().is_debug_contract_enabled(self.id, contract.into())
480 }
481
482 pub fn import_key(&mut self, pub_key: &str, priv_key: &str) -> bool {
483 self.client().import_key(self.id, pub_key.into(), priv_key.into()).unwrap()
484 }
485
486 pub fn get_info(&mut self) -> Result<Value> {
487 let ret = self.client().get_info(self.id);
488 parse_ret(&ret)
489 }
490
491 pub fn create_key(&mut self) -> Result<Value> {
492 let ret = self.client().create_key("K1".into());
493 parse_ret(&ret)
494 }
495
496 pub fn create_key_ex(&mut self, key_type: &str) -> Result<Value> {
497 let ret = self.client().create_key(key_type.into());
498 parse_ret(&ret)
499 }
500
501 pub fn get_account(&mut self, account: &str) -> Result<Value> {
502 let ret = self.client().get_account(self.id, account.into());
503 parse_ret(&ret)
504 }
505
506 pub fn create_account(&mut self, creator: &str, account: &str, owner_key: &str, active_key: &str, ram_bytes: i64, stake_net: i64, stake_cpu: i64) -> Result<Value> {
507 let ret = self.client().create_account(self.id, creator.into(), account.into(), owner_key.into(), active_key.into(), ram_bytes, stake_net, stake_cpu);
508 parse_ret(&ret)
509 }
510
511 pub fn push_action(&mut self, account: &str, action: &str, arguments: ActionArguments, permissions: &str) -> Result<Value> {
512 let _account = String::from(account);
513 let _action = String::from(action);
514
515 let _permissions = String::from(permissions);
516 match self.client().push_action(self.id, _account, _action, arguments, _permissions) {
517 Ok(ret) => {
518 let tx: Value = serde_json::from_slice(&ret).map_err(|err| {
519 ChainTesterError{json: None, error_string: Some(err.to_string())}
520 })?;
521
522 if tx.get("except").is_some() {
523 Err(ChainTesterError{json: Some(tx), error_string: None})
524 } else {
525 Ok(tx)
526 }
527 }
528 Err(err) => {
529 Err(ChainTesterError{
530 json: None, error_string: Some(format!("{:?}", err)),
531 })
532 }
533 }
534 }
535
536 pub fn deploy_contract(&mut self, account: &str, wasm_file: &str, abi_file: &str) -> Result<Value> {
537 let wasm = fs::read(wasm_file).unwrap();
539 let hex_wasm = hex::encode(wasm);
540
541 let set_code_args = format!(
542 r#"
543 {{
544 "account": "{}",
545 "vmtype": 0,
546 "vmversion": 0,
547 "code": "{}"
548 }}
549 "#,
550 account,
551 hex_wasm
552 );
553
554 let permissions = format!(
555 r#"
556 {{
557 "{}": "active"
558 }}
559 "#,
560 account,
561 );
562
563 let raw_set_code_args = self.client().pack_action_args(self.id, "eosio".into(), "setcode".into(), set_code_args).unwrap();
564 let mut actions: Vec<Box<Action>> = Vec::new();
565 let setcode = Action{
566 account: Some("eosio".into()),
567 action: Some("setcode".into()),
568 permissions: Some(permissions.clone()),
569 arguments: Some(ActionArguments::RawArgs(raw_set_code_args)),
570 };
571 actions.push(Box::new(setcode));
572
573 if !abi_file.is_empty() {
574 let abi = fs::read_to_string(abi_file).unwrap();
576 let raw_abi = self.client().pack_abi(abi).unwrap();
577 let hex_raw_abi = hex::encode(raw_abi);
578 let set_abi_args = format!(
579 r#"
580 {{
581 "account": "{}",
582 "abi": "{}"
583 }}
584 "#,
585 account,
586 hex_raw_abi
587 );
588
589 let raw_setabi = self.client().pack_action_args(self.id, "eosio".into(), "setabi".into(), set_abi_args).unwrap();
590 let setabi = Action{
591 account: Some("eosio".into()),
592 action: Some("setabi".into()),
593 permissions: Some(permissions.clone()),
594 arguments: Some(ActionArguments::RawArgs(raw_setabi)),
595 };
596
597 actions.push(Box::new(setabi));
598 }
599
600 self.push_actions(actions)
601 }
602
603 pub fn push_actions(&mut self, actions: Vec<Box<Action>>) -> Result<Value> {
604 let ret = self.client().push_actions(self.id, actions);
605 parse_ret2(&ret)
606 }
607
608 pub fn get_table_rows(&mut self, json: bool, code: &str, scope: &str, table: &str, lower_bound: &str, upper_bound: &str, limit: i64) -> Result<Value> {
609 let param = GetTableRowsPrams {
610 json: json,
611 code: code,
612 scope: scope,
613 table: table,
614 lower_bound: lower_bound,
615 upper_bound: upper_bound,
616 limit: limit,
617 key_type: "",
618 index_position: "",
619 reverse: false,
620 show_payer: true,
621 };
622 return self.get_table_rows_ex(¶m);
623
624 }
625
626 pub fn get_table_rows_ex(&mut self, params: &GetTableRowsPrams) -> Result<Value> {
627 let ret = self.client().get_table_rows(self.id,
628 params.json,
629 params.code.into(),
630 params.scope.into(),
631 params.table.into(),
632 params.lower_bound.into(),
633 params.upper_bound.into(),
634 params.limit,
635 params.key_type.into(),
636 params.index_position.into(),
637 params.reverse,
638 params.show_payer,
639 );
640 parse_ret(&ret)
641 }
642
643 pub fn get_balance(&mut self, account: &str) -> u64 {
644 return self.get_balance_ex(account, "eosio.token", "EOS");
645 }
646
647 pub fn get_balance_ex(&mut self, account: &str, token_account: &str, symbol: &str) -> u64 {
648 let ret = self.get_table_rows(false, token_account, account, "accounts", symbol, "", 1).unwrap();
649 let rows = ret["rows"].as_array().unwrap();
650 println!("++++++++++++rows:{:?}", rows);
651 if rows.len() == 0 {
652 return 0;
653 }
654 let balance = rows[0].as_str().unwrap();
655 let _balance = hex::decode(balance).unwrap();
656 let amount: [u8;8] = match _balance[0..8].try_into() {
657 Ok(v) => v,
658 Err(_) => {
659 panic!("invalid value");
660 }
661 };
662 return u64::from_le_bytes(amount);
663 }
664}
665
666impl From<String> for ActionArguments {
672 fn from(value: String) -> Self {
673 ActionArguments::JsonArgs(value)
674 }
675}
676
677impl From<&str> for ActionArguments {
678 fn from(value: &str) -> Self {
679 ActionArguments::JsonArgs(String::from(value))
680 }
681}
682
683impl From<Vec<u8>> for ActionArguments {
684 fn from(value: Vec<u8>) -> Self {
685 ActionArguments::RawArgs(value)
686 }
687}
688
689impl Drop for ChainTester {
690 fn drop(&mut self) {
691 self.free();
692 }
693}
694
695pub fn new_vm_api_client(
696 host: &str,
697 port: u16,
698) -> thrift::Result<ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>> {
699 let mut c = TTcpChannel::new();
700
701 println!("connecting to VM API server on {}:{}", host, port);
703 thread::sleep(Duration::from_micros(10));
705 let remote_address = format!("{}:{}", host, port);
706 for i in 0..=10 {
707 match c.open(&remote_address) {
708 Ok(()) => {
709 break;
710 }
711 Err(err) => {
712 if i == 10 {
713 panic!("{}", err)
714 } else {
715 println!("+++++++vm_api_client error: {}", err);
716 thread::sleep(Duration::from_micros(200));
717 }
718 }
719 }
720 }
721
722 println!("VM API server connected!");
723
724 let (i_chan, o_chan) = c.split()?;
727
728 let i_tran = TBufferedReadTransport::new(i_chan);
730 let o_tran = TBufferedWriteTransport::new(o_chan);
731
732 let i_prot = TBinaryInputProtocol::new(i_tran, false);
734 let o_prot = TBinaryOutputProtocol::new(o_tran, true);
735 Ok(ApplySyncClient::new(i_prot, o_prot))
737}
738
739pub fn n2s(value: u64) -> String {
741 let charmap = ".12345abcdefghijklmnopqrstuvwxyz".as_bytes();
742 let mut s: [u8; 13] = ['.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8, '.' as u8];
744 let mut tmp = value;
745 for i in 0..13 {
746 let c: u8;
747 if i == 0 {
748 c = charmap[(tmp&0x0f) as usize];
749 } else {
750 c = charmap[(tmp&0x1f) as usize];
751 }
752 s[12-i] = c;
753 if i == 0 {
754 tmp >>= 4
755 } else {
756 tmp >>= 5
757 }
758 }
759
760 let mut i = s.len() - 1;
761 while i != 0 {
762 if s[i] != '.' as u8 {
763 break
764 }
765 i -= 1;
766 }
767
768 let r = match String::from_utf8(s[0..i+1].to_vec()) {
769 Ok(v) => v,
770 Err(_) => String::from(""),
771 };
772 return r;
773}