main.cpp 117.9 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
   return required_keys["required_keys"];
}

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

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

   // Set tapos, default to last irreversible block if it's not specified by the user
D
Daniel Larimer 已提交
270
   block_id_type ref_block_id = info.last_irreversible_block_id;
D
Daniel Larimer 已提交
271 272 273 274
   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 已提交
275
         ref_block_id = ref_block["id"].as<block_id_type>();
A
arhag 已提交
276
      }
D
Daniel Larimer 已提交
277 278
   } 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);
279

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

284
   trx.max_cpu_usage_ms = tx_max_net_usage;
285
   trx.max_net_usage_words = (tx_max_net_usage + 7)/8;
286

M
Matias Romeo 已提交
287
   if (!tx_skip_sign) {
288
      auto required_keys = determine_required_keys(trx);
289
      sign_transaction(trx, required_keys, info.chain_id);
M
Matias Romeo 已提交
290 291 292 293 294 295 296
   }

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

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

303
   return push_transaction(trx, extra_kcpu, compression);
304 305
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
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 已提交
339
void print_result( const fc::variant& result ) { try {
340 341 342 343 344 345 346 347 348 349 350 351
      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();
            }
352 353
         }

354 355 356 357 358 359 360 361 362 363 364 365
         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 已提交
366

367 368 369 370 371 372 373 374 375 376 377 378 379
         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 已提交
380 381
         }
      } else {
K
Kevin Heifner 已提交
382
         cerr << fc::json::to_pretty_string( result ) << endl;
D
Daniel Larimer 已提交
383
      }
D
Daniel Larimer 已提交
384
} FC_CAPTURE_AND_RETHROW( (result) ) }
D
Daniel Larimer 已提交
385 386

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

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

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

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

M
Matias Romeo 已提交
407 408 409
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 已提交
410
      eosio::chain::newaccount{
411 412 413
         .creator      = creator,
         .name         = newaccount,
         .owner        = eosio::chain::authority{1, {{owner, 1}}, {}},
414
         .active       = eosio::chain::authority{1, {{active, 1}}, {}}
M
Matias Romeo 已提交
415 416
      }
   };
417
}
418

D
Daniel Larimer 已提交
419 420 421 422 423 424 425
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);
426
   wdump((result)(arg));
D
Daniel Larimer 已提交
427 428 429
   return chain::action{authorization, code, act, result.get_object()["binargs"].as<bytes>()};
}

K
Kevin Heifner 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
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);
}

448
chain::action create_delegate(const name& from, const name& receiver, const asset& net, const asset& cpu, bool transfer) {
A
Anton Perkov 已提交
449 450 451 452
   fc::variant act_payload = fc::mutable_variant_object()
         ("from", from.to_string())
         ("receiver", receiver.to_string())
         ("stake_net_quantity", net.to_string())
453 454
         ("stake_cpu_quantity", cpu.to_string())
         ("transfer", transfer);
A
Anton Perkov 已提交
455 456 457 458
   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 已提交
459 460
fc::variant regproducer_variant(const account_name& producer,
                                public_key_type key,
461
                                string url, uint16_t location = 0) {
462
   /*
D
Daniel Larimer 已提交
463
   fc::variant_object params = fc::mutable_variant_object()
A
arhag 已提交
464 465 466 467
         ("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)
468
         ("net_usage_leeway", config::default_net_usage_leeway)
A
arhag 已提交
469 470 471 472 473 474 475 476 477 478 479 480 481 482
         ("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_storage_size", max_storage_size)
         ("percent_of_max_inflation_rate", percent_of_max_inflation_rate)
         ("storage_reserve_ratio", storage_reserve_ratio);
483
         */
D
Daniel Larimer 已提交
484 485 486

   return fc::mutable_variant_object()
            ("producer", producer)
487
            ("producer_key", key)
488 489 490
            ("url", url)
            ("location", 0)
            ;
D
Daniel Larimer 已提交
491 492
}

493
chain::action create_transfer(const string& contract, const name& sender, const name& recipient, asset amount, const string& memo ) {
M
Matias Romeo 已提交
494 495 496 497

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

M
Matias Romeo 已提交
501
   auto args = fc::mutable_variant_object
502
      ("code", contract)
M
Matias Romeo 已提交
503 504
      ("action", "transfer")
      ("args", transfer);
505

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

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

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

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

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 已提交
538
                   updateauth{account, permission, parent, auth}};
539 540
}

541 542
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 已提交
543
                   deleteauth{account, permission}};
544 545
}

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

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

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

D
Daniel Larimer 已提交
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
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);
   }
}

582 583 584
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 已提交
585
   eosio::chain::symbol_code sym = a.get_symbol().to_symbol_code();
586 587 588 589 590 591 592 593 594 595 596
   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 已提交
597
         auto p = cache.insert(make_pair( sym, result.max_supply.get_symbol() ));
598 599 600 601 602 603 604 605 606
         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 已提交
607
      a = asset( a.get_amount() * factor, expected_symbol );
608 609 610 611 612 613 614 615 616 617
   } 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 );
}

618
struct set_account_permission_subcommand {
619 620 621 622 623
   string accountStr;
   string permissionStr;
   string authorityJsonOrFile;
   string parentStr;

624
   set_account_permission_subcommand(CLI::App* accountCmd) {
625 626 627
      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 已提交
628
      permissions->add_option("authority", authorityJsonOrFile, localized("[delete] NULL, [create/update] public key, JSON string, or filename defining the authority"))->required();
629
      permissions->add_option("parent", parentStr, localized("[create] The permission name of this parents permission (Defaults to: \"Active\")"));
630

M
Matias Romeo 已提交
631
      add_standard_transaction_options(permissions, "account@active");
632 633

      permissions->set_callback([this] {
634 635
         name account = name(accountStr);
         name permission = name(permissionStr);
636
         bool is_delete = boost::iequals(authorityJsonOrFile, "null");
637

638
         if (is_delete) {
M
Matias Romeo 已提交
639
            send_actions({create_deleteauth(account, permission)});
640
         } else {
D
Daniel Larimer 已提交
641
            authority auth = parse_json_authority_or_key(authorityJsonOrFile);
642

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

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

661 662
               }
            } else {
663
               parent = name(parentStr);
664
            }
665

M
Matias Romeo 已提交
666
            send_actions({create_updateauth(account, permission, parent, auth)});
667
         }
668 669
      });
   }
