main.cpp 117.5 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 830 831
struct create_account_subcommand {
   string creator;
   string account_name;
   string owner_key_str;
   string active_key_str;
   string stake_net;
   string stake_cpu;
   uint32_t buy_ram_bytes_in_kbytes = 8;
   string buy_ram_eos;
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
               action buyram = !buy_ram_eos.empty() ? create_buyram(creator, account_name, to_asset(buy_ram_eos))
A
Anton Perkov 已提交
870
                  : create_buyrambytes(creator, account_name, buy_ram_bytes_in_kbytes * 1024);
871
               action delegate = create_delegate( creator, account_name, to_asset(stake_net), to_asset(stake_cpu), transfer);
A
Anton Perkov 已提交
872 873 874 875 876 877 878 879
               send_actions( { create, buyram, delegate } );
            } else {
               send_actions( { create } );
            }
      });
   }
};

D
Daniel Larimer 已提交
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
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 已提交
919
   vector<eosio::name> producer_names;
D
Daniel Larimer 已提交
920 921 922 923

   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 已提交
924
      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 已提交
925 926 927
      add_standard_transaction_options(vote_producers);

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

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

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

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

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

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

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

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

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

A
Anton Perkov 已提交
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
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"
1146
                            << std::setw(21) << std::left << "CPU bandwidth" << std::endl;
A
Anton Perkov 已提交
1147 1148 1149
                  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()
1150
                               << std::setw(21) << std::left << r["cpu_weight"].as_string()
A
Anton Perkov 已提交
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
                               << std::endl;
                  }
               } else {
                  std::cerr << "Delegated bandwidth not found" << std::endl;
               }
            } else {
               std::cout << fc::json::to_pretty_string(result) << std::endl;
            }
      });
   }
};

1163 1164 1165 1166 1167 1168 1169
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"));
1170 1171
      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();
1172 1173 1174 1175
      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()
1176
               ("payer", from_str)
1177
               ("receiver", receiver_str)
1178
               ("quant", to_asset(amount));
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
            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"));
1191
      sellram->add_option("account", receiver_str, localized("The account to receive EOS for sold RAM"))->required();
1192 1193 1194 1195 1196
      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()
1197
               ("account", receiver_str)
1198
               ("bytes", amount);
1199
            send_actions({create_action({permission_level{receiver_str,config::active_name}}, config::system_account_name, N(sellram), act_payload)});
1200 1201 1202 1203
         });
   }
};

D
Daniel Larimer 已提交
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
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()
1230 1231
                  ("proxy", proxy)
                  ("isproxy", true);
D
Daniel Larimer 已提交
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
         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()
1247 1248 1249
                  ("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 已提交
1250 1251 1252 1253 1254
      });
   }
};

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

   canceldelay_subcommand(CLI::App* actionRoot) {
      auto cancel_delay = actionRoot->add_subcommand("canceldelay", localized("Cancel a delayed transaction"));
1261 1262
      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 已提交
1263 1264 1265 1266
      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] {
1267
         const auto canceling_auth = permission_level{canceling_account, canceling_permission};
D
Daniel Larimer 已提交
1268
         fc::variant act_payload = fc::mutable_variant_object()
1269
                  ("canceling_auth", canceling_auth)
D
Daniel Larimer 已提交
1270
                  ("trx_id", trx_id);
1271
         send_actions({create_action({canceling_auth}, config::system_account_name, N(canceldelay), act_payload)});
D
Daniel Larimer 已提交
1272 1273 1274 1275
      });
   }
};

1276
void get_account( const string& accountName, bool json_format ) {
1277 1278
   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>();
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294

   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 已提交
1295
         }
1296 1297 1298
         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)) );
1299
      }
1300 1301 1302 1303 1304 1305 1306 1307
      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);
1308
         }
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
         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 );
      }
1327

1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351
      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();
      };



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

