1use std::fmt;
2use std::panic;
3
4use std::{fs};
5use std::{thread, time::Duration};
6use std::ops::{Deref, DerefMut};
7
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::{IPCChainTesterSyncClient, TIPCChainTesterSyncClient, ApplySyncClient, Action};
16
17type ClientInputProtocol = TBinaryInputProtocol<TBufferedReadTransport<ReadHalf<TTcpChannel>>>;
18type ClientOutputProtocol = TBinaryOutputProtocol<TBufferedWriteTransport<WriteHalf<TTcpChannel>>>;
19
20
21use std::convert::{From, Into};
22
23use lazy_static::lazy_static; use std::sync::{
25 Mutex,
26 MutexGuard
27};
28
29
30pub struct ChainTesterError {
31 pub json: Option<Value>,
32 pub error_string: Option<String>,
33}
34
35pub enum JsonKeyType {
36 ArrayIndex(usize),
37 MapKey(String)
38}
39
40impl From<usize> for JsonKeyType {
41 fn from(value: usize) -> Self {
42 JsonKeyType::ArrayIndex(value)
43 }
44}
45
46impl From<String> for JsonKeyType {
47 fn from(value: String) -> Self {
48 JsonKeyType::MapKey(value)
49 }
50}
51
52impl ChainTesterError {
53 pub fn get_err(&self) -> Option<String> {
54 let value = &self.json.as_ref().unwrap()["except"]["stack"][0]["data"]["s"];
55 if let Value::String(s) = value {
56 return Some(s.clone())
57 }
58 return None;
59 }
60
61 pub fn check_err(&self, err: &str) {
62 let err2 = &self.get_err().unwrap() ;
63 if err2 != err {
64 panic!("invalid error, expect {}, got {}", err, err2);
65 }
66 }
67}
68
69impl fmt::Display for ChainTesterError {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 if let Some(ref value) = self.json {
72 write!(f, "{}", serde_json::to_string_pretty(value).unwrap())
73 } else {
74 if let Some(ref err) = self.error_string {
75 write!(f, "{}", err)
76 } else {
77 write!(f, "{}", "Unknown error")
78 }
79 }
80 }
81}
82
83impl fmt::Debug for ChainTesterError {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 if let Some(ref value) = self.json {
86 write!(f, "{}", serde_json::to_string_pretty(&value).unwrap())
87 } else {
88 if let Some(ref err) = self.error_string {
89 write!(f, "{}", err)
90 } else {
91 write!(f, "{}", "Unknown error")
92 }
93 }
94 }
95}
96
97pub struct TransactionReturn {
98 pub value: Value
99}
100
101impl fmt::Display for TransactionReturn {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "{}", serde_json::to_string_pretty(&self.value).unwrap())
104 }
105}
106
107impl fmt::Debug 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
113pub type Result<T> = core::result::Result<T, ChainTesterError>;
114
115pub struct GetTableRowsPrams<'a> {
116 pub json: bool,
117 pub code: &'a str,
118 pub scope: &'a str,
119 pub table: &'a str,
120 pub lower_bound: &'a str,
121 pub upper_bound: &'a str,
122 pub limit: i64,
123 pub key_type: &'a str,
124 pub index_position: &'a str,
125 pub reverse: bool,
126 pub show_payer: bool,
127}
128
129impl<'a> Default for GetTableRowsPrams<'a> {
130 fn default() -> Self {
131 Self {
132 json: true,
133 code: "",
134 scope: "",
135 table: "",
136 lower_bound: "",
137 upper_bound: "",
138 limit: 10,
139 key_type: "",
140 index_position: "",
141 reverse: false,
142 show_payer: false
143 }
144 }
145}
146
147pub struct VMAPIClient {
148 vm_api_client: Option<ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>>,
149}
150
151pub struct ChainTesterClient {
152 client: Option<IPCChainTesterSyncClient<ClientInputProtocol, ClientOutputProtocol>>,
153}
154
155lazy_static! {
156 static ref VM_API_CLIENT: Mutex<VMAPIClient> = Mutex::new(VMAPIClient::new());
157}
158
159lazy_static! {
160 static ref CHAIN_TESTER_CLIENT: Mutex<ChainTesterClient> = Mutex::new(ChainTesterClient::new());
161}
162
163pub fn get_vm_api_client() -> MutexGuard<'static, VMAPIClient> {
164 let mut ret = VM_API_CLIENT.lock().unwrap();
165 if ret.vm_api_client.is_none() {
166 ret.init();
167 }
168 return ret;
169}
170
171pub fn close_vm_api_client() {
172 let mut ret = VM_API_CLIENT.lock().unwrap();
173 ret.close();
174}
175
176impl VMAPIClient {
177 fn new() -> Self {
178 VMAPIClient{vm_api_client: None}
179 }
180
181 pub fn init(&mut self) {
182 if self.vm_api_client.is_none() {
183 let host = crate::get_debugger_config().vm_api_server_address.clone();
184 let port = crate::get_debugger_config().vm_api_server_port;
185 let client = new_vm_api_client(&host, port).unwrap();
186 self.vm_api_client = Some(client);
187 }
188 }
189
190 pub fn close(&mut self) {
191 if self.vm_api_client.is_some() {
192 self.vm_api_client = None;
193 }
194 }
195
196 }
200
201impl Deref for VMAPIClient {
202 type Target = ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>;
203
204 fn deref(&self) -> &ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>
205 {
206 self.vm_api_client.as_ref().unwrap()
207 }
208}
209
210impl DerefMut for VMAPIClient {
211 fn deref_mut(&mut self) -> &mut Self::Target {
212 self.vm_api_client.as_mut().unwrap()
213 }
214}
215impl ChainTesterClient {
218 fn new() -> Self {
219 better_panic::install();
220 ChainTesterClient{client: None}
221 }
222
223 fn init(&mut self) {
224 if self.client.is_some() {
225 return;
226 }
227
228 let host = crate::get_debugger_config().debugger_server_address.clone();
229 let port = crate::get_debugger_config().debugger_server_port;
230
231 let mut c = TTcpChannel::new();
232
233 println!("connecting to debugger server on {}:{}", host, port);
235 c.open(&format!("{}:{}", host, port)).unwrap();
236 println!("debugger server connected");
237
238 let (i_chan, o_chan) = c.split().unwrap();
241
242 let i_tran = TBufferedReadTransport::new(i_chan);
244 let o_tran = TBufferedWriteTransport::new(o_chan);
245
246 let i_prot = TBinaryInputProtocol::new(i_tran, false);
248 let o_prot = TBinaryOutputProtocol::new(o_tran, true);
249
250 let mut client = IPCChainTesterSyncClient::new(i_prot, o_prot);
251 client.init_vm_api().unwrap();
252 let _ = get_vm_api_client(); client.init_apply_request().unwrap();
255 let _= crate::server::get_apply_request_server(); self.client = Some(client);
258
259 }
260
261 pub fn close(&mut self) {
262 if self.client.is_some() {
263 self.client = None;
264 }
265 }
266}
267
268impl Deref for ChainTesterClient {
269 type Target = IPCChainTesterSyncClient<ClientInputProtocol, ClientOutputProtocol>;
270
271 fn deref(&self) -> &IPCChainTesterSyncClient<ClientInputProtocol, ClientOutputProtocol>
272 {
273 self.client.as_ref().unwrap()
274 }
275}
276
277impl DerefMut for ChainTesterClient {
278 fn deref_mut(&mut self) -> &mut Self::Target {
279 self.client.as_mut().unwrap()
280 }
281}
282
283pub fn get_chain_tester_client() -> MutexGuard<'static, ChainTesterClient> {
284 let mut ret = CHAIN_TESTER_CLIENT.lock().unwrap();
285 if ret.client.is_none() {
286 ret.init();
287 }
288 return ret;
289}
290
291pub fn close_chain_tester_client() {
292 let mut ret = CHAIN_TESTER_CLIENT.lock().unwrap();
293 ret.close();
294}
295
296pub struct ChainTester {
297 id: i32,
298}
299
300fn parse_ret(ret: &thrift::Result<String>) -> Result<Value> {
301 match ret {
302 Ok(ret) => {
303 let tx: Value = serde_json::from_str(&ret).map_err(|err| {
304 ChainTesterError{json: None, error_string: Some(err.to_string())}
305 })?;
306
307 if tx.get("except").is_some() {
308 Err(ChainTesterError{json: Some(tx), error_string: None})
309 } else {
310 Ok(tx)
311 }
312 }
313 Err(err) => {
314 Err(ChainTesterError{
315 json: None, error_string: Some(format!("{:?}", err)),
316 })
317 }
318 }
319}
320
321fn parse_ret2(ret: &thrift::Result<Vec<u8>>) -> Result<Value> {
322 match ret {
323 Ok(ret) => {
324 let tx: Value = serde_json::from_slice(ret).map_err(|err| {
325 ChainTesterError{json: None, error_string: Some(err.to_string())}
326 })?;
327
328 if tx.get("except").is_some() {
329 Err(ChainTesterError{json: Some(tx), error_string: None})
330 } else {
331 Ok(tx)
332 }
333 }
334 Err(err) => {
335 Err(ChainTesterError{
336 json: None, error_string: Some(format!("{:?}", err)),
337 })
338 }
339 }
340}
341
342impl ChainTester {
343 pub fn new() -> Self {
344 Self { id: get_chain_tester_client().new_chain().unwrap() }
345 }
346
347 fn client(&mut self) -> MutexGuard<'static, ChainTesterClient> {
348 get_chain_tester_client()
349 }
350
351 pub fn free(&mut self) {
352 self.client().free_chain(self.id).unwrap();
353 }
354
355 pub fn produce_block(&mut self) {
356 self.client().produce_block(self.id).unwrap()
357 }
358
359 pub fn enable_debug_contract(&mut self, contract: &str, enable: bool) -> thrift::Result<()> {
360 self.client().enable_debug_contract(self.id, contract.into(), enable)
361 }
362
363 pub fn is_debug_contract_enabled(&mut self, contract: &str) -> thrift::Result<bool> {
364 self.client().is_debug_contract_enabled(self.id, contract.into())
365 }
366
367 pub fn import_key(&mut self, pub_key: &str, priv_key: &str) -> bool {
368 self.client().import_key(self.id, pub_key.into(), priv_key.into()).unwrap()
369 }
370
371 pub fn get_info(&mut self) -> Result<Value> {
372 let ret = self.client().get_info(self.id);
373 parse_ret(&ret)
374 }
375
376 pub fn get_account(&mut self, account: &str) -> Result<Value> {
377 let ret = self.client().get_account(self.id, account.into());
378 parse_ret(&ret)
379 }
380
381 pub fn push_action(&mut self, account: &str, action: &str, arguments: ActionArguments, permissions: &str) -> Result<TransactionReturn> {
382 let _account = String::from(account);
383 let _action = String::from(action);
384
385 let _arguments;
386 match &arguments {
387 ActionArguments::String(s) => {
388 _arguments = s.clone();
389 }
390 ActionArguments::Binary(b) => {
391 _arguments = hex::encode(b);
392 }
393 }
394 let _permissions = String::from(permissions);
395 match self.client().push_action(self.id, _account, _action, _arguments, _permissions) {
396 Ok(ret) => {
397 let tx: Value = serde_json::from_slice(&ret).map_err(|err| {
398 ChainTesterError{json: None, error_string: Some(err.to_string())}
399 })?;
400
401 if tx.get("except").is_some() {
402 Err(ChainTesterError{json: Some(tx), error_string: None})
403 } else {
404 Ok(TransactionReturn{value: tx})
405 }
406 }
407 Err(err) => {
408 Err(ChainTesterError{
409 json: None, error_string: Some(format!("{:?}", err)),
410 })
411 }
412 }
413 }
414
415 pub fn deploy_contract(&mut self, account: &str, wasm_file: &str, abi_file: &str) -> Result<Value> {
416 let wasm = fs::read(wasm_file).unwrap();
418 let hex_wasm = hex::encode(wasm);
419
420 let set_code_args = format!(
421 r#"
422 {{
423 "account": "{}",
424 "vmtype": 0,
425 "vmversion": 0,
426 "code": "{}"
427 }}
428 "#,
429 account,
430 hex_wasm
431 );
432
433 let permissions = format!(
434 r#"
435 {{
436 "{}": "active"
437 }}
438 "#,
439 account,
440 );
441
442 let raw_set_code_args = self.client().pack_action_args(self.id, "eosio".into(), "setcode".into(), set_code_args).unwrap();
443 let mut actions: Vec<Box<Action>> = Vec::new();
444 let setcode = Action{
445 account: Some("eosio".into()),
446 action: Some("setcode".into()),
447 permissions: Some(permissions.clone()),
448 arguments: Some(hex::encode(raw_set_code_args)),
449 };
450 actions.push(Box::new(setcode));
451
452 if !abi_file.is_empty() {
453 let abi = fs::read_to_string(abi_file).unwrap();
455 let raw_abi = self.client().pack_abi(abi).unwrap();
456 let hex_raw_abi = hex::encode(raw_abi);
457
458 let set_abi_args = format!(
459 r#"
460 {{
461 "account": "{}",
462 "abi": "{}"
463 }}
464 "#,
465 account,
466 hex_raw_abi
467 );
468
469 let raw_setabi = self.client().pack_action_args(self.id, "eosio".into(), "setabi".into(), set_abi_args).unwrap();
470 let setabi = Action{
471 account: Some("eosio".into()),
472 action: Some("setabi".into()),
473 permissions: Some(permissions.clone()),
474 arguments: Some(hex::encode(raw_setabi)),
475 };
476
477 actions.push(Box::new(setabi));
478 }
479
480 self.push_actions(actions)
481 }
482
483 pub fn push_actions(&mut self, actions: Vec<Box<Action>>) -> Result<Value> {
484 let ret = self.client().push_actions(self.id, actions);
485 parse_ret2(&ret)
486 }
487
488 pub fn get_table_rows<'a>(&mut self, json: bool, code: &'a str, scope: &'a str, table: &'a str, lower_bound: &'a str, upper_bound: &'a str, limit: i64) -> Result<Value> {
489 let param = GetTableRowsPrams {
490 json: json,
491 code: code,
492 scope: scope,
493 table: table,
494 lower_bound: lower_bound,
495 upper_bound: upper_bound,
496 limit: limit,
497 key_type: "",
498 index_position: "",
499 reverse: false,
500 show_payer: false,
501 };
502 return self.get_table_rows_ex(¶m);
503
504 }
505
506 pub fn get_table_rows_ex(&mut self, params: &GetTableRowsPrams) -> Result<Value> {
507 let ret = self.client().get_table_rows(self.id,
508 params.json,
509 params.code.into(),
510 params.scope.into(),
511 params.table.into(),
512 params.lower_bound.into(),
513 params.upper_bound.into(),
514 params.limit,
515 params.key_type.into(),
516 params.index_position.into(),
517 params.reverse,
518 params.show_payer,
519 );
520 parse_ret(&ret)
521 }
522}
523
524pub enum ActionArguments {
525 String(String),
526 Binary(Vec<u8>),
527}
528
529impl From<String> for ActionArguments {
530 fn from(value: String) -> Self {
531 ActionArguments::String(value)
532 }
533}
534
535impl From<&str> for ActionArguments {
536 fn from(value: &str) -> Self {
537 ActionArguments::String(String::from(value))
538 }
539}
540
541impl From<Vec<u8>> for ActionArguments {
542 fn from(value: Vec<u8>) -> Self {
543 ActionArguments::Binary(value)
544 }
545}
546
547impl Drop for ChainTester {
548 fn drop(&mut self) {
549 self.free();
550 }
551}
552
553pub fn new_vm_api_client(
554 host: &str,
555 port: u16,
556) -> thrift::Result<ApplySyncClient<ClientInputProtocol, ClientOutputProtocol>> {
557 let mut c = TTcpChannel::new();
558
559 println!("connecting to VM API server on {}:{}", host, port);
561 thread::sleep(Duration::from_micros(10));
563 let remote_address = format!("{}:{}", host, port);
564 for i in 0..=10 {
565 match c.open(&remote_address) {
566 Ok(()) => {
567 break;
568 }
569 Err(err) => {
570 if i == 10 {
571 panic!("{}", err)
572 } else {
573 println!("+++++++vm_api_client error: {}", err);
574 thread::sleep(Duration::from_micros(200));
575 }
576 }
577 }
578 }
579
580 println!("VM API server connected!");
581
582 let (i_chan, o_chan) = c.split()?;
585
586 let i_tran = TBufferedReadTransport::new(i_chan);
588 let o_tran = TBufferedWriteTransport::new(o_chan);
589
590 let i_prot = TBinaryInputProtocol::new(i_tran, false);
592 let o_prot = TBinaryOutputProtocol::new(o_tran, true);
593 Ok(ApplySyncClient::new(i_prot, o_prot))
595}
596
597pub fn n2s(value: u64) -> String {
599 let charmap = ".12345abcdefghijklmnopqrstuvwxyz".as_bytes();
600 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];
602 let mut tmp = value;
603 for i in 0..13 {
604 let c: u8;
605 if i == 0 {
606 c = charmap[(tmp&0x0f) as usize];
607 } else {
608 c = charmap[(tmp&0x1f) as usize];
609 }
610 s[12-i] = c;
611 if i == 0 {
612 tmp >>= 4
613 } else {
614 tmp >>= 5
615 }
616 }
617
618 let mut i = s.len() - 1;
619 while i != 0 {
620 if s[i] != '.' as u8 {
621 break
622 }
623 i -= 1;
624 }
625
626 let r = match String::from_utf8(s[0..i+1].to_vec()) {
627 Ok(v) => v,
628 Err(_) => String::from(""),
629 };
630 return r;
631}
632