670

671
};
672

673
struct set_action_permission_subcommand {
674 675 676 677 678
   string accountStr;
   string codeStr;
   string typeStr;
   string requirementStr;

679
   set_action_permission_subcommand(CLI::App* actionRoot) {
680 681 682 683 684
      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();
685

M
Matias Romeo 已提交
686
      add_standard_transaction_options(permissions, "account@active");
687 688

      permissions->set_callback([this] {
689 690 691
         name account = name(accountStr);
         name code = name(codeStr);
         name type = name(typeStr);
692
         bool is_delete = boost::iequals(requirementStr, "null");
693

694
         if (is_delete) {
M
Matias Romeo 已提交
695
            send_actions({create_unlinkauth(account, code, type)});
696
         } else {
697
            name requirement = name(requirementStr);
M
Matias Romeo 已提交
698
            send_actions({create_linkauth(account, code, type, requirement)});
699
         }
700 701 702
      });
   }
};
703

R
Roman Brod 已提交
704 705 706 707 708 709 710 711 712 713 714 715 716 717

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 已提交
718 719
}

720 721 722
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();
723
   while ( !port_used(port)) {
724 725 726 727 728 729 730
      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")});
      }
   }
}

731 732 733
void ensure_keosd_running(CLI::App* app) {
    // get, version, net do not require keosd
    if (tx_skip_sign || app->got_subcommand("get") || app->got_subcommand("version") || app->got_subcommand("net"))
734
        return;
735 736
    if (app->get_subcommand("create")->got_subcommand("key")) // create key does not require wallet
       return;
R
Roman Brod 已提交
737
    auto parsed_url = parse_url(wallet_url);
738
    if (parsed_url.server != "localhost" && parsed_url.server != "127.0.0.1")
R
Roman Brod 已提交
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
        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);
763 764 765 766 767 768 769 770

        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 已提交
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
                                     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 已提交
786
}
D
Daniel Larimer 已提交
787

R
Roman Brod 已提交
788 789

CLI::callback_t obsoleted_option_host_port = [](CLI::results_t) {
D
Daniel Larimer 已提交
790 791 792 793 794 795 796 797 798
   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;
799
   string url;
800
   uint16_t loc = 0;
D
Daniel Larimer 已提交
801 802 803 804 805

   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();
806
      register_producer->add_option("url", url, localized("url where info about producer can be found"), true);
807
      register_producer->add_option("location", loc, localized("relative location for purpose of nearest neighbor scheduling"), 0);
D
Daniel Larimer 已提交
808 809 810 811 812 813 814 815 816
      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))

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

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

   create_account_subcommand(CLI::App* actionRoot, bool s) : simple(s) {
A
Anton Perkov 已提交
836
      auto createAccount = actionRoot->add_subcommand( (simple ? "account" : "newaccount"), localized("Create an account, buy ram, stake for bandwidth for the account"));
A
Anton Perkov 已提交
837 838 839
      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();
840
      createAccount->add_option("ActiveKey", active_key_str, localized("The active public key for the new account"));
A
Anton Perkov 已提交
841 842 843 844 845 846 847 848 849 850

      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")));
851
         createAccount->add_flag("--transfer", transfer,
852
                                 (localized("Transfer voting power and right to unstake EOS to receiver")));
A
Anton Perkov 已提交
853 854
      }

855 856
      add_standard_transaction_options(createAccount);

A
Anton Perkov 已提交
857
      createAccount->set_callback([this] {
858
            if( !active_key_str.size() )
859
               active_key_str = owner_key_str;
A
Anton Perkov 已提交
860 861 862 863 864 865 866 867 868
            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) {
869 870 871 872
               if ( buy_ram_eos.empty() && buy_ram_bytes_in_kbytes == 0) {
                  std::cerr << "ERROR: Either --buy-ram-EOS or --buy-ram-bytes with non-zero value is required" << std::endl;
                  return;
               }
873
               action buyram = !buy_ram_eos.empty() ? create_buyram(creator, account_name, to_asset(buy_ram_eos))
A
Anton Perkov 已提交
874
                  : create_buyrambytes(creator, account_name, buy_ram_bytes_in_kbytes * 1024);
875 876
               auto net = to_asset(stake_net);
               auto cpu = to_asset(stake_cpu);
877
               if ( net.get_amount() == 0 && cpu.get_amount() == 0 ) {
878 879 880 881 882
                  action delegate = create_delegate( creator, account_name, net, cpu, transfer);
                  send_actions( { create, buyram, delegate } );
               } else {
                  send_actions( { create, buyram } );
               }
A
Anton Perkov 已提交
883 884 885 886 887 888 889
            } else {
               send_actions( { create } );
            }
      });
   }
};

D
Daniel Larimer 已提交
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 918 919 920 921 922 923 924 925 926 927 928
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 已提交
929
   vector<eosio::name> producer_names;
D
Daniel Larimer 已提交
930 931 932 933

   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 已提交
934
      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 已提交