1355
      std::cout << "net bandwidth: (averaged over 3 days)" << std::endl;
1356
      if ( res.total_resources.is_object() ) {
1357
         asset net_own = res.delegated_bandwidth.is_object() ? asset::from_string( res.delegated_bandwidth.get_object()["net_weight"].as_string() ) : asset(0) ;
1358
         auto net_others = to_asset(res.total_resources.get_object()["net_weight"].as_string()) - net_own;
1359 1360 1361 1362 1363
         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;
      }
1364

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

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

1391 1392

      std::cout << std::fixed << setprecision(3);
1393 1394 1395 1396 1397 1398 1399
      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;
1400 1401


1402
      if ( res.total_resources.is_object() ) {
1403
         asset cpu_own = res.delegated_bandwidth.is_object() ? asset::from_string( res.delegated_bandwidth.get_object()["cpu_weight"].as_string() ) : asset(0) ;
1404
         auto cpu_others = to_asset(res.total_resources.get_object()["cpu_weight"].as_string()) - cpu_own;
1405 1406 1407 1408 1409
         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;
      }
1410

1411 1412 1413 1414 1415 1416 1417

      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;

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

      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();
1431
               }
1432 1433 1434
               std::cout << std::endl;
            } else {
               std::cout << indent << "<not voted>" << std::endl;
1435 1436
            }
         } else {
1437
            std::cout << "proxy:" << indent << proxy << std::endl;
1438 1439
         }
      }
1440 1441 1442
      std::cout << std::endl;
   } else {
      std::cout << fc::json::to_pretty_string(json) << std::endl;
1443
   }
1444 1445
}

1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
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;
};

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

1461
   CLI::App app{"Command Line Interface to EOSIO Client"};
1462
   app.require_subcommand();
R
Roman Brod 已提交
1463 1464 1465 1466
   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 已提交
1467 1468 1469

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

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

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

1478 1479 1480 1481 1482 1483 1484
   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;
   });

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

   // create key
D
Daniel Larimer 已提交
1490 1491 1492 1493 1494 1495
   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 已提交
1496 1497 1498
   });

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

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

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

   // get block
   string blockArg;
1512 1513
   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 已提交
1514 1515 1516 1517 1518 1519 1520
   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;
1521
   bool print_json;
1522 1523
   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();
1524 1525
   getAccount->add_flag("--json,-j", print_json, localized("Output in JSON format") );
   getAccount->set_callback([&]() { get_account(accountName, print_json); });
N
Nathan Hourt 已提交
1526

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

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

      if( codeFilename.size() ){
1542 1543 1544 1545 1546 1547 1548 1549 1550
         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 已提交
1551 1552 1553 1554
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
1555
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
1556 1557 1558 1559 1560 1561
         auto abi  = fc::json::to_pretty_string( result["abi"] );
         std::ofstream abiout( abiFilename.c_str() );
         abiout << abi;
      }
   });

1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579
   // 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";
      }
   });

1580
   // get table
D
Daniel Larimer 已提交
1581 1582 1583 1584 1585
   string scope;
   string code;
   string table;
   string lower;
   string upper;
1586
   string table_key;
D
Daniel Larimer 已提交
1587 1588
   bool binary = false;
   uint32_t limit = 10;
1589
   auto getTable = get->add_subcommand( "table", localized("Retrieve the contents of a database table"), false);
1590 1591
   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();
1592 1593 1594
   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") );
1595
   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") );
1596 1597
   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 已提交
1598 1599 1600 1601

   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("code",code)
1602
                         ("scope",scope)
D
Daniel Larimer 已提交
1603
                         ("table",table)
1604 1605 1606 1607
                         ("table_key",table_key)
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
D
Daniel Larimer 已提交
1608 1609 1610 1611 1612 1613
                         );

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

1614 1615 1616 1617
   // currency accessors
   // get currency balance
   string symbol;
   auto get_currency = get->add_subcommand( "currency", localized("Retrieve information related to standard currencies"), true);
