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

/**
  @defgroup eosclienttool

  @section intro Introduction to EOSC

  `eosc` is a command line tool that interfaces with the REST api exposed by @ref eosd. In order to use `eosc` you will need to
P
Pravin 已提交
15
  have a local copy of `eosd` running and configured to load the 'eosio::chain_api_plugin'.
16 17 18 19 20 21 22 23

   eosc contains documentation for all of its commands. For a list of all commands known to eosc, simply run it with no arguments:
```
$ ./eosc
Command Line Interface to Eos Daemon
Usage: ./eosc [OPTIONS] SUBCOMMAND

Options:
D
Daniel Larimer 已提交
24
  -h,--help                   Print this help actions and exit
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  -H,--host TEXT=localhost    the host where eosd is running
  -p,--port UINT=8888         the port where eosd is running
  --wallet-host TEXT=localhost
                              the host where eos-walletd is running
  --wallet-port UINT=8888     the port where eos-walletd is running

Subcommands:
  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
  wallet                      Interact with local wallet
  benchmark                   Configure and execute benchmarks
  push                        Push arbitrary transactions to the blockchain

```
To get help with any particular subcommand, run it with no arguments as well:
```
$ ./eosc create
Create various items, on and off the blockchain
Usage: ./eosc create SUBCOMMAND

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

$ ./eosc create account
Create a new account on the blockchain
Usage: ./eosc create account [OPTIONS] creator name OwnerKey ActiveKey

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

Options:
  -s,--skip-signature         Specify that unlocked wallet keys should not be used to sign transaction
```
*/
65 66 67 68 69 70
#include <string>
#include <vector>
#include <boost/asio.hpp>
#include <iostream>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
D
Daniel Larimer 已提交
71
#include <fc/io/console.hpp>
72
#include <fc/exception/exception.hpp>
D
Daniel Larimer 已提交
73
#include <eos/utilities/key_conversion.hpp>
74

75
#include <eosio/chain/config.hpp>
D
Daniel Larimer 已提交
76
#include <eosio/chain_plugin/chain_plugin.hpp>
77
#include <boost/range/algorithm/find_if.hpp>
78
#include <boost/range/algorithm/sort.hpp>
79
#include <boost/range/adaptor/transformed.hpp>
80
#include <boost/algorithm/string/predicate.hpp>
81
#include <boost/algorithm/string/split.hpp>
82
#include <boost/range/algorithm/copy.hpp>
D
Daniel Larimer 已提交
83
#include <boost/algorithm/string/classification.hpp>
84

85 86 87 88 89 90 91 92 93
#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>

94
#include "CLI11.hpp"
95
#include "help_text.hpp"
96
#include "localize.hpp"
97
#include "config.hpp"
98

99
using namespace std;
P
Pravin 已提交
100 101 102 103 104 105
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::utilities;
using namespace eosio::client::help;
using namespace eosio::client::localize;
using namespace eosio::client::config;
106 107 108 109 110 111 112 113 114 115 116 117 118 119
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 \
  )
120

121 122 123 124
string program = "eosc";
string host = "localhost";
uint32_t port = 8888;

K
Kevin Heifner 已提交
125
// restricting use of wallet to localhost
D
Daniel Larimer 已提交
126 127
string wallet_host = "localhost";
uint32_t wallet_port = 8888;
K
Kevin Heifner 已提交
128

B
Brian Johnson 已提交
129 130 131
const string chain_func_base = "/v1/chain";
const string get_info_func = chain_func_base + "/get_info";
const string push_txn_func = chain_func_base + "/push_transaction";
132
const string push_txns_func = chain_func_base + "/push_transactions";
B
Brian Johnson 已提交
133 134 135
const string json_to_bin_func = chain_func_base + "/abi_json_to_bin";
const string get_block_func = chain_func_base + "/get_block";
const string get_account_func = chain_func_base + "/get_account";
D
Daniel Larimer 已提交
136 137
const string get_table_func = chain_func_base + "/get_table_rows";
const string get_code_func = chain_func_base + "/get_code";
K
Kevin Heifner 已提交
138
const string get_required_keys = chain_func_base + "/get_required_keys";
B
Brian Johnson 已提交
139 140 141

const string account_history_func_base = "/v1/account_history";
const string get_transaction_func = account_history_func_base + "/get_transaction";
142
const string get_transactions_func = account_history_func_base + "/get_transactions";
143
const string get_key_accounts_func = account_history_func_base + "/get_key_accounts";
144
const string get_controlled_accounts_func = account_history_func_base + "/get_controlled_accounts";
145

146 147 148 149 150 151 152
const string net_func_base = "/v1/net";
const string net_connect = net_func_base + "/connect";
const string net_disconnect = net_func_base + "/disconnect";
const string net_status = net_func_base + "/status";
const string net_connections = net_func_base + "/connections";


K
Kevin Heifner 已提交
153 154 155 156 157 158 159 160 161 162 163 164
const string wallet_func_base = "/v1/wallet";
const string wallet_create = wallet_func_base + "/create";
const string wallet_open = wallet_func_base + "/open";
const string wallet_list = wallet_func_base + "/list_wallets";
const string wallet_list_keys = wallet_func_base + "/list_keys";
const string wallet_public_keys = wallet_func_base + "/get_public_keys";
const string wallet_lock = wallet_func_base + "/lock";
const string wallet_lock_all = wallet_func_base + "/lock_all";
const string wallet_unlock = wallet_func_base + "/unlock";
const string wallet_import_key = wallet_func_base + "/import_key";
const string wallet_sign_trx = wallet_func_base + "/sign_transaction";

165 166
inline std::vector<name> sort_names( const std::vector<name>& names ) {
   auto results = std::vector<name>(names);
167 168 169 170 171 172
   std::sort( results.begin(), results.end() );
   auto itr = std::unique( results.begin(), results.end() );
   results.erase( itr, results.end() );
   return results;
}