935 936 937
      add_standard_transaction_options(vote_producers);

      vote_producers->set_callback([this] {
D
Daniel Larimer 已提交
938 939 940

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

D
Daniel Larimer 已提交
941 942 943
         fc::variant act_payload = fc::mutable_variant_object()
                  ("voter", voter_str)
                  ("proxy", "")
D
Daniel Larimer 已提交
944
                  ("producers", producer_names);
D
Daniel Larimer 已提交
945 946 947 948 949
         send_actions({create_action({permission_level{voter_str,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

950
struct approve_producer_subcommand {
951
   eosio::name voter;
952 953 954 955
   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"));
956
      approve_producer->add_option("voter", voter, localized("The voting account"))->required();
957 958 959 960 961 962 963 964
      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")
965 966 967
                               ("table_key", "owner")
                               ("lower_bound", voter.value)
                               ("limit", 1)
968 969
            );
            auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
970 971
            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;
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
               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()
988
               ("voter", voter)
989 990
               ("proxy", "")
               ("producers", prods);
991
            send_actions({create_action({permission_level{voter,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
992 993 994 995 996
      });
   }
};

struct unapprove_producer_subcommand {
997
   eosio::name voter;
998 999 1000 1001
   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"));
1002
      approve_producer->add_option("voter", voter, localized("The voting account"))->required();
1003 1004 1005 1006 1007 1008 1009 1010
      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")
1011 1012 1013
                               ("table_key", "owner")
                               ("lower_bound", voter.value)
                               ("limit", 1)
1014 1015
            );
            auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
1016 1017
            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;
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
               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()
1033
               ("voter", voter)
1034 1035
               ("proxy", "")
               ("producers", prods);
1036
            send_actions({create_action({permission_level{voter,config::active_name}}, config::system_account_name, N(voteproducer), act_payload)});
1037 1038 1039 1040
      });
   }
};

A
Anton Perkov 已提交
1041
struct list_producers_subcommand {
1042
   bool print_json = false;
1043
   uint32_t limit = 50;
1044
   std::string lower;
A
Anton Perkov 已提交
1045 1046 1047

   list_producers_subcommand(CLI::App* actionRoot) {
      auto list_producers = actionRoot->add_subcommand("listproducers", localized("List producers"));
1048 1049
      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"));
1050
      list_producers->add_option("-L,--lower", lower, localized("lower bound value of key, defaults to first"));
A
Anton Perkov 已提交
1051
      list_producers->set_callback([this] {
1052 1053 1054 1055 1056
         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 已提交
1057
         }
1058 1059 1060 1061 1062
         auto result = rawResult.as<eosio::chain_apis::read_only::get_producers_result>();
         if ( result.rows.empty() ) {
            std::cout << "No producers found" << std::endl;
            return;
         }
1063 1064 1065 1066
         auto weight = result.total_producer_vote_weight;
         if ( !weight )
            weight = 1;
         printf("%-13s %-54s %-59s %s\n", "Producer", "Producer key", "Url", "Scaled votes");
1067
         for ( auto& row : result.rows )
1068
            printf("%-13.13s %-54.54s %-59.59s %1.4f\n",
1069 1070 1071
                   row["owner"].as_string().c_str(),
                   row["producer_key"].as_string().c_str(),
                   row["url"].as_string().c_str(),
1072
                   row["total_votes"].as_double() / weight);
1073 1074 1075
         if ( !result.more.empty() )
            std::cout << "-L " << result.more << " for more" << std::endl;
      });
A
Anton Perkov 已提交
1076 1077 1078
   }
};

D
Daniel Larimer 已提交
1079 1080 1081 1082 1083 1084
struct delegate_bandwidth_subcommand {
   string from_str;
   string receiver_str;
   string stake_net_amount;
   string stake_cpu_amount;
   string stake_storage_amount;
1085
   bool transfer = false;
D
Daniel Larimer 已提交
1086 1087 1088 1089 1090 1091 1092

   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();
1093
      delegate_bandwidth->add_flag("--transfer", transfer, localized("Transfer voting power and right to unstake EOS to receiver"));
D
Daniel Larimer 已提交
1094 1095 1096 1097 1098 1099
      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)
1100
                  ("stake_net_quantity", to_asset(stake_net_amount))
1101 1102 1103
                  ("stake_cpu_quantity", to_asset(stake_cpu_amount))
                  ("transfer", transfer)
                  ;
D
Daniel Larimer 已提交
1104
                  wdump((act_payload));
D
Daniel Larimer 已提交
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
         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)
1129 1130
                  ("unstake_net_quantity", to_asset(unstake_net_amount))
                  ("unstake_cpu_quantity", to_asset(unstake_cpu_amount));
D
Daniel Larimer 已提交
1131 1132 1133 1134 1135
         send_actions({create_action({permission_level{from_str,config::active_name}}, config::system_account_name, N(undelegatebw), act_payload)});
      });
   }
};

A
Anton Perkov 已提交
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
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"
1156
                            << std::setw(21) << std::left << "CPU bandwidth" << std::endl;
A
Anton Perkov 已提交
1157 1158 1159
                  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()
1160
                               << std::setw(21) << std::left << r["cpu_weight"].as_string()
A
Anton Perkov 已提交
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
                               << std::endl;
                  }
               } else {
                  std::cerr << "Delegated bandwidth not found" << std::endl;
               }
            } else {
               std::cout << fc::json::to_pretty_string(result) << std::endl;
            }
      });
   }
};

1173 1174 1175 1176 1177 1178 1179
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"));
1180 1181
      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();
1182 1183 1184 1185
      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()
1186
               ("payer", from_str)
1187
               ("receiver", receiver_str)
1188
               ("quant", to_asset(amount));
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
            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"));
1201
      sellram->add_option("account", receiver_str, localized("The account to receive EOS for sold RAM"))->required();
1202 1203 1204 1205 1206
      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()
1207
               ("account", receiver_str)
1208
               ("bytes", amount);
1209
            send_actions({create_action({permission_level{receiver_str,config::active_name}}, config::system_account_name, N(sellram), act_payload)});
1210 1211 1212 1213
         });
   }
};

D
Daniel Larimer 已提交
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
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()
1240 1241
                  ("proxy", proxy)
                  ("isproxy", true);
D
Daniel Larimer 已提交
1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
         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()
1257 1258 1259
                  ("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 已提交
1260 1261 1262 1263 1264
      });
   }
};

struct canceldelay_subcommand {
1265 1266
   string canceling_account;
   string canceling_permission;
D
Daniel Larimer 已提交
1267 1268 1269 1270
   string trx_id;

