main.cpp 56.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
#include <string>
#include <vector>
#include <boost/asio.hpp>
P
Pravin 已提交
68
#include <boost/format.hpp>
69 70 71
#include <iostream>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
D
Daniel Larimer 已提交
72
#include <fc/io/console.hpp>
73
#include <fc/exception/exception.hpp>
D
Daniel Larimer 已提交
74
#include <eos/utilities/key_conversion.hpp>
75

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

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

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

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

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

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

B
Brian Johnson 已提交
130 131 132
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";
133
const string push_txns_func = chain_func_base + "/push_transactions";
B
Brian Johnson 已提交
134 135 136
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 已提交
137 138
const string get_table_func = chain_func_base + "/get_table_rows";
const string get_code_func = chain_func_base + "/get_code";
K
Kevin Heifner 已提交
139
const string get_required_keys = chain_func_base + "/get_required_keys";
B
Brian Johnson 已提交
140 141 142

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

147 148 149 150 151 152 153
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 已提交
154 155 156 157 158 159 160 161 162 163 164 165
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";

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

174 175 176 177 178 179 180
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;
181
      std::cerr << localized("Error parsing WebAssembly text file:") << std::endl;
182 183 184 185 186 187
      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;
      }
188
      FC_THROW_EXCEPTION( explained_exception, "wast parse error" );
189 190 191 192 193 194 195 196 197 198 199
   }

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

K
Kevin Heifner 已提交
206
auto tx_expiration = fc::seconds(30);
207 208 209
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 已提交
210 211
      double value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
212 213 214
         return false;
      }
      
K
Kevin Heifner 已提交
215
      tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
216 217 218
      return true;
   };

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

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

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

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

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

247

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

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

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

261
void sign_transaction(signed_transaction& trx) {
K
Kevin Heifner 已提交
262 263 264 265 266 267 268 269 270
   // 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);
271
   trx = signed_trx.as<signed_transaction>();
K
Kevin Heifner 已提交
272 273
}

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

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

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


289 290 291 292
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}}};
293 294 295

      uint64_t deposit = 1;

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

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

D
Daniel Larimer 已提交
304 305 306
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}};
307 308
}

D
Daniel Larimer 已提交
309 310 311
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}};
312 313
}

D
Daniel Larimer 已提交
314 315 316
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}};
317 318
}

D
Daniel Larimer 已提交
319 320 321
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}};
322 323
}

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

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

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

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

349
   set_account_permission_subcommand(CLI::App* accountCmd) {
350
      auto permissions = accountCmd->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
M
Matias Romeo 已提交
351
      permissions->add_option("-p,--permission", permissionAuth,localized("Permission level to authorize, (Defaults to: 'active'"));
352 353 354 355 356
      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 已提交
357
      add_standard_transaction_options(permissions);
358 359

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

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

381
            name parent;
382
            if (parentStr.size() == 0 && permissionStr != "owner") {
383
               // see if we can auto-determine the proper parent
K
Kevin Heifner 已提交
384
               const auto account_result = call(get_account_func, fc::mutable_variant_object("account_name", accountStr));
385 386 387 388 389 390 391 392 393
               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()) {
394
                  parent = name((*itr).get_object()["parent"].get_string());
395 396
               } else {
                  // if this is a new permission and there is no parent we default to "active"
397
                  parent = name("active");
398

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

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

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

418
   set_action_permission_subcommand(CLI::App* actionRoot) {
419 420 421 422 423 424
      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 已提交
425
      add_standard_transaction_options(permissions);
426 427

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

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

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

453
   CLI::App app{"Command Line Interface to Eos Client"};
454
   app.require_subcommand();
455 456 457 458
   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 );
459

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

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

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

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

   // create account
   string creator;
485
   string account_name;
N
Nathan Hourt 已提交
486 487
   string ownerKey;
   string activeKey;
D
Daniel Larimer 已提交
488
   bool skip_sign = false;
489 490
   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();
491
   createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
492 493 494
   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 已提交
495
   add_standard_transaction_options(createAccount);
N
Nathan Hourt 已提交
496
   createAccount->set_callback([&] {
497
      create_account(creator, account_name, public_key_type(ownerKey), public_key_type(activeKey), !skip_sign);
N
Nathan Hourt 已提交
498
   });
499

500 501
   // create producer
   vector<string> permissions;
502
   auto createProducer = create->add_subcommand("producer", localized("Create a new producer on the blockchain"), false);
503
   createProducer->add_option("name", account_name, localized("The name of the new producer"))->required();
504
   createProducer->add_option("OwnerKey", ownerKey, localized("The public key for the producer"))->required();
505
   createProducer->add_option("-p,--permission", permissions,
506 507
                              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 已提交
508
   add_standard_transaction_options(createProducer);
509
   createProducer->set_callback([&account_name, &ownerKey, &permissions, &skip_sign] {
510
      if (permissions.empty()) {
511
         permissions.push_back(account_name + "@active");
512 513 514
      }
      auto account_permissions = get_account_permissions(permissions);

515
      signed_transaction trx;
D
Daniel Larimer 已提交
516 517
      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{}} );
518 519 520 521

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

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

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

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

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

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

      if( codeFilename.size() ){
563
         std::cout << localized("saving wast to ${codeFilename}", ("codeFilename", codeFilename)) << std::endl;
D
Daniel Larimer 已提交
564 565 566 567 568
         auto code = result["wast"].as_string();
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
569
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
570 571 572 573 574 575 576 577 578 579 580 581 582 583
         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;
584 585 586 587 588 589 590 591 592
   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 已提交
593 594 595 596 597 598 599 600 601 602 603 604 605 606

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



607
   // get accounts
608
   string publicKey;
609 610
   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();
611 612 613 614 615
   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;
   });

616

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

N
Nathan Hourt 已提交
626 627
   // get transaction
   string transactionId;
628 629
   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 已提交
630 631 632 633
   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;
   });
