main.cpp 52.4 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 25 26
  -h,--help                   Print this help message and exit
  -H,--host TEXT=localhost    the host where nodeos is running
  -p,--port UINT=8888         the port where nodeos is running
27
  --wallet-host TEXT=localhost
28 29
                              the host where keosd is running
  --wallet-port UINT=8888     the port where keosd is running
30
  -v,--verbose                output verbose actions on error
31 32

Subcommands:
33
  version                     Retrieve version information
34 35 36 37
  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
38
  net                         Interact with local p2p network connections
39
  wallet                      Interact with local wallet
40
  sign                        Sign a transaction
41 42 43 44 45
  push                        Push arbitrary transactions to the blockchain

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

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

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

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

Options:
65 66 67 68 69
  -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')
70 71
```
*/
72 73 74
#include <string>
#include <vector>
#include <boost/asio.hpp>
P
Pravin 已提交
75
#include <boost/format.hpp>
76 77 78
#include <iostream>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
D
Daniel Larimer 已提交
79
#include <fc/io/console.hpp>
80
#include <fc/exception/exception.hpp>
81
#include <eosio/utilities/key_conversion.hpp>
82

83
#include <eosio/chain/config.hpp>
84
#include <eosio/chain/wast_to_wasm.hpp>
D
Daniel Larimer 已提交
85
#include <eosio/chain/transaction_trace.hpp>
D
Daniel Larimer 已提交
86
#include <eosio/chain_plugin/chain_plugin.hpp>
D
Daniel Larimer 已提交
87

88
#include <boost/range/algorithm/find_if.hpp>
89
#include <boost/range/algorithm/sort.hpp>
90
#include <boost/range/adaptor/transformed.hpp>
91
#include <boost/algorithm/string/predicate.hpp>
92
#include <boost/algorithm/string/split.hpp>
93
#include <boost/range/algorithm/copy.hpp>
D
Daniel Larimer 已提交
94
#include <boost/algorithm/string/classification.hpp>
95

96 97 98 99 100 101 102 103 104
#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>

105
#include "CLI11.hpp"
106
#include "help_text.hpp"
107
#include "localize.hpp"
108
#include "config.hpp"
109
#include "httpc.hpp"
110

111
using namespace std;
P
Pravin 已提交
112 113 114 115
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::utilities;
using namespace eosio::client::help;
116
using namespace eosio::client::http;
P
Pravin 已提交
117 118
using namespace eosio::client::localize;
using namespace eosio::client::config;
119 120 121 122 123 124 125 126 127 128 129 130 131 132
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 \
  )
133

134 135 136 137
string program = "eosc";
string host = "localhost";
uint32_t port = 8888;

K
Kevin Heifner 已提交
138
// restricting use of wallet to localhost
D
Daniel Larimer 已提交
139 140
string wallet_host = "localhost";
uint32_t wallet_port = 8888;
K
Kevin Heifner 已提交
141

142 143 144 145
auto   tx_expiration = fc::seconds(30);
bool   tx_force_unique = false;
bool   tx_dont_broadcast = false;
bool   tx_skip_sign = false;
D
Daniel Larimer 已提交
146
bool   tx_print_json = false;
147

148 149
uint32_t tx_max_cpu_usage = 0;
uint32_t tx_max_net_usage = 0;
150

151
vector<string> tx_permission;
K
Kevin Heifner 已提交
152

M
Matias Romeo 已提交
153
void add_standard_transaction_options(CLI::App* cmd, string default_permission = "") {
154
   CLI::callback_t parse_exipration = [](CLI::results_t res) -> bool {
K
Kevin Heifner 已提交
155 156
      double value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
157 158
         return false;
      }
159

K
Kevin Heifner 已提交
160
      tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
161 162 163
      return true;
   };

K
Kevin Heifner 已提交
164
   cmd->add_option("-x,--expiration", parse_exipration, localized("set the time in seconds before a transaction expires, defaults to 30s"));
165
   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"));
166
   cmd->add_flag("-s,--skip-sign", tx_skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
D
Daniel Larimer 已提交
167
   cmd->add_flag("-j,--json", tx_print_json, localized("print result as json"));
168 169
   cmd->add_flag("-d,--dont-broadcast", tx_dont_broadcast, localized("don't broadcast transaction to the network (just print to stdout)"));

M
Matias Romeo 已提交
170 171 172 173
   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()));
174

175 176
   cmd->add_option("--max-cpu-usage", tx_max_cpu_usage, localized("set an upper limit on the cpu usage budget, in instructions-retired, for the execution of the transaction (defaults to 0 which means no limit)"));
   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)"));
177 178
}

K
Kevin Heifner 已提交
179 180
string generate_nonce_value() {
   return fc::to_string(fc::time_point::now().time_since_epoch().count());
181 182
}

K
Kevin Heifner 已提交
183
chain::action generate_nonce() {
184
   auto v = generate_nonce_value();
K
Kevin Heifner 已提交
185 186 187
   variant nonce = fc::mutable_variant_object()
         ("value", v);
   return chain::action( {}, config::system_account_name, "nonce", fc::raw::pack(nonce));
188
}
189

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

202
template<typename T>
203
fc::variant call( const std::string& server, uint16_t port,
204
                  const std::string& path,
205
                  const T& v ) { return eosio::client::http::call( server, port, path, fc::variant(v) ); }
206

207 208
template<typename T>
fc::variant call( const std::string& path,
209
                  const T& v ) { return eosio::client::http::call( host, port, path, fc::variant(v) ); }
210

P
Pravin 已提交
211 212
eosio::chain_apis::read_only::get_info_results get_info() {
  return call(host, port, get_info_func ).as<eosio::chain_apis::read_only::get_info_results>();
213 214
}

215
fc::variant determine_required_keys(const signed_transaction& trx) {
K
Kevin Heifner 已提交
216 217 218
   // TODO better error checking
   const auto& public_keys = call(wallet_host, wallet_port, wallet_public_keys);
   auto get_arg = fc::mutable_variant_object
219 220
           ("transaction", (transaction)trx)
           ("available_keys", public_keys);
K
Kevin Heifner 已提交
221
   const auto& required_keys = call(host, port, get_required_keys, get_arg);
222 223 224 225
   return required_keys["required_keys"];
}

void sign_transaction(signed_transaction& trx, fc::variant& required_keys) {
K
Kevin Heifner 已提交
226
   // TODO determine chain id
227
   fc::variants sign_args = {fc::variant(trx), required_keys, fc::variant(chain_id_type{})};
K
Kevin Heifner 已提交
228
   const auto& signed_trx = call(wallet_host, wallet_port, wallet_sign_trx, sign_args);
229
   trx = signed_trx.as<signed_transaction>();
K
Kevin Heifner 已提交
230 231
}

232
fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
M
Matias Romeo 已提交
233 234 235
   auto info = get_info();
   trx.expiration = info.head_block_time + tx_expiration;
   trx.set_reference_block(info.head_block_id);
236

M
Matias Romeo 已提交
237
   if (tx_force_unique) {
K
Kevin Heifner 已提交
238
      trx.context_free_actions.emplace_back( generate_nonce() );
M
Matias Romeo 已提交
239
   }
K
Kevin Heifner 已提交
240

241 242 243
   auto required_keys = determine_required_keys(trx);
   size_t num_keys = required_keys.is_array() ? required_keys.get_array().size() : 1;

244 245
   trx.max_kcpu_usage = (tx_max_cpu_usage + 1023)/1024;
   trx.max_net_usage_words = (tx_max_net_usage + 7)/8;
246

M
Matias Romeo 已提交
247
   if (!tx_skip_sign) {
248
      sign_transaction(trx, required_keys);
M
Matias Romeo 已提交
249 250 251 252 253 254 255
   }

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

258
fc::variant push_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none ) {
259 260 261
   signed_transaction trx;
   trx.actions = std::forward<decltype(actions)>(actions);

262
   return push_transaction(trx, extra_kcpu, compression);
263 264
}

D
Daniel Larimer 已提交
265 266 267 268
void print_result( const fc::variant& result ) {
      const auto& processed = result["processed"];
      const auto& transaction_id = processed["id"].as_string();
      const auto& status = processed["status"].as_string() ;
269 270
      auto net = processed["net_usage"].as_int64();
      auto cpu = processed["cpu_usage"].as_int64();
D
Daniel Larimer 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

      cout << status << " transaction: " << transaction_id << "  " << net << " bytes  " << cpu << " cycles\n";

      const auto& actions = processed["action_traces"].get_array();
      for( const auto& at : actions ) {
         auto receiver = at["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";
         }
      }
}

using std::cout;
302
void send_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
D
Daniel Larimer 已提交
303 304 305 306 307 308 309
   auto result = push_actions( move(actions), extra_kcpu, compression);

   if( tx_print_json ) {
      cout << fc::json::to_pretty_string( result );
   } else {
      print_result( result );
   }
310 311
}

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

D
Daniel Larimer 已提交
315 316 317 318 319 320
   if( tx_print_json ) {
      cout << fc::json::to_pretty_string( result );
   } else {
      auto trace = result["processed"].as<eosio::chain::transaction_trace>();
      print_result( result );
   }
321
}
322

M
Matias Romeo 已提交
323 324 325 326
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),
      contracts::newaccount{
327 328 329
         .creator      = creator,
         .name         = newaccount,
         .owner        = eosio::chain::authority{1, {{owner, 1}}, {}},
M
Matias Romeo 已提交
330 331 332 333
         .active       = eosio::chain::authority{1, {{active, 1}}, {}},
         .recovery     = eosio::chain::authority{1, {}, {{{creator, config::active_name}, 1}}}
      }
   };
334
}
335

M
Matias Romeo 已提交
336 337 338 339 340 341 342
chain::action create_transfer(const name& sender, const name& recipient, uint64_t amount, const string& memo ) {

   auto transfer = fc::mutable_variant_object
      ("from", sender)
      ("to", recipient)
      ("quantity", asset(amount))
      ("memo", memo);
343

M
Matias Romeo 已提交
344 345 346 347
   auto args = fc::mutable_variant_object
      ("code", name(config::system_account_name))
      ("action", "transfer")
      ("args", transfer);
348

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

M
Matias Romeo 已提交
351 352 353 354
   return action {
      tx_permission.empty() ? vector<chain::permission_level>{{sender,config::active_name}} : get_account_permissions(tx_permission),
      config::system_account_name, "transfer", result.get_object()["binargs"].as<bytes>()
   };
355
}
D
Daniel Larimer 已提交
356

M
Matias Romeo 已提交
357
chain::action create_setabi(const name& account, const contracts::abi_def& abi) {
358
   return action {
M
Matias Romeo 已提交
359 360 361 362
      tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
      contracts::setabi{
         .account   = account,
         .abi       = abi
363 364
      }
   };
365 366
}

M
Matias Romeo 已提交
367
chain::action create_setcode(const name& account, const bytes& code) {
368
   return action {
M
Matias Romeo 已提交
369 370 371 372 373
      tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
      contracts::setcode{
         .account   = account,
         .vmtype    = 0,
         .vmversion = 0,
374
         .code      = code
375 376 377 378 379 380
      }
   };
}

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 已提交
381
                   contracts::updateauth{account, permission, parent, auth}};
382 383
}

384 385
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 已提交
386
                   contracts::deleteauth{account, permission}};
387 388
}

D
Daniel Larimer 已提交
389
chain::action create_linkauth(const name& account, const name& code, const name& type, const name& requirement) {
390
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
391
                   contracts::linkauth{account, code, type, requirement}};
392 393
}

D
Daniel Larimer 已提交
394
chain::action create_unlinkauth(const name& account, const name& code, const name& type) {
395
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
396
                   contracts::unlinkauth{account, code, type}};
397 398
}

399
struct set_account_permission_subcommand {
400 401 402 403 404
   string accountStr;
   string permissionStr;
   string authorityJsonOrFile;
   string parentStr;

405
   set_account_permission_subcommand(CLI::App* accountCmd) {
406 407 408 409 410
      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();
      permissions->add_option("authority", authorityJsonOrFile, localized("[delete] NULL, [create/update] JSON string or filename defining the authority"))->required();
      permissions->add_option("parent", parentStr, localized("[create] The permission name of this parents permission (Defaults to: \"Active\")"));
411

M
Matias Romeo 已提交
412
      add_standard_transaction_options(permissions, "account@active");
413 414

      permissions->set_callback([this] {
415 416
         name account = name(accountStr);
         name permission = name(permissionStr);
417
         bool is_delete = boost::iequals(authorityJsonOrFile, "null");
418

419
         if (is_delete) {
M
Matias Romeo 已提交
420
            send_actions({create_deleteauth(account, permission)});
421
         } else {
D
Daniel Larimer 已提交
422
            authority auth;
423
            if (boost::istarts_with(authorityJsonOrFile, "EOS")) {
424 425
               try {
                  auth = authority(public_key_type(authorityJsonOrFile));
426
               } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", authorityJsonOrFile))
427
            } else {
428
               fc::variant parsedAuthority;
429 430 431 432 433 434 435
               try {
                  if (boost::istarts_with(authorityJsonOrFile, "{")) {
                     parsedAuthority = fc::json::from_string(authorityJsonOrFile);
                  } else {
                     parsedAuthority = fc::json::from_file(authorityJsonOrFile);
                  }
                  auth = parsedAuthority.as<authority>();
436
               } EOS_RETHROW_EXCEPTIONS(authority_type_exception, "Fail to parse Authority JSON")
437

438 439
            }

440
            name parent;
441
            if (parentStr.size() == 0 && permissionStr != "owner") {
442
               // see if we can auto-determine the proper parent
K
Kevin Heifner 已提交
443
               const auto account_result = call(get_account_func, fc::mutable_variant_object("account_name", accountStr));
444
               const auto& existing_permissions = account_result.get_object()["permissions"].get_array();
445 446
               auto permissionPredicate = [this](const auto& perm) {
                  return perm.is_object() &&
447
                        perm.get_object().contains("permission") &&
448
                        boost::equals(perm.get_object()["permission"].get_string(), permissionStr);
449 450 451 452
               };

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

458 459
               }
            } else {
460
               parent = name(parentStr);
461
            }
462

M
Matias Romeo 已提交
463
            send_actions({create_updateauth(account, permission, parent, auth)});
464
         }
465 466
      });
   }
467

468
};
469

470
struct set_action_permission_subcommand {
471 472 473 474 475
   string accountStr;
   string codeStr;
   string typeStr;
   string requirementStr;

476
   set_action_permission_subcommand(CLI::App* actionRoot) {
477 478 479 480 481
      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();
482

M
Matias Romeo 已提交
483
      add_standard_transaction_options(permissions, "account@active");
484 485

      permissions->set_callback([this] {
486 487 488
         name account = name(accountStr);
         name code = name(codeStr);
         name type = name(typeStr);
489
         bool is_delete = boost::iequals(requirementStr, "null");
490

491
         if (is_delete) {
M
Matias Romeo 已提交
492
            send_actions({create_unlinkauth(account, code, type)});
493
         } else {
494
            name requirement = name(requirementStr);
M
Matias Romeo 已提交
495
            send_actions({create_linkauth(account, code, type, requirement)});
496
         }
497 498 499
      });
   }
};
500

501
int main( int argc, char** argv ) {
D
Daniel Larimer 已提交
502
   fc::path binPath = argv[0];
503
   if (binPath.is_relative()) {
504
      binPath = relative(binPath, current_path());
505 506 507 508 509 510
   }

   setlocale(LC_ALL, "");
   bindtextdomain(locale_domain, locale_path);
   textdomain(locale_domain);

511
   CLI::App app{"Command Line Interface to EOSIO Client"};
512
   app.require_subcommand();
513 514
   app.add_option( "-H,--host", host, localized("the host where nodeos is running"), true );
   app.add_option( "-p,--port", port, localized("the port where nodeos is running"), true );
515 516
   app.add_option( "--wallet-host", wallet_host, localized("the host where keosd is running"), true );
   app.add_option( "--wallet-port", wallet_port, localized("the port where keosd is running"), true );
517

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

521 522 523 524 525 526 527
   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;
   });

528
   // Create subcommand
529
   auto create = app.add_subcommand("create", localized("Create various items, on and off the blockchain"), false);
N
Nathan Hourt 已提交
530 531 532
   create->require_subcommand();

   // create key
D
Daniel Larimer 已提交
533 534 535 536 537 538
   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 已提交
539 540 541 542
   });

   // create account
   string creator;
543
   string account_name;
544 545
   string owner_key_str;
   string active_key_str;
M
Matias Romeo 已提交
546

547 548
   auto createAccount = create->add_subcommand("account", localized("Create a new account on the blockchain"), false);
   createAccount->add_option("creator", creator, localized("The name of the account creating the new account"))->required();
549
   createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
550 551
   createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key for the new account"))->required();
   createAccount->add_option("ActiveKey", active_key_str, localized("The active public key for the new account"))->required();
M
Matias Romeo 已提交
552
   add_standard_transaction_options(createAccount, "creator@active");
N
Nathan Hourt 已提交
553
   createAccount->set_callback([&] {
554 555 556
      public_key_type owner_key, active_key;
      try {
         owner_key = public_key_type(owner_key_str);
557
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str))
558
      try {
559
         active_key = public_key_type(active_key_str);
560
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str))
M
Matias Romeo 已提交
561
      send_actions({create_newaccount(creator, account_name, owner_key, active_key)});
562 563
   });

564
   // Get subcommand
565
   auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"), false);
N
Nathan Hourt 已提交
566 567 568
   get->require_subcommand();

   // get info
569
   get->add_subcommand("info", localized("Get current blockchain information"))->set_callback([] {
N
Nathan Hourt 已提交
570 571 572 573 574
      std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
   });

   // get block
   string blockArg;
575 576
   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 已提交
577 578 579 580 581 582 583
   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;
584 585
   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();
N
Nathan Hourt 已提交
586 587
   getAccount->set_callback([&] {
      std::cout << fc::json::to_pretty_string(call(get_account_func,
K
Kevin Heifner 已提交
588
                                                   fc::mutable_variant_object("account_name", accountName)))
N
Nathan Hourt 已提交
589 590 591
                << std::endl;
   });

D
Daniel Larimer 已提交
592 593 594
   // get code
   string codeFilename;
   string abiFilename;
595 596 597 598
   auto getCode = get->add_subcommand("code", localized("Retrieve the code and ABI for an account"), false);
   getCode->add_option("name", accountName, localized("The name of the account whose code should be retrieved"))->required();
   getCode->add_option("-c,--code",codeFilename, localized("The name of the file to save the contract .wast to") );
   getCode->add_option("-a,--abi",abiFilename, localized("The name of the file to save the contract .abi to") );
D
Daniel Larimer 已提交
599
   getCode->set_callback([&] {
K
Kevin Heifner 已提交
600
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", accountName));
D
Daniel Larimer 已提交
601

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

      if( codeFilename.size() ){
605
         std::cout << localized("saving wast to ${codeFilename}", ("codeFilename", codeFilename)) << std::endl;
D
Daniel Larimer 已提交
606 607 608 609 610
         auto code = result["wast"].as_string();
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
611
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
612 613 614 615 616 617
         auto abi  = fc::json::to_pretty_string( result["abi"] );
         std::ofstream abiout( abiFilename.c_str() );
         abiout << abi;
      }
   });

618
   // get table
D
Daniel Larimer 已提交
619 620 621 622 623
   string scope;
   string code;
   string table;
   string lower;
   string upper;
624
   string table_key;
D
Daniel Larimer 已提交
625 626
   bool binary = false;
   uint32_t limit = 10;
627
   auto getTable = get->add_subcommand( "table", localized("Retrieve the contents of a database table"), false);
628 629
   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();
630 631 632
   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") );
633
   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") );
634 635
   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 已提交
636 637 638 639

   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("code",code)
640
                         ("scope",scope)
D
Daniel Larimer 已提交
641
                         ("table",table)
642 643 644 645
                         ("table_key",table_key)
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
D
Daniel Larimer 已提交
646 647 648 649 650 651
                         );

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

652 653 654 655
   // currency accessors
   // get currency balance
   string symbol;
   auto get_currency = get->add_subcommand( "currency", localized("Retrieve information related to standard currencies"), true);
656
   get_currency->require_subcommand();
657 658 659 660 661
   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([&] {
662 663
      auto result = call(get_currency_balance_func, fc::mutable_variant_object("json", false)
         ("account", accountName)
664
         ("code", code)
D
Daniel Larimer 已提交
665
         ("symbol", symbol=="*" ? fc::variant(symbol) : fc::variant() )
666 667
      );

668
      const auto& rows = result.get_array();
669
      if (symbol.empty()) {
D
Daniel Larimer 已提交
670 671 672 673 674
         for( const auto& r : rows ) {
            std::cout << r.as_string()
                      << std::endl;
         }
         /*
675 676
         std::cout << fc::json::to_pretty_string(rows)
                   << std::endl;
D
Daniel Larimer 已提交
677
                   */
678
      } else if ( rows.size() > 0 ){
D
Daniel Larimer 已提交
679
         std::cout << rows[0].as_string()
680 681 682
                   << std::endl;
      }
   });
D
Daniel Larimer 已提交
683

684 685 686 687
   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();
   get_currency_stats->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") );
   get_currency_stats->set_callback([&] {
688
      auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
689
         ("code", code)
690
         ("symbol", symbol)
691 692 693
      );

      if (symbol.empty()) {
694
         std::cout << fc::json::to_pretty_string(result)
695 696
                   << std::endl;
      } else {
697 698 699
         const auto& mapping = result.get_object();
         std::cout << fc::json::to_pretty_string(mapping[symbol])
                   << std::endl;
700 701
      }
   });
D
Daniel Larimer 已提交
702

703
   // get accounts
704
   string public_key_str;
705
   auto getAccounts = get->add_subcommand("accounts", localized("Retrieve accounts associated with a public key"), false);
706
   getAccounts->add_option("public_key", public_key_str, localized("The public key to retrieve accounts for"))->required();
707
   getAccounts->set_callback([&] {
708 709 710
      public_key_type public_key;
      try {
         public_key = public_key_type(public_key_str);
711
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", public_key_str))
712
      auto arg = fc::mutable_variant_object( "public_key", public_key);
713 714 715
      std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
   });

716

717 718
   // get servants
   string controllingAccount;
719 720
   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();
721
   getServants->set_callback([&] {
722
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
723 724 725
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

N
Nathan Hourt 已提交
726
   // get transaction
727
   string transaction_id_str;
728
   auto getTransaction = get->add_subcommand("transaction", localized("Retrieve a transaction from the blockchain"), false);
729
   getTransaction->add_option("id", transaction_id_str, localized("ID of the transaction to retrieve"))->required();
N
Nathan Hourt 已提交
730
   getTransaction->set_callback([&] {
731 732 733
      transaction_id_type transaction_id;
      try {
         transaction_id = transaction_id_type(transaction_id_str);
734
      } EOS_RETHROW_EXCEPTIONS(transaction_id_type_exception, "Invalid transaction ID: ${transaction_id}", ("transaction_id", transaction_id_str))
735
      auto arg= fc::mutable_variant_object( "transaction_id", transaction_id);
N
Nathan Hourt 已提交
736 737
      std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
   });
738

739
   // get transactions
740 741
   string skip_seq_str;
   string num_seq_str;
D
Daniel Larimer 已提交
742
   bool printjson = false;
743
   auto getTransactions = get->add_subcommand("transactions", localized("Retrieve all transactions with specific account name referenced in their scope"), false);
744
   getTransactions->add_option("account_name", account_name, localized("name of account to query on"))->required();
745 746
   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 已提交
747
   getTransactions->add_flag("--json,-j", printjson, localized("print full json"));
748
   getTransactions->set_callback([&] {
749 750 751 752 753 754 755
      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);
756
         } EOS_RETHROW_EXCEPTIONS(chain_type_exception, "Invalid Skip Seq: ${skip_seq}", ("skip_seq", skip_seq_str))
757 758 759 760 761 762
         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);
763
            } EOS_RETHROW_EXCEPTIONS(chain_type_exception, "Invalid Num Seq: ${num_seq}", ("num_seq", num_seq_str))
764 765 766
            arg = fc::mutable_variant_object( "account_name", account_name)("skip_seq", skip_seq_str)("num_seq", num_seq);
         }
      }
D
Daniel Larimer 已提交
767
      auto result = call(get_transactions_func, arg);
D
Daniel Larimer 已提交
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
      if( printjson ) {
         std::cout << fc::json::to_pretty_string(call(get_transactions_func, arg)) << std::endl;
      }
      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();
            const auto& data = trx["data"].get_object();
            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 已提交
792 793
      }

794 795
   });

796
   // set subcommand
797
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
798 799 800
   setSubcommand->require_subcommand();

   // set contract subcommand
N
Nathan Hourt 已提交
801
   string account;
802
   string contractPath;
N
Nathan Hourt 已提交
803 804
   string wastPath;
   string abiPath;
805
   auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
806 807 808 809 810 811 812 813 814
   contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))
                     ->required();
   contractSubcommand->add_option("contract-dir", contractPath, localized("The the path containing the .wast and .abi"))
                     ->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);

815

M
Matias Romeo 已提交
816
   add_standard_transaction_options(contractSubcommand, "account@active");
N
Nathan Hourt 已提交
817 818
   contractSubcommand->set_callback([&] {
      std::string wast;
819
      std::cout << localized("Reading WAST...") << std::endl;
820 821 822
      fc::path cpath(contractPath);
      if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();

823
      if( wastPath == string() )
824 825 826 827
      {
         wastPath = (cpath / (cpath.filename().generic_string()+".wast")).generic_string();
      }

828
      if( abiPath == string() )
829 830 831 832
      {
         abiPath = (cpath / (cpath.filename().generic_string()+".abi")).generic_string();
      }

N
Nathan Hourt 已提交
833
      fc::read_file_contents(wastPath, wast);
834 835

      vector<uint8_t> wasm;
836
      const string binary_wasm_header("\x00\x61\x73\x6d", 4);
837
      if(wast.compare(0, 4, binary_wasm_header) == 0) {
838
         std::cout << localized("Using already assembled WASM...") << std::endl;
839
         wasm = vector<uint8_t>(wast.begin(), wast.end());
840
      }
841 842
      else {
         std::cout << localized("Assembling WASM...") << std::endl;
843
         wasm = wast_to_wasm(wast);
844
      }
N
Nathan Hourt 已提交
845

M
Matias Romeo 已提交
846 847
      std::vector<chain::action> actions;
      actions.emplace_back( create_setcode(account, bytes(wasm.begin(), wasm.end()) ) );
848

D
Daniel Larimer 已提交
849 850 851 852 853
      FC_ASSERT( fc::exists( abiPath ), "no abi file found ${f}", ("f", abiPath)  );

      try {
         actions.emplace_back( create_setabi(account, fc::json::from_file(abiPath).as<contracts::abi_def>()) );
      } EOS_RETHROW_EXCEPTIONS(abi_type_exception,  "Fail to parse ABI JSON")
K
Kevin Heifner 已提交
854

855
      std::cout << localized("Publishing contract...") << std::endl;
D
Daniel Larimer 已提交
856 857
      send_actions(std::move(actions), 10000, packed_transaction::zlib);
      /*
858 859 860 861 862
      auto result = push_actions(std::move(actions), 10000, packed_transaction::zlib);

      if( tx_dont_broadcast ) {
         std::cout << fc::json::to_pretty_string(result) << "\n";
      }
D
Daniel Larimer 已提交
863
      */
N
Nathan Hourt 已提交
864
   });
N
Nathan Hourt 已提交
865

866
   // set account
867
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
868 869

   // set account permission
870
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
871 872

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

875
   // set action permission
876
   auto setActionPermission = set_action_permission_subcommand(setAction);
877

N
Nathan Hourt 已提交
878
   // Transfer subcommand
N
Nathan Hourt 已提交
879 880 881
   string sender;
   string recipient;
   uint64_t amount;
882
   string memo;
883 884 885 886 887
   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"));
K
Kevin Heifner 已提交
888

M
Matias Romeo 已提交
889
   add_standard_transaction_options(transfer, "sender@active");
N
Nathan Hourt 已提交
890
   transfer->set_callback([&] {
891 892 893 894 895
      signed_transaction trx;
      if (tx_force_unique && memo.size() == 0) {
         // use the memo to add a nonce
         memo = generate_nonce_value();
         tx_force_unique = false;
896
      }
897

M
Matias Romeo 已提交
898
      send_actions({create_transfer(sender, recipient, amount, memo)});
N
Nathan Hourt 已提交
899
   });
N
Nathan Hourt 已提交
900

901
   // Net subcommand
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
   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([&] {
      const auto& v = call(host, port, net_connect, new_host);
      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([&] {
      const auto& v = call(host, port, net_disconnect, new_host);
      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([&] {
      const auto& v = call(host, port, net_status, new_host);
      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([&] {
      const auto& v = call(host, port, net_connections, new_host);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });


K
Kevin Heifner 已提交
933 934

   // Wallet subcommand
935
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
936
   wallet->require_subcommand();
K
Kevin Heifner 已提交
937
   // create wallet
D
Daniel Larimer 已提交
938
   string wallet_name = "default";
939 940
   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 已提交
941 942
   createWallet->set_callback([&wallet_name] {
      const auto& v = call(wallet_host, wallet_port, wallet_create, wallet_name);
943 944 945
      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 已提交
946 947 948 949
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
950 951
   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 已提交
952
   openWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
953 954
      /*const auto& v = */call(wallet_host, wallet_port, wallet_open, wallet_name);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
955
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
956 957 958
   });

   // lock wallet
959 960
   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 已提交
961
   lockWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
962
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock, wallet_name);
963
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
D
Daniel Larimer 已提交
964 965
      //std::cout << fc::json::to_pretty_string(v) << std::endl;

K
Kevin Heifner 已提交
966 967 968
   });

   // lock all wallets
969
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
K
Kevin Heifner 已提交
970
   locakAllWallets->set_callback([] {
D
Daniel Larimer 已提交
971 972
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock_all);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
973
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
974 975 976 977
   });

   // unlock wallet
   string wallet_pw;
978 979 980
   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"));
K
Kevin Heifner 已提交
981
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
982
      if( wallet_pw.size() == 0 ) {
983
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
984 985 986 987 988 989
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }


K
Kevin Heifner 已提交
990
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
991
      /*const auto& v = */call(wallet_host, wallet_port, wallet_unlock, vs);
992
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
993
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
994 995 996
   });

   // import keys into wallet
997
   string wallet_key_str;
998 999
   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"));
1000 1001 1002 1003 1004 1005 1006 1007 1008
   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 已提交
1009

K
Kevin Heifner 已提交
1010 1011
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
      const auto& v = call(wallet_host, wallet_port, wallet_import_key, vs);
1012
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
D
Daniel Larimer 已提交
1013
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
1014 1015 1016
   });

   // list wallets
1017
   auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
K
Kevin Heifner 已提交
1018
   listWallet->set_callback([] {
1019
      std::cout << localized("Wallets:") << std::endl;
K
Kevin Heifner 已提交
1020 1021 1022 1023 1024
      const auto& v = call(wallet_host, wallet_port, wallet_list);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // list keys
1025
   auto listKeys = wallet->add_subcommand("keys", localized("List of private keys from all unlocked wallets in wif format."), false);
K
Kevin Heifner 已提交
1026 1027 1028 1029 1030
   listKeys->set_callback([] {
      const auto& v = call(wallet_host, wallet_port, wallet_list_keys);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
   // sign subcommand
   string trx_json_to_sign;
   string str_private_key;
   bool push_trx = false;

   auto sign = app.add_subcommand("sign", localized("Sign a transaction"), false);
   sign->add_option("transaction", trx_json_to_sign,
                                 localized("The JSON of the transaction to sign, or the name of a JSON file containing the transaction"), true)->required();
   sign->add_option("-k,--private-key", str_private_key, localized("The private key that will be used to sign the transaction"));
   sign->add_flag( "-p,--push-transaction", push_trx, localized("Push transaction after signing"));

   sign->set_callback([&] {
      signed_transaction trx;
      if ( is_regular_file(trx_json_to_sign) ) {
         trx = fc::json::from_file(trx_json_to_sign).as<signed_transaction>();
      } else {
         trx = fc::json::from_string(trx_json_to_sign).as<signed_transaction>();
      }

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

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

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

N
Nathan Hourt 已提交
1068
   // Push subcommand
1069
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
N
Nathan Hourt 已提交
1070 1071
   push->require_subcommand();

1072
   // push action
N
Nathan Hourt 已提交
1073 1074 1075
   string contract;
   string action;
   string data;
1076
   vector<string> permissions;
1077
   auto actionsSubcommand = push->add_subcommand("action", localized("Push a transaction with a single action"));
D
Daniel Larimer 已提交
1078 1079
   actionsSubcommand->fallthrough(false);
   actionsSubcommand->add_option("contract", contract,
1080
                                 localized("The account providing the contract to execute"), true)->required();
D
Daniel Larimer 已提交
1081
   actionsSubcommand->add_option("action", action, localized("The action to execute on the contract"), true)
N
Nathan Hourt 已提交
1082
         ->required();
D
Daniel Larimer 已提交
1083
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
1084

D
Daniel Larimer 已提交
1085 1086
   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
1087 1088
      fc::variant action_args_var;
      try {
D
Daniel Larimer 已提交
1089
         action_args_var = fc::json::from_string(data, fc::json::relaxed_parser);
1090
      } EOS_RETHROW_EXCEPTIONS(action_type_exception, "Fail to parse action JSON")
1091

N
Nathan Hourt 已提交
1092 1093 1094
      auto arg= fc::mutable_variant_object
                ("code", contract)
                ("action", action)
1095
                ("args", action_args_var);
N
Nathan Hourt 已提交
1096 1097
      auto result = call(json_to_bin_func, arg);

1098
      auto accountPermissions = get_account_permissions(tx_permission);
1099

M
Matias Romeo 已提交
1100
      send_actions({chain::action{accountPermissions, contract, action, result.get_object()["binargs"].as<bytes>()}});
N
Nathan Hourt 已提交
1101 1102 1103
   });

   // push transaction
1104
   string trx_to_push;
1105
   auto trxSubcommand = push->add_subcommand("transaction", localized("Push an arbitrary JSON transaction"));
1106 1107
   trxSubcommand->add_option("transaction", trx_to_push, localized("The JSON of the transaction to push, or the name of a JSON file containing the transaction"))->required();

N
Nathan Hourt 已提交
1108
   trxSubcommand->set_callback([&] {
1109 1110
      fc::variant trx_var;
      try {
M
Matias Romeo 已提交
1111 1112 1113 1114 1115
         if ( is_regular_file(trx_to_push) ) {
            trx_var = fc::json::from_file(trx_to_push);
         } else {
            trx_var = fc::json::from_string(trx_to_push);
         }
1116
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON")
A
Andrianto Lie 已提交
1117
      signed_transaction trx = trx_var.as<signed_transaction>();
1118
      auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
N
Nathan Hourt 已提交
1119 1120
      std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
   });
1121

1122 1123

   string trxsJson;
1124 1125
   auto trxsSubcommand = push->add_subcommand("transactions", localized("Push an array of arbitrary JSON transactions"));
   trxsSubcommand->add_option("transactions", trxsJson, localized("The JSON array of the transactions to push"))->required();
1126
   trxsSubcommand->set_callback([&] {
1127 1128
      fc::variant trx_var;
      try {
1129
         trx_var = fc::json::from_string(trxsJson);
1130
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON")
1131
      auto trxs_result = call(push_txns_func, trx_var);
1132 1133 1134
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });

1135 1136 1137 1138
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
1139 1140
   } catch (const explained_exception& e) {
      return 1;
1141
   } catch (const fc::exception& e) {
N
Nathan Hourt 已提交
1142
      auto errorString = e.to_detail_string();
1143 1144
      if (errorString.find("Connection refused") != string::npos) {
         if (errorString.find(fc::json::to_string(port)) != string::npos) {
1145
            std::cerr << localized("Failed to connect to nodeos at ${ip}:${port}; is nodeos running?", ("ip", host)("port", port)) << std::endl;
1146
         } else if (errorString.find(fc::json::to_string(wallet_port)) != string::npos) {
1147
            std::cerr << localized("Failed to connect to keosd at ${ip}:${port}; is keosd running?", ("ip", wallet_host)("port", wallet_port)) << std::endl;
1148
         } else {
1149
            std::cerr << localized("Failed to connect") << std::endl;
1150 1151 1152 1153
         }

         if (verbose_errors) {
            elog("connect error: ${e}", ("e", errorString));
1154 1155
         }
      } else {
1156
         // attempt to extract the error code if one is present
1157 1158 1159 1160 1161
         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()));
            }
1162
         }
1163
      }
1164 1165 1166 1167
      return 1;
   }

   return 0;
1168
}