OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
locking_shard.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 "locking_shard.hpp"
7
8#include "messages.hpp"
13
14namespace cbdc::locking_shard {
15 auto locking_shard::discard_dtx(const hash_t& dtx_id) -> bool {
16 std::unique_lock<std::shared_mutex> l(m_mut);
17 bool running = m_running;
18 if(running) {
19 m_applied_dtxs.erase(dtx_id);
20 }
21 return running;
22 }
23
25 const std::pair<uint8_t, uint8_t>& output_range,
26 std::shared_ptr<logging::log> logger,
27 size_t completed_txs_cache_size,
28 const std::string& preseed_file,
29 config::options opts)
30 : interface(output_range),
31 m_logger(std::move(logger)),
32 m_completed_txs(completed_txs_cache_size),
33 m_opts(std::move(opts)) {
34 m_uhs.max_load_factor(std::numeric_limits<float>::max());
35 m_applied_dtxs.max_load_factor(std::numeric_limits<float>::max());
36 m_prepared_dtxs.max_load_factor(std::numeric_limits<float>::max());
37 m_locked.max_load_factor(std::numeric_limits<float>::max());
38
39 static constexpr auto dtx_buckets = 100000;
40 m_applied_dtxs.rehash(dtx_buckets);
41 m_prepared_dtxs.rehash(dtx_buckets);
42
43 static constexpr auto locked_buckets = 10000000;
44 m_locked.rehash(locked_buckets);
45
46 if(!preseed_file.empty()) {
47 m_logger->info("Reading preseed file into memory");
48 if(!read_preseed_file(preseed_file)) {
49 m_logger->error("Preseeding failed");
50 } else {
51 m_logger->info("Preseeding complete -", m_uhs.size(), "utxos");
52 }
53 }
54 }
55
56 auto locking_shard::read_preseed_file(const std::string& preseed_file)
57 -> bool {
58 if(std::filesystem::exists(preseed_file)) {
59 auto in = std::ifstream(preseed_file, std::ios::binary);
60 in.seekg(0, std::ios::end);
61 auto sz = in.tellg();
62 if(sz == -1) {
63 return false;
64 }
65 in.seekg(0, std::ios::beg);
66 auto deser = istream_serializer(in);
67 m_uhs.clear();
68 static constexpr auto uhs_size_factor = 2;
69 auto bucket_count = static_cast<unsigned long>(sz / cbdc::hash_size
70 * uhs_size_factor);
71 m_uhs.rehash(bucket_count);
72 deser >> m_uhs;
73 return true;
74 }
75 return false;
76 }
77
78 auto locking_shard::lock_outputs(std::vector<tx>&& txs,
79 const hash_t& dtx_id)
80 -> std::optional<std::vector<bool>> {
81 std::unique_lock<std::shared_mutex> l(m_mut);
82 if(!m_running) {
83 return std::nullopt;
84 }
85
86 auto prepared_dtx_it = m_prepared_dtxs.find(dtx_id);
87 if(prepared_dtx_it != m_prepared_dtxs.end()) {
88 return prepared_dtx_it->second.m_results;
89 }
90
91 auto ret = std::vector<bool>();
92 ret.reserve(txs.size());
93 for(auto&& tx : txs) {
94 auto success = check_and_lock_tx(tx);
95 ret.push_back(success);
96 }
97 auto p = prepared_dtx();
98 p.m_results = ret;
99 p.m_txs = std::move(txs);
100 m_prepared_dtxs.emplace(dtx_id, std::move(p));
101 return ret;
102 }
103
104 auto locking_shard::check_and_lock_tx(const tx& t) -> bool {
105 bool success{true};
107 t.m_tx,
108 m_opts.m_sentinel_public_keys,
109 m_opts.m_attestation_threshold)) {
110 m_logger->warn("Received invalid compact transaction",
111 to_string(t.m_tx.m_id));
112 success = false;
113 }
114 if(success) {
115 for(const auto& uhs_id : t.m_tx.m_inputs) {
116 if(hash_in_shard_range(uhs_id)
117 && m_uhs.find(uhs_id) == m_uhs.end()) {
118 success = false;
119 break;
120 }
121 }
122 }
123 if(success) {
124 for(const auto& uhs_id : t.m_tx.m_inputs) {
125 if(hash_in_shard_range(uhs_id)) {
126 auto n = m_uhs.extract(uhs_id);
127 assert(!n.empty());
128 m_locked.emplace(uhs_id);
129 }
130 }
131 }
132 return success;
133 }
134
135 auto locking_shard::apply_outputs(std::vector<bool>&& complete_txs,
136 const hash_t& dtx_id) -> bool {
137 std::unique_lock<std::shared_mutex> l(m_mut);
138 if(!m_running) {
139 return false;
140 }
141 auto prepared_dtx_it = m_prepared_dtxs.find(dtx_id);
142 if(prepared_dtx_it == m_prepared_dtxs.end()) {
143 if(m_applied_dtxs.find(dtx_id) == m_applied_dtxs.end()) {
144 m_logger->fatal("Unable to find dtx data for apply",
145 to_string(dtx_id));
146 }
147 return true;
148 }
149 auto& dtx = prepared_dtx_it->second.m_txs;
150 if(complete_txs.size() != dtx.size()) {
151 // This would only happen due to a bug in the controller
152 m_logger->fatal("Incorrect number of complete tx flags for apply",
153 to_string(dtx_id),
154 complete_txs.size(),
155 "vs",
156 dtx.size());
157 }
158 for(size_t i{0}; i < dtx.size(); i++) {
159 auto&& tx = dtx[i];
160 if(hash_in_shard_range(tx.m_tx.m_id)) {
161 m_completed_txs.add(tx.m_tx.m_id);
162 }
163
164 for(auto&& uhs_id : tx.m_tx.m_uhs_outputs) {
165 if(hash_in_shard_range(uhs_id) && complete_txs[i]) {
166 m_uhs.emplace(uhs_id);
167 }
168 }
169 for(auto&& uhs_id : tx.m_tx.m_inputs) {
170 if(hash_in_shard_range(uhs_id)) {
171 auto was_locked = m_locked.erase(uhs_id);
172 if(!complete_txs[i] && (was_locked != 0U)) {
173 m_uhs.emplace(uhs_id);
174 }
175 }
176 }
177 }
178
179 m_prepared_dtxs.erase(dtx_id);
180 m_applied_dtxs.insert(dtx_id);
181 return true;
182 }
183
184 void locking_shard::stop() {
185 m_running = false;
186 }
187
188 auto locking_shard::check_unspent(const hash_t& uhs_id)
189 -> std::optional<bool> {
190 std::shared_lock<std::shared_mutex> l(m_mut);
191 return m_uhs.find(uhs_id) != m_uhs.end()
192 || m_locked.find(uhs_id) != m_locked.end();
193 }
194
195 auto locking_shard::check_tx_id(const hash_t& tx_id)
196 -> std::optional<bool> {
197 return m_completed_txs.contains(tx_id);
198 }
199}
auto discard_dtx(const hash_t &dtx_id) -> bool final
Discards any cached information about a given distributed transaction.
Tools for reading options from a configuration file and building application-specific parameter sets ...
auto hash_in_shard_range(const shard_range_t &range, const hash_t &val) -> bool
Checks if a hash is in the given range handled.
Definition config.cpp:736
auto check_attestations(const transaction::compact_tx &tx, const std::unordered_set< pubkey_t, hashing::null > &pubkeys, size_t threshold) -> bool
Validates the sentinel attestations attached to a compact transaction.
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
auto to_string(const hash_t &val) -> std::string
Converts a hash to a hexadecimal string.
Project-wide configuration options.
Definition config.hpp:132
Transaction type processed by locking shards.
transaction::compact_tx m_tx
Compact TX.
std::vector< hash_t > m_inputs
The set of hashes of the transaction's inputs.
hash_t m_id
The hash of the full transaction returned by tx_id.
std::vector< hash_t > m_uhs_outputs
The set of hashes of the new outputs created in the transaction.