   canceldelay_subcommand(CLI::App* actionRoot) {
      auto cancel_delay = actionRoot->add_subcommand("canceldelay", localized("Cancel a delayed transaction"));
1271 1272
      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 已提交
1273 1274 1275 1276
      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] {
1277
         const auto canceling_auth = permission_level{canceling_account, canceling_permission};
D
Daniel Larimer 已提交
1278
         fc::variant act_payload = fc::mutable_variant_object()
1279
                  ("canceling_auth", canceling_auth)
D
Daniel Larimer 已提交
1280
                  ("trx_id", trx_id);
1281
         send_actions({create_action({canceling_auth}, config::system_account_name, N(canceldelay), act_payload)});
D
Daniel Larimer 已提交
1282 1283 1284 1285
      });
   }
};

1286
void get_account( const string& accountName, bool json_format ) {
1287 1288
   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>();
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304

   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 已提交
1305
         }
1306 1307 1308
         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)) );
1309
      }
1310 1311 1312 1313 1314 1315 1316 1317
      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);
1318
         }
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
         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 );
      }
1337

1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
      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();
      };



1362
      std::cout << "memory: " << std::endl
1363
                << 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;
1364

1365
      std::cout << "net bandwidth: (averaged over 3 days)" << std::endl;
1366
      if ( res.total_resources.is_object() ) {
1367
         asset net_own = res.delegated_bandwidth.is_object() ? asset::from_string( res.delegated_bandwidth.get_object()["net_weight"].as_string() ) : asset(0) ;
1368
         auto net_others = to_asset(res.total_resources.get_object()["net_weight"].as_string()) - net_own;
1369 1370 1371 1372 1373
         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;
      }
1374

1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400

      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();
      };

1401 1402

      std::cout << std::fixed << setprecision(3);
1403 1404 1405 1406 1407 1408 1409
      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;
1410 1411


1412
      if ( res.total_resources.is_object() ) {
1413
         asset cpu_own = res.delegated_bandwidth.is_object() ? asset::from_string( res.delegated_bandwidth.get_object()["cpu_weight"].as_string() ) : asset(0) ;
1414
         auto cpu_others = to_asset(res.total_resources.get_object()["cpu_weight"].as_string()) - cpu_own;
1415 1416 1417 1418 1419
         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;
      }
1420

1421 1422 1423 1424 1425 1426 1427

      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;

1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440

      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();
1441
               }
1442 1443 1444
               std::cout << std::endl;
            } else {
               std::cout << indent << "<not voted>" << std::endl;
1445 1446
            }
         } else {
1447
            std::cout << "proxy:" << indent << proxy << std::endl;
1448 1449
         }
      }
1450 1451 1452
      std::cout << std::endl;
   } else {
      std::cout << fc::json::to_pretty_string(json) << std::endl;
1453
   }
1454 1455
}

1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
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;
};

1466
int main( int argc, char** argv ) {
1467 1468 1469 1470
   setlocale(LC_ALL, "");
   bindtextdomain(locale_domain, locale_path);
   textdomain(locale_domain);

1471
   CLI::App app{"Command Line Interface to EOSIO Client"};
1472
   app.require_subcommand();
R
Roman Brod 已提交
1473 1474 1475 1476
   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 已提交
1477 1478 1479

   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 );
1480

1481
   app.add_option( "-r,--header", header_opt_callback, localized("pass specific HTTP header; repeat this option to pass multiple headers"));
1482
   app.add_flag( "-n,--no-verify", no_verify, localized("don't verify peer certificate when using HTTPS"));
1483
   app.set_callback([&app]{ ensure_keosd_running(&app);});
A
Anton Perkov 已提交
1484

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

1488 1489 1490 1491 1492 1493 1494
   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;
   });

1495
   // Create subcommand
1496
   auto create = app.add_subcommand("create", localized("Create various items, on and off the blockchain"), false);
N
Nathan Hourt 已提交
1497 1498 1499
   create->require_subcommand();

   // create key
D
Daniel Larimer 已提交
1500 1501 1502 1503 1504 1505
   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 已提交
1506 1507 1508
   });

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

1511
   // Get subcommand
1512
   auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"), false);
N
Nathan Hourt 已提交
1513 1514 1515
   get->require_subcommand();

   // get info
1516
   get->add_subcommand("info", localized("Get current blockchain information"))->set_callback([] {
N
Nathan Hourt 已提交
1517 1518 1519 1520 1521
      std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
   });

   // get block
   string blockArg;
1522 1523
   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 已提交
1524 1525 1526 1527 1528 1529 1530
   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;
1531
   bool print_json;
1532 1533
   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();
1534 1535
   getAccount->add_flag("--json,-j", print_json, localized("Output in JSON format") );
   getAccount->set_callback([&]() { get_account(accountName, print_json); });
N
Nathan Hourt 已提交
1536

D
Daniel Larimer 已提交
1537 1538 1539
   // get code
   string codeFilename;
   string abiFilename;
1540
   bool code_as_wasm = false;
1541 1542
   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();
1543
   getCode->add_option("-c,--code",codeFilename, localized("The name of the file to save the contract .wast/wasm to") );
1544
   getCode->add_option("-a,--abi",abiFilename, localized("The name of the file to save the contract .abi to") );
1545
   getCode->add_flag("--wasm", code_as_wasm, localized("Save contract as wasm"));
D
Daniel Larimer 已提交
1546
   getCode->set_callback([&] {
1547
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", accountName)("code_as_wasm",code_as_wasm));
D
Daniel Larimer 已提交
1548

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

      if( codeFilename.size() ){
1552 1553 1554 1555 1556 1557 1558 1559 1560
         std::cout << localized("saving ${type} to ${codeFilename}", ("type", (code_as_wasm ? "wasm" : "wast"))("codeFilename", codeFilename)) << std::endl;
         string code;

         if(code_as_wasm) {
            code = result["wasm"].as_string();
         } else {
            code = result["wast"].as_string();
         }

D
Daniel Larimer 已提交
1561 1562 1563 1564
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
1565
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
1566 1567 1568 1569 1570 1571
         auto abi  = fc::json::to_pretty_string( result["abi"] );
         std::ofstream abiout( abiFilename.c_str() );
         abiout << abi;
      }
   });

