main.cpp 114.8 KB
Newer Older
1 2
/**
 *  @file
3
 *  @copyright defined in eos/LICENSE.txt
4 5
 *  @defgroup eosclienttool EOSIO Command Line Client Reference
 *  @brief Tool for sending transactions and querying state from @ref nodeos
6 7 8 9 10 11
 *  @ingroup eosclienttool
 */

/**
  @defgroup eosclienttool

12
  @section intro Introduction to cleos
13

14 15
  `cleos` is a command line tool that interfaces with the REST api exposed by @ref nodeos. In order to use `cleos` you will need to
  have a local copy of `nodeos` running and configured to load the 'eosio::chain_api_plugin'.
16

17
   cleos contains documentation for all of its commands. For a list of all commands known to cleos, simply run it with no arguments:
18
```
19 20 21
$ ./cleos
Command Line Interface to EOSIO Client
Usage: programs/cleos/cleos [OPTIONS] SUBCOMMAND
22 23

Options:
24
  -h,--help                   Print this help message and exit
D
Daniel Larimer 已提交
25 26 27 28
  -u,--url TEXT=http://localhost:8888/
                              the http/https URL where nodeos is running
  --wallet-url TEXT=http://localhost:8888/
                              the http/https URL where keosd is running
29
  -r,--header                 pass specific HTTP header, repeat this option to pass multiple headers
30
  -n,--no-verify              don't verify peer certificate when using HTTPS
31
  -v,--verbose                output verbose actions on error
32 33

Subcommands:
34
  version                     Retrieve version information
35 36 37 38
  create                      Create various items, on and off the blockchain
  get                         Retrieve various items and information from the blockchain
  set                         Set or update blockchain state
  transfer                    Transfer EOS from account to account
39
  net                         Interact with local p2p network connections
40
  wallet                      Interact with local wallet
41
  sign                        Sign a transaction
42
  push                        Push arbitrary transactions to the blockchain
D
Daniel Larimer 已提交
43
  multisig                    Multisig contract commands
A
arhag 已提交
44

45 46 47
```
To get help with any particular subcommand, run it with no arguments as well:
```
48
$ ./cleos create
49
Create various items, on and off the blockchain
50
Usage: ./cleos create SUBCOMMAND
51 52 53 54 55

Subcommands:
  key                         Create a new keypair and print the public and private keys
  account                     Create a new account on the blockchain

56
$ ./cleos create account
57
Create a new account on the blockchain
58
Usage: ./cleos create account [OPTIONS] creator name OwnerKey ActiveKey
59 60 61 62

Positionals:
  creator TEXT                The name of the account creating the new account
  name TEXT                   The name of the new account
63 64
  OwnerKey TEXT               The owner public key for the new account
  ActiveKey TEXT              The active public key for the new account
65 66

Options:
67 68 69 70 71
  -x,--expiration             set the time in seconds before a transaction expires, defaults to 30s
  -f,--force-unique           force the transaction to be unique. this will consume extra bandwidth and remove any protections against accidently issuing the same transaction multiple times
  -s,--skip-sign              Specify if unlocked wallet keys should be used to sign transaction
  -d,--dont-broadcast         don't broadcast transaction to the network (just print to stdout)
  -p,--permission TEXT ...    An account and permission level to authorize, as in 'account@permission' (defaults to 'creator@active')
72 73
```
*/
A
Anton Perkov 已提交
74

75 76
#include <string>
#include <vector>
77
#include <regex>
78
#include <iostream>
D
Daniel Larimer 已提交
79
#include <fc/crypto/hex.hpp>
80
#include <fc/variant.hpp>
D
Daniel Larimer 已提交
81
#include <fc/io/datastream.hpp>
82
#include <fc/io/json.hpp>
D
Daniel Larimer 已提交
83
#include <fc/io/console.hpp>
84
#include <fc/exception/exception.hpp>
D
Daniel Larimer 已提交
85
#include <fc/variant_object.hpp>
86
#include <eosio/utilities/key_conversion.hpp>
87

A
Anton Perkov 已提交
88
#include <eosio/chain/name.hpp>
89
#include <eosio/chain/config.hpp>
90
#include <eosio/chain/wast_to_wasm.hpp>
D
Daniel Larimer 已提交
91
#include <eosio/chain/trace.hpp>
D
Daniel Larimer 已提交
92
#include <eosio/chain_plugin/chain_plugin.hpp>
D
Daniel Larimer 已提交
93
#include <eosio/chain/contract_types.hpp>
D
Daniel Larimer 已提交
94

A
Anton Perkov 已提交
95 96 97 98 99 100 101 102 103
#pragma push_macro("N")
#undef N

#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <boost/process/spawn.hpp>
104
#include <boost/range/algorithm/find_if.hpp>
105
#include <boost/range/algorithm/sort.hpp>
106
#include <boost/range/adaptor/transformed.hpp>
107
#include <boost/algorithm/string/predicate.hpp>
108
#include <boost/algorithm/string/split.hpp>
109
#include <boost/range/algorithm/copy.hpp>
D
Daniel Larimer 已提交
110
#include <boost/algorithm/string/classification.hpp>
111

A
Anton Perkov 已提交
112 113
#pragma pop_macro("N")

114 115 116 117 118 119 120 121 122
#include <Inline/BasicTypes.h>
#include <IR/Module.h>
#include <IR/Validate.h>
#include <WAST/WAST.h>
#include <WASM/WASM.h>
#include <Runtime/Runtime.h>

#include <fc/io/fstream.hpp>

123
#include "CLI11.hpp"
124
#include "help_text.hpp"
125
#include "localize.hpp"
126
#include "config.hpp"
127
#include "httpc.hpp"
128

129
using namespace std;
P
Pravin 已提交
130 131 132 133
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::utilities;
using namespace eosio::client::help;
134
using namespace eosio::client::http;
P
Pravin 已提交
135 136
using namespace eosio::client::localize;
using namespace eosio::client::config;
137 138 139 140 141 142 143 144 145 146 147 148 149 150
using namespace boost::filesystem;

FC_DECLARE_EXCEPTION( explained_exception, 9000000, "explained exception, see error log" );
FC_DECLARE_EXCEPTION( localized_exception, 10000000, "an error occured" );
#define EOSC_ASSERT( TEST, ... ) \
  FC_EXPAND_MACRO( \
    FC_MULTILINE_MACRO_BEGIN \
      if( UNLIKELY(!(TEST)) ) \
      {                                                   \
        std::cerr << localized( __VA_ARGS__ ) << std::endl;  \
        FC_THROW_EXCEPTION( explained_exception, #TEST ); \
      }                                                   \
    FC_MULTILINE_MACRO_END \
  )
151

D
Daniel Larimer 已提交
152
string url = "http://localhost:8888/";
153
string wallet_url = "http://localhost:8900/";
154
int64_t wallet_unlock_timeout = 0;
155
bool no_verify = false;
156
vector<string> headers;
K
Kevin Heifner 已提交
157

158
auto   tx_expiration = fc::seconds(30);
D
Daniel Larimer 已提交
159
string tx_ref_block_num_or_id;
160 161 162
bool   tx_force_unique = false;
bool   tx_dont_broadcast = false;
bool   tx_skip_sign = false;
D
Daniel Larimer 已提交
163
bool   tx_print_json = false;
164

165
uint8_t  tx_max_cpu_usage = 0;
166
uint32_t tx_max_net_usage = 0;
167

168
vector<string> tx_permission;
K
Kevin Heifner 已提交
169

M
Matias Romeo 已提交
170
void add_standard_transaction_options(CLI::App* cmd, string default_permission = "") {
D
Daniel Larimer 已提交
171
   CLI::callback_t parse_expiration = [](CLI::results_t res) -> bool {
K
Kevin Heifner 已提交
172 173
      double value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
174 175
         return false;
      }
176

K
Kevin Heifner 已提交
177
      tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
178 179 180
      return true;
   };

D
Daniel Larimer 已提交
181
   cmd->add_option("-x,--expiration", parse_expiration, localized("set the time in seconds before a transaction expires, defaults to 30s"));
182
   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"));
183
   cmd->add_flag("-s,--skip-sign", tx_skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
D
Daniel Larimer 已提交
184
   cmd->add_flag("-j,--json", tx_print_json, localized("print result as json"));
185
   cmd->add_flag("-d,--dont-broadcast", tx_dont_broadcast, localized("don't broadcast transaction to the network (just print to stdout)"));
D
Daniel Larimer 已提交
186
   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)")));
187

M
Matias Romeo 已提交
188 189 190 191
   string msg = "An account and permission level to authorize, as in 'account@permission'";
   if(!default_permission.empty())
      msg += " (defaults to '" + default_permission + "')";
   cmd->add_option("-p,--permission", tx_permission, localized(msg.c_str()));
192

193
   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)"));
194
   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)"));
195 196
}

D
Daniel Larimer 已提交
197
vector<chain::permission_level> get_account_permissions(const vector<string>& permissions) {
198 199 200
   auto fixedPermissions = permissions | boost::adaptors::transformed([](const string& p) {
      vector<string> pieces;
      split(pieces, p, boost::algorithm::is_any_of("@"));
201
      if( pieces.size() == 1 ) pieces.push_back( "active" );
D
Daniel Larimer 已提交
202
      return chain::permission_level{ .actor = pieces[0], .permission = pieces[1] };
203
   });
D
Daniel Larimer 已提交
204
   vector<chain::permission_level> accountPermissions;
205 206 207
   boost::range::copy(fixedPermissions, back_inserter(accountPermissions));
   return accountPermissions;
}
208

209
template<typename T>
D
Daniel Larimer 已提交
210
fc::variant call( const std::string& url,
211
                  const std::string& path,
D
Daniel Larimer 已提交
212 213
                  const T& v ) {
   try {
214 215
      eosio::client::http::connection_param *cp = new eosio::client::http::connection_param((std::string&)url, (std::string&)path,
              no_verify ? false : true, headers);
216

217
      return eosio::client::http::do_http_call( *cp, fc::variant(v) );
D
Daniel Larimer 已提交
218 219 220 221 222 223 224 225 226
   }
   catch(boost::system::system_error& e) {
      if(url == ::url)
         std::cerr << localized("Failed to connect to nodeos at ${u}; is nodeos running?", ("u", url)) << std::endl;
      else if(url == ::wallet_url)
         std::cerr << localized("Failed to connect to keosd at ${u}; is keosd running?", ("u", url)) << std::endl;
      throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, e.what())});
   }
}
227

228 229
template<typename T>
fc::variant call( const std::string& path,
230
                  const T& v ) { return call( url, path, fc::variant(v) ); }
231

232 233 234
template<>
fc::variant call( const std::string& url,
                  const std::string& path) { return call( url, path, fc::variant() ); }
235

P
Pravin 已提交
236
eosio::chain_apis::read_only::get_info_results get_info() {
237
   return call(url, get_info_func).as<eosio::chain_apis::read_only::get_info_results>();
D
Daniel Larimer 已提交
238 239
}

240
string generate_nonce_string() {
D
Daniel Larimer 已提交
241 242 243
   return fc::to_string(fc::time_point::now().time_since_epoch().count());
}

244
chain::action generate_nonce_action() {
245
   return chain::action( {}, config::null_account_name, "nonce", fc::raw::pack(fc::time_point::now().time_since_epoch().count()));
246 247
}

248
fc::variant determine_required_keys(const signed_transaction& trx) {
K
Kevin Heifner 已提交
249
   // TODO better error checking
D
Daniel Larimer 已提交
250 251
   //wdump((trx));
   const auto& public_keys = call(wallet_url, wallet_public_keys);
K
Kevin Heifner 已提交
252
   auto get_arg = fc::mutable_variant_object
253 254
           ("transaction", (transaction)trx)
           ("available_keys", public_keys);
D
Daniel Larimer 已提交
255
   const auto& required_keys = call(get_required_keys, get_arg);
256 257 258 259
   return required_keys["required_keys"];
}

void sign_transaction(signed_transaction& trx, fc::variant& required_keys) {
K
Kevin Heifner 已提交
260
   // TODO determine chain id
261
   fc::variants sign_args = {fc::variant(trx), required_keys, fc::variant(chain_id_type{})};
D
Daniel Larimer 已提交
262
   const auto& signed_trx = call(wallet_url, wallet_sign_trx, sign_args);
263
   trx = signed_trx.as<signed_transaction>();
K
Kevin Heifner 已提交
264 265
}

266
fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
M
Matias Romeo 已提交
267 268
   auto info = get_info();
   trx.expiration = info.head_block_time + tx_expiration;
D
Daniel Larimer 已提交
269 270

   // Set tapos, default to last irreversible block if it's not specified by the user
D
Daniel Larimer 已提交
271
   block_id_type ref_block_id = info.last_irreversible_block_id;
D
Daniel Larimer 已提交
272 273 274 275
   try {
      fc::variant ref_block;
      if (!tx_ref_block_num_or_id.empty()) {
         ref_block = call(get_block_func, fc::mutable_variant_object("block_num_or_id", tx_ref_block_num_or_id));
D
Daniel Larimer 已提交
276
         ref_block_id = ref_block["id"].as<block_id_type>();
A
arhag 已提交
277
      }
D
Daniel Larimer 已提交
278 279
   } EOS_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));
   trx.set_reference_block(ref_block_id);
280

M
Matias Romeo 已提交
281
   if (tx_force_unique) {
282
      trx.context_free_actions.emplace_back( generate_nonce_action() );
M
Matias Romeo 已提交
283
   }
K
Kevin Heifner 已提交
284

285 286 287
   auto required_keys = determine_required_keys(trx);
   size_t num_keys = required_keys.is_array() ? required_keys.get_array().size() : 1;

288
   trx.max_cpu_usage_ms = tx_max_net_usage;
289
   trx.max_net_usage_words = (tx_max_net_usage + 7)/8;
290

M
Matias Romeo 已提交
291
   if (!tx_skip_sign) {
292
      sign_transaction(trx, required_keys);
M
Matias Romeo 已提交
293 294 295 296 297 298 299
   }

   if (!tx_dont_broadcast) {
      return call(push_txn_func, packed_transaction(trx, compression));
   } else {
      return fc::variant(trx);
   }
300 301
}