1618
   get_currency->require_subcommand();
1619 1620 1621 1622 1623
   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 已提交
1624
      auto result = call(get_currency_balance_func, fc::mutable_variant_object
1625
         ("account", accountName)
1626
         ("code", code)
D
Daniel Larimer 已提交
1627
         ("symbol", symbol.empty() ? fc::variant() : symbol)
1628 1629
      );

1630
      const auto& rows = result.get_array();
D
Daniel Larimer 已提交
1631 1632
      for( const auto& r : rows ) {
         std::cout << r.as_string()
1633 1634 1635
                   << std::endl;
      }
   });
D
Daniel Larimer 已提交
1636

1637 1638
   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 已提交
1639
   get_currency_stats->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") )->required();
1640
   get_currency_stats->set_callback([&] {
1641
      auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
1642
         ("code", code)
1643
         ("symbol", symbol)
1644 1645
      );

D
Daniel Larimer 已提交
1646 1647
      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
1648
   });
D
Daniel Larimer 已提交
1649

1650
   // get accounts
1651
   string public_key_str;
1652
   auto getAccounts = get->add_subcommand("accounts", localized("Retrieve accounts associated with a public key"), false);
1653
   getAccounts->add_option("public_key", public_key_str, localized("The public key to retrieve accounts for"))->required();
1654
   getAccounts->set_callback([&] {
1655 1656 1657
      public_key_type public_key;
      try {
         public_key = public_key_type(public_key_str);
1658
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", public_key_str))
1659
      auto arg = fc::mutable_variant_object( "public_key", public_key);
1660 1661 1662
      std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
   });

1663

1664 1665
   // get servants
   string controllingAccount;
1666 1667
   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();
1668
   getServants->set_callback([&] {
1669
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
1670 1671 1672
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

N
Nathan Hourt 已提交
1673
   // get transaction
1674
   string transaction_id_str;
1675
   auto getTransaction = get->add_subcommand("transaction", localized("Retrieve a transaction from the blockchain"), false);
1676
   getTransaction->add_option("id", transaction_id_str, localized("ID of the transaction to retrieve"))->required();
N
Nathan Hourt 已提交
1677
   getTransaction->set_callback([&] {
1678 1679
      transaction_id_type transaction_id;
      try {
D
Daniel Larimer 已提交
1680
         while( transaction_id_str.size() < 64 ) transaction_id_str += "0";
1681
         transaction_id = transaction_id_type(transaction_id_str);
1682
      } EOS_RETHROW_EXCEPTIONS(transaction_id_type_exception, "Invalid transaction ID: ${transaction_id}", ("transaction_id", transaction_id_str))
D
Daniel Larimer 已提交
1683
      auto arg= fc::mutable_variant_object( "id", transaction_id);
N
Nathan Hourt 已提交
1684 1685
      std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
   });
1686

A
arhag 已提交
1687
   // get actions
A
Anton Perkov 已提交
1688
   string account_name;
1689 1690
   string skip_seq_str;
   string num_seq_str;
D
Daniel Larimer 已提交
1691
   bool printjson = false;
1692 1693
   bool fullact = false;
   bool prettyact = false;
1694
   bool printconsole = false;
D
Daniel Larimer 已提交
1695 1696 1697 1698 1699 1700

   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 已提交
1701
   getActions->add_option("offset", offset, localized("get actions [pos,pos+offset] for positive offset or [pos-offset,pos) for negative offset"));
D
Daniel Larimer 已提交
1702
   getActions->add_flag("--json,-j", printjson, localized("print full json"));
1703 1704
   getActions->add_flag("--full", fullact, localized("don't truncate action json"));
   getActions->add_flag("--pretty", prettyact, localized("pretty print full action json "));
1705
   getActions->add_flag("--console", printconsole, localized("print console output generated by action "));
D
Daniel Larimer 已提交
1706 1707 1708 1709 1710 1711 1712
   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);
1713 1714 1715 1716 1717 1718 1719 1720


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

1721 1722 1723

          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";
1724 1725
          for( const auto& trace: traces ) {
              std::stringstream out;
D
Daniel Larimer 已提交
1726 1727 1728 1729 1730
              if( trace["block_num"].as_uint64() <= lib )
                 out << "#";
              else
                 out << "?";

1731 1732
              out << setw(5) << trace["account_action_seq"].as_uint64() <<"  ";
              out << setw(24) << trace["block_time"].as_string() <<"  ";
1733 1734 1735

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

1736
              auto id = at["trx_id"].as_string();
1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
              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) + "...";
                 }
              }