634

635 636 637
   // get transactions
   string skip_seq;
   string num_seq;
638
   auto getTransactions = get->add_subcommand("transactions", localized("Retrieve all transactions with specific account name referenced in their scope"), false);
639
   getTransactions->add_option("account_name", account_name, localized("name of account to query on"))->required();
640 641
   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"));
642 643 644 645 646 647
   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 已提交
648
      auto result = call(get_transactions_func, arg);
649
      std::cout << fc::json::to_pretty_string(call(get_transactions_func, arg)) << std::endl;
D
Daniel Larimer 已提交
650 651 652 653 654 655 656 657 658


      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 已提交
659
         const auto& msgs = trx["actions"].get_array();
D
Daniel Larimer 已提交
660 661 662
         std::cout << tobj["seq_num"].as_string() <<"] " << id << "  " << trx["expiration"].as_string() << std::endl;
      }

663 664
   });

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

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

      vector<uint8_t> wasm;
687 688 689
      const string binary_wasm_header = "\x00\x61\x73\x6d";
      if(wast.compare(0, 4, binary_wasm_header)) {
         std::cout << localized("Using already assembled WASM...") << std::endl;
690
         wasm = vector<uint8_t>(wast.begin(), wast.end());
691
      }
692 693 694 695
      else {
         std::cout << localized("Assembling WASM...") << std::endl;
         wasm = assemble_wast(wast);
      }
N
Nathan Hourt 已提交
696

D
Daniel Larimer 已提交
697
      contracts::setcode handler;
N
Nathan Hourt 已提交
698 699
      handler.account = account;
      handler.code.assign(wasm.begin(), wasm.end());
N
Nathan Hourt 已提交
700

701
      signed_transaction trx;
702
      trx.write_scope = sort_names({config::eosio_auth_scope, account});
D
Daniel Larimer 已提交
703
      trx.actions.emplace_back( vector<chain::permission_level>{{account,"active"}}, handler);
704

K
Kevin Heifner 已提交
705 706 707 708 709 710 711
      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);
      }

712
      std::cout << localized("Publishing contract...") << std::endl;
D
Daniel Larimer 已提交
713
      std::cout << fc::json::to_pretty_string(push_transaction(trx, !skip_sign)) << std::endl;
N
Nathan Hourt 已提交
714
   });
N
Nathan Hourt 已提交
715

716 717
   // set producer approve/unapprove subcommand
   string producer;
718
   auto producerSubcommand = setSubcommand->add_subcommand("producer", localized("Approve/unapprove producer"));
719
   producerSubcommand->require_subcommand();