173 174 175 176 177 178 179
vector<uint8_t> assemble_wast( const std::string& wast ) {
   IR::Module module;
   std::vector<WAST::Error> parseErrors;
   WAST::parseModule(wast.c_str(),wast.size(),module,parseErrors);
   if(parseErrors.size())
   {
      // Print any parse errors;
180
      std::cerr << localized("Error parsing WebAssembly text file:") << std::endl;
181 182 183 184 185 186
      for(auto& error : parseErrors)
      {
         std::cerr << ":" << error.locus.describe() << ": " << error.message.c_str() << std::endl;
         std::cerr << error.locus.sourceLine << std::endl;
         std::cerr << std::setw(error.locus.column(8)) << "^" << std::endl;
      }
187
      FC_THROW_EXCEPTION( explained_exception, "wast parse error" );
188 189 190 191 192 193 194 195 196 197 198
   }

   try
   {
      // Serialize the WebAssembly module.
      Serialization::ArrayOutputStream stream;
      WASM::serialize(stream,module);
      return stream.getBytes();
   }
   catch(Serialization::FatalSerializationException exception)
   {
199
      std::cerr << localized("Error serializing WebAssembly binary file:") << std::endl;
200
      std::cerr << exception.message << std::endl;
201
      FC_THROW_EXCEPTION( explained_exception, "wasm serialize error");
202 203 204
   }
}

K
Kevin Heifner 已提交
205
auto tx_expiration = fc::seconds(30);
206 207 208
bool tx_force_unique = false;
void add_standard_transaction_options(CLI::App* cmd) {
   CLI::callback_t parse_exipration = [](CLI::results_t res) -> bool {
K
Kevin Heifner 已提交
209 210
      double value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
211 212 213
         return false;
      }
      
K
Kevin Heifner 已提交
214
      tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
215 216 217
      return true;
   };

K
Kevin Heifner 已提交
218
   cmd->add_option("-x,--expiration", parse_exipration, localized("set the time in seconds before a transaction expires, defaults to 30s"));
219
   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"));
220 221
}

D
Daniel Larimer 已提交
222 223
uint64_t generate_nonce_value() {
   return fc::time_point::now().time_since_epoch().count();
224 225
}

D
Daniel Larimer 已提交
226 227
chain::action generate_nonce() {
   return chain::action( {}, contracts::nonce{.value = generate_nonce_value()} ); 
228
}
229

D
Daniel Larimer 已提交
230
vector<chain::permission_level> get_account_permissions(const vector<string>& permissions) {
231 232 233
   auto fixedPermissions = permissions | boost::adaptors::transformed([](const string& p) {
      vector<string> pieces;
      split(pieces, p, boost::algorithm::is_any_of("@"));
234
      EOSC_ASSERT(pieces.size() == 2, "Invalid permission: ${p}", ("p", p));
D
Daniel Larimer 已提交
235
      return chain::permission_level{ .actor = pieces[0], .permission = pieces[1] };
236
   });
D
Daniel Larimer 已提交
237
   vector<chain::permission_level> accountPermissions;
238 239 240
   boost::range::copy(fixedPermissions, back_inserter(accountPermissions));
   return accountPermissions;
}
241

242
fc::variant call( const std::string& server, uint16_t port,
243 244 245
                  const std::string& path,
                  const fc::variant& postdata = fc::variant() );

246

247
template<typename T>
248
fc::variant call( const std::string& server, uint16_t port,
249 250 251
                  const std::string& path,
                  const T& v ) { return call( server, port, path, fc::variant(v) ); }

252 253 254 255
template<typename T>
fc::variant call( const std::string& path,
                  const T& v ) { return call( host, port, path, fc::variant(v) ); }

P
Pravin 已提交
256 257
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>();
258 259
}

260
void sign_transaction(signed_transaction& trx) {
K
Kevin Heifner 已提交
261 262 263 264 265 266 267 268 269
   // TODO better error checking
   const auto& public_keys = call(wallet_host, wallet_port, wallet_public_keys);
   auto get_arg = fc::mutable_variant_object
         ("transaction", trx)
         ("available_keys", public_keys);
   const auto& required_keys = call(host, port, get_required_keys, get_arg);
   // TODO determine chain id
   fc::variants sign_args = {fc::variant(trx), required_keys["required_keys"], fc::variant(chain_id_type{})};
   const auto& signed_trx = call(wallet_host, wallet_port, wallet_sign_trx, sign_args);
270
   trx = signed_trx.as<signed_transaction>();
K
Kevin Heifner 已提交
271 272
}

273
fc::variant push_transaction( signed_transaction& trx, bool sign ) {
274
    auto info = get_info();
275
    trx.expiration = info.head_block_time + tx_expiration;
D
Daniel Larimer 已提交
276 277 278
    trx.set_reference_block(info.head_block_id);
    boost::sort( trx.write_scope );
    boost::sort( trx.read_scope );
279

K
Kevin Heifner 已提交
280 281 282 283
    if (sign) {
       sign_transaction(trx);
    }

284
    return call( push_txn_func, trx );
285 286 287
}


288 289 290 291
void create_account(name creator, name newaccount, public_key_type owner, public_key_type active, bool sign) {
      auto owner_auth   = eosio::chain::authority{1, {{owner, 1}}, {}};
      auto active_auth  = eosio::chain::authority{1, {{active, 1}}, {}};
      auto recovery_auth = eosio::chain::authority{1, {}, {{{creator, "active"}, 1}}};
292 293 294

      uint64_t deposit = 1;

295
      signed_transaction trx;
D
Daniel Larimer 已提交
296 297 298 299
      trx.write_scope = sort_names({creator,config::system_account_name});
      trx.actions.emplace_back( vector<chain::permission_level>{{creator,"active"}}, 
                                contracts::newaccount{creator, newaccount, owner_auth, active_auth, recovery_auth, deposit});

K
Kevin Heifner 已提交
300
      std::cout << fc::json::to_pretty_string(push_transaction(trx, sign)) << std::endl;
301 302
}

D
Daniel Larimer 已提交
303 304 305
chain::action create_updateauth(const name& account, const name& permission, const name& parent, const authority& auth, const name& permissionAuth) {
   return action { vector<chain::permission_level>{{account,permissionAuth}},
                   contracts::updateauth{account, permission, parent, auth}};
306 307
}