1752 1753 1754
              out << std::setw(24) << std::right<< (code +"::" + func) << " => " << left << std::setw(13) << receiver;

              out << " " << setw(11) << (id.substr(0,8) + "...");
1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765

              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()) );
              }
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
              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()) );
                 }
              }
1777 1778
          }
      }
D
Daniel Larimer 已提交
1779 1780 1781 1782
   });


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

1834
   });
D
Daniel Larimer 已提交
1835
   */
1836

1837
   // set subcommand
1838
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
1839 1840 1841
   setSubcommand->require_subcommand();

   // set contract subcommand
N
Nathan Hourt 已提交
1842
   string account;
1843
   string contractPath;
N
Nathan Hourt 已提交
1844 1845
   string wastPath;
   string abiPath;
1846
   bool shouldSend = true;
1847 1848 1849 1850 1851 1852 1853 1854
   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();

1855
   auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
1856 1857
   contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))
                     ->required();
1858
   contractSubcommand->add_option("contract-dir", contractPath, localized("The path containing the .wast and .abi"))
1859 1860 1861 1862 1863
                     ->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);
1864

1865
   std::vector<chain::action> actions;
1866
   auto set_code_callback = [&]() {
N
Nathan Hourt 已提交
1867
      std::string wast;
1868
      fc::path cpath(contractPath);
1869

1870 1871
      if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();

B
Bucky Kittinger 已提交
1872
      if( wastPath.empty() )
1873
      {
D
Daniel Larimer 已提交
1874 1875 1876
         wastPath = (cpath / (cpath.filename().generic_string()+".wasm")).generic_string();
         if (!fc::exists(wastPath))
            wastPath = (cpath / (cpath.filename().generic_string()+".wast")).generic_string();
1877 1878
      }

D
Daniel Larimer 已提交
1879
      std::cout << localized(("Reading WAST/WASM from " + wastPath + "...").c_str()) << std::endl;
N
Nathan Hourt 已提交
1880
      fc::read_file_contents(wastPath, wast);
B
Bucky Kittinger 已提交
1881
      FC_ASSERT( !wast.empty(), "no wast file found ${f}", ("f", wastPath) );
1882
      vector<uint8_t> wasm;
1883
      const string binary_wasm_header("\x00\x61\x73\x6d", 4);
1884
      if(wast.compare(0, 4, binary_wasm_header) == 0) {
1885
         std::cout << localized("Using already assembled WASM...") << std::endl;
1886
         wasm = vector<uint8_t>(wast.begin(), wast.end());
1887
      }
1888 1889
      else {
         std::cout << localized("Assembling WASM...") << std::endl;
1890
         wasm = wast_to_wasm(wast);
1891
      }
N
Nathan Hourt 已提交
1892

M
Matias Romeo 已提交
1893
      actions.emplace_back( create_setcode(account, bytes(wasm.begin(), wasm.end()) ) );
1894 1895 1896 1897
      if ( shouldSend ) {
         std::cout << localized("Setting Code...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      }
1898 1899 1900 1901
   };

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

1904 1905 1906 1907
      if( abiPath.empty() )
      {
         abiPath = (cpath / (cpath.filename().generic_string()+".abi")).generic_string();
      }
1908

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

      try {
D
Daniel Larimer 已提交
1912
         actions.emplace_back( create_setabi(account, fc::json::from_file(abiPath).as<abi_def>()) );
D
Daniel Larimer 已提交
1913
      } EOS_RETHROW_EXCEPTIONS(abi_type_exception,  "Fail to parse ABI JSON")
1914 1915 1916 1917
      if ( shouldSend ) {
         std::cout << localized("Setting ABI...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      }
1918
   };
K
Kevin Heifner 已提交
1919

1920 1921 1922 1923
   add_standard_transaction_options(contractSubcommand, "account@active");
   add_standard_transaction_options(codeSubcommand, "account@active");
   add_standard_transaction_options(abiSubcommand, "account@active");
   contractSubcommand->set_callback([&] {
1924
      shouldSend = false;
1925 1926
      set_code_callback();
      set_abi_callback();
1927
      std::cout << localized("Publishing contract...") << std::endl;
D
Daniel Larimer 已提交
1928
      send_actions(std::move(actions), 10000, packed_transaction::zlib);
N
Nathan Hourt 已提交
1929
   });
1930 1931
   codeSubcommand->set_callback(set_code_callback);
   abiSubcommand->set_callback(set_abi_callback);
N
Nathan Hourt 已提交
1932

1933
   // set account
1934
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
1935 1936

   // set account permission
1937
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
1938 1939

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

1942
   // set action permission
1943
   auto setActionPermission = set_action_permission_subcommand(setAction);
1944

N
Nathan Hourt 已提交
1945
   // Transfer subcommand
1946
   string con = "eosio.token";
N
Nathan Hourt 已提交
1947 1948
   string sender;
   string recipient;
1949
   string amount;
1950
   string memo;
1951 1952 1953 1954 1955
   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"));
1956
   transfer->add_option("--contract,-c", con, localized("The contract which controls the token"));
K
Kevin Heifner 已提交
1957

M
Matias Romeo 已提交
1958
   add_standard_transaction_options(transfer, "sender@active");
N
Nathan Hourt 已提交
1959
   transfer->set_callback([&] {
1960 1961 1962
      signed_transaction trx;
      if (tx_force_unique && memo.size() == 0) {
         // use the memo to add a nonce
1963
         memo = generate_nonce_string();
1964
         tx_force_unique = false;
1965
      }
1966

1967
      send_actions({create_transfer(con,sender, recipient, to_asset(amount), memo)});
N
Nathan Hourt 已提交
1968
   });
N
Nathan Hourt 已提交
1969

1970
   // Net subcommand
1971 1972 1973 1974 1975 1976
   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 已提交
1977
      const auto& v = call(url, net_connect, new_host);
1978 1979 1980 1981 1982 1983
      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 已提交
1984
      const auto& v = call(url, net_disconnect, new_host);
1985 1986 1987 1988 1989 1990
      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 已提交
1991
      const auto& v = call(url, net_status, new_host);
1992 1993 1994 1995 1996
      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 已提交
1997
      const auto& v = call(url, net_connections, new_host);
1998 1999 2000 2001
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });


K
Kevin Heifner 已提交
2002 2003

   // Wallet subcommand
2004
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
2005
   wallet->require_subcommand();
K
Kevin Heifner 已提交
2006
   // create wallet
D
Daniel Larimer 已提交
2007
   string wallet_name = "default";
2008 2009
   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 已提交
2010
   createWallet->set_callback([&wallet_name] {
2011 2012 2013
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2014
      const auto& v = call(wallet_url, wallet_create, wallet_name);
2015 2016 2017
      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 已提交
2018 2019 2020 2021
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
2022 2023
   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 已提交
2024
   openWallet->set_callback([&wallet_name] {
2025 2026 2027
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);

D
Daniel Larimer 已提交
2028
      call(wallet_url, wallet_open, wallet_name);
2029
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2030 2031 2032
   });

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

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

   // lock all wallets
2044
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
K
Kevin Heifner 已提交
2045
   locakAllWallets->set_callback([] {
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_all);
2050
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
2051 2052 2053 2054
   });

   // unlock wallet
   string wallet_pw;
2055 2056 2057
   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"));
2058
   unlockWallet->add_option( "--unlock-timeout", wallet_unlock_timeout, localized("The timeout for unlocked wallet in seconds"));
K
Kevin Heifner 已提交
2059
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
2060
      if( wallet_pw.size() == 0 ) {
2061
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
2062 2063 2064 2065
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }
2066 2067
      // wait for keosd to come up
      try_port(uint16_t(std::stoi(parse_url(wallet_url).port)), 2000);
D
Daniel Larimer 已提交
2068 2069


K
Kevin Heifner 已提交
2070
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
2071
      call(wallet_url, wallet_unlock, vs);
2072
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
2073 2074 2075
   });

   // import keys into wallet