720
   auto approveCommand = producerSubcommand->add_subcommand("approve", localized("Approve producer"));
P
Pravin 已提交
721
   producerSubcommand->add_subcommand("unapprove", localized("Unapprove producer"));
722
   producerSubcommand->add_option("user-name", account_name, localized("The name of the account approving"))->required();
723
   producerSubcommand->add_option("producer-name", producer, localized("The name of the producer to approve"))->required();
724
   producerSubcommand->add_option("-p,--permission", permissions,
725 726
                              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 已提交
727
   add_standard_transaction_options(producerSubcommand);
728 729 730 731
   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()) {
732
         permissions.push_back(account_name + "@active");
733 734 735
      }
      auto account_permissions = get_account_permissions(permissions);

736
      signed_transaction trx;
D
Daniel Larimer 已提交
737 738
      trx.write_scope = sort_names({config::system_account_name, account_name});
      trx.actions.emplace_back( account_permissions, contracts::okproducer{account_name, producer, approve});
739 740

      push_transaction(trx, !skip_sign);
741
      std::cout << localized("Set producer approval from ${name} for ${producer} to ${approve}",
742
         ("name", account_name)("producer", producer)("value", approve ? "approve" : "unapprove")) << std::endl;
743 744
   });

K
Kevin Heifner 已提交
745 746
   // set proxy subcommand
   string proxy;
747
   auto proxySubcommand = setSubcommand->add_subcommand("proxy", localized("Set proxy account for voting"));
748
   proxySubcommand->add_option("user-name", account_name, localized("The name of the account to proxy from"))->required();
749
   proxySubcommand->add_option("proxy-name", proxy, localized("The name of the account to proxy (unproxy if not provided)"));
K
Kevin Heifner 已提交
750
   proxySubcommand->add_option("-p,--permission", permissions,
751 752
                                  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 已提交
753
   add_standard_transaction_options(proxySubcommand);
K
Kevin Heifner 已提交
754 755
   proxySubcommand->set_callback([&] {
      if (permissions.empty()) {
756
         permissions.push_back(account_name + "@active");
K
Kevin Heifner 已提交
757 758 759
      }
      auto account_permissions = get_account_permissions(permissions);
      if (proxy.empty()) {
760
         proxy = account_name;
K
Kevin Heifner 已提交
761 762
      }

763
      signed_transaction trx;
D
Daniel Larimer 已提交
764 765
      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 已提交
766 767

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

771
   // set account
772
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
773 774

   // set account permission
775
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
776 777

   // set action
778
   auto setAction = setSubcommand->add_subcommand("action", localized("set or update blockchain action state"))->require_subcommand();
779 780
   
   // set action permission
781
   auto setActionPermission = set_action_permission_subcommand(setAction);
782

N
Nathan Hourt 已提交
783
   // Transfer subcommand
N
Nathan Hourt 已提交
784 785 786
   string sender;
   string recipient;
   uint64_t amount;
787
   string memo;
788 789 790 791 792 793
   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"));
794
   add_standard_transaction_options(transfer);
N
Nathan Hourt 已提交
795
   transfer->set_callback([&] {
796
      signed_transaction trx;
D
Daniel Larimer 已提交
797
      trx.write_scope = sort_names({sender,recipient});
798 799 800 801
      
      if (tx_force_unique) {
         if (memo.size() == 0) {
            // use the memo to add a nonce
D
Daniel Larimer 已提交
802
            memo = fc::to_string(generate_nonce_value());
803
         } else {
D
Daniel Larimer 已提交
804 805
            // add a nonce actions
            trx.actions.emplace_back( generate_nonce() );
806 807 808
         }
      }

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


N
Nathan Hourt 已提交
813
      auto info = get_info();
814
      trx.expiration = info.head_block_time + tx_expiration;
D
Daniel Larimer 已提交
815
      trx.set_reference_block( info.head_block_id);
D
Daniel Larimer 已提交
816
      if (!skip_sign) {
K
Kevin Heifner 已提交
817 818
         sign_transaction(trx);
      }
N
Nathan Hourt 已提交
819

N
Nathan Hourt 已提交
820 821
      std::cout << fc::json::to_pretty_string( call( push_txn_func, trx )) << std::endl;
   });
N
Nathan Hourt 已提交
822

823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
   // 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 已提交
855 856

   // Wallet subcommand
857
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
858
   wallet->require_subcommand();
K
Kevin Heifner 已提交
859
   // create wallet
D
Daniel Larimer 已提交
860
   string wallet_name = "default";
861 862
   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 已提交
863 864
   createWallet->set_callback([&wallet_name] {
      const auto& v = call(wallet_host, wallet_port, wallet_create, wallet_name);
865 866 867
      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 已提交
868 869 870 871
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
872 873
   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 已提交
874
   openWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
875 876
      /*const auto& v = */call(wallet_host, wallet_port, wallet_open, wallet_name);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
877
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
878 879 880
   });

   // lock wallet
881 882
   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 已提交
883
   lockWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
884
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock, wallet_name);
885
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
D
Daniel Larimer 已提交
886 887
      //std::cout << fc::json::to_pretty_string(v) << std::endl;

K
Kevin Heifner 已提交
888 889 890
   });

   // lock all wallets
