fisco-bcos-cli 0.4.0

FISCO BCOS CLI in Rust.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
use std::{
    str::FromStr,
    collections::HashMap,
};
use fisco_bcos_service::{
    abi::ABI,
    config::Config,
    ethabi::token::Token,
    serde_json::{
        json,
        Value as JSONValue,
    },
    web3::service::{
        Service as Web3Service,
        ServiceError as Web3ServiceError,
    },
    precompiled::{
        cns_service::CNSService,
        sql_service::SQLService,
        consensus_service::ConsensusService,
        permission_service::PermissionService,
        system_config_service::SystemConfigService,
        chain_governance_service::ChainGovernanceService,
        contract_life_cycle_service::ContractLifeCycleService,
        precompiled_service::PrecompiledServiceError,
    },
    create_config_with_file,
    create_web3_service_with_config,
};

fn valid_args_len(args_length: usize, min_len: usize) -> Result<(), Web3ServiceError> {
    if args_length < min_len {
        Err(Web3ServiceError::CustomError {
            message: format!("Argument count should not less than {:}", min_len),
        })
    } else {
        Ok(())
    }
}

fn convert_str_to_bool(value: &str) -> bool {
    value.eq_ignore_ascii_case("true")
}

fn convert_str_to_number<T:FromStr>(value: &str, default: T) -> T {
    String::from(value).parse::<T>().unwrap_or(default)
}

fn convert_str_to_json(value: &str) -> JSONValue {
    fisco_bcos_service::serde_json::from_str::<JSONValue>(value).unwrap_or(json!(null))
}

fn parse_contract_function_tokens(args: &Vec<String>, config: &Option<Config>) -> Vec<Token> {
    match config {
        None => vec![],
        Some(config) => {
            let args_length = args.len();
            let params: Vec<String> = if args_length > 3 {
                Vec::from(&args[3..])
            } else {
                vec![]
            };
            match ABI::new_with_contract_config(&config.contract, &args[0], config.sm_crypto) {
                Ok(abi) =>  abi.parse_function_tokens(&args[2], &params).unwrap_or(vec![]),
                Err(error) => {
                    println!("\nError: {:?}\n", error);
                    vec![]
                },
            }
        }
    }
}

fn parse_contract_constructor_tokens(args: &Vec<String>, config: &Option<Config>) -> Vec<Token> {
    match config {
        None => vec![],
        Some(config) => {
            let args_length = args.len();
            let params: Vec<String> = if args_length > 2 {
                Vec::from(&args[2..])
            } else {
                vec![]
            };
            match ABI::new_with_contract_config(&config.contract, &args[0], config.sm_crypto) {
                Ok(abi) =>   abi.parse_constructor_tokens(&params).unwrap_or(vec![]),
                Err(error) => {
                    println!("\nError: {:?}\n", error);
                    vec![]
                },
            }
        }
    }
}

pub(crate) struct Cli {
    config: Option<Config>,
    web3_service: Option<Web3Service>,
}

impl Cli {
    fn set_config(&mut self, config_path: &str) {
        match create_config_with_file(config_path) {
            Ok(config) => {
                match create_web3_service_with_config(&config) {
                    Ok(web3_service) => {
                        self.web3_service = Some(web3_service);
                    },
                    Err(error) =>  println!("\n Web3 Service initialize error: {:?}\n", error),
                };
                self.config = Some(config);
            },
            Err(error) => println!("\n Config initialize error: {:?}\n", error),
        };
    }