302
fc::variant push_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none ) {
303 304 305
   signed_transaction trx;
   trx.actions = std::forward<decltype(actions)>(actions);

306
   return push_transaction(trx, extra_kcpu, compression);
307 308
}

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
void print_action( const fc::variant& at ) {
   const auto& receipt = at["receipt"];
   auto receiver = receipt["receiver"].as_string();
   const auto& act = at["act"].get_object();
   auto code = act["account"].as_string();
   auto func = act["name"].as_string();
   auto args = fc::json::to_string( act["data"] );
   auto console = at["console"].as_string();

   /*
   if( code == "eosio" && func == "setcode" )
      args = args.substr(40)+"...";
   if( name(code) == config::system_account_name && func == "setabi" )
      args = args.substr(40)+"...";
   */
   if( args.size() > 100 ) args = args.substr(0,100) + "...";
   cout << "#" << std::setw(14) << right << receiver << " <= " << std::setw(28) << std::left << (code +"::" + func) << " " << args << "\n";
   if( console.size() ) {
      std::stringstream ss(console);
      string line;
      std::getline( ss, line );
      cout << ">> " << line << "\n";
   }
}

void print_action_tree( const fc::variant& action ) {
   print_action( action );
   const auto& inline_traces = action["inline_traces"].get_array();
   for( const auto& t : inline_traces ) {
      print_action_tree( t );
   }
}

D
Daniel Larimer 已提交
342
void print_result( const fc::variant& result ) { try {
343 344 345 346 347 348 349 350 351 352 353 354
      if (result.is_object() && result.get_object().contains("processed")) {
         const auto& processed = result["processed"];
         const auto& transaction_id = processed["id"].as_string();
         string status = processed["receipt"].is_object() ? processed["receipt"]["status"].as_string() : "failed";
         int64_t net = -1;
         int64_t cpu = -1;
         if( processed.get_object().contains( "receipt" )) {
            const auto& receipt = processed["receipt"];
            if( receipt.is_object()) {
               net = receipt["net_usage_words"].as_int64() * 8;
               cpu = receipt["cpu_usage_us"].as_int64();
            }
355 356
         }

357 358 359 360 361 362 363 364 365 366 367 368
         cerr << status << " transaction: " << transaction_id << "  ";
         if( net < 0 ) {
            cerr << "<unknown>";
         } else {
            cerr << net;
         }
         cerr << " bytes  ";
         if( cpu < 0 ) {
            cerr << "<unknown>";
         } else {
            cerr << cpu;
         }
D
Daniel Larimer 已提交
369

370 371 372 373 374 375 376 377 378 379 380 381 382
         cerr << " us\n";

         if( status == "failed" ) {
            auto soft_except = processed["except"].as<optional<fc::exception>>();
            if( soft_except ) {
               edump((soft_except->to_detail_string()));
            }
         } else {
            const auto& actions = processed["action_traces"].get_array();
            for( const auto& a : actions ) {
               print_action_tree( a );
            }
            wlog( "\rwarning: transaction executed locally, but may not be confirmed by the network yet" );
D
Daniel Larimer 已提交
383 384
         }
      } else {
K
Kevin Heifner 已提交
385
         cerr << fc::json::to_pretty_string( result ) << endl;
D
Daniel Larimer 已提交
386
      }
D
Daniel Larimer 已提交
387
} FC_CAPTURE_AND_RETHROW( (result) ) }
D
Daniel Larimer 已提交
388 389

using std::cout;
390
void send_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
D
Daniel Larimer 已提交
391 392 393
   auto result = push_actions( move(actions), extra_kcpu, compression);

   if( tx_print_json ) {
K
Kevin Heifner 已提交
394
      cout << fc::json::to_pretty_string( result ) << endl;
D
Daniel Larimer 已提交
395 396 397
   } else {
      print_result( result );
   }
398 399
}

400
void send_transaction( signed_transaction& trx, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none  ) {
D
Daniel Larimer 已提交
401
   auto result = push_transaction(trx, extra_kcpu, compression);
402

D
Daniel Larimer 已提交
403
   if( tx_print_json ) {
K
Kevin Heifner 已提交
404
      cout << fc::json::to_pretty_string( result ) << endl;
D
Daniel Larimer 已提交
405 406 407
   } else {
      print_result( result );
   }
408
}
409

M
Matias Romeo 已提交
410 411 412
chain::action create_newaccount(const name& creator, const name& newaccount, public_key_type owner, public_key_type active) {
   return action {
      tx_permission.empty() ? vector<chain::permission_level>{{creator,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
413
      eosio::chain::newaccount{
414 415 416
         .creator      = creator,
         .name         = newaccount,
         .owner        = eosio::chain::authority{1, {{owner, 1}}, {}},
417
         .active       = eosio::chain::authority{1, {{active, 1}}, {}}
M
Matias Romeo 已提交
418 419
      }
   };
420
}
421

D
Daniel Larimer 已提交
422 423 424 425 426 427 428
chain::action create_action(const vector<permission_level>& authorization, const account_name& code, const action_name& act, const fc::variant& args) {
   auto arg = fc::mutable_variant_object()
      ("code", code)
      ("action", act)
      ("args", args);

   auto result = call(json_to_bin_func, arg);
429
   wdump((result)(arg));
D
Daniel Larimer 已提交
430 431 432
   return chain::action{authorization, code, act, result.get_object()["binargs"].as<bytes>()};
}

K
Kevin Heifner 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
chain::action create_buyram(const name& creator, const name& newaccount, const asset& quantity) {
   fc::variant act_payload = fc::mutable_variant_object()
         ("payer", creator.to_string())
         ("receiver", newaccount.to_string())
         ("quant", quantity.to_string());
   return create_action(tx_permission.empty() ? vector<chain::permission_level>{{creator,config::active_name}} : get_account_permissions(tx_permission),
                        config::system_account_name, N(buyram), act_payload);
}

chain::action create_buyrambytes(const name& creator, const name& newaccount, uint32_t numbytes) {
   fc::variant act_payload = fc::mutable_variant_object()
         ("payer", creator.to_string())
         ("receiver", newaccount.to_string())
         ("bytes", numbytes);
   return create_action(tx_permission.empty() ? vector<chain::permission_level>{{creator,config::active_name}} : get_account_permissions(tx_permission),
                        config::system_account_name, N(buyrambytes), act_payload);
}

451
chain::action create_delegate(const name& from, const name& receiver, const asset& net, const asset& cpu, bool transfer) {
A
Anton Perkov 已提交
452 453 454 455
   fc::variant act_payload = fc::mutable_variant_object()
         ("from", from.to_string())
         ("receiver", receiver.to_string())
         ("stake_net_quantity", net.to_string())
456 457
         ("stake_cpu_quantity", cpu.to_string())
         ("transfer", transfer);
A
Anton Perkov 已提交
458 459 460 461
   return create_action(tx_permission.empty() ? vector<chain::permission_level>{{from,config::active_name}} : get_account_permissions(tx_permission),
                        config::system_account_name, N(delegatebw), act_payload);
}

D
Daniel Larimer 已提交
462 463
fc::variant regproducer_variant(const account_name& producer,
                                public_key_type key,
464
                                string url, uint16_t location = 0) {
465
   /*
D
Daniel Larimer 已提交
466
   fc::variant_object params = fc::mutable_variant_object()
A
arhag 已提交
467 468 469 470
         ("max_block_net_usage", config::default_max_block_net_usage)
         ("target_block_net_usage_pct", config::default_target_block_net_usage_pct)
         ("max_transaction_net_usage", config::default_max_transaction_net_usage)
         ("base_per_transaction_net_usage", config::default_base_per_transaction_net_usage)
471
         ("net_usage_leeway", config::default_net_usage_leeway)
A
arhag 已提交
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
         ("context_free_discount_net_usage_num", config::default_context_free_discount_net_usage_num)
         ("context_free_discount_net_usage_den", config::default_context_free_discount_net_usage_den)
         ("max_block_cpu_usage", config::default_max_block_cpu_usage)
         ("target_block_cpu_usage_pct", config::default_target_block_cpu_usage_pct)
         ("max_transaction_cpu_usage", config::default_max_transaction_cpu_usage)
         ("max_transaction_lifetime", config::default_max_trx_lifetime)
         ("deferred_trx_expiration_window", config::default_deferred_trx_expiration_window)
         ("max_transaction_delay", config::default_max_trx_delay)
         ("max_inline_action_size", config::default_max_inline_action_size)
         ("max_inline_depth", config::default_max_inline_action_depth)
         ("max_authority_depth", config::default_max_auth_depth)
         ("max_generated_transaction_count", config::default_max_gen_trx_count)
         ("max_storage_size", max_storage_size)
         ("percent_of_max_inflation_rate", percent_of_max_inflation_rate)
         ("storage_reserve_ratio", storage_reserve_ratio);
487
         */
D
Daniel Larimer 已提交
488 489 490

   return fc::mutable_variant_object()
            ("producer", producer)
491
            ("producer_key", key)
492 493 494
            ("url", url)
            ("location", 0)
            ;
D
Daniel Larimer 已提交
495 496
}

497
chain::action create_transfer(const string& contract, const name& sender, const name& recipient, asset amount, const string& memo ) {
M
Matias Romeo 已提交
498 499 500 501

   auto transfer = fc::mutable_variant_object
      ("from", sender)
      ("to", recipient)
502
      ("quantity", amount)
M
Matias Romeo 已提交
503
      ("memo", memo);
504

M
Matias Romeo 已提交
505
   auto args = fc::mutable_variant_object
506
      ("code", contract)
M
Matias Romeo 已提交
507 508
      ("action", "transfer")
      ("args", transfer);
509

M
Matias Romeo 已提交
510
   auto result = call(json_to_bin_func, args);
511

M
Matias Romeo 已提交
512 513
   return action {
      tx_permission.empty() ? vector<chain::permission_level>{{sender,config::active_name}} : get_account_permissions(tx_permission),
514
      contract, "transfer", result.get_object()["binargs"].as<bytes>()
M
Matias Romeo 已提交
515
   };
516
}
D
Daniel Larimer 已提交
517

D
Daniel Larimer 已提交
518
chain::action create_setabi(const name& account, const abi_def& abi) {
519
   return action {
M
Matias Romeo 已提交
520
      tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
521
      setabi{
M
Matias Romeo 已提交
522
         .account   = account,
523
         .abi       = fc::raw::pack(abi)
524 525
      }
   };
526 527
}

M
Matias Romeo 已提交
528
chain::action create_setcode(const name& account, const bytes& code) {
529
   return action {
M
Matias Romeo 已提交
530
      tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
531
      setcode{
M
Matias Romeo 已提交
532 533 534
         .account   = account,
         .vmtype    = 0,
         .vmversion = 0,
535
         .code      = code
536 537 538 539 540 541
      }
   };
}

chain::action create_updateauth(const name& account, const name& permission, const name& parent, const authority& auth) {
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
542
                   updateauth{account, permission, parent, auth}};
543 544
}

545 546
chain::action create_deleteauth(const name& account, const name& permission) {
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
547
                   deleteauth{account, permission}};
548 549
}

D
Daniel Larimer 已提交
550
chain::action create_linkauth(const name& account, const name& code, const name& type, const name& requirement) {
551
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
552
                   linkauth{account, code, type, requirement}};
553 554
}

D
Daniel Larimer 已提交
555
chain::action create_unlinkauth(const name& account, const name& code, const name& type) {
556
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
557
                   unlinkauth{account, code, type}};
558 559
}

560 561
fc::variant json_from_file_or_string(const string& file_or_str, fc::json::parse_type ptype = fc::json::legacy_parser)
{
562
   regex r("^[ \t]*[\{\[]");
D
Daniel Larimer 已提交
563
   if ( !regex_search(file_or_str, r) && fc::is_regular_file(file_or_str) ) {
564 565 566 567 568 569
      return fc::json::from_file(file_or_str, ptype);
   } else {
      return fc::json::from_string(file_or_str, ptype);
   }
}

D
Daniel Larimer 已提交
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
authority parse_json_authority(const std::string& authorityJsonOrFile) {
   try {
      return json_from_file_or_string(authorityJsonOrFile).as<authority>();
   } EOS_RETHROW_EXCEPTIONS(authority_type_exception, "Fail to parse Authority JSON '${data}'", ("data",authorityJsonOrFile))
}

authority parse_json_authority_or_key(const std::string& authorityJsonOrFile) {
   if (boost::istarts_with(authorityJsonOrFile, "EOS")) {
      try {
         return authority(public_key_type(authorityJsonOrFile));
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", authorityJsonOrFile))
   } else {
      return parse_json_authority(authorityJsonOrFile);
   }
}

586 587 588
asset to_asset( const string& code, const string& s ) {
   static map<eosio::chain::symbol_code, eosio::chain::symbol> cache;
   auto a = asset::from_string( s );
K
Kevin Heifner 已提交
589
   eosio::chain::symbol_code sym = a.get_symbol().to_symbol_code();
590 591 592 593 594 595 596 597 598 599 600
   auto it = cache.find( sym );
   auto sym_str = a.symbol_name();
   if ( it == cache.end() ) {
      auto json = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
                       ("code", code)
                       ("symbol", sym_str)
      );
      auto obj = json.get_object();
      auto obj_it = obj.find( sym_str );
      if (obj_it != obj.end()) {
         auto result = obj_it->value().as<eosio::chain_apis::read_only::get_currency_stats_result>();
K
Kevin Heifner 已提交
601
         auto p = cache.insert(make_pair( sym, result.max_supply.get_symbol() ));
602 603 604 605 606 607 608 609 610
         it = p.first;
      } else {
         FC_THROW("Symbol ${s} is not supported by token contract ${c}", ("s", sym_str)("c", code));
      }
   }
   auto expected_symbol = it->second;
   if ( a.decimals() < expected_symbol.decimals() ) {
      auto factor = expected_symbol.precision() / a.precision();
      auto a_old = a;
K
Kevin Heifner 已提交
611
      a = asset( a.get_amount() * factor, expected_symbol );
612 613 614 615 616 617 618 619 620 621
   } else if ( a.decimals() > expected_symbol.decimals() ) {
      FC_THROW("Too many decimal digits in ${a}, only ${d} supported", ("a", a)("d", expected_symbol.decimals()));
   } // else precision matches
   return a;
}

inline asset to_asset( const string& s ) {
   return to_asset( "eosio.token", s );
}