891
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
K
Kevin Heifner 已提交
892
   locakAllWallets->set_callback([] {
D
Daniel Larimer 已提交
893 894
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock_all);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
895
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
896 897 898 899
   });

   // unlock wallet
   string wallet_pw;
900 901 902
   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 已提交
903
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
904
      if( wallet_pw.size() == 0 ) {
905
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
906 907 908 909 910 911
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }


K
Kevin Heifner 已提交
912
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
913
      /*const auto& v = */call(wallet_host, wallet_port, wallet_unlock, vs);
914
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
915
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
916 917 918 919
   });

   // import keys into wallet
   string wallet_key;
920 921 922
   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 已提交
923
   importWallet->set_callback([&wallet_name, &wallet_key] {
D
Daniel Larimer 已提交
924 925
      private_key_type key( wallet_key );
      public_key_type pubkey = key.get_public_key();
D
Daniel Larimer 已提交
926

K
Kevin Heifner 已提交
927 928
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
      const auto& v = call(wallet_host, wallet_port, wallet_import_key, vs);
929
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
D
Daniel Larimer 已提交
930
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
931 932 933
   });

   // list wallets
934
   auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
K
Kevin Heifner 已提交
935
   listWallet->set_callback([] {
936
      std::cout << localized("Wallets:") << std::endl;
K
Kevin Heifner 已提交
937 938 939 940 941
      const auto& v = call(wallet_host, wallet_port, wallet_list);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

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

948
   // Benchmark subcommand
949
   auto benchmark = app.add_subcommand( "benchmark", localized("Configure and execute benchmarks"), false );
950
   benchmark->require_subcommand();
951
   auto benchmark_setup = benchmark->add_subcommand( "setup", localized("Configures initial condition for benchmark") );
952
   uint64_t number_of_accounts = 2;
953
   benchmark_setup->add_option("accounts", number_of_accounts, localized("the number of accounts in transfer among"))->required();
954 955 956 957 958 959
   string c_account;
   benchmark_setup->add_option("creator", c_account, localized("The creator account for benchmark accounts"))->required();
   string owner_key;
   string active_key;
   benchmark_setup->add_option("owner", owner_key, localized("The owner key to use for account creation"))->required();
   benchmark_setup->add_option("active", active_key, localized("The active key to use for account creation"))->required();
K
Kevin Heifner 已提交
960
   add_standard_transaction_options(benchmark_setup);
P
Pravin 已提交
961

962
   benchmark_setup->set_callback([&]{
963
      auto controlling_account_arg = fc::mutable_variant_object( "controlling_account", c_account);
P
Pravin 已提交
964 965 966 967 968 969 970 971 972 973 974 975 976
      auto response_servants = call(get_controlled_accounts_func, controlling_account_arg);
      fc::variant_object response_var;
      fc::from_variant(response_servants, response_var);
      std::vector<std::string> controlled_accounts_vec;
      fc::from_variant(response_var["controlled_accounts"], controlled_accounts_vec);
       long num_existing_accounts = std::count_if(controlled_accounts_vec.begin(),
                   		            controlled_accounts_vec.end(),
					[](auto const &s) { return s.find("benchmark") != std::string::npos;});
      boost::format fmter("%1% accounts already exist");
      fmter % num_existing_accounts;
      EOSC_ASSERT( number_of_accounts > num_existing_accounts, fmter.str().c_str());

      number_of_accounts -= num_existing_accounts;
977
      std::cerr << localized("Creating ${number_of_accounts} accounts with initial balances", ("number_of_accounts",number_of_accounts)) << std::endl;
P
Pravin 已提交
978 979 980 981

      if (num_existing_accounts == 0) {
      	EOSC_ASSERT( number_of_accounts >= 2, "must create at least 2 accounts" );
      }   
982 983 984

      auto info = get_info();

985
      vector<signed_transaction> batch;
986
      batch.reserve( number_of_accounts );
P
Pravin 已提交
987
      for( uint32_t i = num_existing_accounts; i < num_existing_accounts + number_of_accounts; ++i ) {
988
        name newaccount( name("benchmark").value + i );
989 990
        public_key_type owner(owner_key), active(active_key);
        name creator(c_account);
991

992 993 994
        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}}};
995 996 997
        
        uint64_t deposit = 1;
        
998
        signed_transaction trx;
D
Daniel Larimer 已提交
999 1000 1001
        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});
