main.cpp 45.7 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
  -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
  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
57 58
  OwnerKey TEXT               The owner public key for the new account
  ActiveKey TEXT              The active public key for the new account
59 60 61 62 63

Options:
  -s,--skip-signature         Specify that unlocked wallet keys should not be used to sign transaction
```
*/
64 65 66
#include <string>
#include <vector>
#include <boost/asio.hpp>
P
Pravin 已提交
67
#include <boost/format.hpp>
68 69 70
#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>
73
#include <eosio/utilities/key_conversion.hpp>
74

75
#include <eosio/chain/config.hpp>
76
#include <eosio/chain/wast_to_wasm.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
#include "httpc.hpp"
100

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

124 125 126 127
string program = "eosc";
string host = "localhost";
uint32_t port = 8888;

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

132 133 134 135 136
auto   tx_expiration = fc::seconds(30);
bool   tx_force_unique = false;
bool   tx_dont_broadcast = false;
bool   tx_skip_sign = false;
vector<string> tx_permission;
K
Kevin Heifner 已提交
137

M
Matias Romeo 已提交
138
void add_standard_transaction_options(CLI::App* cmd, string default_permission = "") {
139
   CLI::callback_t parse_exipration = [](CLI::results_t res) -> bool {
K
Kevin Heifner 已提交
140 141
      double value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
142 143 144
         return false;
      }
      
K
Kevin Heifner 已提交
145
      tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
146 147 148
      return true;
   };

K
Kevin Heifner 已提交
149
   cmd->add_option("-x,--expiration", parse_exipration, localized("set the time in seconds before a transaction expires, defaults to 30s"));
150
   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"));
151 152 153
   cmd->add_flag("-s,--skip-sign", tx_skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
   cmd->add_flag("-d,--dont-broadcast", tx_dont_broadcast, localized("don't broadcast transaction to the network (just print to stdout)"));

M
Matias Romeo 已提交
154 155 156 157
   string msg = "An account and permission level to authorize, as in 'account@permission'";
   if(!default_permission.empty())
      msg += " (defaults to '" + default_permission + "')";
   cmd->add_option("-p,--permission", tx_permission, localized(msg.c_str()));
158 159
}

K
Kevin Heifner 已提交
160 161
string generate_nonce_value() {
   return fc::to_string(fc::time_point::now().time_since_epoch().count());
162 163
}

K
Kevin Heifner 已提交
164
chain::action generate_nonce() {
165
   auto v = generate_nonce_value();
K
Kevin Heifner 已提交
166 167 168
   variant nonce = fc::mutable_variant_object()
         ("value", v);
   return chain::action( {}, config::system_account_name, "nonce", fc::raw::pack(nonce));
169
}
170

D
Daniel Larimer 已提交
171
vector<chain::permission_level> get_account_permissions(const vector<string>& permissions) {
172 173 174
   auto fixedPermissions = permissions | boost::adaptors::transformed([](const string& p) {
      vector<string> pieces;
      split(pieces, p, boost::algorithm::is_any_of("@"));
175
      EOSC_ASSERT(pieces.size() == 2, "Invalid permission: ${p}", ("p", p));
D
Daniel Larimer 已提交
176
      return chain::permission_level{ .actor = pieces[0], .permission = pieces[1] };
177
   });
D
Daniel Larimer 已提交
178
   vector<chain::permission_level> accountPermissions;
179 180 181
   boost::range::copy(fixedPermissions, back_inserter(accountPermissions));
   return accountPermissions;
}
182

183
template<typename T>
184
fc::variant call( const std::string& server, uint16_t port,
185
                  const std::string& path,
186
                  const T& v ) { return eosio::client::http::call( server, port, path, fc::variant(v) ); }
187

188 189
template<typename T>
fc::variant call( const std::string& path,
190
                  const T& v ) { return eosio::client::http::call( host, port, path, fc::variant(v) ); }
191

P
Pravin 已提交
192 193
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>();
194 195
}

196
void sign_transaction(signed_transaction& trx) {
K
Kevin Heifner 已提交
197 198 199
   // TODO better error checking
   const auto& public_keys = call(wallet_host, wallet_port, wallet_public_keys);
   auto get_arg = fc::mutable_variant_object
200
         ("transaction", (transaction)trx)
K
Kevin Heifner 已提交
201 202 203 204 205
         ("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);
206
   trx = signed_trx.as<signed_transaction>();
K
Kevin Heifner 已提交
207 208
}

M
Matias Romeo 已提交
209 210 211 212
fc::variant push_transaction( signed_transaction& trx, packed_transaction::compression_type compression = packed_transaction::none ) {
   auto info = get_info();
   trx.expiration = info.head_block_time + tx_expiration;
   trx.set_reference_block(info.head_block_id);
213

M
Matias Romeo 已提交
214
   if (tx_force_unique) {
K
Kevin Heifner 已提交
215
      trx.context_free_actions.emplace_back( generate_nonce() );
M
Matias Romeo 已提交
216
   }
K
Kevin Heifner 已提交
217

M
Matias Romeo 已提交
218 219 220 221 222 223 224 225 226
   if (!tx_skip_sign) {
      sign_transaction(trx);
   }

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

M
Matias Romeo 已提交
229
fc::variant push_actions(std::vector<chain::action>&& actions, packed_transaction::compression_type compression = packed_transaction::none ) {
230 231 232
   signed_transaction trx;
   trx.actions = std::forward<decltype(actions)>(actions);

M
Matias Romeo 已提交
233
   return push_transaction(trx, compression);
234 235
}

M
Matias Romeo 已提交
236 237
void send_actions(std::vector<chain::action>&& actions, packed_transaction::compression_type compression = packed_transaction::none ) {
   std::cout << fc::json::to_pretty_string(push_actions(std::forward<decltype(actions)>(actions), compression)) << std::endl;
238 239
}

M
Matias Romeo 已提交
240 241
void send_transaction( signed_transaction& trx, packed_transaction::compression_type compression = packed_transaction::none  ) {
   std::cout << fc::json::to_pretty_string(push_transaction(trx, compression)) << std::endl;
242
}
243

M
Matias Romeo 已提交
244 245 246 247 248 249 250 251 252 253 254
chain::action create_newaccount(const name& creator, const name& newaccount, public_key_type owner, public_key_type active) {
   return action {
      tx_permission.empty() ? vector<chain::permission_level>{{creator,config::active_name}} : get_account_permissions(tx_permission),
      contracts::newaccount{
         .creator      = creator, 
         .name         = newaccount, 
         .owner        = eosio::chain::authority{1, {{owner, 1}}, {}}, 
         .active       = eosio::chain::authority{1, {{active, 1}}, {}},
         .recovery     = eosio::chain::authority{1, {}, {{{creator, config::active_name}, 1}}}
      }
   };
255
}
256

M
Matias Romeo 已提交
257 258 259 260 261 262 263 264 265 266 267 268
chain::action create_transfer(const name& sender, const name& recipient, uint64_t amount, const string& memo ) {

   auto transfer = fc::mutable_variant_object
      ("from", sender)
      ("to", recipient)
      ("quantity", asset(amount))
      ("memo", memo);
   
   auto args = fc::mutable_variant_object
      ("code", name(config::system_account_name))
      ("action", "transfer")
      ("args", transfer);
269

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

M
Matias Romeo 已提交
272 273 274 275
   return action {
      tx_permission.empty() ? vector<chain::permission_level>{{sender,config::active_name}} : get_account_permissions(tx_permission),
      config::system_account_name, "transfer", result.get_object()["binargs"].as<bytes>()
   };
276
}
D
Daniel Larimer 已提交
277

M
Matias Romeo 已提交
278
chain::action create_setabi(const name& account, const contracts::abi_def& abi) {
279
   return action { 
M
Matias Romeo 已提交
280 281 282 283
      tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
      contracts::setabi{
         .account   = account,
         .abi       = abi
284 285
      }
   };
286 287
}

M
Matias Romeo 已提交
288 289 290 291 292 293 294 295
chain::action create_setcode(const name& account, const bytes& code) {
   return action { 
      tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
      contracts::setcode{
         .account   = account,
         .vmtype    = 0,
         .vmversion = 0,
         .code      = code 
296 297 298 299 300 301
      }
   };
}

chain::action create_updateauth(const name& account, const name& permission, const name& parent, const authority& auth) {
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
302
                   contracts::updateauth{account, permission, parent, auth}};
303 304
}

305 306
chain::action create_deleteauth(const name& account, const name& permission) {
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
307
                   contracts::deleteauth{account, permission}};
308 309
}

D
Daniel Larimer 已提交
310
chain::action create_linkauth(const name& account, const name& code, const name& type, const name& requirement) {
311
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
312
                   contracts::linkauth{account, code, type, requirement}};
313 314
}

D
Daniel Larimer 已提交
315
chain::action create_unlinkauth(const name& account, const name& code, const name& type) {
316
   return action { tx_permission.empty() ? vector<chain::permission_level>{{account,config::active_name}} : get_account_permissions(tx_permission),
D
Daniel Larimer 已提交
317
                   contracts::unlinkauth{account, code, type}};
318 319
}

320
struct set_account_permission_subcommand {
321 322 323 324 325
   string accountStr;
   string permissionStr;
   string authorityJsonOrFile;
   string parentStr;

326
   set_account_permission_subcommand(CLI::App* accountCmd) {
327 328 329 330 331
      auto permissions = accountCmd->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
      permissions->add_option("account", accountStr, localized("The account to set/delete a permission authority for"))->required();
      permissions->add_option("permission", permissionStr, localized("The permission name to set/delete an authority for"))->required();
      permissions->add_option("authority", authorityJsonOrFile, localized("[delete] NULL, [create/update] JSON string or filename defining the authority"))->required();
      permissions->add_option("parent", parentStr, localized("[create] The permission name of this parents permission (Defaults to: \"Active\")"));
332

M
Matias Romeo 已提交
333
      add_standard_transaction_options(permissions, "account@active");
334 335

      permissions->set_callback([this] {
336 337
         name account = name(accountStr);
         name permission = name(permissionStr);
338 339 340
         bool is_delete = boost::iequals(authorityJsonOrFile, "null");
         
         if (is_delete) {
M
Matias Romeo 已提交
341
            send_actions({create_deleteauth(account, permission)});
342
         } else {
D
Daniel Larimer 已提交
343
            authority auth;
344
            if (boost::istarts_with(authorityJsonOrFile, "EOS")) {
345 346 347
               try {
                  auth = authority(public_key_type(authorityJsonOrFile));
               } EOS_CAPTURE_AND_RETHROW(public_key_type_exception, "")
348
            } else {
349
               fc::variant parsedAuthority;
350 351 352 353 354 355 356 357 358
               try {
                  if (boost::istarts_with(authorityJsonOrFile, "{")) {
                     parsedAuthority = fc::json::from_string(authorityJsonOrFile);
                  } else {
                     parsedAuthority = fc::json::from_file(authorityJsonOrFile);
                  }
                  auth = parsedAuthority.as<authority>();
               } EOS_CAPTURE_AND_RETHROW(authority_type_exception, "Fail to parse Authority JSON")
                 
359 360
            }

361
            name parent;
362
            if (parentStr.size() == 0 && permissionStr != "owner") {
363
               // see if we can auto-determine the proper parent
K
Kevin Heifner 已提交
364
               const auto account_result = call(get_account_func, fc::mutable_variant_object("account_name", accountStr));
365 366 367 368 369 370 371 372 373
               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()) {
374
                  parent = name((*itr).get_object()["parent"].get_string());
375 376
               } else {
                  // if this is a new permission and there is no parent we default to "active"
377
                  parent = name(config::active_name);
378

379 380
               }
            } else {
381
               parent = name(parentStr);
382
            }
383

M
Matias Romeo 已提交
384
            send_actions({create_updateauth(account, permission, parent, auth)});
385 386 387 388 389
         }      
      });
   }
   
};
390

391
struct set_action_permission_subcommand {
392 393 394 395 396
   string accountStr;
   string codeStr;
   string typeStr;
   string requirementStr;

397
   set_action_permission_subcommand(CLI::App* actionRoot) {
398 399 400 401 402
      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();
403

M
Matias Romeo 已提交
404
      add_standard_transaction_options(permissions, "account@active");
405 406

      permissions->set_callback([this] {
407 408 409
         name account = name(accountStr);
         name code = name(codeStr);
         name type = name(typeStr);
410 411 412
         bool is_delete = boost::iequals(requirementStr, "null");
         
         if (is_delete) {
M
Matias Romeo 已提交
413
            send_actions({create_unlinkauth(account, code, type)});
414
         } else {
415
            name requirement = name(requirementStr);
M
Matias Romeo 已提交
416
            send_actions({create_linkauth(account, code, type, requirement)});
417 418 419 420
         }      
      });
   }
};
421

422
int main( int argc, char** argv ) {
D
Daniel Larimer 已提交
423
   fc::path binPath = argv[0];
424 425 426 427 428 429 430 431
   if (binPath.is_relative()) {
      binPath = relative(binPath, current_path()); 
   }

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

432
   CLI::App app{"Command Line Interface to Eos Client"};
433
   app.require_subcommand();
434 435 436 437
   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 );
438

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

442 443 444 445 446 447 448
   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;
   });

449
   // Create subcommand
450
   auto create = app.add_subcommand("create", localized("Create various items, on and off the blockchain"), false);
N
Nathan Hourt 已提交
451 452 453
   create->require_subcommand();

   // create key
D
Daniel Larimer 已提交
454 455 456 457 458 459
   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 已提交
460 461 462 463
   });

   // create account
   string creator;
464
   string account_name;
465 466
   string owner_key_str;
   string active_key_str;
M
Matias Romeo 已提交
467

468 469
   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();
470
   createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
471 472
   createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key for the new account"))->required();
   createAccount->add_option("ActiveKey", active_key_str, localized("The active public key for the new account"))->required();
M
Matias Romeo 已提交
473
   add_standard_transaction_options(createAccount, "creator@active");
N
Nathan Hourt 已提交
474
   createAccount->set_callback([&] {
475 476 477 478 479
      public_key_type owner_key, active_key;
      try {
         owner_key = public_key_type(owner_key_str);
         active_key = public_key_type(active_key_str);
      } EOS_CAPTURE_AND_RETHROW(public_key_type_exception, "Invalid Public Key")
M
Matias Romeo 已提交
480
      send_actions({create_newaccount(creator, account_name, owner_key, active_key)});
481 482
   });

483
   // Get subcommand
484
   auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"), false);
N
Nathan Hourt 已提交
485 486 487
   get->require_subcommand();

   // get info
488
   get->add_subcommand("info", localized("Get current blockchain information"))->set_callback([] {
N
Nathan Hourt 已提交
489 490 491 492 493
      std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
   });

   // get block
   string blockArg;
494 495
   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 已提交
496 497 498 499 500 501 502
   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;
503 504
   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 已提交
505 506
   getAccount->set_callback([&] {
      std::cout << fc::json::to_pretty_string(call(get_account_func,
K
Kevin Heifner 已提交
507
                                                   fc::mutable_variant_object("account_name", accountName)))
N
Nathan Hourt 已提交
508 509 510
                << std::endl;
   });

D
Daniel Larimer 已提交
511 512 513
   // get code
   string codeFilename;
   string abiFilename;
514 515 516 517
   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 已提交
518
   getCode->set_callback([&] {
K
Kevin Heifner 已提交
519
      auto result = call(get_code_func, fc::mutable_variant_object("account_name", accountName));
D
Daniel Larimer 已提交
520

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

      if( codeFilename.size() ){
524
         std::cout << localized("saving wast to ${codeFilename}", ("codeFilename", codeFilename)) << std::endl;
D
Daniel Larimer 已提交
525 526 527 528 529
         auto code = result["wast"].as_string();
         std::ofstream out( codeFilename.c_str() );
         out << code;
      }
      if( abiFilename.size() ) {
530
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
D
Daniel Larimer 已提交
531 532 533 534 535 536 537 538 539 540 541 542
         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;
543
   string table_key;
D
Daniel Larimer 已提交
544 545
   bool binary = false;
   uint32_t limit = 10;
546 547 548 549 550 551
   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") );
552
   getTable->add_option( "-k,--key", table_key, localized("The name of the key to index by as defined by the abi, defaults to primary key") );
553 554
   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 已提交
555 556 557 558 559 560

   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("scope",scope)
                         ("code",code)
                         ("table",table)
561 562 563 564
                         ("table_key",table_key)
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
D
Daniel Larimer 已提交
565 566 567 568 569 570
                         );

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

571 572 573 574 575 576 577 578 579
   // currency accessors
   // get currency balance
   string symbol;
   auto get_currency = get->add_subcommand( "currency", localized("Retrieve information related to standard currencies"), true);
   auto get_balance = get_currency->add_subcommand( "balance", localized("Retrieve the balance of an account for a given currency"), false);
   get_balance->add_option( "contract", code, localized("The contract that operates the currency") )->required();
   get_balance->add_option( "account", accountName, localized("The account to query balances for") )->required();
   get_balance->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") );
   get_balance->set_callback([&] {
580 581
      auto result = call(get_currency_balance_func, fc::mutable_variant_object("json", false)
         ("account", accountName)
582
         ("code", code)
583
         ("symbol", symbol)
584 585
      );

586
      const auto& rows = result.get_array();
587
      if (symbol.empty()) {
588 589 590 591
         std::cout << fc::json::to_pretty_string(rows)
                   << std::endl;
      } else if ( rows.size() > 0 ){
         std::cout << fc::json::to_pretty_string(rows[0])
592 593 594
                   << std::endl;
      }
   });
D
Daniel Larimer 已提交
595

596 597 598 599
   auto get_currency_stats = get_currency->add_subcommand( "stats", localized("Retrieve the stats of for a given currency"), false);
   get_currency_stats->add_option( "contract", code, localized("The contract that operates the currency") )->required();
   get_currency_stats->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") );
   get_currency_stats->set_callback([&] {
600
      auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
601
         ("code", code)
602
         ("symbol", symbol)
603 604 605
      );

      if (symbol.empty()) {
606
         std::cout << fc::json::to_pretty_string(result)
607 608
                   << std::endl;
      } else {
609 610 611
         const auto& mapping = result.get_object();
         std::cout << fc::json::to_pretty_string(mapping[symbol])
                   << std::endl;
612 613
      }
   });
D
Daniel Larimer 已提交
614

615
   // get accounts
616
   string publicKey;
617 618
   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();
619 620 621 622 623
   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;
   });

624

625 626
   // get servants
   string controllingAccount;
627 628
   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();
629
   getServants->set_callback([&] {
630
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
631 632 633
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

N
Nathan Hourt 已提交
634 635
   // get transaction
   string transactionId;
636 637
   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 已提交
638 639 640 641
   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;
   });
642

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


      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();
666 667 668 669
         const auto& data = trx["data"].get_object();
         const auto& exp  = data["expiration"].as<fc::time_point_sec>();
         const auto& msgs = data["actions"].get_array();
         std::cout << tobj["seq_num"].as_string() <<"] " << id << "  " << data["expiration"].as_string() << std::endl;
D
Daniel Larimer 已提交
670 671
      }

672 673
   });

674
   // set subcommand
675
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
676 677 678
   setSubcommand->require_subcommand();

   // set contract subcommand
N
Nathan Hourt 已提交
679 680 681
   string account;
   string wastPath;
   string abiPath;
682 683
   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();
684
   contractSubcommand->add_option("wast-file", wastPath, localized("The file containing the contract WAST or WASM"))->required()
N
Nathan Hourt 已提交
685
         ->check(CLI::ExistingFile);
686
   auto abi = contractSubcommand->add_option("abi-file,-a,--abi", abiPath, localized("The ABI for the contract"))
N
Nathan Hourt 已提交
687
              ->check(CLI::ExistingFile);
688
   
M
Matias Romeo 已提交
689
   add_standard_transaction_options(contractSubcommand, "account@active");
N
Nathan Hourt 已提交
690 691
   contractSubcommand->set_callback([&] {
      std::string wast;
692
      std::cout << localized("Reading WAST...") << std::endl;
N
Nathan Hourt 已提交
693
      fc::read_file_contents(wastPath, wast);
694 695

      vector<uint8_t> wasm;
696
      const string binary_wasm_header("\x00\x61\x73\x6d", 4);
697
      if(wast.compare(0, 4, binary_wasm_header) == 0) {
698
         std::cout << localized("Using already assembled WASM...") << std::endl;
699
         wasm = vector<uint8_t>(wast.begin(), wast.end());
700
      }
701 702
      else {
         std::cout << localized("Assembling WASM...") << std::endl;
703
         wasm = wast_to_wasm(wast);
704
      }
N
Nathan Hourt 已提交
705

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

K
Kevin Heifner 已提交
709
      if (abi->count()) {
710
         try {
M
Matias Romeo 已提交
711
            actions.emplace_back( create_setabi(account, fc::json::from_file(abiPath).as<contracts::abi_def>()) );
712
         } EOS_CAPTURE_AND_RETHROW(abi_type_exception,  "Fail to parse ABI JSON")
K
Kevin Heifner 已提交
713 714
      }

715
      std::cout << localized("Publishing contract...") << std::endl;
M
Matias Romeo 已提交
716
      send_actions(std::move(actions), packed_transaction::zlib);
N
Nathan Hourt 已提交
717
   });
N
Nathan Hourt 已提交
718

719
   // set account
720
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();
721 722

   // set account permission
723
   auto setAccountPermission = set_account_permission_subcommand(setAccount);
724 725

   // set action
726
   auto setAction = setSubcommand->add_subcommand("action", localized("set or update blockchain action state"))->require_subcommand();
727 728
   
   // set action permission
729
   auto setActionPermission = set_action_permission_subcommand(setAction);
730

N
Nathan Hourt 已提交
731
   // Transfer subcommand
N
Nathan Hourt 已提交
732 733 734
   string sender;
   string recipient;
   uint64_t amount;
735
   string memo;
736 737 738 739 740
   auto transfer = app.add_subcommand("transfer", localized("Transfer EOS from account to account"), false);
   transfer->add_option("sender", sender, localized("The account sending EOS"))->required();
   transfer->add_option("recipient", recipient, localized("The account receiving EOS"))->required();
   transfer->add_option("amount", amount, localized("The amount of EOS to send"))->required();
   transfer->add_option("memo", memo, localized("The memo for the transfer"));
K
Kevin Heifner 已提交
741

M
Matias Romeo 已提交
742
   add_standard_transaction_options(transfer, "sender@active");
N
Nathan Hourt 已提交
743
   transfer->set_callback([&] {
744 745 746 747 748
      signed_transaction trx;
      if (tx_force_unique && memo.size() == 0) {
         // use the memo to add a nonce
         memo = generate_nonce_value();
         tx_force_unique = false;
749
      }
M
Matias Romeo 已提交
750 751
      
      send_actions({create_transfer(sender, recipient, amount, memo)});
N
Nathan Hourt 已提交
752
   });
N
Nathan Hourt 已提交
753

754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
   // 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 已提交
786 787

   // Wallet subcommand
788
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
789
   wallet->require_subcommand();
K
Kevin Heifner 已提交
790
   // create wallet
D
Daniel Larimer 已提交
791
   string wallet_name = "default";
792 793
   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 已提交
794 795
   createWallet->set_callback([&wallet_name] {
      const auto& v = call(wallet_host, wallet_port, wallet_create, wallet_name);
796 797 798
      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 已提交
799 800 801 802
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   // open wallet
803 804
   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 已提交
805
   openWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
806 807
      /*const auto& v = */call(wallet_host, wallet_port, wallet_open, wallet_name);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
808
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
K
Kevin Heifner 已提交
809 810 811
   });

   // lock wallet
812 813
   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 已提交
814
   lockWallet->set_callback([&wallet_name] {
D
Daniel Larimer 已提交
815
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock, wallet_name);
816
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
D
Daniel Larimer 已提交
817 818
      //std::cout << fc::json::to_pretty_string(v) << std::endl;

K
Kevin Heifner 已提交
819 820 821
   });

   // lock all wallets
822
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
K
Kevin Heifner 已提交
823
   locakAllWallets->set_callback([] {
D
Daniel Larimer 已提交
824 825
      /*const auto& v = */call(wallet_host, wallet_port, wallet_lock_all);
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
826
      std::cout << localized("Locked All Wallets") << std::endl;
K
Kevin Heifner 已提交
827 828 829 830
   });

   // unlock wallet
   string wallet_pw;
831 832 833
   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 已提交
834
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
D
Daniel Larimer 已提交
835
      if( wallet_pw.size() == 0 ) {
836
         std::cout << localized("password: ");
D
Daniel Larimer 已提交
837 838 839 840 841 842
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_pw, '\n' );
         fc::set_console_echo(true);
      }


K
Kevin Heifner 已提交
843
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
D
Daniel Larimer 已提交
844
      /*const auto& v = */call(wallet_host, wallet_port, wallet_unlock, vs);
845
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
846
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
847 848 849 850
   });

   // import keys into wallet
   string wallet_key;
851 852 853
   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 已提交
854
   importWallet->set_callback([&wallet_name, &wallet_key] {
D
Daniel Larimer 已提交
855 856
      private_key_type key( wallet_key );
      public_key_type pubkey = key.get_public_key();
D
Daniel Larimer 已提交
857

K
Kevin Heifner 已提交
858 859
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
      const auto& v = call(wallet_host, wallet_port, wallet_import_key, vs);
860
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
D
Daniel Larimer 已提交
861
      //std::cout << fc::json::to_pretty_string(v) << std::endl;
K
Kevin Heifner 已提交
862 863 864
   });

   // list wallets
865
   auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
K
Kevin Heifner 已提交
866
   listWallet->set_callback([] {
867
      std::cout << localized("Wallets:") << std::endl;
K
Kevin Heifner 已提交
868 869 870 871 872
      const auto& v = call(wallet_host, wallet_port, wallet_list);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

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

879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
   // sign subcommand
   string trx_json_to_sign;
   string str_private_key;
   bool push_trx = false;

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

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

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

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

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

N
Nathan Hourt 已提交
916
   // Push subcommand
917
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
N
Nathan Hourt 已提交
918 919
   push->require_subcommand();

920
   // push action
N
Nathan Hourt 已提交
921 922 923
   string contract;
   string action;
   string data;
924
   vector<string> permissions;
925
   auto actionsSubcommand = push->add_subcommand("action", localized("Push a transaction with a single action"));
D
Daniel Larimer 已提交
926 927
   actionsSubcommand->fallthrough(false);
   actionsSubcommand->add_option("contract", contract,
928
                                 localized("The account providing the contract to execute"), true)->required();
D
Daniel Larimer 已提交
929
   actionsSubcommand->add_option("action", action, localized("The action to execute on the contract"), true)
N
Nathan Hourt 已提交
930
         ->required();
D
Daniel Larimer 已提交
931
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();
932

D
Daniel Larimer 已提交
933 934
   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
N
Nathan Hourt 已提交
935
      ilog("Converting argument to binary...");
936 937 938 939 940
      fc::variant action_args_var;
      try {
         action_args_var = fc::json::from_string(data);
      } EOS_CAPTURE_AND_RETHROW(action_type_exception, "Fail to parse action JSON")

N
Nathan Hourt 已提交
941 942 943
      auto arg= fc::mutable_variant_object
                ("code", contract)
                ("action", action)
944
                ("args", action_args_var);
N
Nathan Hourt 已提交
945 946
      auto result = call(json_to_bin_func, arg);

947
      auto accountPermissions = get_account_permissions(tx_permission);
948

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

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

N
Nathan Hourt 已提交
957
   trxSubcommand->set_callback([&] {
958 959
      fc::variant trx_var;
      try {
M
Matias Romeo 已提交
960 961 962 963 964 965
         if ( is_regular_file(trx_to_push) ) {
            trx_var = fc::json::from_file(trx_to_push);
         } else {
            trx_var = fc::json::from_string(trx_to_push);
         }
      } EOS_CAPTURE_AND_RETHROW(transaction_type_exception, "Fail to parse transaction JSON")      signed_transaction trx = trx_var.as<signed_transaction>();
966
      auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
N
Nathan Hourt 已提交
967 968
      std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
   });
969

970 971

   string trxsJson;
972 973
   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();
974
   trxsSubcommand->set_callback([&] {
975 976
      fc::variant trx_var;
      try {
977
         trx_var = fc::json::from_string(trxsJson);
978
      } EOS_CAPTURE_AND_RETHROW(transaction_type_exception, "Fail to parse transaction JSON")
979
      auto trxs_result = call(push_txns_func, trx_var);
980 981 982
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });

983 984 985 986
   try {
       app.parse(argc, argv);
   } catch (const CLI::ParseError &e) {
       return app.exit(e);
987 988
   } catch (const explained_exception& e) {
      return 1;
989
   } catch (const fc::exception& e) {
N
Nathan Hourt 已提交
990
      auto errorString = e.to_detail_string();
991 992
      if (errorString.find("Connection refused") != string::npos) {
         if (errorString.find(fc::json::to_string(port)) != string::npos) {
993
            std::cerr << localized("Failed to connect to eosd at ${ip}:${port}; is eosd running?", ("ip", host)("port", port)) << std::endl;
994
         } else if (errorString.find(fc::json::to_string(wallet_port)) != string::npos) {
995
            std::cerr << localized("Failed to connect to eos-walletd at ${ip}:${port}; is eos-walletd running?", ("ip", wallet_host)("port", wallet_port)) << std::endl;
996
         } else {
997
            std::cerr << localized("Failed to connect") << std::endl;
998 999 1000 1001
         }

         if (verbose_errors) {
            elog("connect error: ${e}", ("e", errorString));
1002 1003
         }
      } else {
1004
         // attempt to extract the error code if one is present
1005 1006 1007 1008 1009
         if (!print_recognized_errors(e, verbose_errors)) {
            // Error is not recognized
            if (!print_help_text(e) || verbose_errors) {
               elog("Failed with error: ${e}", ("e", verbose_errors ? e.to_detail_string() : e.to_string()));
            }
1010
         }
1011
      }
1012 1013 1014 1015
      return 1;
   }

   return 0;
1016
}