D
Daniel Larimer 已提交
308 309 310
chain::action create_deleteauth(const name& account, const name& permission, const name& permissionAuth) {
   return action { vector<chain::permission_level>{{account,permissionAuth}},
                   contracts::deleteauth{account, permission}};
311 312
}

D
Daniel Larimer 已提交
313 314 315
chain::action create_linkauth(const name& account, const name& code, const name& type, const name& requirement) {
   return action { vector<chain::permission_level>{{account,"active"}},
                   contracts::linkauth{account, code, type, requirement}};
316 317
}

D
Daniel Larimer 已提交
318 319 320
chain::action create_unlinkauth(const name& account, const name& code, const name& type) {
   return action { vector<chain::permission_level>{{account,"active"}},
                   contracts::unlinkauth{account, code, type}};
321 322
}

D
Daniel Larimer 已提交
323
void send_transaction(const std::vector<chain::action>& actions, const std::vector<name>& scopes, bool skip_sign = false) {
324
   signed_transaction trx;
D
Daniel Larimer 已提交
325 326 327
   trx.write_scope = sort_names(scopes);
   for (const auto& m: actions) {
      trx.actions.emplace_back( m );
328 329 330
   }

   auto info = get_info();
331
   trx.expiration = info.head_block_time + tx_expiration;
D
Daniel Larimer 已提交
332
   trx.set_reference_block(info.head_block_id);
333 334 335 336 337 338 339
   if (!skip_sign) {
      sign_transaction(trx);
   }

   std::cout << fc::json::to_pretty_string( call( push_txn_func, trx )) << std::endl;
}

340
struct set_account_permission_subcommand {
341 342 343 344
   string accountStr;
   string permissionStr;
   string authorityJsonOrFile;
   string parentStr;
M
Matias Romeo 已提交
345
   string permissionAuth = "active";
346 347
   bool skip_sign;

348
   set_account_permission_subcommand(CLI::App* accountCmd) {
349
      auto permissions = accountCmd->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
M
Matias Romeo 已提交
350
      permissions->add_option("-p,--permission", permissionAuth,localized("Permission level to authorize, (Defaults to: 'active'"));
351 352 353 354 355
      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\")"));
      permissions->add_flag("-s,--skip-sign", skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
K
Kevin Heifner 已提交
356
      add_standard_transaction_options(permissions);
357 358

      permissions->set_callback([this] {
359 360
         name account = name(accountStr);
         name permission = name(permissionStr);
361 362 363
         bool is_delete = boost::iequals(authorityJsonOrFile, "null");
         
         if (is_delete) {
D
Daniel Larimer 已提交
364
            send_transaction({create_deleteauth(account, permission, name(permissionAuth))}, {account, config::system_account_name}, skip_sign);
365
         } else {
D
Daniel Larimer 已提交
366
            authority auth;
367
            if (boost::istarts_with(authorityJsonOrFile, "EOS")) {
D
Daniel Larimer 已提交
368
               auth = authority(public_key_type(authorityJsonOrFile));
369
            } else {
370 371 372 373 374 375 376
               fc::variant parsedAuthority;
               if (boost::istarts_with(authorityJsonOrFile, "{")) {
                  parsedAuthority = fc::json::from_string(authorityJsonOrFile);
               } else {
                  parsedAuthority = fc::json::from_file(authorityJsonOrFile);
               }

D
Daniel Larimer 已提交
377
               auth = parsedAuthority.as<authority>();
378 379
            }

380
            name parent;
381
            if (parentStr.size() == 0 && permissionStr != "owner") {
382
               // see if we can auto-determine the proper parent
K
Kevin Heifner 已提交
383
               const auto account_result = call(get_account_func, fc::mutable_variant_object("account_name", accountStr));
384 385 386 387 388 389 390 391 392
               const auto& existing_permissions = account_result.get_object()["permissions"].get_array();
               auto permissionPredicate = [this](const auto& perm) { 
                  return perm.is_object() && 
                        perm.get_object().contains("permission") &&
                        boost::equals(perm.get_object()["permission"].get_string(), permissionStr); 
               };

               auto itr = boost::find_if(existing_permissions, permissionPredicate);
               if (itr != existing_permissions.end()) {
393
                  parent = name((*itr).get_object()["parent"].get_string());
394 395
               } else {
                  // if this is a new permission and there is no parent we default to "active"
396
                  parent = name("active");
397

398 399
               }
            } else {
400
               parent = name(parentStr);
401
            }
402

D
Daniel Larimer 已提交
403
            send_transaction({create_updateauth(account, permission, parent, auth, name(permissionAuth))}, {name(account), config::system_account_name}, skip_sign);
404 405 406 407 408
         }      
      });
   }
   
};
409

410
struct set_action_permission_subcommand {
411 412 413 414 415 416
   string accountStr;
   string codeStr;
   string typeStr;
   string requirementStr;
   bool skip_sign;

417
   set_action_permission_subcommand(CLI::App* actionRoot) {
418 419 420 421 422 423
      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();
      permissions->add_flag("-s,--skip-sign", skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
K
Kevin Heifner 已提交
424
      add_standard_transaction_options(permissions);
425 426

      permissions->set_callback([this] {
427 428 429
         name account = name(accountStr);
         name code = name(codeStr);
         name type = name(typeStr);
430 431 432
         bool is_delete = boost::iequals(requirementStr, "null");
         
         if (is_delete) {
D
Daniel Larimer 已提交
433
            send_transaction({create_unlinkauth(account, code, type)}, {account, config::system_account_name}, skip_sign);
434
         } else {
435
            name requirement = name(requirementStr);
D
Daniel Larimer 已提交
436
            send_transaction({create_linkauth(account, code, type, requirement)}, {name(account), config::system_account_name}, skip_sign);
437 438 439 440
         }      
      });
   }
};
441

442
int main( int argc, char** argv ) {
D
Daniel Larimer 已提交
443
   fc::path binPath = argv[0];
444 445 446 447 448 449 450 451
   if (binPath.is_relative()) {
      binPath = relative(binPath, current_path()); 
   }

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

452
   CLI::App app{"Command Line Interface to Eos Client"};
453
   app.require_subcommand();
454 455 456 457
   app.add_option( "-H,--host", host, localized("the host where eosd is running"), true );
   app.add_option( "-p,--port", port, localized("the port where eosd is running"), true );
   app.add_option( "--wallet-host", wallet_host, localized("the host where eos-walletd is running"), true );
   app.add_option( "--wallet-port", wallet_port, localized("the port where eos-walletd is running"), true );
458

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

462 463 464 465 466 467 468
   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;
   });

