Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #include "ametsuchi/impl/temporary_wsv_impl.hpp"
7 :
8 : #include <boost/format.hpp>
9 : #include "ametsuchi/impl/postgres_command_executor.hpp"
10 : #include "cryptography/public_key.hpp"
11 : #include "interfaces/commands/command.hpp"
12 : #include "interfaces/permission_to_string.hpp"
13 : #include "interfaces/transaction.hpp"
14 :
15 : namespace iroha {
16 : namespace ametsuchi {
17 : TemporaryWsvImpl::TemporaryWsvImpl(
18 : std::unique_ptr<soci::session> sql,
19 : std::shared_ptr<shared_model::interface::CommonObjectsFactory> factory,
20 : std::shared_ptr<shared_model::interface::PermissionToString>
21 : perm_converter)
22 712 : : sql_(std::move(sql)),
23 712 : command_executor_(std::make_unique<PostgresCommandExecutor>(
24 712 : *sql_, std::move(perm_converter))),
25 712 : log_(logger::log("TemporaryWSV")) {
26 712 : *sql_ << "BEGIN";
27 712 : }
28 :
29 : expected::Result<void, validation::CommandError>
30 : TemporaryWsvImpl::validateSignatures(
31 : const shared_model::interface::Transaction &transaction) {
32 725 : auto keys_range = transaction.signatures()
33 725 : | boost::adaptors::transformed(
34 : [](const auto &s) { return s.publicKey().hex(); });
35 725 : auto keys = std::accumulate(
36 725 : std::next(std::begin(keys_range)),
37 725 : std::end(keys_range),
38 725 : keys_range.front(),
39 : [](auto acc, const auto &val) { return acc + "'), ('" + val; });
40 : // not using bool since it is not supported by SOCI
41 725 : boost::optional<uint8_t> signatories_valid;
42 :
43 725 : boost::format query(R"(SELECT sum(count) = :signatures_count
44 : AND sum(quorum) <= :signatures_count
45 : FROM
46 : (SELECT count(public_key)
47 : FROM ( VALUES ('%s') ) AS CTE1(public_key)
48 : WHERE public_key IN
49 : (SELECT public_key
50 : FROM account_has_signatory
51 : WHERE account_id = :account_id ) ) AS CTE2(count),
52 : (SELECT quorum
53 : FROM account
54 : WHERE account_id = :account_id) AS CTE3(quorum))");
55 :
56 : try {
57 725 : *sql_ << (query % keys).str(), soci::into(signatories_valid),
58 725 : soci::use(boost::size(keys_range), "signatures_count"),
59 725 : soci::use(transaction.creatorAccountId(), "account_id");
60 725 : } catch (const std::exception &e) {
61 0 : auto error_str = "Transaction " + transaction.toString()
62 0 : + " failed signatures validation with db error: " + e.what();
63 : // TODO [IR-1816] Akvinikym 29.10.18: substitute error code magic number
64 : // with named constant
65 0 : return expected::makeError(validation::CommandError{
66 0 : "signatures validation", 1, error_str, false});
67 0 : }
68 :
69 725 : if (signatories_valid and *signatories_valid) {
70 719 : return {};
71 : } else {
72 6 : auto error_str = "Transaction " + transaction.toString()
73 6 : + " failed signatures validation";
74 : // TODO [IR-1816] Akvinikym 29.10.18: substitute error code magic number
75 : // with named constant
76 6 : return expected::makeError(validation::CommandError{
77 6 : "signatures validation", 2, error_str, false});
78 6 : }
79 725 : }
80 :
81 : expected::Result<void, validation::CommandError> TemporaryWsvImpl::apply(
82 : const shared_model::interface::Transaction &transaction) {
83 725 : const auto &tx_creator = transaction.creatorAccountId();
84 725 : command_executor_->setCreatorAccountId(tx_creator);
85 725 : command_executor_->doValidation(true);
86 : auto execute_command =
87 : [this](auto &command) -> expected::Result<void, CommandError> {
88 : // Validate and execute command
89 1893 : return boost::apply_visitor(*command_executor_, command.get());
90 : };
91 :
92 : auto savepoint_wrapper = createSavepoint("savepoint_temp_wsv");
93 :
94 725 : return validateSignatures(transaction) |
95 : [savepoint = std::move(savepoint_wrapper),
96 : &execute_command,
97 725 : &transaction]()
98 : -> expected::Result<void, validation::CommandError> {
99 : // check transaction's commands validity
100 719 : const auto &commands = transaction.commands();
101 719 : validation::CommandError cmd_error;
102 : for (size_t i = 0; i < commands.size(); ++i) {
103 : // in case of failed command, rollback and return
104 1893 : auto cmd_is_valid =
105 1893 : execute_command(commands[i])
106 : .match([](expected::Value<void> &) { return true; },
107 : [i, &cmd_error](expected::Error<CommandError> &error) {
108 50 : cmd_error = {error.error.command_name,
109 50 : error.error.error_code,
110 50 : error.error.error_extra,
111 : true,
112 : i};
113 50 : return false;
114 0 : });
115 1893 : if (not cmd_is_valid) {
116 50 : return expected::makeError(cmd_error);
117 : }
118 1843 : }
119 : // success
120 669 : savepoint->release();
121 669 : return {};
122 719 : };
123 725 : }
124 :
125 : std::unique_ptr<TemporaryWsv::SavepointWrapper>
126 : TemporaryWsvImpl::createSavepoint(const std::string &name) {
127 727 : return std::make_unique<TemporaryWsvImpl::SavepointWrapperImpl>(
128 727 : SavepointWrapperImpl(*this, name));
129 0 : }
130 :
131 : TemporaryWsvImpl::~TemporaryWsvImpl() {
132 712 : *sql_ << "ROLLBACK";
133 712 : }
134 :
135 : TemporaryWsvImpl::SavepointWrapperImpl::SavepointWrapperImpl(
136 : const iroha::ametsuchi::TemporaryWsvImpl &wsv,
137 : std::string savepoint_name)
138 727 : : sql_{*wsv.sql_},
139 727 : savepoint_name_{std::move(savepoint_name)},
140 727 : is_released_{false} {
141 727 : sql_ << "SAVEPOINT " + savepoint_name_ + ";";
142 727 : };
143 :
144 : void TemporaryWsvImpl::SavepointWrapperImpl::release() {
145 670 : is_released_ = true;
146 670 : }
147 :
148 : TemporaryWsvImpl::SavepointWrapperImpl::~SavepointWrapperImpl() {
149 1454 : if (not is_released_) {
150 784 : sql_ << "ROLLBACK TO SAVEPOINT " + savepoint_name_ + ";";
151 784 : } else {
152 670 : sql_ << "RELEASE SAVEPOINT " + savepoint_name_ + ";";
153 : }
154 1454 : }
155 :
156 : } // namespace ametsuchi
157 : } // namespace iroha
|