OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
config.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 "config.hpp"
7
8#include <algorithm>
9#include <cassert>
10#include <sstream>
11
12namespace cbdc::config {
13 auto parse_ip_port(const std::string& in_str) -> network::endpoint_t {
14 // TODO: error handling for string parsing
15 std::istringstream ss(in_str);
16
17 std::string host;
18 [[maybe_unused]] const auto& host_res = std::getline(ss, host, ':');
19 assert(host_res);
20
21 std::string port_str;
22 [[maybe_unused]] const auto& port_res
23 = std::getline(ss, port_str, ':');
24 assert(port_res);
25
26 auto port = std::stoul(port_str);
27 assert(port <= std::numeric_limits<unsigned short>::max());
28
29 return {host, static_cast<unsigned short>(port)};
30 }
31
32 void get_shard_key_prefix(std::stringstream& ss, size_t shard_id) {
33 ss << shard_prefix << shard_id << config_separator;
34 }
35
36 auto get_shard_endpoint_key(size_t shard_id) -> std::string {
37 std::stringstream ss;
38 get_shard_key_prefix(ss, shard_id);
39 ss << endpoint_postfix;
40 return ss.str();
41 }
42
43 auto get_atomizer_endpoint_key(size_t atomizer_id) -> std::string {
44 std::stringstream ss;
45 ss << atomizer_prefix << atomizer_id << config_separator
46 << endpoint_postfix;
47 return ss.str();
48 }
49
50 auto get_atomizer_raft_endpoint_key(size_t atomizer_id) -> std::string {
51 std::stringstream ss;
52 ss << atomizer_prefix << atomizer_id << config_separator
53 << raft_endpoint_postfix;
54 return ss.str();
55 }
56
57 auto get_atomizer_loglevel_key(size_t atomizer_id) -> std::string {
58 std::stringstream ss;
59 ss << atomizer_prefix << atomizer_id << config_separator
60 << loglevel_postfix;
61 return ss.str();
62 }
63
64 auto get_sentinel_endpoint_key(size_t sentinel_id) -> std::string {
65 std::stringstream ss;
66 ss << sentinel_prefix << sentinel_id << config_separator
67 << endpoint_postfix;
68 return ss.str();
69 }
70
71 auto get_shard_db_key(size_t shard_id) -> std::string {
72 std::stringstream ss;
73 get_shard_key_prefix(ss, shard_id);
74 ss << db_postfix;
75 return ss.str();
76 }
77
78 auto get_shard_end_key(size_t shard_id) -> std::string {
79 std::stringstream ss;
80 get_shard_key_prefix(ss, shard_id);
81 ss << end_postfix;
82 return ss.str();
83 }
84
85 auto get_shard_start_key(size_t shard_id) -> std::string {
86 std::stringstream ss;
87 get_shard_key_prefix(ss, shard_id);
88 ss << start_postfix;
89 return ss.str();
90 }
91
92 void get_archiver_key_prefix(std::stringstream& ss, size_t archiver_id) {
93 ss << archiver_prefix << archiver_id << config_separator;
94 }
95
96 auto get_archiver_endpoint_key(size_t archiver_id) -> std::string {
97 std::stringstream ss;
98 get_archiver_key_prefix(ss, archiver_id);
99 ss << endpoint_postfix;
100 return ss.str();
101 }
102
103 auto get_archiver_loglevel_key(size_t archiver_id) -> std::string {
104 std::stringstream ss;
105 get_archiver_key_prefix(ss, archiver_id);
106 ss << loglevel_postfix;
107 return ss.str();
108 }
109
110 auto get_archiver_db_key(size_t archiver_id) -> std::string {
111 std::stringstream ss;
112 get_archiver_key_prefix(ss, archiver_id);
113 ss << db_postfix;
114 return ss.str();
115 }
116
117 auto get_shard_loglevel_key(size_t shard_id) -> std::string {
118 std::stringstream ss;
119 get_shard_key_prefix(ss, shard_id);
120 ss << loglevel_postfix;
121 return ss.str();
122 }
123
124 void get_sentinel_key_prefix(std::stringstream& ss, size_t sentinel_id) {
125 ss << sentinel_prefix << sentinel_id << config_separator;
126 }
127
128 auto get_sentinel_loglevel_key(size_t sentinel_id) -> std::string {
129 std::stringstream ss;
130 get_sentinel_key_prefix(ss, sentinel_id);
131 ss << loglevel_postfix;
132 return ss.str();
133 }
134
135 void get_watchtower_key_prefix(std::stringstream& ss,
136 size_t watchtower_id) {
137 ss << watchtower_prefix << watchtower_id << config_separator;
138 }
139
140 auto get_watchtower_client_endpoint_key(size_t watchtower_id)
141 -> std::string {
142 std::stringstream ss;
143 get_watchtower_key_prefix(ss, watchtower_id);
144 ss << watchtower_client_ep_postfix;
145 return ss.str();
146 }
147
148 auto get_watchtower_internal_endpoint_key(size_t watchtower_id)
149 -> std::string {
150 std::stringstream ss;
151 get_watchtower_key_prefix(ss, watchtower_id);
152 ss << watchtower_internal_ep_postfix;
153 return ss.str();
154 }
155
156 auto get_watchtower_loglevel_key(size_t watchtower_id) -> std::string {
157 std::stringstream ss;
158 get_watchtower_key_prefix(ss, watchtower_id);
159 ss << loglevel_postfix;
160 return ss.str();
161 }
162
163 auto get_shard_node_count_key(size_t shard_id) -> std::string {
164 std::stringstream ss;
165 get_shard_key_prefix(ss, shard_id);
166 ss << count_postfix;
167 return ss.str();
168 }
169
170 auto get_shard_raft_endpoint_key(size_t shard_id, size_t node_id)
171 -> std::string {
172 std::stringstream ss;
173 get_shard_key_prefix(ss, shard_id);
174 ss << node_id << config_separator << raft_endpoint_postfix;
175 return ss.str();
176 }
177
178 auto get_shard_endpoint_key(size_t shard_id, size_t node_id)
179 -> std::string {
180 std::stringstream ss;
181 get_shard_key_prefix(ss, shard_id);
182 ss << node_id << config_separator << endpoint_postfix;
183 return ss.str();
184 }
185
186 auto get_shard_readonly_endpoint_key(size_t shard_id, size_t node_id)
187 -> std::string {
188 std::stringstream ss;
189 get_shard_key_prefix(ss, shard_id);
190 ss << node_id << config_separator << readonly << config_separator
191 << endpoint_postfix;
192 return ss.str();
193 }
194
195 void get_coordinator_key_prefix(std::stringstream& ss,
196 size_t coordinator_id) {
197 ss << coordinator_prefix << coordinator_id << config_separator;
198 }
199
200 auto get_coordinator_endpoint_key(size_t coordinator_id, size_t node_id)
201 -> std::string {
202 std::stringstream ss;
203 get_coordinator_key_prefix(ss, coordinator_id);
204 ss << node_id << config_separator << endpoint_postfix;
205 return ss.str();
206 }
207
208 auto get_coordinator_raft_endpoint_key(size_t coordinator_id,
209 size_t node_id) -> std::string {
210 std::stringstream ss;
211 get_coordinator_key_prefix(ss, coordinator_id);
212 ss << node_id << config_separator << raft_endpoint_postfix;
213 return ss.str();
214 }
215
216 auto get_coordinator_node_count_key(size_t coordinator_id) -> std::string {
217 std::stringstream ss;
218 get_coordinator_key_prefix(ss, coordinator_id);
219 ss << count_postfix;
220 return ss.str();
221 }
222
223 auto get_coordinator_loglevel_key(size_t coordinator_id) -> std::string {
224 std::stringstream ss;
225 ss << coordinator_prefix << coordinator_id << config_separator
226 << loglevel_postfix;
227 return ss.str();
228 }
229
230 auto get_sentinel_private_key_key(size_t sentinel_id) -> std::string {
231 auto ss = std::stringstream();
232 get_sentinel_key_prefix(ss, sentinel_id);
233 ss << private_key_postfix;
234 return ss.str();
235 }
236
237 auto get_sentinel_public_key_key(size_t sentinel_id) -> std::string {
238 auto ss = std::stringstream();
239 get_sentinel_key_prefix(ss, sentinel_id);
240 ss << public_key_postfix;
241 return ss.str();
242 }
243
244 auto read_shard_endpoints(options& opts, const parser& cfg)
245 -> std::optional<std::string> {
246 const auto shard_count = cfg.get_ulong(shard_count_key).value_or(0);
247 if(opts.m_twophase_mode) {
248 opts.m_locking_shard_endpoints.resize(shard_count);
249 opts.m_locking_shard_raft_endpoints.resize(shard_count);
250 opts.m_locking_shard_readonly_endpoints.resize(shard_count);
251 for(size_t i{0}; i < shard_count; i++) {
252 const auto node_count_key = get_shard_node_count_key(i);
253 const auto node_count = cfg.get_ulong(node_count_key);
254 if(!node_count) {
255 return "No node count specified for shard "
256 + std::to_string(i) + " (" + node_count_key + ")";
257 }
258 for(size_t j{0}; j < *node_count; j++) {
259 const auto raft_ep_key = get_shard_raft_endpoint_key(i, j);
260 const auto raft_ep = cfg.get_endpoint(raft_ep_key);
261 if(!raft_ep) {
262 return "No raft endpoint specified for shard "
263 + std::to_string(i) + " node " + std::to_string(j)
264 + " (" + raft_ep_key + ")";
265 }
266 opts.m_locking_shard_raft_endpoints[i].emplace_back(
267 *raft_ep);
268
269 const auto ep_key = get_shard_endpoint_key(i, j);
270 const auto ep = cfg.get_endpoint(ep_key);
271 if(!ep) {
272 return "No endpoint specified for shard "
273 + std::to_string(i) + " node "
274 + std::to_string(j);
275 }
276 opts.m_locking_shard_endpoints[i].emplace_back(*ep);
277
278 const auto ro_ep_key
280 const auto ro_ep = cfg.get_endpoint(ro_ep_key);
281 if(!ro_ep) {
282 return "No read-only endpoint specified for shard "
283 + std::to_string(i) + " node " + std::to_string(j)
284 + " (" + ro_ep_key + ")";
285 }
286 opts.m_locking_shard_readonly_endpoints[i].emplace_back(
287 *ro_ep);
288 }
289 }
290 }
291
292 return std::nullopt;
293 }
294
295 auto read_shard_options(options& opts, const parser& cfg)
296 -> std::optional<std::string> {
297 const auto shard_count = cfg.get_ulong(shard_count_key).value_or(0);
298 for(size_t i{0}; i < shard_count; i++) {
299 if(!opts.m_twophase_mode) {
300 const auto shard_ep_key = get_shard_endpoint_key(i);
301 const auto shard_ep = cfg.get_endpoint(shard_ep_key);
302 if(!shard_ep) {
303 return "No endpoint specified for shard "
304 + std::to_string(i) + " (" + shard_ep_key + ")";
305 }
306 opts.m_shard_endpoints.push_back(*shard_ep);
307
308 const auto shard_db_str = get_shard_db_key(i);
309 const auto shard_db = cfg.get_string(shard_db_str);
310 if(!shard_db) {
311 return "No db directory specified for shard "
312 + std::to_string(i) + " (" + shard_db_str + ")";
313 }
314 opts.m_shard_db_dirs.push_back(*shard_db);
315 }
316
317 const auto shard_loglevel_key = get_shard_loglevel_key(i);
318 const auto shard_loglevel = cfg.get_loglevel(shard_loglevel_key)
319 .value_or(defaults::log_level);
320 opts.m_shard_loglevels.push_back(shard_loglevel);
321
322 const auto start_key = get_shard_start_key(i);
323 const auto range_start = cfg.get_ulong(start_key);
324 if(!range_start) {
325 return "No range start specified for shard "
326 + std::to_string(i) + " (" + start_key + ")";
327 }
328 const auto end_key = get_shard_end_key(i);
329 const auto range_end = cfg.get_ulong(end_key);
330 if(!range_end) {
331 return "No range end specified for shard " + std::to_string(i)
332 + " (" + end_key + ")";
333 }
334 const auto shard_range
335 = std::make_pair(static_cast<uint8_t>(*range_start),
336 static_cast<uint8_t>(*range_end));
337 opts.m_shard_ranges.push_back(shard_range);
338 }
339
340 opts.m_shard_completed_txs_cache_size
341 = cfg.get_ulong(shard_completed_txs_cache_size)
342 .value_or(opts.m_shard_completed_txs_cache_size);
343
344 opts.m_seed_from = cfg.get_ulong(seed_from).value_or(opts.m_seed_from);
345 opts.m_seed_to = cfg.get_ulong(seed_to).value_or(opts.m_seed_to);
346 if(opts.m_seed_from != opts.m_seed_to) {
347 auto priv_str = cfg.get_string(seed_privkey);
348 if(!priv_str) {
349 return "Seed range defined but missing a private key";
350 }
351 if(priv_str.value().size() != sizeof(privkey_t) * 2) {
352 return "Invalid seed private key length";
353 }
354 opts.m_seed_privkey = hash_from_hex(priv_str.value());
355 opts.m_seed_value
356 = cfg.get_ulong(seed_value).value_or(opts.m_seed_value);
357 }
358
359 return std::nullopt;
360 }
361
363 -> std::optional<std::string> {
364 const auto coordinator_count
365 = cfg.get_ulong(coordinator_count_key).value_or(0);
366 opts.m_coordinator_endpoints.resize(coordinator_count);
367 opts.m_coordinator_raft_endpoints.resize(coordinator_count);
368 for(size_t i{0}; i < coordinator_count; i++) {
369 const auto loglevel_key = get_coordinator_loglevel_key(i);
370 const auto coordinator_loglevel
371 = cfg.get_loglevel(loglevel_key).value_or(defaults::log_level);
372 opts.m_coordinator_loglevels.push_back(coordinator_loglevel);
373
374 const auto node_count_key = get_coordinator_node_count_key(i);
375 const auto node_count = cfg.get_ulong(node_count_key);
376 if(!node_count) {
377 return "No node count specified for coordinator "
378 + std::to_string(i) + " (" + node_count_key + ")";
379 }
380 for(size_t j{0}; j < *node_count; j++) {
381 const auto raft_ep_key
383 const auto raft_ep = cfg.get_endpoint(raft_ep_key);
384 if(!raft_ep) {
385 return "No raft endpoint specified for coordinator "
386 + std::to_string(i) + " node " + std::to_string(j)
387 + " (" + raft_ep_key + ")";
388 }
389 opts.m_coordinator_raft_endpoints[i].emplace_back(*raft_ep);
390
391 const auto ep_key = get_coordinator_endpoint_key(i, j);
392 const auto ep = cfg.get_endpoint(ep_key);
393 if(!ep) {
394 return "No endpoint specified for coordinator "
395 + std::to_string(i) + " node " + std::to_string(j)
396 + " (" + ep_key + ")";
397 }
398 opts.m_coordinator_endpoints[i].emplace_back(*ep);
399 }
400 }
401
402 opts.m_coordinator_max_threads
403 = cfg.get_ulong(coordinator_max_threads)
404 .value_or(opts.m_coordinator_max_threads);
405
406 return std::nullopt;
407 }
408
409 auto read_sentinel_options(options& opts, const parser& cfg)
410 -> std::optional<std::string> {
411 opts.m_attestation_threshold
412 = cfg.get_ulong(attestation_threshold_key)
413 .value_or(opts.m_attestation_threshold);
414
415 const auto sentinel_count
416 = cfg.get_ulong(sentinel_count_key).value_or(0);
417 for(size_t i{0}; i < sentinel_count; i++) {
418 const auto sentinel_ep_key = get_sentinel_endpoint_key(i);
419 const auto sentinel_ep = cfg.get_endpoint(sentinel_ep_key);
420 if(!sentinel_ep) {
421 return "No endpoint specified for sentinel "
422 + std::to_string(i) + " (" + sentinel_ep_key + ")";
423 }
424 opts.m_sentinel_endpoints.push_back(*sentinel_ep);
425
426 const auto sentinel_loglevel_key = get_sentinel_loglevel_key(i);
427 const auto sentinel_loglevel
428 = cfg.get_loglevel(sentinel_loglevel_key)
429 .value_or(defaults::log_level);
430 opts.m_sentinel_loglevels.push_back(sentinel_loglevel);
431
432 const auto sentinel_private_key_key
434 const auto sentinel_private_key
435 = cfg.get_string(sentinel_private_key_key);
436 if(sentinel_private_key.has_value()) {
437 auto key = hash_from_hex(sentinel_private_key.value());
438 opts.m_sentinel_private_keys[i] = key;
439 }
440
441 const auto sentinel_public_key_key
443 const auto sentinel_public_key
444 = cfg.get_string(sentinel_public_key_key);
445 if(!sentinel_public_key.has_value()) {
446 if(opts.m_attestation_threshold == 0) {
447 continue;
448 }
449 return "No public key specified for sentinel "
450 + std::to_string(i) + " (" + sentinel_public_key_key
451 + ")";
452 }
453 auto key = hash_from_hex(sentinel_public_key.value());
454 opts.m_sentinel_public_keys.insert(key);
455 }
456 return std::nullopt;
457 }
458
459 auto read_atomizer_options(options& opts, const parser& cfg)
460 -> std::optional<std::string> {
461 const auto atomizer_count
462 = cfg.get_ulong(atomizer_count_key).value_or(0);
463 for(size_t i{0}; i < atomizer_count; i++) {
464 const auto atomizer_ep_key = get_atomizer_endpoint_key(i);
465 const auto atomizer_ep = cfg.get_endpoint(atomizer_ep_key);
466 if(!atomizer_ep) {
467 return "No endpoint specified for atomizer "
468 + std::to_string(i) + " (" + atomizer_ep_key + ")";
469 }
470 opts.m_atomizer_endpoints.push_back(*atomizer_ep);
471
472 const auto atomizer_loglevel_key = get_atomizer_loglevel_key(i);
473 const auto atomizer_loglevel
474 = cfg.get_loglevel(atomizer_loglevel_key);
475 const auto loglevel
476 = atomizer_loglevel.value_or(defaults::log_level);
477 opts.m_atomizer_loglevels.push_back(loglevel);
478
479 const auto endpoint_key = get_atomizer_raft_endpoint_key(i);
480 const auto endpoint_str = cfg.get_endpoint(endpoint_key);
481 if(!endpoint_str) {
482 return "No raft endpoint specified for atomizer "
483 + std::to_string(i) + " (" + endpoint_key + ")";
484 }
485 opts.m_atomizer_raft_endpoints.push_back(endpoint_str.value());
486 }
487
488 opts.m_target_block_interval
489 = cfg.get_ulong(target_block_interval_key)
490 .value_or(opts.m_target_block_interval);
491
492 opts.m_stxo_cache_depth
493 = cfg.get_ulong(stxo_cache_key).value_or(opts.m_stxo_cache_depth);
494
495 return std::nullopt;
496 }
497
498 auto read_archiver_options(options& opts, const parser& cfg)
499 -> std::optional<std::string> {
500 const auto archiver_count
501 = cfg.get_ulong(archiver_count_key).value_or(0);
502 for(size_t i{0}; i < archiver_count; i++) {
503 const auto archiver_ep_key = get_archiver_endpoint_key(i);
504 const auto archiver_ep = cfg.get_endpoint(archiver_ep_key);
505 if(!archiver_ep) {
506 return "No endpoint specified for archiver "
507 + std::to_string(i) + " (" + archiver_ep_key + ")";
508 }
509 opts.m_archiver_endpoints.push_back(*archiver_ep);
510
511 const auto archiver_loglevel_key = get_archiver_loglevel_key(i);
512 const auto archiver_loglevel
513 = cfg.get_loglevel(archiver_loglevel_key)
514 .value_or(defaults::log_level);
515 opts.m_archiver_loglevels.push_back(archiver_loglevel);
516
517 const auto archiver_db_str = get_archiver_db_key(i);
518 const auto archiver_db = cfg.get_string(archiver_db_str);
519 if(!archiver_db) {
520 return "No db directory specified for archiver "
521 + std::to_string(i) + " (" + archiver_db_str + ")";
522 }
523 opts.m_archiver_db_dirs.push_back(*archiver_db);
524 }
525
526 return std::nullopt;
527 }
528
529 auto read_watchtower_options(options& opts, const parser& cfg)
530 -> std::optional<std::string> {
531 const auto watchtower_count
532 = cfg.get_ulong(watchtower_count_key).value_or(0);
533 for(size_t i{0}; i < watchtower_count; i++) {
534 const auto watchtower_client_ep_key
536 const auto watchtower_client_ep
537 = cfg.get_endpoint(watchtower_client_ep_key);
538 if(!watchtower_client_ep) {
539 return "No client endpoint specified for watchtower "
540 + std::to_string(i) + " (" + watchtower_client_ep_key
541 + ")";
542 }
543 opts.m_watchtower_client_endpoints.push_back(
544 *watchtower_client_ep);
545
546 const auto watchtower_internal_ep_key
548 const auto watchtower_internal_ep
549 = cfg.get_endpoint(watchtower_internal_ep_key);
550 if(!watchtower_internal_ep) {
551 return "No internal endpoint specified for watchtower "
552 + std::to_string(i) + std::to_string(i) + " ("
553 + watchtower_internal_ep_key + ")";
554 }
555 opts.m_watchtower_internal_endpoints.push_back(
556 *watchtower_internal_ep);
557
558 const auto watchtower_loglevel_key
560 const auto watchtower_loglevel
561 = cfg.get_loglevel(watchtower_loglevel_key)
562 .value_or(defaults::log_level);
563 opts.m_watchtower_loglevels.push_back(watchtower_loglevel);
564 }
565
566 opts.m_watchtower_block_cache_size
567 = cfg.get_ulong(watchtower_block_cache_size_key)
568 .value_or(opts.m_watchtower_block_cache_size);
569 opts.m_watchtower_error_cache_size
570 = cfg.get_ulong(watchtower_error_cache_size_key)
571 .value_or(opts.m_watchtower_error_cache_size);
572
573 return std::nullopt;
574 }
575
576 void read_raft_options(options& opts, const parser& cfg) {
577 opts.m_election_timeout_upper = static_cast<int32_t>(
578 cfg.get_ulong(election_timeout_upper_key)
579 .value_or(opts.m_election_timeout_upper));
580 opts.m_election_timeout_lower = static_cast<int32_t>(
581 cfg.get_ulong(election_timeout_lower_key)
582 .value_or(opts.m_election_timeout_lower));
583 opts.m_heartbeat = static_cast<int32_t>(
584 cfg.get_ulong(heartbeat_key).value_or(opts.m_heartbeat));
586 = static_cast<int32_t>(cfg.get_ulong(snapshot_distance_key)
587 .value_or(opts.m_snapshot_distance));
589 = static_cast<int32_t>(cfg.get_ulong(raft_batch_size_key)
590 .value_or(opts.m_raft_max_batch));
591
592 opts.m_batch_size
593 = cfg.get_ulong(batch_size_key).value_or(opts.m_batch_size);
594 }
595
596 void read_loadgen_options(options& opts, const parser& cfg) {
597 opts.m_input_count
598 = cfg.get_ulong(input_count_key).value_or(opts.m_input_count);
599 opts.m_output_count
600 = cfg.get_ulong(output_count_key).value_or(opts.m_output_count);
601 opts.m_invalid_rate
602 = cfg.get_decimal(invalid_rate_key).value_or(opts.m_invalid_rate);
603 opts.m_fixed_tx_rate = cfg.get_decimal(fixed_tx_rate_key)
604 .value_or(opts.m_fixed_tx_rate);
605 opts.m_fixed_tx_mode
606 = opts.m_input_count != 0 && opts.m_output_count != 0;
607 opts.m_window_size
608 = cfg.get_ulong(window_size_key).value_or(opts.m_window_size);
609
610 opts.m_initial_mint_count = cfg.get_ulong(initial_mint_count_key)
611 .value_or(opts.m_initial_mint_count);
612 opts.m_initial_mint_value = cfg.get_ulong(initial_mint_value_key)
613 .value_or(opts.m_initial_mint_value);
614
615 opts.m_loadgen_count
616 = cfg.get_ulong(loadgen_count_key).value_or(opts.m_loadgen_count);
617 }
618
619 auto read_options(const std::string& config_file)
620 -> std::variant<options, std::string> {
621 auto opts = options{};
622 auto cfg = parser(config_file);
623
624 opts.m_twophase_mode = cfg.get_ulong(two_phase_mode).value_or(0) != 0;
625
626 auto err = read_sentinel_options(opts, cfg);
627 if(err.has_value()) {
628 return err.value();
629 }
630
631 err = read_shard_endpoints(opts, cfg);
632 if(err.has_value()) {
633 return err.value();
634 }
635
636 err = read_shard_options(opts, cfg);
637 if(err.has_value()) {
638 return err.value();
639 }
640
641 err = read_atomizer_options(opts, cfg);
642 if(err.has_value()) {
643 return err.value();
644 }
645
646 err = read_archiver_options(opts, cfg);
647 if(err.has_value()) {
648 return err.value();
649 }
650
651 err = read_watchtower_options(opts, cfg);
652 if(err.has_value()) {
653 return err.value();
654 }
655
656 err = read_coordinator_options(opts, cfg);
657 if(err.has_value()) {
658 return err.value();
659 }
660
661 read_raft_options(opts, cfg);
662
663 read_loadgen_options(opts, cfg);
664
665 return opts;
666 }
667
668 auto load_options(const std::string& config_file)
669 -> std::variant<options, std::string> {
670 auto opt = read_options(config_file);
671 if(std::holds_alternative<options>(opt)) {
672 auto res = check_options(std::get<options>(opt));
673 if(res) {
674 return *res;
675 }
676 }
677 return opt;
678 }
679
680 auto check_options(const options& opts) -> std::optional<std::string> {
681 // TODO: refactor options to use maps rather than vectors and move all
682 // config checking to this function.
683 if(opts.m_twophase_mode) {
684 if(opts.m_sentinel_endpoints.empty()) {
685 return "Two-phase mode requires at least one configured "
686 "sentinel";
687 }
688 if(opts.m_sentinel_endpoints.size()
689 < opts.m_attestation_threshold) {
690 return "The number of required attestations is larger \n"
691 "than the number of sentinels that can provide them.";
692 }
693 if(opts.m_locking_shard_endpoints.empty()) {
694 return "Two-phase mode requires at least one configured shard";
695 }
696 if(opts.m_coordinator_endpoints.empty()) {
697 return "Two-phase mode required at least one configured "
698 "coordinator";
699 }
700 } else {
701 if(opts.m_watchtower_client_endpoints.empty()) {
702 return "Atomizer mode requires at least one configured "
703 "watchtower";
704 }
705 if(opts.m_archiver_endpoints.empty()) {
706 return "Atomizer mode requires at least one configured "
707 "archiver";
708 }
709 if(opts.m_shard_endpoints.empty()
710 && !opts.m_sentinel_endpoints.empty()) {
711 return "Sentinels require at least one configured shard";
712 }
713 if(opts.m_atomizer_endpoints.empty()) {
714 return "Atomizer mode requires at least one configured "
715 "atomizer";
716 }
717 }
718
719 if(opts.m_seed_from != opts.m_seed_to) {
720 if(opts.m_seed_from > opts.m_seed_to) {
721 return "shard_seed_from > shard_seed_to";
722 }
723 if(opts.m_seed_value == 0) {
724 return "Seed range defined but value is zero";
725 }
726 }
727
728 if(opts.m_sentinel_public_keys.size() < opts.m_attestation_threshold) {
729 return "Not enough sentinel public keys to reach the attestation "
730 "threshold";
731 }
732
733 return std::nullopt;
734 }
735
736 auto hash_in_shard_range(const shard_range_t& range, const hash_t& val)
737 -> bool {
738 return val[0] >= range.first && val[0] <= range.second;
739 }
740
741 auto loadgen_seed_range(const options& opts, size_t gen_id)
742 -> std::pair<size_t, size_t> {
743 assert(gen_id < opts.m_loadgen_count);
744 auto total_seed_range = opts.m_seed_to - opts.m_seed_from;
745 auto seed_range_sz = total_seed_range / opts.m_loadgen_count;
746 auto our_range_start = opts.m_seed_from + (gen_id * seed_range_sz);
747 auto our_range_end = our_range_start + seed_range_sz - 1;
748 return {our_range_start, our_range_end};
749 }
750
751 auto get_args(int argc, char** argv) -> std::vector<std::string> {
752 auto args = std::vector<char*>(static_cast<size_t>(argc));
753 std::memcpy(args.data(),
754 argv,
755 static_cast<size_t>(argc) * sizeof(argv));
756 auto ret = std::vector<std::string>();
757 ret.reserve(static_cast<size_t>(argc));
758 for(auto* arg : args) {
759 auto str = std::string(arg);
760 ret.emplace_back(std::move(str));
761 }
762 return ret;
763 }
764
765 parser::parser(const std::string& filename) {
766 std::ifstream file(filename);
767 assert(file.good());
768
769 init(file);
770 }
771
772 parser::parser(std::istream& stream) {
773 init(stream);
774 }
775
776 void parser::init(std::istream& stream) {
777 std::string line;
778 while(std::getline(stream, line)) {
779 std::istringstream line_stream(line);
780 std::string key;
781 if(std::getline(line_stream, key, '=')) {
782 std::string value;
783 if(std::getline(line_stream, value)) {
784 m_options.emplace(key, parse_value(value));
785 }
786 }
787 }
788 }
789
790 auto parser::get_string(const std::string& key) const
791 -> std::optional<std::string> {
792 return get_val<std::string>(key);
793 }
794
795 auto parser::get_ulong(const std::string& key) const
796 -> std::optional<size_t> {
797 return get_val<size_t>(key);
798 }
799
800 auto parser::get_endpoint(const std::string& key) const
801 -> std::optional<network::endpoint_t> {
802 const auto val_str = get_string(key);
803 if(!val_str.has_value()) {
804 return std::nullopt;
805 }
806
807 return parse_ip_port(val_str.value());
808 }
809
810 auto parser::get_loglevel(const std::string& key) const
811 -> std::optional<logging::log_level> {
812 const auto val_str = get_string(key);
813 if(!val_str.has_value()) {
814 return std::nullopt;
815 }
816 return logging::parse_loglevel(val_str.value());
817 }
818
819 auto parser::get_decimal(const std::string& key) const
820 -> std::optional<double> {
821 return get_val<double>(key);
822 }
823
824 auto parser::find_or_env(const std::string& key) const
825 -> std::optional<value_t> {
826 auto upper_key = key;
827 std::transform(upper_key.begin(),
828 upper_key.end(),
829 upper_key.begin(),
830 [](unsigned char c) {
831 return std::toupper(c);
832 });
833 if(const auto* env_v = std::getenv(upper_key.c_str())) {
834 auto value = std::string(env_v);
835 return parse_value(value);
836 }
837
838 auto it = m_options.find(key);
839 if(it != m_options.end()) {
840 return it->second;
841 }
842
843 return std::nullopt;
844 }
845
846 auto parser::parse_value(const std::string& value) -> value_t {
847 if(value[0] != '\"' && value[value.size() - 1] != '\"') {
848 if(value.find('.') == std::string::npos) {
849 const auto as_int = static_cast<size_t>(std::stoull(value));
850 return as_int;
851 }
852 const auto as_dbl = std::stod(value);
853 return as_dbl;
854 }
855
856 const auto unquoted = value.substr(1, value.size() - 2);
857 return unquoted;
858 }
859}
Reads configuration parameters line-by-line from a file.
Definition config.hpp:323
auto get_endpoint(const std::string &key) const -> std::optional< network::endpoint_t >
Return the value for the given key if its value is an endpoint.
Definition config.cpp:800
auto get_string(const std::string &key) const -> std::optional< std::string >
Returns the given key if its value is a string.
Definition config.cpp:790
auto get_decimal(const std::string &key) const -> std::optional< double >
Return the value for the given key if its value is a double.
Definition config.cpp:819
auto get_ulong(const std::string &key) const -> std::optional< size_t >
Return the value for the given key if its value is a long.
Definition config.cpp:795
parser(const std::string &filename)
Constructor.
Definition config.cpp:765
auto get_loglevel(const std::string &key) const -> std::optional< logging::log_level >
Return the value for the given key if its value is a loglevel.
Definition config.cpp:810
Tools for reading options from a configuration file and building application-specific parameter sets ...
auto check_options(const options &opts) -> std::optional< std::string >
Checks a fully populated options struct for invariants.
Definition config.cpp:680
void get_watchtower_key_prefix(std::stringstream &ss, size_t watchtower_id)
Definition config.cpp:135
auto read_shard_options(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:295
auto get_shard_endpoint_key(size_t shard_id) -> std::string
Definition config.cpp:36
auto get_shard_end_key(size_t shard_id) -> std::string
Definition config.cpp:78
auto read_watchtower_options(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:529
void get_coordinator_key_prefix(std::stringstream &ss, size_t coordinator_id)
Definition config.cpp:195
auto get_archiver_endpoint_key(size_t archiver_id) -> std::string
Definition config.cpp:96
auto get_sentinel_endpoint_key(size_t sentinel_id) -> std::string
Definition config.cpp:64
auto get_archiver_db_key(size_t archiver_id) -> std::string
Definition config.cpp:110
void read_loadgen_options(options &opts, const parser &cfg)
Definition config.cpp:596
auto read_archiver_options(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:498
auto get_watchtower_loglevel_key(size_t watchtower_id) -> std::string
Definition config.cpp:156
void read_raft_options(options &opts, const parser &cfg)
Definition config.cpp:576
auto get_coordinator_raft_endpoint_key(size_t coordinator_id, size_t node_id) -> std::string
Definition config.cpp:208
auto load_options(const std::string &config_file) -> std::variant< options, std::string >
Loads options from the given config file and check for invariants.
Definition config.cpp:668
auto get_atomizer_loglevel_key(size_t atomizer_id) -> std::string
Definition config.cpp:57
auto get_shard_raft_endpoint_key(size_t shard_id, size_t node_id) -> std::string
Definition config.cpp:170
auto get_shard_loglevel_key(size_t shard_id) -> std::string
Definition config.cpp:117
auto get_watchtower_client_endpoint_key(size_t watchtower_id) -> std::string
Definition config.cpp:140
auto get_archiver_loglevel_key(size_t archiver_id) -> std::string
Definition config.cpp:103
auto get_coordinator_endpoint_key(size_t coordinator_id, size_t node_id) -> std::string
Definition config.cpp:200
auto get_atomizer_endpoint_key(size_t atomizer_id) -> std::string
Definition config.cpp:43
auto get_coordinator_node_count_key(size_t coordinator_id) -> std::string
Definition config.cpp:216
auto get_watchtower_internal_endpoint_key(size_t watchtower_id) -> std::string
Definition config.cpp:148
auto read_options(const std::string &config_file) -> std::variant< options, std::string >
Read options from the given config file without checking invariants.
Definition config.cpp:619
auto get_shard_node_count_key(size_t shard_id) -> std::string
Definition config.cpp:163
auto read_atomizer_options(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:459
auto get_shard_readonly_endpoint_key(size_t shard_id, size_t node_id) -> std::string
Definition config.cpp:186
void get_sentinel_key_prefix(std::stringstream &ss, size_t sentinel_id)
Definition config.cpp:124
void get_archiver_key_prefix(std::stringstream &ss, size_t archiver_id)
Definition config.cpp:92
auto get_atomizer_raft_endpoint_key(size_t atomizer_id) -> std::string
Definition config.cpp:50
auto get_shard_db_key(size_t shard_id) -> std::string
Definition config.cpp:71
auto read_shard_endpoints(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:244
auto get_sentinel_public_key_key(size_t sentinel_id) -> std::string
Definition config.cpp:237
auto read_sentinel_options(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:409
void get_shard_key_prefix(std::stringstream &ss, size_t shard_id)
Definition config.cpp:32
auto get_sentinel_loglevel_key(size_t sentinel_id) -> std::string
Definition config.cpp:128
auto get_shard_start_key(size_t shard_id) -> std::string
Definition config.cpp:85
auto parse_ip_port(const std::string &in_str) -> network::endpoint_t
Definition config.cpp:13
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 get_coordinator_loglevel_key(size_t coordinator_id) -> std::string
Definition config.cpp:223
auto get_args(int argc, char **argv) -> std::vector< std::string >
Converts c-args from an executable's main function into a vector of strings.
Definition config.cpp:751
auto get_sentinel_private_key_key(size_t sentinel_id) -> std::string
Definition config.cpp:230
auto loadgen_seed_range(const options &opts, size_t gen_id) -> std::pair< size_t, size_t >
Calculates the sub-range of total seeded outputs for a particular load generator ID.
Definition config.cpp:741
std::pair< uint8_t, uint8_t > shard_range_t
[start, end] inclusive.
Definition config.hpp:129
auto read_coordinator_options(options &opts, const parser &cfg) -> std::optional< std::string >
Definition config.cpp:362
auto parse_loglevel(const std::string &level) -> std::optional< log_level >
Parses a capitalized string into a log level.
Definition logging.cpp:70
std::pair< ip_address, port_number_t > endpoint_t
[host name, port number].
Definition socket.hpp:19
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
std::array< unsigned char, pubkey_len > privkey_t
A private key of a public/private keypair.
Definition keys.hpp:23
auto hash_from_hex(const std::string &val) -> hash_t
Parses a hexadecimal representation of a hash.
Project-wide configuration options.
Definition config.hpp:132
size_t m_input_count
Number of inputs in fixed-size transactions from atomizer-cli.
Definition config.hpp:138
size_t m_window_size
Maximum number of unconfirmed transactions in atomizer-cli.
Definition config.hpp:136
int32_t m_raft_max_batch
Maximum number of raft log entries to batch into one RPC message.
Definition config.hpp:183
size_t m_loadgen_count
Number of load generators over which to split pre-seeded UTXOs.
Definition config.hpp:251
size_t m_initial_mint_count
Number of outputs in the initial mint transaction.
Definition config.hpp:237
size_t m_batch_size
Maximum transaction batch size for one log entry in the raft atomizer or one batch in the coordinator...
Definition config.hpp:167
int32_t m_snapshot_distance
Raft snapshot distance, in number of log entries.
Definition config.hpp:181
double m_fixed_tx_rate
Proportion of fixed transactions sent from atomizer-cli.
Definition config.hpp:144
int32_t m_election_timeout_lower
Raft election timeout lower bound in milliseconds.
Definition config.hpp:176
double m_invalid_rate
Proportion of invalid transactions sent from atomizer-cli.
Definition config.hpp:142
bool m_fixed_tx_mode
Flag set if m_input_count or m_output_count are greater than zero.
Definition config.hpp:211
size_t m_initial_mint_value
Value for all outputs in the initial mint transaction.
Definition config.hpp:239
int32_t m_election_timeout_upper
Raft election timeout upper bound in milliseconds.
Definition config.hpp:173
int32_t m_heartbeat
Raft heartbeat timeout in milliseconds.
Definition config.hpp:179
size_t m_output_count
Number of outputs in fixed-size transactions from atomizer-cli.
Definition config.hpp:140