16 std::unique_lock<std::shared_mutex> l(m_mut);
17 bool running = m_running;
19 m_applied_dtxs.erase(dtx_id);
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,
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());
39 static constexpr auto dtx_buckets = 100000;
40 m_applied_dtxs.rehash(dtx_buckets);
41 m_prepared_dtxs.rehash(dtx_buckets);
43 static constexpr auto locked_buckets = 10000000;
44 m_locked.rehash(locked_buckets);
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");
51 m_logger->info(
"Preseeding complete -", m_uhs.size(),
"utxos");
56 auto locking_shard::read_preseed_file(
const std::string& preseed_file)
58 if(std::filesystem::exists(preseed_file)) {
59 auto in = std::ifstream(preseed_file, std::ios::binary);
60 in.seekg(0, std::ios::end);
65 in.seekg(0, std::ios::beg);
66 auto deser = istream_serializer(in);
68 static constexpr auto uhs_size_factor = 2;
69 auto bucket_count =
static_cast<unsigned long>(sz / cbdc::hash_size
71 m_uhs.rehash(bucket_count);
78 auto locking_shard::lock_outputs(std::vector<tx>&& txs,
80 -> std::optional<std::vector<bool>> {
81 std::unique_lock<std::shared_mutex> l(m_mut);
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;
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);
97 auto p = prepared_dtx();
99 p.m_txs = std::move(txs);
100 m_prepared_dtxs.emplace(dtx_id, std::move(p));
104 auto locking_shard::check_and_lock_tx(
const tx& t) ->
bool {
108 m_opts.m_sentinel_public_keys,
109 m_opts.m_attestation_threshold)) {
110 m_logger->warn(
"Received invalid compact transaction",
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()) {
124 for(
const auto& uhs_id : t.m_tx.m_inputs) {
126 auto n = m_uhs.extract(uhs_id);
128 m_locked.emplace(uhs_id);
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);
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",
149 auto& dtx = prepared_dtx_it->second.m_txs;
150 if(complete_txs.size() != dtx.size()) {
152 m_logger->fatal(
"Incorrect number of complete tx flags for apply",
158 for(
size_t i{0}; i < dtx.size(); i++) {
165 if(hash_in_shard_range(uhs_id) && complete_txs[i]) {
166 m_uhs.emplace(uhs_id);
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);
179 m_prepared_dtxs.erase(dtx_id);
180 m_applied_dtxs.insert(dtx_id);
184 void locking_shard::stop() {
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();
195 auto locking_shard::check_tx_id(
const hash_t& tx_id)
196 -> std::optional<bool> {
197 return m_completed_txs.contains(tx_id);
Interface for a locking shard.
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.
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.
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.