622
struct set_account_permission_subcommand {
623 624 625 626 627
   string accountStr;
   string permissionStr;
   string authorityJsonOrFile;
   string parentStr;

628
   set_account_permission_subcommand(CLI::App* accountCmd) {
629 630 631
      auto permissions = accountCmd->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
      permissions->add_option("account", accountStr, localized("The account to set/delete a permission authority for"))->required();
      permissions->add_option("permission", permissionStr, localized("The permission name to set/delete an authority for"))->required();
D
Daniel Larimer 已提交
632
      permissions->add_option("authority", authorityJsonOrFile, localized("[delete] NULL, [create/update] public key, JSON string, or filename defining the authority"))->required();
633
      permissions->add_option("parent", parentStr, localized("[create] The permission name of this parents permission (Defaults to: \"Active\")"));
634

M
Matias Romeo 已提交
635
      add_standard_transaction_options(permissions, "account@active");
636 637

      permissions->set_callback([this] {
638 639
         name account = name(accountStr);
         name permission = name(permissionStr);
640
         bool is_delete = boost::iequals(authorityJsonOrFile, "null");
641

642
         if (is_delete) {
M
Matias Romeo 已提交
643
            send_actions({create_deleteauth(account, permission)});
644
         } else {
D
Daniel Larimer 已提交
645
            authority auth = parse_json_authority_or_key(authorityJsonOrFile);
646

647
            name parent;
648
            if (parentStr.size() == 0 && permissionStr != "owner") {
649
               // see if we can auto-determine the proper parent
K
Kevin Heifner 已提交
650
               const auto account_result = call(get_account_func, fc::mutable_variant_object("account_name", accountStr));
651
               const auto& existing_permissions = account_result.get_object()["permissions"].get_array();
652 653
               auto permissionPredicate = [this](const auto& perm) {
                  return perm.is_object() &&
654
                        perm.get_object().contains("permission") &&
655
                        boost::equals(perm.get_object()["permission"].get_string(), permissionStr);
656 657 658 659
               };

               auto itr = boost::find_if(existing_permissions, permissionPredicate);
               if (itr != existing_permissions.end()) {
660
                  parent = name((*itr).get_object()["parent"].get_string());
661 662
               } else {
                  // if this is a new permission and there is no parent we default to "active"
663
                  parent = name(config::active_name);
664

665 666
               }
            } else {
667
               parent = name(parentStr);
668
            }
669

M
Matias Romeo 已提交
670
            send_actions({create_updateauth(account, permission, parent, auth)});
671
         }
672 673
      });
   }
674

675
};
676

677
struct set_action_permission_subcommand {
678 679 680 681 682
   string accountStr;
   string codeStr;
   string typeStr;
   string requirementStr;

683
   set_action_permission_subcommand(CLI::App* actionRoot) {
684 685 686 687 688
      auto permissions = actionRoot->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
      permissions->add_option("account", accountStr, localized("The account to set/delete a permission authority for"))->required();
      permissions->add_option("code", codeStr, localized("The account that owns the code for the action"))->required();
      permissions->add_option("type", typeStr, localized("the type of the action"))->required();
      permissions->add_option("requirement", requirementStr, localized("[delete] NULL, [set/update] The permission name require for executing the given action"))->required();
689

M
Matias Romeo 已提交
690
      add_standard_transaction_options(permissions, "account@active");
691 692

      permissions->set_callback([this] {
693 694 695
         name account = name(accountStr);
         name code = name(codeStr);
         name type = name(typeStr);
696
         bool is_delete = boost::iequals(requirementStr, "null");
697

698
         if (is_delete) {
M
Matias Romeo 已提交
699
            send_actions({create_unlinkauth(account, code, type)});
700
         } else {
701
            name requirement = name(requirementStr);
M
Matias Romeo 已提交
702
            send_actions({create_linkauth(account, code, type, requirement)});
703
         }
704 705 706
      });
   }
};
707

R
Roman Brod 已提交
708 709 710 711 712 713 714 715 716 717 718 719 720 721

bool port_used(uint16_t port) {
    using namespace boost::asio;

    io_service ios;
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port);
    boost::asio::ip::tcp::socket socket(ios);
    boost::system::error_code ec = error::would_block;
    //connecting/failing to connect to localhost should be always fast - don't care about timeouts
    socket.async_connect(endpoint, [&](const boost::system::error_code& error) { ec = error; } );
    do {
        ios.run_one();
    } while (ec == error::would_block);
    return !ec;
A
Anton Perkov 已提交
722 723
}

724 725 726
void try_port( uint16_t port, uint32_t duration ) {
   using namespace std::chrono;
   auto start_time = duration_cast<std::chrono::milliseconds>( system_clock::now().time_since_epoch() ).count();
727
   while ( !port_used(port)) {
728 729 730 731 732 733 734
      if (duration_cast<std::chrono::milliseconds>( system_clock::now().time_since_epoch()).count() - start_time > duration ) {
         std::cerr << "Unable to connect to keosd, if keosd is running please kill the process and try again.\n";
         throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, "Unable to connect to keosd")});
      }
   }
}

R
Roman Brod 已提交
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
void ensure_keosd_running() {
    auto parsed_url = parse_url(wallet_url);
    if (parsed_url.server != "localhost" && parsed_url.server == "127.0.0.1")
        return;

    auto wallet_port = std::stoi(parsed_url.port);
    if (wallet_port < 0 || wallet_port > 65535) {
        FC_THROW("port is not in valid range");
    }

    if (port_used(uint16_t(wallet_port)))  // Hopefully taken by keosd
        return;


    boost::filesystem::path binPath = boost::dll::program_location();
    binPath.remove_filename();
    // This extra check is necessary when running cleos like this: ./cleos ...
    if (binPath.filename_is_dot())
        binPath.remove_filename();
    binPath.append("keosd"); // if cleos and keosd are in the same installation directory
    if (!boost::filesystem::exists(binPath)) {
        binPath.remove_filename().remove_filename().append("keosd").append("keosd");
    }

    if (boost::filesystem::exists(binPath)) {
        namespace bp = boost::process;
        binPath = boost::filesystem::canonical(binPath);
762 763 764 765 766 767 768 769

        vector<std::string> pargs;
        pargs.push_back("--http-server-address=127.0.0.1:" + parsed_url.port);
        if (wallet_unlock_timeout > 0) {
            pargs.push_back("--unlock-timeout=" + fc::to_string(wallet_unlock_timeout));
        }

        ::boost::process::child keos(binPath, pargs,
R
Roman Brod 已提交
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
                                     bp::std_in.close(),
                                     bp::std_out > bp::null,
                                     bp::std_err > bp::null);
        if (keos.running()) {
            std::cerr << binPath << " launched" << std::endl;
            keos.detach();
            sleep(1);
        } else {
            std::cerr << "No wallet service listening on 127.0.0.1:"
                      << std::to_string(wallet_port) << ". Failed to launch " << binPath << std::endl;
        }
    } else {
        std::cerr << "No wallet service listening on 127.0.0.1: " << std::to_string(wallet_port)
                  << ". Cannot automatically start keosd because keosd was not found." << std::endl;
    }
A
Anton Perkov 已提交
785
}
D
Daniel Larimer 已提交
786

R
Roman Brod 已提交
787 788

CLI::callback_t obsoleted_option_host_port = [](CLI::results_t) {
D
Daniel Larimer 已提交
789 790 791 792 793 794 795 796 797
   std::cerr << localized("Host and port options (-H, --wallet-host, etc.) have been replaced with -u/--url and --wallet-url\n"
                          "Use for example -u http://localhost:8888 or --url https://example.invalid/\n");
   exit(1);
   return false;
};

struct register_producer_subcommand {
   string producer_str;
   string producer_key_str;
798
   string url;
799
   uint16_t loc = 0;
D
Daniel Larimer 已提交
800 801 802 803 804

   register_producer_subcommand(CLI::App* actionRoot) {
      auto register_producer = actionRoot->add_subcommand("regproducer", localized("Register a new producer"));
      register_producer->add_option("account", producer_str, localized("The account to register as a producer"))->required();
      register_producer->add_option("producer_key", producer_key_str, localized("The producer's public key"))->required();
805
      register_producer->add_option("url", url, localized("url where info about producer can be found"), true);
806
      register_producer->add_option("location", loc, localized("relative location for purpose of nearest neighbor scheduling"), 0);
D
Daniel Larimer 已提交
807 808 809 810 811 812 813 814 815
      add_standard_transaction_options(register_producer);


      register_producer->set_callback([this] {
         public_key_type producer_key;
         try {
            producer_key = public_key_type(producer_key_str);
         } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid producer public key: ${public_key}", ("public_key", producer_key_str))

816
         auto regprod_var = regproducer_variant(producer_str, producer_key, url, loc );
D
Daniel Larimer 已提交
817 818 819 820 821
         send_actions({create_action({permission_level{producer_str,config::active_name}}, config::system_account_name, N(regproducer), regprod_var)});
      });
   }
};

A
Anton Perkov 已提交
822 823 824 825 826 827 828 829 830
struct create_account_subcommand {
   string creator;
   string account_name;
   string owner_key_str;
   string active_key_str;
   string stake_net;
   string stake_cpu;
   uint32_t buy_ram_bytes_in_kbytes = 8;
   string buy_ram_eos;
831
   bool transfer;
A
Anton Perkov 已提交
832 833 834
   bool simple;

   create_account_subcommand(CLI::App* actionRoot, bool s) : simple(s) {
A
Anton Perkov 已提交
835
      auto createAccount = actionRoot->add_subcommand( (simple ? "account" : "newaccount"), localized("Create an account, buy ram, stake for bandwidth for the account"));
A
Anton Perkov 已提交
836 837 838
      createAccount->add_option("creator", creator, localized("The name of the account creating the new account"))->required();
      createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
      createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key for the new account"))->required();
839
      createAccount->add_option("ActiveKey", active_key_str, localized("The active public key for the new account"));
A
Anton Perkov 已提交
840 841 842 843 844 845 846 847 848 849

      if (!simple) {
         createAccount->add_option("--stake-net", stake_net,
                                   (localized("The amount of EOS delegated for net bandwidth")))->required();
         createAccount->add_option("--stake-cpu", stake_cpu,
                                   (localized("The amount of EOS delegated for CPU bandwidth")))->required();
         createAccount->add_option("--buy-ram-bytes", buy_ram_bytes_in_kbytes,
                                   (localized("The amount of RAM bytes to purchase for the new account in kilobytes KiB, default is 8 KiB")));
         createAccount->add_option("--buy-ram-EOS", buy_ram_eos,
                                   (localized("The amount of RAM bytes to purchase for the new account in EOS")));
850
         createAccount->add_flag("--transfer", transfer,
851
                                 (localized("Transfer voting power and right to unstake EOS to receiver")));
A
Anton Perkov 已提交
852 853
      }

854 855
      add_standard_transaction_options(createAccount);

A
Anton Perkov 已提交
856
      createAccount->set_callback([this] {
857
            if( !active_key_str.size() )
858
               active_key_str = owner_key_str;
A
Anton Perkov 已提交
859 860 861 862 863 864 865 866 867
            public_key_type owner_key, active_key;
            try {
               owner_key = public_key_type(owner_key_str);
            } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str));
            try {
               active_key = public_key_type(active_key_str);
            } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str));
            auto create = create_newaccount(creator, account_name, owner_key, active_key);
            if (!simple) {
868
               action buyram = !buy_ram_eos.empty() ? create_buyram(creator, account_name, to_asset(buy_ram_eos))
A
Anton Perkov 已提交
869
                  : create_buyrambytes(creator, account_name, buy_ram_bytes_in_kbytes * 1024);
870
               action delegate = create_delegate( creator, account_name, to_asset(stake_net), to_asset(stake_cpu), transfer);
A
Anton Perkov 已提交
871 872 873 874 875 876 877 878
               send_actions( { create, buyram, delegate } );
            } else {
               send_actions( { create } );
            }
      });
   }
};

D
Daniel Larimer 已提交
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
struct unregister_producer_subcommand {
   string producer_str;

   unregister_producer_subcommand(CLI::App* actionRoot) {
      auto unregister_producer = actionRoot->add_subcommand("unregprod", localized("Unregister an existing producer"));
      unregister_producer->add_option("account", producer_str, localized("The account to unregister as a producer"))->required();
      add_standard_transaction_options(unregister_producer);

      unregister_producer->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("producer", producer_str);

         send_actions({create_action({permission_level{producer_str,config::active_name}}, config::system_account_name, N(unregprod), act_payload)});
      });
   }
};

struct vote_producer_proxy_subcommand {
   string voter_str;
   string proxy_str;