    async fn call_web3_service(&self, method: &str, args: &Vec<String>) {
        let args_length = args.len();
        let web3_service = self.web3_service.as_ref().unwrap();
        let response = match method {
            "get_client_version" => web3_service.get_client_version().await,
            "get_block_number" => web3_service.get_block_number().await.map(|v| json!(v)),
            "get_pbft_view" => web3_service.get_pbft_view().await.map(|v| json!(v)),
            "get_sealer_list" => web3_service.get_sealer_list().await.map(|v| json!(v)),
            "get_observer_list" => web3_service.get_observer_list().await.map(|v| json!(v)),
            "get_consensus_status" => web3_service.get_consensus_status().await,
            "get_sync_status" => web3_service.get_sync_status().await,
            "get_peers" => web3_service.get_peers().await.map(|v| json!(v)),
            "get_group_peers" => web3_service.get_group_peers().await.map(|v| json!(v)),
            "get_node_id_list" => web3_service.get_node_id_list().await.map(|v| json!(v)),
            "get_group_list" => web3_service.get_group_list().await.map(|v| json!(v)),
            "get_block_by_hash" => {
               match valid_args_len(args_length, 2) {
                   Err(err) => Err(err),
                   Ok(_) => web3_service.get_block_by_hash(
                       &args[0],
                       convert_str_to_bool(&args[1])
                   ).await,
               }
            },
            "get_block_by_number" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_block_by_number(
                        &args[0],
                        convert_str_to_bool(&args[1])
                    ).await,
                }
            },
            "get_block_header_by_hash" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_block_header_by_hash(
                        &args[0],
                        convert_str_to_bool(&args[1])
                    ).await,
                }
            },
            "get_block_header_by_number" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_block_header_by_number(
                        &args[0],
                        convert_str_to_bool(&args[1])
                    ).await,
                }
            },
            "get_block_hash_by_number" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_block_hash_by_number(&args[0])
                        .await.map(|v| json!(v)),
                }
            },
            "get_transaction_by_hash" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_transaction_by_hash(&args[0]).await,
                }
            },
            "get_transaction_by_block_hash_and_index" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_transaction_by_block_hash_and_index(
                        &args[0],
                        &args[1],
                    ).await,
                }
            },
            "get_transaction_by_block_number_and_index" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_transaction_by_block_number_and_index(
                        &args[0],
                        &args[1],
                    ).await,
                }
            },
            "get_transaction_receipt" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_transaction_receipt(&args[0]).await,
                }
            },
            "get_pending_transactions" => web3_service.get_pending_transactions().await,
            "get_pending_tx_size" => web3_service.get_pending_tx_size().await.map(|v| json!(v)),
            "get_code" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_code(&args[0]).await.map(|v| json!(v)),
                }
            },
            "get_total_transaction_count" => web3_service.get_total_transaction_count().await,
            "call" => {
                match valid_args_len(args_length, 3) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        let tokens = parse_contract_function_tokens(args, &self.config);
                        let response= web3_service.call(
                            &args[0],
                            &args[1],
                            &args[2],
                            &tokens,
                        ).await;
                        match response {
                            Err(err) => Err(err),
                            Ok(data) => {
                                println!("\n{:?}\n", data);
                                Ok(json!(null))
                            }
                        }
                    },
                }
            },
            "send_raw_transaction" => {
                match valid_args_len(args_length, 3) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        let tokens = parse_contract_function_tokens(args, &self.config);
                        web3_service.send_raw_transaction(
                            &args[0],
                            &args[1],
                            &args[2],
                            &tokens,
                        ).await.map(|v| json!(v))
                    },
                }
            },
            "send_raw_transaction_and_get_proof" => {
                match valid_args_len(args_length, 3) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        let tokens = parse_contract_function_tokens(args, &self.config);
                        web3_service.send_raw_transaction_and_get_proof(
                            &args[0],
                            &args[1],
                            &args[2],
                            &tokens,
                        ).await.map(|v| json!(v))
                    },
                }
            },
            "deploy" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        let tokens = parse_contract_constructor_tokens(args, &self.config);
                        web3_service.deploy(&args[0], &tokens).await
                    }
                }
            },
            "compile" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        let link_libraries: Option<HashMap<String, String>> = if args_length > 1 {
                            Some(fisco_bcos_service::serde_json::from_str::<HashMap<String, String>>(&args[1]).unwrap())
                        } else {
                            None
                        };
                        web3_service.compile(&args[0], &link_libraries).await.map(|_| json!(null))
                    }
                }
            },
            "get_system_config_by_key" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_system_config_by_key(&args[0]).await.map(|v| json!(v)),
                }
            },
            "get_transaction_by_hash_with_proof" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_transaction_by_hash_with_proof(&args[0]).await,
                }
            },
            "get_transaction_receipt_by_hash_with_proof" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => web3_service.get_transaction_receipt_by_hash_with_proof(&args[0]).await,
                }
            },
            "generate_group" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        let params = convert_str_to_json(&args[0]);
                        web3_service.generate_group(&params).await
                    },
                }
            },
            "start_group" => web3_service.start_group().await,
            "stop_group" => web3_service.stop_group().await,
            "remove_group" => web3_service.remove_group().await,
            "recover_group" => web3_service.recover_group().await,
            "query_group_status" => web3_service.query_group_status().await,
            "get_node_info" => web3_service.get_node_info().await,
            "get_batch_receipts_by_block_number_and_range" => {
                match valid_args_len(args_length, 4) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        web3_service.get_batch_receipts_by_block_number_and_range(
                            &args[0],
                            convert_str_to_number::<u32>(&args[1], 0),
                            convert_str_to_number::<i32>(&args[2], -1),
                            convert_str_to_bool(&args[3]),
                        ).await
                    },
                }
            },
            "get_batch_receipts_by_block_hash_and_range" => {
                match valid_args_len(args_length, 4) {
                    Err(err) => Err(err),
                    Ok(_) => {
                        web3_service.get_batch_receipts_by_block_hash_and_range(
                            &args[0],
                            convert_str_to_number::<u32>(&args[1], 0),
                            convert_str_to_number::<i32>(&args[2], -1),
                            convert_str_to_bool(&args[3]),
                        ).await
                    },
                }
            },
            command => Err(Web3ServiceError::CustomError {
                message: format!("Unavailable command {:?}", command),
            })
        };
        match response {
            Ok(data) => {
                if !data.is_null() {
                    println!("\n{:?}\n", data)
                }
            },
            Err(error) => println!("\nError: {:?}\n", error),
        };
    }

    async fn call_system_config_service(&self, args: &Vec<String>) {
        let args_length = args.len();
        match valid_args_len(args_length, 2) {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(_) => {
                let web3_service = self.web3_service.as_ref().unwrap();
                let system_config_service = SystemConfigService::new(web3_service);
                let response = system_config_service.set_value_by_key(&args[0], &args[1]).await;
                match response {
                    Err(error) => println!("\nError: {:?}\n", error),
                    Ok(data) =>  println!("\n{:?}\n", data),
                };
            }
        };
    }

    async fn call_consensus_service(&self, method: &str, args: &Vec<String>) {
        let args_length = args.len();
        match valid_args_len(args_length, 1) {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(_) => {
                let web3_service = self.web3_service.as_ref().unwrap();
                let consensus_service = ConsensusService::new(web3_service);
                let response = match method {
                    "consensus:add_sealer" => consensus_service.add_sealer(&args[0]).await,
                    "consensus:add_observer" => consensus_service.add_observer(&args[0]).await,
                    "consensus:remove" => consensus_service.remove(&args[0]).await,
                    command => Err(PrecompiledServiceError::CustomError {
                        message: format!("Unavailable command {:?}", command),
                    })
                };
                match response {
                    Err(error) => println!("\nError: {:?}\n", error),
                    Ok(data) =>  println!("\n{:?}\n", data),
                };
            }
        };
    }

    async fn call_cns_service(&self, method: &str, args: &Vec<String>) {
        let args_length = args.len();
        let web3_service = self.web3_service.as_ref().unwrap();
        let cns_service = CNSService::new(web3_service);
        let response = match method {
            "cns:insert" => {
                match valid_args_len(args_length, 4) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => cns_service.insert(&args[0], &args[1], &args[2], &args[3]).await.map(|v| json!(v)),
                }
            },
            "cns:select_by_name" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => cns_service.select_by_name(&args[0]).await,
                }
            },
            "cns:select_by_name_and_version" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => cns_service.select_by_name_and_version(&args[0], &args[1]).await,
                }
            },
            "cns:get_contract_address" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => cns_service.get_contract_address(&args[0], &args[1]).await.map(|v| json!(v)),
                }
            },
            command => Err(PrecompiledServiceError::CustomError {
                message: format!("Unavailable command {:?}", command),
            })
        };
        match response {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(data) =>  println!("\n{:?}\n", data),
        };
    }

    async fn call_permission_service(&self, method: &str, args: &Vec<String>) {
        let args_length = args.len();
        let web3_service = self.web3_service.as_ref().unwrap();
        let permission_service = PermissionService::new(web3_service);
        let response = match method {
            "permission:insert" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => permission_service.insert(&args[0], &args[1]).await.map(|v| json!(v)),
                }
            },
            "permission:remove" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => permission_service.remove(&args[0], &args[1]).await.map(|v| json!(v)),
                }
            },
            "permission:query_by_name" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => permission_service.query_by_name(&args[0]).await,
                }
            },
            "permission:grant_write" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => permission_service.grant_write(&args[0], &args[1]).await.map(|v| json!(v)),
                }
            },
            "permission:revoke_write" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => permission_service.revoke_write(&args[0], &args[1]).await.map(|v| json!(v)),
                }
            },
            "permission:query_permission" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => permission_service.query_permission(&args[0]).await,
                }
            },
            command => Err(PrecompiledServiceError::CustomError {
                message: format!("Unavailable command {:?}", command),
            })
        };
        match response {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(data) =>  println!("\n{:?}\n", data),
        };
    }

    async fn call_contract_life_cycle_service(&self, method: &str, args: &Vec<String>) {
        let args_length = args.len();
        let web3_service = self.web3_service.as_ref().unwrap();
        let contract_life_cycle_service = ContractLifeCycleService::new(web3_service);
        let response = match method {
            "contract_life_cycle:freeze" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => contract_life_cycle_service.freeze(&args[0]).await.map(|v| json!(v)),
                }
            },
            "contract_life_cycle:unfreeze" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => contract_life_cycle_service.unfreeze(&args[0]).await.map(|v| json!(v)),
                }
            },
            "contract_life_cycle:grant_manager" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => contract_life_cycle_service.grant_manager(&args[0], &args[1]).await.map(|v| json!(v)),
                }
            },
            "contract_life_cycle:get_status" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => contract_life_cycle_service.get_status(&args[0]).await.map(|v| json!(v)),
                }
            },
            "contract_life_cycle:list_manager" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => contract_life_cycle_service.list_manager(&args[0]).await.map(|v| json!(v)),
                }
            },
            command => Err(PrecompiledServiceError::CustomError {
                message: format!("Unavailable command {:?}", command),
            })
        };
        match response {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(data) =>  println!("\n{:?}\n", data),
        };
    }

    async fn call_chain_governance_service(&self, method: &str, args: &Vec<String>) {
        let args_length = args.len();
        let web3_service = self.web3_service.as_ref().unwrap();
        let chain_governance_service = ChainGovernanceService::new(web3_service);
        let response = match method {
            "chain_governance_service:grant_committee_member" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.grant_committee_member(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:revoke_committee_member" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.revoke_committee_member(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:list_committee_members" => chain_governance_service.list_committee_members().await,
            "chain_governance_service:query_committee_member_weight" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.query_committee_member_weight(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:update_committee_member_weight" => {
                match valid_args_len(args_length, 2) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.update_committee_member_weight(
                        &args[0],
                        convert_str_to_number::<i32>(&args[1], 1),
                    ).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:query_votes_of_member" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.query_votes_of_member(&args[0]).await,
                }
            },
            "chain_governance_service:query_votes_of_threshold" => chain_governance_service.query_votes_of_threshold().await,
            "chain_governance_service:update_threshold" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.update_threshold(
                        convert_str_to_number::<i32>(&args[0], 0),
                    ).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:query_threshold" => chain_governance_service.query_threshold().await.map(|v| json!(v)),
            "chain_governance_service:grant_operator" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.grant_operator(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:revoke_operator" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.revoke_operator(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:list_operators" => chain_governance_service.list_operators().await,
            "chain_governance_service:freeze_account" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.freeze_account(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:unfreeze_account" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.unfreeze_account(&args[0]).await.map(|v| json!(v)),
                }
            },
            "chain_governance_service:get_account_status" => {
                match valid_args_len(args_length, 1) {
                    Err(err) => Err(PrecompiledServiceError::Web3ServiceError(err)),
                    Ok(_) => chain_governance_service.get_account_status(&args[0]).await.map(|v| json!(v)),
                }
            },
            command => Err(PrecompiledServiceError::CustomError {
                message: format!("Unavailable command {:?}", command),
            })
        };
        match response {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(data) =>  println!("\n{:?}\n", data),
        };
    }

    async fn call_sql_service(&self, args: &Vec<String>) {
        let args_length = args.len();
        match valid_args_len(args_length, 1) {
            Err(error) => println!("\nError: {:?}\n", error),
            Ok(_) => {
                let web3_service = self.web3_service.as_ref().unwrap();
                let sql_service = SQLService::new(web3_service);
                let response = sql_service.execute(&args[0]).await;
                match response {
                    Err(error) => println!("\nError: {:?}\n", error),
                    Ok(data) =>  println!("\n{:?}\n", data),
                };
            }
        };
    }

    fn echo_help(&self) {
        println!("\n1. Use set_config to initialize environment(e.g., set_config ./config/config.json).");
        println!("2. Use the below APIs to interact with FISCO BCOS:\n");
        println!("* get_client_version                                         Query the current node version.");
        println!("* get_block_number                                           Query the number of most recent block.");
        println!("* get_pbft_view                                              Query the pbft view of node.");
        println!("* get_sealer_list                                            Query nodeId list for sealer nodes.");
        println!("* get_observer_list                                          Query nodeId list for observer nodes.");
        println!("* get_consensus_status                                       Query consensus status.");
        println!("* get_sync_status                                            Query sync status.");
        println!("* get_peers                                                  Query peers currently connected to the client.");
        println!("* get_group_peers                                            Query nodeId list for sealer and observer nodes.");
        println!("* get_node_id_list                                           Query nodeId list for all connected nodes.");
        println!("* get_group_list                                             Query group list.");
        println!("* get_block_by_hash                                          Query information about a block by hash.");
        println!("* get_block_by_number                                        Query information about a block by number.");
        println!("* get_block_header_by_hash                                   Query information about a block header by hash.");
        println!("* get_block_header_by_number                                 Query information about a block header by block number.");
        println!("* get_block_hash_by_number                                   Query block hash by block number.");
        println!("* get_transaction_by_hash                                    Query information about a transaction requested by transaction hash.");
        println!("* get_transaction_by_block_hash_and_index                    Query information about a transaction by block hash and transaction index position.");
        println!("* get_transaction_by_block_number_and_index                  Query information about a transaction by block number and transaction index position.");
        println!("* get_transaction_receipt                                    Query the receipt of a transaction by transaction hash.");
        println!("* get_pending_transactions                                   Query pending transactions.");
        println!("* get_pending_tx_size                                        Query pending transactions size.");
        println!("* get_code                                                   Query code at a given address.");
        println!("* get_total_transaction_count                                Query total transaction count.");
        println!("* get_system_config_by_key                                   Query a system config value by key.");
        println!("* call                                                       Call a contract by a function and parameters.");
        println!("* send_raw_transaction                                       Execute a signed transaction with a contract function and parameters.");
        println!("* send_raw_transaction_and_get_proof                         Execute a signed transaction with a contract function and parameters.");
        println!("* deploy                                                     Deploy a contract on blockchain.");
        println!("* compile                                                    Compile sol file to abi & bin files.");
        println!("* get_transaction_by_hash_with_proof                         Query the transaction and transaction proof by transaction hash.");
        println!("* get_transaction_receipt_by_hash_with_proof                 Query the receipt and transaction receipt proof by transaction hash.");
        println!("* generate_group                                             Generate a group for the specified node.");
        println!("* start_group                                                Start the specified group of the specified node.");
        println!("* stop_group                                                 Stop the specified group of the specified node.");
        println!("* remove_group                                               Remove the specified group of the specified node.");
        println!("* recover_group                                              Recover the specified group of the specified node.");
        println!("* query_group_status                                         Query the status of the specified group of the specified node.");
        println!("* get_node_info                                              Query the specified node information.");
        println!("* get_batch_receipts_by_block_number_and_range               Get batched transaction receipts according to block number and the transaction range.");
        println!("* get_batch_receipts_by_block_hash_and_range                 Get batched transaction receipts according to block hash and the transaction range.");
        println!("* system_config:set_value_by_key                             SystemConfigPrecompiled: Set a system config value by key.");
        println!("* consensus:add_sealer                                       ConsensusPrecompiled: Add a sealer node.");
        println!("* consensus:add_observer                                     ConsensusPrecompiled: Add an observer node.");
        println!("* consensus:remove                                           ConsensusPrecompiled: Remove a node.");
        println!("* cns:insert                                                 CNSPrecompiled: Insert CNS information for the given contract");
        println!("* cns:select_by_name                                         CNSPrecompiled: Query CNS information by contract name.");
        println!("* cns:select_by_name_and_version                             CNSPrecompiled: Query CNS information by contract name and contract version.");
        println!("* cns:get_contract_address                                   CNSPrecompiled: Query contract address by contract name.");
        println!("* permission:insert                                          PermissionPrecompiled: Grant the specified account write permission for the specified table.");
        println!("* permission:remove                                          PermissionPrecompiled: Remove the specified account write permission for the specified table.");
        println!("* permission:query_by_name                                   PermissionPrecompiled: Query the accounts who have write permission for the specified table.");
        println!("* permission:grant_write                                     PermissionPrecompiled: Grant the specified account write permission for the specified contract.");
        println!("* permission:revoke_write                                    PermissionPrecompiled: Revoke the specified account write permission for the specified contract.");
        println!("* permission:query_permission                                PermissionPrecompiled: Query the accounts who have write permission for the specified contract.");
        println!("* contract_life_cycle:freeze                                 ContractLifeCyclePrecompiled: Freeze the specified contract.");
        println!("* contract_life_cycle:unfreeze                               ContractLifeCyclePrecompiled: Unfreeze the specified contract.");
        println!("* contract_life_cycle:grant_manager                          ContractLifeCyclePrecompiled: Authorize a account to be the manager of the contract.");
        println!("* contract_life_cycle:get_status                             ContractLifeCyclePrecompiled: Query the status of the specified contract.");
        println!("* contract_life_cycle:list_manager                           ContractLifeCyclePrecompiled: Query the managers of the specified contract.");
        println!("* chain_governance_service:grant_committee_member            ChainGovernancePrecompiled: Grant the account committee member.");
        println!("* chain_governance_service:revoke_committee_member           ChainGovernancePrecompiled: Revoke the account from committee member.");
        println!("* chain_governance_service:list_committee_members            ChainGovernancePrecompiled: List all committee members.");
        println!("* chain_governance_service:query_committee_member_weight     ChainGovernancePrecompiled: Query the committee member weight.");
        println!("* chain_governance_service:update_committee_member_weight    ChainGovernancePrecompiled: Update the committee member weight.");
        println!("* chain_governance_service:query_votes_of_member             ChainGovernancePrecompiled: Query votes of a committee member.");
        println!("* chain_governance_service:query_votes_of_threshold          ChainGovernancePrecompiled: Query votes of updateThreshold operation.");
        println!("* chain_governance_service:update_threshold                  ChainGovernancePrecompiled: Update the threshold.");
        println!("* chain_governance_service:query_threshold                   ChainGovernancePrecompiled: Query the threshold.");
        println!("* chain_governance_service:grant_operator                    ChainGovernancePrecompiled: Grant the operator.");
        println!("* chain_governance_service:revoke_operator                   ChainGovernancePrecompiled: Revoke the operator.");
        println!("* chain_governance_service:list_operators                    ChainGovernancePrecompiled: List all operators.");
        println!("* chain_governance_service:freeze_account                    ChainGovernancePrecompiled: Freeze the contract");
        println!("* chain_governance_service:unfreeze_account                  ChainGovernancePrecompiled: Unfreeze the contract.");
        println!("* chain_governance_service:get_account_status                ChainGovernancePrecompiled: Get the contract status.");
        println!("* sql                                                        Execute CRUD operations with SQL.\n");
    }

    pub(crate) fn new() -> Cli {
        Cli { config: None, web3_service: None }
    }

    pub(crate) async fn run_command(&mut self, command: &str) {
        let re = fancy_regex::Regex::new(r#"(".+"|'.+'|[^\s]+)"#).unwrap();
        let parts: Vec<&str> = re.find_iter(command)
            .map(|item| item.unwrap().as_str().trim_start_matches("'").trim_end_matches("'"))
            .collect();
        let method = parts[0];
        let args: Vec<String> = if parts.len() > 1 {
            parts[1..].iter().map(|&v| v.to_owned()).collect()
        } else {
            vec![]
        };
        let args_length = args.len();
        match method {
            "help" => self.echo_help(),
            "set_config" => {
                match valid_args_len(args_length, 1) {
                    Ok(_) => self.set_config(&args[0]),
                    Err(error) => println!("\nError: {:?}\n", error),
                }
            },
            _ => {
                if self.config.is_none() {
                    println!("\nError: Please initialize the environment with set_config first\n");
                } else {
                    if method.starts_with("system_config:") {
                        self.call_system_config_service(&args).await
                    } else if method.starts_with("consensus:") {
                        self.call_consensus_service(method, &args).await
                    } else if method.starts_with("cns:") {
                        self.call_cns_service(method, &args).await
                    } else if method.starts_with("permission:") {
                        self.call_permission_service(method, &args).await
                    } else if method.starts_with("contract_life_cycle:") {
                        self.call_contract_life_cycle_service(method, &args).await
                    } else if method.starts_with("chain_governance_service:")  {
                        self.call_chain_governance_service(method, &args).await
                    } else if method.eq("sql") {
                        self.call_sql_service(&args).await
                    } else {
                        self.call_web3_service(method, &args).await
                    }
                }
            },
        };
    }
}