Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved.
3 : * http://soramitsu.co.jp
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include "consensus/yac/storage/yac_proposal_storage.hpp"
19 :
20 : using namespace logger;
21 :
22 : namespace iroha {
23 : namespace consensus {
24 : namespace yac {
25 :
26 : // --------| private api |--------
27 :
28 : auto YacProposalStorage::findStore(const YacHash &store_hash) {
29 : // find exist
30 776 : auto iter = std::find_if(block_storages_.begin(),
31 776 : block_storages_.end(),
32 : [&store_hash](auto block_storage) {
33 : auto storage_key =
34 54 : block_storage.getStorageKey();
35 54 : return storage_key == store_hash;
36 54 : });
37 776 : if (iter != block_storages_.end()) {
38 47 : return iter;
39 : }
40 : // insert and return new
41 729 : return block_storages_.emplace(
42 729 : block_storages_.end(),
43 729 : YacHash(store_hash.vote_round,
44 729 : store_hash.vote_hashes.proposal_hash,
45 729 : store_hash.vote_hashes.block_hash),
46 729 : peers_in_round_,
47 729 : supermajority_checker_);
48 776 : }
49 :
50 : // --------| public api |--------
51 :
52 : YacProposalStorage::YacProposalStorage(
53 : Round store_round,
54 : PeersNumberType peers_in_round,
55 : std::shared_ptr<SupermajorityChecker> supermajority_checker)
56 729 : : current_state_(boost::none),
57 729 : storage_key_(store_round),
58 729 : peers_in_round_(peers_in_round),
59 729 : supermajority_checker_(supermajority_checker) {
60 729 : log_ = log("ProposalStorage");
61 729 : }
62 :
63 : boost::optional<Answer> YacProposalStorage::insert(VoteMessage msg) {
64 1516 : if (shouldInsert(msg)) {
65 : // insert to block store
66 :
67 776 : log_->info("Vote with round [{}, {}] and hashes [{}, {}] looks valid",
68 776 : msg.hash.vote_round.block_round,
69 776 : msg.hash.vote_round.reject_round,
70 776 : msg.hash.vote_hashes.proposal_hash,
71 776 : msg.hash.vote_hashes.block_hash);
72 :
73 776 : auto iter = findStore(msg.hash);
74 776 : auto block_state = iter->insert(msg);
75 :
76 : // Single BlockStorage always returns CommitMessage because it
77 : // aggregates votes for a single hash.
78 776 : if (block_state) {
79 : // supermajority on block achieved
80 728 : current_state_ = std::move(block_state);
81 728 : } else {
82 : // try to find reject case
83 48 : auto reject_state = findRejectProof();
84 48 : if (reject_state) {
85 4 : log_->info("Found reject proof");
86 4 : current_state_ = std::move(reject_state);
87 4 : }
88 48 : }
89 776 : }
90 1516 : return getState();
91 0 : }
92 :
93 : boost::optional<Answer> YacProposalStorage::insert(
94 : std::vector<VoteMessage> messages) {
95 : std::for_each(messages.begin(), messages.end(), [this](auto vote) {
96 1496 : this->insert(std::move(vote));
97 1496 : });
98 1465 : return getState();
99 : }
100 :
101 : const Round &YacProposalStorage::getStorageKey() const {
102 : return storage_key_;
103 : }
104 :
105 : boost::optional<Answer> YacProposalStorage::getState() const {
106 2982 : return current_state_;
107 : }
108 :
109 : // --------| private api |--------
110 :
111 : bool YacProposalStorage::shouldInsert(const VoteMessage &msg) {
112 : return checkProposalRound(msg.hash.vote_round)
113 1516 : and checkPeerUniqueness(msg);
114 : }
115 :
116 : bool YacProposalStorage::checkProposalRound(const Round &vote_round) {
117 1516 : return vote_round == storage_key_;
118 : }
119 :
120 : bool YacProposalStorage::checkPeerUniqueness(const VoteMessage &msg) {
121 1515 : return std::all_of(block_storages_.begin(),
122 1515 : block_storages_.end(),
123 : [&msg](YacBlockStorage &storage) {
124 796 : if (storage.getStorageKey() != msg.hash) {
125 10 : return true;
126 : }
127 786 : return not storage.isContains(msg);
128 796 : });
129 : }
130 :
131 : boost::optional<Answer> YacProposalStorage::findRejectProof() {
132 48 : auto max_vote = std::max_element(block_storages_.begin(),
133 48 : block_storages_.end(),
134 : [](auto &left, auto &right) {
135 8 : return left.getNumberOfVotes()
136 8 : < right.getNumberOfVotes();
137 : })
138 48 : ->getNumberOfVotes();
139 :
140 48 : auto all_votes =
141 48 : std::accumulate(block_storages_.begin(),
142 48 : block_storages_.end(),
143 : 0ull,
144 : [](auto &acc, auto &storage) {
145 56 : return acc + storage.getNumberOfVotes();
146 : });
147 :
148 48 : auto is_reject = supermajority_checker_->hasReject(
149 48 : max_vote, all_votes, peers_in_round_);
150 :
151 48 : if (is_reject) {
152 4 : std::vector<VoteMessage> result;
153 4 : result.reserve(all_votes);
154 4 : std::for_each(block_storages_.begin(),
155 4 : block_storages_.end(),
156 : [&result](auto &storage) {
157 8 : auto votes_from_block_storage = storage.getVotes();
158 8 : std::move(votes_from_block_storage.begin(),
159 8 : votes_from_block_storage.end(),
160 8 : std::back_inserter(result));
161 8 : });
162 :
163 4 : return Answer(RejectMessage(std::move(result)));
164 4 : }
165 :
166 44 : return boost::none;
167 48 : }
168 :
169 : } // namespace yac
170 : } // namespace consensus
171 : } // namespace iroha
|