2076
   string wallet_key_str;
2077 2078
   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"));
2079 2080 2081 2082 2083 2084 2085 2086 2087
   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 已提交
2088

K
Kevin Heifner 已提交
2089
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
D
Daniel Larimer 已提交
2090
      call(wallet_url, wallet_import_key, vs);
2091
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
K
Kevin Heifner 已提交
2092
   });
2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104

   // 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 已提交
2105 2106

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

2112
      std::cout << localized("Wallets:") << std::endl;
D
Daniel Larimer 已提交
2113
      const auto& v = call(wallet_url, wallet_list);
K
Kevin Heifner 已提交
2114 2115 2116 2117
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

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

2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142
      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 已提交
2143 2144 2145
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

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

2151
      const auto& v = call(wallet_url, keosd_stop);
A
Anton Perkov 已提交
2152 2153 2154 2155 2156
      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;
      }
2157 2158
   });

2159 2160 2161
   // sign subcommand
   string trx_json_to_sign;
   string str_private_key;
2162
   string str_chain_id;
2163 2164 2165 2166
   bool push_trx = false;

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

   sign->set_callback([&] {
2173
      signed_transaction trx = json_from_file_or_string(trx_json_to_sign).as<signed_transaction>();
2174 2175

      fc::optional<chain_id_type> chain_id;
2176 2177 2178 2179 2180 2181 2182 2183

      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);
      }