1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589
   // get abi
   string filename;
   auto getAbi = get->add_subcommand("abi", localized("Retrieve the ABI for an account"), false);
   getAbi->add_option("name", accountName, localized("The name of the account whose abi should be retrieved"))->required();
   getAbi->add_option("-f,--file",filename, localized("The name of the file to save the contract .abi to instead of writing to console") );
   getAbi->set_callback([&] {
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", accountName));

      auto abi  = fc::json::to_pretty_string( result["abi"] );
      if( filename.size() ) {
         std::cout << localized("saving abi to ${filename}", ("filename", filename)) << std::endl;
         std::ofstream abiout( filename.c_str() );
         abiout << abi;
      } else {
         std::cout << abi << "\n";
      }
   });

1590
   // get table
D
Daniel Larimer 已提交
1591 1592 1593 1594 1595
   string scope;
   string code;
   string table;
   string lower;
   string upper;
1596
   string table_key;
D
Daniel Larimer 已提交
1597 1598
   bool binary = false;
   uint32_t limit = 10;
1599
   auto getTable = get->add_subcommand( "table", localized("Retrieve the contents of a database table"), false);
1600 1601
   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();
1602 1603 1604
   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") );
1605
   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") );
1606 1607
   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 已提交
1608 1609 1610 1611

   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("code",code)
1612
                         ("scope",scope)
D
Daniel Larimer 已提交
1613
                         ("table",table)
1614 1615 1616 1617
                         ("table_key",table_key)
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
D
Daniel Larimer 已提交
1618 1619 1620 1621 1622 1623
                         );

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

1624 1625 1626 1627
   // currency accessors
   // get currency balance
   string symbol;
   auto get_currency = get->add_subcommand( "currency", localized("Retrieve information related to standard currencies"), true);
1628
   get_currency->require_subcommand();
1629 1630 1631 1632 1633
   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 已提交
1634
      auto result = call(get_currency_balance_func, fc::mutable_variant_object
1635
         ("account", accountName)
1636
         ("code", code)
D
Daniel Larimer 已提交
1637
         ("symbol", symbol.empty() ? fc::variant() : symbol)
1638 1639
      );

1640
      const auto& rows = result.get_array();
D
Daniel Larimer 已提交
1641 1642
      for( const auto& r : rows ) {
         std::cout << r.as_string()
1643 1644 1645
                   << std::endl;
      }
   });
D
Daniel Larimer 已提交
1646

1647 1648
   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 已提交
1649
   get_currency_stats->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") )->required();
1650
   get_currency_stats->set_callback([&] {
1651
      auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
1652
         ("code", code)
1653
         ("symbol", symbol)
1654 1655
      );

D
Daniel Larimer 已提交
1656 1657
      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
1658
   });
D
Daniel Larimer 已提交
1659

1660
   // get accounts
1661
   string public_key_str;
1662
   auto getAccounts = get->add_subcommand("accounts", localized("Retrieve accounts associated with a public key"), false);
1663
   getAccounts->add_option("public_key", public_key_str, localized("The public key to retrieve accounts for"))->required();
1664
   getAccounts->set_callback([&] {
1665 1666 1667
      public_key_type public_key;
      try {
         public_key = public_key_type(public_key_str);
1668
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", public_key_str))
1669
      auto arg = fc::mutable_variant_object( "public_key", public_key);
1670 1671 1672
      std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
   });

1673

1674 1675
   // get servants
   string controllingAccount;
1676 1677
   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();
1678
   getServants->set_callback([&] {
1679
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
1680 1681 1682
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

N
Nathan Hourt 已提交
1683
   // get transaction
1684
   string transaction_id_str;
1685
   auto getTransaction = get->add_subcommand("transaction", localized("Retrieve a transaction from the blockchain"), false);
1686
   getTransaction->add_option("id", transaction_id_str, localized("ID of the transaction to retrieve"))->required();
N
Nathan Hourt 已提交
1687
   getTransaction->set_callback([&] {
1688 1689
      transaction_id_type transaction_id;
      try {
D
Daniel Larimer 已提交
1690
         while( transaction_id_str.size() < 64 ) transaction_id_str += "0";
1691
         transaction_id = transaction_id_type(transaction_id_str);
1692
      } EOS_RETHROW_EXCEPTIONS(transaction_id_type_exception, "Invalid transaction ID: ${transaction_id}", ("transaction_id", transaction_id_str))
D
Daniel Larimer 已提交
1693
      auto arg= fc::mutable_variant_object( "id", transaction_id);
N
Nathan Hourt 已提交
1694 1695
      std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
   });
1696

A
arhag 已提交
1697
   // get actions
A
Anton Perkov 已提交
1698
   string account_name;
1699 1700
   string skip_seq_str;
   string num_seq_str;
D
Daniel Larimer 已提交
1701
   bool printjson = false;
1702 1703
   bool fullact = false;
   bool prettyact = false;
1704
   bool printconsole = false;
D
Daniel Larimer 已提交
1705 1706 1707 1708 1709 1710

   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 已提交
1711
   getActions->add_option("offset", offset, localized("get actions [pos,pos+offset] for positive offset or [pos-offset,pos) for negative offset"));
D
Daniel Larimer 已提交
1712
   getActions->add_flag("--json,-j", printjson, localized("print full json"));
1713 1714
   getActions->add_flag("--full", fullact, localized("don't truncate action json"));
   getActions->add_flag("--pretty", prettyact, localized("pretty print full action json "));
1715
   getActions->add_flag("--console", printconsole, localized("print console output generated by action "));
D
Daniel Larimer 已提交
1716 1717 1718 1719 1720 1721 1722
   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);
1723 1724 1725 1726 1727 1728 1729 1730


      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();

1731 1732 1733

          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";
1734 1735
          for( const auto& trace: traces ) {
              std::stringstream out;
D
Daniel Larimer 已提交
1736 1737 1738 1739 1740
              if( trace["block_num"].as_uint64() <= lib )
                 out << "#";
              else
                 out << "?";

1741 1742
              out << setw(5) << trace["account_action_seq"].as_uint64() <<"  ";
              out << setw(24) << trace["block_time"].as_string() <<"  ";
1743 1744 1745

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

1746
              auto id = at["trx_id"].as_string();
1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761
              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) + "...";
                 }
              }
