Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #ifndef IROHA_SHARED_MODEL_TRANSACTION_VALIDATOR_HPP
7 : #define IROHA_SHARED_MODEL_TRANSACTION_VALIDATOR_HPP
8 :
9 : #include <boost/format.hpp>
10 : #include <boost/variant.hpp>
11 :
12 : #include "interfaces/commands/add_asset_quantity.hpp"
13 : #include "interfaces/commands/add_peer.hpp"
14 : #include "interfaces/commands/add_signatory.hpp"
15 : #include "interfaces/commands/append_role.hpp"
16 : #include "interfaces/commands/command.hpp"
17 : #include "interfaces/commands/create_account.hpp"
18 : #include "interfaces/commands/create_asset.hpp"
19 : #include "interfaces/commands/create_domain.hpp"
20 : #include "interfaces/commands/create_role.hpp"
21 : #include "interfaces/commands/detach_role.hpp"
22 : #include "interfaces/commands/grant_permission.hpp"
23 : #include "interfaces/commands/remove_signatory.hpp"
24 : #include "interfaces/commands/revoke_permission.hpp"
25 : #include "interfaces/commands/set_account_detail.hpp"
26 : #include "interfaces/commands/set_quorum.hpp"
27 : #include "interfaces/commands/subtract_asset_quantity.hpp"
28 : #include "interfaces/commands/transfer_asset.hpp"
29 : #include "interfaces/transaction.hpp"
30 : #include "validators/abstract_validator.hpp"
31 : #include "validators/answer.hpp"
32 :
33 : namespace shared_model {
34 : namespace validation {
35 :
36 : /**
37 : * Visitor used by transaction validator to validate each command
38 : * @tparam FieldValidator - field validator type
39 : * @note this class is not thread safe and never going to be
40 : * so copy constructor and assignment operator are disabled explicitly
41 : */
42 : template <typename FieldValidator>
43 : class CommandValidatorVisitor
44 : : public boost::static_visitor<ReasonsGroupType> {
45 : public:
46 : CommandValidatorVisitor(const CommandValidatorVisitor &) = delete;
47 : CommandValidatorVisitor &operator=(const CommandValidatorVisitor &) =
48 : delete;
49 :
50 : CommandValidatorVisitor(
51 : const FieldValidator &validator = FieldValidator())
52 25149 : : validator_(validator) {}
53 :
54 : ReasonsGroupType operator()(
55 : const interface::AddAssetQuantity &aaq) const {
56 1346 : ReasonsGroupType reason;
57 1348 : addInvalidCommand(reason, "AddAssetQuantity");
58 :
59 1336 : validator_.validateAssetId(reason, aaq.assetId());
60 1362 : validator_.validateAmount(reason, aaq.amount());
61 :
62 1387 : return reason;
63 1387 : }
64 :
65 : ReasonsGroupType operator()(const interface::AddPeer &ap) const {
66 530 : ReasonsGroupType reason;
67 530 : addInvalidCommand(reason, "AddPeer");
68 :
69 530 : validator_.validatePeer(reason, ap.peer());
70 :
71 530 : return reason;
72 530 : }
73 :
74 : ReasonsGroupType operator()(const interface::AddSignatory &as) const {
75 1242 : ReasonsGroupType reason;
76 1242 : addInvalidCommand(reason, "AddSignatory");
77 :
78 1229 : validator_.validateAccountId(reason, as.accountId());
79 1271 : validator_.validatePubkey(reason, as.pubkey());
80 :
81 1281 : return reason;
82 1281 : }
83 :
84 : ReasonsGroupType operator()(const interface::AppendRole &ar) const {
85 3844 : ReasonsGroupType reason;
86 3844 : addInvalidCommand(reason, "AppendRole");
87 :
88 3837 : validator_.validateAccountId(reason, ar.accountId());
89 3872 : validator_.validateRoleId(reason, ar.roleName());
90 :
91 3900 : return reason;
92 3900 : }
93 :
94 : ReasonsGroupType operator()(const interface::CreateAccount &ca) const {
95 4568 : ReasonsGroupType reason;
96 4568 : addInvalidCommand(reason, "CreateAccount");
97 :
98 4556 : validator_.validatePubkey(reason, ca.pubkey());
99 4539 : validator_.validateAccountName(reason, ca.accountName());
100 4566 : validator_.validateDomainId(reason, ca.domainId());
101 :
102 4635 : return reason;
103 4638 : }
104 :
105 : ReasonsGroupType operator()(const interface::CreateAsset &ca) const {
106 1164 : ReasonsGroupType reason;
107 1164 : addInvalidCommand(reason, "CreateAsset");
108 :
109 1153 : validator_.validateAssetName(reason, ca.assetName());
110 1163 : validator_.validateDomainId(reason, ca.domainId());
111 1171 : validator_.validatePrecision(reason, ca.precision());
112 :
113 1178 : return reason;
114 1178 : }
115 :
116 : ReasonsGroupType operator()(const interface::CreateDomain &cd) const {
117 1301 : ReasonsGroupType reason;
118 1301 : addInvalidCommand(reason, "CreateDomain");
119 :
120 1293 : validator_.validateDomainId(reason, cd.domainId());
121 1304 : validator_.validateRoleId(reason, cd.userDefaultRole());
122 :
123 1318 : return reason;
124 1318 : }
125 :
126 : ReasonsGroupType operator()(const interface::CreateRole &cr) const {
127 4413 : ReasonsGroupType reason;
128 4413 : addInvalidCommand(reason, "CreateRole");
129 :
130 4393 : validator_.validateRoleId(reason, cr.roleName());
131 : cr.rolePermissions().iterate([&reason, this](auto i) {
132 28776 : validator_.validateRolePermission(reason, i);
133 28776 : });
134 :
135 4481 : return reason;
136 4481 : }
137 :
138 : ReasonsGroupType operator()(const interface::DetachRole &dr) const {
139 3704 : ReasonsGroupType reason;
140 3705 : addInvalidCommand(reason, "DetachRole");
141 :
142 3705 : validator_.validateAccountId(reason, dr.accountId());
143 3739 : validator_.validateRoleId(reason, dr.roleName());
144 :
145 3757 : return reason;
146 3764 : }
147 :
148 : ReasonsGroupType operator()(const interface::GrantPermission &gp) const {
149 357 : ReasonsGroupType reason;
150 357 : addInvalidCommand(reason, "GrantPermission");
151 :
152 356 : validator_.validateAccountId(reason, gp.accountId());
153 366 : validator_.validateGrantablePermission(reason, gp.permissionName());
154 :
155 370 : return reason;
156 370 : }
157 :
158 : ReasonsGroupType operator()(const interface::RemoveSignatory &rs) const {
159 122 : ReasonsGroupType reason;
160 124 : addInvalidCommand(reason, "RemoveSignatory");
161 :
162 124 : validator_.validateAccountId(reason, rs.accountId());
163 128 : validator_.validatePubkey(reason, rs.pubkey());
164 :
165 128 : return reason;
166 128 : }
167 : ReasonsGroupType operator()(const interface::RevokePermission &rp) const {
168 136 : ReasonsGroupType reason;
169 136 : addInvalidCommand(reason, "RevokePermission");
170 :
171 135 : validator_.validateAccountId(reason, rp.accountId());
172 142 : validator_.validateGrantablePermission(reason, rp.permissionName());
173 :
174 145 : return reason;
175 145 : }
176 :
177 : ReasonsGroupType operator()(
178 : const interface::SetAccountDetail &sad) const {
179 728 : ReasonsGroupType reason;
180 728 : addInvalidCommand(reason, "SetAccountDetail");
181 :
182 708 : validator_.validateAccountId(reason, sad.accountId());
183 725 : validator_.validateAccountDetailKey(reason, sad.key());
184 737 : validator_.validateAccountDetailValue(reason, sad.value());
185 :
186 753 : return reason;
187 753 : }
188 :
189 : ReasonsGroupType operator()(const interface::SetQuorum &sq) const {
190 223 : ReasonsGroupType reason;
191 223 : addInvalidCommand(reason, "SetQuorum");
192 :
193 221 : validator_.validateAccountId(reason, sq.accountId());
194 224 : validator_.validateQuorum(reason, sq.newQuorum());
195 :
196 225 : return reason;
197 225 : }
198 :
199 : ReasonsGroupType operator()(
200 : const interface::SubtractAssetQuantity &saq) const {
201 53 : ReasonsGroupType reason;
202 53 : addInvalidCommand(reason, "SubtractAssetQuantity");
203 :
204 50 : validator_.validateAssetId(reason, saq.assetId());
205 54 : validator_.validateAmount(reason, saq.amount());
206 :
207 54 : return reason;
208 54 : }
209 :
210 : ReasonsGroupType operator()(const interface::TransferAsset &ta) const {
211 881 : ReasonsGroupType reason;
212 881 : addInvalidCommand(reason, "TransferAsset");
213 :
214 893 : if (ta.srcAccountId() == ta.destAccountId()) {
215 2 : reason.second.emplace_back(
216 : "Source and destination accounts cannot be the same");
217 2 : }
218 :
219 885 : validator_.validateAccountId(reason, ta.srcAccountId());
220 886 : validator_.validateAccountId(reason, ta.destAccountId());
221 889 : validator_.validateAssetId(reason, ta.assetId());
222 899 : validator_.validateAmount(reason, ta.amount());
223 897 : validator_.validateDescription(reason, ta.description());
224 :
225 906 : return reason;
226 906 : }
227 :
228 : private:
229 : FieldValidator validator_;
230 25056 : mutable int command_counter{0};
231 :
232 : // adds command to a reason, appends and increments counter
233 : void addInvalidCommand(ReasonsGroupType &reason,
234 : const std::string &command_name) const {
235 25211 : reason.first =
236 24930 : (boost::format("%d %s") % command_counter % command_name).str();
237 25211 : command_counter++;
238 25211 : }
239 : };
240 :
241 : /**
242 : * Class that validates commands from transaction
243 : * @tparam FieldValidator
244 : * @tparam CommandValidator
245 : */
246 : template <typename FieldValidator, typename CommandValidator>
247 : class TransactionValidator
248 : : public AbstractValidator<interface::Transaction> {
249 : private:
250 : template <typename CreatedTimeValidator>
251 : Answer validateImpl(const interface::Transaction &tx,
252 : CreatedTimeValidator &&validator) const {
253 9225 : Answer answer;
254 9225 : std::string tx_reason_name = "Transaction";
255 9263 : ReasonsGroupType tx_reason(tx_reason_name, GroupedReasons());
256 :
257 9547 : if (tx.commands().empty()) {
258 5 : tx_reason.second.push_back(
259 5 : "Transaction should contain at least one command");
260 5 : }
261 :
262 9289 : field_validator_.validateCreatorAccountId(tx_reason,
263 9289 : tx.creatorAccountId());
264 9296 : std::forward<CreatedTimeValidator>(validator)(tx_reason,
265 9296 : tx.createdTime());
266 9320 : field_validator_.validateQuorum(tx_reason, tx.quorum());
267 9438 : if (tx.batchMeta() != boost::none)
268 144 : field_validator_.validateBatchMeta(tx_reason, **tx.batchMeta());
269 :
270 9414 : if (not tx_reason.second.empty()) {
271 16 : answer.addReason(std::move(tx_reason));
272 16 : }
273 :
274 34473 : for (const auto &command : tx.commands()) {
275 25197 : auto reason = boost::apply_visitor(CommandValidator(), command.get());
276 25197 : if (not reason.second.empty()) {
277 65 : answer.addReason(std::move(reason));
278 65 : }
279 25020 : }
280 :
281 9532 : return answer;
282 9532 : }
283 :
284 : public:
285 : explicit TransactionValidator(
286 : const FieldValidator &field_validator = FieldValidator())
287 2147 : : field_validator_(field_validator) {}
288 :
289 : /**
290 : * Applies validation to given transaction
291 : * @param tx - transaction to validate
292 : * @return Answer containing found error if any
293 : */
294 : Answer validate(const interface::Transaction &tx) const override {
295 : return validateImpl(tx, [this](auto &reason, auto time) {
296 7830 : field_validator_.validateCreatedTime(reason, time);
297 7830 : });
298 : }
299 :
300 : /**
301 : * Validates transaction against current_timestamp instead of time
302 : * provider
303 : */
304 : Answer validate(const interface::Transaction &tx,
305 : interface::types::TimestampType current_timestamp) const {
306 1602 : return validateImpl(tx,
307 : [this, current_timestamp](auto &reason, auto time) {
308 1602 : field_validator_.validateCreatedTime(
309 1602 : reason, time, current_timestamp);
310 1602 : });
311 : }
312 :
313 : protected:
314 : FieldValidator field_validator_;
315 : };
316 :
317 : } // namespace validation
318 : } // namespace shared_model
319 :
320 : #endif // IROHA_SHARED_MODEL_TRANSACTION_VALIDATOR_HPP
|