2184 2185 2186 2187 2188 2189 2190 2191 2192

      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));
2193
      trx.sign(priv_key, *chain_id);
2194 2195 2196 2197 2198 2199 2200 2201 2202

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

2207
   // push action
W
wqxiang88 已提交
2208
   string contract_account;
N
Nathan Hourt 已提交
2209 2210
   string action;
   string data;
2211
   vector<string> permissions;
2212
   auto actionsSubcommand = push->add_subcommand("action", localized("Push a transaction with a single action"));
D
Daniel Larimer 已提交
2213
   actionsSubcommand->fallthrough(false);
W
wqxiang88 已提交
2214
   actionsSubcommand->add_option("account", contract_account,
2215
                                 localized("The account providing the contract to execute"), true)->required();
2216 2217
   actionsSubcommand->add_option("action", action,
                                 localized("A JSON string or filename defining the action to execute on the contract"), true)->required();
D
Daniel Larimer 已提交
2218
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
2219

D
Daniel Larimer 已提交
2220 2221
   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
2222 2223
      fc::variant action_args_var;
      try {
2224 2225
         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))
2226

N
Nathan Hourt 已提交
2227
      auto arg= fc::mutable_variant_object
W
wqxiang88 已提交
2228
                ("code", contract_account)
N
Nathan Hourt 已提交
2229
                ("action", action)
2230
                ("args", action_args_var);
N
Nathan Hourt 已提交
2231 2232
      auto result = call(json_to_bin_func, arg);