1762 1763 1764
              out << std::setw(24) << std::right<< (code +"::" + func) << " => " << left << std::setw(13) << receiver;

              out << " " << setw(11) << (id.substr(0,8) + "...");
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775

              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()) );
              }
1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786
              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()) );
                 }
              }
1787 1788
          }
      }
D
Daniel Larimer 已提交
1789 1790 1791 1792
   });


   /*
1793
   auto getTransactions = get->add_subcommand("transactions", localized("Retrieve all transactions with specific account name referenced in their scope"), false);
1794
   getTransactions->add_option("account_name", account_name, localized("name of account to query on"))->required();
1795 1796
   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 已提交
1797
   getTransactions->add_flag("--json,-j", printjson, localized("print full json"));
1798
   getTransactions->set_callback([&] {
1799 1800 1801 1802 1803 1804 1805
      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);
1806
         } EOS_RETHROW_EXCEPTIONS(chain_type_exception, "Invalid Skip Seq: ${skip_seq}", ("skip_seq", skip_seq_str))
1807 1808 1809 1810 1811 1812
         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);
1813
            } EOS_RETHROW_EXCEPTIONS(chain_type_exception, "Invalid Num Seq: ${num_seq}", ("num_seq", num_seq_str))
1814 1815 1816
            arg = fc::mutable_variant_object( "account_name", account_name)("skip_seq", skip_seq_str)("num_seq", num_seq);
         }
      }
D
Daniel Larimer 已提交
1817
      auto result = call(get_transactions_func, arg);
D
Daniel Larimer 已提交
1818
      if( printjson ) {
J
Joel 已提交
1819
         std::cout << fc::json::to_pretty_string(result) << std::endl;
D
Daniel Larimer 已提交
1820 1821 1822 1823 1824 1825 1826
      }
      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 已提交
1827
            const auto& data = trx["transaction"].get_object();
D
Daniel Larimer 已提交
1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841
            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 已提交
1842 1843
      }

1844
   });
D
Daniel Larimer 已提交
1845
   */
1846

1847
   // set subcommand
1848
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
1849 1850 1851
   setSubcommand->require_subcommand();

   // set contract subcommand
N
Nathan Hourt 已提交
1852
   string account;
1853
   string contractPath;
N
Nathan Hourt 已提交
1854 1855
   string wastPath;
   string abiPath;
1856
   bool shouldSend = true;
1857 1858 1859 1860 1861 1862 1863 1864
   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();

1865
   auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
1866 1867
   contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))
                     ->required();
1868
   contractSubcommand->add_option("contract-dir", contractPath, localized("The path containing the .wast and .abi"))
1869 1870 1871 1872 1873
                     ->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);
1874

1875
   std::vector<chain::action> actions;
