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/postgres_block_query.hpp"
7 :
8 : #include <boost/format.hpp>
9 : #include <boost/range/adaptor/transformed.hpp>
10 : #include <boost/range/algorithm/for_each.hpp>
11 :
12 : #include "ametsuchi/impl/soci_utils.hpp"
13 : #include "common/byteutils.hpp"
14 :
15 : namespace iroha {
16 : namespace ametsuchi {
17 : PostgresBlockQuery::PostgresBlockQuery(
18 : soci::session &sql,
19 : KeyValueStorage &file_store,
20 : std::shared_ptr<shared_model::interface::BlockJsonDeserializer>
21 : converter)
22 44 : : sql_(sql),
23 44 : block_store_(file_store),
24 44 : converter_(std::move(converter)),
25 44 : log_(logger::log("PostgresBlockQuery")) {}
26 :
27 : PostgresBlockQuery::PostgresBlockQuery(
28 : std::unique_ptr<soci::session> sql,
29 : KeyValueStorage &file_store,
30 : std::shared_ptr<shared_model::interface::BlockJsonDeserializer>
31 : converter)
32 2450 : : psql_(std::move(sql)),
33 2450 : sql_(*psql_),
34 2450 : block_store_(file_store),
35 2450 : converter_(std::move(converter)),
36 2450 : log_(logger::log("PostgresBlockQuery")) {}
37 :
38 : std::vector<BlockQuery::wBlock> PostgresBlockQuery::getBlocks(
39 : shared_model::interface::types::HeightType height, uint32_t count) {
40 257 : shared_model::interface::types::HeightType last_id =
41 257 : block_store_.last_id();
42 257 : auto to = std::min(last_id, height + count - 1);
43 257 : std::vector<BlockQuery::wBlock> result;
44 257 : if (height > to or count == 0) {
45 2 : return result;
46 : }
47 516 : for (auto i = height; i <= to; i++) {
48 261 : auto block = getBlock(i);
49 261 : block.match(
50 : [&result](
51 : expected::Value<std::unique_ptr<shared_model::interface::Block>>
52 258 : &v) { result.emplace_back(std::move(v.value)); },
53 : [this](const expected::Error<std::string> &e) {
54 3 : log_->error(e.error);
55 3 : });
56 261 : }
57 255 : return result;
58 257 : }
59 :
60 : std::vector<BlockQuery::wBlock> PostgresBlockQuery::getBlocksFrom(
61 : shared_model::interface::types::HeightType height) {
62 247 : return getBlocks(height, block_store_.last_id());
63 : }
64 :
65 : std::vector<BlockQuery::wBlock> PostgresBlockQuery::getTopBlocks(
66 : uint32_t count) {
67 1 : auto last_id = block_store_.last_id();
68 1 : count = std::min(count, last_id);
69 1 : return getBlocks(last_id - count + 1, count);
70 : }
71 :
72 : std::vector<shared_model::interface::types::HeightType>
73 : PostgresBlockQuery::getBlockIds(
74 : const shared_model::interface::types::AccountIdType &account_id) {
75 21 : std::vector<shared_model::interface::types::HeightType> result;
76 : soci::indicator ind;
77 21 : std::string row;
78 21 : soci::statement st =
79 21 : (sql_.prepare << "SELECT DISTINCT height FROM height_by_account_set "
80 21 : "WHERE account_id = :id",
81 21 : soci::into(row, ind),
82 : soci::use(account_id));
83 21 : st.execute();
84 :
85 : processSoci(st, ind, row, [&result](std::string &r) {
86 28 : result.push_back(std::stoull(r));
87 28 : });
88 21 : return result;
89 21 : }
90 :
91 : boost::optional<shared_model::interface::types::HeightType>
92 : PostgresBlockQuery::getBlockId(const shared_model::crypto::Hash &hash) {
93 9 : boost::optional<uint64_t> blockId = boost::none;
94 9 : boost::optional<std::string> block_str;
95 9 : auto hash_str = hash.hex();
96 :
97 9 : sql_ << "SELECT height FROM height_by_hash WHERE hash = :hash",
98 9 : soci::into(block_str), soci::use(hash_str);
99 9 : if (block_str) {
100 5 : blockId = std::stoull(block_str.get());
101 5 : } else {
102 : log_->info("No block with transaction {}", hash.toString());
103 : }
104 : return blockId;
105 9 : }
106 :
107 : std::function<void(std::vector<std::string> &result)>
108 : PostgresBlockQuery::callback(std::vector<wTransaction> &blocks,
109 : uint64_t block_id) {
110 : return [this, &blocks, block_id](std::vector<std::string> &result) {
111 28 : auto block = getBlock(block_id);
112 : block.match(
113 : [&result, &blocks](
114 : expected::Value<std::unique_ptr<shared_model::interface::Block>>
115 : &v) {
116 28 : boost::for_each(
117 : result | boost::adaptors::transformed([](const auto &x) {
118 19 : std::istringstream iss(x);
119 : size_t size;
120 19 : iss >> size;
121 19 : return size;
122 19 : }),
123 : [&](const auto &x) {
124 28 : blocks.emplace_back(clone(v.value->transactions()[x]));
125 19 : });
126 28 : },
127 : [this](const expected::Error<std::string> &e) {
128 0 : log_->error(e.error);
129 0 : });
130 28 : };
131 : }
132 :
133 : std::vector<BlockQuery::wTransaction>
134 : PostgresBlockQuery::getAccountTransactions(
135 : const shared_model::interface::types::AccountIdType &account_id) {
136 8 : std::vector<BlockQuery::wTransaction> result;
137 8 : auto block_ids = this->getBlockIds(account_id);
138 8 : if (block_ids.empty()) {
139 2 : return result;
140 : }
141 13 : for (const auto &block_id : block_ids) {
142 7 : std::vector<std::string> index;
143 : soci::indicator ind;
144 7 : std::string row;
145 7 : soci::statement st =
146 7 : (sql_.prepare
147 7 : << "SELECT DISTINCT index FROM index_by_creator_height "
148 7 : "WHERE creator_id = :id AND height = :height",
149 7 : soci::into(row, ind),
150 7 : soci::use(account_id),
151 7 : soci::use(block_id));
152 7 : st.execute();
153 :
154 7 : processSoci(
155 : st, ind, row, [&index](std::string &r) { index.push_back(r); });
156 7 : this->callback(result, block_id)(index);
157 7 : }
158 6 : return result;
159 8 : }
160 :
161 : std::vector<BlockQuery::wTransaction>
162 : PostgresBlockQuery::getAccountAssetTransactions(
163 : const shared_model::interface::types::AccountIdType &account_id,
164 : const shared_model::interface::types::AssetIdType &asset_id) {
165 13 : std::vector<BlockQuery::wTransaction> result;
166 13 : auto block_ids = this->getBlockIds(account_id);
167 13 : if (block_ids.empty()) {
168 1 : return result;
169 : }
170 :
171 33 : for (const auto &block_id : block_ids) {
172 21 : std::vector<std::string> index;
173 : soci::indicator ind;
174 21 : std::string row;
175 21 : soci::statement st =
176 21 : (sql_.prepare
177 21 : << "SELECT DISTINCT index FROM index_by_id_height_asset "
178 : "WHERE id = :id AND height = :height AND asset_id = "
179 21 : ":asset_id",
180 21 : soci::into(row, ind),
181 21 : soci::use(account_id),
182 21 : soci::use(block_id),
183 21 : soci::use(asset_id));
184 21 : st.execute();
185 :
186 21 : processSoci(
187 : st, ind, row, [&index](std::string &r) { index.push_back(r); });
188 21 : this->callback(result, block_id)(index);
189 21 : }
190 12 : return result;
191 13 : }
192 :
193 : std::vector<boost::optional<BlockQuery::wTransaction>>
194 : PostgresBlockQuery::getTransactions(
195 : const std::vector<shared_model::crypto::Hash> &tx_hashes) {
196 4 : std::vector<boost::optional<BlockQuery::wTransaction>> result;
197 4 : std::for_each(tx_hashes.begin(),
198 4 : tx_hashes.end(),
199 : [this, &result](const auto &tx_hash) {
200 6 : result.push_back(this->getTxByHashSync(tx_hash));
201 6 : });
202 4 : return result;
203 4 : }
204 :
205 : boost::optional<BlockQuery::wTransaction>
206 : PostgresBlockQuery::getTxByHashSync(
207 : const shared_model::crypto::Hash &hash) {
208 9 : return getBlockId(hash) |
209 : [this](const auto &block_id) {
210 5 : auto result = this->getBlock(block_id);
211 5 : return result.match(
212 : [](expected::Value<
213 : std::unique_ptr<shared_model::interface::Block>> &v)
214 : -> boost::optional<
215 : std::unique_ptr<shared_model::interface::Block>> {
216 5 : return std::move(v.value);
217 : },
218 : [this](const expected::Error<std::string> &e)
219 : -> boost::optional<
220 : std::unique_ptr<shared_model::interface::Block>> {
221 0 : log_->error(e.error);
222 0 : return boost::none;
223 : });
224 5 : }
225 : | [&hash, this](const auto &block) {
226 5 : auto it = std::find_if(
227 5 : block->transactions().begin(),
228 5 : block->transactions().end(),
229 : [&hash](const auto &tx) { return tx.hash() == hash; });
230 5 : if (it != block->transactions().end()) {
231 5 : return boost::make_optional<PostgresBlockQuery::wTransaction>(
232 5 : clone(*it));
233 : } else {
234 0 : log_->error("Failed to find transaction {} in block {}",
235 0 : hash.hex(),
236 0 : block->height());
237 : // return type specification for lambda breaks formatting.
238 : // That is why optional is constructed explicitly
239 0 : return boost::optional<PostgresBlockQuery::wTransaction>(
240 : boost::none);
241 : }
242 5 : };
243 0 : }
244 :
245 : boost::optional<TxCacheStatusType> PostgresBlockQuery::checkTxPresence(
246 : const shared_model::crypto::Hash &hash) {
247 6 : int res = -1;
248 6 : const auto &hash_str = hash.hex();
249 :
250 : try {
251 6 : sql_ << "SELECT status FROM tx_status_by_hash WHERE hash = :hash",
252 6 : soci::into(res), soci::use(hash_str);
253 6 : } catch (const std::exception &e) {
254 0 : log_->error("Failed to execute query: {}", e.what());
255 0 : return boost::none;
256 0 : }
257 :
258 : // res > 0 => Committed
259 : // res == 0 => Rejected
260 : // res < 0 => Missing
261 6 : if (res > 0) {
262 4 : return boost::make_optional<TxCacheStatusType>(
263 4 : tx_cache_status_responses::Committed{hash});
264 2 : } else if (res == 0) {
265 1 : return boost::make_optional<TxCacheStatusType>(
266 1 : tx_cache_status_responses::Rejected{hash});
267 : }
268 1 : return boost::make_optional<TxCacheStatusType>(
269 1 : tx_cache_status_responses::Missing{hash});
270 6 : }
271 :
272 : uint32_t PostgresBlockQuery::getTopBlockHeight() {
273 708 : return block_store_.last_id();
274 : }
275 :
276 : expected::Result<BlockQuery::wBlock, std::string>
277 : PostgresBlockQuery::getTopBlock() {
278 1494 : return getBlock(block_store_.last_id())
279 1494 : .match(
280 : [](expected::Value<
281 : std::unique_ptr<shared_model::interface::Block>> &v)
282 : -> expected::Result<BlockQuery::wBlock, std::string> {
283 976 : return expected::makeValue<BlockQuery::wBlock>(
284 976 : std::move(v.value));
285 0 : },
286 : [](expected::Error<std::string> &e)
287 : -> expected::Result<BlockQuery::wBlock, std::string> {
288 518 : return expected::makeError(std::move(e.error));
289 0 : });
290 0 : }
291 :
292 : expected::Result<std::unique_ptr<shared_model::interface::Block>,
293 : std::string>
294 : PostgresBlockQuery::getBlock(
295 : shared_model::interface::types::HeightType id) const {
296 1788 : auto serialized_block = block_store_.get(id);
297 1788 : if (not serialized_block) {
298 519 : auto error = boost::format("Failed to retrieve block with id %d") % id;
299 519 : return expected::makeError(error.str());
300 519 : }
301 1269 : return converter_->deserialize(bytesToString(*serialized_block));
302 1788 : }
303 : } // namespace ametsuchi
304 : } // namespace iroha
|