Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #include "interactive/interactive_transaction_cli.hpp"
7 :
8 : #include <fstream>
9 : #include <utility>
10 :
11 : #include "backend/protobuf/transaction.hpp"
12 : #include "client.hpp"
13 : #include "grpc_response_handler.hpp"
14 : #include "logger/logger.hpp"
15 : #include "model/commands/append_role.hpp"
16 : #include "model/commands/create_role.hpp"
17 : #include "model/commands/detach_role.hpp"
18 : #include "model/commands/grant_permission.hpp"
19 : #include "model/commands/revoke_permission.hpp"
20 : #include "model/commands/set_account_detail.hpp"
21 : #include "model/converters/json_common.hpp"
22 : #include "model/converters/json_transaction_factory.hpp"
23 : #include "model/converters/pb_common.hpp"
24 : #include "model/converters/pb_transaction_factory.hpp"
25 : #include "model/model_crypto_provider.hpp" // for ModelCryptoProvider
26 : #include "model/sha3_hash.hpp"
27 : #include "parser/parser.hpp" // for parser::ParseValue
28 : #include "validators/permissions.hpp"
29 :
30 : using namespace iroha::model;
31 : using namespace shared_model::permissions;
32 :
33 : namespace iroha_cli {
34 : namespace interactive {
35 :
36 : void InteractiveTransactionCli::createCommandMenu() {
37 0 : commands_description_map_ = {
38 0 : {ADD_ASSET_QTY, "Add Asset Quantity"},
39 0 : {ADD_PEER, "Add Peer to Iroha Network"},
40 0 : {ADD_SIGN, "Add Signatory to Account"},
41 0 : {CREATE_ACC, "Create Account"},
42 0 : {CREATE_DOMAIN, "Create Domain"},
43 0 : {CREATE_ASSET, "Create Asset"},
44 0 : {REMOVE_SIGN, "Remove Signatory"},
45 0 : {SET_QUO, "Set Account Quorum"},
46 0 : {SUB_ASSET_QTY, "Subtract Assets Quantity"},
47 0 : {TRAN_ASSET, "Transfer Assets"},
48 0 : {CREATE_ROLE, "Create new role"},
49 0 : {APPEND_ROLE, "Add new role to account"},
50 0 : {DETACH_ROLE, "Detach role from account"},
51 0 : {GRANT_PERM, "Grant permission over your account"},
52 0 : {REVOKE_PERM, "Revoke permission from account"},
53 0 : {SET_ACC_KV, "Set account key/value detail"}
54 : // commands_description_map_
55 : };
56 :
57 0 : const auto acc_id = "Account Id";
58 0 : const auto ast_id = "Asset Id";
59 0 : const auto dom_id = "Domain Id";
60 0 : const auto add_amount_str = "Amount to add, e.g 123.456";
61 0 : const auto sub_amount_str = "Amount to subtract, e.g 123.456";
62 0 : const auto transfer_amount_str = "Amount to transfer, e.g 123.456";
63 0 : const auto peer_id = "Full address of a peer";
64 0 : const auto pub_key = "Public Key";
65 0 : const auto acc_name = "Account Name";
66 0 : const auto ast_name = "Asset name";
67 0 : const auto ast_precision = "Asset precision";
68 0 : const auto quorum = "Quorum";
69 0 : const auto role = "Role name";
70 0 : const auto perm = "Permission name";
71 0 :
72 0 : const auto can_read_self = "Can read all information about their account";
73 0 : const auto can_edit_self = "Can change their quorum/signatory";
74 0 : const auto can_read_all = "Can read all other accounts";
75 0 : const auto can_transfer_receive = "Can receive/transfer assets";
76 0 : const auto can_asset_creator = "Can create/add new assets";
77 0 : const auto can_roles = "Can create/append roles";
78 0 :
79 0 : command_params_map_ = {
80 0 : {ADD_ASSET_QTY, makeParamsDescription({ast_id, add_amount_str})},
81 0 : {ADD_PEER, makeParamsDescription({peer_id, pub_key})},
82 0 : {ADD_SIGN, makeParamsDescription({acc_id, pub_key})},
83 0 : {CREATE_ACC, makeParamsDescription({acc_name, dom_id, pub_key})},
84 0 : {CREATE_DOMAIN,
85 0 : makeParamsDescription({dom_id, std::string("Default ") + role})},
86 0 : {CREATE_ASSET,
87 0 : makeParamsDescription({ast_name, dom_id, ast_precision})},
88 0 : {REMOVE_SIGN, makeParamsDescription({acc_id, pub_key})},
89 0 : {SET_QUO, makeParamsDescription({acc_id, quorum})},
90 0 : {SUB_ASSET_QTY, makeParamsDescription({ast_id, sub_amount_str})},
91 0 : {TRAN_ASSET,
92 0 : makeParamsDescription({std::string("Src") + acc_id,
93 0 : std::string("Dest") + acc_id,
94 0 : ast_id,
95 0 : transfer_amount_str})},
96 0 : {CREATE_ROLE,
97 0 : makeParamsDescription({role,
98 0 : can_read_self,
99 0 : can_edit_self,
100 0 : can_read_all,
101 0 : can_transfer_receive,
102 0 : can_asset_creator,
103 0 : can_create_domain,
104 0 : can_roles,
105 0 : can_create_account})},
106 0 : {APPEND_ROLE, makeParamsDescription({acc_id, role})},
107 0 : {DETACH_ROLE, makeParamsDescription({acc_id, role})},
108 0 : {GRANT_PERM, makeParamsDescription({acc_id, perm})},
109 0 : {REVOKE_PERM, makeParamsDescription({acc_id, perm})},
110 0 : {SET_ACC_KV, makeParamsDescription({acc_id, "key", "value"})}
111 : // command parameters descriptions
112 : };
113 :
114 0 : command_handlers_ = {
115 0 : {ADD_ASSET_QTY, &InteractiveTransactionCli::parseAddAssetQuantity},
116 0 : {ADD_PEER, &InteractiveTransactionCli::parseAddPeer},
117 0 : {ADD_SIGN, &InteractiveTransactionCli::parseAddSignatory},
118 0 : {CREATE_ACC, &InteractiveTransactionCli::parseCreateAccount},
119 0 : {CREATE_DOMAIN, &InteractiveTransactionCli::parseCreateDomain},
120 0 : {CREATE_ASSET, &InteractiveTransactionCli::parseCreateAsset},
121 0 : {REMOVE_SIGN, &InteractiveTransactionCli::parseRemoveSignatory},
122 0 : {SET_QUO, &InteractiveTransactionCli::parseSetQuorum},
123 0 : {SUB_ASSET_QTY,
124 0 : &InteractiveTransactionCli::parseSubtractAssetQuantity},
125 0 : {TRAN_ASSET, &InteractiveTransactionCli::parseTransferAsset},
126 0 : {CREATE_ROLE, &InteractiveTransactionCli::parseCreateRole},
127 0 : {APPEND_ROLE, &InteractiveTransactionCli::parseAppendRole},
128 0 : {DETACH_ROLE, &InteractiveTransactionCli::parseDetachRole},
129 0 : {GRANT_PERM, &InteractiveTransactionCli::parseGrantPermission},
130 0 : {REVOKE_PERM, &InteractiveTransactionCli::parseGrantPermission},
131 0 : {SET_ACC_KV, &InteractiveTransactionCli::parseSetAccountDetail}
132 : // Command parsers
133 : };
134 :
135 0 : commands_menu_ = formMenu(
136 0 : command_handlers_, command_params_map_, commands_description_map_);
137 : // Add "go back" option
138 0 : addBackOption(commands_menu_);
139 0 : }
140 :
141 : void InteractiveTransactionCli::createResultMenu() {
142 : // --- Add result menu points ---
143 :
144 0 : auto result_desciption = getCommonDescriptionMap();
145 0 : const auto ADD_CMD = "add";
146 :
147 0 : result_desciption.insert(
148 0 : {ADD_CMD, "Add one more command to the transaction"});
149 0 : result_desciption.insert(
150 0 : {BACK_CODE, "Go back and start a new transaction"});
151 :
152 0 : result_params_map = getCommonParamsMap(default_peer_ip_, default_port_);
153 :
154 0 : result_params_map.insert({ADD_CMD, {}});
155 0 : result_params_map.insert({BACK_CODE, {}});
156 :
157 0 : result_handlers_ = {
158 0 : {SAVE_CODE, &InteractiveTransactionCli::parseSaveFile},
159 0 : {SEND_CODE, &InteractiveTransactionCli::parseSendToIroha},
160 0 : {ADD_CMD, &InteractiveTransactionCli::parseAddCommand},
161 0 : {BACK_CODE, &InteractiveTransactionCli::parseGoBack}
162 : // Parsers for result
163 : };
164 :
165 0 : result_menu_ =
166 0 : formMenu(result_handlers_, result_params_map, result_desciption);
167 0 : }
168 :
169 : InteractiveTransactionCli::InteractiveTransactionCli(
170 : const std::string &creator_account,
171 : const std::string &default_peer_ip,
172 : int default_port,
173 : const std::shared_ptr<iroha::model::ModelCryptoProvider> &provider,
174 : logger::LoggerManagerTreePtr response_handler_log_manager,
175 : logger::LoggerPtr pb_qry_factory_log,
176 : logger::LoggerPtr log)
177 0 : : current_context_(MAIN),
178 0 : creator_(creator_account),
179 0 : default_peer_ip_(default_peer_ip),
180 0 : default_port_(default_port),
181 0 : provider_(provider),
182 0 : response_handler_log_manager_(
183 0 : std::move(response_handler_log_manager)),
184 0 : pb_qry_factory_log_(std::move(pb_qry_factory_log)),
185 0 : log_(std::move(log)) {
186 0 : createCommandMenu();
187 0 : createResultMenu();
188 0 : }
189 :
190 : void InteractiveTransactionCli::run() {
191 0 : bool is_parsing = true;
192 0 : current_context_ = MAIN;
193 0 : printMenu("Forming a new transactions, choose command to add: ",
194 0 : commands_menu_);
195 : // Creating a new transaction
196 0 : while (is_parsing) {
197 0 : auto line = promptString("> ");
198 0 : if (not line) {
199 : // The promtSting returns error, terminating symbol
200 0 : is_parsing = false;
201 0 : break;
202 : }
203 0 : switch (current_context_) {
204 : case MAIN:
205 0 : is_parsing = parseCommand(line.value());
206 0 : break;
207 : case RESULT:
208 0 : is_parsing = parseResult(line.value());
209 0 : break;
210 : default:
211 : // shouldn't get here
212 0 : BOOST_ASSERT_MSG(false, "not implemented");
213 : break;
214 : }
215 0 : }
216 0 : }
217 :
218 : bool InteractiveTransactionCli::parseCommand(std::string line) {
219 0 : if (isBackOption(line)) {
220 : // Switch current context
221 0 : return false;
222 : }
223 :
224 0 : auto res = handleParse<std::shared_ptr<iroha::model::Command>>(
225 0 : this, line, command_handlers_, command_params_map_);
226 :
227 0 : if (not res) {
228 : // Continue parsing
229 0 : return true;
230 : }
231 :
232 0 : commands_.push_back(res.value());
233 0 : current_context_ = RESULT;
234 0 : printMenu("Command is formed. Choose what to do:", result_menu_);
235 0 : return true;
236 0 : }
237 :
238 : std::shared_ptr<iroha::model::Command>
239 : InteractiveTransactionCli::parseCreateRole(
240 : std::vector<std::string> params) {
241 0 : auto role = params[0];
242 0 : auto read_self = parser::parseValue<bool>(params[1]);
243 0 : auto edit_self = parser::parseValue<bool>(params[2]);
244 0 : auto read_all = parser::parseValue<bool>(params[3]);
245 0 : auto transfer_receive = parser::parseValue<bool>(params[4]);
246 0 : auto asset_create = parser::parseValue<bool>(params[5]);
247 0 : auto create_domain = parser::parseValue<bool>(params[6]);
248 0 : auto roles = parser::parseValue<bool>(params[7]);
249 0 : auto create_account = parser::parseValue<bool>(params[8]);
250 :
251 0 : if (not(read_self and edit_self and read_all and transfer_receive
252 0 : and asset_create and create_domain and roles
253 0 : and create_account)) {
254 0 : std::cout << "Wrong format for permission" << std::endl;
255 0 : return nullptr;
256 : }
257 :
258 0 : std::set<std::string> perms;
259 0 : if (read_self.value()) {
260 0 : perms.insert(read_self_group.begin(), read_self_group.end());
261 0 : }
262 0 : if (edit_self.value()) {
263 0 : perms.insert(edit_self_group.begin(), edit_self_group.end());
264 0 : perms.insert(grant_group.begin(), grant_group.end());
265 0 : }
266 :
267 0 : if (read_all.value()) {
268 0 : perms.insert(read_all_group.begin(), read_all_group.end());
269 0 : }
270 0 : if (transfer_receive.value()) {
271 0 : perms.insert(can_transfer);
272 0 : perms.insert(can_receive);
273 0 : }
274 0 : if (asset_create.value()) {
275 0 : perms.insert(asset_creator_group.begin(), asset_creator_group.end());
276 0 : }
277 0 : if (create_domain.value()) {
278 0 : perms.insert(can_create_domain);
279 0 : }
280 0 : if (roles.value()) {
281 0 : perms.insert(can_create_role);
282 0 : perms.insert(can_append_role);
283 0 : }
284 0 : if (create_account.value()) {
285 0 : perms.insert(can_create_account);
286 0 : }
287 :
288 0 : return std::make_shared<CreateRole>(role, perms);
289 0 : }
290 :
291 : std::shared_ptr<iroha::model::Command>
292 : InteractiveTransactionCli::parseAppendRole(
293 : std::vector<std::string> params) {
294 0 : auto acc_id = params[0];
295 0 : auto role = params[1];
296 0 : return std::make_shared<AppendRole>(acc_id, role);
297 0 : }
298 :
299 : std::shared_ptr<iroha::model::Command>
300 : InteractiveTransactionCli::parseDetachRole(
301 : std::vector<std::string> params) {
302 0 : auto acc_id = params[0];
303 0 : auto role = params[1];
304 0 : return std::make_shared<DetachRole>(acc_id, role);
305 0 : }
306 :
307 : std::shared_ptr<iroha::model::Command>
308 : InteractiveTransactionCli::parseGrantPermission(
309 : std::vector<std::string> params) {
310 0 : auto acc_id = params[0];
311 0 : auto permission = params[1];
312 0 : return std::make_shared<GrantPermission>(acc_id, permission);
313 0 : }
314 :
315 : std::shared_ptr<iroha::model::Command>
316 : InteractiveTransactionCli::parseRevokePermission(
317 : std::vector<std::string> params) {
318 0 : auto acc_id = params[0];
319 0 : auto permission = params[1];
320 0 : return std::make_shared<RevokePermission>(acc_id, permission);
321 0 : }
322 :
323 : std::shared_ptr<iroha::model::Command>
324 : InteractiveTransactionCli::parseAddAssetQuantity(
325 : std::vector<std::string> params) {
326 0 : auto asset_id = params[0];
327 0 : auto amount = params[1];
328 0 : return generator_.generateAddAssetQuantity(asset_id, amount);
329 0 : }
330 :
331 : std::shared_ptr<iroha::model::Command>
332 : InteractiveTransactionCli::parseAddPeer(std::vector<std::string> params) {
333 0 : auto address = params[0];
334 0 : auto key = params[1];
335 0 : iroha::pubkey_t pubkey;
336 0 : pubkey = iroha::hexstringToArray<iroha::pubkey_t::size()>(key).value();
337 0 : return generator_.generateAddPeer(Peer(address, pubkey));
338 0 : }
339 :
340 : std::shared_ptr<iroha::model::Command>
341 : InteractiveTransactionCli::parseAddSignatory(
342 : std::vector<std::string> params) {
343 0 : auto account_id = params[0];
344 0 : auto key = params[1];
345 0 : iroha::pubkey_t pubkey;
346 0 : pubkey = iroha::hexstringToArray<iroha::pubkey_t::size()>(key).value();
347 0 : return generator_.generateAddSignatory(account_id, pubkey);
348 0 : }
349 :
350 : std::shared_ptr<iroha::model::Command>
351 : InteractiveTransactionCli::parseCreateAccount(
352 : std::vector<std::string> params) {
353 0 : auto account_id = params[0];
354 0 : auto domain_id = params[1];
355 0 : auto key = params[2];
356 0 : iroha::pubkey_t pubkey;
357 0 : pubkey = iroha::hexstringToArray<iroha::pubkey_t::size()>(key).value();
358 0 : return generator_.generateCreateAccount(account_id, domain_id, pubkey);
359 0 : }
360 :
361 : std::shared_ptr<iroha::model::Command>
362 : InteractiveTransactionCli::parseCreateDomain(
363 : std::vector<std::string> params) {
364 0 : auto domain_id = params[0];
365 0 : auto user_default_role = params[1];
366 0 : return generator_.generateCreateDomain(domain_id, user_default_role);
367 0 : }
368 :
369 : std::shared_ptr<iroha::model::Command>
370 : InteractiveTransactionCli::parseCreateAsset(
371 : std::vector<std::string> params) {
372 0 : auto asset_name = params[0];
373 0 : auto domain_id = params[1];
374 0 : auto val = parser::parseValue<uint32_t>(params[2]);
375 0 : if (not val) {
376 0 : std::cout << "Wrong format for precision" << std::endl;
377 0 : return nullptr;
378 : }
379 0 : return generator_.generateCreateAsset(asset_name, domain_id, val.value());
380 0 : }
381 :
382 : std::shared_ptr<iroha::model::Command>
383 : InteractiveTransactionCli::parseRemoveSignatory(
384 : std::vector<std::string> params) {
385 0 : auto account_id = params[0];
386 0 : auto key = params[1];
387 0 : iroha::pubkey_t pubkey;
388 0 : pubkey = iroha::hexstringToArray<iroha::pubkey_t::size()>(key).value();
389 0 : return generator_.generateRemoveSignatory(account_id, pubkey);
390 0 : }
391 :
392 : std::shared_ptr<iroha::model::Command>
393 : InteractiveTransactionCli::parseSetQuorum(std::vector<std::string> params) {
394 0 : auto account_id = params[0];
395 0 : auto quorum = parser::parseValue<uint64_t>(params[1]);
396 0 : if (not quorum) {
397 0 : std::cout << "Wrong format for quorum" << std::endl;
398 0 : return nullptr;
399 : }
400 0 : return generator_.generateSetQuorum(account_id, quorum.value());
401 0 : }
402 :
403 : std::shared_ptr<iroha::model::Command>
404 : InteractiveTransactionCli::parseSubtractAssetQuantity(
405 : std::vector<std::string> params) {
406 0 : auto asset_id = params[0];
407 0 : auto amount = params[1];
408 0 : return generator_.generateSubtractAssetQuantity(asset_id, amount);
409 0 : }
410 :
411 : std::shared_ptr<iroha::model::Command>
412 : InteractiveTransactionCli::parseTransferAsset(
413 : std::vector<std::string> params) {
414 0 : auto src_account_id = params[0];
415 0 : auto dest_account_id = params[1];
416 0 : auto asset_id = params[2];
417 0 : auto amount = params[3];
418 0 : return generator_.generateTransferAsset(
419 : src_account_id, dest_account_id, asset_id, amount);
420 0 : }
421 :
422 : std::shared_ptr<iroha::model::Command>
423 : InteractiveTransactionCli::parseSetAccountDetail(
424 : std::vector<std::string> params) {
425 0 : auto account_id = params[0];
426 0 : auto key = params[1];
427 0 : auto value = params[2];
428 0 : return std::make_shared<SetAccountDetail>(account_id, key, value);
429 0 : }
430 :
431 : // --------- Result parsers -------------
432 :
433 : bool InteractiveTransactionCli::parseResult(std::string line) {
434 : // Find in result handler map
435 : auto res =
436 0 : handleParse<bool>(this, line, result_handlers_, result_params_map);
437 0 : return res.get_value_or(true);
438 : }
439 :
440 : bool InteractiveTransactionCli::parseSendToIroha(
441 : std::vector<std::string> params) {
442 0 : auto address = parseIrohaPeerParams(
443 0 : std::move(params), default_peer_ip_, default_port_);
444 0 : if (not address) {
445 0 : return true;
446 : }
447 :
448 : // Forming a transaction
449 :
450 0 : auto tx = tx_generator_.generateTransaction(creator_, commands_);
451 : // clear commands so that we can start creating new tx
452 0 : commands_.clear();
453 :
454 0 : provider_->sign(tx);
455 :
456 0 : GrpcResponseHandler response_handler(response_handler_log_manager_);
457 0 : auto shared_tx = shared_model::proto::Transaction(
458 0 : iroha::model::converters::PbTransactionFactory().serialize(tx));
459 0 : response_handler.handle(CliClient(address.value().first,
460 0 : address.value().second,
461 0 : pb_qry_factory_log_)
462 0 : .sendTx(shared_tx));
463 :
464 0 : printTxHash(tx);
465 0 : printEnd();
466 : // Stop parsing
467 0 : return false;
468 0 : }
469 :
470 : bool InteractiveTransactionCli::parseSaveFile(
471 : std::vector<std::string> params) {
472 0 : auto path = params[0];
473 0 : std::ofstream output_file(path);
474 0 : if (not output_file) {
475 0 : std::cout << "Wrong path" << std::endl;
476 : // Continue parsing
477 0 : return true;
478 : }
479 :
480 : // Forming a transaction
481 0 : auto tx = tx_generator_.generateTransaction(creator_, commands_);
482 :
483 : // clear commands so that we can start creating new tx
484 0 : commands_.clear();
485 :
486 0 : provider_->sign(tx);
487 :
488 0 : iroha::model::converters::JsonTransactionFactory json_factory;
489 0 : auto json_doc = json_factory.serialize(tx);
490 0 : auto json_string = iroha::model::converters::jsonToString(json_doc);
491 0 : output_file << json_string;
492 0 : std::cout << "Successfully saved!" << std::endl;
493 0 : printEnd();
494 : // Stop parsing
495 0 : return false;
496 0 : }
497 :
498 : bool InteractiveTransactionCli::parseGoBack(
499 : std::vector<std::string> params) {
500 0 : current_context_ = MAIN;
501 : // Remove all old commands
502 0 : commands_.clear();
503 0 : printEnd();
504 0 : printMenu("Forming a new transaction. Choose command to add: ",
505 0 : commands_menu_);
506 : // Continue parsing
507 0 : return true;
508 0 : }
509 :
510 : bool InteractiveTransactionCli::parseAddCommand(
511 : std::vector<std::string> params) {
512 0 : current_context_ = MAIN;
513 0 : printEnd();
514 0 : printMenu("Choose command to add: ", commands_menu_);
515 : // Continue parsing
516 0 : return true;
517 0 : }
518 :
519 : void InteractiveTransactionCli::printTxHash(iroha::model::Transaction &tx) {
520 : std::cout
521 0 : << "Congratulation, your transaction was accepted for processing."
522 0 : << std::endl;
523 0 : std::cout << "Its hash is " << iroha::hash(tx).to_hexstring()
524 0 : << std::endl;
525 0 : }
526 :
527 : } // namespace interactive
528 : } // namespace iroha_cli
|