15 std::istringstream ss(in_str);
18 [[maybe_unused]]
const auto& host_res = std::getline(ss, host,
':');
22 [[maybe_unused]]
const auto& port_res
23 = std::getline(ss, port_str,
':');
26 auto port = std::stoul(port_str);
27 assert(port <= std::numeric_limits<unsigned short>::max());
29 return {host,
static_cast<unsigned short>(port)};
33 ss << shard_prefix << shard_id << config_separator;
39 ss << endpoint_postfix;
45 ss << atomizer_prefix << atomizer_id << config_separator
52 ss << atomizer_prefix << atomizer_id << config_separator
53 << raft_endpoint_postfix;
59 ss << atomizer_prefix << atomizer_id << config_separator
66 ss << sentinel_prefix << sentinel_id << config_separator
93 ss << archiver_prefix << archiver_id << config_separator;
99 ss << endpoint_postfix;
104 std::stringstream ss;
106 ss << loglevel_postfix;
111 std::stringstream ss;
118 std::stringstream ss;
120 ss << loglevel_postfix;
125 ss << sentinel_prefix << sentinel_id << config_separator;
129 std::stringstream ss;
131 ss << loglevel_postfix;
136 size_t watchtower_id) {
137 ss << watchtower_prefix << watchtower_id << config_separator;
142 std::stringstream ss;
144 ss << watchtower_client_ep_postfix;
150 std::stringstream ss;
152 ss << watchtower_internal_ep_postfix;
157 std::stringstream ss;
159 ss << loglevel_postfix;
164 std::stringstream ss;
172 std::stringstream ss;
174 ss << node_id << config_separator << raft_endpoint_postfix;
180 std::stringstream ss;
182 ss << node_id << config_separator << endpoint_postfix;
188 std::stringstream ss;
190 ss << node_id << config_separator << readonly << config_separator
196 size_t coordinator_id) {
197 ss << coordinator_prefix << coordinator_id << config_separator;
202 std::stringstream ss;
204 ss << node_id << config_separator << endpoint_postfix;
209 size_t node_id) -> std::string {
210 std::stringstream ss;
212 ss << node_id << config_separator << raft_endpoint_postfix;
217 std::stringstream ss;
224 std::stringstream ss;
225 ss << coordinator_prefix << coordinator_id << config_separator
231 auto ss = std::stringstream();
233 ss << private_key_postfix;
238 auto ss = std::stringstream();
240 ss << public_key_postfix;
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++) {
253 const auto node_count = cfg.get_ulong(node_count_key);
255 return "No node count specified for shard "
256 + std::to_string(i) +
" (" + node_count_key +
")";
258 for(
size_t j{0}; j < *node_count; j++) {
260 const auto raft_ep = cfg.get_endpoint(raft_ep_key);
262 return "No raft endpoint specified for shard "
263 + std::to_string(i) +
" node " + std::to_string(j)
264 +
" (" + raft_ep_key +
")";
266 opts.m_locking_shard_raft_endpoints[i].emplace_back(
270 const auto ep = cfg.get_endpoint(ep_key);
272 return "No endpoint specified for shard "
273 + std::to_string(i) +
" node "
276 opts.m_locking_shard_endpoints[i].emplace_back(*ep);
280 const auto ro_ep = cfg.get_endpoint(ro_ep_key);
282 return "No read-only endpoint specified for shard "
283 + std::to_string(i) +
" node " + std::to_string(j)
284 +
" (" + ro_ep_key +
")";
286 opts.m_locking_shard_readonly_endpoints[i].emplace_back(
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) {
301 const auto shard_ep = cfg.get_endpoint(shard_ep_key);
303 return "No endpoint specified for shard "
304 + std::to_string(i) +
" (" + shard_ep_key +
")";
306 opts.m_shard_endpoints.push_back(*shard_ep);
309 const auto shard_db = cfg.get_string(shard_db_str);
311 return "No db directory specified for shard "
312 + std::to_string(i) +
" (" + shard_db_str +
")";
314 opts.m_shard_db_dirs.push_back(*shard_db);
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);
323 const auto range_start = cfg.get_ulong(start_key);
325 return "No range start specified for shard "
326 + std::to_string(i) +
" (" + start_key +
")";
329 const auto range_end = cfg.get_ulong(end_key);
331 return "No range end specified for shard " + std::to_string(i)
332 +
" (" + end_key +
")";
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);
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);
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);
349 return "Seed range defined but missing a private key";
351 if(priv_str.value().size() !=
sizeof(
privkey_t) * 2) {
352 return "Invalid seed private key length";
356 = cfg.get_ulong(seed_value).value_or(opts.m_seed_value);
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++) {
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);
375 const auto node_count = cfg.get_ulong(node_count_key);
377 return "No node count specified for coordinator "
378 + std::to_string(i) +
" (" + node_count_key +
")";
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);
385 return "No raft endpoint specified for coordinator "
386 + std::to_string(i) +
" node " + std::to_string(j)
387 +
" (" + raft_ep_key +
")";
389 opts.m_coordinator_raft_endpoints[i].emplace_back(*raft_ep);
392 const auto ep = cfg.get_endpoint(ep_key);
394 return "No endpoint specified for coordinator "
395 + std::to_string(i) +
" node " + std::to_string(j)
396 +
" (" + ep_key +
")";
398 opts.m_coordinator_endpoints[i].emplace_back(*ep);
402 opts.m_coordinator_max_threads
403 = cfg.get_ulong(coordinator_max_threads)
404 .value_or(opts.m_coordinator_max_threads);
410 -> std::optional<std::string> {
411 opts.m_attestation_threshold
412 = cfg.get_ulong(attestation_threshold_key)
413 .value_or(opts.m_attestation_threshold);
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++) {
419 const auto sentinel_ep = cfg.get_endpoint(sentinel_ep_key);
421 return "No endpoint specified for sentinel "
422 + std::to_string(i) +
" (" + sentinel_ep_key +
")";
424 opts.m_sentinel_endpoints.push_back(*sentinel_ep);
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);
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()) {
438 opts.m_sentinel_private_keys[i] = key;
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) {
449 return "No public key specified for sentinel "
450 + std::to_string(i) +
" (" + sentinel_public_key_key
454 opts.m_sentinel_public_keys.insert(key);
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++) {
465 const auto atomizer_ep = cfg.get_endpoint(atomizer_ep_key);
467 return "No endpoint specified for atomizer "
468 + std::to_string(i) +
" (" + atomizer_ep_key +
")";
470 opts.m_atomizer_endpoints.push_back(*atomizer_ep);
473 const auto atomizer_loglevel
474 = cfg.get_loglevel(atomizer_loglevel_key);
476 = atomizer_loglevel.value_or(defaults::log_level);
477 opts.m_atomizer_loglevels.push_back(loglevel);
480 const auto endpoint_str = cfg.get_endpoint(endpoint_key);
482 return "No raft endpoint specified for atomizer "
483 + std::to_string(i) +
" (" + endpoint_key +
")";
485 opts.m_atomizer_raft_endpoints.push_back(endpoint_str.value());
488 opts.m_target_block_interval
489 = cfg.get_ulong(target_block_interval_key)
490 .value_or(opts.m_target_block_interval);
492 opts.m_stxo_cache_depth
493 = cfg.get_ulong(stxo_cache_key).value_or(opts.m_stxo_cache_depth);
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++) {
504 const auto archiver_ep = cfg.get_endpoint(archiver_ep_key);
506 return "No endpoint specified for archiver "
507 + std::to_string(i) +
" (" + archiver_ep_key +
")";
509 opts.m_archiver_endpoints.push_back(*archiver_ep);
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);
518 const auto archiver_db = cfg.get_string(archiver_db_str);
520 return "No db directory specified for archiver "
521 + std::to_string(i) +
" (" + archiver_db_str +
")";
523 opts.m_archiver_db_dirs.push_back(*archiver_db);
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
543 opts.m_watchtower_client_endpoints.push_back(
544 *watchtower_client_ep);
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 +
")";
555 opts.m_watchtower_internal_endpoints.push_back(
556 *watchtower_internal_ep);
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);
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);
578 cfg.
get_ulong(election_timeout_upper_key)
581 cfg.
get_ulong(election_timeout_lower_key)
586 =
static_cast<int32_t
>(cfg.
get_ulong(snapshot_distance_key)
589 =
static_cast<int32_t
>(cfg.
get_ulong(raft_batch_size_key)
620 -> std::variant<options, std::string> {
622 auto cfg =
parser(config_file);
624 opts.m_twophase_mode = cfg.get_ulong(two_phase_mode).value_or(0) != 0;
627 if(err.has_value()) {
632 if(err.has_value()) {
637 if(err.has_value()) {
642 if(err.has_value()) {
647 if(err.has_value()) {
652 if(err.has_value()) {
657 if(err.has_value()) {
669 -> std::variant<options, std::string> {
671 if(std::holds_alternative<options>(opt)) {
683 if(opts.m_twophase_mode) {
684 if(opts.m_sentinel_endpoints.empty()) {
685 return "Two-phase mode requires at least one configured "
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.";
693 if(opts.m_locking_shard_endpoints.empty()) {
694 return "Two-phase mode requires at least one configured shard";
696 if(opts.m_coordinator_endpoints.empty()) {
697 return "Two-phase mode required at least one configured "
701 if(opts.m_watchtower_client_endpoints.empty()) {
702 return "Atomizer mode requires at least one configured "
705 if(opts.m_archiver_endpoints.empty()) {
706 return "Atomizer mode requires at least one configured "
709 if(opts.m_shard_endpoints.empty()
710 && !opts.m_sentinel_endpoints.empty()) {
711 return "Sentinels require at least one configured shard";
713 if(opts.m_atomizer_endpoints.empty()) {
714 return "Atomizer mode requires at least one configured "
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";
723 if(opts.m_seed_value == 0) {
724 return "Seed range defined but value is zero";
728 if(opts.m_sentinel_public_keys.size() < opts.m_attestation_threshold) {
729 return "Not enough sentinel public keys to reach the attestation "
738 return val[0] >= range.first && val[0] <= range.second;
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};
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(),
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));
766 std::ifstream file(filename);
776 void parser::init(std::istream& stream) {
778 while(std::getline(stream, line)) {
779 std::istringstream line_stream(line);
781 if(std::getline(line_stream, key,
'=')) {
783 if(std::getline(line_stream, value)) {
784 m_options.emplace(key, parse_value(value));
791 -> std::optional<std::string> {
792 return get_val<std::string>(key);
796 -> std::optional<size_t> {
797 return get_val<size_t>(key);
801 -> std::optional<network::endpoint_t> {
802 const auto val_str = get_string(key);
803 if(!val_str.has_value()) {
811 -> std::optional<logging::log_level> {
812 const auto val_str = get_string(key);
813 if(!val_str.has_value()) {
820 -> std::optional<double> {
821 return get_val<double>(key);
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(),
830 [](
unsigned char c) {
831 return std::toupper(c);
833 if(
const auto* env_v = std::getenv(upper_key.c_str())) {
834 auto value = std::string(env_v);
835 return parse_value(value);
838 auto it = m_options.find(key);
839 if(it != m_options.end()) {
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));
852 const auto as_dbl = std::stod(value);
856 const auto unquoted = value.substr(1, value.size() - 2);
Reads configuration parameters line-by-line from a file.
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.
auto get_string(const std::string &key) const -> std::optional< std::string >
Returns the given key if its value is a string.
auto get_decimal(const std::string &key) const -> std::optional< double >
Return the value for the given key if its value is a double.
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.
parser(const std::string &filename)
Constructor.
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.
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.
void get_watchtower_key_prefix(std::stringstream &ss, size_t watchtower_id)
auto read_shard_options(options &opts, const parser &cfg) -> std::optional< std::string >
auto get_shard_endpoint_key(size_t shard_id) -> std::string
auto get_shard_end_key(size_t shard_id) -> std::string
auto read_watchtower_options(options &opts, const parser &cfg) -> std::optional< std::string >
void get_coordinator_key_prefix(std::stringstream &ss, size_t coordinator_id)
auto get_archiver_endpoint_key(size_t archiver_id) -> std::string
auto get_sentinel_endpoint_key(size_t sentinel_id) -> std::string
auto get_archiver_db_key(size_t archiver_id) -> std::string
void read_loadgen_options(options &opts, const parser &cfg)
auto read_archiver_options(options &opts, const parser &cfg) -> std::optional< std::string >
auto get_watchtower_loglevel_key(size_t watchtower_id) -> std::string
void read_raft_options(options &opts, const parser &cfg)
auto get_coordinator_raft_endpoint_key(size_t coordinator_id, size_t node_id) -> std::string
auto load_options(const std::string &config_file) -> std::variant< options, std::string >
Loads options from the given config file and check for invariants.
auto get_atomizer_loglevel_key(size_t atomizer_id) -> std::string
auto get_shard_raft_endpoint_key(size_t shard_id, size_t node_id) -> std::string
auto get_shard_loglevel_key(size_t shard_id) -> std::string
auto get_watchtower_client_endpoint_key(size_t watchtower_id) -> std::string
auto get_archiver_loglevel_key(size_t archiver_id) -> std::string
auto get_coordinator_endpoint_key(size_t coordinator_id, size_t node_id) -> std::string
auto get_atomizer_endpoint_key(size_t atomizer_id) -> std::string
auto get_coordinator_node_count_key(size_t coordinator_id) -> std::string
auto get_watchtower_internal_endpoint_key(size_t watchtower_id) -> std::string
auto read_options(const std::string &config_file) -> std::variant< options, std::string >
Read options from the given config file without checking invariants.
auto get_shard_node_count_key(size_t shard_id) -> std::string
auto read_atomizer_options(options &opts, const parser &cfg) -> std::optional< std::string >
auto get_shard_readonly_endpoint_key(size_t shard_id, size_t node_id) -> std::string
void get_sentinel_key_prefix(std::stringstream &ss, size_t sentinel_id)
void get_archiver_key_prefix(std::stringstream &ss, size_t archiver_id)
auto get_atomizer_raft_endpoint_key(size_t atomizer_id) -> std::string
auto get_shard_db_key(size_t shard_id) -> std::string
auto read_shard_endpoints(options &opts, const parser &cfg) -> std::optional< std::string >
auto get_sentinel_public_key_key(size_t sentinel_id) -> std::string
auto read_sentinel_options(options &opts, const parser &cfg) -> std::optional< std::string >
void get_shard_key_prefix(std::stringstream &ss, size_t shard_id)
auto get_sentinel_loglevel_key(size_t sentinel_id) -> std::string
auto get_shard_start_key(size_t shard_id) -> std::string
auto parse_ip_port(const std::string &in_str) -> network::endpoint_t
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 get_coordinator_loglevel_key(size_t coordinator_id) -> std::string
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.
auto get_sentinel_private_key_key(size_t sentinel_id) -> std::string
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.
std::pair< uint8_t, uint8_t > shard_range_t
[start, end] inclusive.
auto read_coordinator_options(options &opts, const parser &cfg) -> std::optional< std::string >
auto parse_loglevel(const std::string &level) -> std::optional< log_level >
Parses a capitalized string into a log level.
std::pair< ip_address, port_number_t > endpoint_t
[host name, port number].
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.
auto hash_from_hex(const std::string &val) -> hash_t
Parses a hexadecimal representation of a hash.
Project-wide configuration options.
size_t m_input_count
Number of inputs in fixed-size transactions from atomizer-cli.
size_t m_window_size
Maximum number of unconfirmed transactions in atomizer-cli.
int32_t m_raft_max_batch
Maximum number of raft log entries to batch into one RPC message.
size_t m_loadgen_count
Number of load generators over which to split pre-seeded UTXOs.
size_t m_initial_mint_count
Number of outputs in the initial mint transaction.
size_t m_batch_size
Maximum transaction batch size for one log entry in the raft atomizer or one batch in the coordinator...
int32_t m_snapshot_distance
Raft snapshot distance, in number of log entries.
double m_fixed_tx_rate
Proportion of fixed transactions sent from atomizer-cli.
int32_t m_election_timeout_lower
Raft election timeout lower bound in milliseconds.
double m_invalid_rate
Proportion of invalid transactions sent from atomizer-cli.
bool m_fixed_tx_mode
Flag set if m_input_count or m_output_count are greater than zero.
size_t m_initial_mint_value
Value for all outputs in the initial mint transaction.
int32_t m_election_timeout_upper
Raft election timeout upper bound in milliseconds.
int32_t m_heartbeat
Raft heartbeat timeout in milliseconds.
size_t m_output_count
Number of outputs in fixed-size transactions from atomizer-cli.