1002

1003
        trx.expiration = info.head_block_time + tx_expiration; 
D
Daniel Larimer 已提交
1004
        trx.set_reference_block(info.head_block_id);
1005
        batch.emplace_back(trx);
P
Pravin 已提交
1006
	info = get_info();
1007 1008 1009 1010 1011
      }
      auto result = call( push_txns_func, batch );
      std::cout << fc::json::to_pretty_string(result) << std::endl;
   });

1012
   auto benchmark_transfer = benchmark->add_subcommand( "transfer", localized("executes random transfers among accounts") );
1013 1014
   uint64_t number_of_transfers = 0;
   bool loop = false;
1015 1016 1017
   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 已提交
1018
   add_standard_transaction_options(benchmark_transfer);
1019
   benchmark_transfer->set_callback([&]{
1020
      EOSC_ASSERT( number_of_accounts >= 2, "must create at least 2 accounts" );
1021

1022
      std::cerr << localized("Funding ${number_of_accounts} accounts from init", ("number_of_accounts",number_of_accounts)) << std::endl;
1023
      auto info = get_info();
1024
      vector<signed_transaction> batch;
1025 1026
      batch.reserve(100);
      for( uint32_t i = 0; i < number_of_accounts; ++i ) {
1027 1028
         name sender( "initb" );
         name recipient( name("benchmark").value + i);
1029
         uint32_t amount = 100000;
1030

1031
         signed_transaction trx;
D
Daniel Larimer 已提交
1032 1033 1034
         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});
1035
         trx.expiration = info.head_block_time + tx_expiration; 
D
Daniel Larimer 已提交
1036
         trx.set_reference_block(info.head_block_id);
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051

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


1052
      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;
1053 1054 1055 1056 1057
      while( true ) {
         auto info = get_info();
         uint64_t amount = 1;

         for( uint32_t i = 0; i < number_of_transfers; ++i ) {
1058
            signed_transaction trx;
1059

1060 1061
            name sender( name("benchmark").value + rand() % number_of_accounts );
            name recipient( name("benchmark").value + rand() % number_of_accounts );
1062 1063

            while( recipient == sender )
1064
               recipient = name( name("benchmark").value + rand() % number_of_accounts );
1065 1066 1067


            auto memo = fc::variant(fc::time_point::now()).as_string() + " " + fc::variant(fc::time_point::now().time_since_epoch()).as_string();
D
Daniel Larimer 已提交
1068 1069 1070
            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});
1071
            trx.expiration = info.head_block_time + tx_expiration; 
D
Daniel Larimer 已提交
1072
            trx.set_reference_block( info.head_block_id);
1073 1074

            batch.emplace_back(trx);
1075
            if( batch.size() == 40 ) {
1076 1077 1078
               auto result = call( push_txns_func, batch );
               //std::cout << fc::json::to_pretty_string(result) << std::endl;
               batch.resize(0);
K
Kevin Heifner 已提交
1079
	            info = get_info();
1080 1081
            }
         }
P
Pravin 已提交
1082 1083 1084 1085 1086 1087 1088

	 if (batch.size() > 0) {
	       auto result = call( push_txns_func, batch );
               std::cout << fc::json::to_pretty_string(result) << std::endl;
               batch.resize(0);
               info = get_info();
	 }
