Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #include "validation/impl/stateful_validator_impl.hpp"
7 :
8 : #include <string>
9 :
10 : #include <boost/algorithm/cxx11/all_of.hpp>
11 : #include <boost/format.hpp>
12 : #include <boost/range/adaptor/filtered.hpp>
13 : #include <boost/range/adaptor/indexed.hpp>
14 : #include <boost/range/adaptor/transformed.hpp>
15 : #include "common/result.hpp"
16 : #include "interfaces/iroha_internal/batch_meta.hpp"
17 : #include "logger/logger.hpp"
18 : #include "validation/utils.hpp"
19 :
20 : namespace iroha {
21 : namespace validation {
22 :
23 : /**
24 : * Complements initial transaction check with command-by-command check
25 : * @param temporary_wsv to apply commands on
26 : * @param transactions_errors_log to write errors to
27 : * @param tx to be checked
28 : * @return empty result, if check is successful, command error otherwise
29 : */
30 : static bool checkTransactions(
31 : ametsuchi::TemporaryWsv &temporary_wsv,
32 : validation::TransactionsErrors &transactions_errors_log,
33 : const shared_model::interface::Transaction &tx) {
34 738 : return temporary_wsv.apply(tx).match(
35 : [](expected::Value<void> &) { return true; },
36 : [&tx, &transactions_errors_log](
37 : expected::Error<validation::CommandError> &error) {
38 60 : transactions_errors_log.emplace_back(validation::TransactionError{
39 60 : tx.hash(), std::move(error.error)});
40 60 : return false;
41 0 : });
42 0 : };
43 :
44 : /**
45 : * Validate all transactions supplied; includes special rules, such as batch
46 : * validation etc
47 : * @param txs to be validated
48 : * @param temporary_wsv to apply transactions on
49 : * @param transactions_errors_log to write errors to
50 : * @param batch_parser to parse batches from transaction range
51 : * @return range of transactions, which passed stateful validation
52 : */
53 : static auto validateTransactions(
54 : const shared_model::interface::types::TransactionsCollectionType &txs,
55 : ametsuchi::TemporaryWsv &temporary_wsv,
56 : validation::TransactionsErrors &transactions_errors_log,
57 : const shared_model::interface::TransactionBatchParser &batch_parser) {
58 724 : std::vector<bool> validation_results;
59 724 : validation_results.reserve(boost::size(txs));
60 :
61 1455 : for (auto batch : batch_parser.parseBatches(txs)) {
62 : auto validation = [&](auto &tx) {
63 738 : return checkTransactions(temporary_wsv, transactions_errors_log, tx);
64 : };
65 731 : if (batch.front().batchMeta()
66 731 : and batch.front().batchMeta()->get()->type()
67 7 : == shared_model::interface::types::BatchType::ATOMIC) {
68 : // check all batch's transactions for validness
69 4 : auto savepoint = temporary_wsv.createSavepoint(
70 4 : "batch_" + batch.front().hash().hex());
71 4 : bool validation_result = false;
72 :
73 4 : if (boost::algorithm::all_of(batch, validation)) {
74 : // batch is successful; release savepoint
75 2 : validation_result = true;
76 2 : savepoint->release();
77 2 : } else {
78 2 : auto failed_tx_hash = transactions_errors_log.back().tx_hash;
79 6 : for (const auto &tx : batch) {
80 4 : if (tx.hash() != failed_tx_hash) {
81 2 : transactions_errors_log.emplace_back(
82 2 : validation::TransactionError{
83 2 : tx.hash(),
84 : // TODO igor-egorov 22.01.2019 IR-245 add a separate
85 : // error code for failed batch case
86 2 : validation::CommandError{
87 2 : "",
88 : 1, // internal error code
89 2 : "Another transaction failed the batch",
90 : true,
91 2 : std::numeric_limits<size_t>::max()}});
92 2 : }
93 : }
94 2 : }
95 :
96 4 : validation_results.insert(
97 4 : validation_results.end(), boost::size(batch), validation_result);
98 4 : } else {
99 1458 : for (const auto &tx : batch) {
100 731 : validation_results.push_back(validation(tx));
101 : }
102 : }
103 731 : }
104 :
105 724 : return txs | boost::adaptors::indexed()
106 724 : | boost::adaptors::filtered(
107 : [validation_results =
108 724 : std::move(validation_results)](const auto &el) {
109 739 : return validation_results.at(el.index());
110 : })
111 724 : | boost::adaptors::transformed(
112 : [](const auto &el) -> decltype(auto) { return el.value(); });
113 724 : }
114 :
115 : StatefulValidatorImpl::StatefulValidatorImpl(
116 : std::unique_ptr<shared_model::interface::UnsafeProposalFactory> factory,
117 : std::shared_ptr<shared_model::interface::TransactionBatchParser>
118 : batch_parser,
119 : logger::LoggerPtr log)
120 250 : : factory_(std::move(factory)),
121 250 : batch_parser_(std::move(batch_parser)),
122 250 : log_(std::move(log)) {}
123 :
124 : std::unique_ptr<validation::VerifiedProposalAndErrors>
125 : StatefulValidatorImpl::validate(
126 : const shared_model::interface::Proposal &proposal,
127 : ametsuchi::TemporaryWsv &temporaryWsv) {
128 724 : log_->info("transactions in proposal: {}",
129 724 : proposal.transactions().size());
130 :
131 724 : auto validation_result = std::make_unique<VerifiedProposalAndErrors>();
132 : auto valid_txs =
133 724 : validateTransactions(proposal.transactions(),
134 724 : temporaryWsv,
135 724 : validation_result->rejected_transactions,
136 724 : *batch_parser_);
137 :
138 : // Since proposal came from ordering gate it was already validated.
139 : // All transactions are validated as well
140 : // This allows for unsafe construction of proposal
141 724 : validation_result->verified_proposal =
142 724 : std::const_pointer_cast<const shared_model::interface::Proposal>(
143 724 : std::shared_ptr<shared_model::interface::Proposal>(
144 724 : factory_->unsafeCreateProposal(
145 724 : proposal.height(), proposal.createdTime(), valid_txs)));
146 :
147 724 : log_->info("transactions in verified proposal: {}",
148 724 : validation_result->verified_proposal->transactions().size());
149 724 : return validation_result;
150 724 : }
151 : } // namespace validation
152 : } // namespace iroha
|