469
   // Create subcommand
470
   auto create = app.add_subcommand("create", localized("Create various items, on and off the blockchain"), false);
N
Nathan Hourt 已提交
471 472 473
   create->require_subcommand();

   // create key
D
Daniel Larimer 已提交
474 475 476 477 478 479
   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 已提交
480 481 482 483
   });

   // create account
   string creator;
484
   string account_name;
N
Nathan Hourt 已提交
485 486
   string ownerKey;
   string activeKey;
D
Daniel Larimer 已提交
487
   bool skip_sign = false;
488 489
   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();
490
   createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
491 492 493
   createAccount->add_option("OwnerKey", ownerKey, localized("The owner public key for the account"))->required();
   createAccount->add_option("ActiveKey", activeKey, localized("The active public key for the account"))->required();
   createAccount->add_flag("-s,--skip-signature", skip_sign, localized("Specify that unlocked wallet keys should not be used to sign transaction"));
K
Kevin Heifner 已提交
494
   add_standard_transaction_options(createAccount);
N
Nathan Hourt 已提交
495
   createAccount->set_callback([&] {
496
      create_account(creator, account_name, public_key_type(ownerKey), public_key_type(activeKey), !skip_sign);
N
Nathan Hourt 已提交
497
   });
498

499 500
   // create producer
   vector<string> permissions;
501
   auto createProducer = create->add_subcommand("producer", localized("Create a new producer on the blockchain"), false);
502
   createProducer->add_option("name", account_name, localized("The name of the new producer"))->required();
503
   createProducer->add_option("OwnerKey", ownerKey, localized("The public key for the producer"))->required();
504
   createProducer->add_option("-p,--permission", permissions,
505 506
                              localized("An account and permission level to authorize, as in 'account@permission' (default user@active)"));
   createProducer->add_flag("-s,--skip-signature", skip_sign, localized("Specify that unlocked wallet keys should not be used to sign transaction"));
K
Kevin Heifner 已提交
507
   add_standard_transaction_options(createProducer);
508
   createProducer->set_callback([&account_name, &ownerKey, &permissions, &skip_sign] {
509
      if (permissions.empty()) {
510
         permissions.push_back(account_name + "@active");
511 512 513
      }
      auto account_permissions = get_account_permissions(permissions);

514
      signed_transaction trx;
D
Daniel Larimer 已提交
515 516
      trx.write_scope = sort_names({config::system_account_name, account_name});
      trx.actions.emplace_back(  account_permissions, contracts::setproducer{account_name, public_key_type(ownerKey), chain_config{}} );
517 518 519 520

      std::cout << fc::json::to_pretty_string(push_transaction(trx, !skip_sign)) << std::endl;
   });

521
   // Get subcommand
522
   auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"), false);
N
Nathan Hourt 已提交
523 524 525
   get->require_subcommand();

   // get info
526
   get->add_subcommand("info", localized("Get current blockchain information"))->set_callback([] {
N
Nathan Hourt 已提交
527 528 529 530 531
      std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
   });

   // get block
   string blockArg;
532 533
   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 已提交
534 535 536 537 538 539 540
   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;
541 542
   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 已提交
543 544
   getAccount->set_callback([&] {
      std::cout << fc::json::to_pretty_string(call(get_account_func,
K
Kevin Heifner 已提交
545
                                                   fc::mutable_variant_object("account_name", accountName)))
N
Nathan Hourt 已提交
546 547 548
                << std::endl;
   });

D
Daniel Larimer 已提交
549 550 551
   // get code
   string codeFilename;
   string abiFilename;
552 553 554 555
   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 已提交
556
   getCode->set_callback([&] {
K
Kevin Heifner 已提交
557
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", accountName));
D
Daniel Larimer 已提交
558

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

      if( codeFilename.size() ){
562
         std::cout << localized("saving wast to ${codeFilename}", ("codeFilename", codeFilename)) << std::endl;
D
Daniel Larimer 已提交
563 564 565 566 567
         auto code = result["wast"].as_string();
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
568
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581 582
         auto abi  = fc::json::to_pretty_string( result["abi"] );
         std::ofstream abiout( abiFilename.c_str() );
         abiout << abi;
      }
   });

   // get table 
   string scope;
   string code;
   string table;
   string lower;
   string upper;
   bool binary = false;
   uint32_t limit = 10;
583 584 585 586 587 588 589 590 591
   auto getTable = get->add_subcommand( "table", localized("Retrieve the contents of a database table"), false);
   getTable->add_option( "scope", scope, localized("The account scope where the table is found") )->required();
   getTable->add_option( "contract", code, localized("The contract within scope who owns the table") )->required();
   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") );
   getTable->add_option( "-k,--key", limit, localized("The name of the key to index by as defined by the abi, defaults to primary key") );
   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 已提交
592 593 594 595 596 597 598 599 600 601 602 603 604 605

   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("scope",scope)
                         ("code",code)
                         ("table",table)
                         );

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



606
   // get accounts
607
   string publicKey;
608 609
   auto getAccounts = get->add_subcommand("accounts", localized("Retrieve accounts associated with a public key"), false);
   getAccounts->add_option("public_key", publicKey, localized("The public key to retrieve accounts for"))->required();
610 611 612 613 614
   getAccounts->set_callback([&] {
      auto arg = fc::mutable_variant_object( "public_key", publicKey);
      std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
   });

615

616 617
   // get servants
   string controllingAccount;
618 619
   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();