1089 1090 1091 1092 1093 1094
         if( !loop ) break;
      }
   });

   

N
Nathan Hourt 已提交
1095
   // Push subcommand
1096
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
N
Nathan Hourt 已提交
1097 1098
   push->require_subcommand();

D
Daniel Larimer 已提交
1099
   // push actions
N
Nathan Hourt 已提交
1100 1101 1102 1103
   string contract;
   string action;
   string data;
   vector<string> scopes;
D
Daniel Larimer 已提交
1104 1105 1106
   auto actionsSubcommand = push->add_subcommand("actions", localized("Push a transaction with a single actions"));
   actionsSubcommand->fallthrough(false);
   actionsSubcommand->add_option("contract", contract,
1107
                                 localized("The account providing the contract to execute"), true)->required();
D
Daniel Larimer 已提交
1108
   actionsSubcommand->add_option("action", action, localized("The action to execute on the contract"), true)
N
Nathan Hourt 已提交
1109
         ->required();
D
Daniel Larimer 已提交
1110 1111
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
   actionsSubcommand->add_option("-p,--permission", permissions,
1112
                                 localized("An account and permission level to authorize, as in 'account@permission'"));
D
Daniel Larimer 已提交
1113 1114 1115 1116
   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 已提交
1117 1118 1119 1120 1121 1122 1123
      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);

1124
      auto accountPermissions = get_account_permissions(permissions);
1125

1126
      signed_transaction trx;
D
Daniel Larimer 已提交
1127 1128
      /* TODO: restore this function
      transaction_emplace_serialized_actions(trx, contract, action, accountPermissions,
1129
                                                      result.get_object()["binargs"].as<bytes>());
D
Daniel Larimer 已提交
1130
      */
1131 1132

      if (tx_force_unique) {
D
Daniel Larimer 已提交
1133
         trx.actions.emplace_back( generate_nonce() );
1134 1135
      }                                                      

D
Daniel Larimer 已提交
1136 1137 1138 1139
      for( const auto& scope : scopes ) {
         vector<string> subscopes;
         boost::split( subscopes, scope, boost::is_any_of( ", :" ) );
         for( const auto& s : subscopes )
D
Daniel Larimer 已提交
1140
            trx.write_scope.emplace_back(s);
D
Daniel Larimer 已提交
1141
      }
1142
      std::cout << fc::json::to_pretty_string(push_transaction(trx, !skip_sign )) << std::endl;
N
Nathan Hourt 已提交
1143 1144 1145 1146
   });

   // push transaction
   string trxJson;
1147 1148
   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 已提交
1149 1150 1151 1152
   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;
   });
1153

1154 1155

   string trxsJson;
1156 1157
   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();
1158 1159 1160 1161 1162
   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;
   });

1163 1164 1165 1166
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
1167 1168
   } catch (const explained_exception& e) {
      return 1;
1169
   } catch (const fc::exception& e) {
N
Nathan Hourt 已提交
1170
      auto errorString = e.to_detail_string();
1171 1172
      if (errorString.find("Connection refused") != string::npos) {
         if (errorString.find(fc::json::to_string(port)) != string::npos) {
1173
            std::cerr << localized("Failed to connect to eosd at ${ip}:${port}; is eosd running?", ("ip", host)("port", port)) << std::endl;
1174
         } else if (errorString.find(fc::json::to_string(wallet_port)) != string::npos) {
1175
            std::cerr << localized("Failed to connect to eos-walletd at ${ip}:${port}; is eos-walletd running?", ("ip", wallet_host)("port", wallet_port)) << std::endl;
1176
         } else {
1177
            std::cerr << localized("Failed to connect") << std::endl;
1178 1179 1180 1181
         }

         if (verbose_errors) {
            elog("connect error: ${e}", ("e", errorString));
1182 1183
         }
      } else {
1184
         // attempt to extract the error code if one is present
1185
         if (verbose_errors || !print_help_text(e)) {
1186
            elog("Failed with error: ${e}", ("e", verbose_errors ? e.to_detail_string() : e.to_string()));
1187
         }
1188
      }
1189 1190 1191 1192
      return 1;
   }

   return 0;
1193
}