OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
watchtower.cpp
Go to the documentation of this file.
1// Copyright (c) 2021 MIT Digital Currency Initiative,
2// Federal Reserve Bank of Boston
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include "watchtower.hpp"
7
8#include "status_update.hpp"
11
12#include <algorithm>
13
14namespace cbdc::watchtower {
16 std::unique_lock lk(m_bc_mut);
17 m_bc.push_block(std::move(blk));
18 }
19
20 void watchtower::add_errors(std::vector<tx_error>&& errs) {
21 std::shared_lock lk0(m_bc_mut, std::defer_lock);
22 std::unique_lock lk1(m_ec_mut, std::defer_lock);
23 std::lock(lk0, lk1);
24 auto repeated_tx_filter = [&](const auto& err) -> bool {
25 auto res = false;
26 auto check_uhs = [&](const hash_t& err_tx_id, auto&& info) {
27 for(const auto& uhs : info.input_uhs_ids()) {
28 if(auto spent = m_bc.check_spent(uhs)) {
29 auto [height, tx_id] = spent.value();
30 if(err_tx_id == tx_id) {
31 res = true;
32 return;
33 }
34 }
35 if(auto unspent = m_bc.check_unspent(uhs)) {
36 auto [height, tx_id] = unspent.value();
37 if(err_tx_id == tx_id) {
38 res = true;
39 return;
40 }
41 }
42 }
43 };
44 std::visit(overloaded{[&](tx_error_inputs_spent&& info) {
45 check_uhs(err.tx_id(), std::move(info));
46 },
47 [&](tx_error_inputs_dne&& info) {
48 check_uhs(err.tx_id(), std::move(info));
49 },
50 [&](auto&& /* info */) {}},
51 std::move(err.info()));
52 return res;
53 };
54 errs.erase(
55 std::remove_if(errs.begin(), errs.end(), repeated_tx_filter),
56 errs.end());
57 m_ec.push_errors(std::move(errs));
58 }
59
60 auto watchtower::check_uhs_id_statuses(const std::vector<hash_t>& uhs_ids,
61 const hash_t& tx_id,
62 bool internal_err,
63 bool tx_err,
64 uint64_t best_height)
65 -> std::vector<status_update_state> {
66 std::vector<status_update_state> states;
67 states.reserve(uhs_ids.size());
68 for(const auto& uhs_id : uhs_ids) {
69 auto found_status = false;
70 if(internal_err) {
71 states.emplace_back(
73 best_height,
74 uhs_id});
75 found_status = true;
76 } else if(tx_err) {
77 if(auto uhs_err = m_ec.check_uhs_id(uhs_id)) {
78 states.emplace_back(
79 status_update_state{search_status::invalid_input,
80 best_height,
81 uhs_id});
82 } else {
83 states.emplace_back(
84 status_update_state{search_status::tx_rejected,
85 best_height,
86 uhs_id});
87 }
88 found_status = true;
89 }
90 if(auto spent = m_bc.check_spent(uhs_id)) {
91 auto [height, s_tx_id] = spent.value();
92 if(s_tx_id == tx_id) {
93 states.emplace_back(
94 status_update_state{search_status::spent,
95 height,
96 uhs_id});
97 found_status = true;
98 }
99 } else if(auto unspent = m_bc.check_unspent(uhs_id)) {
100 auto [height, us_tx_id] = unspent.value();
101 if(us_tx_id == tx_id) {
102 states.emplace_back(
103 status_update_state{search_status::unspent,
104 height,
105 uhs_id});
106 found_status = true;
107 }
108 }
109 if(!found_status) {
110 states.emplace_back(
111 status_update_state{search_status::no_history,
112 best_height,
113 uhs_id});
114 }
115 }
116 return states;
117 }
118
119 auto
121 -> std::unique_ptr<response> {
122 std::unordered_map<hash_t,
123 std::vector<status_update_state>,
125 chks;
126 {
127 std::shared_lock lk0(m_bc_mut, std::defer_lock);
128 std::shared_lock lk1(m_ec_mut, std::defer_lock);
129 std::lock(lk0, lk1);
130 auto best_height = m_bc.best_block_height();
131 for(const auto& [tx_id, uhs_ids] : req.uhs_ids()) {
132 auto tx_err = m_ec.check_tx_id(tx_id);
133 bool internal_err{false};
134 if(tx_err.has_value()
135 && (std::holds_alternative<tx_error_sync>(
136 tx_err.value().info())
137 || std::holds_alternative<tx_error_stxo_range>(
138 tx_err.value().info()))) {
139 internal_err = true;
140 }
141 auto states = check_uhs_id_statuses(uhs_ids,
142 tx_id,
143 internal_err,
144 tx_err.has_value(),
145 best_height);
146 chks.emplace(std::make_pair(tx_id, std::move(states)));
147 }
148 }
149
150 return std::make_unique<response>(status_request_check_success{chks});
151 }
152
154 const best_block_height_request& /* unused */)
155 -> std::unique_ptr<response> {
156 std::shared_lock lk(m_bc_mut);
157 return std::make_unique<response>(
158 best_block_height_response{m_bc.best_block_height()});
159 }
160
161 watchtower::watchtower(size_t block_cache_size, size_t error_cache_size)
162 : m_bc{block_cache_size},
163 m_ec{error_cache_size} {}
164
166 const best_block_height_request& /* unused */) const -> bool {
167 return true;
168 }
169
173
175 const best_block_height_response& rhs) const -> bool {
176 return (rhs.m_height == m_height);
177 }
178
180 : m_height(height) {}
181
185
186 auto best_block_height_response::height() const -> uint64_t {
187 return m_height;
188 }
189
190 auto request::operator==(const request& rhs) const -> bool {
191 return m_req == rhs.m_req;
192 }
193
194 request::request(request_t req) : m_req(std::move(req)) {}
195
199
200 auto request::payload() const -> const request_t& {
201 return m_req;
202 }
203
204 auto response::operator==(const response& rhs) const -> bool {
205 return m_resp == rhs.m_resp;
206 }
207
208 response::response(response_t resp) : m_resp(std::move(resp)) {}
209
213
214 auto response::payload() const -> const response_t& {
215 return m_resp;
216 }
217}
Interface for serializing objects into and out of raw bytes representations.
Contains the watchtower's known best block height.
auto height() const -> uint64_t
Returns the states of a set of UHS IDs, following the order of the UHS IDs in the containing StatusUp...
auto operator==(const best_block_height_response &rhs) const -> bool
void push_block(cbdc::atomizer::block &&blk)
Moves a block into the block cache, evicting the oldest block if the cache has reached its maximum si...
auto check_unspent(const hash_t &uhs_id) const -> std::optional< block_cache_result >
Checks to see if the given UHS ID is spendable according to the blocks in the cache.
auto check_spent(const hash_t &uhs_id) const -> std::optional< block_cache_result >
Checks to see if the given UHS ID has been spent according to the blocks in the cache.
void push_errors(std::vector< tx_error > &&errs)
Moves an error into the error cache, evicting the oldest error if the cache has reached its maximum s...
RPC request message to the watchtower external endpoint.
auto payload() const -> const request_t &
Return the request payload.
auto operator==(const request &rhs) const -> bool
std::variant< status_update_request, best_block_height_request > request_t
RPC response message from the watchtower external endpoint.
std::variant< status_request_check_success, best_block_height_response > response_t
auto payload() const -> const response_t &
Return the response payload.
auto operator==(const response &rhs) const -> bool
Indicates a successful check request, sent with a StatusUpdateResponse.
Network request to interact with the Watchtower's status update service.
Represents the internal state of an ongoing status update request.
Indicates a shard that tried to process a given transaction could not locate one or more of the trans...
Indicates that the given transaction contains one or more inputs that have already been spent in othe...
void add_errors(std::vector< tx_error > &&errs)
Adds an error from an internal component to the Watchtower's error cache.
auto handle_status_update_request(const status_update_request &req) -> std::unique_ptr< response >
Composes a response to a status update request based on the data available.
void add_block(cbdc::atomizer::block &&blk)
Adds a new block from the Atomizer to the Watchtower.
auto handle_best_block_height_request(const best_block_height_request &req) -> std::unique_ptr< response >
Composes a response to a status update best block height request.
@ invalid_input
The transaction processing system rejected the transaction because the requested UHS ID was already s...
@ spent
The STXO set contains the requested UHS ID.
@ unspent
The UTXO set contains the requested UHS ID.
@ internal_error
The transaction processing system failed to process the transaction containing the requested UHS ID d...
@ tx_rejected
The transaction processing system rejected the requested UHS ID's transaction due to a problem with a...
@ no_history
The Watchtower has finished scanning the block history for the UHS ID in the request and hasn't found...
auto get_variant(serializer &deser) -> std::variant< Ts... >
Deserializes a variant where the alternatives are all default constructible or all are not default co...
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
Batch of compact transactions settled by the atomizer.
Definition block.hpp:19
SipHash function to generate STL data structure hash keys for system IDs.
Definition hashmap.hpp:27
Variant handler template.
Request the watchtower's known best block height.
auto operator==(const best_block_height_request &) const -> bool
Watchtower core functionality.