veriblock-pop-cpp
C++11 Libraries for leveraging VeriBlock Proof-Of-Proof blockchain technology.
mempool.hpp
1// Copyright (c) 2019-2022 Xenios SEZC
2// https://www.veriblock.org
3// Distributed under the MIT software license, see the accompanying
4// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
5
6#ifndef ALT_INTEGRATION_VERIBLOCK_MEMPOOL_HPP
7#define ALT_INTEGRATION_VERIBLOCK_MEMPOOL_HPP
8
9#include <map>
10#include <set>
11#include <unordered_map>
12#include <vector>
13
14#include "blockchain/alt_block_tree.hpp"
15#include "blockchain/mempool_block_tree.hpp"
16#include "entities/popdata.hpp"
17#include "mempool_result.hpp"
18#include "serde.hpp"
19#include "signals.hpp"
20#include "value_sorted_map.hpp"
21#include "veriblock/pop/assert.hpp"
22#include "veriblock/pop/json.hpp"
23#include "veriblock/pop/validation_state.hpp"
24
25namespace altintegration {
26
41struct MemPool {
42 enum Status { VALID = 0, FAILED_STATEFUL = 1, FAILED_STATELESS = 2 };
43
45 struct SubmitResult {
46 Status status = VALID;
47
48 // by default created in valid state
49 SubmitResult() = default;
50
51 SubmitResult(bool state) {
52 VBK_ASSERT_MSG(
53 state, "SubmitResult can be implicitly constructed from bool=true");
54 }
55
56 SubmitResult(Status status, bool /* ignore */) : status(status) {}
57
58 operator bool() const { return isAccepted(); }
59
60 bool isAccepted() const {
61 return status == VALID || status == FAILED_STATEFUL;
62 }
63
64 bool isValid() const { return status == VALID; }
65
66 bool isFailedStateless() const { return status == FAILED_STATELESS; }
67
68 bool isFailedStateful() const { return status == FAILED_STATEFUL; }
69 };
70
71 using vbk_hash_t = typename VbkBlock::prev_hash_t;
72
73 template <typename Payload>
74 using payload_map =
75 std::unordered_map<typename Payload::id_t, std::shared_ptr<Payload>>;
76
77 template <typename Payload>
78 using payload_value_sorted_map =
79 ValueSortedMap<typename Payload::id_t, std::shared_ptr<Payload>>;
80
81 using vbk_map_t = payload_map<VbkBlock>;
82 using atv_map_t = payload_map<ATV>;
83 using vtb_map_t = payload_map<VTB>;
84 using relations_map_t = payload_map<VbkPayloadsRelations>;
85
86 using vbk_value_sorted_map_t = payload_value_sorted_map<VbkBlock>;
87 using vtb_value_sorted_map_t = payload_value_sorted_map<VTB>;
88 using atv_value_sorted_map_t = payload_value_sorted_map<ATV>;
89
90 ~MemPool() = default;
91 MemPool(AltBlockTree& tree);
92
102 template <typename T,
103 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
104 VBK_CHECK_RETURN bool isKnown(const typename T::id_t& id,
105 const bool onlyInMempool = false) const {
106 // is `id` in mempool?
107 auto* inmempool = get<T>(id);
108 if (inmempool != nullptr) {
109 return true;
110 }
111
112 if (onlyInMempool) {
113 // we did not find in mempool, so exit early
114 return false;
115 }
116
117 const std::vector<uint8_t> v(id.begin(), id.end());
118
119 auto& tree = mempool_tree_.alt();
120 auto& fpl = tree.getFinalizedPayloadsIndex();
121 const auto* blockhash = fpl.find(v);
122 if (blockhash != nullptr) {
123 // finalized payloads index stores only payloads from finalized blocks.
124 // all finalized blocks are on active chain.
125 return true;
126 }
127
128 auto& pl = tree.getPayloadsIndex();
129 // is `id` in payloads index?
130 const auto& set = pl.find(v);
131 if (set.empty()) {
132 return false;
133 }
134
135 // check if any of candidates is on active chain
136 for (const auto& hash : set) {
137 const auto* candidate = tree.getBlockIndex(hash);
138 // all candidates must exist in a tree after split on
139 // PayloadsIndex+FinalizedPayloadsIndex. only finalized blocks may not
140 // exist in a tree.
141 VBK_ASSERT_MSG(candidate != nullptr, candidate->toPrettyString());
142 if (tree.getBestChain().contains(candidate)) {
143 // candidate is on main chain
144 return true;
145 }
146 }
147
148 // none of candidates are on main chain
149 return false;
150 }
151
153 template <typename T,
154 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
155 VBK_CHECK_RETURN const T* get(const typename T::id_t& id) const {
156 const auto& map = getMap<T>();
157 auto it = map.find(id);
158 if (it != map.end()) {
159 return it->second.get();
160 }
161
162 const auto& inflight = getInFlightMap<T>();
163 auto it2 = inflight.find(id);
164 if (it2 != inflight.end()) {
165 return it2->second.get();
166 }
167
168 return nullptr;
169 }
170
189 template <typename T,
190 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
192 bool doIsBlockOldCheck,
193 ValidationState& state) {
194 ReadStream stream(bytes);
195 T payload;
196 if (!DeserializeFromVbkEncoding(stream, payload, state)) {
197 return {FAILED_STATELESS,
198 state.Invalid("pop-mempool-submit-deserialize")};
199 }
200
201 return submit<T>(payload, doIsBlockOldCheck, state);
202 }
203
222 template <typename T,
223 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
224 VBK_CHECK_RETURN SubmitResult submit(const T& pl,
225 bool doIsBlockOldCheck,
226 ValidationState& state) {
227 return submit<T>(std::make_shared<T>(pl), doIsBlockOldCheck, state);
228 }
229
247 template <typename T,
248 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
249 VBK_CHECK_RETURN SubmitResult submit(const std::shared_ptr<T>& pl,
250 bool doIsBlockOldCheck,
251 ValidationState& state) {
252 (void)pl;
253 (void)state;
254 (void)doIsBlockOldCheck;
255 static_assert(sizeof(T) == 0, "Undefined type used in MemPool::submit");
256 return {};
257 }
258
260 template <typename T,
261 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
262 const payload_map<T>& getMap() const {
263 static_assert(sizeof(T) == 0, "Undefined type used in MemPool::getMap");
264 }
265
267 template <typename T,
268 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
269 const payload_value_sorted_map<T>& getInFlightMap() const {
270 static_assert(sizeof(T) == 0,
271 "Undefined type used in MemPool::getInFlightMap");
272 }
273
274 std::vector<BtcBlock::hash_t> getMissingBtcBlocks() const;
275
288 VBK_CHECK_RETURN PopData generatePopData();
294 VBK_CHECK_RETURN PopData generatePopData(
295 const std::function<void(const ATV&, const ValidationState&)>& onATV,
296 const std::function<void(const VTB&, const ValidationState&)>& onVTB,
297 const std::function<void(const VbkBlock&, const ValidationState&)>&
298 onVBK);
299
307 void removeAll(const PopData& popData);
308
313 void cleanUp();
314
318 void clear();
319
327 template <typename T,
328 typename = typename std::enable_if<IsPopPayload<T>::value>::type>
329 size_t onAccepted(std::function<void(const T& p)> f) {
330 auto& sig = getSignal<T>();
331 return sig.connect(f);
332 }
333
335 signals::Signal<void(const ATV& atv)> on_atv_accepted;
337 signals::Signal<void(const VTB& atv)> on_vtb_accepted;
339 signals::Signal<void(const VbkBlock& atv)> on_vbkblock_accepted;
340
341 private:
342 MemPoolBlockTree mempool_tree_;
343 // relations between VBK block and payloads
344 relations_map_t relations_;
345 vbk_map_t vbkblocks_;
346 atv_map_t stored_atvs_;
347 vtb_map_t stored_vtbs_;
348
349 atv_value_sorted_map_t atvs_in_flight_{
350 [](const std::shared_ptr<ATV>& v1,
351 const std::shared_ptr<ATV>& v2) -> bool {
352 return v1->blockOfProof.getHeight() < v2->blockOfProof.getHeight();
353 }};
354 vtb_value_sorted_map_t vtbs_in_flight_{
355 [](const std::shared_ptr<VTB>& v1,
356 const std::shared_ptr<VTB>& v2) -> bool {
357 return v1->containingBlock.getHeight() <
358 v2->containingBlock.getHeight();
359 }};
360 vbk_value_sorted_map_t vbkblocks_in_flight_{
361 [](const std::shared_ptr<VbkBlock>& v1,
362 const std::shared_ptr<VbkBlock>& v2) -> bool {
363 return v1->getHeight() < v2->getHeight();
364 }};
365
366 VbkPayloadsRelations& getOrPutVbkRelation(
367 const std::shared_ptr<VbkBlock>& block);
368
369 void tryConnectPayloads();
370
371 template <typename T>
372 void makePayloadConnected(const std::shared_ptr<T>& t) {
373 auto& signal = getSignal<T>();
374 auto& inflight = getInFlightMapMut<T>();
375 auto& connected = getMapMut<T>();
376
377 auto id = t->getId();
378 connected[id] = t;
379 inflight.erase(id);
380 signal.emit(*t);
381 }
382
383 template <typename POP>
384 void cleanupStale(std::set<std::shared_ptr<POP>,
385 VbkPayloadsRelations::AtvCombinedComparator>& c,
386 std::function<void(POP&)> remove) {
387 for (auto it = c.begin(); it != c.end();) {
388 auto& pl = **it;
389 ValidationState state;
390 auto valid = mempool_tree_.checkContextually(pl, state);
391 it = !valid ? (remove(pl), c.erase(it)) : std::next(it);
392 }
393 }
394
395 template <typename POP>
396 void cleanupStale(std::vector<std::shared_ptr<POP>>& c,
397 std::function<void(POP&)> remove) {
398 for (auto it = c.begin(); it != c.end();) {
399 auto& pl = **it;
400 ValidationState state;
401 auto valid = mempool_tree_.checkContextually(pl, state);
402 it = !valid ? (remove(pl), c.erase(it)) : std::next(it);
403 }
404 }
405
406 template <typename POP>
407 void cleanupStale(payload_value_sorted_map<POP>& c) {
408 for (auto it = c.begin(); it != c.end();) {
409 auto& pl = *it->second;
410 ValidationState state;
411 auto valid = mempool_tree_.checkContextually(pl, state);
412 it = !valid ? c.erase(it) : std::next(it);
413 }
414 }
415
416 template <typename Pop>
417 signals::Signal<void(const Pop&)>& getSignal() {
418 static_assert(sizeof(Pop) == 0, "Unknown type in getSignal");
419 }
420
422 template <typename T>
423 payload_map<T>& getMapMut() {
424 return const_cast<payload_map<T>&>(this->getMap<T>());
425 }
426
428 template <typename T>
429 payload_value_sorted_map<T>& getInFlightMapMut() {
430 return const_cast<payload_value_sorted_map<T>&>(this->getInFlightMap<T>());
431 }
432};
433
434// clang-format off
436template <> MemPool::SubmitResult MemPool::submit<ATV>(const std::shared_ptr<ATV>& atv, bool doIsBlockOldCheck, ValidationState& state);
438template <> MemPool::SubmitResult MemPool::submit<VTB>(const std::shared_ptr<VTB>& vtb, bool doIsBlockOldCheck, ValidationState& state);
440template <> MemPool::SubmitResult MemPool::submit<VbkBlock>(const std::shared_ptr<VbkBlock>& block, bool doIsBlockOldCheck, ValidationState& state);
442template <> const MemPool::payload_map<VbkBlock>& MemPool::getMap() const;
444template <> const MemPool::payload_map<ATV>& MemPool::getMap() const;
446template <> const MemPool::payload_map<VTB>& MemPool::getMap() const;
448template<> const MemPool::payload_value_sorted_map<VbkBlock>& MemPool::getInFlightMap() const;
450template<> const MemPool::payload_value_sorted_map<ATV>& MemPool::getInFlightMap() const;
452template<> const MemPool::payload_value_sorted_map<VTB>& MemPool::getInFlightMap() const;
454template <> signals::Signal<void(const ATV&)>& MemPool::getSignal();
456template <> signals::Signal<void(const VTB&)>& MemPool::getSignal();
458template <> signals::Signal<void(const VbkBlock&)>& MemPool::getSignal();
459// clang-format on
460
462namespace detail {
463
464template <typename Value, typename T>
465inline void mapToJson(Value& obj,
466 const MemPool& mp,
467 const std::string& key,
468 bool verbose = false) {
469 auto arr = json::makeEmptyArray<Value>();
470 for (auto& p : mp.getMap<T>()) {
471 if (verbose) {
472 json::arrayPushBack(arr, ToJSON<Value>(*p.second));
473 } else {
474 json::arrayPushBack(arr, ToJSON<Value>(p.first));
475 }
476 }
477 for (auto& p : mp.getInFlightMap<T>()) {
478 if (verbose) {
479 json::arrayPushBack(arr, ToJSON<Value>(*p.second));
480 } else {
481 json::arrayPushBack(arr, ToJSON<Value>(p.first));
482 }
483 }
484 json::putKV(obj, key, arr);
485}
486} // namespace detail
487
489template <typename Value>
490Value ToJSON(const MemPool& mp, bool verbose = false) {
491 auto obj = json::makeEmptyObject<Value>();
492
493 detail::mapToJson<Value, VbkBlock>(obj, mp, "vbkblocks", verbose);
494 detail::mapToJson<Value, ATV>(obj, mp, "atvs", verbose);
495 detail::mapToJson<Value, VTB>(obj, mp, "vtbs", verbose);
496
497 return obj;
498}
499
500} // namespace altintegration
501
502#endif // !
Class that is used for storing validation state.
Defines logging helpers.
Definition: block.hpp:14
bool DeserializeFromVbkEncoding(ReadStream &stream, AltBlockAddon &out, ValidationState &state)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Atlchain endorsement.
Definition: atv.hpp:36
Represents simplified view on Altchain's block tree, maintains VBK tree and BTC tree.
Result of submission to mempool.
Definition: mempool.hpp:45
Data structure, that stores in memory all valid payloads (ATV, VTB, VbkBlock) submitted by other peer...
Definition: mempool.hpp:41
VBK_CHECK_RETURN PopData generatePopData()
(POW) Miners should execute this to get POP content for "next block".
void removeAll(const PopData &popData)
Remove payloads from mempool by their IDs.
VBK_CHECK_RETURN const T * get(const typename T::id_t &id) const
getter for payloads stored in mempool
Definition: mempool.hpp:155
void clear()
Clear mempool from all payloads.
signals::Signal< void(const VTB &atv)> on_vtb_accepted
fires when new valid VTB is accepted to mempool
Definition: mempool.hpp:337
VBK_CHECK_RETURN SubmitResult submit(Slice< const uint8_t > bytes, bool doIsBlockOldCheck, ValidationState &state)
Add new payload to mempool.
Definition: mempool.hpp:191
size_t onAccepted(std::function< void(const T &p)> f)
Subscribe on "accepted" event - fires whenever new payload is added into mempool.
Definition: mempool.hpp:329
VBK_CHECK_RETURN SubmitResult submit(const std::shared_ptr< T > &pl, bool doIsBlockOldCheck, ValidationState &state)
Add new payload to mempool.
Definition: mempool.hpp:249
VBK_CHECK_RETURN bool isKnown(const typename T::id_t &id, const bool onlyInMempool=false) const
Use this method to determine if payload of type T with id id is already known to active chain.
Definition: mempool.hpp:104
signals::Signal< void(const VbkBlock &atv)> on_vbkblock_accepted
fires when new valid VbkBlock is accepted to mempool
Definition: mempool.hpp:339
void cleanUp()
Remove paylaods that are statefully invalid anymore (duplicated, staled payloads)
VBK_CHECK_RETURN PopData generatePopData(const std::function< void(const ATV &, const ValidationState &)> &onATV, const std::function< void(const VTB &, const ValidationState &)> &onVTB, const std::function< void(const VbkBlock &, const ValidationState &)> &onVBK)
This is an overloaded member function, provided for convenience. It differs from the above function o...
VBK_CHECK_RETURN SubmitResult submit(const T &pl, bool doIsBlockOldCheck, ValidationState &state)
Add new payload to mempool.
Definition: mempool.hpp:224
signals::Signal< void(const ATV &atv)> on_atv_accepted
fires when new valid ATV is accepted to mempool
Definition: mempool.hpp:335
Represents ALT block body of POP-related info.
Definition: popdata.hpp:27
Binary reading stream, that is useful during binary deserialization.
Definition: read_stream.hpp:22
Non-owning contiguous array.
Definition: slice.hpp:22
Veriblock to Bitcoin publication, committed to Veriblock blockchain in containingBlock.
Definition: vtb.hpp:37
Veriblock block.
Definition: vbkblock.hpp:32