620
   getServants->set_callback([&] {
621
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
622 623 624
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

N
Nathan Hourt 已提交
625 626
   // get transaction
   string transactionId;
627 628
   auto getTransaction = get->add_subcommand("transaction", localized("Retrieve a transaction from the blockchain"), false);
   getTransaction->add_option("id", transactionId, localized("ID of the transaction to retrieve"))->required();
N
Nathan Hourt 已提交
629 630 631 632
   getTransaction->set_callback([&] {
      auto arg= fc::mutable_variant_object( "transaction_id", transactionId);
      std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
   });
633

634 635 636
   // get transactions
   string skip_seq;
   string num_seq;
637
   auto getTransactions = get->add_subcommand("transactions", localized("Retrieve all transactions with specific account name referenced in their scope"), false);
638
   getTransactions->add_option("account_name", account_name, localized("name of account to query on"))->required();
639 640
   getTransactions->add_option("skip_seq", skip_seq, localized("Number of most recent transactions to skip (0 would start at most recent transaction)"));
   getTransactions->add_option("num_seq", num_seq, localized("Number of transactions to return"));
641 642 643 644 645 646
   getTransactions->set_callback([&] {
      auto arg = (skip_seq.empty())
                  ? fc::mutable_variant_object( "account_name", account_name)
                  : (num_seq.empty())
                     ? fc::mutable_variant_object( "account_name", account_name)("skip_seq", skip_seq)
                     : fc::mutable_variant_object( "account_name", account_name)("skip_seq", skip_seq)("num_seq", num_seq);
D
Daniel Larimer 已提交
647
      auto result = call(get_transactions_func, arg);
648
      std::cout << fc::json::to_pretty_string(call(get_transactions_func, arg)) << std::endl;
D
Daniel Larimer 已提交
649 650 651 652 653 654 655 656 657


      const auto& trxs = result.get_object()["transactions"].get_array();
      for( const auto& t : trxs ) {
         const auto& tobj = t.get_object();
         int64_t seq_num  = tobj["seq_num"].as<int64_t>();
         string  id       = tobj["transaction_id"].as_string();
         const auto& trx  = tobj["transaction"].get_object();
         const auto& exp  = trx["expiration"].as<fc::time_point_sec>();
D
Daniel Larimer 已提交
658
         const auto& msgs = trx["actions"].get_array();
D
Daniel Larimer 已提交
659 660 661
         std::cout << tobj["seq_num"].as_string() <<"] " << id << "  " << trx["expiration"].as_string() << std::endl;
      }

662 663
   });

664
   // set subcommand
665
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
666 667 668
   setSubcommand->require_subcommand();

   // set contract subcommand
N
Nathan Hourt 已提交
669 670 671
   string account;
   string wastPath;
   string abiPath;
672 673 674
   auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
   contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))->required();
   contractSubcommand->add_option("wast-file", wastPath, localized("The file containing the contract WAST"))->required()
N
Nathan Hourt 已提交
675
         ->check(CLI::ExistingFile);
676
   auto abi = contractSubcommand->add_option("abi-file,-a,--abi", abiPath, localized("The ABI for the contract"))
N
Nathan Hourt 已提交
677
              ->check(CLI::ExistingFile);