   vote_producer_proxy_subcommand(CLI::App* actionRoot) {
      auto vote_proxy = actionRoot->add_subcommand("proxy", localized("Vote your stake through a proxy"));
      vote_proxy->add_option("voter", voter_str, localized("The voting account"))->required();
      vote_proxy->add_option("proxy", proxy_str, localized("The proxy account"))->required();
      add_standard_transaction_options(vote_proxy);

      vote_proxy->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("voter", voter_str)
                  ("proxy", proxy_str)
                  ("producers", std::vector<account_name>{});
         send_actions({create_action({permission_level{voter_str,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

struct vote_producers_subcommand {
   string voter_str;
D
Daniel Larimer 已提交
918
   vector<eosio::name> producer_names;
D
Daniel Larimer 已提交
919 920 921 922

   vote_producers_subcommand(CLI::App* actionRoot) {
      auto vote_producers = actionRoot->add_subcommand("prods", localized("Vote for one or more producers"));
      vote_producers->add_option("voter", voter_str, localized("The voting account"))->required();
D
Daniel Larimer 已提交
923
      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();
D
Daniel Larimer 已提交
924 925 926
      add_standard_transaction_options(vote_producers);

      vote_producers->set_callback([this] {
D
Daniel Larimer 已提交
927 928 929

         std::sort( producer_names.begin(), producer_names.end() );

D
Daniel Larimer 已提交
930 931 932
         fc::variant act_payload = fc::mutable_variant_object()
                  ("voter", voter_str)
                  ("proxy", "")
D
Daniel Larimer 已提交
933
                  ("producers", producer_names);
D
Daniel Larimer 已提交
934 935 936 937 938
         send_actions({create_action({permission_level{voter_str,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

939
struct approve_producer_subcommand {
940
   eosio::name voter;
941 942 943 944
   eosio::name producer_name;

   approve_producer_subcommand(CLI::App* actionRoot) {
      auto approve_producer = actionRoot->add_subcommand("approve", localized("Add one producer to list of voted producers"));
945
      approve_producer->add_option("voter", voter, localized("The voting account"))->required();
946 947 948 949 950 951 952 953
      approve_producer->add_option("producer", producer_name, localized("The account to vote for"))->required();
      add_standard_transaction_options(approve_producer);

      approve_producer->set_callback([this] {
            auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", name(config::system_account_name).to_string())
                               ("scope", name(config::system_account_name).to_string())
                               ("table", "voters")
954 955 956
                               ("table_key", "owner")
                               ("lower_bound", voter.value)
                               ("limit", 1)
957 958
            );
            auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
959 960
            if ( res.rows.empty() || res.rows[0]["owner"].as_string() != name(voter).to_string() ) {
               std::cerr << "Voter info not found for account " << voter << std::endl;
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
               return;
            }
            FC_ASSERT( 1 == res.rows.size(), "More than one voter_info for account" );
            auto prod_vars = res.rows[0]["producers"].get_array();
            vector<eosio::name> prods;
            for ( auto& x : prod_vars ) {
               prods.push_back( name(x.as_string()) );
            }
            prods.push_back( producer_name );
            std::sort( prods.begin(), prods.end() );
            auto it = std::unique( prods.begin(), prods.end() );
            if (it != prods.end() ) {
               std::cerr << "Producer \"" << producer_name << "\" is already on the list." << std::endl;
               return;
            }
            fc::variant act_payload = fc::mutable_variant_object()
977
               ("voter", voter)
978 979
               ("proxy", "")
               ("producers", prods);
980
            send_actions({create_action({permission_level{voter,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
981 982 983 984 985
      });
   }
};

struct unapprove_producer_subcommand {
986
   eosio::name voter;
987 988 989 990
   eosio::name producer_name;

   unapprove_producer_subcommand(CLI::App* actionRoot) {
      auto approve_producer = actionRoot->add_subcommand("unapprove", localized("Remove one producer from list of voted producers"));
991
      approve_producer->add_option("voter", voter, localized("The voting account"))->required();
992 993 994 995 996 997 998 999
      approve_producer->add_option("producer", producer_name, localized("The account to remove from voted producers"))->required();
      add_standard_transaction_options(approve_producer);

      approve_producer->set_callback([this] {
            auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", name(config::system_account_name).to_string())
                               ("scope", name(config::system_account_name).to_string())
                               ("table", "voters")
1000 1001 1002
                               ("table_key", "owner")
                               ("lower_bound", voter.value)
                               ("limit", 1)
1003 1004
            );
            auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
1005 1006
            if ( res.rows.empty() || res.rows[0]["owner"].as_string() != name(voter).to_string() ) {
               std::cerr << "Voter info not found for account " << voter << std::endl;
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
               return;
            }
            FC_ASSERT( 1 == res.rows.size(), "More than one voter_info for account" );
            auto prod_vars = res.rows[0]["producers"].get_array();
            vector<eosio::name> prods;
            for ( auto& x : prod_vars ) {
               prods.push_back( name(x.as_string()) );
            }
            auto it = std::remove( prods.begin(), prods.end(), producer_name );
            if (it == prods.end() ) {
               std::cerr << "Cannot remove: producer \"" << producer_name << "\" is not on the list." << std::endl;
               return;
            }
            prods.erase( it, prods.end() ); //should always delete only one element
            fc::variant act_payload = fc::mutable_variant_object()
1022
               ("voter", voter)
1023 1024
               ("proxy", "")
               ("producers", prods);
1025
            send_actions({create_action({permission_level{voter,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
1026 1027 1028 1029
      });
   }
};

A
Anton Perkov 已提交
1030
struct list_producers_subcommand {
1031
   bool print_json = false;
1032
   uint32_t limit = 50;
1033
   std::string lower;
A
Anton Perkov 已提交
1034 1035 1036

   list_producers_subcommand(CLI::App* actionRoot) {
      auto list_producers = actionRoot->add_subcommand("listproducers", localized("List producers"));
1037 1038
      list_producers->add_flag("--json,-j", print_json, localized("Output in JSON format"));
      list_producers->add_option("-l,--limit", limit, localized("The maximum number of rows to return"));
1039
      list_producers->add_option("-L,--lower", lower, localized("lower bound value of key, defaults to first"));
A
Anton Perkov 已提交
1040
      list_producers->set_callback([this] {
1041 1042 1043 1044 1045
         auto rawResult = call(get_producers_func, fc::mutable_variant_object
            ("json", true)("lower_bound", lower)("limit", limit));
         if ( print_json ) {
            std::cout << fc::json::to_pretty_string(rawResult) << std::endl;
            return;
A
Anton Perkov 已提交
1046
         }
1047 1048 1049 1050 1051
         auto result = rawResult.as<eosio::chain_apis::read_only::get_producers_result>();
         if ( result.rows.empty() ) {
            std::cout << "No producers found" << std::endl;
            return;
         }
1052 1053 1054 1055
         auto weight = result.total_producer_vote_weight;
         if ( !weight )
            weight = 1;
         printf("%-13s %-54s %-59s %s\n", "Producer", "Producer key", "Url", "Scaled votes");
1056
         for ( auto& row : result.rows )
1057
            printf("%-13.13s %-54.54s %-59.59s %1.4f\n", 
1058 1059 1060
                   row["owner"].as_string().c_str(),
                   row["producer_key"].as_string().c_str(),
                   row["url"].as_string().c_str(),
1061
                   row["total_votes"].as_double() / weight);
1062 1063 1064
         if ( !result.more.empty() )
            std::cout << "-L " << result.more << " for more" << std::endl;
      });
A
Anton Perkov 已提交
1065 1066 1067
   }
};

D
Daniel Larimer 已提交
1068 1069 1070 1071 1072 1073
struct delegate_bandwidth_subcommand {
   string from_str;
   string receiver_str;
   string stake_net_amount;
   string stake_cpu_amount;
   string stake_storage_amount;
1074
   bool transfer = false;
D
Daniel Larimer 已提交
1075 1076 1077 1078 1079 1080 1081

   delegate_bandwidth_subcommand(CLI::App* actionRoot) {
      auto delegate_bandwidth = actionRoot->add_subcommand("delegatebw", localized("Delegate bandwidth"));
      delegate_bandwidth->add_option("from", from_str, localized("The account to delegate bandwidth from"))->required();
      delegate_bandwidth->add_option("receiver", receiver_str, localized("The account to receive the delegated bandwidth"))->required();
      delegate_bandwidth->add_option("stake_net_quantity", stake_net_amount, localized("The amount of EOS to stake for network bandwidth"))->required();
      delegate_bandwidth->add_option("stake_cpu_quantity", stake_cpu_amount, localized("The amount of EOS to stake for CPU bandwidth"))->required();
1082
      delegate_bandwidth->add_flag("--transfer", transfer, localized("Transfer voting power and right to unstake EOS to receiver"));
D
Daniel Larimer 已提交
1083 1084 1085 1086 1087 1088
      add_standard_transaction_options(delegate_bandwidth);

      delegate_bandwidth->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("from", from_str)
                  ("receiver", receiver_str)
1089
                  ("stake_net_quantity", to_asset(stake_net_amount))
1090 1091 1092
                  ("stake_cpu_quantity", to_asset(stake_cpu_amount))
                  ("transfer", transfer)
                  ;
D
Daniel Larimer 已提交
1093
                  wdump((act_payload));
D
Daniel Larimer 已提交
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
         send_actions({create_action({permission_level{from_str,config::active_name}}, config::system_account_name, N(delegatebw), act_payload)});
      });
   }
};

struct undelegate_bandwidth_subcommand {
   string from_str;
   string receiver_str;
   string unstake_net_amount;
   string unstake_cpu_amount;
   uint64_t unstake_storage_bytes;

   undelegate_bandwidth_subcommand(CLI::App* actionRoot) {
      auto undelegate_bandwidth = actionRoot->add_subcommand("undelegatebw", localized("Undelegate bandwidth"));
      undelegate_bandwidth->add_option("from", from_str, localized("The account undelegating bandwidth"))->required();
      undelegate_bandwidth->add_option("receiver", receiver_str, localized("The account to undelegate bandwidth from"))->required();
      undelegate_bandwidth->add_option("unstake_net_quantity", unstake_net_amount, localized("The amount of EOS to undelegate for network bandwidth"))->required();
      undelegate_bandwidth->add_option("unstake_cpu_quantity", unstake_cpu_amount, localized("The amount of EOS to undelegate for CPU bandwidth"))->required();
      add_standard_transaction_options(undelegate_bandwidth);

      undelegate_bandwidth->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("from", from_str)
                  ("receiver", receiver_str)
1118 1119
                  ("unstake_net_quantity", to_asset(unstake_net_amount))
                  ("unstake_cpu_quantity", to_asset(unstake_cpu_amount));
D
Daniel Larimer 已提交
1120 1121 1122 1123 1124
         send_actions({create_action({permission_level{from_str,config::active_name}}, config::system_account_name, N(undelegatebw), act_payload)});
      });
   }
};

A
Anton Perkov 已提交
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
struct list_bw_subcommand {
   eosio::name account;
   bool print_json = false;

   list_bw_subcommand(CLI::App* actionRoot) {
      auto list_bw = actionRoot->add_subcommand("listbw", localized("List delegated bandwidth"));
      list_bw->add_option("account", account, localized("The account delegated bandwidth"))->required();
      list_bw->add_flag("--json,-j", print_json, localized("Output in JSON format") );

      list_bw->set_callback([this] {
            //get entire table in scope of user account
            auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", name(config::system_account_name).to_string())
                               ("scope", account.to_string())
                               ("table", "delband")
            );
            if (!print_json) {
               auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
               if ( !res.rows.empty() ) {
                  std::cout << std::setw(13) << std::left << "Receiver" << std::setw(21) << std::left << "Net bandwidth"
1145
                            << std::setw(21) << std::left << "CPU bandwidth" << std::endl;
A
Anton Perkov 已提交
1146 1147 1148
                  for ( auto& r : res.rows ){
                     std::cout << std::setw(13) << std::left << r["to"].as_string()
                               << std::setw(21) << std::left << r["net_weight"].as_string()
1149
                               << std::setw(21) << std::left << r["cpu_weight"].as_string()
A
Anton Perkov 已提交
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
                               << std::endl;
                  }
               } else {
                  std::cerr << "Delegated bandwidth not found" << std::endl;
               }
            } else {
               std::cout << fc::json::to_pretty_string(result) << std::endl;
            }
      });
   }
};

1162 1163 1164 1165 1166 1167 1168
struct buyram_subcommand {
   string from_str;
   string receiver_str;
   string amount;

   buyram_subcommand(CLI::App* actionRoot) {
      auto buyram = actionRoot->add_subcommand("buyram", localized("Buy RAM"));
1169 1170
      buyram->add_option("payer", from_str, localized("The account paying for RAM"))->required();
      buyram->add_option("receiver", receiver_str, localized("The account receiving bought RAM"))->required();
1171 1172 1173 1174
      buyram->add_option("tokens", amount, localized("The amount of EOS to pay for RAM"))->required();
      add_standard_transaction_options(buyram);
      buyram->set_callback([this] {
            fc::variant act_payload = fc::mutable_variant_object()
1175
               ("payer", from_str)
1176
               ("receiver", receiver_str)
1177
               ("quant", to_asset(amount));
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
            send_actions({create_action({permission_level{from_str,config::active_name}}, config::system_account_name, N(buyram), act_payload)});
         });
   }
};

struct sellram_subcommand {
   string from_str;
   string receiver_str;
   uint64_t amount;

   sellram_subcommand(CLI::App* actionRoot) {
      auto sellram = actionRoot->add_subcommand("sellram", localized("Sell RAM"));
1190
      sellram->add_option("account", receiver_str, localized("The account to receive EOS for sold RAM"))->required();
1191 1192 1193 1194 1195
      sellram->add_option("bytes", amount, localized("Number of RAM bytes to sell"))->required();
      add_standard_transaction_options(sellram);

      sellram->set_callback([this] {
            fc::variant act_payload = fc::mutable_variant_object()
1196
               ("account", receiver_str)
1197
               ("bytes", amount);
1198
            send_actions({create_action({permission_level{receiver_str,config::active_name}}, config::system_account_name, N(sellram), act_payload)});
1199 1200 1201 1202
         });
   }
};

D
Daniel Larimer 已提交
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
struct claimrewards_subcommand {
   string owner;

   claimrewards_subcommand(CLI::App* actionRoot) {
      auto claim_rewards = actionRoot->add_subcommand("claimrewards", localized("Claim producer rewards"));
      claim_rewards->add_option("owner", owner, localized("The account to claim rewards for"))->required();
      add_standard_transaction_options(claim_rewards);

      claim_rewards->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("owner", owner);
         send_actions({create_action({permission_level{owner,config::active_name}}, config::system_account_name, N(claimrewards), act_payload)});
      });
   }
};

struct regproxy_subcommand {
   string proxy;

   regproxy_subcommand(CLI::App* actionRoot) {
      auto register_proxy = actionRoot->add_subcommand("regproxy", localized("Register an account as a proxy (for voting)"));
      register_proxy->add_option("proxy", proxy, localized("The proxy account to register"))->required();
      add_standard_transaction_options(register_proxy);

      register_proxy->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
1229 1230
                  ("proxy", proxy)
                  ("isproxy", true);
D
Daniel Larimer 已提交
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
         send_actions({create_action({permission_level{proxy,config::active_name}}, config::system_account_name, N(regproxy), act_payload)});
      });
   }
};

struct unregproxy_subcommand {
   string proxy;

   unregproxy_subcommand(CLI::App* actionRoot) {
      auto unregister_proxy = actionRoot->add_subcommand("unregproxy", localized("Unregister an account as a proxy (for voting)"));
      unregister_proxy->add_option("proxy", proxy, localized("The proxy account to unregister"))->required();
      add_standard_transaction_options(unregister_proxy);

      unregister_proxy->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
1246 1247 1248
                  ("proxy", proxy)
                  ("isproxy", false);
         send_actions({create_action({permission_level{proxy,config::active_name}}, config::system_account_name, N(regproxy), act_payload)});
D
Daniel Larimer 已提交
1249 1250 1251 1252 1253
      });
   }
};

struct canceldelay_subcommand {
1254 1255
   string canceling_account;
   string canceling_permission;
D
Daniel Larimer 已提交
1256 1257 1258 1259
   string trx_id;

   canceldelay_subcommand(CLI::App* actionRoot) {
      auto cancel_delay = actionRoot->add_subcommand("canceldelay", localized("Cancel a delayed transaction"));
1260 1261
      cancel_delay->add_option("canceling_account", canceling_account, localized("Account from authorization on the original delayed transaction"))->required();
      cancel_delay->add_option("canceling_permission", canceling_permission, localized("Permission from authorization on the original delayed transaction"))->required();
D
Daniel Larimer 已提交
1262 1263 1264 1265
      cancel_delay->add_option("trx_id", trx_id, localized("The transaction id of the original delayed transaction"))->required();
      add_standard_transaction_options(cancel_delay);

      cancel_delay->set_callback([this] {
1266
         const auto canceling_auth = permission_level{canceling_account, canceling_permission};
D
Daniel Larimer 已提交
1267
         fc::variant act_payload = fc::mutable_variant_object()
1268
                  ("canceling_auth", canceling_auth)
D
Daniel Larimer 已提交
1269
                  ("trx_id", trx_id);
1270
         send_actions({create_action({canceling_auth}, config::system_account_name, N(canceldelay), act_payload)});
D
Daniel Larimer 已提交
1271 1272 1273 1274
      });
   }
};

1275
void get_account( const string& accountName, bool json_format ) {
1276 1277
   auto json = call(get_account_func, fc::mutable_variant_object("account_name", accountName));
   auto res = json.as<eosio::chain_apis::read_only::get_account_results>();
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293

   if (!json_format) {
      std::cout << "privileged: " << ( res.privileged ? "true" : "false") << std::endl;

      constexpr size_t indent_size = 5;
      const string indent(indent_size, ' ');

      std::cout << "permissions: " << std::endl;
      unordered_map<name, vector<name>/*children*/> tree;
      vector<name> roots; //we don't have multiple roots, but we can easily handle them here, so let's do it just in case
      unordered_map<name, eosio::chain_apis::permission> cache;
      for ( auto& perm : res.permissions ) {
         if ( perm.parent ) {
            tree[perm.parent].push_back( perm.perm_name );
         } else {
            roots.push_back( perm.perm_name );
A
Anton Perkov 已提交
1294
         }
1295 1296 1297
         auto name = perm.perm_name; //keep copy before moving `perm`, since thirst argument of emplace can be evaluated first
         // looks a little crazy, but should be efficient
         cache.insert( std::make_pair(name, std::move(perm)) );
1298
      }
1299 1300 1301 1302 1303 1304 1305 1306
      std::function<void (account_name, int)> dfs_print = [&]( account_name name, int depth ) -> void {
         auto& p = cache.at(name);
         std::cout << indent << std::string(depth*3, ' ') << name << ' ' << std::setw(5) << p.required_auth.threshold << ":    ";
         for ( auto it = p.required_auth.keys.begin(); it != p.required_auth.keys.end(); ++it ) {
            if ( it != p.required_auth.keys.begin() ) {
               std::cout  << ", ";
            }
            std::cout << it->weight << ' ' << string(it->key);
1307
         }
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
         for ( auto& acc : p.required_auth.accounts ) {
            std::cout << acc.weight << ' ' << string(acc.permission.actor) << '@' << string(acc.permission.permission) << ", ";
         }
         std::cout << std::endl;
         auto it = tree.find( name );
         if (it != tree.end()) {
            auto& children = it->second;
            sort( children.begin(), children.end() );
            for ( auto& n : children ) {
               // we have a tree, not a graph, so no need to check for already visited nodes
               dfs_print( n, depth+1 );
            }
         } // else it's a leaf node
      };
      std::sort(roots.begin(), roots.end());
      for ( auto r : roots ) {
         dfs_print( r, 0 );
      }
1326

1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350
      auto to_pretty_net = []( double bytes ) {
         string unit = "bytes";

         if( bytes >= 1024*1024*1024*1024ll ){
            unit = "Tb";
            bytes /= 1024*1024*1024*1024ll;
         }else if( bytes >= 1024*1024*1024 ){
            unit = "Gb";
            bytes /= 1024*1024*1024;
         } else if( bytes >= 1024*1024 ){
            unit = "Mb";
            bytes /= 1024*1024;
         } else if( bytes >= 1024 ) {
            unit = "Kb";
            bytes /= 1024;
         }
         std::stringstream ss;
         ss << setprecision(4);
         ss << bytes << " " << std::left << setw(5) << unit;
         return ss.str();
      };



1351
      std::cout << "memory: " << std::endl
1352
                << 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;
1353

1354
      std::cout << "net bandwidth: (averaged over 3 days)" << std::endl;
1355
      if ( res.total_resources.is_object() ) {
1356
         asset net_own = res.delegated_bandwidth.is_object() ? asset::from_string( res.delegated_bandwidth.get_object()["net_weight"].as_string() ) : asset(0) ;
1357
         auto net_others = to_asset(res.total_resources.get_object()["net_weight"].as_string()) - net_own;
1358 1359 1360 1361 1362
         std::cout << indent << "staked:" << std::setw(20) << net_own
                   << std::string(11, ' ') << "(total stake delegated from account to self)" << std::endl
                   << indent << "delegated:" << std::setw(17) << net_others
                   << std::string(11, ' ') << "(total staked delegated to account from others)" << std::endl;
      }
1363

1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389

      auto to_pretty_time = []( double micro ) {
         string unit = "us";

         if( micro > 1000000*60 ) {
            micro /= 1000000*60*60ll;
            unit = "hr";
         }
         else if( micro > 1000000*60 ) {
            micro /= 1000000*60;
            unit = "min";
         }
         else if( micro > 1000000 ) {
            micro /= 1000000;
            unit = "sec";
         }
         else if( micro > 1000 ) {
            micro /= 1000;
            unit = "ms";
         }
         std::stringstream ss;
         ss << setprecision(4);
         ss << micro<< " " << std::left << setw(5) << unit;
         return ss.str();
      };

1390 1391

      std::cout << std::fixed << setprecision(3);
1392 1393 1394 1395 1396 1397 1398
      std::cout << indent << std::left << std::setw(11) << "used:"      << std::right << std::setw(18) << to_pretty_net( res.net_limit.used ) << "\n";
      std::cout << indent << std::left << std::setw(11) << "available:" << std::right << std::setw(18) << to_pretty_net( res.net_limit.available ) << "\n";
      std::cout << indent << std::left << std::setw(11) << "limit:"     << std::right << std::setw(18) << to_pretty_net( res.net_limit.max ) << "\n";
      std::cout << std::endl;


      std::cout << "cpu bandwidth: (averaged over 3 days)" << std::endl;
1399 1400


1401
      if ( res.total_resources.is_object() ) {
1402
         asset cpu_own = res.delegated_bandwidth.is_object() ? asset::from_string( res.delegated_bandwidth.get_object()["cpu_weight"].as_string() ) : asset(0) ;
1403
         auto cpu_others = to_asset(res.total_resources.get_object()["cpu_weight"].as_string()) - cpu_own;
1404 1405 1406 1407 1408
         std::cout << indent << "staked:" << std::setw(20) << cpu_own
                   << std::string(11, ' ') << "(total stake delegated from account to self)" << std::endl
                   << indent << "delegated:" << std::setw(17) << cpu_others
                   << std::string(11, ' ') << "(total staked delegated to account from others)" << std::endl;
      }
1409

1410 1411 1412 1413 1414 1415 1416

      std::cout << std::fixed << setprecision(3);
      std::cout << indent << std::left << std::setw(11) << "used:"      << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.used ) << "\n";
      std::cout << indent << std::left << std::setw(11) << "available:" << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.available ) << "\n";
      std::cout << indent << std::left << std::setw(11) << "limit:"     << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.max ) << "\n";
      std::cout << std::endl;

1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429

      if ( res.voter_info.is_object() ) {
         auto& obj = res.voter_info.get_object();
         string proxy = obj["proxy"].as_string();
         if ( proxy.empty() ) {
            auto& prods = obj["producers"].get_array();
            std::cout << "producers:";
            if ( !prods.empty() ) {
               for ( int i = 0; i < prods.size(); ++i ) {
                  if ( i%3 == 0 ) {
                     std::cout << std::endl << indent;
                  }
                  std::cout << std::setw(16) << std::left << prods[i].as_string();
1430
               }
1431 1432 1433
               std::cout << std::endl;
            } else {
               std::cout << indent << "<not voted>" << std::endl;
1434 1435
            }
         } else {
1436
            std::cout << "proxy:" << indent << proxy << std::endl;
1437 1438
         }
      }
1439 1440 1441
      std::cout << std::endl;
   } else {
      std::cout << fc::json::to_pretty_string(json) << std::endl;
1442
   }
1443 1444
}

1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
CLI::callback_t header_opt_callback = [](CLI::results_t res) {
   vector<string>::iterator itr;

   for (itr = res.begin(); itr != res.end(); itr++) {
       headers.push_back(*itr);
   }

   return true;
};

1455
int main( int argc, char** argv ) {
1456 1457 1458 1459
   setlocale(LC_ALL, "");
   bindtextdomain(locale_domain, locale_path);
   textdomain(locale_domain);

1460
   CLI::App app{"Command Line Interface to EOSIO Client"};
1461
   app.require_subcommand();
R
Roman Brod 已提交
1462 1463 1464 1465
   app.add_option( "-H,--host", obsoleted_option_host_port, localized("the host where nodeos is running") )->group("hidden");
   app.add_option( "-p,--port", obsoleted_option_host_port, localized("the port where nodeos is running") )->group("hidden");
   app.add_option( "--wallet-host", obsoleted_option_host_port, localized("the host where keosd is running") )->group("hidden");
   app.add_option( "--wallet-port", obsoleted_option_host_port, localized("the port where keosd is running") )->group("hidden");
D
Daniel Larimer 已提交
1466 1467 1468

   app.add_option( "-u,--url", url, localized("the http/https URL where nodeos is running"), true );
   app.add_option( "--wallet-url", wallet_url, localized("the http/https URL where keosd is running"), true );
1469

1470
   app.add_option( "-r,--header", header_opt_callback, localized("pass specific HTTP header; repeat this option to pass multiple headers"));
1471
   app.add_flag( "-n,--no-verify", no_verify, localized("don't verify peer certificate when using HTTPS"));
R
Roman Brod 已提交
1472
   app.set_callback([] { ensure_keosd_running();});
A
Anton Perkov 已提交
1473

1474
   bool verbose_errors = false;
D
Daniel Larimer 已提交
1475
   app.add_flag( "-v,--verbose", verbose_errors, localized("output verbose actions on error"));
1476

1477 1478 1479 1480 1481 1482 1483
   auto version = app.add_subcommand("version", localized("Retrieve version information"), false);
   version->require_subcommand();

   version->add_subcommand("client", localized("Retrieve version information of the client"))->set_callback([] {
     std::cout << localized("Build version: ${ver}", ("ver", eosio::client::config::version_str)) << std::endl;
   });

1484
   // Create subcommand
1485
   auto create = app.add_subcommand("create", localized("Create various items, on and off the blockchain"), false);
N
Nathan Hourt 已提交
1486 1487 1488
   create->require_subcommand();

   // create key
D
Daniel Larimer 已提交
1489 1490 1491 1492 1493 1494
   create->add_subcommand("key", localized("Create a new keypair and print the public and private keys"))->set_callback( [](){
      auto pk    = private_key_type::generate();
      auto privs = string(pk);
      auto pubs  = string(pk.get_public_key());
      std::cout << localized("Private key: ${key}", ("key",  privs) ) << std::endl;
      std::cout << localized("Public key: ${key}", ("key", pubs ) ) << std::endl;
N
Nathan Hourt 已提交
1495 1496 1497
   });

   // create account
A
Anton Perkov 已提交
1498
   auto createAccount = create_account_subcommand( create, true /*simple*/ );
1499

1500
   // Get subcommand
1501
   auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"), false);
N
Nathan Hourt 已提交
1502 1503 1504
   get->require_subcommand();

   // get info
1505
   get->add_subcommand("info", localized("Get current blockchain information"))->set_callback([] {
N
Nathan Hourt 已提交
1506 1507 1508 1509 1510
      std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
   });

   // get block
   string blockArg;
1511 1512
   auto getBlock = get->add_subcommand("block", localized("Retrieve a full block from the blockchain"), false);
   getBlock->add_option("block", blockArg, localized("The number or ID of the block to retrieve"))->required();
N
Nathan Hourt 已提交
1513 1514 1515 1516 1517 1518 1519
   getBlock->set_callback([&blockArg] {
      auto arg = fc::mutable_variant_object("block_num_or_id", blockArg);
      std::cout << fc::json::to_pretty_string(call(get_block_func, arg)) << std::endl;
   });

   // get account
   string accountName;
1520
   bool print_json;
1521 1522
   auto getAccount = get->add_subcommand("account", localized("Retrieve an account from the blockchain"), false);
   getAccount->add_option("name", accountName, localized("The name of the account to retrieve"))->required();
1523 1524
   getAccount->add_flag("--json,-j", print_json, localized("Output in JSON format") );
   getAccount->set_callback([&]() { get_account(accountName, print_json); });
N
Nathan Hourt 已提交
1525

D
Daniel Larimer 已提交
1526 1527 1528
   // get code
   string codeFilename;
   string abiFilename;
1529 1530 1531 1532
   auto getCode = get->add_subcommand("code", localized("Retrieve the code and ABI for an account"), false);
   getCode->add_option("name", accountName, localized("The name of the account whose code should be retrieved"))->required();
   getCode->add_option("-c,--code",codeFilename, localized("The name of the file to save the contract .wast to") );
   getCode->add_option("-a,--abi",abiFilename, localized("The name of the file to save the contract .abi to") );
D
Daniel Larimer 已提交
1533
   getCode->set_callback([&] {
K
Kevin Heifner 已提交
1534
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", accountName));
D
Daniel Larimer 已提交
1535

1536
      std::cout << localized("code hash: ${code_hash}", ("code_hash", result["code_hash"].as_string())) << std::endl;
D
Daniel Larimer 已提交
1537 1538

      if( codeFilename.size() ){
1539
         std::cout << localized("saving wast to ${codeFilename}", ("codeFilename", codeFilename)) << std::endl;
D
Daniel Larimer 已提交
1540 1541 1542 1543 1544
         auto code = result["wast"].as_string();
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
1545
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
1546 1547 1548 1549 1550 1551
         auto abi  = fc::json::to_pretty_string( result["abi"] );
         std::ofstream abiout( abiFilename.c_str() );
         abiout << abi;
      }
   });

1552
   // get table
D
Daniel Larimer 已提交
1553 1554 1555 1556 1557
   string scope;
   string code;
   string table;
   string lower;
   string upper;
1558
   string table_key;
D
Daniel Larimer 已提交
1559 1560
   bool binary = false;
   uint32_t limit = 10;
1561
   auto getTable = get->add_subcommand( "table", localized("Retrieve the contents of a database table"), false);
1562 1563
   getTable->add_option( "contract", code, localized("The contract who owns the table") )->required();
   getTable->add_option( "scope", scope, localized("The scope within the contract in which the table is found") )->required();
1564 1565 1566
   getTable->add_option( "table", table, localized("The name of the table as specified by the contract abi") )->required();
   getTable->add_option( "-b,--binary", binary, localized("Return the value as BINARY rather than using abi to interpret as JSON") );
   getTable->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") );
1567
   getTable->add_option( "-k,--key", table_key, localized("The name of the key to index by as defined by the abi, defaults to primary key") );
1568 1569
   getTable->add_option( "-L,--lower", lower, localized("JSON representation of lower bound value of key, defaults to first") );
   getTable->add_option( "-U,--upper", upper, localized("JSON representation of upper bound value value of key, defaults to last") );
D
Daniel Larimer 已提交
1570 1571 1572 1573

   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("code",code)
1574
                         ("scope",scope)
D
Daniel Larimer 已提交
1575
                         ("table",table)
1576 1577 1578 1579
                         ("table_key",table_key)
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
D
Daniel Larimer 已提交
1580 1581 1582 1583 1584 1585
                         );

      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
   });

1586 1587 1588 1589
   // currency accessors
   // get currency balance
   string symbol;
   auto get_currency = get->add_subcommand( "currency", localized("Retrieve information related to standard currencies"), true);
1590
   get_currency->require_subcommand();
1591 1592 1593 1594 1595
   auto get_balance = get_currency->add_subcommand( "balance", localized("Retrieve the balance of an account for a given currency"), false);
   get_balance->add_option( "contract", code, localized("The contract that operates the currency") )->required();
   get_balance->add_option( "account", accountName, localized("The account to query balances for") )->required();
   get_balance->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") );
   get_balance->set_callback([&] {
D
Daniel Larimer 已提交
1596
      auto result = call(get_currency_balance_func, fc::mutable_variant_object
1597
         ("account", accountName)
1598
         ("code", code)
D
Daniel Larimer 已提交
1599
         ("symbol", symbol.empty() ? fc::variant() : symbol)
1600 1601
      );

1602
      const auto& rows = result.get_array();
D
Daniel Larimer 已提交
1603 1604
      for( const auto& r : rows ) {
         std::cout << r.as_string()
1605 1606 1607
                   << std::endl;
      }
   });
D
Daniel Larimer 已提交
1608

1609 1610
   auto get_currency_stats = get_currency->add_subcommand( "stats", localized("Retrieve the stats of for a given currency"), false);
   get_currency_stats->add_option( "contract", code, localized("The contract that operates the currency") )->required();
D
Daniel Larimer 已提交
1611
   get_currency_stats->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") )->required();
1612
   get_currency_stats->set_callback([&] {
1613
      auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
1614
         ("code", code)
1615
         ("symbol", symbol)
1616 1617
      );

D
Daniel Larimer 已提交
1618 1619
      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
1620
   });
D
Daniel Larimer 已提交
1621

1622
   // get accounts
1623
   string public_key_str;
1624
   auto getAccounts = get->add_subcommand("accounts", localized("Retrieve accounts associated with a public key"), false);
1625
   getAccounts->add_option("public_key", public_key_str, localized("The public key to retrieve accounts for"))->required();
1626
   getAccounts->set_callback([&] {
1627 1628 1629
      public_key_type public_key;
      try {
         public_key = public_key_type(public_key_str);
1630
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", public_key_str))
1631
      auto arg = fc::mutable_variant_object( "public_key", public_key);
1632 1633 1634
      std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
   });

1635

1636 1637
   // get servants
   string controllingAccount;
1638 1639
   auto getServants = get->add_subcommand("servants", localized("Retrieve accounts which are servants of a given account "), false);
   getServants->add_option("account", controllingAccount, localized("The name of the controlling account"))->required();
1640
   getServants->set_callback([&] {
1641
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
1642 1643 1644
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

N
Nathan Hourt 已提交
1645
   // get transaction
1646
   string transaction_id_str;
1647
   auto getTransaction = get->add_subcommand("transaction", localized("Retrieve a transaction from the blockchain"), false);
1648
   getTransaction->add_option("id", transaction_id_str, localized("ID of the transaction to retrieve"))->required();
N
Nathan Hourt 已提交
1649
   getTransaction->set_callback([&] {
1650 1651
      transaction_id_type transaction_id;
      try {
D
Daniel Larimer 已提交
1652
         while( transaction_id_str.size() < 64 ) transaction_id_str += "0";
1653
         transaction_id = transaction_id_type(transaction_id_str);
1654
      } EOS_RETHROW_EXCEPTIONS(transaction_id_type_exception, "Invalid transaction ID: ${transaction_id}", ("transaction_id", transaction_id_str))
D
Daniel Larimer 已提交
1655
      auto arg= fc::mutable_variant_object( "id", transaction_id);
N
Nathan Hourt 已提交
1656 1657
      std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
   });
1658

A
arhag 已提交
1659
   // get actions
A
Anton Perkov 已提交
1660
   string account_name;
1661 1662
   string skip_seq_str;
   string num_seq_str;
D
Daniel Larimer 已提交
1663
   bool printjson = false;
1664 1665
   bool fullact = false;
   bool prettyact = false;
1666
   bool printconsole = false;
D
Daniel Larimer 已提交
1667 1668 1669 1670 1671 1672

   int32_t pos_seq = -1;
   int32_t offset = -20;
   auto getActions = get->add_subcommand("actions", localized("Retrieve all actions with specific account name referenced in authorization or receiver"), false);
   getActions->add_option("account_name", account_name, localized("name of account to query on"))->required();
   getActions->add_option("pos", pos_seq, localized("sequence number of action for this account, -1 for last"));
D
Daniel Larimer 已提交
1673
   getActions->add_option("offset", offset, localized("get actions [pos,pos+offset] for positive offset or [pos-offset,pos) for negative offset"));
D
Daniel Larimer 已提交
1674
   getActions->add_flag("--json,-j", printjson, localized("print full json"));
1675 1676
   getActions->add_flag("--full", fullact, localized("don't truncate action json"));
   getActions->add_flag("--pretty", prettyact, localized("pretty print full action json "));
1677
   getActions->add_flag("--console", printconsole, localized("print console output generated by action "));
D
Daniel Larimer 已提交
1678 1679 1680 1681 1682 1683 1684
   getActions->set_callback([&] {
      fc::mutable_variant_object arg;
      arg( "account_name", account_name );
      arg( "pos", pos_seq );
      arg( "offset", offset);

      auto result = call(get_actions_func, arg);
1685 1686 1687 1688 1689 1690 1691 1692


      if( printjson ) {
         std::cout << fc::json::to_pretty_string(result) << std::endl;
      } else {
          auto& traces = result["actions"].get_array();
          uint32_t lib = result["last_irreversible_block"].as_uint64();

1693 1694 1695

          cout  << "#" << setw(5) << "seq" << "  " << setw( 24 ) << left << "when"<< "  " << setw(24) << right << "contract::action" << " => " << setw(13) << left << "receiver" << " " << setw(11) << left << "trx id..." << " args\n";
          cout  << "================================================================================================================\n";
1696 1697
          for( const auto& trace: traces ) {
              std::stringstream out;
D
Daniel Larimer 已提交
1698 1699 1700 1701 1702
              if( trace["block_num"].as_uint64() <= lib )
                 out << "#";
              else
                 out << "?";

1703 1704
              out << setw(5) << trace["account_action_seq"].as_uint64() <<"  ";
              out << setw(24) << trace["block_time"].as_string() <<"  ";
1705 1706 1707

              const auto& at = trace["action_trace"].get_object();

1708
              auto id = at["trx_id"].as_string();
1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723
              const auto& receipt = at["receipt"];
              auto receiver = receipt["receiver"].as_string();
              const auto& act = at["act"].get_object();
              auto code = act["account"].as_string();
              auto func = act["name"].as_string();
              string args;
              if( prettyact ) {
                  args = fc::json::to_pretty_string( act["data"] );
              }
              else {
                 args = fc::json::to_string( act["data"] );
                 if( !fullact ) {
                    args = args.substr(0,60) + "...";
                 }
              }
1724 1725 1726
              out << std::setw(24) << std::right<< (code +"::" + func) << " => " << left << std::setw(13) << receiver;

              out << " " << setw(11) << (id.substr(0,8) + "...");
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737

              if( fullact || prettyact ) out << "\n";
              else out << " ";

              out << args ;//<< "\n";

              if( trace["block_num"].as_uint64() <= lib ) {
                 dlog( "\r${m}", ("m",out.str()) );
              } else {
                 wlog( "\r${m}", ("m",out.str()) );
              }
1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748
              if( printconsole ) {
                 auto console = at["console"].as_string();
                 if( console.size() ) {
                    stringstream out;
                    std::stringstream ss(console);
                    string line;
                    std::getline( ss, line );
                    out << ">> " << line << "\n";
                    cerr << out.str(); //ilog( "\r${m}                                   ", ("m",out.str()) );
                 }
              }
1749 1750
          }
      }
D
Daniel Larimer 已提交
1751 1752 1753 1754
   });


   /*
1755
   auto getTransactions = get->add_subcommand("transactions", localized("Retrieve all transactions with specific account name referenced in their scope"), false);
1756
   getTransactions->add_option("account_name", account_name, localized("name of account to query on"))->required();
1757 1758
   getTransactions->add_option("skip_seq", skip_seq_str, localized("Number of most recent transactions to skip (0 would start at most recent transaction)"));
   getTransactions->add_option("num_seq", num_seq_str, localized("Number of transactions to return"));
D
Daniel Larimer 已提交
1759
   getTransactions->add_flag("--json,-j", printjson, localized("print full json"));
1760
   getTransactions->set_callback([&] {
1761 1762 1763 1764 1765 1766 1767
      fc::mutable_variant_object arg;
      if (skip_seq_str.empty()) {
         arg = fc::mutable_variant_object( "account_name", account_name);
      } else {
         uint64_t skip_seq;
         try {
            skip_seq = boost::lexical_cast<uint64_t>(skip_seq_str);
1768
         } EOS_RETHROW_EXCEPTIONS(chain_type_exception, "Invalid Skip Seq: ${skip_seq}", ("skip_seq", skip_seq_str))
1769 1770 1771 1772 1773 1774
         if (num_seq_str.empty()) {
            arg = fc::mutable_variant_object( "account_name", account_name)("skip_seq", skip_seq);
         } else {
            uint64_t num_seq;
            try {
               num_seq = boost::lexical_cast<uint64_t>(num_seq_str);
1775
            } EOS_RETHROW_EXCEPTIONS(chain_type_exception, "Invalid Num Seq: ${num_seq}", ("num_seq", num_seq_str))
1776 1777 1778
            arg = fc::mutable_variant_object( "account_name", account_name)("skip_seq", skip_seq_str)("num_seq", num_seq);
         }
      }
D
Daniel Larimer 已提交
1779
      auto result = call(get_transactions_func, arg);
D
Daniel Larimer 已提交
1780
      if( printjson ) {
J
Joel 已提交
1781
         std::cout << fc::json::to_pretty_string(result) << std::endl;
D
Daniel Larimer 已提交
1782 1783 1784 1785 1786 1787 1788
      }
      else {
         const auto& trxs = result.get_object()["transactions"].get_array();
         for( const auto& t : trxs ) {

            const auto& tobj = t.get_object();
            const auto& trx  = tobj["transaction"].get_object();
J
Joel 已提交
1789
            const auto& data = trx["transaction"].get_object();
D
Daniel Larimer 已提交
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
            const auto& msgs = data["actions"].get_array();

            for( const auto& msg : msgs ) {
               int64_t seq_num  = tobj["seq_num"].as<int64_t>();
               string  id       = tobj["transaction_id"].as_string();
               const auto& exp  = data["expiration"].as<fc::time_point_sec>();
               std::cout << tobj["seq_num"].as_string() <<"] " << id.substr(0,8) << "...  " << data["expiration"].as_string() << "  ";
               auto code = msg["account"].as_string();
               auto func = msg["name"].as_string();
               auto args = fc::json::to_string( msg["data"] );
               std::cout << setw(26) << left << (code + "::" + func) << "  " << args;
               std::cout << std::endl;
            }
         }
D
Daniel Larimer 已提交
1804 1805
      }

1806
   });
D
Daniel Larimer 已提交
1807
   */
1808

1809
   // set subcommand
1810
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
1811 1812 1813
   setSubcommand->require_subcommand();

   // set contract subcommand
N
Nathan Hourt 已提交
1814
   string account;
1815
   string contractPath;
N
Nathan Hourt 已提交
1816 1817
   string wastPath;
   string abiPath;
1818
   bool shouldSend = true;
1819 1820 1821 1822 1823 1824 1825 1826
   auto codeSubcommand = setSubcommand->add_subcommand("code", localized("Create or update the code on an account"));
   codeSubcommand->add_option("account", account, localized("The account to set code for"))->required();
   codeSubcommand->add_option("code-file", wastPath, localized("The fullpath containing the contract WAST or WASM"))->required();

   auto abiSubcommand = setSubcommand->add_subcommand("abi", localized("Create or update the abi on an account"));
   abiSubcommand->add_option("account", account, localized("The account to set the ABI for"))->required();
   abiSubcommand->add_option("abi-file", abiPath, localized("The fullpath containing the contract WAST or WASM"))->required();

1827
   auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
1828 1829
   contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))
                     ->required();
1830
   contractSubcommand->add_option("contract-dir", contractPath, localized("The path containing the .wast and .abi"))
1831 1832 1833 1834 1835
                     ->required();
   contractSubcommand->add_option("wast-file", wastPath, localized("The file containing the contract WAST or WASM relative to contract-dir"));
//                     ->check(CLI::ExistingFile);
   auto abi = contractSubcommand->add_option("abi-file,-a,--abi", abiPath, localized("The ABI for the contract relative to contract-dir"));
//                                ->check(CLI::ExistingFile);
1836

1837
   std::vector<chain::action> actions;
1838
   auto set_code_callback = [&]() {
N
Nathan Hourt 已提交
1839
      std::string wast;
1840
      fc::path cpath(contractPath);
1841

1842 1843
      if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();

B
Bucky Kittinger 已提交
1844
      if( wastPath.empty() )
1845
      {
D
Daniel Larimer 已提交
1846 1847 1848
         wastPath = (cpath / (cpath.filename().generic_string()+".wasm")).generic_string();
         if (!fc::exists(wastPath))
            wastPath = (cpath / (cpath.filename().generic_string()+".wast")).generic_string();
1849 1850
      }

D
Daniel Larimer 已提交
1851
      std::cout << localized(("Reading WAST/WASM from " + wastPath + "...").c_str()) << std::endl;
N
Nathan Hourt 已提交
1852
      fc::read_file_contents(wastPath, wast);
B
Bucky Kittinger 已提交
1853
      FC_ASSERT( !wast.empty(), "no wast file found ${f}", ("f", wastPath) );
1854
      vector<uint8_t> wasm;
1855
      const string binary_wasm_header("\x00\x61\x73\x6d", 4);
1856
      if(wast.compare(0, 4, binary_wasm_header) == 0) {
1857
         std::cout << localized("Using already assembled WASM...") << std::endl;
1858
         wasm = vector<uint8_t>(wast.begin(), wast.end());
1859
      }
1860 1861
      else {
         std::cout << localized("Assembling WASM...") << std::endl;
1862
         wasm = wast_to_wasm(wast);
1863
      }
N
Nathan Hourt 已提交
1864

M
Matias Romeo 已提交
1865
      actions.emplace_back( create_setcode(account, bytes(wasm.begin(), wasm.end()) ) );
1866 1867 1868 1869
      if ( shouldSend ) {
         std::cout << localized("Setting Code...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      }
1870 1871 1872 1873
   };

   auto set_abi_callback = [&]() {
      fc::path cpath(contractPath);
B
Bucky Kittinger 已提交
1874 1875
      if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();

1876 1877 1878 1879
      if( abiPath.empty() )
      {
         abiPath = (cpath / (cpath.filename().generic_string()+".abi")).generic_string();
      }
1880

D
Daniel Larimer 已提交
1881 1882 1883
      FC_ASSERT( fc::exists( abiPath ), "no abi file found ${f}", ("f", abiPath)  );

      try {
D
Daniel Larimer 已提交
1884
         actions.emplace_back( create_setabi(account, fc::json::from_file(abiPath).as<abi_def>()) );
D
Daniel Larimer 已提交
1885
      } EOS_RETHROW_EXCEPTIONS(abi_type_exception,  "Fail to parse ABI JSON")
1886 1887 1888 1889
      if ( shouldSend ) {
         std::cout << localized("Setting ABI...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      }
1890
   };
K
Kevin Heifner 已提交
1891

1892 1893 1894 1895
   add_standard_transaction_options(contractSubcommand, "account@active");
   add_standard_transaction_options(codeSubcommand, "account@active");
   add_standard_transaction_options(abiSubcommand, "account@active");
   contractSubcommand->set_callback([&] {
1896
      shouldSend = false;
1897 1898
      set_code_callback();
      set_abi_callback();
1899
      std::cout << localized("Publishing contract...") << std::endl;
D
Daniel Larimer 已提交
1900
      send_actions(std::move(actions), 10000, packed_transaction::zlib);
N
Nathan Hourt 已提交
1901
   });
1902 1903
   codeSubcommand->set_callback(set_code_callback);
   abiSubcommand->set_callback(set_abi_callback);
N
Nathan Hourt 已提交
1904

1905
   // set account
1906
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
1907 1908

   // set account permission
1909
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
1910 1911

   // set action
1912
   auto setAction = setSubcommand->add_subcommand("action", localized("set or update blockchain action state"))->require_subcommand();
1913

1914
   // set action permission
1915
   auto setActionPermission = set_action_permission_subcommand(setAction);
1916

N
Nathan Hourt 已提交
1917
   // Transfer subcommand
1918
   string con = "eosio.token";
N
Nathan Hourt 已提交
1919 1920
   string sender;
   string recipient;
1921
   string amount;
1922
   string memo;
1923 1924 1925 1926 1927
   auto transfer = app.add_subcommand("transfer", localized("Transfer EOS from account to account"), false);
   transfer->add_option("sender", sender, localized("The account sending EOS"))->required();
   transfer->add_option("recipient", recipient, localized("The account receiving EOS"))->required();
   transfer->add_option("amount", amount, localized("The amount of EOS to send"))->required();
   transfer->add_option("memo", memo, localized("The memo for the transfer"));
1928
   transfer->add_option("--contract,-c", con, localized("The contract which controls the token"));
K
Kevin Heifner 已提交
1929

M
Matias Romeo 已提交
1930
   add_standard_transaction_options(transfer, "sender@active");
N
Nathan Hourt 已提交
1931
   transfer->set_callback([&] {
1932 1933 1934
      signed_transaction trx;
      if (tx_force_unique && memo.size() == 0) {
         // use the memo to add a nonce
1935
         memo = generate_nonce_string();
1936
         tx_force_unique = false;
1937
      }
1938

1939
      send_actions({create_transfer(con,sender, recipient, to_asset(amount), memo)});
N
Nathan Hourt 已提交
1940
   });
N
Nathan Hourt 已提交
1941

1942
   // Net subcommand
1943 1944 1945 1946 1947 1948
   string new_host;
   auto net = app.add_subcommand( "net", localized("Interact with local p2p network connections"), false );
   net->require_subcommand();
   auto connect = net->add_subcommand("connect", localized("start a new connection to a peer"), false);
   connect->add_option("host", new_host, localized("The hostname:port to connect to."))->required();
   connect->set_callback([&] {
B
Bucky Kittinger 已提交
1949
      const auto& v = call(url, net_connect, new_host);
1950 1951 1952 1953 1954 1955
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto disconnect = net->add_subcommand("disconnect", localized("close an existing connection"), false);
   disconnect->add_option("host", new_host, localized("The hostname:port to disconnect from."))->required();
   disconnect->set_callback([&] {
B
Bucky Kittinger 已提交
1956
      const auto& v = call(url, net_disconnect, new_host);
1957 1958 1959 1960 1961 1962
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto status = net->add_subcommand("status", localized("status of existing connection"), false);
   status->add_option("host", new_host, localized("The hostname:port to query status of connection"))->required();
   status->set_callback([&] {
B
Bucky Kittinger 已提交
1963
      const auto& v = call(url, net_status, new_host);
1964 1965 1966 1967 1968
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto connections = net->add_subcommand("peers", localized("status of all existing peers"), false);
   connections->set_callback([&] {
B
Bucky Kittinger 已提交
1969
      const auto& v = call(url, net_connections, new_host);
1970 1971 1972 1973
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });


K
Kevin Heifner 已提交
1974 1975

   // Wallet subcommand
1976
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
1977
   wallet->require_subcommand();
K
Kevin Heifner 已提交
1978
   // create wallet
D
Daniel Larimer 已提交
1979
   string wallet_name = "default";
1980 1981
   auto createWallet = wallet->add_subcommand("create", localized("Create a new wallet locally"), false);
   createWallet->add_option("-n,--name", wallet_name, localized("The name of the new wallet"), true);
K
Kevin Heifner 已提交
1982
   createWallet->set_callback([&wallet_name] {
1983 1984 1985
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
1986
      const auto& v = call(wallet_url, wallet_create, wallet_name);
1987 1988 1989
      std::cout << localized("Creating wallet: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
      std::cout << localized("Save password to use in the future to unlock this wallet.") << std::endl;
      std::cout << localized("Without password imported keys will not be retrievable.") << std::endl;
K
Kevin Heifner 已提交
1990 1991 1992 1993
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
1994 1995
   auto openWallet = wallet->add_subcommand("open", localized("Open an existing wallet"), false);
   openWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to open"));
K
Kevin Heifner 已提交
1996
   openWallet->set_callback([&wallet_name] {
1997 1998 1999
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2000
      call(wallet_url, wallet_open, wallet_name);
2001
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2002 2003 2004
   });

   // lock wallet
2005 2006
   auto lockWallet = wallet->add_subcommand("lock", localized("Lock wallet"), false);
   lockWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to lock"));
K
Kevin Heifner 已提交
2007
   lockWallet->set_callback([&wallet_name] {
2008 2009 2010
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2011
      call(wallet_url, wallet_lock, wallet_name);
2012
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2013 2014 2015
   });

   // lock all wallets
2016
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
K
Kevin Heifner 已提交
2017
   locakAllWallets->set_callback([] {
2018 2019 2020
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2021
      call(wallet_url, wallet_lock_all);
2022
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
2023 2024 2025 2026
   });

   // unlock wallet
   string wallet_pw;
2027 2028 2029
   auto unlockWallet = wallet->add_subcommand("unlock", localized("Unlock wallet"), false);
   unlockWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to unlock"));
   unlockWallet->add_option("--password", wallet_pw, localized("The password returned by wallet create"));
2030
   unlockWallet->add_option( "--unlock-timeout", wallet_unlock_timeout, localized("The timeout for unlocked wallet in seconds"));
K
Kevin Heifner 已提交
2031
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
2032
      if( wallet_pw.size() == 0 ) {
2033
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
2034 2035 2036 2037
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }
2038 2039
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);
D
Daniel Larimer 已提交
2040 2041


K
Kevin Heifner 已提交
2042
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
2043
      call(wallet_url, wallet_unlock, vs);
2044
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2045 2046 2047
   });

   // import keys into wallet
2048
   string wallet_key_str;
2049 2050
   auto importWallet = wallet->add_subcommand("import", localized("Import private key into wallet"), false);
   importWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to import key into"));
2051 2052 2053 2054 2055 2056 2057 2058 2059
   importWallet->add_option("key", wallet_key_str, localized("Private key in WIF format to import"))->required();
   importWallet->set_callback([&wallet_name, &wallet_key_str] {
      private_key_type wallet_key;
      try {
         wallet_key = private_key_type( wallet_key_str );
      } catch (...) {
          EOS_THROW(private_key_type_exception, "Invalid private key: ${private_key}", ("private_key", wallet_key_str))
      }
      public_key_type pubkey = wallet_key.get_public_key();
D
Daniel Larimer 已提交
2060

K
Kevin Heifner 已提交
2061
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
D
Daniel Larimer 已提交
2062
      call(wallet_url, wallet_import_key, vs);
2063
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
K
Kevin Heifner 已提交
2064
   });
2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076

   // create a key within wallet
   string wallet_create_key_type;
   auto createKeyInWallet = wallet->add_subcommand("create_key", localized("Create private key within wallet"), false);
   createKeyInWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to create key into"), true);
   createKeyInWallet->add_option("key_type", wallet_create_key_type, localized("Key type to create (K1/R1)"), true)->set_type_name("K1/R1");
   createKeyInWallet->set_callback([&wallet_name, &wallet_create_key_type] {
      //an empty key type is allowed -- it will let the underlying wallet pick which type it prefers
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_create_key_type)};
      const auto& v = call(wallet_url, wallet_create_key, vs);
      std::cout << localized("Created new private key with a public key of: ") << fc::json::to_pretty_string(v) << std::endl;
   });
K
Kevin Heifner 已提交
2077 2078

   // list wallets
2079
   auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
K
Kevin Heifner 已提交
2080
   listWallet->set_callback([] {
2081 2082 2083
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

2084
      std::cout << localized("Wallets:") << std::endl;
D
Daniel Larimer 已提交
2085
      const auto& v = call(wallet_url, wallet_list);
K
Kevin Heifner 已提交
2086 2087 2088 2089
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // list keys
2090
   auto listKeys = wallet->add_subcommand("keys", localized("List of private keys from all unlocked wallets in wif format."), false);
K
Kevin Heifner 已提交
2091
   listKeys->set_callback([] {
2092 2093 2094
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2095
      const auto& v = call(wallet_url, wallet_list_keys);
K
Kevin Heifner 已提交
2096 2097 2098
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

2099 2100
   auto stopKeosd = wallet->add_subcommand("stop", localized("Stop keosd (doesn't work with nodeos)."), false);
   stopKeosd->set_callback([] {
2101 2102 2103
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

2104
      const auto& v = call(wallet_url, keosd_stop);
A
Anton Perkov 已提交
2105 2106 2107 2108 2109
      if ( !v.is_object() || v.get_object().size() != 0 ) { //on success keosd responds with empty object
         std::cerr << fc::json::to_pretty_string(v) << std::endl;
      } else {
         std::cout << "OK" << std::endl;
      }
2110 2111
   });

2112 2113 2114 2115 2116 2117 2118
   // sign subcommand
   string trx_json_to_sign;
   string str_private_key;
   bool push_trx = false;

   auto sign = app.add_subcommand("sign", localized("Sign a transaction"), false);
   sign->add_option("transaction", trx_json_to_sign,
2119
                                 localized("The JSON string or filename defining the transaction to sign"), true)->required();
2120 2121 2122 2123
   sign->add_option("-k,--private-key", str_private_key, localized("The private key that will be used to sign the transaction"));
   sign->add_flag( "-p,--push-transaction", push_trx, localized("Push transaction after signing"));

   sign->set_callback([&] {
2124
      signed_transaction trx = json_from_file_or_string(trx_json_to_sign).as<signed_transaction>();
2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143

      if( str_private_key.size() == 0 ) {
         std::cerr << localized("private key: ");
         fc::set_console_echo(false);
         std::getline( std::cin, str_private_key, '\n' );
         fc::set_console_echo(true);
      }

      auto priv_key = fc::crypto::private_key::regenerate(*utilities::wif_to_key(str_private_key));
      trx.sign(priv_key, chain_id_type{});

      if(push_trx) {
         auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
         std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
      } else {
         std::cout << fc::json::to_pretty_string(trx) << std::endl;
      }
   });

N
Nathan Hourt 已提交
2144
   // Push subcommand
2145
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
N
Nathan Hourt 已提交
2146 2147
   push->require_subcommand();

2148
   // push action
W
wqxiang88 已提交
2149
   string contract_account;
N
Nathan Hourt 已提交
2150 2151
   string action;
   string data;
2152
   vector<string> permissions;
2153
   auto actionsSubcommand = push->add_subcommand("action", localized("Push a transaction with a single action"));
D
Daniel Larimer 已提交
2154
   actionsSubcommand->fallthrough(false);
W
wqxiang88 已提交
2155
   actionsSubcommand->add_option("account", contract_account,
2156
                                 localized("The account providing the contract to execute"), true)->required();
2157 2158
   actionsSubcommand->add_option("action", action,
                                 localized("A JSON string or filename defining the action to execute on the contract"), true)->required();
D
Daniel Larimer 已提交
2159
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
2160

D
Daniel Larimer 已提交
2161 2162
   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
2163 2164
      fc::variant action_args_var;
      try {
2165 2166
         action_args_var = json_from_file_or_string(data, fc::json::relaxed_parser);
      } EOS_RETHROW_EXCEPTIONS(action_type_exception, "Fail to parse action JSON data='${data}'", ("data",data))
2167

N
Nathan Hourt 已提交
2168
      auto arg= fc::mutable_variant_object
W
wqxiang88 已提交
2169
                ("code", contract_account)
N
Nathan Hourt 已提交
2170
                ("action", action)
2171
                ("args", action_args_var);
N
Nathan Hourt 已提交
2172 2173
      auto result = call(json_to_bin_func, arg);

2174
      auto accountPermissions = get_account_permissions(tx_permission);
2175

W
wqxiang88 已提交
2176
      send_actions({chain::action{accountPermissions, contract_account, action, result.get_object()["binargs"].as<bytes>()}});
N
Nathan Hourt 已提交
2177 2178 2179
   });

   // push transaction
2180
   string trx_to_push;
2181
   auto trxSubcommand = push->add_subcommand("transaction", localized("Push an arbitrary JSON transaction"));
2182
   trxSubcommand->add_option("transaction", trx_to_push, localized("The JSON string or filename defining the transaction to push"))->required();
2183

N
Nathan Hourt 已提交
2184
   trxSubcommand->set_callback([&] {
2185 2186
      fc::variant trx_var;
      try {
2187 2188
         trx_var = json_from_file_or_string(trx_to_push);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_push))
A
Andrianto Lie 已提交
2189
      signed_transaction trx = trx_var.as<signed_transaction>();
2190
      auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
N
Nathan Hourt 已提交
2191 2192
      std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
   });
2193

2194 2195

   string trxsJson;
2196
   auto trxsSubcommand = push->add_subcommand("transactions", localized("Push an array of arbitrary JSON transactions"));
2197
   trxsSubcommand->add_option("transactions", trxsJson, localized("The JSON string or filename defining the array of the transactions to push"))->required();
2198
   trxsSubcommand->set_callback([&] {
2199 2200
      fc::variant trx_var;
      try {
2201 2202
         trx_var = json_from_file_or_string(trxsJson);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trxsJson))
2203
      auto trxs_result = call(push_txns_func, trx_var);
2204 2205 2206
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });

D
Daniel Larimer 已提交
2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282

   // multisig subcommand
   auto msig = app.add_subcommand("multisig", localized("Multisig contract commands"), false);
   msig->require_subcommand();

   // multisig propose
   string proposal_name;
   string requested_perm;
   string transaction_perm;
   string proposed_transaction;
   string proposed_contract;
   string proposed_action;
   string proposer;
   unsigned int proposal_expiration_hours = 24;
   CLI::callback_t parse_expiration_hours = [&](CLI::results_t res) -> bool {
      unsigned int value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
         return false;
      }

      proposal_expiration_hours = static_cast<uint64_t>(value_s);
      return true;
   };

   auto propose_action = msig->add_subcommand("propose", localized("Propose transaction"));
   //auto propose_action = msig->add_subcommand("action", localized("Propose action"));
   add_standard_transaction_options(propose_action);
   propose_action->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   propose_action->add_option("requested_permissions", requested_perm, localized("The JSON string or filename defining requested permissions"))->required();
   propose_action->add_option("trx_permissions", transaction_perm, localized("The JSON string or filename defining transaction permissions"))->required();
   propose_action->add_option("contract", proposed_contract, localized("contract to wich deferred transaction should be delivered"))->required();
   propose_action->add_option("action", proposed_action, localized("action of deferred transaction"))->required();
   propose_action->add_option("data", proposed_transaction, localized("The JSON string or filename defining the action to propose"))->required();
   propose_action->add_option("proposer", proposer, localized("Account proposing the transaction"));
   propose_action->add_option("proposal_expiration", parse_expiration_hours, localized("Proposal expiration interval in hours"));

   propose_action->set_callback([&] {
      fc::variant requested_perm_var;
      try {
         requested_perm_var = json_from_file_or_string(requested_perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",requested_perm))
      fc::variant transaction_perm_var;
      try {
         transaction_perm_var = json_from_file_or_string(transaction_perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",transaction_perm))
      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string(proposed_transaction);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",proposed_transaction))
      transaction proposed_trx = trx_var.as<transaction>();

      auto arg= fc::mutable_variant_object()
         ("code", proposed_contract)
         ("action", proposed_action)
         ("args", trx_var);

      auto result = call(json_to_bin_func, arg);
      //std::cout << "Result: "; fc::json::to_stream(std::cout, result); std::cout << std::endl;

      bytes proposed_trx_serialized = result.get_object()["binargs"].as<bytes>();

      vector<permission_level> reqperm;
      try {
         reqperm = requested_perm_var.as<vector<permission_level>>();
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Wrong requested permissions format: '${data}'", ("data",requested_perm_var));

      vector<permission_level> trxperm;
      try {
         trxperm = transaction_perm_var.as<vector<permission_level>>();
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Wrong transaction permissions format: '${data}'", ("data",transaction_perm_var));

      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!proposer.empty()) {
            accountPermissions = vector<permission_level>{{proposer, config::active_name}};
         } else {
2283
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <proposer> or -p)");
D
Daniel Larimer 已提交
2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
         }
      }
      if (proposer.empty()) {
         proposer = name(accountPermissions.at(0).actor).to_string();
      }

      transaction trx;

      trx.expiration = fc::time_point_sec( fc::time_point::now() + fc::hours(proposal_expiration_hours) );
      trx.ref_block_num = 0;
      trx.ref_block_prefix = 0;
      trx.max_net_usage_words = 0;
2296
      trx.max_cpu_usage_ms = 0;
D
Daniel Larimer 已提交
2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415
      trx.delay_sec = 0;
      trx.actions = { chain::action(trxperm, name(proposed_contract), name(proposed_action), proposed_trx_serialized) };

      fc::to_variant(trx, trx_var);

      arg = fc::mutable_variant_object()
         ("code", "eosio.msig")
         ("action", "propose")
         ("args", fc::mutable_variant_object()
          ("proposer", proposer )
          ("proposal_name", proposal_name)
          ("requested", requested_perm_var)
          ("trx", trx_var)
         );
      result = call(json_to_bin_func, arg);
      send_actions({chain::action{accountPermissions, "eosio.msig", "propose", result.get_object()["binargs"].as<bytes>()}});
   });

   //resolver for ABI serializer to decode actions in proposed transaction in multisig contract
   auto resolver = [](const name& code) -> optional<abi_serializer> {
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", code.to_string()));
      if (result["abi"].is_object()) {
         //std::cout << "ABI: " << fc::json::to_pretty_string(result) << std::endl;
         return optional<abi_serializer>(abi_serializer(result["abi"].as<abi_def>()));
      } else {
         std::cerr << "ABI for contract " << code.to_string() << " not found. Action data will be shown in hex only." << std::endl;
         return optional<abi_serializer>();
      }
   };

   // multisig review
   auto review = msig->add_subcommand("review", localized("Review transaction"));
   add_standard_transaction_options(review);
   review->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   review->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();

   review->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                         ("code", "eosio.msig")
                         ("scope", proposer)
                         ("table", "proposal")
                         ("table_key", "")
                         ("lower_bound", eosio::chain::string_to_name(proposal_name.c_str()))
                         ("upper_bound", "")
                         ("limit", 1)
                         );
      //std::cout << fc::json::to_pretty_string(result) << std::endl;

      fc::variants rows = result.get_object()["rows"].get_array();
      if (rows.empty()) {
         std::cerr << "Proposal not found" << std::endl;
         return;
      }
      fc::mutable_variant_object obj = rows[0].get_object();
      if (obj["proposal_name"] != proposal_name) {
         std::cerr << "Proposal not found" << std::endl;
         return;
      }
      auto trx_hex = obj["packed_transaction"].as_string();
      vector<char> trx_blob(trx_hex.size()/2);
      fc::from_hex(trx_hex, trx_blob.data(), trx_blob.size());
      transaction trx = fc::raw::unpack<transaction>(trx_blob);

      fc::variant trx_var;
      abi_serializer abi;
      abi.to_variant(trx, trx_var, resolver);
      obj["transaction"] = trx_var;
      std::cout << fc::json::to_pretty_string(obj)
                << std::endl;
   });

   string perm;
   auto approve_or_unapprove = [&](const string& action) {
      fc::variant perm_var;
      try {
         perm_var = json_from_file_or_string(perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",perm))
      auto arg = fc::mutable_variant_object()
         ("code", "eosio.msig")
         ("action", action)
         ("args", fc::mutable_variant_object()
          ("proposer", proposer)
          ("proposal_name", proposal_name)
          ("level", perm_var)
         );
      auto result = call(json_to_bin_func, arg);
      auto accountPermissions = tx_permission.empty() ? vector<chain::permission_level>{{sender,config::active_name}} : get_account_permissions(tx_permission);
      send_actions({chain::action{accountPermissions, "eosio.msig", action, result.get_object()["binargs"].as<bytes>()}});
   };

   // multisig approve
   auto approve = msig->add_subcommand("approve", localized("Approve proposed transaction"));
   add_standard_transaction_options(approve);
   approve->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   approve->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   approve->add_option("permissions", perm, localized("The JSON string of filename defining approving permissions"))->required();
   approve->set_callback([&] { approve_or_unapprove("approve"); });

   // multisig unapprove
   auto unapprove = msig->add_subcommand("unapprove", localized("Unapprove proposed transaction"));
   add_standard_transaction_options(unapprove);
   unapprove->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   unapprove->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   unapprove->add_option("permissions", perm, localized("The JSON string of filename defining approving permissions"))->required();
   unapprove->set_callback([&] { approve_or_unapprove("unapprove"); });

   // multisig cancel
   string canceler;
   auto cancel = msig->add_subcommand("cancel", localized("Cancel proposed transaction"));
   add_standard_transaction_options(cancel);
   cancel->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   cancel->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   cancel->add_option("canceler", canceler, localized("canceler name (string)"));
   cancel->set_callback([&]() {
      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!canceler.empty()) {
            accountPermissions = vector<permission_level>{{canceler, config::active_name}};
         } else {
2416
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <canceler> or -p)");
D
Daniel Larimer 已提交
2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447
         }
      }
      if (canceler.empty()) {
         canceler = name(accountPermissions.at(0).actor).to_string();
      }
      auto arg = fc::mutable_variant_object()
         ("code", "eosio.msig")
         ("action", "cancel")
         ("args", fc::mutable_variant_object()
          ("proposer", proposer)
          ("proposal_name", proposal_name)
          ("canceler", canceler)
         );
      auto result = call(json_to_bin_func, arg);
      send_actions({chain::action{accountPermissions, "eosio.msig", "cancel", result.get_object()["binargs"].as<bytes>()}});
      }
   );

   // multisig exec
   string executer;
   auto exec = msig->add_subcommand("exec", localized("Execute proposed transaction"));
   add_standard_transaction_options(exec);
   exec->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   exec->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   exec->add_option("executer", executer, localized("account paying for execution (string)"));
   exec->set_callback([&] {
      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!executer.empty()) {
            accountPermissions = vector<permission_level>{{executer, config::active_name}};
         } else {
2448
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <executer> or -p)");
D
Daniel Larimer 已提交
2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472
         }
      }
      if (executer.empty()) {
         executer = name(accountPermissions.at(0).actor).to_string();
      }

      auto arg = fc::mutable_variant_object()
         ("code", "eosio.msig")
         ("action", "exec")
         ("args", fc::mutable_variant_object()
          ("proposer", proposer )
          ("proposal_name", proposal_name)
          ("executer", executer)
         );
      auto result = call(json_to_bin_func, arg);
      //std::cout << "Result: " << result << std::endl;
      send_actions({chain::action{accountPermissions, "eosio.msig", "exec", result.get_object()["binargs"].as<bytes>()}});
      }
   );

   // system subcommand
   auto system = app.add_subcommand("system", localized("Send eosio.system contract action to the blockchain."), false);
   system->require_subcommand();

A
Anton Perkov 已提交
2473
   auto createAccountSystem = create_account_subcommand( system, false /*simple*/ );
D
Daniel Larimer 已提交
2474 2475 2476 2477 2478 2479 2480
   auto registerProducer = register_producer_subcommand(system);
   auto unregisterProducer = unregister_producer_subcommand(system);

   auto voteProducer = system->add_subcommand("voteproducer", localized("Vote for a producer"));
   voteProducer->require_subcommand();
   auto voteProxy = vote_producer_proxy_subcommand(voteProducer);
   auto voteProducers = vote_producers_subcommand(voteProducer);
2481 2482
   auto approveProducer = approve_producer_subcommand(voteProducer);
   auto unapproveProducer = unapprove_producer_subcommand(voteProducer);
D
Daniel Larimer 已提交
2483

A
Anton Perkov 已提交
2484 2485
   auto listProducers = list_producers_subcommand(system);

D
Daniel Larimer 已提交
2486 2487
   auto delegateBandWidth = delegate_bandwidth_subcommand(system);
   auto undelegateBandWidth = undelegate_bandwidth_subcommand(system);
A
Anton Perkov 已提交
2488
   auto listBandWidth = list_bw_subcommand(system);
D
Daniel Larimer 已提交
2489

2490 2491 2492
   auto biyram = buyram_subcommand(system);
   auto sellram = sellram_subcommand(system);

D
Daniel Larimer 已提交
2493 2494 2495 2496 2497 2498 2499
   auto claimRewards = claimrewards_subcommand(system);

   auto regProxy = regproxy_subcommand(system);
   auto unregProxy = unregproxy_subcommand(system);

   auto cancelDelay = canceldelay_subcommand(system);

2500 2501 2502 2503
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
2504 2505
   } catch (const explained_exception& e) {
      return 1;
D
Daniel Larimer 已提交
2506 2507 2508 2509
   } catch (connection_exception& e) {
      if (verbose_errors) {
         elog("connect error: ${e}", ("e", e.to_detail_string()));
      }
2510
   } catch (const fc::exception& e) {
D
Daniel Larimer 已提交
2511 2512 2513 2514 2515
      // attempt to extract the error code if one is present
      if (!print_recognized_errors(e, verbose_errors)) {
         // Error is not recognized
         if (!print_help_text(e) || verbose_errors) {
            elog("Failed with error: ${e}", ("e", verbose_errors ? e.to_detail_string() : e.to_string()));
2516
         }
2517
      }
2518 2519 2520 2521
      return 1;
   }

   return 0;
2522
}