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 : */
40 : template <typename FieldValidator>
41 : class CommandValidatorVisitor
42 : : public boost::static_visitor<ReasonsGroupType> {
43 : public:
44 : CommandValidatorVisitor(
45 : const FieldValidator &validator = FieldValidator())
46 2147 : : validator_(validator) {}
47 :
48 : ReasonsGroupType operator()(
49 : const interface::AddAssetQuantity &aaq) const {
50 428 : ReasonsGroupType reason;
51 428 : addInvalidCommand(reason, "AddAssetQuantity");
52 :
53 428 : validator_.validateAssetId(reason, aaq.assetId());
54 428 : validator_.validateAmount(reason, aaq.amount());
55 :
56 428 : return reason;
57 428 : }
58 :
59 : ReasonsGroupType operator()(const interface::AddPeer &ap) const {
60 526 : ReasonsGroupType reason;
61 526 : addInvalidCommand(reason, "AddPeer");
62 :
63 526 : validator_.validatePeer(reason, ap.peer());
64 :
65 526 : return reason;
66 526 : }
67 :
68 : ReasonsGroupType operator()(const interface::AddSignatory &as) const {
69 379 : ReasonsGroupType reason;
70 379 : addInvalidCommand(reason, "AddSignatory");
71 :
72 379 : validator_.validateAccountId(reason, as.accountId());
73 379 : validator_.validatePubkey(reason, as.pubkey());
74 :
75 379 : return reason;
76 379 : }
77 :
78 : ReasonsGroupType operator()(const interface::AppendRole &ar) const {
79 1815 : ReasonsGroupType reason;
80 1815 : addInvalidCommand(reason, "AppendRole");
81 :
82 1815 : validator_.validateAccountId(reason, ar.accountId());
83 1815 : validator_.validateRoleId(reason, ar.roleName());
84 :
85 1815 : return reason;
86 1815 : }
87 :
88 : ReasonsGroupType operator()(const interface::CreateAccount &ca) const {
89 2129 : ReasonsGroupType reason;
90 2129 : addInvalidCommand(reason, "CreateAccount");
91 :
92 2129 : validator_.validatePubkey(reason, ca.pubkey());
93 2129 : validator_.validateAccountName(reason, ca.accountName());
94 2129 : validator_.validateDomainId(reason, ca.domainId());
95 :
96 2129 : return reason;
97 2129 : }
98 :
99 : ReasonsGroupType operator()(const interface::CreateAsset &ca) const {
100 758 : ReasonsGroupType reason;
101 758 : addInvalidCommand(reason, "CreateAsset");
102 :
103 758 : validator_.validateAssetName(reason, ca.assetName());
104 758 : validator_.validateDomainId(reason, ca.domainId());
105 758 : validator_.validatePrecision(reason, ca.precision());
106 :
107 758 : return reason;
108 758 : }
109 :
110 : ReasonsGroupType operator()(const interface::CreateDomain &cd) const {
111 890 : ReasonsGroupType reason;
112 890 : addInvalidCommand(reason, "CreateDomain");
113 :
114 890 : validator_.validateDomainId(reason, cd.domainId());
115 890 : validator_.validateRoleId(reason, cd.userDefaultRole());
116 :
117 890 : return reason;
118 890 : }
119 :
120 : ReasonsGroupType operator()(const interface::CreateRole &cr) const {
121 2343 : ReasonsGroupType reason;
122 2343 : addInvalidCommand(reason, "CreateRole");
123 :
124 2343 : validator_.validateRoleId(reason, cr.roleName());
125 : cr.rolePermissions().iterate([&reason, this](auto i) {
126 23377 : validator_.validateRolePermission(reason, i);
127 23377 : });
128 :
129 2343 : return reason;
130 2343 : }
131 :
132 : ReasonsGroupType operator()(const interface::DetachRole &dr) const {
133 1778 : ReasonsGroupType reason;
134 1778 : addInvalidCommand(reason, "DetachRole");
135 :
136 1778 : validator_.validateAccountId(reason, dr.accountId());
137 1778 : validator_.validateRoleId(reason, dr.roleName());
138 :
139 1778 : return reason;
140 1778 : }
141 :
142 : ReasonsGroupType operator()(const interface::GrantPermission &gp) const {
143 98 : ReasonsGroupType reason;
144 98 : addInvalidCommand(reason, "GrantPermission");
145 :
146 98 : validator_.validateAccountId(reason, gp.accountId());
147 98 : validator_.validateGrantablePermission(reason, gp.permissionName());
148 :
149 98 : return reason;
150 98 : }
151 :
152 : ReasonsGroupType operator()(const interface::RemoveSignatory &rs) const {
153 43 : ReasonsGroupType reason;
154 43 : addInvalidCommand(reason, "RemoveSignatory");
155 :
156 43 : validator_.validateAccountId(reason, rs.accountId());
157 43 : validator_.validatePubkey(reason, rs.pubkey());
158 :
159 43 : return reason;
160 43 : }
161 : ReasonsGroupType operator()(const interface::RevokePermission &rp) const {
162 34 : ReasonsGroupType reason;
163 34 : addInvalidCommand(reason, "RevokePermission");
164 :
165 34 : validator_.validateAccountId(reason, rp.accountId());
166 34 : validator_.validateGrantablePermission(reason, rp.permissionName());
167 :
168 34 : return reason;
169 34 : }
170 :
171 : ReasonsGroupType operator()(
172 : const interface::SetAccountDetail &sad) const {
173 222 : ReasonsGroupType reason;
174 222 : addInvalidCommand(reason, "SetAccountDetail");
175 :
176 222 : validator_.validateAccountId(reason, sad.accountId());
177 222 : validator_.validateAccountDetailKey(reason, sad.key());
178 222 : validator_.validateAccountDetailValue(reason, sad.value());
179 :
180 222 : return reason;
181 222 : }
182 :
183 : ReasonsGroupType operator()(const interface::SetQuorum &sq) const {
184 149 : ReasonsGroupType reason;
185 149 : addInvalidCommand(reason, "SetQuorum");
186 :
187 149 : validator_.validateAccountId(reason, sq.accountId());
188 149 : validator_.validateQuorum(reason, sq.newQuorum());
189 :
190 149 : return reason;
191 149 : }
192 :
193 : ReasonsGroupType operator()(
194 : const interface::SubtractAssetQuantity &saq) const {
195 20 : ReasonsGroupType reason;
196 20 : addInvalidCommand(reason, "SubtractAssetQuantity");
197 :
198 20 : validator_.validateAssetId(reason, saq.assetId());
199 20 : validator_.validateAmount(reason, saq.amount());
200 :
201 20 : return reason;
202 20 : }
203 :
204 : ReasonsGroupType operator()(const interface::TransferAsset &ta) const {
205 288 : ReasonsGroupType reason;
206 288 : addInvalidCommand(reason, "TransferAsset");
207 :
208 288 : if (ta.srcAccountId() == ta.destAccountId()) {
209 2 : reason.second.emplace_back(
210 : "Source and destination accounts cannot be the same");
211 2 : }
212 :
213 288 : validator_.validateAccountId(reason, ta.srcAccountId());
214 288 : validator_.validateAccountId(reason, ta.destAccountId());
215 288 : validator_.validateAssetId(reason, ta.assetId());
216 288 : validator_.validateAmount(reason, ta.amount());
217 288 : validator_.validateDescription(reason, ta.description());
218 :
219 288 : return reason;
220 288 : }
221 :
222 : private:
223 : FieldValidator validator_;
224 2147 : mutable int command_counter{0};
225 :
226 : // adds command to a reason, appends and increments counter
227 : void addInvalidCommand(ReasonsGroupType &reason,
228 : const std::string &command_name) const {
229 11900 : reason.first =
230 11900 : (boost::format("%d %s") % command_counter % command_name).str();
231 11900 : command_counter++;
232 11900 : }
233 : };
234 :
235 : /**
236 : * Class that validates commands from transaction
237 : * @tparam FieldValidator
238 : * @tparam CommandValidator
239 : */
240 : template <typename FieldValidator, typename CommandValidator>
241 : class TransactionValidator
242 : : public AbstractValidator<interface::Transaction> {
243 : private:
244 : template <typename CreatedTimeValidator>
245 : Answer validateImpl(const interface::Transaction &tx,
246 : CreatedTimeValidator &&validator) const {
247 3722 : Answer answer;
248 3722 : std::string tx_reason_name = "Transaction";
249 3722 : ReasonsGroupType tx_reason(tx_reason_name, GroupedReasons());
250 :
251 3722 : if (tx.commands().empty()) {
252 5 : tx_reason.second.push_back(
253 5 : "Transaction should contain at least one command");
254 5 : }
255 :
256 3722 : field_validator_.validateCreatorAccountId(tx_reason,
257 3722 : tx.creatorAccountId());
258 3722 : std::forward<CreatedTimeValidator>(validator)(tx_reason,
259 3722 : tx.createdTime());
260 3722 : field_validator_.validateQuorum(tx_reason, tx.quorum());
261 3722 : if (tx.batchMeta() != boost::none)
262 85 : field_validator_.validateBatchMeta(tx_reason, **tx.batchMeta());
263 :
264 3722 : if (not tx_reason.second.empty()) {
265 21 : answer.addReason(std::move(tx_reason));
266 21 : }
267 :
268 15636 : for (const auto &command : tx.commands()) {
269 11914 : auto reason = boost::apply_visitor(command_validator_, command.get());
270 11914 : if (not reason.second.empty()) {
271 65 : answer.addReason(std::move(reason));
272 65 : }
273 11914 : }
274 :
275 3722 : return answer;
276 3722 : }
277 :
278 : public:
279 : explicit TransactionValidator(
280 : const FieldValidator &field_validator = FieldValidator(),
281 : const CommandValidator &command_validator = CommandValidator())
282 2147 : : field_validator_(field_validator),
283 2147 : command_validator_(command_validator) {}
284 :
285 : /**
286 : * Applies validation to given transaction
287 : * @param tx - transaction to validate
288 : * @return Answer containing found error if any
289 : */
290 : Answer validate(const interface::Transaction &tx) const override {
291 : return validateImpl(tx, [this](auto &reason, auto time) {
292 1994 : field_validator_.validateCreatedTime(reason, time);
293 1994 : });
294 : }
295 :
296 : /**
297 : * Validates transaction against current_timestamp instead of time
298 : * provider
299 : */
300 : Answer validate(const interface::Transaction &tx,
301 : interface::types::TimestampType current_timestamp) const {
302 1728 : return validateImpl(tx,
303 : [this, current_timestamp](auto &reason, auto time) {
304 1728 : field_validator_.validateCreatedTime(
305 1728 : reason, time, current_timestamp);
306 1728 : });
307 : }
308 :
309 : protected:
310 : FieldValidator field_validator_;
311 : CommandValidator command_validator_;
312 : };
313 :
314 : } // namespace validation
315 : } // namespace shared_model
316 :
317 : #endif // IROHA_SHARED_MODEL_TRANSACTION_VALIDATOR_HPP
|