678
   contractSubcommand->add_flag("-s,--skip-sign", skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
K
Kevin Heifner 已提交
679
   add_standard_transaction_options(contractSubcommand);
N
Nathan Hourt 已提交
680 681
   contractSubcommand->set_callback([&] {
      std::string wast;
682
      std::cout << localized("Reading WAST...") << std::endl;
N
Nathan Hourt 已提交
683
      fc::read_file_contents(wastPath, wast);
684
      std::cout << localized("Assembling WASM...") << std::endl;
N
Nathan Hourt 已提交
685 686
      auto wasm = assemble_wast(wast);

D
Daniel Larimer 已提交
687
      contracts::setcode handler;
N
Nathan Hourt 已提交
688 689
      handler.account = account;
      handler.code.assign(wasm.begin(), wasm.end());
N
Nathan Hourt 已提交
690

691
      signed_transaction trx;
D
Daniel Larimer 已提交
692 693
      trx.write_scope = sort_names({config::system_account_name, account});
      trx.actions.emplace_back( vector<chain::permission_level>{{account,"active"}}, handler);
694

K
Kevin Heifner 已提交
695 696 697 698 699 700 701
      if (abi->count()) {
         contracts::setabi handler;
         handler.account = account;
         handler.abi = fc::json::from_file(abiPath).as<contracts::abi_def>();
         trx.actions.emplace_back( vector<chain::permission_level>{{account,"active"}}, handler);
      }

702
      std::cout << localized("Publishing contract...") << std::endl;
D
Daniel Larimer 已提交
703
      std::cout << fc::json::to_pretty_string(push_transaction(trx, !skip_sign)) << std::endl;
N
Nathan Hourt 已提交
704
   });
N
Nathan Hourt 已提交
705

706 707
   // set producer approve/unapprove subcommand
   string producer;
708
   auto producerSubcommand = setSubcommand->add_subcommand("producer", localized("Approve/unapprove producer"));
709
   producerSubcommand->require_subcommand();
710 711
   auto approveCommand = producerSubcommand->add_subcommand("approve", localized("Approve producer"));
   auto unapproveCommand = producerSubcommand->add_subcommand("unapprove", localized("Unapprove producer"));
712
   producerSubcommand->add_option("user-name", account_name, localized("The name of the account approving"))->required();
713
   producerSubcommand->add_option("producer-name", producer, localized("The name of the producer to approve"))->required();
714
   producerSubcommand->add_option("-p,--permission", permissions,
715 716
                              localized("An account and permission level to authorize, as in 'account@permission' (default user@active)"));
   producerSubcommand->add_flag("-s,--skip-signature", skip_sign, localized("Specify that unlocked wallet keys should not be used to sign transaction"));
K
Kevin Heifner 已提交
717
   add_standard_transaction_options(producerSubcommand);
718 719 720 721
   producerSubcommand->set_callback([&] {
      // don't need to check unapproveCommand because one of approve or unapprove is required
      bool approve = producerSubcommand->got_subcommand(approveCommand);
      if (permissions.empty()) {
722
         permissions.push_back(account_name + "@active");
723 724 725
      }
      auto account_permissions = get_account_permissions(permissions);

726
      signed_transaction trx;
D
Daniel Larimer 已提交
727 728
      trx.write_scope = sort_names({config::system_account_name, account_name});
      trx.actions.emplace_back( account_permissions, contracts::okproducer{account_name, producer, approve});
729 730

      push_transaction(trx, !skip_sign);
731
      std::cout << localized("Set producer approval from ${name} for ${producer} to ${approve}",
732
         ("name", account_name)("producer", producer)("value", approve ? "approve" : "unapprove")) << std::endl;
733 734
   });

K
Kevin Heifner 已提交
735 736
   // set proxy subcommand
   string proxy;
737
   auto proxySubcommand = setSubcommand->add_subcommand("proxy", localized("Set proxy account for voting"));
738
   proxySubcommand->add_option("user-name", account_name, localized("The name of the account to proxy from"))->required();
739
   proxySubcommand->add_option("proxy-name", proxy, localized("The name of the account to proxy (unproxy if not provided)"));
K
Kevin Heifner 已提交
740
   proxySubcommand->add_option("-p,--permission", permissions,
741 742
                                  localized("An account and permission level to authorize, as in 'account@permission' (default user@active)"));
   proxySubcommand->add_flag("-s,--skip-signature", skip_sign, localized("Specify that unlocked wallet keys should not be used to sign transaction"));
K
Kevin Heifner 已提交
743
   add_standard_transaction_options(proxySubcommand);
K
Kevin Heifner 已提交
744 745
   proxySubcommand->set_callback([&] {
      if (permissions.empty()) {
746
         permissions.push_back(account_name + "@active");
K
Kevin Heifner 已提交
747 748 749
      }
      auto account_permissions = get_account_permissions(permissions);
      if (proxy.empty()) {
750
         proxy = account_name;
K
Kevin Heifner 已提交
751 752
      }

753
      signed_transaction trx;
D
Daniel Larimer 已提交
754 755
      trx.write_scope = sort_names({config::system_account_name, account_name});
      trx.actions.emplace_back( account_permissions, contracts::setproxy{account_name, proxy});
K
Kevin Heifner 已提交
756 757

      push_transaction(trx, !skip_sign);
758
      std::cout << localized("Set proxy for ${name} to ${proxy}", ("name", account_name)("proxy", proxy)) << std::endl;
K
Kevin Heifner 已提交
759 760
   });

761
   // set account
762
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
763 764

   // set account permission
765
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
766 767

   // set action
768
   auto setAction = setSubcommand->add_subcommand("action", localized("set or update blockchain action state"))->require_subcommand();
769 770
   
   // set action permission
771
   auto setActionPermission = set_action_permission_subcommand(setAction);
772

N
Nathan Hourt 已提交
773
   // Transfer subcommand
N
Nathan Hourt 已提交
774 775 776
   string sender;
   string recipient;
   uint64_t amount;
777
   string memo;
778 779 780 781 782 783
   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"));
   transfer->add_flag("-s,--skip-sign", skip_sign, localized("Specify that unlocked wallet keys should not be used to sign transaction"));
784
   add_standard_transaction_options(transfer);
N
Nathan Hourt 已提交
785
   transfer->set_callback([&] {
786
      signed_transaction trx;
D
Daniel Larimer 已提交
787
      trx.write_scope = sort_names({sender,recipient});
788 789 790 791
      
      if (tx_force_unique) {
         if (memo.size() == 0) {
            // use the memo to add a nonce
D
Daniel Larimer 已提交
792
            memo = fc::to_string(generate_nonce_value());
793
         } else {
D
Daniel Larimer 已提交
794 795
            // add a nonce actions
            trx.actions.emplace_back( generate_nonce() );
796 797 798
         }
      }

D
Daniel Larimer 已提交
799 800
      trx.actions.emplace_back( vector<chain::permission_level>{{sender,"active"}}, 
                                contracts::transfer{ .from = sender, .to = recipient, .amount = amount, .memo = memo});
801 802


N
Nathan Hourt 已提交
803
      auto info = get_info();
804
      trx.expiration = info.head_block_time + tx_expiration;
D
Daniel Larimer 已提交
805
      trx.set_reference_block( info.head_block_id);
D
Daniel Larimer 已提交
806
      if (!skip_sign) {
K
Kevin Heifner 已提交
807 808
         sign_transaction(trx);
      }
N
Nathan Hourt 已提交
809

N
Nathan Hourt 已提交
810 811
      std::cout << fc::json::to_pretty_string( call( push_txn_func, trx )) << std::endl;
   });
N
Nathan Hourt 已提交
812

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
   // Net subcommand 
   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 已提交
845 846

   // Wallet subcommand
847
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
848
   wallet->require_subcommand();
K
Kevin Heifner 已提交
849
   // create wallet
D
Daniel Larimer 已提交
850
   string wallet_name = "default";
851 852
   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 已提交
853 854
   createWallet->set_callback([&wallet_name] {
      const auto& v = call(wallet_host, wallet_port, wallet_create, wallet_name);
855 856 857
      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 已提交
858 859 860 861
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
862 863
   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 已提交
864
   openWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
865 866
      /*const auto& v = */call(wallet_host, wallet_port, wallet_open, wallet_name);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
867
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
868 869 870
   });

   // lock wallet
871 872
   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 已提交
873
   lockWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
874
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock, wallet_name);
875
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
D
Daniel Larimer 已提交
876 877
      //std::cout << fc::json::to_pretty_string(v) << std::endl;

K
Kevin Heifner 已提交
878 879 880
   });

   // lock all wallets
881
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
K
Kevin Heifner 已提交
882
   locakAllWallets->set_callback([] {
D
Daniel Larimer 已提交
883 884
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock_all);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
885
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
886 887 888 889
   });

   // unlock wallet
   string wallet_pw;
890 891 892
   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 已提交
893
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
894
      if( wallet_pw.size() == 0 ) {
895
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
896 897 898 899 900 901
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }


K
Kevin Heifner 已提交
902
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
903
      /*const auto& v = */call(wallet_host, wallet_port, wallet_unlock, vs);
904
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
D
Daniel Larimer 已提交
905
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
906 907 908 909
   });

   // import keys into wallet
   string wallet_key;