1876
   auto set_code_callback = [&]() {
N
Nathan Hourt 已提交
1877
      std::string wast;
1878
      fc::path cpath(contractPath);
1879

1880 1881
      if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();

B
Bucky Kittinger 已提交
1882
      if( wastPath.empty() )
1883
      {
D
Daniel Larimer 已提交
1884 1885 1886
         wastPath = (cpath / (cpath.filename().generic_string()+".wasm")).generic_string();
         if (!fc::exists(wastPath))
            wastPath = (cpath / (cpath.filename().generic_string()+".wast")).generic_string();
1887 1888
      }

D
Daniel Larimer 已提交
1889
      std::cout << localized(("Reading WAST/WASM from " + wastPath + "...").c_str()) << std::endl;
N
Nathan Hourt 已提交
1890
      fc::read_file_contents(wastPath, wast);
B
Bucky Kittinger 已提交
1891
      FC_ASSERT( !wast.empty(), "no wast file found ${f}", ("f", wastPath) );
1892
      vector<uint8_t> wasm;
1893
      const string binary_wasm_header("\x00\x61\x73\x6d", 4);
1894
      if(wast.compare(0, 4, binary_wasm_header) == 0) {
1895
         std::cout << localized("Using already assembled WASM...") << std::endl;
1896
         wasm = vector<uint8_t>(wast.begin(), wast.end());
1897
      }
1898 1899
      else {
         std::cout << localized("Assembling WASM...") << std::endl;
1900
         wasm = wast_to_wasm(wast);
1901
      }
N
Nathan Hourt 已提交
1902

M
Matias Romeo 已提交
1903
      actions.emplace_back( create_setcode(account, bytes(wasm.begin(), wasm.end()) ) );
1904 1905 1906 1907
      if ( shouldSend ) {
         std::cout << localized("Setting Code...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      }
1908 1909 1910 1911
   };

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

1914 1915 1916 1917
      if( abiPath.empty() )
      {
         abiPath = (cpath / (cpath.filename().generic_string()+".abi")).generic_string();
      }
1918

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

      try {
D
Daniel Larimer 已提交
1922
         actions.emplace_back( create_setabi(account, fc::json::from_file(abiPath).as<abi_def>()) );
D
Daniel Larimer 已提交
1923
      } EOS_RETHROW_EXCEPTIONS(abi_type_exception,  "Fail to parse ABI JSON")
1924 1925 1926 1927
      if ( shouldSend ) {
         std::cout << localized("Setting ABI...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      }
1928
   };
K
Kevin Heifner 已提交
1929

1930 1931 1932 1933
   add_standard_transaction_options(contractSubcommand, "account@active");
   add_standard_transaction_options(codeSubcommand, "account@active");
   add_standard_transaction_options(abiSubcommand, "account@active");
   contractSubcommand->set_callback([&] {
1934
      shouldSend = false;
1935 1936
      set_code_callback();
      set_abi_callback();
1937
      std::cout << localized("Publishing contract...") << std::endl;
D
Daniel Larimer 已提交
1938
      send_actions(std::move(actions), 10000, packed_transaction::zlib);
N
Nathan Hourt 已提交
1939
   });
1940 1941
   codeSubcommand->set_callback(set_code_callback);
   abiSubcommand->set_callback(set_abi_callback);
N
Nathan Hourt 已提交
1942

1943
   // set account
1944
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
1945 1946

   // set account permission
1947
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
1948 1949

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

1952
   // set action permission
1953
   auto setActionPermission = set_action_permission_subcommand(setAction);
1954

N
Nathan Hourt 已提交
1955
   // Transfer subcommand
1956
   string con = "eosio.token";
N
Nathan Hourt 已提交
1957 1958
   string sender;
   string recipient;
1959
   string amount;
1960
   string memo;
1961 1962 1963 1964 1965
   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"));
1966
   transfer->add_option("--contract,-c", con, localized("The contract which controls the token"));
K
Kevin Heifner 已提交
1967

M
Matias Romeo 已提交
1968
   add_standard_transaction_options(transfer, "sender@active");
N
Nathan Hourt 已提交
1969
   transfer->set_callback([&] {
1970 1971 1972
      signed_transaction trx;
      if (tx_force_unique && memo.size() == 0) {
         // use the memo to add a nonce
1973
         memo = generate_nonce_string();
1974
         tx_force_unique = false;
1975
      }
1976

1977
      send_actions({create_transfer(con,sender, recipient, to_asset(amount), memo)});
N
Nathan Hourt 已提交
1978
   });
N
Nathan Hourt 已提交
1979

1980
   // Net subcommand
1981 1982 1983 1984 1985 1986
   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 已提交
1987
      const auto& v = call(url, net_connect, new_host);
1988 1989 1990 1991 1992 1993
      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 已提交
1994
      const auto& v = call(url, net_disconnect, new_host);
1995 1996 1997 1998 1999 2000
      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 已提交
2001
      const auto& v = call(url, net_status, new_host);
2002 2003 2004 2005 2006
      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 已提交
2007
      const auto& v = call(url, net_connections, new_host);
2008 2009 2010 2011
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });


K
Kevin Heifner 已提交
2012 2013

   // Wallet subcommand
2014
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
2015
   wallet->require_subcommand();
K
Kevin Heifner 已提交
2016
   // create wallet
D
Daniel Larimer 已提交
2017
   string wallet_name = "default";
2018 2019
   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 已提交
2020
   createWallet->set_callback([&wallet_name] {
2021 2022 2023
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2024
      const auto& v = call(wallet_url, wallet_create, wallet_name);
2025 2026 2027
      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 已提交
2028 2029 2030 2031
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
2032 2033
   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 已提交
2034
   openWallet->set_callback([&wallet_name] {
2035 2036 2037
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2038
      call(wallet_url, wallet_open, wallet_name);
2039
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2040 2041 2042
   });

   // lock wallet
2043 2044
   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 已提交
2045
   lockWallet->set_callback([&wallet_name] {
2046 2047 2048
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2049
      call(wallet_url, wallet_lock, wallet_name);
2050
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2051 2052 2053
   });

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

D
Daniel Larimer 已提交
2059
      call(wallet_url, wallet_lock_all);
2060
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
2061 2062 2063 2064
   });

   // unlock wallet
   string wallet_pw;
2065 2066 2067
   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"));
2068
   unlockWallet->add_option( "--unlock-timeout", wallet_unlock_timeout, localized("The timeout for unlocked wallet in seconds"));
K
Kevin Heifner 已提交
2069
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
2070
      if( wallet_pw.size() == 0 ) {
2071
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
2072 2073 2074 2075
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }
2076 2077
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);
D
Daniel Larimer 已提交
2078 2079


K
Kevin Heifner 已提交
2080
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
2081
      call(wallet_url, wallet_unlock, vs);
2082
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2083 2084 2085
   });

   // import keys into wallet
2086
   string wallet_key_str;
2087 2088
   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"));
2089 2090 2091 2092 2093 2094 2095 2096 2097
   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 已提交
2098

K
Kevin Heifner 已提交
2099
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
D
Daniel Larimer 已提交
2100
      call(wallet_url, wallet_import_key, vs);
2101
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
K
Kevin Heifner 已提交
2102
   });
2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114

   // 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 已提交
2115 2116

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

2122
      std::cout << localized("Wallets:") << std::endl;
D
Daniel Larimer 已提交
2123
      const auto& v = call(wallet_url, wallet_list);
K
Kevin Heifner 已提交
2124 2125 2126 2127
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // list keys
2128
   auto listKeys = wallet->add_subcommand("keys", localized("List of public keys from all unlocked wallets."), false);