2233
      auto accountPermissions = get_account_permissions(tx_permission);
2234

W
wqxiang88 已提交
2235
      send_actions({chain::action{accountPermissions, contract_account, action, result.get_object()["binargs"].as<bytes>()}});
N
Nathan Hourt 已提交
2236 2237 2238
   });

   // push transaction
2239
   string trx_to_push;
2240
   auto trxSubcommand = push->add_subcommand("transaction", localized("Push an arbitrary JSON transaction"));
2241
   trxSubcommand->add_option("transaction", trx_to_push, localized("The JSON string or filename defining the transaction to push"))->required();
2242

N
Nathan Hourt 已提交
2243
   trxSubcommand->set_callback([&] {
2244 2245
      fc::variant trx_var;
      try {
2246 2247
         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 已提交
2248
      signed_transaction trx = trx_var.as<signed_transaction>();
2249
      auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
N
Nathan Hourt 已提交
2250 2251
      std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
   });
2252

2253 2254

   string trxsJson;
2255
   auto trxsSubcommand = push->add_subcommand("transactions", localized("Push an array of arbitrary JSON transactions"));
2256
   trxsSubcommand->add_option("transactions", trxsJson, localized("The JSON string or filename defining the array of the transactions to push"))->required();
2257
   trxsSubcommand->set_callback([&] {
2258 2259
      fc::variant trx_var;
      try {
2260 2261
         trx_var = json_from_file_or_string(trxsJson);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trxsJson))
2262
      auto trxs_result = call(push_txns_func, trx_var);
2263 2264 2265
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });

D
Daniel Larimer 已提交
2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 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

   // 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 {
2342
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <proposer> or -p)");
D
Daniel Larimer 已提交
2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354
         }
      }
      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;
2355
      trx.max_cpu_usage_ms = 0;
D
Daniel Larimer 已提交
2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 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
      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 {
2475
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <canceler> or -p)");
D
Daniel Larimer 已提交
2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506
         }
      }
      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 {
2507
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <executer> or -p)");
D
Daniel Larimer 已提交
2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531
         }
      }
      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 已提交
2532
   auto createAccountSystem = create_account_subcommand( system, false /*simple*/ );
D
Daniel Larimer 已提交
2533 2534 2535 2536 2537 2538 2539
   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);
2540 2541
   auto approveProducer = approve_producer_subcommand(voteProducer);
   auto unapproveProducer = unapprove_producer_subcommand(voteProducer);
D
Daniel Larimer 已提交
2542

A
Anton Perkov 已提交
2543 2544
   auto listProducers = list_producers_subcommand(system);

D
Daniel Larimer 已提交
2545 2546
   auto delegateBandWidth = delegate_bandwidth_subcommand(system);
   auto undelegateBandWidth = undelegate_bandwidth_subcommand(system);
A
Anton Perkov 已提交
2547
   auto listBandWidth = list_bw_subcommand(system);
D
Daniel Larimer 已提交
2548

2549 2550 2551
   auto biyram = buyram_subcommand(system);
   auto sellram = sellram_subcommand(system);

D
Daniel Larimer 已提交
2552 2553 2554 2555 2556 2557 2558
   auto claimRewards = claimrewards_subcommand(system);

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

   auto cancelDelay = canceldelay_subcommand(system);

2559 2560 2561 2562
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
2563 2564
   } catch (const explained_exception& e) {
      return 1;
D
Daniel Larimer 已提交
2565 2566 2567 2568
   } catch (connection_exception& e) {
      if (verbose_errors) {
         elog("connect error: ${e}", ("e", e.to_detail_string()));
      }
2569
   } catch (const fc::exception& e) {
D
Daniel Larimer 已提交
2570 2571 2572 2573 2574
      // 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()));
2575
         }
2576
      }
2577 2578 2579 2580
      return 1;
   }

   return 0;
2581
}