910 911 912
   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"));
   importWallet->add_option("key", wallet_key, localized("Private key in WIF format to import"))->required();
K
Kevin Heifner 已提交
913
   importWallet->set_callback([&wallet_name, &wallet_key] {
D
Daniel Larimer 已提交
914 915
      private_key_type key( wallet_key );
      public_key_type pubkey = key.get_public_key();
D
Daniel Larimer 已提交
916

K
Kevin Heifner 已提交
917 918
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
      const auto& v = call(wallet_host, wallet_port, wallet_import_key, vs);
919
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
D
Daniel Larimer 已提交
920
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
921 922 923
   });

   // list wallets
924
   auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
K
Kevin Heifner 已提交
925
   listWallet->set_callback([] {
926
      std::cout << localized("Wallets:") << std::endl;
K
Kevin Heifner 已提交
927 928 929 930 931
      const auto& v = call(wallet_host, wallet_port, wallet_list);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

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

938
   // Benchmark subcommand
939
   auto benchmark = app.add_subcommand( "benchmark", localized("Configure and execute benchmarks"), false );
940
   benchmark->require_subcommand();
941
   auto benchmark_setup = benchmark->add_subcommand( "setup", localized("Configures initial condition for benchmark") );
942
   uint64_t number_of_accounts = 2;
943
   benchmark_setup->add_option("accounts", number_of_accounts, localized("the number of accounts in transfer among"))->required();
K
Kevin Heifner 已提交
944
   add_standard_transaction_options(benchmark_setup);
945
   benchmark_setup->set_callback([&]{
946 947
      std::cerr << localized("Creating ${number_of_accounts} accounts with initial balances", ("number_of_accounts",number_of_accounts)) << std::endl;
      EOSC_ASSERT( number_of_accounts >= 2, "must create at least 2 accounts" );
948 949 950

      auto info = get_info();

951
      vector<signed_transaction> batch;
952 953
      batch.reserve( number_of_accounts );
      for( uint32_t i = 0; i < number_of_accounts; ++i ) {
954
        name newaccount( name("benchmark").value + i );
955
        public_key_type owner, active;
956
        name creator("inita" );
957

958 959 960
        auto owner_auth   = eosio::chain::authority{1, {{owner, 1}}, {}};
        auto active_auth  = eosio::chain::authority{1, {{active, 1}}, {}};
        auto recovery_auth = eosio::chain::authority{1, {}, {{{creator, "active"}, 1}}};
961 962 963
        
        uint64_t deposit = 1;
        
964
        signed_transaction trx;
D
Daniel Larimer 已提交
965 966 967
        trx.write_scope = sort_names({creator,config::system_account_name});
        trx.actions.emplace_back( vector<chain::permission_level>{{creator,"active"}}, 
                                  contracts::newaccount{creator, newaccount, owner_auth, active_auth, recovery_auth, deposit});
968

969
        trx.expiration = info.head_block_time + tx_expiration; 
D
Daniel Larimer 已提交
970
        trx.set_reference_block(info.head_block_id);
971 972 973 974 975 976
        batch.emplace_back(trx);
      }
      auto result = call( push_txns_func, batch );
      std::cout << fc::json::to_pretty_string(result) << std::endl;
   });

977
   auto benchmark_transfer = benchmark->add_subcommand( "transfer", localized("executes random transfers among accounts") );
978 979
   uint64_t number_of_transfers = 0;
   bool loop = false;
980 981 982
   benchmark_transfer->add_option("accounts", number_of_accounts, localized("the number of accounts in transfer among"))->required();
   benchmark_transfer->add_option("count", number_of_transfers, localized("the number of transfers to execute"))->required();
   benchmark_transfer->add_option("loop", loop, localized("whether or not to loop for ever"));
K
Kevin Heifner 已提交
983
   add_standard_transaction_options(benchmark_transfer);
984
   benchmark_transfer->set_callback([&]{
985
      EOSC_ASSERT( number_of_accounts >= 2, "must create at least 2 accounts" );
986

987
      std::cerr << localized("Funding ${number_of_accounts} accounts from init", ("number_of_accounts",number_of_accounts)) << std::endl;
988
      auto info = get_info();
989
      vector<signed_transaction> batch;
990 991
      batch.reserve(100);
      for( uint32_t i = 0; i < number_of_accounts; ++i ) {
992 993
         name sender( "initb" );
         name recipient( name("benchmark").value + i);
994
         uint32_t amount = 100000;
995

996
         signed_transaction trx;
D
Daniel Larimer 已提交
997 998 999
         trx.write_scope = sort_names({sender,recipient});
         trx.actions.emplace_back( vector<chain::permission_level>{{sender,"active"}},
                                   contracts::transfer{ .from = sender, .to = recipient, .amount = amount, .memo = memo});
1000
         trx.expiration = info.head_block_time + tx_expiration; 
D
Daniel Larimer 已提交
1001
         trx.set_reference_block(info.head_block_id);
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016

         batch.emplace_back(trx);
         if( batch.size() == 100 ) {
            auto result = call( push_txns_func, batch );
      //      std::cout << fc::json::to_pretty_string(result) << std::endl;
            batch.resize(0);
         }
      }
      if( batch.size() ) {
         auto result = call( push_txns_func, batch );
         //std::cout << fc::json::to_pretty_string(result) << std::endl;
         batch.resize(0);
      }


1017
      std::cerr << localized("Generating random ${number_of_transfers} transfers among ${number_of_accounts}",("number_of_transfers",number_of_transfers)("number_of_accounts",number_of_accounts)) << std::endl;
1018 1019 1020 1021 1022
      while( true ) {
         auto info = get_info();
         uint64_t amount = 1;

         for( uint32_t i = 0; i < number_of_transfers; ++i ) {
1023
            signed_transaction trx;
1024

1025 1026
            name sender( name("benchmark").value + rand() % number_of_accounts );
            name recipient( name("benchmark").value + rand() % number_of_accounts );
1027 1028

            while( recipient == sender )
1029
               recipient = name( name("benchmark").value + rand() % number_of_accounts );
1030 1031 1032


            auto memo = fc::variant(fc::time_point::now()).as_string() + " " + fc::variant(fc::time_point::now().time_since_epoch()).as_string();
D
Daniel Larimer 已提交
1033 1034 1035
            trx.write_scope = sort_names({sender,recipient});
            trx.actions.emplace_back(  vector<chain::permission_level>{{sender,"active"}},
                                       contracts::transfer{ .from = sender, .to = recipient, .amount = amount, .memo = memo});
1036
            trx.expiration = info.head_block_time + tx_expiration; 
D
Daniel Larimer 已提交
1037
            trx.set_reference_block( info.head_block_id);
1038 1039

            batch.emplace_back(trx);
1040
            if( batch.size() == 40 ) {
1041 1042 1043
               auto result = call( push_txns_func, batch );
               //std::cout << fc::json::to_pretty_string(result) << std::endl;
               batch.resize(0);
K
Kevin Heifner 已提交
1044
	            info = get_info();
1045 1046 1047 1048 1049 1050 1051 1052
            }
         }
         if( !loop ) break;
      }
   });

   

N
Nathan Hourt 已提交
1053
   // Push subcommand
1054
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
N
Nathan Hourt 已提交
1055 1056
   push->require_subcommand();

D
Daniel Larimer 已提交
1057
   // push actions
N
Nathan Hourt 已提交
1058 1059 1060 1061
   string contract;
   string action;
   string data;
   vector<string> scopes;
D
Daniel Larimer 已提交
1062 1063 1064
   auto actionsSubcommand = push->add_subcommand("actions", localized("Push a transaction with a single actions"));
   actionsSubcommand->fallthrough(false);
   actionsSubcommand->add_option("contract", contract,
1065
                                 localized("The account providing the contract to execute"), true)->required();
D
Daniel Larimer 已提交
1066
   actionsSubcommand->add_option("action", action, localized("The action to execute on the contract"), true)
N
Nathan Hourt 已提交
1067
         ->required();
D
Daniel Larimer 已提交
1068 1069
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
   actionsSubcommand->add_option("-p,--permission", permissions,
1070
                                 localized("An account and permission level to authorize, as in 'account@permission'"));
D
Daniel Larimer 已提交
1071 1072 1073 1074
   actionsSubcommand->add_option("-S,--scope", scopes, localized("An comma separated list of accounts in scope for this operation"), true);
   actionsSubcommand->add_flag("-s,--skip-sign", skip_sign, localized("Specify that unlocked wallet keys should not be used to sign transaction"));
   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
N
Nathan Hourt 已提交
1075 1076 1077 1078 1079 1080 1081
      ilog("Converting argument to binary...");
      auto arg= fc::mutable_variant_object
                ("code", contract)
                ("action", action)
                ("args", fc::json::from_string(data));
      auto result = call(json_to_bin_func, arg);

1082
      auto accountPermissions = get_account_permissions(permissions);
1083

1084
      signed_transaction trx;
D
Daniel Larimer 已提交
1085 1086
      /* TODO: restore this function
      transaction_emplace_serialized_actions(trx, contract, action, accountPermissions,
1087
                                                      result.get_object()["binargs"].as<bytes>());
D
Daniel Larimer 已提交
1088
      */
1089 1090

      if (tx_force_unique) {
D
Daniel Larimer 已提交
1091
         trx.actions.emplace_back( generate_nonce() );
1092 1093
      }                                                      

D
Daniel Larimer 已提交
1094 1095 1096 1097
      for( const auto& scope : scopes ) {
         vector<string> subscopes;
         boost::split( subscopes, scope, boost::is_any_of( ", :" ) );
         for( const auto& s : subscopes )
D
Daniel Larimer 已提交
1098
            trx.write_scope.emplace_back(s);
D
Daniel Larimer 已提交
1099
      }
1100
      std::cout << fc::json::to_pretty_string(push_transaction(trx, !skip_sign )) << std::endl;
N
Nathan Hourt 已提交
1101 1102 1103 1104
   });

   // push transaction
   string trxJson;