K
Kevin Heifner 已提交
2129
   listKeys->set_callback([] {
2130 2131 2132
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152
      const auto& v = call(wallet_url, wallet_public_keys);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // list private keys
   auto listPrivKeys = wallet->add_subcommand("private_keys", localized("List of private keys from an unlocked wallet in wif or PVT_R1 format."), false);
   listPrivKeys->add_option("-n,--name", wallet_name, localized("The name of the wallet to list keys from"), true);
   listPrivKeys->add_option("--password", wallet_pw, localized("The password returned by wallet create"));
   listPrivKeys->set_callback([&wallet_name, &wallet_pw] {
      if( wallet_pw.size() == 0 ) {
         std::cout << localized("password: ");
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
      const auto& v = call(wallet_url, wallet_list_keys, vs);
K
Kevin Heifner 已提交
2153 2154 2155
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

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

2161
      const auto& v = call(wallet_url, keosd_stop);
A
Anton Perkov 已提交
2162 2163 2164 2165 2166
      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;
      }
2167 2168
   });

2169 2170 2171
   // sign subcommand
   string trx_json_to_sign;
   string str_private_key;
2172
   string str_chain_id;
2173 2174 2175 2176
   bool push_trx = false;

   auto sign = app.add_subcommand("sign", localized("Sign a transaction"), false);
   sign->add_option("transaction", trx_json_to_sign,
2177
                                 localized("The JSON string or filename defining the transaction to sign"), true)->required();
2178
   sign->add_option("-k,--private-key", str_private_key, localized("The private key that will be used to sign the transaction"));
2179
   sign->add_option("-c,--chain-id", str_chain_id, localized("The chain id that will be used to sign the transaction"));
2180 2181 2182
   sign->add_flag( "-p,--push-transaction", push_trx, localized("Push transaction after signing"));

   sign->set_callback([&] {
2183
      signed_transaction trx = json_from_file_or_string(trx_json_to_sign).as<signed_transaction>();
2184 2185

      fc::optional<chain_id_type> chain_id;
2186 2187 2188 2189 2190 2191 2192 2193

      if( str_chain_id.size() == 0 ) {
         ilog( "grabbing chain_id from nodeos" );
         auto info = get_info();
         chain_id = info.chain_id;
      } else {
         chain_id = chain_id_type(str_chain_id);
      }
2194 2195 2196 2197 2198 2199 2200 2201 2202

      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));
2203
      trx.sign(priv_key, *chain_id);
2204 2205 2206 2207 2208 2209 2210 2211 2212

      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 已提交
2213
   // Push subcommand
2214
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
N
Nathan Hourt 已提交
2215 2216
   push->require_subcommand();

2217
   // push action
W
wqxiang88 已提交
2218
   string contract_account;
N
Nathan Hourt 已提交
2219 2220
   string action;
   string data;
2221
   vector<string> permissions;
2222
   auto actionsSubcommand = push->add_subcommand("action", localized("Push a transaction with a single action"));
D
Daniel Larimer 已提交
2223
   actionsSubcommand->fallthrough(false);
W
wqxiang88 已提交
2224
   actionsSubcommand->add_option("account", contract_account,
2225
                                 localized("The account providing the contract to execute"), true)->required();
2226 2227
   actionsSubcommand->add_option("action", action,
                                 localized("A JSON string or filename defining the action to execute on the contract"), true)->required();
D
Daniel Larimer 已提交
2228
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
2229

D
Daniel Larimer 已提交
2230 2231
   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
2232 2233
      fc::variant action_args_var;
      try {
2234 2235
         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))
2236

N
Nathan Hourt 已提交
2237
      auto arg= fc::mutable_variant_object
W
wqxiang88 已提交
2238
                ("code", contract_account)
N
Nathan Hourt 已提交
2239
                ("action", action)
2240
                ("args", action_args_var);
N
Nathan Hourt 已提交
2241 2242
      auto result = call(json_to_bin_func, arg);

2243
      auto accountPermissions = get_account_permissions(tx_permission);
2244

W
wqxiang88 已提交
2245
      send_actions({chain::action{accountPermissions, contract_account, action, result.get_object()["binargs"].as<bytes>()}});
N
Nathan Hourt 已提交
2246 2247 2248
   });

   // push transaction
2249
   string trx_to_push;
2250
   auto trxSubcommand = push->add_subcommand("transaction", localized("Push an arbitrary JSON transaction"));
2251
   trxSubcommand->add_option("transaction", trx_to_push, localized("The JSON string or filename defining the transaction to push"))->required();
2252

N
Nathan Hourt 已提交
2253
   trxSubcommand->set_callback([&] {
2254 2255
      fc::variant trx_var;
      try {
2256 2257
         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 已提交
2258
      signed_transaction trx = trx_var.as<signed_transaction>();
2259
      auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
N
Nathan Hourt 已提交
2260 2261
      std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
   });
2262

2263 2264

   string trxsJson;
2265
   auto trxsSubcommand = push->add_subcommand("transactions", localized("Push an array of arbitrary JSON transactions"));
2266
   trxsSubcommand->add_option("transactions", trxsJson, localized("The JSON string or filename defining the array of the transactions to push"))->required();
2267
   trxsSubcommand->set_callback([&] {
2268 2269
      fc::variant trx_var;
      try {
2270 2271
         trx_var = json_from_file_or_string(trxsJson);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trxsJson))
2272
      auto trxs_result = call(push_txns_func, trx_var);
2273 2274 2275
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });

D
Daniel Larimer 已提交
2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 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

   // 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 {
2352
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <proposer> or -p)");
D
Daniel Larimer 已提交
2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364
         }
      }
      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;
2365
      trx.max_cpu_usage_ms = 0;
D
Daniel Larimer 已提交
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 2416 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 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484
      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 {
2485
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <canceler> or -p)");
D
Daniel Larimer 已提交
2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516
         }
      }
      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 {
2517
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <executer> or -p)");
D
Daniel Larimer 已提交
2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541
         }
      }
      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 已提交
2542
   auto createAccountSystem = create_account_subcommand( system, false /*simple*/ );
D
Daniel Larimer 已提交
2543 2544 2545 2546 2547 2548 2549
   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);
2550 2551
   auto approveProducer = approve_producer_subcommand(voteProducer);
   auto unapproveProducer = unapprove_producer_subcommand(voteProducer);
D
Daniel Larimer 已提交
2552

A
Anton Perkov 已提交
2553 2554
   auto listProducers = list_producers_subcommand(system);

D
Daniel Larimer 已提交
2555 2556
   auto delegateBandWidth = delegate_bandwidth_subcommand(system);
   auto undelegateBandWidth = undelegate_bandwidth_subcommand(system);
A
Anton Perkov 已提交
2557
   auto listBandWidth = list_bw_subcommand(system);
D
Daniel Larimer 已提交
2558

2559 2560 2561
   auto biyram = buyram_subcommand(system);
   auto sellram = sellram_subcommand(system);

D
Daniel Larimer 已提交
2562 2563 2564 2565 2566 2567 2568
   auto claimRewards = claimrewards_subcommand(system);

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

   auto cancelDelay = canceldelay_subcommand(system);

2569 2570 2571 2572
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
2573 2574
   } catch (const explained_exception& e) {
      return 1;
D
Daniel Larimer 已提交
2575 2576 2577 2578
   } catch (connection_exception& e) {
      if (verbose_errors) {
         elog("connect error: ${e}", ("e", e.to_detail_string()));
      }
2579
   } catch (const fc::exception& e) {
D
Daniel Larimer 已提交
2580 2581 2582 2583 2584
      // 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()));
2585
         }
2586
      }
2587 2588 2589 2590
      return 1;
   }

   return 0;
2591
}