Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
67#include <pwd.h>
68#include <string>
69#include <vector>
70#include <regex>
71#include <iostream>
72#include <fc/crypto/hex.hpp>
73#include <fc/variant.hpp>
74#include <fc/io/datastream.hpp>
75#include <fc/io/json.hpp>
76#include <fc/io/console.hpp>
78#include <fc/variant_object.hpp>
79
80#include <sysio/chain/name.hpp>
82#include <sysio/chain/trace.hpp>
85
87
88#pragma push_macro("N")
89#undef N
90
91#include <boost/asio.hpp>
92#include <boost/format.hpp>
93#include <boost/dll/runtime_symbol_info.hpp>
94#include <boost/filesystem.hpp>
95#include <boost/process.hpp>
96#include <boost/process/spawn.hpp>
97#include <boost/range/algorithm/find_if.hpp>
98#include <boost/range/algorithm/sort.hpp>
99#include <boost/range/adaptor/transformed.hpp>
100#include <boost/algorithm/string/predicate.hpp>
101#include <boost/algorithm/string/split.hpp>
102#include <boost/range/algorithm/copy.hpp>
103#include <boost/algorithm/string/classification.hpp>
104
105#pragma pop_macro("N")
106
107#include <Inline/BasicTypes.h>
108#include <IR/Module.h>
109#include <IR/Validate.h>
110#include <WASM/WASM.h>
111#include <Runtime/Runtime.h>
112
113#include <fc/io/fstream.hpp>
114
115#define CLI11_HAS_FILESYSTEM 0
116#include "CLI11.hpp"
117#include "help_text.hpp"
118#include "localize.hpp"
119#include "config.hpp"
120#include "httpc.hpp"
121
122using namespace std;
123using namespace sysio;
124using namespace sysio::chain;
125using namespace sysio::client::help;
126using namespace sysio::client::http;
127using namespace sysio::client::localize;
128using namespace sysio::client::config;
129using namespace boost::filesystem;
130using auth_type = std::variant<public_key_type, permission_level>;
131
132FC_DECLARE_EXCEPTION( explained_exception, 9000000, "explained exception, see error log" );
133FC_DECLARE_EXCEPTION( localized_exception, 10000000, "an error occured" );
134#define SYSC_ASSERT( TEST, ... ) \
135 FC_EXPAND_MACRO( \
136 FC_MULTILINE_MACRO_BEGIN \
137 if( UNLIKELY(!(TEST)) ) \
138 { \
139 std::cerr << localized( __VA_ARGS__ ) << std::endl; \
140 FC_THROW_EXCEPTION( explained_exception, #TEST ); \
141 } \
142 FC_MULTILINE_MACRO_END \
143 )
144
145//copy pasta from kiod's main.cpp
147{
148 bfs::path home;
149 struct passwd* pwd = getpwuid(getuid());
150 if(pwd) {
151 home = pwd->pw_dir;
152 }
153 else {
154 home = getenv("HOME");
155 }
156 if(home.empty())
157 home = "./";
158 return home;
159}
160
161std::string clean_output( std::string str ) {
162 const bool escape_control_chars = false;
163 return fc::escape_string( str, nullptr, escape_control_chars );
164}
165
166string url = "http://127.0.0.1:8888/";
167string default_wallet_url = "unix://" + (determine_home_directory() / "sysio-wallet" / (string(key_store_executable_name) + ".sock")).string();
168string wallet_url; //to be set to default_wallet_url in main
169bool no_verify = false;
171
173const fc::microseconds abi_serializer_max_time = fc::seconds(10); // No risk to client side serialization taking a long time
175bool tx_force_unique = false;
176bool tx_dont_broadcast = false;
177bool tx_return_packed = false;
178bool tx_skip_sign = false;
179bool tx_print_json = false;
181bool tx_read_only = false;
182bool tx_retry_lib = false;
184bool tx_use_old_rpc = false;
187bool print_request = false;
188bool print_response = false;
189bool no_auto_kiod = false;
190bool verbose = false;
192
195
197
199
201
202void add_standard_transaction_options(CLI::App* cmd, string default_permission = "") {
203 CLI::callback_t parse_expiration = [](CLI::results_t res) -> bool {
204 double value_s;
205 if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
206 return false;
207 }
208
209 tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
210 return true;
211 };
212
213 cmd->add_option("-x,--expiration", parse_expiration, localized("Set the time in seconds before a transaction expires, defaults to 30s"));
214 cmd->add_flag("-f,--force-unique", tx_force_unique, localized("Force the transaction to be unique. this will consume extra bandwidth and remove any protections against accidently issuing the same transaction multiple times"));
215 cmd->add_flag("-s,--skip-sign", tx_skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
216 cmd->add_flag("-j,--json", tx_print_json, localized("Print result as JSON"));
217 cmd->add_option("--json-file", tx_json_save_file, localized("Save result in JSON format into a file"));
218 cmd->add_flag("-d,--dont-broadcast", tx_dont_broadcast, localized("Don't broadcast transaction to the network (just print to stdout)"));
219 cmd->add_flag("--return-packed", tx_return_packed, localized("Used in conjunction with --dont-broadcast to get the packed transaction"));
220 cmd->add_option("-r,--ref-block", tx_ref_block_num_or_id, (localized("Set the reference block num or block id used for TAPOS (Transaction as Proof-of-Stake)")));
221 cmd->add_flag("--use-old-rpc", tx_use_old_rpc, localized("Use old RPC push_transaction, rather than new RPC send_transaction"));
222 cmd->add_flag("--use-old-send-rpc", tx_use_old_send_rpc, localized("Use old RPC send_transaction, rather than new RPC /v1/chain/send_transaction2"));
223
224 string msg = "An account and permission level to authorize, as in 'account@permission'";
225 if(!default_permission.empty())
226 msg += " (defaults to '" + default_permission + "')";
227 cmd->add_option("-p,--permission", tx_permission, localized(msg.c_str()));
228
229 cmd->add_option("--max-cpu-usage-ms", tx_max_cpu_usage, localized("Set an upper limit on the milliseconds of cpu usage budget, for the execution of the transaction (defaults to 0 which means no limit)"));
230 cmd->add_option("--max-net-usage", tx_max_net_usage, localized("Set an upper limit on the net usage budget, in bytes, for the transaction (defaults to 0 which means no limit)"));
231
232 cmd->add_option("--delay-sec", delaysec, localized("Set the delay_sec seconds, defaults to 0s"));
233 cmd->add_option("-t,--return-failure-trace", tx_rtn_failure_trace, localized("Return partial traces on failed transactions"));
234 cmd->add_option("--retry-irreversible", tx_retry_lib, localized("Request node to retry transaction until it is irreversible or expires, blocking call"));
235 cmd->add_option("--retry-num-blocks", tx_retry_num_blocks, localized("Request node to retry transaction until in a block of given height, blocking call"));
236}
237
239 auto fixedPermissions = permissions | boost::adaptors::transformed([](const string& p) {
240 vector<string> pieces;
241 split(pieces, p, boost::algorithm::is_any_of("@"));
242 if( pieces.size() == 1 ) pieces.push_back( "active" );
243 return chain::permission_level{ .actor = name(pieces[0]), .permission = name(pieces[1]) };
244 });
245 vector<chain::permission_level> accountPermissions;
246 boost::range::copy(fixedPermissions, back_inserter(accountPermissions));
247 return accountPermissions;
248}
249
251 if (permissions.empty())
252 return vector<chain::permission_level>{default_permission};
253 else
255}
256
257template<typename T>
258fc::variant call( const std::string& url,
259 const std::string& path,
260 const T& v ) {
261 try {
262 auto sp = std::make_unique<sysio::client::http::connection_param>(context, parse_url(url) + path, no_verify ? false : true, headers);
264 }
265 catch(boost::system::system_error& e) {
266 std::string exec_name;
267 if(url == ::url) {
268 exec_name = node_executable_name;
269 } else if(url == ::wallet_url) {
270 exec_name = key_store_executable_name;
271 }
272 std::cerr << localized( "Failed http request to ${n} at ${u}; is ${n} running?\n"
273 " Error: ${e}",
274 ("n", exec_name)("u", url)("e", e.what()) ) << std::endl;
275 throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, e.what())});
276 }
277}
278
279template<typename T>
280fc::variant call( const std::string& path,
281 const T& v ) { return call( url, path, fc::variant(v) ); }
282
283template<>
284fc::variant call( const std::string& url,
285 const std::string& path) { return call( url, path, fc::variant() ); }
286
290
292 return fc::to_string(fc::time_point::now().time_since_epoch().count());
293}
294
296 return chain::action( {}, config::null_account_name, name("nonce"), fc::raw::pack(fc::time_point::now().time_since_epoch().count()));
297}
298
299void prompt_for_wallet_password(string& pw, const string& name) {
300 if(pw.size() == 0 && name != "SecureEnclave") {
301 std::cout << localized("password: ");
303 std::getline( std::cin, pw, '\n' );
305 }
306}
307
309 // TODO better error checking
310 //wdump((trx));
311 const auto& public_keys = call(wallet_url, wallet_public_keys);
312 auto get_arg = fc::mutable_variant_object
313 ("transaction", (transaction)trx)
314 ("available_keys", public_keys);
315 const auto& required_keys = call(get_required_keys, get_arg);
316 return required_keys["required_keys"];
317}
318
319void sign_transaction(signed_transaction& trx, fc::variant& required_keys, const chain_id_type& chain_id) {
320 fc::variants sign_args = {fc::variant(trx), required_keys, fc::variant(chain_id)};
321 const auto& signed_trx = call(wallet_url, wallet_sign_trx, sign_args);
322 trx = signed_trx.as<signed_transaction>();
323}
324
326 auto info = get_info();
327
328 if (trx.signatures.size() == 0) { // #5445 can't change txn content if already signed
329 trx.expiration = info.head_block_time + tx_expiration;
330
331 // Set tapos, default to last irreversible block if it's not specified by the user
332 block_id_type ref_block_id = info.last_irreversible_block_id;
333 try {
334 fc::variant ref_block;
335 if (!tx_ref_block_num_or_id.empty()) {
337 ref_block_id = ref_block["id"].as<block_id_type>();
338 }
339 } SYS_RETHROW_EXCEPTIONS(invalid_ref_block_exception, "Invalid reference block num or id: ${block_num_or_id}", ("block_num_or_id", tx_ref_block_num_or_id));
340 trx.set_reference_block(ref_block_id);
341
342 if (tx_force_unique) {
343 trx.context_free_actions.emplace_back( generate_nonce_action() );
344 }
345
348 trx.delay_sec = delaysec;
349 }
350
351 if (!tx_skip_sign) {
352 auto required_keys = determine_required_keys(trx);
353 sign_transaction(trx, required_keys, info.chain_id);
354 }
355
356 if (!tx_dont_broadcast) {
357 SYSC_ASSERT( !(tx_use_old_rpc && tx_use_old_send_rpc), "ERROR: --use-old-rpc and --use-old-send-rpc are mutually exclusive" );
358 SYSC_ASSERT( !(tx_retry_lib && tx_retry_num_blocks > 0), "ERROR: --retry-irreversible and --retry-num-blocks are mutually exclusive" );
359 if (tx_use_old_rpc) {
360 SYSC_ASSERT( !tx_read_only, "ERROR: --read-only can not be used with --use-old-rpc" );
361 SYSC_ASSERT( !tx_rtn_failure_trace, "ERROR: --return-failure-trace can not be used with --use-old-rpc" );
362 SYSC_ASSERT( !tx_retry_lib, "ERROR: --retry-irreversible can not be used with --use-old-rpc" );
363 SYSC_ASSERT( !tx_retry_num_blocks, "ERROR: --retry-num-blocks can not be used with --use-old-rpc" );
364 return call( push_txn_func, packed_transaction( trx, compression ) );
365 } else if (tx_use_old_send_rpc) {
366 SYSC_ASSERT( !tx_read_only, "ERROR: --read-only can not be used with --use-old-send-rpc" );
367 SYSC_ASSERT( !tx_rtn_failure_trace, "ERROR: --return-failure-trace can not be used with --use-old-send-rpc" );
368 SYSC_ASSERT( !tx_retry_lib, "ERROR: --retry-irreversible can not be used with --use-old-send-rpc" );
369 SYSC_ASSERT( !tx_retry_num_blocks, "ERROR: --retry-num-blocks can not be used with --use-old-send-rpc" );
370 try {
371 return call( send_txn_func, packed_transaction( trx, compression ) );
372 } catch( chain::missing_chain_api_plugin_exception& ) {
373 std::cerr << "New RPC send_transaction may not be supported. Add flag --use-old-rpc to use old RPC push_transaction instead." << std::endl;
374 throw;
375 }
376 } else {
377 if( tx_read_only ) {
378 SYSC_ASSERT( !tx_retry_lib, "ERROR: --retry-irreversible can not be used with --read-only" );
379 SYSC_ASSERT( !tx_retry_num_blocks, "ERROR: --retry-num-blocks can not be used with --read-only" );
380 try {
381 auto compute_txn_arg = fc::mutable_variant_object ("transaction",
382 packed_transaction(trx,compression));
383 return call( compute_txn_func, compute_txn_arg);
384 } catch( chain::missing_chain_api_plugin_exception& ) {
385 std::cerr << "New RPC compute_transaction may not be supported. Submit to a different node." << std::endl;
386 throw;
387 }
388 } else {
389 try {
390 bool retry = tx_retry_lib || tx_retry_num_blocks > 0;
391 auto args = fc::mutable_variant_object()
392 ( "return_failure_trace", tx_rtn_failure_trace )
393 ( "retry_trx", retry );
394 if( tx_retry_num_blocks > 0 ) args( "retry_trx_num_blocks", tx_retry_num_blocks );
395 args( "transaction", packed_transaction( trx, compression ) );
396 return call( send2_txn_func, args );
397 } catch( chain::missing_chain_api_plugin_exception& ) {
398 std::cerr << "New RPC send_transaction2 may not be supported.\n"
399 << "Add flag --use-old-send-rpc or --use-old-rpc to use old RPC send_transaction." << std::endl;
400 throw;
401 }
402 }
403 }
404 } else {
405 if (!tx_return_packed) {
406 return fc::variant(trx);
407 } else {
408 return fc::variant(packed_transaction(trx, compression));
409 }
410 }
411}
412
415 trx.actions = std::forward<decltype(actions)>(actions);
416
417 return push_transaction(trx, compression);
418}
419
421 std::string return_value, return_value_prefix{"return value: "};
422 const auto & iter_value = at.get_object().find("return_value_data");
423 const auto & iter_hex = at.get_object().find("return_value_hex_data");
424
425 if( iter_value != at.get_object().end() ) {
426 return_value = fc::json::to_string(iter_value->value(), fc::time_point::maximum());
427 }
428 else if( iter_hex != at.get_object().end() ) {
429 return_value = iter_hex->value().as_string();
430 return_value_prefix = "return value (hex): ";
431 }
432
433 if( !return_value.empty() ) {
434 if( return_value.size() > 100 ) {
435 return_value = return_value.substr(0, 100) + "...";
436 }
437 cout << "=>" << std::setw(46) << std::right << return_value_prefix << return_value << "\n";
438 }
439}
440
441void print_action( const fc::variant& at ) {
442 auto receiver = at["receiver"].as_string();
443 const auto& act = at["act"].get_object();
444 auto code = act["account"].as_string();
445 auto func = act["name"].as_string();
446 auto args = fc::json::to_string( act["data"], fc::time_point::maximum() );
447 auto console = at["console"].as_string();
448
449 /*
450 if( code == "sysio" && func == "setcode" )
451 args = args.substr(40)+"...";
452 if( name(code) == config::system_account_name && func == "setabi" )
453 args = args.substr(40)+"...";
454 */
455 if( args.size() > 100 ) args = args.substr(0,100) + "...";
456 cout << "#" << std::setw(14) << right << receiver << " <= " << std::setw(28) << std::left << (code +"::" + func) << " " << args << "\n";
458 if( console.size() ) {
459 std::stringstream ss(console);
460 string line;
461 while( std::getline( ss, line ) ) {
462 cout << ">> " << clean_output( std::move( line ) ) << "\n";
463 if( !verbose ) break;
464 line.clear();
465 }
466 }
467}
468
469//resolver for ABI serializer to decode actions in proposed transaction in multisig contract
470auto abi_serializer_resolver = [](const name& account) -> std::optional<abi_serializer> {
471 static unordered_map<account_name, std::optional<abi_serializer> > abi_cache;
472 auto it = abi_cache.find( account );
473 if ( it == abi_cache.end() ) {
474 auto result = call(get_abi_func, fc::mutable_variant_object("account_name", account));
475 auto abi_results = result.as<sysio::chain_apis::read_only::get_abi_results>();
476
477 std::optional<abi_serializer> abis;
478 if( abi_results.abi.has_value() ) {
479 abis.emplace( *abi_results.abi, abi_serializer::create_yield_function( abi_serializer_max_time ) );
480 } else {
481 std::cerr << "ABI for contract " << account.to_string() << " not found. Action data will be shown in hex only." << std::endl;
482 }
483 abi_cache.emplace( account, abis );
484
485 return abis;
486 }
487
488 return it->second;
489};
490
491bytes variant_to_bin( const account_name& account, const action_name& action, const fc::variant& action_args_var ) {
492 auto abis = abi_serializer_resolver( account );
493 FC_ASSERT( abis, "No ABI found for ${contract}", ("contract", account));
494
495 auto action_type = abis->get_action_type( action );
496 FC_ASSERT( !action_type.empty(), "Unknown action ${action} in contract ${contract}", ("action", action)( "contract", account ));
497 return abis->variant_to_binary( action_type, action_args_var, abi_serializer::create_yield_function( abi_serializer_max_time ) );
498}
499
500fc::variant bin_to_variant( const account_name& account, const action_name& action, const bytes& action_args) {
501 auto abis = abi_serializer_resolver( account );
502 FC_ASSERT( abis, "No ABI found for ${contract}", ("contract", account));
503
504 auto action_type = abis->get_action_type( action );
505 FC_ASSERT( !action_type.empty(), "Unknown action ${action} in contract ${contract}", ("action", action)( "contract", account ));
506 return abis->binary_to_variant( action_type, action_args, abi_serializer::create_yield_function( abi_serializer_max_time ) );
507}
508
510{
511 regex r("^[ \t]*[\{\[]");
512 if ( !regex_search(file_or_str, r) && fc::is_regular_file(file_or_str) ) {
513 try {
514 return fc::json::from_file(file_or_str, ptype);
515 } SYS_RETHROW_EXCEPTIONS(json_parse_exception, "Fail to parse JSON from file: ${file}", ("file", file_or_str));
516
517 } else {
518 try {
519 return fc::json::from_string(file_or_str, ptype);
520 } SYS_RETHROW_EXCEPTIONS(json_parse_exception, "Fail to parse JSON from string: ${string}", ("string", file_or_str));
521 }
522}
523
524bytes json_or_file_to_bin( const account_name& account, const action_name& action, const string& data_or_filename ) {
525 fc::variant action_args_var;
526 if( !data_or_filename.empty() ) {
527 action_args_var = json_from_file_or_string(data_or_filename, fc::json::parse_type::relaxed_parser);
528 }
529 return variant_to_bin( account, action, action_args_var );
530}
531
534 if( action.get_object().contains( "inline_traces" ) ) {
535 const auto& inline_traces = action["inline_traces"].get_array();
536 for( const auto& t : inline_traces ) {
538 }
539 }
540}
541
542int get_return_code( const fc::variant& result ) {
543 // if a trx with a processed, then check to see if it failed execution for return value
544 int r = 0;
545 if (result.is_object() && result.get_object().contains("processed")) {
546 const auto& processed = result["processed"];
547 if( processed.is_object() && processed.get_object().contains( "except" ) ) {
548 const auto& except = processed["except"];
549 if( except.is_object() ) {
550 try {
551 auto soft_except = except.as<fc::exception>();
552 auto code = soft_except.code();
553 if( code > std::numeric_limits<int>::max() ) {
554 r = 1;
555 } else {
556 r = static_cast<int>( code );
557 }
558 if( r == 0 ) r = 1;
559 } catch( ... ) {
560 r = 1;
561 }
562 }
563 }
564 }
565 return r;
566}
567
568void print_result( const fc::variant& result ) { try {
569 if (result.is_object() && result.get_object().contains("processed")) {
570 const auto& processed = result["processed"];
571 const auto& transaction_id = processed["id"].as_string();
572 string status = "failed";
573 int64_t net = -1;
574 int64_t cpu = -1;
575 if( processed.get_object().contains( "receipt" )) {
576 const auto& receipt = processed["receipt"];
577 if( receipt.is_object()) {
578 status = receipt["status"].as_string();
579 net = receipt["net_usage_words"].as_int64() * 8;
580 cpu = receipt["cpu_usage_us"].as_int64();
581 }
582 }
583
584 cerr << status << " transaction: " << transaction_id << " ";
585 if( net < 0 ) {
586 cerr << "<unknown>";
587 } else {
588 cerr << net;
589 }
590 cerr << " bytes ";
591 if( cpu < 0 ) {
592 cerr << "<unknown>";
593 } else {
594 cerr << cpu;
595 }
596
597 cerr << " us\n";
598
599 if( status == "failed" ) {
600 auto soft_except = processed["except"].as<std::optional<fc::exception>>();
601 if( soft_except ) {
602 edump((soft_except->to_detail_string()));
603 }
604 } else {
605 const auto& actions = processed["action_traces"].get_array();
606 for( const auto& a : actions ) {
608 }
609 wlog( "\rwarning: transaction executed locally, but may not be confirmed by the network yet" );
610 }
611 } else {
612 cerr << fc::json::to_pretty_string( result ) << endl;
613 }
614} FC_CAPTURE_AND_RETHROW( (result) ) }
615
616void send_actions(std::vector<chain::action>&& actions, packed_transaction::compression_type compression = packed_transaction::compression_type::none ) {
617 std::ofstream out;
618 if (tx_json_save_file.length()) {
619 out.open(tx_json_save_file);
620 SYSC_ASSERT(!out.fail(), "ERROR: Failed to create file \"${p}\"", ("p", tx_json_save_file));
621 }
622 auto result = push_actions( move(actions), compression);
623
624 string jsonstr;
625 if (tx_json_save_file.length()) {
626 jsonstr = fc::json::to_pretty_string( result );
627 out << jsonstr;
628 out.close();
629 }
630 return_code = get_return_code( result );
631 if( tx_print_json ) {
632 if (jsonstr.length() == 0) {
633 jsonstr = fc::json::to_pretty_string( result );
634 }
635 cout << jsonstr << endl;
636 } else {
637 print_result( result );
638 }
639}
640
642 std::ofstream out;
643 if (tx_json_save_file.length()) {
644 out.open(tx_json_save_file);
645 SYSC_ASSERT(!out.fail(), "ERROR: Failed to create file \"${p}\"", ("p", tx_json_save_file));
646 }
647 auto result = push_transaction(trx, compression);
648
649 string jsonstr;
650 if (tx_json_save_file.length()) {
651 jsonstr = fc::json::to_pretty_string( result );
652 out << jsonstr;
653 out.close();
654 }
655 return_code = get_return_code( result );
656 if( tx_print_json ) {
657 if (jsonstr.length() == 0) {
658 jsonstr = fc::json::to_pretty_string( result );
659 }
660 cout << jsonstr << endl;
661 } else {
662 print_result( result );
663 }
664}
665
667 auto at_pos = s.find('@');
668 return permission_level { name(s.substr(0, at_pos)), name(s.substr(at_pos + 1)) };
669}
670
671chain::action create_newaccount(const name& creator, const name& newaccount, auth_type owner, auth_type active) {
672 return action {
673 get_account_permissions(tx_permission, {creator,config::active_name}),
675 .creator = creator,
676 .name = newaccount,
677 .owner = std::holds_alternative<public_key_type>(owner) ? authority(std::get<public_key_type>(owner)) : authority(std::get<permission_level>(owner)),
678 .active = std::holds_alternative<public_key_type>(active) ? authority(std::get<public_key_type>(active)) : authority(std::get<permission_level>(active))
679 }
680 };
681}
682
683chain::action create_action(const vector<permission_level>& authorization, const account_name& code, const action_name& act, const fc::variant& args) {
684 return chain::action{authorization, code, act, variant_to_bin(code, act, args)};
685}
686
687chain::action create_buyram(const name& creator, const name& newaccount, const asset& quantity) {
689 ("payer", creator.to_string())
690 ("receiver", newaccount.to_string())
691 ("quant", quantity.to_string());
692 return create_action(get_account_permissions(tx_permission, {creator,config::active_name}),
693 config::system_account_name, "buyram"_n, act_payload);
694}
695
696chain::action create_buyrambytes(const name& creator, const name& newaccount, uint32_t numbytes) {
698 ("payer", creator.to_string())
699 ("receiver", newaccount.to_string())
700 ("bytes", numbytes);
701 return create_action(get_account_permissions(tx_permission, {creator,config::active_name}),
702 config::system_account_name, "buyrambytes"_n, act_payload);
703}
704
705chain::action create_delegate(const name& from, const name& receiver, const asset& net, const asset& cpu, bool transfer) {
707 ("from", from.to_string())
708 ("receiver", receiver.to_string())
709 ("stake_net_quantity", net.to_string())
710 ("stake_cpu_quantity", cpu.to_string())
711 ("transfer", transfer);
712 return create_action(get_account_permissions(tx_permission, {from,config::active_name}),
713 config::system_account_name, "delegatebw"_n, act_payload);
714}
715
716fc::variant regproducer_variant(const account_name& producer, const public_key_type& key, const string& url, uint16_t location) {
718 ("producer", producer)
719 ("producer_key", key)
720 ("url", url)
721 ("location", location)
722 ;
723}
724
725chain::action create_open(const string& contract, const name& owner, symbol sym, const name& ram_payer) {
726 auto open_ = fc::mutable_variant_object
727 ("owner", owner)
728 ("symbol", sym)
729 ("ram_payer", ram_payer);
730 return action {
731 get_account_permissions(tx_permission, {ram_payer, config::active_name}),
732 name(contract), "open"_n, variant_to_bin( name(contract), "open"_n, open_ )
733 };
734}
735
736chain::action create_transfer(const string& contract, const name& sender, const name& recipient, asset amount, const string& memo ) {
737
738 auto transfer = fc::mutable_variant_object
739 ("from", sender)
740 ("to", recipient)
741 ("quantity", amount)
742 ("memo", memo);
743
744 return action {
745 get_account_permissions(tx_permission, {sender,config::active_name}),
746 name(contract), "transfer"_n, variant_to_bin( name(contract), "transfer"_n, transfer )
747 };
748}
749
750chain::action create_setabi(const name& account, const bytes& abi) {
751 return action {
752 get_account_permissions(tx_permission, {account,config::active_name}),
753 setabi{
754 .account = account,
755 .abi = abi
756 }
757 };
758}
759
760chain::action create_setcode(const name& account, const bytes& code) {
761 return action {
762 get_account_permissions(tx_permission, {account,config::active_name}),
763 setcode{
764 .account = account,
765 .vmtype = 0,
766 .vmversion = 0,
767 .code = code
768 }
769 };
770}
771
772chain::action create_updateauth(const name& account, const name& permission, const name& parent, const authority& auth) {
773 return action { get_account_permissions(tx_permission, {account,config::active_name}),
774 updateauth{account, permission, parent, auth}};
775}
776
777chain::action create_deleteauth(const name& account, const name& permission) {
778 return action { get_account_permissions(tx_permission, {account,config::active_name}),
779 deleteauth{account, permission}};
780}
781
782chain::action create_linkauth(const name& account, const name& code, const name& type, const name& requirement) {
783 return action { get_account_permissions(tx_permission, {account,config::active_name}),
784 linkauth{account, code, type, requirement}};
785}
786
787chain::action create_unlinkauth(const name& account, const name& code, const name& type) {
788 return action { get_account_permissions(tx_permission, {account,config::active_name}),
789 unlinkauth{account, code, type}};
790}
791
792authority parse_json_authority(const std::string& authorityJsonOrFile) {
793 fc::variant authority_var = json_from_file_or_string(authorityJsonOrFile);
794 try {
795 return authority_var.as<authority>();
796 } SYS_RETHROW_EXCEPTIONS(authority_type_exception, "Invalid authority format '${data}'",
797 ("data", fc::json::to_string(authority_var, fc::time_point::maximum())))
798}
799
800authority parse_json_authority_or_key(const std::string& authorityJsonOrFile) {
801 if (boost::istarts_with(authorityJsonOrFile, "SYS") || boost::istarts_with(authorityJsonOrFile, "PUB_R1")) {
802 try {
803 return authority(public_key_type(authorityJsonOrFile));
804 } SYS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", authorityJsonOrFile))
805 } else {
806 auto result = parse_json_authority(authorityJsonOrFile);
807 SYS_ASSERT( sysio::chain::validate(result), authority_type_exception, "Authority failed validation! ensure that keys, accounts, and waits are sorted and that the threshold is valid and satisfiable!");
808 return result;
809 }
810}
811
812asset to_asset( account_name code, const string& s ) {
813 static map< pair<account_name, sysio::chain::symbol_code>, sysio::chain::symbol> cache;
814 auto a = asset::from_string( s );
815 sysio::chain::symbol_code sym = a.get_symbol().to_symbol_code();
816 auto it = cache.find( make_pair(code, sym) );
817 auto sym_str = a.symbol_name();
818 if ( it == cache.end() ) {
820 ("code", code)
821 ("symbol", sym_str)
822 );
823 auto obj = json.get_object();
824 auto obj_it = obj.find( sym_str );
825 if (obj_it != obj.end()) {
826 auto result = obj_it->value().as<sysio::chain_apis::read_only::get_currency_stats_result>();
827 auto p = cache.emplace( make_pair( code, sym ), result.max_supply.get_symbol() );
828 it = p.first;
829 } else {
830 SYS_THROW(symbol_type_exception, "Symbol ${s} is not supported by token contract ${c}", ("s", sym_str)("c", code));
831 }
832 }
833 auto expected_symbol = it->second;
834 if ( a.decimals() < expected_symbol.decimals() ) {
835 auto factor = expected_symbol.precision() / a.precision();
836 a = asset( a.get_amount() * factor, expected_symbol );
837 } else if ( a.decimals() > expected_symbol.decimals() ) {
838 SYS_THROW(symbol_type_exception, "Too many decimal digits in ${a}, only ${d} supported", ("a", a)("d", expected_symbol.decimals()));
839 } // else precision matches
840 return a;
841}
842
843inline asset to_asset( const string& s ) {
844 return to_asset( "sysio.token"_n, s );
845}
846
848 string account;
851 string parent;
852 bool add_code = false;
853 bool remove_code = false;
854
856 auto permissions = accountCmd->add_subcommand("permission", localized("Set parameters dealing with account permissions"));
857 permissions->add_option("account", account, localized("The account to set/delete a permission authority for"))->required();
858 permissions->add_option("permission", permission, localized("The permission name to set/delete an authority for"))->required();
859 permissions->add_option("authority", authority_json_or_file, localized("[delete] NULL, [create/update] public key, JSON string or filename defining the authority, [code] contract name"));
860 permissions->add_option("parent", parent, localized("[create] The permission name of this parents permission, defaults to 'active'"));
861 permissions->add_flag("--add-code", add_code, localized("[code] add '${code}' permission to specified permission authority", ("code", name(config::sysio_code_name))));
862 permissions->add_flag("--remove-code", remove_code, localized("[code] remove '${code}' permission from specified permission authority", ("code", name(config::sysio_code_name))));
863
864 add_standard_transaction_options(permissions, "account@active");
865
866 permissions->callback([this] {
867 SYSC_ASSERT( !(add_code && remove_code), "ERROR: Either --add-code or --remove-code can be set" );
868 SYSC_ASSERT( (add_code ^ remove_code) || !authority_json_or_file.empty(), "ERROR: authority should be specified unless add or remove code permission" );
869
870 authority auth;
871
872 bool need_parent = parent.empty() && (name(permission) != name("owner"));
873 bool need_auth = add_code || remove_code;
874
875 if ( !need_auth && boost::iequals(authority_json_or_file, "null") ) {
876 send_actions( { create_deleteauth(name(account), name(permission)) } );
877 return;
878 }
879
880 if ( need_parent || need_auth ) {
883 auto itr = std::find_if(res.permissions.begin(), res.permissions.end(), [&](const auto& perm) {
884 return perm.perm_name == name(permission);
885 });
886
887 if ( need_parent ) {
888 // see if we can auto-determine the proper parent
889 if ( itr != res.permissions.end() ) {
890 parent = (*itr).parent.to_string();
891 } else {
892 // if this is a new permission and there is no parent we default to "active"
893 parent = config::active_name.to_string();
894 }
895 }
896
897 if ( need_auth ) {
898 auto actor = (authority_json_or_file.empty()) ? name(account) : name(authority_json_or_file);
899 auto code_name = config::sysio_code_name;
900
901 if ( itr != res.permissions.end() ) {
902 // fetch existing authority
903 auth = std::move((*itr).required_auth);
904
905 auto code_perm = permission_level { actor, code_name };
906 auto itr2 = std::lower_bound(auth.accounts.begin(), auth.accounts.end(), code_perm, [&](const auto& perm_level, const auto& value) {
907 return perm_level.permission < value; // Safe since valid authorities must order the permissions in accounts in ascending order
908 });
909
910 if ( add_code ) {
911 if ( itr2 != auth.accounts.end() && itr2->permission == code_perm ) {
912 // authority already contains code permission, promote its weight to satisfy threshold
913 if ( (*itr2).weight < auth.threshold ) {
914 if ( auth.threshold > std::numeric_limits<weight_type>::max() ) {
915 std::cerr << "ERROR: Threshold is too high to be satisfied by sole code permission" << std::endl;
916 return;
917 }
918 std::cerr << localized("The weight of '${actor}@${code}' in '${permission}' permission authority will be increased up to threshold",
919 ("actor", actor)("code", code_name)("permission", permission)) << std::endl;
920 (*itr2).weight = static_cast<weight_type>(auth.threshold);
921 } else {
922 std::cerr << localized("ERROR: The permission '${permission}' already contains '${actor}@${code}'",
923 ("permission", permission)("actor", actor)("code", code_name)) << std::endl;
924 return ;
925 }
926 } else {
927 // add code permission to specified authority
928 if ( auth.threshold > std::numeric_limits<weight_type>::max() ) {
929 std::cerr << "ERROR: Threshold is too high to be satisfied by sole code permission" << std::endl;
930 return;
931 }
932 auth.accounts.insert( itr2, permission_level_weight {
933 .permission = { actor, code_name },
934 .weight = static_cast<weight_type>(auth.threshold)
935 });
936 }
937 } else {
938 if ( itr2 != auth.accounts.end() && itr2->permission == code_perm ) {
939 // remove code permission, if authority becomes empty by the removal of code permission, delete permission
940 auth.accounts.erase( itr2 );
941 if ( auth.keys.empty() && auth.accounts.empty() && auth.waits.empty() ) {
942 send_actions( { create_deleteauth(name(account), name(permission)) } );
943 return;
944 }
945 } else {
946 // authority doesn't contain code permission
947 std::cerr << localized("ERROR: '${actor}@${code}' does not exist in '${permission}' permission authority",
948 ("actor", actor)("code", code_name)("permission", permission)) << std::endl;
949 return;
950 }
951 }
952 } else {
953 if ( add_code ) {
954 // create new permission including code permission
955 auth.threshold = 1;
956 auth.accounts.push_back( permission_level_weight {
957 .permission = { actor, code_name },
958 .weight = 1
959 });
960 } else {
961 // specified permission doesn't exist, so failed to remove code permission from it
962 std::cerr << localized("ERROR: The permission '${permission}' does not exist", ("permission", permission)) << std::endl;
963 return;
964 }
965 }
966 }
967 }
968
969 if ( !need_auth ) {
971 }
972
973 send_actions( { create_updateauth(name(account), name(permission), name(parent), auth) } );
974 });
975 }
976};
977
980 string codeStr;
981 string typeStr;
983
985 auto permissions = actionRoot->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
986 permissions->add_option("account", accountStr, localized("The account to set/delete a permission authority for"))->required();
987 permissions->add_option("code", codeStr, localized("The account that owns the code for the action"))->required();
988 permissions->add_option("type", typeStr, localized("The type of the action"))->required();
989 permissions->add_option("requirement", requirementStr, localized("[delete] NULL, [set/update] The permission name require for executing the given action"))->required();
990
991 add_standard_transaction_options(permissions, "account@active");
992
993 permissions->callback([this] {
994 name account = name(accountStr);
995 name code = name(codeStr);
996 name type = name(typeStr);
997 bool is_delete = boost::iequals(requirementStr, "null");
998
999 if (is_delete) {
1000 send_actions({create_unlinkauth(account, code, type)});
1001 } else {
1002 name requirement = name(requirementStr);
1003 send_actions({create_linkauth(account, code, type, requirement)});
1004 }
1005 });
1006 }
1007};
1008
1009
1011 using namespace boost::asio;
1012
1013 io_service ios;
1014 local::stream_protocol::endpoint endpoint(wallet_url.substr(strlen("unix://")));
1015 local::stream_protocol::socket socket(ios);
1016 boost::system::error_code ec;
1017 socket.connect(endpoint, ec);
1018
1019 return !ec;
1020}
1021
1022void try_local_port(uint32_t duration) {
1023 using namespace std::chrono;
1024 auto start_time = duration_cast<std::chrono::milliseconds>( system_clock::now().time_since_epoch() ).count();
1025 while ( !local_port_used()) {
1026 if (duration_cast<std::chrono::milliseconds>( system_clock::now().time_since_epoch()).count() - start_time > duration ) {
1027 std::cerr << "Unable to connect to " << key_store_executable_name << ", if " << key_store_executable_name << " is running please kill the process and try again.\n";
1028 throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, "Unable to connect to ${k}", ("k", key_store_executable_name))});
1029 }
1030 }
1031}
1032
1034 if (no_auto_kiod)
1035 return;
1036 // get, version, net, convert do not require kiod
1037 if (tx_skip_sign || app->got_subcommand("get") || app->got_subcommand("version") || app->got_subcommand("net") || app->got_subcommand("convert"))
1038 return;
1039 if (app->get_subcommand("create")->got_subcommand("key")) // create key does not require wallet
1040 return;
1041 if (app->get_subcommand("multisig")->got_subcommand("review")) // multisig review does not require wallet
1042 return;
1043 if (auto* subapp = app->get_subcommand("system")) {
1044 if (subapp->got_subcommand("listproducers") || subapp->got_subcommand("listbw") || subapp->got_subcommand("bidnameinfo")) // system list* do not require wallet
1045 return;
1046 }
1048 return;
1049
1050 if (local_port_used())
1051 return;
1052
1053 boost::filesystem::path binPath = boost::dll::program_location();
1054 binPath.remove_filename();
1055 // This extra check is necessary when running clio like this: ./clio ...
1056 if (binPath.filename_is_dot())
1057 binPath.remove_filename();
1058 binPath.append(key_store_executable_name); // if clio and kiod are in the same installation directory
1059 if (!boost::filesystem::exists(binPath)) {
1060 binPath.remove_filename().remove_filename().append("kiod").append(key_store_executable_name);
1061 }
1062
1063 if (boost::filesystem::exists(binPath)) {
1064 namespace bp = boost::process;
1065 binPath = boost::filesystem::canonical(binPath);
1066
1067 vector<std::string> pargs;
1068 pargs.push_back("--http-server-address");
1069 pargs.push_back("");
1070 pargs.push_back("--https-server-address");
1071 pargs.push_back("");
1072 pargs.push_back("--unix-socket-path");
1073 pargs.push_back(string(key_store_executable_name) + ".sock");
1074
1075 ::boost::process::child keos(binPath, pargs,
1076 bp::std_in.close(),
1077 bp::std_out > bp::null,
1078 bp::std_err > bp::null);
1079 if (keos.running()) {
1080 std::cerr << binPath << " launched" << std::endl;
1081 keos.detach();
1082 try_local_port(2000);
1083 } else {
1084 std::cerr << "No wallet service listening on " << wallet_url << ". Failed to launch " << binPath << std::endl;
1085 }
1086 } else {
1087 std::cerr << "No wallet service listening on "
1088 << ". Cannot automatically start " << key_store_executable_name << " because " << key_store_executable_name << " was not found." << std::endl;
1089 }
1090}
1091
1092
1094 std::cerr << localized("Host and port options (-H, --wallet-host, etc.) have been replaced with -u/--url and --wallet-url\n"
1095 "Use for example -u http://localhost:8888 or --url https://example.invalid/\n");
1096 exit(1);
1097 return false;
1098};
1099
1103 string url;
1105
1107 auto register_producer = actionRoot->add_subcommand("regproducer", localized("Register a new producer"));
1108 register_producer->add_option("account", producer_str, localized("The account to register as a producer"))->required();
1109 register_producer->add_option("producer_key", producer_key_str, localized("The producer's public key"))->required();
1110 register_producer->add_option("url", url, localized("The URL where info about producer can be found"), true);
1111 register_producer->add_option("location", loc, localized("Relative location for purpose of nearest neighbor scheduling"), true);
1112 add_standard_transaction_options(register_producer, "account@active");
1113
1114
1115 register_producer->callback([this] {
1116 public_key_type producer_key;
1117 try {
1118 producer_key = public_key_type(producer_key_str);
1119 } SYS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid producer public key: ${public_key}", ("public_key", producer_key_str))
1120
1121 auto regprod_var = regproducer_variant(name(producer_str), producer_key, url, loc );
1122 auto accountPermissions = get_account_permissions(tx_permission, {name(producer_str), config::active_name});
1123 send_actions({create_action(accountPermissions, config::system_account_name, "regproducer"_n, regprod_var)});
1124 });
1125 }
1126};
1127
1129 string creator;
1138 bool transfer = false;
1139 bool simple = false;
1140
1142 auto createAccount = actionRoot->add_subcommand(
1143 (simple ? "account" : "newaccount"),
1144 (simple ? localized("Create a new account on the blockchain (assumes system contract does not restrict RAM usage)")
1145 : localized("Create a new account on the blockchain with initial resources") )
1146 );
1147 createAccount->add_option("creator", creator, localized("The name of the account creating the new account"))->required();
1148 createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
1149 createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key or permission level for the new account"))->required();
1150 createAccount->add_option("ActiveKey", active_key_str, localized("The active public key or permission level for the new account"));
1151
1152 if (!simple) {
1153 createAccount->add_option("--stake-net", stake_net,
1154 (localized("The amount of tokens delegated for net bandwidth")))->required();
1155 createAccount->add_option("--stake-cpu", stake_cpu,
1156 (localized("The amount of tokens delegated for CPU bandwidth")))->required();
1157 createAccount->add_option("--buy-ram-kbytes", buy_ram_bytes_in_kbytes,
1158 (localized("The amount of RAM bytes to purchase for the new account in kibibytes (KiB)")));
1159 createAccount->add_option("--buy-ram-bytes", buy_ram_bytes,
1160 (localized("The amount of RAM bytes to purchase for the new account in bytes")));
1161 createAccount->add_option("--buy-ram", buy_ram_eos,
1162 (localized("The amount of RAM bytes to purchase for the new account in tokens")));
1163 createAccount->add_flag("--transfer", transfer,
1164 (localized("Transfer voting power and right to unstake tokens to receiver")));
1165 }
1166
1167 add_standard_transaction_options(createAccount, "creator@active");
1168
1169 createAccount->callback([this] {
1170 auth_type owner, active;
1171
1172 if( owner_key_str.find('@') != string::npos ) {
1173 try {
1175 } SYS_RETHROW_EXCEPTIONS( explained_exception, "Invalid owner permission level: ${permission}", ("permission", owner_key_str) )
1176 } else {
1177 try {
1179 } SYS_RETHROW_EXCEPTIONS( public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str) );
1180 }
1181
1182 if( active_key_str.empty() ) {
1183 active = owner;
1184 } else if( active_key_str.find('@') != string::npos ) {
1185 try {
1187 } SYS_RETHROW_EXCEPTIONS( explained_exception, "Invalid active permission level: ${permission}", ("permission", active_key_str) )
1188 } else {
1189 try {
1191 } SYS_RETHROW_EXCEPTIONS( public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str) );
1192 }
1193
1194 auto create = create_newaccount(name(creator), name(account_name), owner, active);
1195 if (!simple) {
1196 SYSC_ASSERT( buy_ram_eos.size() || buy_ram_bytes_in_kbytes || buy_ram_bytes, "ERROR: One of --buy-ram, --buy-ram-kbytes or --buy-ram-bytes should have non-zero value" );
1197 SYSC_ASSERT( !buy_ram_bytes_in_kbytes || !buy_ram_bytes, "ERROR: --buy-ram-kbytes and --buy-ram-bytes cannot be set at the same time" );
1200 auto net = to_asset(stake_net);
1201 auto cpu = to_asset(stake_cpu);
1202 if ( net.get_amount() != 0 || cpu.get_amount() != 0 ) {
1203 action delegate = create_delegate( name(creator), name(account_name), net, cpu, transfer);
1204 send_actions( { create, buyram, delegate } );
1205 } else {
1206 send_actions( { create, buyram } );
1207 }
1208 } else {
1209 send_actions( { create } );
1210 }
1211 });
1212 }
1213};
1214
1217
1219 auto unregister_producer = actionRoot->add_subcommand("unregprod", localized("Unregister an existing producer"));
1220 unregister_producer->add_option("account", producer_str, localized("The account to unregister as a producer"))->required();
1221 add_standard_transaction_options(unregister_producer, "account@active");
1222
1223 unregister_producer->callback([this] {
1225 ("producer", producer_str);
1226
1227 auto accountPermissions = get_account_permissions(tx_permission, {name(producer_str), config::active_name});
1228 send_actions({create_action(accountPermissions, config::system_account_name, "unregprod"_n, act_payload)});
1229 });
1230 }
1231};
1232
1236
1238 auto vote_proxy = actionRoot->add_subcommand("proxy", localized("Vote your stake through a proxy"));
1239 vote_proxy->add_option("voter", voter_str, localized("The voting account"))->required();
1240 vote_proxy->add_option("proxy", proxy_str, localized("The proxy account"))->required();
1241 add_standard_transaction_options(vote_proxy, "voter@active");
1242
1243 vote_proxy->callback([this] {
1245 ("voter", voter_str)
1246 ("proxy", proxy_str)
1247 ("producers", std::vector<account_name>{});
1248 auto accountPermissions = get_account_permissions(tx_permission, {name(voter_str), config::active_name});
1249 send_actions({create_action(accountPermissions, config::system_account_name, "voteproducer"_n, act_payload)});
1250 });
1251 }
1252};
1253
1257
1259 auto vote_producers = actionRoot->add_subcommand("prods", localized("Vote for one or more producers"));
1260 vote_producers->add_option("voter", voter_str, localized("The voting account"))->required();
1261 vote_producers->add_option("producers", producer_names, localized("The account(s) to vote for. All options from this position and following will be treated as the producer list."))->required();
1262 add_standard_transaction_options(vote_producers, "voter@active");
1263
1264 vote_producers->callback([this] {
1265
1266 std::sort( producer_names.begin(), producer_names.end() );
1267
1269 ("voter", voter_str)
1270 ("proxy", "")
1271 ("producers", producer_names);
1272 auto accountPermissions = get_account_permissions(tx_permission, {name(voter_str), config::active_name});
1273 send_actions({create_action(accountPermissions, config::system_account_name, "voteproducer"_n, act_payload)});
1274 });
1275 }
1276};
1277
1279 string voter;
1281
1283 auto approve_producer = actionRoot->add_subcommand("approve", localized("Add one producer to list of voted producers"));
1284 approve_producer->add_option("voter", voter, localized("The voting account"))->required();
1285 approve_producer->add_option("producer", producer_name, localized("The account to vote for"))->required();
1286 add_standard_transaction_options(approve_producer, "voter@active");
1287
1288 approve_producer->callback([this] {
1289 auto result = call(get_table_func, fc::mutable_variant_object("json", true)
1290 ("code", name(config::system_account_name).to_string())
1291 ("scope", name(config::system_account_name).to_string())
1292 ("table", "voters")
1293 ("table_key", "owner")
1294 ("lower_bound", name(voter).to_uint64_t())
1295 ("upper_bound", name(voter).to_uint64_t() + 1)
1296 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
1297 // Change to voter.value when clio no longer needs to support nodeop versions older than 1.5.0
1298 ("limit", 1)
1299 );
1301 // Condition in if statement below can simply be res.rows.empty() when clio no longer needs to support nodeop versions older than 1.5.0
1302 // Although since this subcommand will actually change the voter's vote, it is probably better to just keep this check to protect
1303 // against future potential chain_plugin bugs.
1304 if( res.rows.empty() || res.rows[0].get_object()["owner"].as_string() != name(voter).to_string() ) {
1305 std::cerr << "Voter info not found for account " << voter << std::endl;
1306 return;
1307 }
1308 SYS_ASSERT( 1 == res.rows.size(), multiple_voter_info, "More than one voter_info for account" );
1309 auto prod_vars = res.rows[0]["producers"].get_array();
1310 vector<sysio::name> prods;
1311 for ( auto& x : prod_vars ) {
1312 prods.push_back( name(x.as_string()) );
1313 }
1314 prods.push_back( name(producer_name) );
1315 std::sort( prods.begin(), prods.end() );
1316 auto it = std::unique( prods.begin(), prods.end() );
1317 if (it != prods.end() ) {
1318 std::cerr << "Producer \"" << producer_name << "\" is already on the list." << std::endl;
1319 return;
1320 }
1322 ("voter", voter)
1323 ("proxy", "")
1324 ("producers", prods);
1325 auto accountPermissions = get_account_permissions(tx_permission, {name(voter), config::active_name});
1326 send_actions({create_action(accountPermissions, config::system_account_name, "voteproducer"_n, act_payload)});
1327 });
1328 }
1329};
1330
1332 string voter;
1334
1336 auto approve_producer = actionRoot->add_subcommand("unapprove", localized("Remove one producer from list of voted producers"));
1337 approve_producer->add_option("voter", voter, localized("The voting account"))->required();
1338 approve_producer->add_option("producer", producer_name, localized("The account to remove from voted producers"))->required();
1339 add_standard_transaction_options(approve_producer, "voter@active");
1340
1341 approve_producer->callback([this] {
1342 auto result = call(get_table_func, fc::mutable_variant_object("json", true)
1343 ("code", name(config::system_account_name).to_string())
1344 ("scope", name(config::system_account_name).to_string())
1345 ("table", "voters")
1346 ("table_key", "owner")
1347 ("lower_bound", name(voter).to_uint64_t())
1348 ("upper_bound", name(voter).to_uint64_t() + 1)
1349 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
1350 // Change to voter.value when clio no longer needs to support nodeop versions older than 1.5.0
1351 ("limit", 1)
1352 );
1354 // Condition in if statement below can simply be res.rows.empty() when clio no longer needs to support nodeop versions older than 1.5.0
1355 // Although since this subcommand will actually change the voter's vote, it is probably better to just keep this check to protect
1356 // against future potential chain_plugin bugs.
1357 if( res.rows.empty() || res.rows[0].get_object()["owner"].as_string() != name(voter).to_string() ) {
1358 std::cerr << "Voter info not found for account " << voter << std::endl;
1359 return;
1360 }
1361 SYS_ASSERT( 1 == res.rows.size(), multiple_voter_info, "More than one voter_info for account" );
1362 auto prod_vars = res.rows[0]["producers"].get_array();
1363 vector<sysio::name> prods;
1364 for ( auto& x : prod_vars ) {
1365 prods.push_back( name(x.as_string()) );
1366 }
1367 auto it = std::remove( prods.begin(), prods.end(), name(producer_name) );
1368 if (it == prods.end() ) {
1369 std::cerr << "Cannot remove: producer \"" << producer_name << "\" is not on the list." << std::endl;
1370 return;
1371 }
1372 prods.erase( it, prods.end() ); //should always delete only one element
1374 ("voter", voter)
1375 ("proxy", "")
1376 ("producers", prods);
1377 auto accountPermissions = get_account_permissions(tx_permission, {name(voter), config::active_name});
1378 send_actions({create_action(accountPermissions, config::system_account_name, "voteproducer"_n, act_payload)});
1379 });
1380 }
1381};
1382
1384 bool print_json = false;
1385 uint32_t limit = 50;
1386 std::string lower;
1387
1389 auto list_producers = actionRoot->add_subcommand("listproducers", localized("List producers"));
1390 list_producers->add_flag("--json,-j", print_json, localized("Output in JSON format"));
1391 list_producers->add_option("-l,--limit", limit, localized("The maximum number of rows to return"));
1392 list_producers->add_option("-L,--lower", lower, localized("Lower bound value of key, defaults to first"));
1393 list_producers->callback([this] {
1395 ("json", true)("lower_bound", lower)("limit", limit));
1396 if ( print_json ) {
1397 std::cout << fc::json::to_pretty_string(rawResult) << std::endl;
1398 return;
1399 }
1400 auto result = rawResult.as<sysio::chain_apis::read_only::get_producers_result>();
1401 if ( result.rows.empty() ) {
1402 std::cout << "No producers found" << std::endl;
1403 return;
1404 }
1405 auto weight = result.total_producer_vote_weight;
1406 if ( !weight )
1407 weight = 1;
1408 printf("%-13s %-57s %-59s %s\n", "Producer", "Producer key", "Url", "Scaled votes");
1409 for ( auto& row : result.rows )
1410 printf("%-13.13s %-57.57s %-59.59s %1.4f\n",
1411 row["owner"].as_string().c_str(),
1412 row["producer_key"].as_string().c_str(),
1413 clean_output( row["url"].as_string() ).c_str(),
1414 row["total_votes"].as_double() / weight);
1415 if ( !result.more.empty() )
1416 std::cout << "-L " << clean_output( result.more ) << " for more" << std::endl;
1417 });
1418 }
1419};
1420
1422 bool print_json = false;
1423
1424 void print(const char* name, const fc::variant& schedule) {
1425 if (schedule.is_null()) {
1426 printf("%s schedule empty\n\n", name);
1427 return;
1428 }
1429 printf("%s schedule version %s\n", name, schedule["version"].as_string().c_str());
1430 printf(" %-13s %s\n", "Producer", "Producer Authority");
1431 printf(" %-13s %s\n", "=============", "==================");
1432 for( auto& row: schedule["producers"].get_array() ) {
1433 if( row.get_object().contains("block_signing_key") ) {
1434 // pre 2.0
1435 printf( " %-13s %s\n", row["producer_name"].as_string().c_str(), row["block_signing_key"].as_string().c_str() );
1436 } else {
1437 printf( " %-13s ", row["producer_name"].as_string().c_str() );
1438 auto a = row["authority"].as<block_signing_authority>();
1439 static_assert( std::is_same<decltype(a), std::variant<block_signing_authority_v0>>::value,
1440 "Updates maybe needed if block_signing_authority changes" );
1441 block_signing_authority_v0 auth = std::get<block_signing_authority_v0>(a);
1442 printf( "%s\n", fc::json::to_string( auth, fc::time_point::maximum() ).c_str() );
1443 }
1444 }
1445 printf("\n");
1446 }
1447
1449 auto get_schedule = actionRoot->add_subcommand("schedule", localized("Retrieve the producer schedule"));
1450 get_schedule->add_flag("--json,-j", print_json, localized("Output in JSON format"));
1451 get_schedule->callback([this] {
1453 if ( print_json ) {
1454 std::cout << fc::json::to_pretty_string(result) << std::endl;
1455 return;
1456 }
1457 print("active", result["active"]);
1458 print("pending", result["pending"]);
1459 print("proposed", result["proposed"]);
1460 });
1461 }
1462};
1463
1466
1468 auto get_transaction_id = actionRoot->add_subcommand("transaction_id", localized("Get transaction id given transaction object"));
1469 get_transaction_id->add_option("transaction", trx_to_check, localized("The JSON string or filename defining the transaction which transaction id we want to retrieve"))->required();
1470
1471 get_transaction_id->callback([&] {
1472 try {
1474 if( trx_var.is_object() ) {
1475 fc::variant_object& vo = trx_var.get_object();
1476 // if actions.data & actions.hex_data provided, use the hex_data since only currently support unexploded data
1477 if( vo.contains("actions") ) {
1478 if( vo["actions"].is_array() ) {
1480 fc::variants& action_variants = mvo["actions"].get_array();
1481 for( auto& action_v : action_variants ) {
1482 if( !action_v.is_object() ) {
1483 std::cerr << "Empty 'action' in transaction" << endl;
1484 return;
1485 }
1486 fc::variant_object& action_vo = action_v.get_object();
1487 if( action_vo.contains( "data" ) && action_vo.contains( "hex_data" ) ) {
1488 fc::mutable_variant_object maction_vo = action_vo;
1489 maction_vo["data"] = maction_vo["hex_data"];
1490 action_vo = maction_vo;
1491 vo = mvo;
1492 } else if( action_vo.contains( "data" ) ) {
1493 if( !action_vo["data"].is_string() ) {
1494 std::cerr << "get transaction_id only supports un-exploded 'data' (hex form)" << std::endl;
1495 return;
1496 }
1497 }
1498 }
1499 } else {
1500 std::cerr << "transaction JSON 'actions' is not an array" << std::endl;
1501 return;
1502 }
1503 } else {
1504 std::cerr << "transaction JSON does not include 'actions'" << std::endl;
1505 return;
1506 }
1507 auto trx = trx_var.as<transaction>();
1508 transaction_id_type id = trx.id();
1509 if( id == transaction().id() ) {
1510 std::cerr << "file/string does not represent a transaction" << std::endl;
1511 } else {
1512 std::cout << string( id ) << std::endl;
1513 }
1514 } else {
1515 std::cerr << "file/string does not represent a transaction" << std::endl;
1516 }
1517 } SYS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_check))
1518 });
1519 }
1520};
1521
1523 string from_str;
1530 bool transfer = false;
1531
1533 auto delegate_bandwidth = actionRoot->add_subcommand("delegatebw", localized("Delegate bandwidth"));
1534 delegate_bandwidth->add_option("from", from_str, localized("The account to delegate bandwidth from"))->required();
1535 delegate_bandwidth->add_option("receiver", receiver_str, localized("The account to receive the delegated bandwidth"))->required();
1536 delegate_bandwidth->add_option("stake_net_quantity", stake_net_amount, localized("The amount of tokens to stake for network bandwidth"))->required();
1537 delegate_bandwidth->add_option("stake_cpu_quantity", stake_cpu_amount, localized("The amount of tokens to stake for CPU bandwidth"))->required();
1538 delegate_bandwidth->add_option("--buyram", buy_ram_amount, localized("The amount of tokens to buy RAM with"));
1539 delegate_bandwidth->add_option("--buy-ram-bytes", buy_ram_bytes, localized("The amount of RAM to buy in bytes"));
1540 delegate_bandwidth->add_flag("--transfer", transfer, localized("Transfer voting power and right to unstake tokens to receiver"));
1541 add_standard_transaction_options(delegate_bandwidth, "from@active");
1542
1543 delegate_bandwidth->callback([this] {
1545 ("from", from_str)
1546 ("receiver", receiver_str)
1547 ("stake_net_quantity", to_asset(stake_net_amount))
1548 ("stake_cpu_quantity", to_asset(stake_cpu_amount))
1549 ("transfer", transfer);
1550 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
1551 std::vector<chain::action> acts{create_action(accountPermissions, config::system_account_name, "delegatebw"_n, act_payload)};
1552 SYSC_ASSERT( !(buy_ram_amount.size()) || !buy_ram_bytes, "ERROR: --buyram and --buy-ram-bytes cannot be set at the same time" );
1553 if (buy_ram_amount.size()) {
1555 } else if (buy_ram_bytes) {
1557 }
1558 send_actions(std::move(acts));
1559 });
1560 }
1561};
1562
1564 string from_str;
1569
1571 auto undelegate_bandwidth = actionRoot->add_subcommand("undelegatebw", localized("Undelegate bandwidth"));
1572 undelegate_bandwidth->add_option("from", from_str, localized("The account undelegating bandwidth"))->required();
1573 undelegate_bandwidth->add_option("receiver", receiver_str, localized("The account to undelegate bandwidth from"))->required();
1574 undelegate_bandwidth->add_option("unstake_net_quantity", unstake_net_amount, localized("The amount of tokens to undelegate for network bandwidth"))->required();
1575 undelegate_bandwidth->add_option("unstake_cpu_quantity", unstake_cpu_amount, localized("The amount of tokens to undelegate for CPU bandwidth"))->required();
1576 add_standard_transaction_options(undelegate_bandwidth, "from@active");
1577
1578 undelegate_bandwidth->callback([this] {
1580 ("from", from_str)
1581 ("receiver", receiver_str)
1582 ("unstake_net_quantity", to_asset(unstake_net_amount))
1583 ("unstake_cpu_quantity", to_asset(unstake_cpu_amount));
1584 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
1585 send_actions({create_action(accountPermissions, config::system_account_name, "undelegatebw"_n, act_payload)});
1586 });
1587 }
1588};
1589
1595 auto bidname = actionRoot->add_subcommand("bidname", localized("Name bidding"));
1596 bidname->add_option("bidder", bidder_str, localized("The bidding account"))->required();
1597 bidname->add_option("newname", newname_str, localized("The bidding name"))->required();
1598 bidname->add_option("bid", bid_amount, localized("The amount of tokens to bid"))->required();
1599 add_standard_transaction_options(bidname, "bidder@active");
1600 bidname->callback([this] {
1602 ("bidder", bidder_str)
1603 ("newname", newname_str)
1604 ("bid", to_asset(bid_amount));
1605 auto accountPermissions = get_account_permissions(tx_permission, {name(bidder_str), config::active_name});
1606 send_actions({create_action(accountPermissions, config::system_account_name, "bidname"_n, act_payload)});
1607 });
1608 }
1609};
1610
1612 bool print_json = false;
1613 string newname;
1615 auto list_producers = actionRoot->add_subcommand("bidnameinfo", localized("Get bidname info"));
1616 list_producers->add_flag("--json,-j", print_json, localized("Output in JSON format"));
1617 list_producers->add_option("newname", newname, localized("The bidding name"))->required();
1618 list_producers->callback([this] {
1619 auto rawResult = call(get_table_func, fc::mutable_variant_object("json", true)
1620 ("code", "sysio")("scope", "sysio")("table", "namebids")
1621 ("lower_bound", name(newname).to_uint64_t())
1622 ("upper_bound", name(newname).to_uint64_t() + 1)
1623 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
1624 // Change to newname.value when clio no longer needs to support nodeop versions older than 1.5.0
1625 ("limit", 1));
1626 if ( print_json ) {
1627 std::cout << fc::json::to_pretty_string(rawResult) << std::endl;
1628 return;
1629 }
1630 auto result = rawResult.as<sysio::chain_apis::read_only::get_table_rows_result>();
1631 // Condition in if statement below can simply be res.rows.empty() when clio no longer needs to support nodeop versions older than 1.5.0
1632 if( result.rows.empty() || result.rows[0].get_object()["newname"].as_string() != name(newname).to_string() ) {
1633 std::cout << "No bidname record found" << std::endl;
1634 return;
1635 }
1636 const auto& row = result.rows[0];
1637 string time = row["last_bid_time"].as_string();
1638 try {
1640 } catch (fc::parse_error_exception&) {
1641 }
1642 int64_t bid = row["high_bid"].as_int64();
1643 std::cout << std::left << std::setw(18) << "bidname:" << std::right << std::setw(24) << row["newname"].as_string() << "\n"
1644 << std::left << std::setw(18) << "highest bidder:" << std::right << std::setw(24) << row["high_bidder"].as_string() << "\n"
1645 << std::left << std::setw(18) << "highest bid:" << std::right << std::setw(24) << (bid > 0 ? bid : -bid) << "\n"
1646 << std::left << std::setw(18) << "last bid time:" << std::right << std::setw(24) << time << std::endl;
1647 if (bid < 0) std::cout << "This auction has already closed" << std::endl;
1648 });
1649 }
1650};
1651
1653 string account;
1654 bool print_json = false;
1655
1657 auto list_bw = actionRoot->add_subcommand("listbw", localized("List delegated bandwidth"));
1658 list_bw->add_option("account", account, localized("The account delegated bandwidth"))->required();
1659 list_bw->add_flag("--json,-j", print_json, localized("Output in JSON format") );
1660
1661 list_bw->callback([this] {
1662 //get entire table in scope of user account
1663 auto result = call(get_table_func, fc::mutable_variant_object("json", true)
1664 ("code", name(config::system_account_name).to_string())
1665 ("scope", name(account).to_string())
1666 ("table", "delband")
1667 );
1668 if (!print_json) {
1670 if ( !res.rows.empty() ) {
1671 std::cout << std::setw(13) << std::left << "Receiver" << std::setw(21) << std::left << "Net bandwidth"
1672 << std::setw(21) << std::left << "CPU bandwidth" << std::endl;
1673 for ( auto& r : res.rows ){
1674 std::cout << std::setw(13) << std::left << r["to"].as_string()
1675 << std::setw(21) << std::left << r["net_weight"].as_string()
1676 << std::setw(21) << std::left << r["cpu_weight"].as_string()
1677 << std::endl;
1678 }
1679 } else {
1680 std::cerr << "Delegated bandwidth not found" << std::endl;
1681 }
1682 } else {
1683 std::cout << fc::json::to_pretty_string(result) << std::endl;
1684 }
1685 });
1686 }
1687};
1688
1690 string from_str;
1692 string amount;
1693 bool kbytes = false;
1694 bool bytes = false;
1695
1697 auto buyram = actionRoot->add_subcommand("buyram", localized("Buy RAM"));
1698 buyram->add_option("payer", from_str, localized("The account paying for RAM"))->required();
1699 buyram->add_option("receiver", receiver_str, localized("The account receiving bought RAM"))->required();
1700 buyram->add_option("amount", amount, localized("The amount of tokens to pay for RAM, or number of bytes/kibibytes of RAM if --bytes/--kbytes is set"))->required();
1701 buyram->add_flag("--kbytes,-k", kbytes, localized("The amount to buy in kibibytes (KiB)"));
1702 buyram->add_flag("--bytes,-b", bytes, localized("The amount to buy in bytes"));
1703 add_standard_transaction_options(buyram, "payer@active");
1704 buyram->callback([this] {
1705 SYSC_ASSERT( !kbytes || !bytes, "ERROR: --kbytes and --bytes cannot be set at the same time" );
1706 if (kbytes || bytes) {
1708 } else {
1710 }
1711 });
1712 }
1713};
1714
1716 string from_str;
1719
1721 auto sellram = actionRoot->add_subcommand("sellram", localized("Sell RAM"));
1722 sellram->add_option("account", receiver_str, localized("The account to receive tokens for sold RAM"))->required();
1723 sellram->add_option("bytes", amount, localized("The amount of RAM bytes to sell"))->required();
1724 add_standard_transaction_options(sellram, "account@active");
1725
1726 sellram->callback([this] {
1728 ("account", receiver_str)
1729 ("bytes", amount);
1730 auto accountPermissions = get_account_permissions(tx_permission, {name(receiver_str), config::active_name});
1731 send_actions({create_action(accountPermissions, config::system_account_name, "sellram"_n, act_payload)});
1732 });
1733 }
1734};
1735
1737 string owner;
1738
1740 auto claim_rewards = actionRoot->add_subcommand("claimrewards", localized("Claim producer rewards"));
1741 claim_rewards->add_option("owner", owner, localized("The account to claim rewards for"))->required();
1742 add_standard_transaction_options(claim_rewards, "owner@active");
1743
1744 claim_rewards->callback([this] {
1746 ("owner", owner);
1747 auto accountPermissions = get_account_permissions(tx_permission, {name(owner), config::active_name});
1748 send_actions({create_action(accountPermissions, config::system_account_name, "claimrewards"_n, act_payload)});
1749 });
1750 }
1751};
1752
1754 string proxy;
1755
1757 auto register_proxy = actionRoot->add_subcommand("regproxy", localized("Register an account as a proxy (for voting)"));
1758 register_proxy->add_option("proxy", proxy, localized("The proxy account to register"))->required();
1759 add_standard_transaction_options(register_proxy, "proxy@active");
1760
1761 register_proxy->callback([this] {
1763 ("proxy", proxy)
1764 ("isproxy", true);
1765 auto accountPermissions = get_account_permissions(tx_permission, {name(proxy), config::active_name});
1766 send_actions({create_action(accountPermissions, config::system_account_name, "regproxy"_n, act_payload)});
1767 });
1768 }
1769};
1770
1772 string proxy;
1773
1775 auto unregister_proxy = actionRoot->add_subcommand("unregproxy", localized("Unregister an account as a proxy (for voting)"));
1776 unregister_proxy->add_option("proxy", proxy, localized("The proxy account to unregister"))->required();
1777 add_standard_transaction_options(unregister_proxy, "proxy@active");
1778
1779 unregister_proxy->callback([this] {
1781 ("proxy", proxy)
1782 ("isproxy", false);
1783 auto accountPermissions = get_account_permissions(tx_permission, {name(proxy), config::active_name});
1784 send_actions({create_action(accountPermissions, config::system_account_name, "regproxy"_n, act_payload)});
1785 });
1786 }
1787};
1788
1792 string trx_id;
1793
1795 auto cancel_delay = actionRoot->add_subcommand("canceldelay", localized("Cancel a delayed transaction"));
1796 cancel_delay->add_option("canceling_account", canceling_account, localized("Account from authorization on the original delayed transaction"))->required();
1797 cancel_delay->add_option("canceling_permission", canceling_permission, localized("Permission from authorization on the original delayed transaction"))->required();
1798 cancel_delay->add_option("trx_id", trx_id, localized("The transaction id of the original delayed transaction"))->required();
1799 add_standard_transaction_options(cancel_delay, "canceling_account@canceling_permission");
1800
1801 cancel_delay->callback([this] {
1804 ("canceling_auth", canceling_auth)
1805 ("trx_id", trx_id);
1806 auto accountPermissions = get_account_permissions(tx_permission, canceling_auth);
1807 send_actions({create_action(accountPermissions, config::system_account_name, "canceldelay"_n, act_payload)});
1808 });
1809 }
1810};
1811
1815 const name act_name{ "deposit"_n };
1816
1818 auto deposit = actionRoot->add_subcommand("deposit", localized("Deposit into owner's REX fund by transfering from owner's liquid token balance"));
1819 deposit->add_option("owner", owner_str, localized("Account which owns the REX fund"))->required();
1820 deposit->add_option("amount", amount_str, localized("Amount to be deposited into REX fund"))->required();
1821 add_standard_transaction_options(deposit, "owner@active");
1822 deposit->callback([this] {
1824 ("owner", owner_str)
1825 ("amount", amount_str);
1826 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
1827 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1828 });
1829 }
1830};
1831
1835 const name act_name{ "withdraw"_n };
1836
1838 auto withdraw = actionRoot->add_subcommand("withdraw", localized("Withdraw from owner's REX fund by transfering to owner's liquid token balance"));
1839 withdraw->add_option("owner", owner_str, localized("Account which owns the REX fund"))->required();
1840 withdraw->add_option("amount", amount_str, localized("Amount to be withdrawn from REX fund"))->required();
1841 add_standard_transaction_options(withdraw, "owner@active");
1842 withdraw->callback([this] {
1844 ("owner", owner_str)
1845 ("amount", amount_str);
1846 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
1847 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1848 });
1849 }
1850};
1851
1853 string from_str;
1855 const name act_name{ "buyrex"_n };
1856
1858 auto buyrex = actionRoot->add_subcommand("buyrex", localized("Buy REX using tokens in owner's REX fund"));
1859 buyrex->add_option("from", from_str, localized("Account buying REX tokens"))->required();
1860 buyrex->add_option("amount", amount_str, localized("Amount to be taken from REX fund and used in buying REX"))->required();
1861 add_standard_transaction_options(buyrex, "from@active");
1862 buyrex->callback([this] {
1864 ("from", from_str)
1865 ("amount", amount_str);
1866 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
1867 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1868 });
1869 }
1870};
1871
1873 string from_str;
1875 const name act_name1{ "deposit"_n };
1876 const name act_name2{ "buyrex"_n };
1877
1879 auto lendrex = actionRoot->add_subcommand("lendrex", localized("Deposit tokens to REX fund and use the tokens to buy REX"));
1880 lendrex->add_option("from", from_str, localized("Account buying REX tokens"))->required();
1881 lendrex->add_option("amount", amount_str, localized("Amount of liquid tokens to be used in buying REX"))->required();
1882 add_standard_transaction_options(lendrex, "from@active");
1883 lendrex->callback([this] {
1885 ("owner", from_str)
1886 ("amount", amount_str);
1888 ("from", from_str)
1889 ("amount", amount_str);
1890 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
1891 send_actions({create_action(accountPermissions, config::system_account_name, act_name1, act_payload1),
1892 create_action(accountPermissions, config::system_account_name, act_name2, act_payload2)});
1893 });
1894 }
1895};
1896
1902 const name act_name{ "unstaketorex"_n };
1903
1905 auto unstaketorex = actionRoot->add_subcommand("unstaketorex", localized("Buy REX using staked tokens"));
1906 unstaketorex->add_option("owner", owner_str, localized("Account buying REX tokens"))->required();
1907 unstaketorex->add_option("receiver", receiver_str, localized("Account that tokens have been staked to"))->required();
1908 unstaketorex->add_option("from_net", from_net_str, localized("Amount to be unstaked from Net resources and used in REX purchase"))->required();
1909 unstaketorex->add_option("from_cpu", from_cpu_str, localized("Amount to be unstaked from CPU resources and used in REX purchase"))->required();
1910 add_standard_transaction_options(unstaketorex, "owner@active");
1911 unstaketorex->callback([this] {
1913 ("owner", owner_str)
1914 ("receiver", receiver_str)
1915 ("from_net", from_net_str)
1916 ("from_cpu", from_cpu_str);
1917 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
1918 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1919 });
1920 }
1921};
1922
1924 string from_str;
1925 string rex_str;
1926 const name act_name{ "sellrex"_n };
1927
1929 auto sellrex = actionRoot->add_subcommand("sellrex", localized("Sell REX tokens"));
1930 sellrex->add_option("from", from_str, localized("Account selling REX tokens"))->required();
1931 sellrex->add_option("rex", rex_str, localized("Amount of REX tokens to be sold"))->required();
1932 add_standard_transaction_options(sellrex, "from@active");
1933 sellrex->callback([this] {
1935 ("from", from_str)
1936 ("rex", rex_str);
1937 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
1938 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1939 });
1940 }
1941};
1942
1945 const name act_name{ "cnclrexorder"_n };
1946
1948 auto cancelrexorder = actionRoot->add_subcommand("cancelrexorder", localized("Cancel queued REX sell order if one exists"));
1949 cancelrexorder->add_option("owner", owner_str, localized("Owner account of sell order"))->required();
1950 add_standard_transaction_options(cancelrexorder, "owner@active");
1951 cancelrexorder->callback([this] {
1952 fc::variant act_payload = fc::mutable_variant_object()("owner", owner_str);
1953 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
1954 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1955 });
1956 }
1957};
1958
1960 string from_str;
1964 const name act_name{ "rentcpu"_n };
1965
1967 auto rentcpu = actionRoot->add_subcommand("rentcpu", localized("Rent CPU bandwidth for 30 days"));
1968 rentcpu->add_option("from", from_str, localized("Account paying rent fees"))->required();
1969 rentcpu->add_option("receiver", receiver_str, localized("Account to whom rented CPU bandwidth is staked"))->required();
1970 rentcpu->add_option("loan_payment", loan_payment_str, localized("Loan fee to be paid, used to calculate amount of rented bandwidth"))->required();
1971 rentcpu->add_option("loan_fund", loan_fund_str, localized("Loan fund to be used in automatic renewal, can be 0 tokens"))->required();
1972 add_standard_transaction_options(rentcpu, "from@active");
1973 rentcpu->callback([this] {
1975 ("from", from_str)
1976 ("receiver", receiver_str)
1977 ("loan_payment", loan_payment_str)
1978 ("loan_fund", loan_fund_str);
1979 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
1980 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
1981 });
1982 }
1983};
1984
1986 string from_str;
1990 const name act_name{ "rentnet"_n };
1991
1993 auto rentnet = actionRoot->add_subcommand("rentnet", localized("Rent Network bandwidth for 30 days"));
1994 rentnet->add_option("from", from_str, localized("Account paying rent fees"))->required();
1995 rentnet->add_option("receiver", receiver_str, localized("Account to whom rented Network bandwidth is staked"))->required();
1996 rentnet->add_option("loan_payment", loan_payment_str, localized("Loan fee to be paid, used to calculate amount of rented bandwidth"))->required();
1997 rentnet->add_option("loan_fund", loan_fund_str, localized("Loan fund to be used in automatic renewal, can be 0 tokens"))->required();
1998 add_standard_transaction_options(rentnet, "from@active");
1999 rentnet->callback([this] {
2001 ("from", from_str)
2002 ("receiver", receiver_str)
2003 ("loan_payment", loan_payment_str)
2004 ("loan_fund", loan_fund_str);
2005 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
2006 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2007 });
2008 }
2009};
2010
2012 string from_str;
2015 const name act_name{ "fundcpuloan"_n };
2016
2018 auto fundcpuloan = actionRoot->add_subcommand("fundcpuloan", localized("Deposit into a CPU loan fund"));
2019 fundcpuloan->add_option("from", from_str, localized("Loan owner"))->required();
2020 fundcpuloan->add_option("loan_num", loan_num_str, localized("Loan ID"))->required();
2021 fundcpuloan->add_option("payment", payment_str, localized("Amount to be deposited"))->required();
2022 add_standard_transaction_options(fundcpuloan, "from@active");
2023 fundcpuloan->callback([this] {
2025 ("from", from_str)
2026 ("loan_num", loan_num_str)
2027 ("payment", payment_str);
2028 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
2029 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2030 });
2031 }
2032};
2033
2035 string from_str;
2038 const name act_name{ "fundnetloan"_n };
2039
2041 auto fundnetloan = actionRoot->add_subcommand("fundnetloan", localized("Deposit into a Network loan fund"));
2042 fundnetloan->add_option("from", from_str, localized("Loan owner"))->required();
2043 fundnetloan->add_option("loan_num", loan_num_str, localized("Loan ID"))->required();
2044 fundnetloan->add_option("payment", payment_str, localized("Amount to be deposited"))->required();
2045 add_standard_transaction_options(fundnetloan, "from@active");
2046 fundnetloan->callback([this] {
2048 ("from", from_str)
2049 ("loan_num", loan_num_str)
2050 ("payment", payment_str);
2051 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
2052 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2053 });
2054 }
2055};
2056
2058 string from_str;
2061 const name act_name{ "defcpuloan"_n };
2062
2064 auto defcpuloan = actionRoot->add_subcommand("defundcpuloan", localized("Withdraw from a CPU loan fund"));
2065 defcpuloan->add_option("from", from_str, localized("Loan owner"))->required();
2066 defcpuloan->add_option("loan_num", loan_num_str, localized("Loan ID"))->required();
2067 defcpuloan->add_option("amount", amount_str, localized("Amount to be withdrawn"))->required();
2068 add_standard_transaction_options(defcpuloan, "from@active");
2069 defcpuloan->callback([this] {
2071 ("from", from_str)
2072 ("loan_num", loan_num_str)
2073 ("amount", amount_str);
2074 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
2075 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2076 });
2077 }
2078};
2079
2081 string from_str;
2084 const name act_name{ "defnetloan"_n };
2085
2087 auto defnetloan = actionRoot->add_subcommand("defundnetloan", localized("Withdraw from a Network loan fund"));
2088 defnetloan->add_option("from", from_str, localized("Loan owner"))->required();
2089 defnetloan->add_option("loan_num", loan_num_str, localized("Loan ID"))->required();
2090 defnetloan->add_option("amount", amount_str, localized("Amount to be withdrawn"))->required();
2091 add_standard_transaction_options(defnetloan, "from@active");
2092 defnetloan->callback([this] {
2094 ("from", from_str)
2095 ("loan_num", loan_num_str)
2096 ("amount", amount_str);
2097 auto accountPermissions = get_account_permissions(tx_permission, {name(from_str), config::active_name});
2098 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2099 });
2100 }
2101};
2102
2105 string rex_str;
2106 const name act_name{ "mvtosavings"_n };
2107
2109 auto mvtosavings = actionRoot->add_subcommand("mvtosavings", localized("Move REX tokens to savings bucket"));
2110 mvtosavings->add_option("owner", owner_str, localized("REX owner"))->required();
2111 mvtosavings->add_option("rex", rex_str, localized("Amount of REX to be moved to savings bucket"))->required();
2112 add_standard_transaction_options(mvtosavings, "owner@active");
2113 mvtosavings->callback([this] {
2115 ("owner", owner_str)
2116 ("rex", rex_str);
2117 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
2118 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2119 });
2120 }
2121};
2122
2125 string rex_str;
2126 const name act_name{ "mvfrsavings"_n };
2127
2129 auto mvfrsavings = actionRoot->add_subcommand("mvfromsavings", localized("Move REX tokens out of savings bucket"));
2130 mvfrsavings->add_option("owner", owner_str, localized("REX owner"))->required();
2131 mvfrsavings->add_option("rex", rex_str, localized("Amount of REX to be moved out of savings bucket"))->required();
2132 add_standard_transaction_options(mvfrsavings, "owner@active");
2133 mvfrsavings->callback([this] {
2135 ("owner", owner_str)
2136 ("rex", rex_str);
2137 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
2138 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2139 });
2140 }
2141};
2142
2145 const name act_name{ "updaterex"_n };
2146
2148 auto updaterex = actionRoot->add_subcommand("updaterex", localized("Update REX owner vote stake and vote weight"));
2149 updaterex->add_option("owner", owner_str, localized("REX owner"))->required();
2150 add_standard_transaction_options(updaterex, "owner@active");
2151 updaterex->callback([this] {
2152 fc::variant act_payload = fc::mutable_variant_object()("owner", owner_str);
2153 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
2154 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2155 });
2156 }
2157};
2158
2161 const name act_name{ "consolidate"_n };
2162
2164 auto consolidate = actionRoot->add_subcommand("consolidate", localized("Consolidate REX maturity buckets into one that matures in 4 days"));
2165 consolidate->add_option("owner", owner_str, localized("REX owner"))->required();
2166 add_standard_transaction_options(consolidate, "owner@active");
2167 consolidate->callback([this] {
2168 fc::variant act_payload = fc::mutable_variant_object()("owner", owner_str);
2169 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
2170 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2171 });
2172 }
2173};
2174
2176 string user_str;
2177 string max_str;
2178 const name act_name{ "rexexec"_n };
2179
2181 auto rexexec = actionRoot->add_subcommand("rexexec", localized("Perform REX maintenance by processing expired loans and unfilled sell orders"));
2182 rexexec->add_option("user", user_str, localized("User executing the action"))->required();
2183 rexexec->add_option("max", max_str, localized("Maximum number of CPU loans, Network loans, and sell orders to be processed"))->required();
2184 add_standard_transaction_options(rexexec, "user@active");
2185 rexexec->callback([this] {
2187 ("user", user_str)
2188 ("max", max_str);
2189 auto accountPermissions = get_account_permissions(tx_permission, {name(user_str), config::active_name});
2190 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2191 });
2192 }
2193};
2194
2197 const name act_name{ "closerex"_n };
2198
2200 auto closerex = actionRoot->add_subcommand("closerex", localized("Delete unused REX-related user table entries"));
2201 closerex->add_option("owner", owner_str, localized("REX owner"))->required();
2202 add_standard_transaction_options(closerex, "owner@active");
2203 closerex->callback([this] {
2204 fc::variant act_payload = fc::mutable_variant_object()("owner", owner_str);
2205 auto accountPermissions = get_account_permissions(tx_permission, {name(owner_str), config::active_name});
2206 send_actions({create_action(accountPermissions, config::system_account_name, act_name, act_payload)});
2207 });
2208 }
2209};
2210
2211void get_account( const string& accountName, const string& coresym, bool json_format ) {
2213 if (coresym.empty()) {
2214 json = call(get_account_func, fc::mutable_variant_object("account_name", accountName));
2215 }
2216 else {
2217 json = call(get_account_func, fc::mutable_variant_object("account_name", accountName)("expected_core_symbol", symbol::from_string(coresym)));
2218 }
2219
2221 if (!json_format) {
2222 asset staked;
2223 asset unstaking;
2224
2225 if( res.core_liquid_balance ) {
2226 unstaking = asset( 0, res.core_liquid_balance->get_symbol() ); // Correct core symbol for unstaking asset.
2227 staked = asset( 0, res.core_liquid_balance->get_symbol() ); // Correct core symbol for staked asset.
2228 }
2229
2230 std::cout << "created: " << string(res.created) << std::endl;
2231
2232 if(res.privileged) std::cout << "privileged: true" << std::endl;
2233
2234 constexpr size_t indent_size = 5;
2235 const string indent(indent_size, ' ');
2236
2237 std::cout << "permissions: " << std::endl;
2238 unordered_map<name, vector<name>/*children*/> tree;
2239 vector<name> roots; //we don't have multiple roots, but we can easily handle them here, so let's do it just in case
2240 unordered_map<name, sysio::chain_apis::permission> cache;
2241 for ( auto& perm : res.permissions ) {
2242 if ( perm.parent ) {
2243 tree[perm.parent].push_back( perm.perm_name );
2244 } else {
2245 roots.push_back( perm.perm_name );
2246 }
2247 auto name = perm.perm_name; //keep copy before moving `perm`, since thirst argument of emplace can be evaluated first
2248 // looks a little crazy, but should be efficient
2249 cache.insert( std::make_pair(name, std::move(perm)) );
2250 }
2251
2252 using dfs_fn_t = std::function<void (const sysio::chain_apis::permission&, int)>;
2253 std::function<void (account_name, int, dfs_fn_t&)> dfs_exec = [&]( account_name name, int depth, dfs_fn_t& f ) -> void {
2254 auto& p = cache.at(name);
2255
2256 f(p, depth);
2257 auto it = tree.find( name );
2258 if (it != tree.end()) {
2259 auto& children = it->second;
2260 sort( children.begin(), children.end() );
2261 for ( auto& n : children ) {
2262 // we have a tree, not a graph, so no need to check for already visited nodes
2263 dfs_exec( n, depth+1, f );
2264 }
2265 } // else it's a leaf node
2266 };
2267
2268 dfs_fn_t print_auth = [&]( const sysio::chain_apis::permission& p, int depth ) -> void {
2269 std::cout << indent << std::string(depth*3, ' ') << p.perm_name << ' ' << std::setw(5) << p.required_auth.threshold << ": ";
2270
2271 const char *sep = "";
2272 for ( auto it = p.required_auth.keys.begin(); it != p.required_auth.keys.end(); ++it ) {
2273 std::cout << sep << it->weight << ' ' << it->key.to_string();
2274 sep = ", ";
2275 }
2276 for ( auto& acc : p.required_auth.accounts ) {
2277 std::cout << sep << acc.weight << ' ' << acc.permission.actor.to_string() << '@' << acc.permission.permission.to_string();
2278 sep = ", ";
2279 }
2280 std::cout << std::endl;
2281 };
2282 std::sort(roots.begin(), roots.end());
2283 for ( auto r : roots ) {
2284 dfs_exec( r, 0, print_auth );
2285 }
2286 std::cout << std::endl;
2287
2288 std::cout << "permission links: " << std::endl;
2289 dfs_fn_t print_links = [&](const sysio::chain_apis::permission& p, int) -> void {
2290 if (p.linked_actions) {
2291 if (!p.linked_actions->empty()) {
2292 std::cout << indent << p.perm_name.to_string() + ":" << std::endl;
2293 for ( auto it = p.linked_actions->begin(); it != p.linked_actions->end(); ++it ) {
2294 auto action_value = it->action ? it->action->to_string() : std::string("*");
2295 std::cout << indent << indent << it->account << "::" << action_value << std::endl;
2296 }
2297 }
2298 }
2299 };
2300
2301 for ( auto r : roots ) {
2302 dfs_exec( r, 0, print_links);
2303 }
2304
2305 // print linked actions
2306 std::cout << indent << "sysio.any: " << std::endl;
2307 for (const auto& it : res.sysio_any_linked_actions) {
2308 auto action_value = it.action ? it.action->to_string() : std::string("*");
2309 std::cout << indent << indent << it.account << "::" << action_value << std::endl;
2310 }
2311
2312 std::cout << std::endl;
2313
2314 auto to_pretty_net = []( int64_t nbytes, uint8_t width_for_units = 5 ) {
2315 if(nbytes == -1) {
2316 // special case. Treat it as unlimited
2317 return std::string("unlimited");
2318 }
2319
2320 string unit = "bytes";
2321 double bytes = static_cast<double> (nbytes);
2322 if (bytes >= 1024 * 1024 * 1024 * 1024ll) {
2323 unit = "TiB";
2324 bytes /= 1024 * 1024 * 1024 * 1024ll;
2325 } else if (bytes >= 1024 * 1024 * 1024) {
2326 unit = "GiB";
2327 bytes /= 1024 * 1024 * 1024;
2328 } else if (bytes >= 1024 * 1024) {
2329 unit = "MiB";
2330 bytes /= 1024 * 1024;
2331 } else if (bytes >= 1024) {
2332 unit = "KiB";
2333 bytes /= 1024;
2334 }
2335 std::stringstream ss;
2336 ss << setprecision(4);
2337 ss << bytes << " ";
2338 if( width_for_units > 0 )
2339 ss << std::left << setw( width_for_units );
2340 ss << unit;
2341 return ss.str();
2342 };
2343
2344
2345
2346 std::cout << "memory: " << std::endl
2347 << indent << "quota: " << std::setw(15) << to_pretty_net(res.ram_quota) << " used: " << std::setw(15) << to_pretty_net(res.ram_usage) << std::endl << std::endl;
2348
2349 std::cout << "net bandwidth: " << std::endl;
2350 if ( res.total_resources.is_object() ) {
2351 auto net_total = to_asset(res.total_resources.get_object()["net_weight"].as_string());
2352
2353 if( net_total.get_symbol() != unstaking.get_symbol() ) {
2354 // Core symbol of nodeop responding to the request is different than core symbol built into clio
2355 unstaking = asset( 0, net_total.get_symbol() ); // Correct core symbol for unstaking asset.
2356 staked = asset( 0, net_total.get_symbol() ); // Correct core symbol for staked asset.
2357 }
2358
2359 if( res.self_delegated_bandwidth.is_object() ) {
2360 asset net_own = asset::from_string( res.self_delegated_bandwidth.get_object()["net_weight"].as_string() );
2361 staked = net_own;
2362
2363 auto net_others = net_total - net_own;
2364
2365 std::cout << indent << "staked:" << std::setw(20) << net_own
2366 << std::string(11, ' ') << "(total stake delegated from account to self)" << std::endl
2367 << indent << "delegated:" << std::setw(17) << net_others
2368 << std::string(11, ' ') << "(total staked delegated to account from others)" << std::endl;
2369 }
2370 else {
2371 auto net_others = net_total;
2372 std::cout << indent << "delegated:" << std::setw(17) << net_others
2373 << std::string(11, ' ') << "(total staked delegated to account from others)" << std::endl;
2374 }
2375 }
2376
2377
2378 auto to_pretty_time = []( int64_t nmicro, uint8_t width_for_units = 5 ) {
2379 if(nmicro == -1) {
2380 // special case. Treat it as unlimited
2381 return std::string("unlimited");
2382 }
2383 string unit = "us";
2384 double micro = static_cast<double>(nmicro);
2385
2386 if( micro > 1000000*60*60ll ) {
2387 micro /= 1000000*60*60ll;
2388 unit = "hr";
2389 }
2390 else if( micro > 1000000*60 ) {
2391 micro /= 1000000*60;
2392 unit = "min";
2393 }
2394 else if( micro > 1000000 ) {
2395 micro /= 1000000;
2396 unit = "sec";
2397 }
2398 else if( micro > 1000 ) {
2399 micro /= 1000;
2400 unit = "ms";
2401 }
2402 std::stringstream ss;
2403 ss << setprecision(4);
2404 ss << micro << " ";
2405 if( width_for_units > 0 )
2406 ss << std::left << setw( width_for_units );
2407 ss << unit;
2408 return ss.str();
2409 };
2410
2411
2412 std::cout << std::fixed << setprecision(3);
2413 std::cout << indent << std::left << std::setw(11) << "used:" << std::right << std::setw(18) << to_pretty_net( res.net_limit.used ) << "\n";
2414 std::cout << indent << std::left << std::setw(11) << "available:" << std::right << std::setw(18) << to_pretty_net( res.net_limit.available ) << "\n";
2415 std::cout << indent << std::left << std::setw(11) << "limit:" << std::right << std::setw(18) << to_pretty_net( res.net_limit.max ) << "\n";
2416 std::cout << std::endl;
2417
2418 std::cout << "cpu bandwidth:" << std::endl;
2419
2420 if ( res.total_resources.is_object() ) {
2421 auto cpu_total = to_asset(res.total_resources.get_object()["cpu_weight"].as_string());
2422
2423 if( res.self_delegated_bandwidth.is_object() ) {
2424 asset cpu_own = asset::from_string( res.self_delegated_bandwidth.get_object()["cpu_weight"].as_string() );
2425 staked += cpu_own;
2426
2427 auto cpu_others = cpu_total - cpu_own;
2428
2429 std::cout << indent << "staked:" << std::setw(20) << cpu_own
2430 << std::string(11, ' ') << "(total stake delegated from account to self)" << std::endl
2431 << indent << "delegated:" << std::setw(17) << cpu_others
2432 << std::string(11, ' ') << "(total staked delegated to account from others)" << std::endl;
2433 } else {
2434 auto cpu_others = cpu_total;
2435 std::cout << indent << "delegated:" << std::setw(17) << cpu_others
2436 << std::string(11, ' ') << "(total staked delegated to account from others)" << std::endl;
2437 }
2438 }
2439
2440 std::cout << std::fixed << setprecision(3);
2441 std::cout << indent << std::left << std::setw(11) << "used:" << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.used ) << "\n";
2442 std::cout << indent << std::left << std::setw(11) << "available:" << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.available ) << "\n";
2443 std::cout << indent << std::left << std::setw(11) << "limit:" << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.max ) << "\n";
2444 std::cout << std::endl;
2445
2446 if( res.subjective_cpu_bill_limit ) {
2447 std::cout << "subjective cpu bandwidth:" << std::endl;
2448 std::cout << indent << std::left << std::setw(11) << "used:" << std::right << std::setw(18) << to_pretty_time( (res.subjective_cpu_bill_limit)->used ) << "\n";
2449 std::cout << std::endl;
2450 }
2451
2452 if( res.refund_request.is_object() ) {
2453 auto obj = res.refund_request.get_object();
2454 auto request_time = fc::time_point_sec::from_iso_string( obj["request_time"].as_string() );
2455 fc::time_point refund_time = request_time + fc::days(3);
2456 auto now = res.head_block_time;
2457 asset net = asset::from_string( obj["net_amount"].as_string() );
2458 asset cpu = asset::from_string( obj["cpu_amount"].as_string() );
2459 unstaking = net + cpu;
2460
2461 if( unstaking > asset( 0, unstaking.get_symbol() ) ) {
2462 std::cout << std::fixed << setprecision(3);
2463 std::cout << "unstaking tokens:" << std::endl;
2464 std::cout << indent << std::left << std::setw(25) << "time of unstake request:" << std::right << std::setw(20) << string(request_time);
2465 if( now >= refund_time ) {
2466 std::cout << " (available to claim now with 'sysio::refund' action)\n";
2467 } else {
2468 std::cout << " (funds will be available in " << to_pretty_time( (refund_time - now).count(), 0 ) << ")\n";
2469 }
2470 std::cout << indent << std::left << std::setw(25) << "from net bandwidth:" << std::right << std::setw(18) << net << std::endl;
2471 std::cout << indent << std::left << std::setw(25) << "from cpu bandwidth:" << std::right << std::setw(18) << cpu << std::endl;
2472 std::cout << indent << std::left << std::setw(25) << "total:" << std::right << std::setw(18) << unstaking << std::endl;
2473 std::cout << std::endl;
2474 }
2475 }
2476
2477 if( res.core_liquid_balance ) {
2478 std::cout << res.core_liquid_balance->get_symbol().name() << " balances: " << std::endl;
2479 std::cout << indent << std::left << std::setw(11)
2480 << "liquid:" << std::right << std::setw(18) << *res.core_liquid_balance << std::endl;
2481 std::cout << indent << std::left << std::setw(11)
2482 << "staked:" << std::right << std::setw(18) << staked << std::endl;
2483 std::cout << indent << std::left << std::setw(11)
2484 << "unstaking:" << std::right << std::setw(18) << unstaking << std::endl;
2485 std::cout << indent << std::left << std::setw(11) << "total:" << std::right << std::setw(18) << (*res.core_liquid_balance + staked + unstaking) << std::endl;
2486 std::cout << std::endl;
2487 }
2488
2489 if( res.rex_info.is_object() ) {
2490 auto& obj = res.rex_info.get_object();
2491 asset vote_stake = asset::from_string( obj["vote_stake"].as_string() );
2492 asset rex_balance = asset::from_string( obj["rex_balance"].as_string() );
2493 std::cout << rex_balance.get_symbol().name() << " balances: " << std::endl;
2494 std::cout << indent << std::left << std::setw(11)
2495 << "balance:" << std::right << std::setw(18) << rex_balance << std::endl;
2496 std::cout << indent << std::left << std::setw(11)
2497 << "staked:" << std::right << std::setw(18) << vote_stake << std::endl;
2498 std::cout << std::endl;
2499 }
2500
2501 if ( res.voter_info.is_object() ) {
2502 auto& obj = res.voter_info.get_object();
2503 string proxy = obj["proxy"].as_string();
2504 if ( proxy.empty() ) {
2505 auto& prods = obj["producers"].get_array();
2506 std::cout << "producers:";
2507 if ( !prods.empty() ) {
2508 for ( size_t i = 0; i < prods.size(); ++i ) {
2509 if ( i%3 == 0 ) {
2510 std::cout << std::endl << indent;
2511 }
2512 std::cout << std::setw(16) << std::left << prods[i].as_string();
2513 }
2514 std::cout << std::endl;
2515 } else {
2516 std::cout << indent << "<not voted>" << std::endl;
2517 }
2518 } else {
2519 std::cout << "proxy:" << indent << proxy << std::endl;
2520 }
2521 }
2522 std::cout << std::endl;
2523 } else {
2524 std::cout << fc::json::to_pretty_string(json) << std::endl;
2525 }
2526}
2527
2530
2531 for (itr = res.begin(); itr != res.end(); itr++) {
2532 headers.push_back(*itr);
2533 }
2534
2535 return true;
2536};
2537
2538int main( int argc, char** argv ) {
2542
2543 CLI::App app{"Command Line Interface to SYSIO Client"};
2544 app.require_subcommand();
2545 // Hide obsolete options by putting them into a group with an empty name.
2546 app.add_option( "-H,--host", obsoleted_option_host_port, localized("The host where ${n} is running", ("n", node_executable_name)) )->group("");
2547 app.add_option( "-p,--port", obsoleted_option_host_port, localized("The port where ${n} is running", ("n", node_executable_name)) )->group("");
2548 app.add_option( "--wallet-host", obsoleted_option_host_port, localized("The host where ${k} is running", ("k", key_store_executable_name)) )->group("");
2549 app.add_option( "--wallet-port", obsoleted_option_host_port, localized("The port where ${k} is running", ("k", key_store_executable_name)) )->group("");
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
2554 app.add_option( "-r,--header", header_opt_callback, localized("Pass specific HTTP header; repeat this option to pass multiple headers"));
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)));
2557 app.parse_complete_callback([&app]{ ensure_kiod_running(&app);});
2558
2559 app.add_flag( "-v,--verbose", verbose, localized("Output verbose errors and action console output"));
2560 app.add_flag("--print-request", print_request, localized("Print HTTP request to STDERR"));
2561 app.add_flag("--print-response", print_response, localized("Print HTTP response to STDERR"));
2562
2563 auto version = app.add_subcommand("version", localized("Retrieve version information"));
2564 version->require_subcommand();
2565
2566 version->add_subcommand("client", localized("Retrieve basic version information of the client"))->callback([] {
2567 std::cout << sysio::version::version_client() << '\n';
2568 });
2569
2570 version->add_subcommand("full", localized("Retrieve full version information of the client"))->callback([] {
2571 std::cout << sysio::version::version_full() << '\n';
2572 });
2573
2574 // Create subcommand
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 // create key
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 // create account
2606 auto createAccount = create_account_subcommand( create, true /*simple*/ );
2607
2608 // convert subcommand
2609 auto convert = app.add_subcommand("convert", localized("Pack and unpack transactions")); // TODO also add converting action args based on abi from here ?
2610 convert->require_subcommand();
2611
2612 // pack transaction
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([&] {
2619 fc::variant trx_var = json_from_file_or_string( plain_signed_transaction_json );
2620 if( pack_action_data_flag ) {
2622 try {
2624 } SYS_RETHROW_EXCEPTIONS( transaction_type_exception, "Invalid transaction format: '${data}'",
2625 ("data", fc::json::to_string(trx_var, fc::time_point::maximum())))
2627 } else {
2628 try {
2629 signed_transaction trx = trx_var.as<signed_transaction>();
2631 } SYS_RETHROW_EXCEPTIONS( transaction_type_exception, "Fail to convert transaction, --pack-action-data likely needed" )
2632 }
2633 });
2634
2635 // unpack transaction
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([&] {
2642 fc::variant packed_trx_var = json_from_file_or_string( packed_transaction_json );
2643 packed_transaction packed_trx;
2644 try {
2645 fc::from_variant<packed_transaction>( packed_trx_var, packed_trx );
2646 } SYS_RETHROW_EXCEPTIONS( transaction_type_exception, "Invalid packed transaction format: '${data}'",
2647 ("data", fc::json::to_string(packed_trx_var, fc::time_point::maximum())))
2648 signed_transaction strx = packed_trx.get_signed_transaction();
2649 fc::variant trx_var;
2650 if( unpack_action_data_flag ) {
2652 } else {
2653 trx_var = strx;
2654 }
2655 std::cout << fc::json::to_pretty_string( trx_var ) << std::endl;
2656 });
2657
2658 // pack action data
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([&] {
2667 fc::variant unpacked_action_data_json = json_from_file_or_string(unpacked_action_data_string);
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);
2671 } SYS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse 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 // unpack action data
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());
2687 fc::variant unpacked_action_data_json = bin_to_variant(name(packed_action_data_account_string), name(packed_action_data_name_string), packed_action_data_blob);
2688 std::cout << fc::json::to_pretty_string(unpacked_action_data_json) << std::endl;
2689 });
2690
2691 // Get subcommand
2692 auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"));
2693 get->require_subcommand();
2694
2695 // get info
2696 get->add_subcommand("info", localized("Get current blockchain information"))->callback([] {
2697 std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
2698 });
2699
2700 // get transaction status
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 }
2711 auto arg= fc::mutable_variant_object( "id", status_transaction_id);
2712 std::cout << fc::json::to_pretty_string(call(get_transaction_status_func, arg)) << std::endl;
2713 });
2714
2715 // get block
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 {
2728 block_num = fc::to_int64(blockArg);
2729 } catch (...) {
2730 // error is handled in assertion below
2731 }
2732 SYSC_ASSERT( block_num.has_value() && (*block_num > 0), "Invalid block num: ${block_num}", ("block_num", blockArg) );
2733 const auto arg = fc::variant_object("block_num", static_cast<uint32_t>(*block_num));
2734 std::cout << fc::json::to_pretty_string(call(get_block_info_func, arg)) << std::endl;
2735 } else {
2736 const auto arg = fc::variant_object("block_num_or_id", blockArg);
2737 if (get_bhs) {
2738 std::cout << fc::json::to_pretty_string(call(get_block_header_state_func, arg)) << std::endl;
2739 } else {
2740 std::cout << fc::json::to_pretty_string(call(get_block_func, arg)) << std::endl;
2741 }
2742 }
2743 });
2744
2745 // get account
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 // get code
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 {
2767 const auto result = call(get_raw_code_and_abi_func, fc::mutable_variant_object("account_name", accountName));
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
2771 fc::sha256 hash;
2772 if(wasm_v.size())
2773 hash = fc::sha256::hash(wasm_v.data(), wasm_v.size());
2774 code_hash = (string)hash;
2775
2776 wasm = string(wasm_v.begin(), wasm_v.end());
2777
2778 abi_def abi_d;
2779 if(abi_serializer::to_abi(abi_v, abi_d))
2780 abi = fc::json::to_pretty_string(abi_d);
2781 }
2782 catch(chain::missing_chain_api_plugin_exception&) {
2783 //see if this is an old nodeop that doesn't support get_raw_code_and_abi
2784 const auto old_result = call(get_code_func, fc::mutable_variant_object("account_name", accountName)("code_as_wasm",code_as_wasm));
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;
2788 abi = fc::json::to_pretty_string(old_result["abi"]);
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 // get abi
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([&] {
2812 auto result = call(get_abi_func, fc::mutable_variant_object("account_name", accountName));
2813
2814 auto abi = fc::json::to_pretty_string( result["abi"] );
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 // get table
2825 string scope;
2826 string code;
2827 string table;
2828 string lower;
2829 string upper;
2830 string table_key;
2831 string key_type;
2832 string encode_type{"dec"};
2833 bool binary = false;
2834 uint32_t limit = 10;
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([&] {
2861 auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
2862 ("code",code)
2863 ("scope",scope)
2864 ("table",table)
2865 ("table_key",table_key) // not used
2866 ("lower_bound",lower)
2867 ("upper_bound",upper)
2868 ("limit",limit)
2869 ("key_type",key_type)
2870 ("index_position", index_position)
2871 ("encode_type", encode_type)
2872 ("reverse", reverse)
2873 ("show_payer", show_payer)
2874 );
2875
2876 std::cout << fc::json::to_pretty_string(result)
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([&] {
2888 auto result = call(get_table_by_scope_func, fc::mutable_variant_object("code",code)
2889 ("table",table)
2890 ("lower_bound",lower)
2891 ("upper_bound",upper)
2892 ("limit",limit)
2893 ("reverse", reverse)
2894 );
2895 std::cout << fc::json::to_pretty_string(result)
2896 << std::endl;
2897 });
2898
2899 // currency accessors
2900 // get currency balance
2901 string symbol;
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)
2914 ("symbol", symbol.empty() ? fc::variant() : symbol)
2915 );
2916 if (!currency_balance_print_json) {
2917 const auto& rows = result.get_array();
2918 for( const auto& r : rows ) {
2919 std::cout << clean_output( r.as_string() ) << std::endl;
2920 }
2921 } else {
2922 std::cout << fc::json::to_pretty_string(result) << std::endl;
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([&] {
2930 auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
2931 ("code", code)
2932 ("symbol", symbol)
2933 );
2934
2935 std::cout << fc::json::to_pretty_string(result)
2936 << std::endl;
2937 });
2938
2939 // get accounts
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 {
2946 public_key = public_key_type(public_key_str);
2947 } SYS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", public_key_str))
2948 auto arg = fc::mutable_variant_object( "public_key", public_key);
2949 std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
2950 });
2951
2952
2953 // get servants
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([&] {
2958 auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
2959 std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
2960 });
2961
2962 // get transaction (history api plugin)
2963 string transaction_id_str;
2964 uint32_t block_num_hint = 0;
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([&] {
2969 auto arg= fc::mutable_variant_object( "id", transaction_id_str);
2970 if ( block_num_hint > 0 ) {
2971 arg = arg("block_num_hint", block_num_hint);
2972 }
2973 std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
2974 });
2975
2976 // get transaction_trace (trace api plugin)
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([&] {
2980 auto arg= fc::mutable_variant_object( "id", transaction_id_str);
2981 std::cout << fc::json::to_pretty_string(call(get_transaction_trace_func, arg)) << std::endl;
2982 });
2983
2984 // get block_trace
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([&] {
2990 auto arg= fc::mutable_variant_object( "block_num", blockNum);
2991 std::cout << fc::json::to_pretty_string(call(get_block_trace_func, arg)) << std::endl;
2992 });
2993
2994 // get actions
2995 string account_name;
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
3003 int32_t pos_seq = -1;
3004 int32_t offset = -20;
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([&] {
3015 arg( "account_name", account_name );
3016 arg( "pos", pos_seq );
3017 arg( "offset", offset);
3018
3019 auto result = call(get_actions_func, arg);
3020
3021
3022 if( printjson ) {
3023 std::cout << fc::json::to_pretty_string(result) << std::endl;
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 ) {
3051 args = fc::json::to_pretty_string( act["data"] );
3052 }
3053 else {
3054 args = fc::json::to_string( act["data"], fc::time_point::maximum() );
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 ;//<< "\n";
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 }
3084 cerr << sout.str(); //ilog( "\r${m} ", ("m",out.str()) );
3085 }
3086 }
3087 }
3088 }
3089 });
3090
3091 auto getSchedule = get_schedule_subcommand{get};
3092 auto getTransactionId = get_transaction_id_subcommand{get};
3093
3094 // set subcommand
3095 auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
3096 setSubcommand->require_subcommand();
3097
3098 // set contract subcommand
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"));//->required();
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"));//->required();
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 // ->required();
3123 contractSubcommand->add_option("wasm-file", wasmPath, localized("The file containing the contract WASM relative to contract-dir"));
3124// ->check(CLI::ExistingFile);
3125 contractSubcommand->add_option("abi-file,-a,--abi", abiPath, localized("The ABI for the contract relative to contract-dir"));
3126// ->check(CLI::ExistingFile);
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;
3134 bool duplicate = false;
3135 fc::sha256 old_hash, new_hash;
3136 if (!suppress_duplicate_check) {
3137 try {
3138 const auto result = call(get_code_hash_func, fc::mutable_variant_object("account_name", account));
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
3146 bytes code_bytes;
3147 if(!contract_clear){
3148 std::string wasm;
3149 fc::path cpath = fc::canonical(fc::path(contractPath));
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;
3158 fc::read_file_contents(wasmPath, wasm);
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()) {
3171 new_hash = fc::sha256::hash(&(code_bytes[0]), code_bytes.size());
3172 }
3173 duplicate = (old_hash == new_hash);
3174 }
3175
3176 if (!duplicate) {
3177 actions.emplace_back( create_setcode(name(account), code_bytes ) );
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
3189 bytes old_abi;
3190 bool duplicate = false;
3191 if (!suppress_duplicate_check) {
3192 try {
3193 const auto result = call(get_raw_abi_func, fc::mutable_variant_object("account_name", account));
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
3201 bytes abi_bytes;
3202 if(!contract_clear){
3203 fc::path cpath = fc::canonical(fc::path(contractPath));
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
3213 abi_bytes = fc::raw::pack(fc::json::from_file(abiPath).as<abi_def>());
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
3222 if (!duplicate) {
3223 try {
3224 actions.emplace_back( create_setabi(name(account), abi_bytes) );
3225 } SYS_RETHROW_EXCEPTIONS(abi_type_exception, "Fail to parse ABI JSON")
3226 if ( shouldSend ) {
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
3235 add_standard_transaction_options(contractSubcommand, "account@active");
3236 add_standard_transaction_options(codeSubcommand, "account@active");
3237 add_standard_transaction_options(abiSubcommand, "account@active");
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 // set account
3254 auto setAccount = setSubcommand->add_subcommand("account", localized("Set or update blockchain account state"))->require_subcommand();
3255
3256 // set account permission
3257 auto setAccountPermission = set_account_permission_subcommand(setAccount);
3258
3259 // set action
3260 auto setAction = setSubcommand->add_subcommand("action", localized("Set or update blockchain action state"))->require_subcommand();
3261
3262 // set action permission
3263 auto setActionPermission = set_action_permission_subcommand(setAction);
3264
3265 // Transfer subcommand
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
3280 add_standard_transaction_options(transfer, "sender@active");
3281 transfer->callback([&] {
3282 if (tx_force_unique && memo.size() == 0) {
3283 // use the memo to add a nonce
3284 memo = generate_nonce_string();
3285 tx_force_unique = false;
3286 }
3287
3288 auto transfer_amount = to_asset(name(con), amount);
3289 auto transfer = create_transfer(con, name(sender), name(recipient), transfer_amount, memo);
3290 if (!pay_ram) {
3291 send_actions( { transfer });
3292 } else {
3293 auto open_ = create_open(con, name(recipient), transfer_amount.get_symbol(), name(sender));
3294 send_actions( { open_, transfer } );
3295 }
3296 });
3297
3298 // Net subcommand
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([&] {
3305 const auto& v = call(url, net_connect, new_host);
3306 std::cout << fc::json::to_pretty_string(v) << std::endl;
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([&] {
3312 const auto& v = call(url, net_disconnect, new_host);
3313 std::cout << fc::json::to_pretty_string(v) << std::endl;
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([&] {
3319 const auto& v = call(url, net_status, new_host);
3320 std::cout << fc::json::to_pretty_string(v) << std::endl;
3321 });
3322
3323 auto connections = net->add_subcommand("peers", localized("Status of all existing peers"));
3324 connections->callback([&] {
3325 const auto& v = call(url, net_connections);
3326 std::cout << fc::json::to_pretty_string(v) << std::endl;
3327 });
3328
3329
3330
3331 // Wallet subcommand
3332 auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"));
3333 wallet->require_subcommand();
3334 // create wallet
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
3345 const auto& v = call(wallet_url, wallet_create, wallet_name);
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) {
3350 std::cout << fc::json::to_pretty_string(v) << std::endl;
3351 } else {
3352 std::cerr << localized("saving password to ${filename}", ("filename", password_file)) << std::endl;
3353 auto password_str = fc::json::to_pretty_string(v);
3354 boost::replace_all(password_str, "\"", "");
3355 std::ofstream out( password_file.c_str() );
3356 out << password_str;
3357 }
3358 });
3359
3360 // open wallet
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] {
3364 call(wallet_url, wallet_open, wallet_name);
3365 std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
3366 });
3367
3368 // lock wallet
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] {
3372 call(wallet_url, wallet_lock, wallet_name);
3373 std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
3374 });
3375
3376 // lock all wallets
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 // unlock wallet
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] {
3389 prompt_for_wallet_password(wallet_pw, wallet_name);
3390
3391 fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
3393 std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
3394 });
3395
3396 // import keys into wallet
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: ");
3404 fc::set_console_echo(false);
3405 std::getline( std::cin, wallet_key_str, '\n' );
3407 }
3408
3409 private_key_type wallet_key;
3410 try {
3411 wallet_key = private_key_type( wallet_key_str );
3412 } catch (...) {
3413 SYS_THROW(private_key_type_exception, "Invalid private key")
3414 }
3415 public_key_type pubkey = wallet_key.get_public_key();
3416
3417 fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
3419 std::cout << localized("imported private key for: ${pubkey}", ("pubkey", pubkey.to_string())) << std::endl;
3420 });
3421
3422 // remove keys from wallet
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] {
3429 prompt_for_wallet_password(wallet_pw, wallet_name);
3431 try {
3432 pubkey = public_key_type( wallet_rm_key_str );
3433 } catch (...) {
3434 SYS_THROW(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", wallet_rm_key_str))
3435 }
3436 fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw), fc::variant(wallet_rm_key_str)};
3438 std::cout << localized("removed private key for: ${pubkey}", ("pubkey", wallet_rm_key_str)) << std::endl;
3439 });
3440
3441 // create a key within wallet
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 //an empty key type is allowed -- it will let the underlying wallet pick which type it prefers
3448 fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_create_key_type)};
3449 const auto& v = call(wallet_url, wallet_create_key, vs);
3450 std::cout << localized("Created new private key with a public key of: ") << fc::json::to_pretty_string(v) << std::endl;
3451 });
3452
3453 // list wallets
3454 auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"));
3455 listWallet->callback([] {
3456 std::cout << localized("Wallets:") << std::endl;
3457 const auto& v = call(wallet_url, wallet_list);
3458 std::cout << fc::json::to_pretty_string(v) << std::endl;
3459 });
3460
3461 // list keys
3462 auto listKeys = wallet->add_subcommand("keys", localized("List of public keys from all unlocked wallets."));
3463 listKeys->callback([] {
3464 const auto& v = call(wallet_url, wallet_public_keys);
3465 std::cout << fc::json::to_pretty_string(v) << std::endl;
3466 });
3467
3468 // list private keys
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] {
3473 prompt_for_wallet_password(wallet_pw, wallet_name);
3474 fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
3475 const auto& v = call(wallet_url, wallet_list_keys, vs);
3476 std::cout << fc::json::to_pretty_string(v) << std::endl;
3477 });
3478
3479 auto stopKiod = wallet->add_subcommand("stop", localized("Stop ${k}.", ("k", key_store_executable_name)));
3480 stopKiod->callback([] {
3481 const auto& v = call(wallet_url, kiod_stop);
3482 if ( !v.is_object() || v.get_object().size() != 0 ) { //on success kiod responds with empty object
3483 std::cerr << fc::json::to_pretty_string(v) << std::endl;
3484 } else {
3485 std::cout << "OK" << std::endl;
3486 }
3487 });
3488
3489 // sign subcommand
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" );
3508 fc::variant trx_var = json_from_file_or_string(trx_json_to_sign);
3509
3510 // If transaction was packed, unpack it before signing
3511 bool was_packed_trx = false;
3512 if( trx_var.is_object() ) {
3513 fc::variant_object& vo = trx_var.get_object();
3514 if( vo.contains("packed_trx") ) {
3515 packed_transaction packed_trx;
3516 try {
3517 fc::from_variant<packed_transaction>( trx_var, packed_trx );
3518 } SYS_RETHROW_EXCEPTIONS( transaction_type_exception, "Invalid packed transaction format: '${data}'",
3519 ("data", fc::json::to_string(trx_var, fc::time_point::maximum())))
3520 const signed_transaction& strx = packed_trx.get_signed_transaction();
3521 trx_var = strx;
3522 was_packed_trx = true;
3523 }
3524 }
3525
3527 try {
3528 trx = trx_var.as<signed_transaction>();
3529 } SYS_RETHROW_EXCEPTIONS(transaction_type_exception, "Invalid transaction format: '${data}'",
3530 ("data", fc::json::to_string(trx_var, fc::time_point::maximum())))
3531
3532 std::optional<chain_id_type> chain_id;
3533
3534 if( str_chain_id.size() == 0 ) {
3535 ilog( "grabbing chain_id from ${n}", ("n", node_executable_name) );
3536 auto info = get_info();
3537 chain_id = info.chain_id;
3538 } else {
3539 chain_id = chain_id_type(str_chain_id);
3540 }
3541
3542 if( str_public_key.size() > 0 ) {
3543 public_key_type pub_key;
3544 try {
3545 pub_key = public_key_type(str_public_key);
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 });
3548 sign_transaction(trx, keys_var, *chain_id);
3549 } else {
3550 if( str_private_key.size() == 0 ) {
3551 std::cerr << localized("private key: ");
3552 fc::set_console_echo(false);
3553 std::getline( std::cin, str_private_key, '\n' );
3555 }
3556 private_key_type priv_key;
3557 try {
3558 priv_key = private_key_type(str_private_key);
3559 } SYS_RETHROW_EXCEPTIONS(private_key_type_exception, "Invalid private key")
3560 trx.sign(priv_key, *chain_id);
3561 }
3562
3563 if(push_trx) {
3565 std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
3566 } else {
3567 if ( was_packed_trx ) { // pack it as before
3569 } else {
3570 std::cout << fc::json::to_pretty_string(trx) << std::endl;
3571 }
3572 }
3573 });
3574
3575 // Push subcommand
3576 auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"));
3577 push->require_subcommand();
3578
3579 // push action
3580 string contract_account;
3581 string action;
3582 string data;
3583 vector<string> permissions;
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
3592 add_standard_transaction_options(actionsSubcommand);
3593 actionsSubcommand->callback([&] {
3594 fc::variant action_args_var;
3595 if( !data.empty() ) {
3597 }
3598 auto accountPermissions = get_account_permissions(tx_permission);
3599
3600 send_actions({chain::action{accountPermissions, name(contract_account), name(action),
3601 variant_to_bin( name(contract_account), name(action), action_args_var ) }});
3602 });
3603
3604 // push transaction
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();
3608 add_standard_transaction_options(trxSubcommand);
3609 trxSubcommand->add_flag("-o,--read-only", tx_read_only, localized("Specify a transaction is read-only"));
3610
3611 trxSubcommand->callback([&] {
3612 fc::variant trx_var = json_from_file_or_string(trx_to_push);
3613 try {
3614 signed_transaction trx = trx_var.as<signed_transaction>();
3615 std::cout << fc::json::to_pretty_string( push_transaction( trx )) << std::endl;
3616 } catch( const std::exception& ) {
3617 // unable to convert so try via abi
3620 std::cout << fc::json::to_pretty_string( push_transaction( trx )) << std::endl;
3621 }
3622 });
3623
3624 // push transactions
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([&] {
3629 fc::variant trx_var = json_from_file_or_string(trxsJson);
3630 auto trxs_result = call(push_txns_func, trx_var);
3631 std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
3632 });
3633
3634
3635 // multisig subcommand
3636 auto msig = app.add_subcommand("multisig", localized("Multisig contract commands"));
3637 msig->require_subcommand();
3638
3639 // multisig propose
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;
3648 CLI::callback_t parse_expiration_hours = [&](CLI::results_t res) -> bool {
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"));
3659 add_standard_transaction_options(propose_action, "proposer@active");
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([&] {
3670 fc::variant requested_perm_var = json_from_file_or_string(requested_perm);
3671 fc::variant transaction_perm_var = json_from_file_or_string(transaction_perm);
3672 fc::variant trx_var = json_from_file_or_string(proposed_transaction);
3673 transaction proposed_trx;
3674 try {
3675 proposed_trx = trx_var.as<transaction>();
3676 } SYS_RETHROW_EXCEPTIONS(transaction_type_exception, "Invalid transaction format: '${data}'",
3677 ("data", fc::json::to_string(trx_var, fc::time_point::maximum())))
3678 bytes proposed_trx_serialized = variant_to_bin( name(proposed_contract), name(proposed_action), trx_var );
3679
3681 try {
3682 reqperm = requested_perm_var.as<vector<permission_level>>();
3683 } SYS_RETHROW_EXCEPTIONS(transaction_type_exception, "Wrong requested permissions format: '${data}'", ("data",requested_perm_var));
3684
3686 try {
3687 trxperm = transaction_perm_var.as<vector<permission_level>>();
3688 } SYS_RETHROW_EXCEPTIONS(transaction_type_exception, "Wrong transaction permissions format: '${data}'", ("data",transaction_perm_var));
3689
3690 auto accountPermissions = get_account_permissions(tx_permission);
3691 if (accountPermissions.empty()) {
3692 if (!proposer.empty()) {
3693 accountPermissions = vector<permission_level>{{name(proposer), config::active_name}};
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
3702 transaction trx;
3703
3704 trx.expiration = fc::time_point_sec( fc::time_point::now() + fc::hours(proposal_expiration_hours) );
3705 trx.ref_block_num = 0;
3706 trx.ref_block_prefix = 0;
3707 trx.max_net_usage_words = 0;
3708 trx.max_cpu_usage_ms = 0;
3709 trx.delay_sec = 0;
3710 trx.actions = { chain::action(trxperm, name(proposed_contract), name(proposed_action), proposed_trx_serialized) };
3711
3712 fc::to_variant(trx, trx_var);
3713
3714 auto args = fc::mutable_variant_object()
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 //multisig propose transaction
3724 auto propose_trx = msig->add_subcommand("propose_trx", localized("Propose transaction"));
3725 add_standard_transaction_options(propose_trx, "proposer@active");
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([&] {
3732 fc::variant requested_perm_var = json_from_file_or_string(requested_perm);
3733 fc::variant trx_var = json_from_file_or_string(trx_to_push);
3734
3735 auto accountPermissions = get_account_permissions(tx_permission);
3736 if (accountPermissions.empty()) {
3737 if (!proposer.empty()) {
3738 accountPermissions = vector<permission_level>{{name(proposer), config::active_name}};
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
3747 auto args = fc::mutable_variant_object()
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 // multisig review
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([&] {
3765 const auto result1 = call(get_table_func, fc::mutable_variant_object("json", true)
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 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
3773 // Change to name(proposal_name).value when clio no longer needs to support nodeop versions older than 1.5.0
3774 ("limit", 1)
3775 );
3776 //std::cout << fc::json::to_pretty_string(result) << std::endl;
3777
3778 const auto& rows1 = result1.get_object()["rows"].get_array();
3779 // Condition in if statement below can simply be rows.empty() when clio no longer needs to support nodeop versions older than 1.5.0
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;
3794 std::map<sysio::account_name, std::pair<fc::time_point, vector<decltype(all_approvals)::iterator>>> provided_approvers;
3795
3796 bool new_multisig = true;
3797 if( show_approvals_in_multisig_review ) {
3798 fc::variants rows2;
3799
3800 try {
3801 const auto& result2 = call(get_table_func, fc::mutable_variant_object("json", true)
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 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
3809 // Change to name(proposal_name).value when clio no longer needs to support nodeop versions older than 1.5.0
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() ) {
3821 auto pl = ra["level"].as<permission_level>();
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() ) {
3826 auto pl = pa["level"].as<permission_level>();
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 {
3831 const auto result3 = call(get_table_func, fc::mutable_variant_object("json", true)
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 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
3839 // Change to name(proposal_name).value when clio no longer needs to support nodeop versions older than 1.5.0
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() ) {
3851 auto pl = ra.as<permission_level>();
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() ) {
3856 auto pl = pa.as<permission_level>();
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 ) {
3864 const auto result4 = call(get_table_func, fc::mutable_variant_object("json", true)
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 // Less than ideal upper_bound usage preserved so clio can still work with old buggy nodeop versions
3872 // Change to name(proposal_name).value when clio no longer needs to support nodeop versions older than 1.5.0
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();
3893 vector<char> trx_blob(trx_hex.size()/2);
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
3907 fc::variant trx_var;
3908 abi_serializer abi;
3910 obj["transaction"] = trx_var;
3911
3912 if( show_approvals_in_multisig_review ) {
3913 fc::variants approvals;
3914
3915 for( const auto& approval : all_approvals ) {
3916 fc::mutable_variant_object approval_obj;
3917 approval_obj["level"] = approval.first;
3918 switch( approval.second.second ) {
3919 case approval_status::unapproved:
3920 {
3921 approval_obj["status"] = "unapproved";
3922 if( approval.second.first != fc::time_point{} ) {
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
3950 std::cout << fc::json::to_pretty_string(obj) << std::endl;
3951 });
3952
3953 string perm;
3954 string proposal_hash;
3955 auto approve_or_unapprove = [&](const string& action) {
3956 fc::variant perm_var = json_from_file_or_string(perm);
3957
3958 auto args = fc::mutable_variant_object()
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
3967 auto accountPermissions = get_account_permissions(tx_permission, {name(proposer), config::active_name});
3968 send_actions({chain::action{accountPermissions, "sysio.msig"_n, name(action), variant_to_bin( "sysio.msig"_n, name(action), args ) }});
3969 };
3970
3971 // multisig approve
3972 auto approve = msig->add_subcommand("approve", localized("Approve proposed transaction"));
3973 add_standard_transaction_options(approve, "proposer@active");
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 // multisig unapprove
3981 auto unapprove = msig->add_subcommand("unapprove", localized("Unapprove proposed transaction"));
3982 add_standard_transaction_options(unapprove, "proposer@active");
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 // multisig invalidate
3989 string invalidator;
3990 auto invalidate = msig->add_subcommand("invalidate", localized("Invalidate all multisig approvals of an account"));
3991 add_standard_transaction_options(invalidate, "invalidator@active");
3992 invalidate->add_option("invalidator", invalidator, localized("invalidator name (string)"))->required();
3993 invalidate->callback([&] {
3994 auto args = fc::mutable_variant_object()
3995 ("account", invalidator);
3996
3997 auto accountPermissions = get_account_permissions(tx_permission, {name(invalidator), config::active_name});
3998 send_actions({chain::action{accountPermissions, "sysio.msig"_n, "invalidate"_n, variant_to_bin( "sysio.msig"_n, "invalidate"_n, args ) }});
3999 });
4000
4001 // multisig cancel
4002 string canceler;
4003 auto cancel = msig->add_subcommand("cancel", localized("Cancel proposed transaction"));
4004 add_standard_transaction_options(cancel, "canceler@active");
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([&]() {
4009 auto accountPermissions = get_account_permissions(tx_permission);
4010 if (accountPermissions.empty()) {
4011 if (!canceler.empty()) {
4012 accountPermissions = vector<permission_level>{{name(canceler), config::active_name}};
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 }
4020 auto args = fc::mutable_variant_object()
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 // multisig exec
4030 string executer;
4031 auto exec = msig->add_subcommand("exec", localized("Execute proposed transaction"));
4032 add_standard_transaction_options(exec, "executer@active");
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([&] {
4037 auto accountPermissions = get_account_permissions(tx_permission);
4038 if (accountPermissions.empty()) {
4039 if (!executer.empty()) {
4040 accountPermissions = vector<permission_level>{{name(executer), config::active_name}};
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
4049 auto args = fc::mutable_variant_object()
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 // wrap subcommand
4059 auto wrap = app.add_subcommand("wrap", localized("Wrap contract commands"));
4060 wrap->require_subcommand();
4061
4062 // wrap exec
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"));
4067 add_standard_transaction_options(wrap_exec, "executer@active & --contract@active");
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([&] {
4073 fc::variant trx_var = json_from_file_or_string(trx_to_exec);
4074
4075 auto accountPermissions = get_account_permissions(tx_permission);
4076 if( accountPermissions.empty() ) {
4077 accountPermissions = vector<permission_level>{{name(executer), config::active_name}, {name(wrap_con), config::active_name}};
4078 }
4079
4080 auto args = fc::mutable_variant_object()
4081 ("executer", executer )
4082 ("trx", trx_var);
4083
4084 send_actions({chain::action{accountPermissions, name(wrap_con), "exec"_n, variant_to_bin( name(wrap_con), "exec"_n, args ) }});
4085 });
4086
4087 // system subcommand
4088 auto system = app.add_subcommand("system", localized("Send sysio.system contract action to the blockchain."));
4089 system->require_subcommand();
4090
4091 auto createAccountSystem = create_account_subcommand( system, false /*simple*/ );
4092 auto registerProducer = register_producer_subcommand(system);
4093 auto unregisterProducer = unregister_producer_subcommand(system);
4094
4095 auto voteProducer = system->add_subcommand("voteproducer", localized("Vote for a producer"));
4096 voteProducer->require_subcommand();
4097 auto voteProxy = vote_producer_proxy_subcommand(voteProducer);
4098 auto voteProducers = vote_producers_subcommand(voteProducer);
4099 auto approveProducer = approve_producer_subcommand(voteProducer);
4100 auto unapproveProducer = unapprove_producer_subcommand(voteProducer);
4101
4102 auto listProducers = list_producers_subcommand(system);
4103
4104 auto delegateBandWidth = delegate_bandwidth_subcommand(system);
4105 auto undelegateBandWidth = undelegate_bandwidth_subcommand(system);
4106 auto listBandWidth = list_bw_subcommand(system);
4107 auto bidname = bidname_subcommand(system);
4108 auto bidnameinfo = bidname_info_subcommand(system);
4109
4110 auto buyram = buyram_subcommand(system);
4111 auto sellram = sellram_subcommand(system);
4112
4113 auto claimRewards = claimrewards_subcommand(system);
4114
4115 auto regProxy = regproxy_subcommand(system);
4116 auto unregProxy = unregproxy_subcommand(system);
4117
4118 auto cancelDelay = canceldelay_subcommand(system);
4119
4120 auto rex = system->add_subcommand("rex", localized("Actions related to REX (the resource exchange)"));
4121 rex->require_subcommand();
4122 auto deposit = deposit_subcommand(rex);
4123 auto withdraw = withdraw_subcommand(rex);
4124 auto buyrex = buyrex_subcommand(rex);
4125 auto lendrex = lendrex_subcommand(rex);
4126 auto unstaketorex = unstaketorex_subcommand(rex);
4127 auto sellrex = sellrex_subcommand(rex);
4128 auto cancelrexorder = cancelrexorder_subcommand(rex);
4129 auto mvtosavings = mvtosavings_subcommand(rex);
4130 auto mvfromsavings = mvfrsavings_subcommand(rex);
4131 auto rentcpu = rentcpu_subcommand(rex);
4132 auto rentnet = rentnet_subcommand(rex);
4133 auto fundcpuloan = fundcpuloan_subcommand(rex);
4134 auto fundnetloan = fundnetloan_subcommand(rex);
4135 auto defcpuloan = defcpuloan_subcommand(rex);
4136 auto defnetloan = defnetloan_subcommand(rex);
4137 auto consolidate = consolidate_subcommand(rex);
4138 auto updaterex = updaterex_subcommand(rex);
4139 auto rexexec = rexexec_subcommand(rex);
4140 auto closerex = closerex_subcommand(rex);
4141
4142 auto handle_error = [&](const auto& e)
4143 {
4144 // attempt to extract the error code if one is present
4145 if (!print_recognized_errors(e, verbose)) {
4146 // Error is not recognized
4147 if (!print_help_text(e) || verbose) {
4148 elog("Failed with error: ${e}", ("e", verbose ? e.to_detail_string() : e.to_string()));
4149 }
4150 }
4151 return 1;
4152 };
4153
4154 try {
4155 app.parse(argc, argv);
4156 } catch (const CLI::ParseError &e) {
4157 return app.exit(e);
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& ) {
4166 elog("bad alloc");
4167 return 1;
4168 } catch( const boost::interprocess::bad_alloc& ) {
4169 elog("bad alloc");
4170 return 1;
4171 } catch (const fc::exception& e) {
4172 return handle_error(e);
4173 } catch (const std::exception& e) {
4175 }
4176
4177 return return_code;
4178}
const mie::Vuint & p
Definition bn.cpp:27
const mie::Vuint & r
Definition bn.cpp:28
std::string name
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
#define SYS_RETHROW_EXCEPTIONS(exception_type, FORMAT,...)
Creates a command line program, with very few defaults.
Definition CLI11.hpp:4614
Option * add_flag(std::string flag_name)
Add a flag with no description or variable assignment.
Definition CLI11.hpp:5265
App * add_subcommand(std::string subcommand_name="", std::string subcommand_description="")
Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag.
Definition CLI11.hpp:5538
Option * add_option(std::string option_name, callback_t option_callback, std::string option_description="", bool defaulted=false, std::function< std::string()> func={})
Definition CLI11.hpp:5099
CRTP * required(bool value=true)
Set the option as required.
Definition CLI11.hpp:3400
Anything that can error in Parse.
Definition CLI11.hpp:648
static private_key generate()
public_key get_public_key() const
static private_key generate_r1()
contains only the public point of an elliptic curve key.
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
int64_t code() const
static variant from_file(const fc::path &p, const parse_type ptype=parse_type::legacy_parser, const uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition json.cpp:797
parse_type
Definition json.hpp:23
static string to_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
Definition json.cpp:674
static string to_pretty_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
Definition json.cpp:775
static variant from_string(const string &utf8_str, const parse_type ptype=parse_type::legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition json.cpp:442
logger & set_log_level(log_level e)
Definition logger.cpp:100
static logger get(const fc::string &name=DEFAULT_LOGGER)
Definition logger.cpp:88
An order-preserving dictionary of variants.
mutable_variant_object & set(string key, variant var) &
wraps boost::filesystem::path to provide platform independent path manipulation.
fc::path filename() const
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
static std_exception_wrapper from_current_exception(const std::exception &e)
static time_point_sec from_iso_string(const fc::string &s)
Definition time.cpp:39
static time_point now()
Definition time.cpp:14
static constexpr time_point maximum()
Definition time.hpp:46
An order-preserving dictionary of variants.
iterator end() const
iterator find(const string &key) const
bool contains(const char *key) const
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
Definition variant.hpp:191
variant_object & get_object()
Definition variant.cpp:554
T as() const
Definition variant.hpp:327
bool is_object() const
Definition variant.cpp:363
string as_string() const
Definition variant.cpp:469
string name() const
Definition symbol.hpp:104
static symbol from_string(const string &from)
Definition symbol.hpp:68
Defines exception's used by fc.
#define FC_CAPTURE_AND_RETHROW(...)
#define FC_DECLARE_EXCEPTION(TYPE, CODE, WHAT)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
int * count
return
char ** argv
void close(T *e, websocketpp::connection_hdl hdl)
#define localized(str,...)
Definition localize.hpp:10
#define FC_LOG_MESSAGE(LOG_LEVEL, FORMAT,...)
A helper method for generating log messages.
#define wlog(FORMAT,...)
Definition logger.hpp:124
#define edump(SEQ)
Definition logger.hpp:162
#define dlog(FORMAT,...)
Definition logger.hpp:101
#define ilog(FORMAT,...)
Definition logger.hpp:118
#define DEFAULT_LOGGER
Definition logger.hpp:7
#define elog(FORMAT,...)
Definition logger.hpp:130
std::function< bool(const results_t &)> callback_t
callback function definition
Definition CLI11.hpp:3329
std::vector< std::string > results_t
Definition CLI11.hpp:3327
application & app()
void unpack(Stream &s, std::deque< T > &value)
Definition raw.hpp:540
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
std::string string
Definition string.hpp:10
void read_file_contents(const fc::path &filename, std::string &result)
Definition fstream.cpp:14
void set_console_echo(bool enable_echo)
Definition console.cpp:28
bool exists(const path &p)
fc::string to_string(double)
Definition string.cpp:131
std::vector< log_message > log_messages
bool is_regular_file(const path &p)
uint8_t from_hex(char c)
Definition hex.cpp:6
constexpr microseconds hours(int64_t h)
Definition time.hpp:35
std::vector< fc::variant > variants
Definition variant.hpp:173
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
fc::string to_hex(const char *d, uint32_t s)
Definition hex.cpp:17
uint64_t to_uint64(const fc::string &)
Definition string.cpp:105
path canonical(const path &p)
void from_variant(const fc::variant &v, sysio::chain::chain_id_type &cid)
constexpr microseconds days(int64_t d)
Definition time.hpp:36
void to_variant(const sysio::chain::shared_public_key &var, fc::variant &vo)
Definition authority.cpp:4
std::string escape_string(const std::string_view &str, const json::yield_function_t &yield, bool escape_control_chars=true)
Definition json.cpp:495
int64_t to_int64(const fc::string &)
Definition string.cpp:92
Definition name.hpp:106
fc::crypto::public_key public_key_type
Definition types.hpp:76
std::variant< block_signing_authority_v0 > block_signing_authority
sysio::chain::action_name action_name
bool validate(const Authority &auth)
uint16_t weight_type
Definition types.hpp:238
fc::crypto::private_key private_key_type
Definition types.hpp:77
name account_name
Definition types.hpp:120
vector< char > bytes
Definition types.hpp:243
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
Definition httpc.hpp:100
const string get_controlled_accounts_func
Definition httpc.hpp:114
const string get_transaction_status_func
Definition httpc.hpp:83
const string wallet_lock
Definition httpc.hpp:129
const string wallet_sign_trx
Definition httpc.hpp:135
const string get_schedule_func
Definition httpc.hpp:104
const string get_producers_func
Definition httpc.hpp:103
const string get_key_accounts_func
Definition httpc.hpp:113
const string send_txn_func
Definition httpc.hpp:84
fc::variant do_http_call(const connection_param &cp, const fc::variant &postdata, bool print_request, bool print_response)
Definition httpc.cpp:184
const string get_currency_stats_func
Definition httpc.hpp:102
const string net_connect
Definition httpc.hpp:117
const string get_code_hash_func
Definition httpc.hpp:97
const string get_info_func
Definition httpc.hpp:82
const string wallet_public_keys
Definition httpc.hpp:128
const string wallet_remove_key
Definition httpc.hpp:133
const string get_block_trace_func
Definition httpc.hpp:111
const string get_account_func
Definition httpc.hpp:93
std::unique_ptr< detail::http_context_impl, detail::http_context_deleter > http_context
Definition httpc.hpp:15
const string get_code_func
Definition httpc.hpp:96
const string net_connections
Definition httpc.hpp:120
const string get_abi_func
Definition httpc.hpp:98
const string get_actions_func
Definition httpc.hpp:109
const string wallet_list
Definition httpc.hpp:126
const string wallet_create_key
Definition httpc.hpp:134
const string wallet_import_key
Definition httpc.hpp:132
const string get_table_func
Definition httpc.hpp:94
const string get_currency_balance_func
Definition httpc.hpp:101
http_context create_http_context()
Definition httpc.cpp:43
const string wallet_open
Definition httpc.hpp:125
const string push_txn_func
Definition httpc.hpp:85
const string get_block_header_state_func
Definition httpc.hpp:92
const string get_transaction_func
Definition httpc.hpp:112
const string wallet_unlock
Definition httpc.hpp:131
const string get_transaction_trace_func
Definition httpc.hpp:110
const string wallet_list_keys
Definition httpc.hpp:127
const string net_disconnect
Definition httpc.hpp:118
const string send2_txn_func
Definition httpc.hpp:86
const string push_txns_func
Definition httpc.hpp:88
parsed_url parse_url(const string &server_url)
Definition httpc.cpp:108
const string get_block_info_func
Definition httpc.hpp:91
const string get_table_by_scope_func
Definition httpc.hpp:95
const string kiod_stop
Definition httpc.hpp:136
const string compute_txn_func
Definition httpc.hpp:87
const string get_raw_abi_func
Definition httpc.hpp:99
const string wallet_create
Definition httpc.hpp:124
const string wallet_lock_all
Definition httpc.hpp:130
const string net_status
Definition httpc.hpp:119
const string get_block_func
Definition httpc.hpp:90
const string get_required_keys
Definition httpc.hpp:105
const std::string & version_client()
< Grab the basic version information of the client; example: v1.8.0-rc1
Definition version.cpp:5
const std::string & version_full()
Definition version.cpp:10
@ duplicate
the connection is redundant
Definition protocol.hpp:49
#define value
Definition pkcs11.h:157
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
#define T(meth, val, expected)
chain::action create_action(const vector< permission_level > &authorization, const account_name &code, const action_name &act, const fc::variant &args)
Definition main.cpp:683
const fc::microseconds abi_serializer_max_time
Definition main.cpp:173
chain::permission_level to_permission_level(const std::string &s)
Definition main.cpp:666
void get_account(const string &accountName, const string &coresym, bool json_format)
Definition main.cpp:2211
void print_result(const fc::variant &result)
Definition main.cpp:568
#define SYSC_ASSERT(TEST,...)
Definition main.cpp:134
bool tx_rtn_failure_trace
Definition main.cpp:180
chain::action create_updateauth(const name &account, const name &permission, const name &parent, const authority &auth)
Definition main.cpp:772
asset to_asset(account_name code, const string &s)
Definition main.cpp:812
string default_wallet_url
Definition main.cpp:167
void sign_transaction(signed_transaction &trx, fc::variant &required_keys, const chain_id_type &chain_id)
Definition main.cpp:319
string tx_json_save_file
Definition main.cpp:186
bool tx_read_only
Definition main.cpp:181
vector< chain::permission_level > get_account_permissions(const vector< string > &permissions)
Definition main.cpp:238
uint32_t tx_max_net_usage
Definition main.cpp:194
CLI::callback_t header_opt_callback
Definition main.cpp:2528
chain::action create_delegate(const name &from, const name &receiver, const asset &net, const asset &cpu, bool transfer)
Definition main.cpp:705
fc::variant push_actions(std::vector< chain::action > &&actions, packed_transaction::compression_type compression=packed_transaction::compression_type::none)
Definition main.cpp:413
std::variant< public_key_type, permission_level > auth_type
Definition main.cpp:130
void print_return_value(const fc::variant &at)
Definition main.cpp:420
bool tx_use_old_rpc
Definition main.cpp:184
void prompt_for_wallet_password(string &pw, const string &name)
Definition main.cpp:299
bool tx_use_old_send_rpc
Definition main.cpp:185
bool tx_return_packed
Definition main.cpp:177
bool tx_retry_lib
Definition main.cpp:182
auto tx_expiration
Definition main.cpp:172
bool tx_dont_broadcast
Definition main.cpp:176
bool tx_print_json
Definition main.cpp:179
chain::action create_open(const string &contract, const name &owner, symbol sym, const name &ram_payer)
Definition main.cpp:725
sysio::chain_apis::read_only::get_info_results get_info()
Definition main.cpp:287
bool print_request
Definition main.cpp:187
chain::action create_transfer(const string &contract, const name &sender, const name &recipient, asset amount, const string &memo)
Definition main.cpp:736
bool tx_skip_sign
Definition main.cpp:178
chain::action create_linkauth(const name &account, const name &code, const name &type, const name &requirement)
Definition main.cpp:782
uint8_t tx_max_cpu_usage
Definition main.cpp:193
sysio::client::http::http_context context
Definition main.cpp:200
chain::action generate_nonce_action()
Definition main.cpp:295
int get_return_code(const fc::variant &result)
Definition main.cpp:542
uint32_t delaysec
Definition main.cpp:196
void send_transaction(signed_transaction &trx, packed_transaction::compression_type compression=packed_transaction::compression_type::none)
Definition main.cpp:641
chain::action create_buyram(const name &creator, const name &newaccount, const asset &quantity)
Definition main.cpp:687
int return_code
Definition main.cpp:191
CLI::callback_t obsoleted_option_host_port
Definition main.cpp:1093
chain::action create_setcode(const name &account, const bytes &code)
Definition main.cpp:760
chain::action create_unlinkauth(const name &account, const name &code, const name &type)
Definition main.cpp:787
string url
Definition main.cpp:166
void add_standard_transaction_options(CLI::App *cmd, string default_permission="")
Definition main.cpp:202
void ensure_kiod_running(CLI::App *app)
Definition main.cpp:1033
authority parse_json_authority_or_key(const std::string &authorityJsonOrFile)
Definition main.cpp:800
void send_actions(std::vector< chain::action > &&actions, packed_transaction::compression_type compression=packed_transaction::compression_type::none)
Definition main.cpp:616
void print_action_tree(const fc::variant &action)
Definition main.cpp:532
uint16_t tx_retry_num_blocks
Definition main.cpp:183
bool no_verify
Definition main.cpp:169
std::string clean_output(std::string str)
Definition main.cpp:161
bytes variant_to_bin(const account_name &account, const action_name &action, const fc::variant &action_args_var)
Definition main.cpp:491
void print_action(const fc::variant &at)
Definition main.cpp:441
bool tx_force_unique
Definition main.cpp:175
string wallet_url
Definition main.cpp:168
fc::variant determine_required_keys(const signed_transaction &trx)
Definition main.cpp:308
auto abi_serializer_resolver
Definition main.cpp:470
vector< string > tx_permission
Definition main.cpp:198
bool print_response
Definition main.cpp:188
vector< string > headers
Definition main.cpp:170
string tx_ref_block_num_or_id
Definition main.cpp:174
fc::variant json_from_file_or_string(const string &file_or_str, fc::json::parse_type ptype=fc::json::parse_type::legacy_parser)
Definition main.cpp:509
bytes json_or_file_to_bin(const account_name &account, const action_name &action, const string &data_or_filename)
Definition main.cpp:524
chain::action create_deleteauth(const name &account, const name &permission)
Definition main.cpp:777
chain::action create_setabi(const name &account, const bytes &abi)
Definition main.cpp:750
authority parse_json_authority(const std::string &authorityJsonOrFile)
Definition main.cpp:792
bool local_port_used()
Definition main.cpp:1010
fc::variant bin_to_variant(const account_name &account, const action_name &action, const bytes &action_args)
Definition main.cpp:500
bool no_auto_kiod
Definition main.cpp:189
bfs::path determine_home_directory()
Definition main.cpp:146
fc::variant regproducer_variant(const account_name &producer, const public_key_type &key, const string &url, uint16_t location)
Definition main.cpp:716
chain::action create_newaccount(const name &creator, const name &newaccount, auth_type owner, auth_type active)
Definition main.cpp:671
void try_local_port(uint32_t duration)
Definition main.cpp:1022
fc::variant call(const std::string &url, const std::string &path, const T &v)
Definition main.cpp:258
chain::action create_buyrambytes(const name &creator, const name &newaccount, uint32_t numbytes)
Definition main.cpp:696
string generate_nonce_string()
Definition main.cpp:291
fc::variant push_transaction(signed_transaction &trx, packed_transaction::compression_type compression=packed_transaction::compression_type::none)
Definition main.cpp:325
unsigned short uint16_t
Definition stdint.h:125
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
signed int int32_t
Definition stdint.h:123
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
approve_producer_subcommand(CLI::App *actionRoot)
Definition main.cpp:1282
bidname_info_subcommand(CLI::App *actionRoot)
Definition main.cpp:1614
bidname_subcommand(CLI::App *actionRoot)
Definition main.cpp:1594
string receiver_str
Definition main.cpp:1691
buyram_subcommand(CLI::App *actionRoot)
Definition main.cpp:1696
const name act_name
Definition main.cpp:1855
buyrex_subcommand(CLI::App *actionRoot)
Definition main.cpp:1857
string amount_str
Definition main.cpp:1854
string canceling_permission
Definition main.cpp:1791
canceldelay_subcommand(CLI::App *actionRoot)
Definition main.cpp:1794
cancelrexorder_subcommand(CLI::App *actionRoot)
Definition main.cpp:1947
claimrewards_subcommand(CLI::App *actionRoot)
Definition main.cpp:1739
const name act_name
Definition main.cpp:2197
closerex_subcommand(CLI::App *actionRoot)
Definition main.cpp:2199
const name act_name
Definition main.cpp:2161
consolidate_subcommand(CLI::App *actionRoot)
Definition main.cpp:2163
create_account_subcommand(CLI::App *actionRoot, bool s)
Definition main.cpp:1141
uint32_t buy_ram_bytes_in_kbytes
Definition main.cpp:1135
const name act_name
Definition main.cpp:2061
defcpuloan_subcommand(CLI::App *actionRoot)
Definition main.cpp:2063
defnetloan_subcommand(CLI::App *actionRoot)
Definition main.cpp:2086
const name act_name
Definition main.cpp:2084
delegate_bandwidth_subcommand(CLI::App *actionRoot)
Definition main.cpp:1532
const name act_name
Definition main.cpp:1815
deposit_subcommand(CLI::App *actionRoot)
Definition main.cpp:1817
const name act_name
Definition main.cpp:2015
fundcpuloan_subcommand(CLI::App *actionRoot)
Definition main.cpp:2017
fundnetloan_subcommand(CLI::App *actionRoot)
Definition main.cpp:2040
const name act_name
Definition main.cpp:2038
get_schedule_subcommand(CLI::App *actionRoot)
Definition main.cpp:1448
void print(const char *name, const fc::variant &schedule)
Definition main.cpp:1424
get_transaction_id_subcommand(CLI::App *actionRoot)
Definition main.cpp:1467
const name act_name1
Definition main.cpp:1875
const name act_name2
Definition main.cpp:1876
lendrex_subcommand(CLI::App *actionRoot)
Definition main.cpp:1878
list_bw_subcommand(CLI::App *actionRoot)
Definition main.cpp:1656
list_producers_subcommand(CLI::App *actionRoot)
Definition main.cpp:1388
const name act_name
Definition main.cpp:2126
mvfrsavings_subcommand(CLI::App *actionRoot)
Definition main.cpp:2128
mvtosavings_subcommand(CLI::App *actionRoot)
Definition main.cpp:2108
const name act_name
Definition main.cpp:2106
register_producer_subcommand(CLI::App *actionRoot)
Definition main.cpp:1106
regproxy_subcommand(CLI::App *actionRoot)
Definition main.cpp:1756
string loan_fund_str
Definition main.cpp:1963
string receiver_str
Definition main.cpp:1961
const name act_name
Definition main.cpp:1964
string loan_payment_str
Definition main.cpp:1962
rentcpu_subcommand(CLI::App *actionRoot)
Definition main.cpp:1966
string loan_payment_str
Definition main.cpp:1988
string receiver_str
Definition main.cpp:1987
string loan_fund_str
Definition main.cpp:1989
const name act_name
Definition main.cpp:1990
rentnet_subcommand(CLI::App *actionRoot)
Definition main.cpp:1992
const name act_name
Definition main.cpp:2178
rexexec_subcommand(CLI::App *actionRoot)
Definition main.cpp:2180
string receiver_str
Definition main.cpp:1717
sellram_subcommand(CLI::App *actionRoot)
Definition main.cpp:1720
uint64_t amount
Definition main.cpp:1718
const name act_name
Definition main.cpp:1926
sellrex_subcommand(CLI::App *actionRoot)
Definition main.cpp:1928
set_account_permission_subcommand(CLI::App *accountCmd)
Definition main.cpp:855
set_action_permission_subcommand(CLI::App *actionRoot)
Definition main.cpp:984
static yield_function_t create_yield_function(const fc::microseconds &max_serialization_time)
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)
string to_string() const
Definition asset.cpp:19
const symbol & get_symbol() const
Definition asset.hpp:34
static asset from_string(const string &from)
Definition asset.cpp:31
vector< wait_weight > waits
vector< permission_level_weight > accounts
vector< key_weight > keys
Immutable except for fc::from_variant.
Definition name.hpp:43
name(std::string_view str)
Definition name.hpp:56
std::string to_string() const
Definition name.cpp:19
const signed_transaction & get_signed_transaction() const
vector< signature_type > signatures
const signature_type & sign(const private_key_type &key, const chain_id_type &chain_id)
void set_reference_block(const block_id_type &reference_block)
fc::unsigned_int delay_sec
upper limit on the total CPU time billed for this transaction
uint16_t ref_block_num
specifies a block num in the last 2^16 blocks.
uint32_t ref_block_prefix
specifies the lower 32 bits of the blockid at get_ref_blocknum
uint8_t max_cpu_usage_ms
upper limit on total network bandwidth (in 8 byte words) billed for this transaction
time_point_sec expiration
the time at which a transaction expires
vector< action > actions
vector< action > context_free_actions
transaction_id_type id() const
unapprove_producer_subcommand(CLI::App *actionRoot)
Definition main.cpp:1335
undelegate_bandwidth_subcommand(CLI::App *actionRoot)
Definition main.cpp:1570
unregister_producer_subcommand(CLI::App *actionRoot)
Definition main.cpp:1218
unregproxy_subcommand(CLI::App *actionRoot)
Definition main.cpp:1774
unstaketorex_subcommand(CLI::App *actionRoot)
Definition main.cpp:1904
updaterex_subcommand(CLI::App *actionRoot)
Definition main.cpp:2147
const name act_name
Definition main.cpp:2145
vote_producer_proxy_subcommand(CLI::App *actionRoot)
Definition main.cpp:1237
vote_producers_subcommand(CLI::App *actionRoot)
Definition main.cpp:1258
vector< std::string > producer_names
Definition main.cpp:1256
withdraw_subcommand(CLI::App *actionRoot)
Definition main.cpp:1837
const name act_name
Definition main.cpp:1835
yh_object_type type
Definition yubihsm.h:672
char * s
CK_BYTE_PTR pubkey
struct @108 key_type