1105 1106
   auto trxSubcommand = push->add_subcommand("transaction", localized("Push an arbitrary JSON transaction"));
   trxSubcommand->add_option("transaction", trxJson, localized("The JSON of the transaction to push"))->required();
N
Nathan Hourt 已提交
1107 1108 1109 1110
   trxSubcommand->set_callback([&] {
      auto trx_result = call(push_txn_func, fc::json::from_string(trxJson));
      std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
   });
1111

1112 1113

   string trxsJson;
1114 1115
   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();
1116 1117 1118 1119 1120
   trxsSubcommand->set_callback([&] {
      auto trxs_result = call(push_txn_func, fc::json::from_string(trxsJson));
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });

1121 1122 1123 1124
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
1125 1126
   } catch (const explained_exception& e) {
      return 1;
1127
   } catch (const fc::exception& e) {
N
Nathan Hourt 已提交
1128
      auto errorString = e.to_detail_string();
1129 1130
      if (errorString.find("Connection refused") != string::npos) {
         if (errorString.find(fc::json::to_string(port)) != string::npos) {
1131
            std::cerr << localized("Failed to connect to eosd at ${ip}:${port}; is eosd running?", ("ip", host)("port", port)) << std::endl;
1132
         } else if (errorString.find(fc::json::to_string(wallet_port)) != string::npos) {
1133
            std::cerr << localized("Failed to connect to eos-walletd at ${ip}:${port}; is eos-walletd running?", ("ip", wallet_host)("port", wallet_port)) << std::endl;
1134
         } else {
1135
            std::cerr << localized("Failed to connect") << std::endl;
1136 1137 1138 1139
         }

         if (verbose_errors) {
            elog("connect error: ${e}", ("e", errorString));
1140 1141
         }
      } else {
1142 1143
         // attempt to extract the error code if one is present
         if (!print_help_text(e) || verbose_errors) {
1144
            elog("Failed with error: ${e}", ("e", verbose_errors ? e.to_detail_string() : e.to_string()));
1145
         }
1146
      }
1147 1148 1149 1150
      return 1;
   }

   return 0;
1151
}