2538 {
2542
2543 CLI::App app{
"Command Line Interface to SYSIO Client"};
2544 app.require_subcommand();
2545
2550
2551 app.add_option(
"-u,--url",
url,
localized(
"The http/https URL where ${n} is running", (
"n", node_executable_name)),
true );
2552 app.add_option(
"--wallet-url",
wallet_url,
localized(
"The http/https URL where ${k} is running", (
"k", key_store_executable_name)),
true );
2553
2555 app.add_flag(
"-n,--no-verify",
no_verify,
localized(
"Don't verify peer certificate when using HTTPS"));
2556 app.add_flag(
"--no-auto-" +
string(key_store_executable_name),
no_auto_kiod,
localized(
"Don't automatically launch a ${k} if one is not currently running", (
"k", key_store_executable_name)));
2558
2559 app.add_flag(
"-v,--verbose", verbose,
localized(
"Output verbose errors and action console output"));
2562
2564 version->require_subcommand();
2565
2566 version->add_subcommand(
"client",
localized(
"Retrieve basic version information of the client"))->callback([] {
2568 });
2569
2570 version->add_subcommand(
"full",
localized(
"Retrieve full version information of the client"))->callback([] {
2572 });
2573
2574
2575 auto create =
app.add_subcommand(
"create",
localized(
"Create various items, on and off the blockchain"));
2576 create->require_subcommand();
2577
2578 bool r1 = false;
2579 string key_file;
2580 bool print_console = false;
2581
2582 auto create_key = create->add_subcommand(
"key",
localized(
"Create a new keypair and print the public and private keys"))->callback( [&r1, &key_file, &print_console](){
2583 if (key_file.empty() && !print_console) {
2584 std::cerr << "ERROR: Either indicate a file using \"--file\" or pass \"--to-console\"" << std::endl;
2585 return;
2586 }
2587
2589 auto privs = pk.to_string();
2590 auto pubs = pk.get_public_key().
to_string();
2591 if (print_console) {
2592 std::cout <<
localized(
"Private key: ${key}", (
"key", privs) ) << std::endl;
2593 std::cout <<
localized(
"Public key: ${key}", (
"key", pubs ) ) << std::endl;
2594 } else {
2595 std::cerr <<
localized(
"saving keys to ${filename}", (
"filename", key_file)) << std::endl;
2596 std::ofstream out( key_file.c_str() );
2597 out <<
localized(
"Private key: ${key}", (
"key", privs) ) << std::endl;
2598 out <<
localized(
"Public key: ${key}", (
"key", pubs ) ) << std::endl;
2599 }
2600 });
2601 create_key->add_flag( "--r1", r1, "Generate a key using the R1 curve (iPhone), instead of the K1 curve (Bitcoin)" );
2602 create_key->add_option(
"-f,--file", key_file,
localized(
"Name of file to write private/public key output to. (Must be set, unless \"--to-console\" is passed"));
2603 create_key->add_flag(
"--to-console", print_console,
localized(
"Print private/public keys to console."));
2604
2605
2607
2608
2609 auto convert =
app.add_subcommand(
"convert",
localized(
"Pack and unpack transactions"));
2610 convert->require_subcommand();
2611
2612
2613 string plain_signed_transaction_json;
2614 bool pack_action_data_flag = false;
2615 auto pack_transaction = convert->add_subcommand(
"pack_transaction",
localized(
"From plain signed JSON to packed form"));
2616 pack_transaction->add_option(
"transaction", plain_signed_transaction_json,
localized(
"The plain signed JSON (string)"))->required();
2617 pack_transaction->add_flag(
"--pack-action-data", pack_action_data_flag,
localized(
"Pack all action data within transaction, needs interaction with ${n}", (
"n", node_executable_name)));
2618 pack_transaction->callback([&] {
2620 if( pack_action_data_flag ) {
2622 try {
2628 try {
2631 }
SYS_RETHROW_EXCEPTIONS( transaction_type_exception,
"Fail to convert transaction, --pack-action-data likely needed" )
2632 }
2633 });
2634
2635
2636 string packed_transaction_json;
2637 bool unpack_action_data_flag = false;
2638 auto unpack_transaction = convert->add_subcommand(
"unpack_transaction",
localized(
"From packed to plain signed JSON form"));
2639 unpack_transaction->add_option(
"transaction", packed_transaction_json,
localized(
"The packed transaction JSON (string containing packed_trx and optionally compression fields)"))->required();
2640 unpack_transaction->add_flag(
"--unpack-action-data", unpack_action_data_flag,
localized(
"Unpack all action data within transaction, needs interaction with ${n}", (
"n", node_executable_name)));
2641 unpack_transaction->callback([&] {
2644 try {
2650 if( unpack_action_data_flag ) {
2652 } else {
2653 trx_var = strx;
2654 }
2656 });
2657
2658
2659 string unpacked_action_data_account_string;
2660 string unpacked_action_data_name_string;
2661 string unpacked_action_data_string;
2662 auto pack_action_data = convert->add_subcommand(
"pack_action_data",
localized(
"From JSON action data to packed form"));
2663 pack_action_data->add_option(
"account", unpacked_action_data_account_string,
localized(
"The name of the account hosting the contract"))->required();
2664 pack_action_data->add_option(
"name", unpacked_action_data_name_string,
localized(
"The name of the function called by this action"))->required();
2665 pack_action_data->add_option(
"unpacked_action_data", unpacked_action_data_string,
localized(
"The action data expressed as JSON"))->required();
2666 pack_action_data->callback([&] {
2668 bytes packed_action_data_string;
2669 try {
2670 packed_action_data_string =
variant_to_bin(
name(unpacked_action_data_account_string),
name(unpacked_action_data_name_string), unpacked_action_data_json);
2672 std::cout <<
fc::
to_hex(packed_action_data_string.data(), packed_action_data_string.size()) <<
std::endl;
2673 });
2674
2675
2676 string packed_action_data_account_string;
2677 string packed_action_data_name_string;
2678 string packed_action_data_string;
2679 auto unpack_action_data = convert->add_subcommand("unpack_action_data",
localized("From packed to JSON
action data form"));
2680 unpack_action_data->add_option("account", packed_action_data_account_string,
localized("The
name of the account that hosts the contract"))->required();
2681 unpack_action_data->add_option("
name", packed_action_data_name_string,
localized("The
name of the function that'
s called by this
action"))->required();
2682 unpack_action_data->add_option("packed_action_data", packed_action_data_string,
localized("The
action data expressed as packed hex
string"))->required();
2683 unpack_action_data->callback([&] {
2684 SYS_ASSERT( packed_action_data_string.size() >= 2, transaction_type_exception,
"No packed_action_data found" );
2685 vector<char> packed_action_data_blob(packed_action_data_string.size()/2);
2686 fc::from_hex(packed_action_data_string, packed_action_data_blob.data(), packed_action_data_blob.size());
2689 });
2690
2691
2692 auto get =
app.add_subcommand(
"get",
localized(
"Retrieve various items and information from the blockchain"));
2693 get->require_subcommand();
2694
2695
2696 get->add_subcommand(
"info",
localized(
"Get current blockchain information"))->callback([] {
2698 });
2699
2700
2701 string status_transaction_id;
2702 auto getTransactionStatus = get->add_subcommand(
"transaction-status",
localized(
"Get transaction status information"));
2703 getTransactionStatus->add_option(
"id", status_transaction_id,
localized(
"ID of the transaction to retrieve"))->required();
2704 getTransactionStatus->callback([&status_transaction_id] {
2705 try {
2706 chain::transaction_id_type transaction_id(status_transaction_id);
2707 } catch (...) {
2708 std::cerr << "Unable to convert " << status_transaction_id << " to transaction id." << std::endl;
2709 throw;
2710 }
2713 });
2714
2715
2716 string blockArg;
2717 bool get_bhs = false;
2718 bool get_binfo = false;
2719 auto getBlock = get->add_subcommand(
"block",
localized(
"Retrieve a full block from the blockchain"));
2720 getBlock->add_option(
"block", blockArg,
localized(
"The number or ID of the block to retrieve"))->required();
2721 getBlock->add_flag(
"--header-state", get_bhs,
localized(
"Get block header state from fork database instead") );
2722 getBlock->add_flag(
"--info", get_binfo,
localized(
"Get block info from the blockchain by block num only") );
2723 getBlock->callback([&blockArg, &get_bhs, &get_binfo] {
2724 SYSC_ASSERT( !(get_bhs && get_binfo),
"ERROR: Either --header-state or --info can be set" );
2725 if (get_binfo) {
2726 std::optional<int64_t> block_num;
2727 try {
2729 } catch (...) {
2730
2731 }
2732 SYSC_ASSERT( block_num.has_value() && (*block_num > 0),
"Invalid block num: ${block_num}", (
"block_num", blockArg) );
2735 } else {
2737 if (get_bhs) {
2739 } else {
2741 }
2742 }
2743 });
2744
2745
2746 string accountName;
2747 string coresym;
2748 bool print_json = false;
2749 auto getAccount = get->add_subcommand(
"account",
localized(
"Retrieve an account from the blockchain"));
2750 getAccount->add_option(
"name", accountName,
localized(
"The name of the account to retrieve"))->required();
2751 getAccount->add_option(
"core-symbol", coresym,
localized(
"The expected core symbol of the chain you are querying"));
2752 getAccount->add_flag(
"--json,-j", print_json,
localized(
"Output in JSON format") );
2753 getAccount->callback([&]() {
get_account(accountName, coresym, print_json); });
2754
2755
2756 string codeFilename;
2757 string abiFilename;
2758 bool code_as_wasm = true;
2759 auto getCode = get->add_subcommand(
"code",
localized(
"Retrieve the code and ABI for an account"));
2760 getCode->add_option(
"name", accountName,
localized(
"The name of the account whose code should be retrieved"))->required();
2761 getCode->add_option(
"-c,--code",codeFilename,
localized(
"The name of the file to save the contract wasm to") );
2762 getCode->add_option(
"-a,--abi",abiFilename,
localized(
"The name of the file to save the contract .abi to") );
2763 getCode->add_flag(
"--wasm", code_as_wasm,
localized(
"Save contract as wasm (ignored, default)"));
2764 getCode->callback([&] {
2765 string code_hash, wasm, abi;
2766 try {
2768 const std::vector<char> wasm_v = result["wasm"].as_blob().data;
2769 const std::vector<char> abi_v = result["abi"].as_blob().data;
2770
2772 if(wasm_v.size())
2774 code_hash = (
string)hash;
2775
2776 wasm =
string(wasm_v.begin(), wasm_v.end());
2777
2781 }
2782 catch(chain::missing_chain_api_plugin_exception&) {
2783
2785 code_hash = old_result["code_hash"].as_string();
2786 wasm = old_result["wasm"].as_string();
2787 std::cout <<
localized(
"Warning: communicating to older ${n} which returns malformed binary wasm", (
"n", node_executable_name)) << std::endl;
2789 }
2790
2791 std::cout <<
localized(
"code hash: ${code_hash}", (
"code_hash", code_hash)) << std::endl;
2792
2793 if( codeFilename.size() ){
2794 std::cout <<
localized(
"saving wasm to ${codeFilename}", (
"codeFilename", codeFilename)) << std::endl;
2795
2796 std::ofstream out( codeFilename.c_str() );
2797 out << wasm;
2798 }
2799 if( abiFilename.size() ) {
2800 std::cout <<
localized(
"saving abi to ${abiFilename}", (
"abiFilename", abiFilename)) << std::endl;
2801 std::ofstream abiout( abiFilename.c_str() );
2802 abiout << abi;
2803 }
2804 });
2805
2806
2807 string filename;
2808 auto getAbi = get->add_subcommand(
"abi",
localized(
"Retrieve the ABI for an account"));
2809 getAbi->add_option(
"name", accountName,
localized(
"The name of the account whose abi should be retrieved"))->required();
2810 getAbi->add_option(
"-f,--file",filename,
localized(
"The name of the file to save the contract .abi to instead of writing to console") );
2811 getAbi->callback([&] {
2813
2815 if( filename.size() ) {
2816 std::cerr <<
localized(
"saving abi to ${filename}", (
"filename", filename)) << std::endl;
2817 std::ofstream abiout( filename.c_str() );
2818 abiout << abi;
2819 } else {
2820 std::cout << abi << "\n";
2821 }
2822 });
2823
2824
2825 string scope;
2826 string code;
2827 string table;
2828 string lower;
2829 string upper;
2830 string table_key;
2832 string encode_type{"dec"};
2835 string index_position;
2836 bool reverse = false;
2837 bool show_payer = false;
2838 auto getTable = get->add_subcommand(
"table",
localized(
"Retrieve the contents of a database table"));
2839 getTable->add_option(
"account", code,
localized(
"The account who owns the table") )->required();
2840 getTable->add_option(
"scope", scope,
localized(
"The scope within the contract in which the table is found") )->required();
2841 getTable->add_option(
"table", table,
localized(
"The name of the table as specified by the contract abi") )->required();
2842 getTable->add_option(
"-l,--limit", limit,
localized(
"The maximum number of rows to return") );
2843 getTable->add_option(
"-k,--key", table_key,
localized(
"Deprecated") );
2844 getTable->add_option(
"-L,--lower", lower,
localized(
"JSON representation of lower bound value of key, defaults to first") );
2845 getTable->add_option(
"-U,--upper", upper,
localized(
"JSON representation of upper bound value of key, defaults to last") );
2846 getTable->add_option( "--index", index_position,
2847 localized(
"Index number, 1 - primary (first), 2 - secondary index (in order defined by multi_index), 3 - third index, etc.\n"
2848 "\t\t\t\tNumber or name of index can be specified, e.g. 'secondary' or '2'."));
2849 getTable->add_option(
"--key-type",
key_type,
2850 localized(
"The key type of --index, primary only supports (i64), all others support (i64, i128, i256, float64, float128, ripemd160, sha256).\n"
2851 "\t\t\t\tSpecial type 'name' indicates an account name."));
2852 getTable->add_option( "--encode-type", encode_type,
2853 localized(
"The encoding type of key_type (i64 , i128 , float64, float128) only support decimal encoding e.g. 'dec'"
2854 "i256 - supports both 'dec' and 'hex', ripemd160 and sha256 is 'hex' only"));
2855 getTable->add_flag(
"-b,--binary", binary,
localized(
"Return the value as BINARY rather than using abi to interpret as JSON"));
2856 getTable->add_flag(
"-r,--reverse", reverse,
localized(
"Iterate in reverse order"));
2857 getTable->add_flag(
"--show-payer", show_payer,
localized(
"Show RAM payer"));
2858
2859
2860 getTable->callback([&] {
2862 ("code",code)
2863 ("scope",scope)
2864 ("table",table)
2865 ("table_key",table_key)
2866 ("lower_bound",lower)
2867 ("upper_bound",upper)
2868 ("limit",limit)
2870 ("index_position", index_position)
2871 ("encode_type", encode_type)
2872 ("reverse", reverse)
2873 ("show_payer", show_payer)
2874 );
2875
2877 << std::endl;
2878 });
2879
2880 auto getScope = get->add_subcommand(
"scope",
localized(
"Retrieve a list of scopes and tables owned by a contract"));
2881 getScope->add_option(
"contract", code,
localized(
"The contract who owns the table") )->required();
2882 getScope->add_option(
"-t,--table", table,
localized(
"The name of the table as filter") );
2883 getScope->add_option(
"-l,--limit", limit,
localized(
"The maximum number of rows to return") );
2884 getScope->add_option(
"-L,--lower", lower,
localized(
"Lower bound of scope") );
2885 getScope->add_option(
"-U,--upper", upper,
localized(
"Upper bound of scope") );
2886 getScope->add_flag(
"-r,--reverse", reverse,
localized(
"Iterate in reverse order"));
2887 getScope->callback([&] {
2889 ("table",table)
2890 ("lower_bound",lower)
2891 ("upper_bound",upper)
2892 ("limit",limit)
2893 ("reverse", reverse)
2894 );
2896 << std::endl;
2897 });
2898
2899
2900
2902 bool currency_balance_print_json = false;
2903 auto get_currency = get->add_subcommand(
"currency",
localized(
"Retrieve information related to standard currencies"));
2904 get_currency->require_subcommand();
2905 auto get_balance = get_currency->add_subcommand(
"balance",
localized(
"Retrieve the balance of an account for a given currency"));
2906 get_balance->add_option(
"contract", code,
localized(
"The contract that operates the currency") )->required();
2907 get_balance->add_option(
"account", accountName,
localized(
"The account to query balances for") )->required();
2908 get_balance->add_option(
"symbol",
symbol,
localized(
"The symbol for the currency if the contract operates multiple currencies") );
2909 get_balance->add_flag(
"--json,-j", currency_balance_print_json,
localized(
"Output in JSON format") );
2910 get_balance->callback([&] {
2912 ("account", accountName)
2913 ("code", code)
2915 );
2916 if (!currency_balance_print_json) {
2917 const auto& rows = result.get_array();
2918 for(
const auto&
r : rows ) {
2920 }
2921 } else {
2923 }
2924 });
2925
2926 auto get_currency_stats = get_currency->add_subcommand(
"stats",
localized(
"Retrieve the stats of for a given currency"));
2927 get_currency_stats->add_option(
"contract", code,
localized(
"The contract that operates the currency") )->required();
2928 get_currency_stats->add_option(
"symbol",
symbol,
localized(
"The symbol for the currency if the contract operates multiple currencies") )->required();
2929 get_currency_stats->callback([&] {
2931 ("code", code)
2933 );
2934
2936 << std::endl;
2937 });
2938
2939
2940 string public_key_str;
2941 auto getAccounts = get->add_subcommand(
"accounts",
localized(
"Retrieve accounts associated with a public key"));
2942 getAccounts->add_option(
"public_key", public_key_str,
localized(
"The public key to retrieve accounts for"))->required();
2943 getAccounts->callback([&] {
2945 try {
2947 }
SYS_RETHROW_EXCEPTIONS(public_key_type_exception,
"Invalid public key: ${public_key}", (
"public_key", public_key_str))
2950 });
2951
2952
2953
2954 string controllingAccount;
2955 auto getServants = get->add_subcommand(
"servants",
localized(
"Retrieve accounts which are servants of a given account "));
2956 getServants->add_option(
"account", controllingAccount,
localized(
"The name of the controlling account"))->required();
2957 getServants->callback([&] {
2960 });
2961
2962
2963 string transaction_id_str;
2965 auto getTransaction = get->add_subcommand(
"transaction",
localized(
"Retrieve a transaction from the blockchain"));
2966 getTransaction->add_option(
"id", transaction_id_str,
localized(
"ID of the transaction to retrieve"))->required();
2967 getTransaction->add_option(
"-b,--block-hint", block_num_hint,
localized(
"The block number this transaction may be in") );
2968 getTransaction->callback([&] {
2970 if ( block_num_hint > 0 ) {
2971 arg = arg("block_num_hint", block_num_hint);
2972 }
2974 });
2975
2976
2977 auto getTransactionTrace = get->add_subcommand(
"transaction_trace",
localized(
"Retrieve a transaction from trace logs"));
2978 getTransactionTrace->add_option(
"id", transaction_id_str,
localized(
"ID of the transaction to retrieve"))->required();
2979 getTransactionTrace->callback([&] {
2982 });
2983
2984
2985 string blockNum;
2986 auto getBlockTrace = get->add_subcommand(
"block_trace",
localized(
"Retrieve a block from trace logs"));
2987 getBlockTrace->add_option(
"block", blockNum,
localized(
"The number of the block to retrieve"))->required();
2988
2989 getBlockTrace->callback([&] {
2992 });
2993
2994
2996 string skip_seq_str;
2997 string num_seq_str;
2998 bool printjson = false;
2999 bool fullact = false;
3000 bool prettyact = false;
3001 bool printconsole = false;
3002
3005 auto getActions = get->add_subcommand(
"actions",
localized(
"Retrieve all actions with specific account name referenced in authorization or receiver"));
3006 getActions->add_option(
"account_name",
account_name,
localized(
"Name of account to query on"))->required();
3007 getActions->add_option(
"pos", pos_seq,
localized(
"Sequence number of action for this account, -1 for last"));
3008 getActions->add_option(
"offset", offset,
localized(
"Get actions [pos,pos+offset] for positive offset or [pos-offset,pos) for negative offset"));
3009 getActions->add_flag(
"--json,-j", printjson,
localized(
"Print full JSON"));
3010 getActions->add_flag(
"--full", fullact,
localized(
"Don't truncate action output"));
3011 getActions->add_flag(
"--pretty", prettyact,
localized(
"Pretty print full action JSON"));
3012 getActions->add_flag(
"--console", printconsole,
localized(
"Print console output generated by action "));
3013 getActions->callback([&] {
3016 arg( "pos", pos_seq );
3017 arg( "offset", offset);
3018
3020
3021
3022 if( printjson ) {
3024 } else {
3025 auto& traces = result["actions"].get_array();
3026 uint32_t lib = result[
"last_irreversible_block"].as_uint64();
3027
3028
3029 cout << "#" << setw(5) << "seq" << " " << setw( 24 ) << left << "when"<< " " << setw(24) << right << "contract::action" << " => " << setw(13) << left << "receiver" << " " << setw(11) << left << "trx id..." << " args\n";
3030 cout << "================================================================================================================\n";
3031 for( const auto& trace: traces ) {
3032 std::stringstream out;
3033 if( trace["block_num"].as_uint64() <= lib )
3034 out << "#";
3035 else
3036 out << "?";
3037
3038 out << setw(5) << trace["account_action_seq"].as_uint64() <<" ";
3039 out << setw(24) << trace["block_time"].as_string() <<" ";
3040
3041 const auto& at = trace["action_trace"].get_object();
3042
3043 auto id = at["trx_id"].as_string();
3044 const auto& receipt = at["receipt"];
3045 auto receiver = receipt["receiver"].as_string();
3046 const auto& act = at["act"].get_object();
3047 auto code = act["account"].as_string();
3048 auto func = act[
"name"].as_string();
3049 string args;
3050 if( prettyact ) {
3052 }
3053 else {
3055 if( !fullact ) {
3056 args = args.substr(0,60) + "...";
3057 }
3058 }
3059 out << std::setw(24) << std::right<< (code +
"::" +
func) <<
" => " << left << std::setw(13) << receiver;
3060
3061 out << " " << setw(11) << (id.substr(0,8) + "...");
3062
3063 if( fullact || prettyact ) out << "\n";
3064 else out << " ";
3065
3066 out << args ;
3067
3068 if( trace["block_num"].as_uint64() <= lib ) {
3069 dlog(
"\r${m}", (
"m",out.str()) );
3070 } else {
3071 wlog(
"\r${m}", (
"m",out.str()) );
3072 }
3073 if( printconsole ) {
3074 auto console = at["console"].as_string();
3075 if( console.size() ) {
3076 stringstream sout;
3077 std::stringstream
ss(console);
3078 string line;
3079 while( std::getline( ss, line ) ) {
3080 sout <<
">> " <<
clean_output( std::move( line ) ) <<
"\n";
3081 if( !fullact ) break;
3082 line.clear();
3083 }
3085 }
3086 }
3087 }
3088 }
3089 });
3090
3093
3094
3095 auto setSubcommand =
app.add_subcommand(
"set",
localized(
"Set or update blockchain state"));
3096 setSubcommand->require_subcommand();
3097
3098
3099 string account;
3100 string contractPath;
3101 string wasmPath;
3102 string abiPath;
3103 bool shouldSend = true;
3104 bool contract_clear = false;
3105 bool suppress_duplicate_check = false;
3106 auto codeSubcommand = setSubcommand->add_subcommand(
"code",
localized(
"Create or update the code on an account"));
3107 codeSubcommand->add_option(
"account", account,
localized(
"The account to set code for"))->required();
3108 codeSubcommand->add_option(
"code-file", wasmPath,
localized(
"The path containing the contract WASM"));
3109 codeSubcommand->add_flag(
"-c,--clear", contract_clear,
localized(
"Remove code on an account"));
3110 codeSubcommand->add_flag(
"--suppress-duplicate-check", suppress_duplicate_check,
localized(
"Don't check for duplicate"));
3111
3112 auto abiSubcommand = setSubcommand->add_subcommand(
"abi",
localized(
"Create or update the abi on an account"));
3113 abiSubcommand->add_option(
"account", account,
localized(
"The account to set the ABI for"))->required();
3114 abiSubcommand->add_option(
"abi-file", abiPath,
localized(
"The path containing the contract ABI"));
3115 abiSubcommand->add_flag(
"-c,--clear", contract_clear,
localized(
"Remove abi on an account"));
3116 abiSubcommand->add_flag(
"--suppress-duplicate-check", suppress_duplicate_check,
localized(
"Don't check for duplicate"));
3117
3118 auto contractSubcommand = setSubcommand->add_subcommand(
"contract",
localized(
"Create or update the contract on an account"));
3119 contractSubcommand->add_option(
"account", account,
localized(
"The account to publish a contract for"))
3120 ->required();
3121 contractSubcommand->add_option(
"contract-dir", contractPath,
localized(
"The path containing the .wasm and .abi"));
3122
3123 contractSubcommand->add_option(
"wasm-file", wasmPath,
localized(
"The file containing the contract WASM relative to contract-dir"));
3124
3125 contractSubcommand->add_option(
"abi-file,-a,--abi", abiPath,
localized(
"The ABI for the contract relative to contract-dir"));
3126
3127 contractSubcommand->add_flag(
"-c,--clear", contract_clear,
localized(
"Remove contract on an account"));
3128 contractSubcommand->add_flag(
"--suppress-duplicate-check", suppress_duplicate_check,
localized(
"Don't check for duplicate"));
3129
3130 std::vector<chain::action> actions;
3131 auto set_code_callback = [&]() {
3132
3133 std::vector<char> old_wasm;
3136 if (!suppress_duplicate_check) {
3137 try {
3139 old_hash =
fc::sha256(result[
"code_hash"].as_string());
3140 } catch (...) {
3141 std::cerr << "Failed to get existing code hash, continue without duplicate check..." << std::endl;
3142 suppress_duplicate_check = true;
3143 }
3144 }
3145
3147 if(!contract_clear){
3148 std::string wasm;
3150
3151 if( wasmPath.empty() ) {
3152 wasmPath = (cpath / (cpath.
filename().generic_string()+
".wasm")).generic_string();
3153 } else if ( boost::filesystem::path(wasmPath).is_relative() ) {
3154 wasmPath = (cpath / wasmPath).generic_string();
3155 }
3156
3157 std::cerr <<
localized((
"Reading WASM from " + wasmPath +
"...").c_str()) << std::endl;
3159 SYS_ASSERT( !wasm.empty(), wasm_file_not_found,
"no wasm file found ${f}", (
"f", wasmPath) );
3160
3161 const string binary_wasm_header("\x00\x61\x73\x6d\x01\x00\x00\x00", 8);
3162 if(wasm.compare(0, 8, binary_wasm_header))
3163 std::cerr <<
localized(
"WARNING: ") << wasmPath <<
localized(
" doesn't look like a binary WASM file. Is it something else, like WAST? Trying anyway...") << std::endl;
3164 code_bytes =
bytes(wasm.begin(), wasm.end());
3165 } else {
3166 code_bytes =
bytes();
3167 }
3168
3169 if (!suppress_duplicate_check) {
3170 if (code_bytes.size()) {
3172 }
3174 }
3175
3178 if ( shouldSend ) {
3179 std::cerr <<
localized(
"Setting Code...") << std::endl;
3181 }
3182 } else {
3183 std::cerr <<
localized(
"Skipping set code because the new code is the same as the existing code") << std::endl;
3184 }
3185 };
3186
3187 auto set_abi_callback = [&]() {
3188
3191 if (!suppress_duplicate_check) {
3192 try {
3194 old_abi = result["abi"].as_blob().data;
3195 } catch (...) {
3196 std::cerr << "Failed to get existing raw abi, continue without duplicate check..." << std::endl;
3197 suppress_duplicate_check = true;
3198 }
3199 }
3200
3202 if(!contract_clear){
3204
3205 if( abiPath.empty() ) {
3206 abiPath = (cpath / (cpath.
filename().generic_string()+
".abi")).generic_string();
3207 } else if ( boost::filesystem::path(abiPath).is_relative() ) {
3208 abiPath = (cpath / abiPath).generic_string();
3209 }
3210
3211 SYS_ASSERT(
fc::exists( abiPath ), abi_file_not_found,
"no abi file found ${f}", (
"f", abiPath) );
3212
3214 } else {
3215 abi_bytes =
bytes();
3216 }
3217
3218 if (!suppress_duplicate_check) {
3219 duplicate = (old_abi.size() == abi_bytes.size() && std::equal(old_abi.begin(), old_abi.end(), abi_bytes.begin()));
3220 }
3221
3223 try {
3227 std::cerr <<
localized(
"Setting ABI...") << std::endl;
3229 }
3230 } else {
3231 std::cerr <<
localized(
"Skipping set abi because the new abi is the same as the existing abi") << std::endl;
3232 }
3233 };
3234
3238 contractSubcommand->callback([&] {
3239 if(!contract_clear)
SYS_ASSERT( !contractPath.empty(), contract_exception,
" contract-dir is null ", (
"f", contractPath) );
3240 shouldSend = false;
3241 set_code_callback();
3242 set_abi_callback();
3243 if (actions.size()) {
3244 std::cerr <<
localized(
"Publishing contract...") << std::endl;
3246 } else {
3247 std::cout << "no transaction is sent" << std::endl;
3248 }
3249 });
3250 codeSubcommand->callback(set_code_callback);
3251 abiSubcommand->callback(set_abi_callback);
3252
3253
3254 auto setAccount = setSubcommand->add_subcommand(
"account",
localized(
"Set or update blockchain account state"))->require_subcommand();
3255
3256
3258
3259
3260 auto setAction = setSubcommand->add_subcommand(
"action",
localized(
"Set or update blockchain action state"))->require_subcommand();
3261
3262
3264
3265
3266 string con = "sysio.token";
3267 string sender;
3268 string recipient;
3269 string amount;
3270 string memo;
3271 bool pay_ram = false;
3272 auto transfer =
app.add_subcommand(
"transfer",
localized(
"Transfer tokens from account to account"));
3273 transfer->add_option(
"sender", sender,
localized(
"The account sending tokens"))->required();
3274 transfer->add_option(
"recipient", recipient,
localized(
"The account receiving tokens"))->required();
3275 transfer->add_option(
"amount", amount,
localized(
"The amount of tokens to send"))->required();
3276 transfer->add_option(
"memo", memo,
localized(
"The memo for the transfer"));
3277 transfer->add_option(
"--contract,-c", con,
localized(
"The contract that controls the token"));
3278 transfer->add_flag(
"--pay-ram-to-open", pay_ram,
localized(
"Pay RAM to open recipient's token balance row"));
3279
3281 transfer->callback([&] {
3283
3286 }
3287
3290 if (!pay_ram) {
3292 } else {
3293 auto open_ =
create_open(con,
name(recipient), transfer_amount.get_symbol(),
name(sender));
3295 }
3296 });
3297
3298
3299 string new_host;
3300 auto net =
app.add_subcommand(
"net",
localized(
"Interact with local p2p network connections"));
3301 net->require_subcommand();
3302 auto connect = net->add_subcommand(
"connect",
localized(
"Start a new connection to a peer"));
3303 connect->add_option(
"host", new_host,
localized(
"The hostname:port to connect to."))->required();
3304 connect->callback([&] {
3307 });
3308
3309 auto disconnect = net->add_subcommand(
"disconnect",
localized(
"Close an existing connection"));
3310 disconnect->add_option(
"host", new_host,
localized(
"The hostname:port to disconnect from."))->required();
3311 disconnect->callback([&] {
3314 });
3315
3316 auto status = net->add_subcommand(
"status",
localized(
"Status of existing connection"));
3317 status->add_option(
"host", new_host,
localized(
"The hostname:port to query status of connection"))->required();
3318 status->callback([&] {
3321 });
3322
3323 auto connections = net->add_subcommand(
"peers",
localized(
"Status of all existing peers"));
3324 connections->callback([&] {
3327 });
3328
3329
3330
3331
3332 auto wallet =
app.add_subcommand(
"wallet",
localized(
"Interact with local wallet"));
3333 wallet->require_subcommand();
3334
3335 string wallet_name = "default";
3336 string password_file;
3337 auto createWallet = wallet->add_subcommand(
"create",
localized(
"Create a new wallet locally"));
3338 createWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the new wallet"),
true);
3339 createWallet->add_option(
"-f,--file", password_file,
localized(
"Name of file to write wallet password output to. (Must be set, unless \"--to-console\" is passed"));
3340 createWallet->add_flag(
"--to-console", print_console,
localized(
"Print password to console."));
3341 createWallet->callback([&wallet_name, &password_file, &print_console] {
3342 SYSC_ASSERT( !password_file.empty() ^ print_console,
"ERROR: Either indicate a file using \"--file\" or pass \"--to-console\"" );
3343 SYSC_ASSERT( password_file.empty() || !std::ofstream(password_file.c_str()).fail(),
"ERROR: Failed to create file in specified path" );
3344
3346 std::cout <<
localized(
"Creating wallet: ${wallet_name}", (
"wallet_name", wallet_name)) << std::endl;
3347 std::cout <<
localized(
"Save password to use in the future to unlock this wallet.") << std::endl;
3348 std::cout <<
localized(
"Without password imported keys will not be retrievable.") << std::endl;
3349 if (print_console) {
3351 } else {
3352 std::cerr <<
localized(
"saving password to ${filename}", (
"filename", password_file)) << std::endl;
3354 boost::replace_all(password_str, "\"", "");
3355 std::ofstream out( password_file.c_str() );
3356 out << password_str;
3357 }
3358 });
3359
3360
3361 auto openWallet = wallet->add_subcommand(
"open",
localized(
"Open an existing wallet"));
3362 openWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to open"));
3363 openWallet->callback([&wallet_name] {
3365 std::cout <<
localized(
"Opened: ${wallet_name}", (
"wallet_name", wallet_name)) << std::endl;
3366 });
3367
3368
3369 auto lockWallet = wallet->add_subcommand(
"lock",
localized(
"Lock wallet"));
3370 lockWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to lock"));
3371 lockWallet->callback([&wallet_name] {
3373 std::cout <<
localized(
"Locked: ${wallet_name}", (
"wallet_name", wallet_name)) << std::endl;
3374 });
3375
3376
3377 auto locakAllWallets = wallet->add_subcommand(
"lock_all",
localized(
"Lock all unlocked wallets"));
3378 locakAllWallets->callback([] {
3380 std::cout <<
localized(
"Locked All Wallets") << std::endl;
3381 });
3382
3383
3384 string wallet_pw;
3385 auto unlockWallet = wallet->add_subcommand(
"unlock",
localized(
"Unlock wallet"));
3386 unlockWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to unlock"));
3387 unlockWallet->add_option(
"--password", wallet_pw,
localized(
"The password returned by wallet create"))->expected(0, 1);
3388 unlockWallet->callback([&wallet_name, &wallet_pw] {
3390
3393 std::cout <<
localized(
"Unlocked: ${wallet_name}", (
"wallet_name", wallet_name)) << std::endl;
3394 });
3395
3396
3397 string wallet_key_str;
3398 auto importWallet = wallet->add_subcommand(
"import",
localized(
"Import private key into wallet"));
3399 importWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to import key into"));
3400 importWallet->add_option(
"--private-key", wallet_key_str,
localized(
"Private key in WIF format to import"))->expected(0, 1);
3401 importWallet->callback([&wallet_name, &wallet_key_str] {
3402 if( wallet_key_str.size() == 0 ) {
3403 std::cout <<
localized(
"private key: ");
3405 std::getline( std::cin, wallet_key_str, '\n' );
3407 }
3408
3410 try {
3412 } catch (...) {
3413 SYS_THROW(private_key_type_exception,
"Invalid private key")
3414 }
3416
3419 std::cout <<
localized(
"imported private key for: ${pubkey}", (
"pubkey",
pubkey.to_string())) << std::endl;
3420 });
3421
3422
3423 string wallet_rm_key_str;
3424 auto removeKeyWallet = wallet->add_subcommand(
"remove_key",
localized(
"Remove key from wallet"));
3425 removeKeyWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to remove key from"));
3426 removeKeyWallet->add_option(
"key", wallet_rm_key_str,
localized(
"Public key in WIF format to remove"))->required();
3427 removeKeyWallet->add_option(
"--password", wallet_pw,
localized(
"The password returned by wallet create"))->expected(0, 1);
3428 removeKeyWallet->callback([&wallet_name, &wallet_pw, &wallet_rm_key_str] {
3431 try {
3433 } catch (...) {
3434 SYS_THROW(public_key_type_exception,
"Invalid public key: ${public_key}", (
"public_key", wallet_rm_key_str))
3435 }
3438 std::cout <<
localized(
"removed private key for: ${pubkey}", (
"pubkey", wallet_rm_key_str)) << std::endl;
3439 });
3440
3441
3442 string wallet_create_key_type;
3443 auto createKeyInWallet = wallet->add_subcommand(
"create_key",
localized(
"Create private key within wallet"));
3444 createKeyInWallet->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to create key into"),
true);
3445 createKeyInWallet->add_option(
"key_type", wallet_create_key_type,
localized(
"Key type to create (K1/R1)"),
true)->type_name(
"K1/R1");
3446 createKeyInWallet->callback([&wallet_name, &wallet_create_key_type] {
3447
3451 });
3452
3453
3454 auto listWallet = wallet->add_subcommand(
"list",
localized(
"List opened wallets, * = unlocked"));
3455 listWallet->callback([] {
3456 std::cout <<
localized(
"Wallets:") << std::endl;
3459 });
3460
3461
3462 auto listKeys = wallet->add_subcommand(
"keys",
localized(
"List of public keys from all unlocked wallets."));
3463 listKeys->callback([] {
3466 });
3467
3468
3469 auto listPrivKeys = wallet->add_subcommand(
"private_keys",
localized(
"List of private keys from an unlocked wallet in wif or PVT_R1 format."));
3470 listPrivKeys->add_option(
"-n,--name", wallet_name,
localized(
"The name of the wallet to list keys from"),
true);
3471 listPrivKeys->add_option(
"--password", wallet_pw,
localized(
"The password returned by wallet create"))->expected(0, 1);
3472 listPrivKeys->callback([&wallet_name, &wallet_pw] {
3477 });
3478
3479 auto stopKiod = wallet->add_subcommand(
"stop",
localized(
"Stop ${k}.", (
"k", key_store_executable_name)));
3480 stopKiod->callback([] {
3482 if ( !v.is_object() || v.get_object().size() != 0 ) {
3484 } else {
3485 std::cout << "OK" << std::endl;
3486 }
3487 });
3488
3489
3490 string trx_json_to_sign;
3491 string str_private_key;
3492 string str_chain_id;
3493 string str_private_key_file;
3494 string str_public_key;
3495 bool push_trx = false;
3496
3497 auto sign =
app.add_subcommand(
"sign",
localized(
"Sign a transaction"));
3498 sign->add_option("transaction", trx_json_to_sign,
3499 localized(
"The JSON string or filename defining the transaction to sign"),
true)->required();
3500 sign->add_option(
"-k,--private-key", str_private_key,
localized(
"The private key that will be used to sign the transaction"))->expected(0, 1);
3501 sign->add_option(
"--public-key", str_public_key,
localized(
"Ask ${exec} to sign with the corresponding private key of the given public key", (
"exec", key_store_executable_name)));
3502 sign->add_option(
"-c,--chain-id", str_chain_id,
localized(
"The chain id that will be used to sign the transaction"));
3503 sign->add_flag(
"-p,--push-transaction", push_trx,
localized(
"Push transaction after signing"));
3504
3505 sign->callback([&] {
3506
3507 SYSC_ASSERT( str_private_key.empty() || str_public_key.empty(),
"ERROR: Either -k/--private-key or --public-key or none of them can be set" );
3509
3510
3511 bool was_packed_trx = false;
3516 try {
3521 trx_var = strx;
3522 was_packed_trx = true;
3523 }
3524 }
3525
3531
3533
3534 if( str_chain_id.size() == 0 ) {
3535 ilog(
"grabbing chain_id from ${n}", (
"n", node_executable_name) );
3537 chain_id = info.chain_id;
3538 } else {
3540 }
3541
3542 if( str_public_key.size() > 0 ) {
3544 try {
3546 }
SYS_RETHROW_EXCEPTIONS(public_key_type_exception,
"Invalid public key: ${public_key}", (
"public_key", str_public_key))
3547 fc::variant keys_var(flat_set<public_key_type>{ pub_key });
3549 } else {
3550 if( str_private_key.size() == 0 ) {
3551 std::cerr <<
localized(
"private key: ");
3553 std::getline( std::cin, str_private_key, '\n' );
3555 }
3557 try {
3560 trx.sign(priv_key, *chain_id);
3561 }
3562
3566 } else {
3567 if ( was_packed_trx ) {
3569 } else {
3571 }
3572 }
3573 });
3574
3575
3576 auto push =
app.add_subcommand(
"push",
localized(
"Push arbitrary transactions to the blockchain"));
3577 push->require_subcommand();
3578
3579
3580 string contract_account;
3582 string data;
3584 auto actionsSubcommand = push->add_subcommand(
"action",
localized(
"Push a transaction with a single action"));
3585 actionsSubcommand->fallthrough(false);
3586 actionsSubcommand->add_option("account", contract_account,
3587 localized(
"The account providing the contract to execute"),
true)->required();
3588 actionsSubcommand->add_option(
"action",
action,
3589 localized(
"A JSON string or filename defining the action to execute on the contract"),
true)->required();
3590 actionsSubcommand->add_option(
"data", data,
localized(
"The arguments to the contract"))->required();
3591
3593 actionsSubcommand->callback([&] {
3595 if( !data.empty() ) {
3597 }
3599
3602 });
3603
3604
3605 string trx_to_push;
3606 auto trxSubcommand = push->add_subcommand(
"transaction",
localized(
"Push an arbitrary JSON transaction"));
3607 trxSubcommand->add_option(
"transaction", trx_to_push,
localized(
"The JSON string or filename defining the transaction to push"))->required();
3609 trxSubcommand->add_flag(
"-o,--read-only",
tx_read_only,
localized(
"Specify a transaction is read-only"));
3610
3611 trxSubcommand->callback([&] {
3613 try {
3616 } catch( const std::exception& ) {
3617
3621 }
3622 });
3623
3624
3625 string trxsJson;
3626 auto trxsSubcommand = push->add_subcommand(
"transactions",
localized(
"Push an array of arbitrary JSON transactions"));
3627 trxsSubcommand->add_option(
"transactions", trxsJson,
localized(
"The JSON string or filename defining the array of the transactions to push"))->required();
3628 trxsSubcommand->callback([&] {
3632 });
3633
3634
3635
3636 auto msig =
app.add_subcommand(
"multisig",
localized(
"Multisig contract commands"));
3637 msig->require_subcommand();
3638
3639
3640 string proposal_name;
3641 string requested_perm;
3642 string transaction_perm;
3643 string proposed_transaction;
3644 string proposed_contract;
3645 string proposed_action;
3646 string proposer;
3647 unsigned int proposal_expiration_hours = 24;
3649 unsigned int value_s;
3650 if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
3651 return false;
3652 }
3653
3654 proposal_expiration_hours =
static_cast<uint64_t>(value_s);
3655 return true;
3656 };
3657
3658 auto propose_action = msig->add_subcommand(
"propose",
localized(
"Propose action"));
3660 propose_action->add_option(
"proposal_name", proposal_name,
localized(
"The proposal name (string)"))->required();
3661 propose_action->add_option(
"requested_permissions", requested_perm,
localized(
"The JSON string or filename defining requested permissions"))->required();
3662 propose_action->add_option(
"trx_permissions", transaction_perm,
localized(
"The JSON string or filename defining transaction permissions"))->required();
3663 propose_action->add_option(
"contract", proposed_contract,
localized(
"The contract to which deferred transaction should be delivered"))->required();
3664 propose_action->add_option(
"action", proposed_action,
localized(
"The action of deferred transaction"))->required();
3665 propose_action->add_option(
"data", proposed_transaction,
localized(
"The JSON string or filename defining the action to propose"))->required();
3666 propose_action->add_option(
"proposer", proposer,
localized(
"Account proposing the transaction"));
3667 propose_action->add_option(
"proposal_expiration", parse_expiration_hours,
localized(
"Proposal expiration interval in hours"));
3668
3669 propose_action->callback([&] {
3674 try {
3679
3683 }
SYS_RETHROW_EXCEPTIONS(transaction_type_exception,
"Wrong requested permissions format: '${data}'", (
"data",requested_perm_var));
3684
3686 try {
3688 }
SYS_RETHROW_EXCEPTIONS(transaction_type_exception,
"Wrong transaction permissions format: '${data}'", (
"data",transaction_perm_var));
3689
3691 if (accountPermissions.empty()) {
3692 if (!proposer.empty()) {
3694 } else {
3695 SYS_THROW(missing_auth_exception,
"Authority is not provided (either by multisig parameter <proposer> or -p)");
3696 }
3697 }
3698 if (proposer.empty()) {
3699 proposer =
name(accountPermissions.at(0).actor).to_string();
3700 }
3701
3703
3710 trx.
actions = { chain::action(trxperm,
name(proposed_contract),
name(proposed_action), proposed_trx_serialized) };
3711
3713
3715 ("proposer", proposer )
3716 ("proposal_name", proposal_name)
3717 ("requested", requested_perm_var)
3718 ("trx", trx_var);
3719
3720 send_actions({chain::action{accountPermissions,
"sysio.msig"_n,
"propose"_n,
variant_to_bin(
"sysio.msig"_n,
"propose"_n, args ) }});
3721 });
3722
3723
3724 auto propose_trx = msig->add_subcommand(
"propose_trx",
localized(
"Propose transaction"));
3726 propose_trx->add_option(
"proposal_name", proposal_name,
localized(
"The proposal name (string)"))->required();
3727 propose_trx->add_option(
"requested_permissions", requested_perm,
localized(
"The JSON string or filename defining requested permissions"))->required();
3728 propose_trx->add_option(
"transaction", trx_to_push,
localized(
"The JSON string or filename defining the transaction to push"))->required();
3729 propose_trx->add_option(
"proposer", proposer,
localized(
"Account proposing the transaction"));
3730
3731 propose_trx->callback([&] {
3734
3736 if (accountPermissions.empty()) {
3737 if (!proposer.empty()) {
3739 } else {
3740 SYS_THROW(missing_auth_exception,
"Authority is not provided (either by multisig parameter <proposer> or -p)");
3741 }
3742 }
3743 if (proposer.empty()) {
3744 proposer =
name(accountPermissions.at(0).actor).to_string();
3745 }
3746
3748 ("proposer", proposer )
3749 ("proposal_name", proposal_name)
3750 ("requested", requested_perm_var)
3751 ("trx", trx_var);
3752
3753 send_actions({chain::action{accountPermissions,
"sysio.msig"_n,
"propose"_n,
variant_to_bin(
"sysio.msig"_n,
"propose"_n, args ) }});
3754 });
3755
3756
3757
3758 bool show_approvals_in_multisig_review = false;
3759 auto review = msig->add_subcommand(
"review",
localized(
"Review transaction"));
3760 review->add_option(
"proposer", proposer,
localized(
"The proposer name (string)"))->required();
3761 review->add_option(
"proposal_name", proposal_name,
localized(
"The proposal name (string)"))->required();
3762 review->add_flag(
"--show-approvals", show_approvals_in_multisig_review,
localized(
"Show the status of the approvals requested within the proposal") );
3763
3764 review->callback([&] {
3766 ("code", "sysio.msig")
3767 ("scope", proposer)
3768 ("table", "proposal")
3769 ("table_key", "")
3770 (
"lower_bound",
name(proposal_name).to_uint64_t())
3771 (
"upper_bound",
name(proposal_name).to_uint64_t() + 1)
3772
3773
3774 ("limit", 1)
3775 );
3776
3777
3778 const auto& rows1 = result1.get_object()["rows"].get_array();
3779
3780 if( rows1.empty() || rows1[0].get_object()["proposal_name"] != proposal_name ) {
3781 std::cerr << "Proposal not found" << std::endl;
3782 return;
3783 }
3784
3785 const auto& proposal_object = rows1[0].get_object();
3786
3787 enum class approval_status {
3788 unapproved,
3789 approved,
3790 invalidated
3791 };
3792
3793 std::map<permission_level, std::pair<fc::time_point, approval_status>> all_approvals;
3795
3796 bool new_multisig = true;
3797 if( show_approvals_in_multisig_review ) {
3799
3800 try {
3802 ("code", "sysio.msig")
3803 ("scope", proposer)
3804 ("table", "approvals2")
3805 ("table_key", "")
3806 (
"lower_bound",
name(proposal_name).to_uint64_t())
3807 (
"upper_bound",
name(proposal_name).to_uint64_t() + 1)
3808
3809
3810 ("limit", 1)
3811 );
3812 rows2 = result2.get_object()["rows"].get_array();
3813 } catch( ... ) {
3814 new_multisig = false;
3815 }
3816
3817 if( !rows2.empty() && rows2[0].get_object()["proposal_name"] == proposal_name ) {
3818 const auto& approvals_object = rows2[0].get_object();
3819
3820 for( const auto& ra : approvals_object["requested_approvals"].get_array() ) {
3822 all_approvals.emplace( pl, std::make_pair(ra["time"].as<fc::time_point>(), approval_status::unapproved) );
3823 }
3824
3825 for( const auto& pa : approvals_object["provided_approvals"].get_array() ) {
3827 auto res = all_approvals.emplace( pl, std::make_pair(pa["time"].as<fc::time_point>(), approval_status::approved) );
3828 provided_approvers[pl.actor].second.push_back( res.first );
3829 }
3830 } else {
3832 ("code", "sysio.msig")
3833 ("scope", proposer)
3834 ("table", "approvals")
3835 ("table_key", "")
3836 (
"lower_bound",
name(proposal_name).to_uint64_t())
3837 (
"upper_bound",
name(proposal_name).to_uint64_t() + 1)
3838
3839
3840 ("limit", 1)
3841 );
3842 const auto& rows3 = result3.get_object()["rows"].get_array();
3843 if( rows3.empty() || rows3[0].get_object()["proposal_name"] != proposal_name ) {
3844 std::cerr << "Proposal not found" << std::endl;
3845 return;
3846 }
3847
3848 const auto& approvals_object = rows3[0].get_object();
3849
3850 for( const auto& ra : approvals_object["requested_approvals"].get_array() ) {
3852 all_approvals.emplace( pl, std::make_pair(
fc::time_point{}, approval_status::unapproved) );
3853 }
3854
3855 for( const auto& pa : approvals_object["provided_approvals"].get_array() ) {
3857 auto res = all_approvals.emplace( pl, std::make_pair(
fc::time_point{}, approval_status::approved) );
3858 provided_approvers[pl.actor].second.push_back( res.first );
3859 }
3860 }
3861
3862 if( new_multisig ) {
3863 for(
auto&
a : provided_approvers ) {
3865 ("code", "sysio.msig")
3866 ("scope", "sysio.msig")
3867 ("table", "invals")
3868 ("table_key", "")
3869 (
"lower_bound",
a.first.to_uint64_t())
3870 (
"upper_bound",
a.first.to_uint64_t() + 1)
3871
3872
3873 ("limit", 1)
3874 );
3875 const auto& rows4 = result4.get_object()["rows"].get_array();
3876 if( rows4.empty() || rows4[0].get_object()[
"account"].as<
sysio::name>() !=
a.first ) {
3877 continue;
3878 }
3879
3880 auto invalidation_time = rows4[0].get_object()[
"last_invalidation_time"].as<
fc::time_point>();
3881 a.second.first = invalidation_time;
3882
3883 for(
auto& itr :
a.second.second ) {
3884 if( invalidation_time >= itr->second.first ) {
3885 itr->second.second = approval_status::invalidated;
3886 }
3887 }
3888 }
3889 }
3890 }
3891
3892 auto trx_hex = proposal_object["packed_transaction"].as_string();
3894 fc::from_hex(trx_hex, trx_blob.data(), trx_blob.size());
3896
3898 obj["proposer"] = proposer;
3899 obj["proposal_name"] = proposal_object["proposal_name"];
3900 obj[
"transaction_id"] = trx.
id();
3901
3902 for( const auto& entry : proposal_object ) {
3903 if( entry.key() == "proposal_name" ) continue;
3904 obj.
set( entry.key(), entry.value() );
3905 }
3906
3910 obj["transaction"] = trx_var;
3911
3912 if( show_approvals_in_multisig_review ) {
3914
3915 for( const auto& approval : all_approvals ) {
3917 approval_obj["level"] = approval.first;
3918 switch( approval.second.second ) {
3919 case approval_status::unapproved:
3920 {
3921 approval_obj["status"] = "unapproved";
3923 approval_obj["last_unapproval_time"] = approval.second.first;
3924 }
3925 }
3926 break;
3927 case approval_status::approved:
3928 {
3929 approval_obj["status"] = "approved";
3930 if( new_multisig ) {
3931 approval_obj["last_approval_time"] = approval.second.first;
3932 }
3933 }
3934 break;
3935 case approval_status::invalidated:
3936 {
3937 approval_obj["status"] = "invalidated";
3938 approval_obj["last_approval_time"] = approval.second.first;
3939 approval_obj["invalidation_time"] = provided_approvers[approval.first.actor].first;
3940 }
3941 break;
3942 }
3943
3944 approvals.push_back( std::move(approval_obj) );
3945 }
3946
3947 obj["approvals"] = std::move(approvals);
3948 }
3949
3951 });
3952
3953 string perm;
3954 string proposal_hash;
3955 auto approve_or_unapprove = [&](
const string&
action) {
3957
3959 ("proposer", proposer)
3960 ("proposal_name", proposal_name)
3961 ("level", perm_var);
3962
3963 if( proposal_hash.size() ) {
3964 args("proposal_hash", proposal_hash);
3965 }
3966
3969 };
3970
3971
3972 auto approve = msig->add_subcommand(
"approve",
localized(
"Approve proposed transaction"));
3974 approve->add_option(
"proposer", proposer,
localized(
"The proposer name (string)"))->required();
3975 approve->add_option(
"proposal_name", proposal_name,
localized(
"The proposal name (string)"))->required();
3976 approve->add_option(
"permissions", perm,
localized(
"The JSON string of filename defining approving permissions"))->required();
3977 approve->add_option(
"proposal_hash", proposal_hash,
localized(
"Hash of proposed transaction (i.e. transaction ID) to optionally enforce as a condition of the approval"));
3978 approve->callback([&] { approve_or_unapprove("approve"); });
3979
3980
3981 auto unapprove = msig->add_subcommand(
"unapprove",
localized(
"Unapprove proposed transaction"));
3983 unapprove->add_option(
"proposer", proposer,
localized(
"The proposer name (string)"))->required();
3984 unapprove->add_option(
"proposal_name", proposal_name,
localized(
"The proposal name (string)"))->required();
3985 unapprove->add_option(
"permissions", perm,
localized(
"The JSON string of filename defining approving permissions"))->required();
3986 unapprove->callback([&] { approve_or_unapprove("unapprove"); });
3987
3988
3989 string invalidator;
3990 auto invalidate = msig->add_subcommand(
"invalidate",
localized(
"Invalidate all multisig approvals of an account"));
3992 invalidate->add_option(
"invalidator", invalidator,
localized(
"invalidator name (string)"))->required();
3993 invalidate->callback([&] {
3995 ("account", invalidator);
3996
3998 send_actions({chain::action{accountPermissions,
"sysio.msig"_n,
"invalidate"_n,
variant_to_bin(
"sysio.msig"_n,
"invalidate"_n, args ) }});
3999 });
4000
4001
4002 string canceler;
4003 auto cancel = msig->add_subcommand(
"cancel",
localized(
"Cancel proposed transaction"));
4005 cancel->add_option(
"proposer", proposer,
localized(
"The proposer name (string)"))->required();
4006 cancel->add_option(
"proposal_name", proposal_name,
localized(
"proposal name (string)"))->required();
4007 cancel->add_option(
"canceler", canceler,
localized(
"The canceler name (string)"));
4008 cancel->callback([&]() {
4010 if (accountPermissions.empty()) {
4011 if (!canceler.empty()) {
4013 } else {
4014 SYS_THROW(missing_auth_exception,
"Authority is not provided (either by multisig parameter <canceler> or -p)");
4015 }
4016 }
4017 if (canceler.empty()) {
4018 canceler =
name(accountPermissions.at(0).actor).to_string();
4019 }
4021 ("proposer", proposer)
4022 ("proposal_name", proposal_name)
4023 ("canceler", canceler);
4024
4025 send_actions({chain::action{accountPermissions,
"sysio.msig"_n,
"cancel"_n,
variant_to_bin(
"sysio.msig"_n,
"cancel"_n, args ) }});
4026 }
4027 );
4028
4029
4030 string executer;
4031 auto exec = msig->add_subcommand(
"exec",
localized(
"Execute proposed transaction"));
4033 exec->add_option(
"proposer", proposer,
localized(
"The proposer name (string)"))->required();
4034 exec->add_option(
"proposal_name", proposal_name,
localized(
"The proposal name (string)"))->required();
4035 exec->add_option(
"executer", executer,
localized(
"The account paying for execution (string)"));
4036 exec->callback([&] {
4038 if (accountPermissions.empty()) {
4039 if (!executer.empty()) {
4041 } else {
4042 SYS_THROW(missing_auth_exception,
"Authority is not provided (either by multisig parameter <executer> or -p)");
4043 }
4044 }
4045 if (executer.empty()) {
4046 executer =
name(accountPermissions.at(0).actor).to_string();
4047 }
4048
4050 ("proposer", proposer )
4051 ("proposal_name", proposal_name)
4052 ("executer", executer);
4053
4054 send_actions({chain::action{accountPermissions,
"sysio.msig"_n,
"exec"_n,
variant_to_bin(
"sysio.msig"_n,
"exec"_n, args ) }});
4055 }
4056 );
4057
4058
4059 auto wrap =
app.add_subcommand(
"wrap",
localized(
"Wrap contract commands"));
4060 wrap->require_subcommand();
4061
4062
4063 string wrap_con = "sysio.wrap";
4064 executer = "";
4065 string trx_to_exec;
4066 auto wrap_exec =
wrap->add_subcommand(
"exec",
localized(
"Execute a transaction while bypassing authorization checks"));
4068 wrap_exec->add_option(
"executer", executer,
localized(
"Account executing the transaction and paying for the deferred transaction RAM"))->required();
4069 wrap_exec->add_option(
"transaction", trx_to_exec,
localized(
"The JSON string or filename defining the transaction to execute"))->required();
4070 wrap_exec->add_option(
"--contract,-c", wrap_con,
localized(
"The account which controls the wrap contract"));
4071
4072 wrap_exec->callback([&] {
4074
4076 if( accountPermissions.empty() ) {
4078 }
4079
4081 ("executer", executer )
4082 ("trx", trx_var);
4083
4085 });
4086
4087
4088 auto system =
app.add_subcommand(
"system",
localized(
"Send sysio.system contract action to the blockchain."));
4089 system->require_subcommand();
4090
4094
4095 auto voteProducer = system->add_subcommand(
"voteproducer",
localized(
"Vote for a producer"));
4096 voteProducer->require_subcommand();
4101
4103
4109
4112
4114
4117
4119
4120 auto rex = system->add_subcommand(
"rex",
localized(
"Actions related to REX (the resource exchange)"));
4121 rex->require_subcommand();
4141
4142 auto handle_error = [&](const auto& e)
4143 {
4144
4146
4148 elog(
"Failed with error: ${e}", (
"e", verbose ? e.to_detail_string() : e.to_string()));
4149 }
4150 }
4151 return 1;
4152 };
4153
4154 try {
4158 } catch (const explained_exception& e) {
4159 return 1;
4160 } catch (connection_exception& e) {
4161 if (verbose) {
4162 elog(
"connect error: ${e}", (
"e", e.to_detail_string()));
4163 }
4164 return 1;
4165 } catch ( const std::bad_alloc& ) {
4167 return 1;
4168 } catch( const boost::interprocess::bad_alloc& ) {
4170 return 1;
4172 return handle_error(e);
4173 } catch (const std::exception& e) {
4175 }
4176
4178}
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Creates a command line program, with very few defaults.
Anything that can error in Parse.
public_key get_public_key() const
static private_key generate_r1()
contains only the public point of an elliptic curve key.
static string to_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
logger & set_log_level(log_level e)
static logger get(const fc::string &name=DEFAULT_LOGGER)
mutable_variant_object & set(string key, variant var) &
fc::path filename() const
static sha256 hash(const char *d, uint32_t dlen)
static std_exception_wrapper from_current_exception(const std::exception &e)
static constexpr time_point maximum()
An order-preserving dictionary of variants.
bool contains(const char *key) const
variant_object & get_object()
auto generate(SourceLineInfo const &lineInfo, L const &generatorExpression) -> decltype(std::declval< decltype(generatorExpression())>().get())
void unpack(Stream &s, std::deque< T > &value)
void read_file_contents(const fc::path &filename, std::string &result)
void set_console_echo(bool enable_echo)
bool exists(const path &p)
std::string to_pretty_string(int64_t)
constexpr microseconds hours(int64_t h)
std::vector< fc::variant > variants
fc::string to_hex(const char *d, uint32_t s)
path canonical(const path &p)
void from_variant(const fc::variant &v, sysio::chain::chain_id_type &cid)
void to_variant(const sysio::chain::shared_public_key &var, fc::variant &vo)
int64_t to_int64(const fc::string &)
fc::crypto::public_key public_key_type
fc::crypto::private_key private_key_type
bool print_recognized_errors(const fc::exception &e, const bool verbose_errors)
bool print_help_text(const fc::exception &e)
const string get_raw_code_and_abi_func
const string get_controlled_accounts_func
const string get_transaction_status_func
const string get_key_accounts_func
const string get_currency_stats_func
const string get_code_hash_func
const string wallet_remove_key
const string get_block_trace_func
const string get_code_func
const string net_connections
const string get_abi_func
const string get_actions_func
const string wallet_create_key
const string wallet_import_key
const string get_table_func
const string get_currency_balance_func
http_context create_http_context()
const string push_txn_func
const string get_block_header_state_func
const string get_transaction_func
const string wallet_unlock
const string get_transaction_trace_func
const string wallet_list_keys
const string net_disconnect
const string push_txns_func
const string get_block_info_func
const string get_table_by_scope_func
const string get_raw_abi_func
const string wallet_create
const string wallet_lock_all
const string get_block_func
const std::string & version_client()
< Grab the basic version information of the client; example: v1.8.0-rc1
const std::string & version_full()
@ duplicate
the connection is redundant
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
void get_account(const string &accountName, const string &coresym, bool json_format)
#define SYSC_ASSERT(TEST,...)
void sign_transaction(signed_transaction &trx, fc::variant &required_keys, const chain_id_type &chain_id)
CLI::callback_t header_opt_callback
void prompt_for_wallet_password(string &pw, const string &name)
chain::action create_open(const string &contract, const name &owner, symbol sym, const name &ram_payer)
sysio::chain_apis::read_only::get_info_results get_info()
chain::action create_transfer(const string &contract, const name &sender, const name &recipient, asset amount, const string &memo)
CLI::callback_t obsoleted_option_host_port
chain::action create_setcode(const name &account, const bytes &code)
void add_standard_transaction_options(CLI::App *cmd, string default_permission="")
void ensure_kiod_running(CLI::App *app)
void send_actions(std::vector< chain::action > &&actions, packed_transaction::compression_type compression=packed_transaction::compression_type::none)
std::string clean_output(std::string str)
chain::action create_setabi(const name &account, const bytes &abi)
fc::variant bin_to_variant(const account_name &account, const action_name &action, const bytes &action_args)
string generate_nonce_string()
fc::variant push_transaction(signed_transaction &trx, packed_transaction::compression_type compression=packed_transaction::compression_type::none)
static bool to_abi(const Vec &abi_vec, abi_def &abi)
static void to_variant(const T &o, fc::variant &vo, Resolver resolver, const yield_function_t &yield)
namespace sysio::chain::impl
static void from_variant(const fc::variant &v, T &o, Resolver resolver, const yield_function_t &yield)
transaction_id_type id() const