17 std::shared_ptr<logging::log> logger)
18 : m_sentinel_id(sentinel_id),
20 m_logger(std::move(logger)),
22 opts.m_coordinator_endpoints[sentinel_id
23 % static_cast<uint32_t>(
24 opts.m_coordinator_endpoints
28 if(m_opts.m_sentinel_endpoints.empty()) {
29 m_logger->error(
"No sentinel endpoints are defined.");
33 if(m_sentinel_id >= m_opts.m_sentinel_endpoints.size()) {
35 "The sentinel ID is too large for the number of sentinels.");
39 auto skey = m_opts.m_sentinel_private_keys.find(m_sentinel_id);
40 if(skey == m_opts.m_sentinel_private_keys.end()) {
41 if(m_opts.m_attestation_threshold > 0) {
42 m_logger->error(
"No private key specified");
46 m_privkey = skey->second;
52 auto retry_delay = std::chrono::seconds(1);
53 auto retry_threshold = 4;
54 while(!m_coordinator_client.init() && retry_threshold-- > 0) {
55 m_logger->warn(
"Failed to start coordinator client.");
57 std::this_thread::sleep_for(retry_delay);
58 if(retry_threshold > 0) {
60 m_logger->warn(
"Retrying...");
64 for(
const auto& ep : m_opts.m_sentinel_endpoints) {
65 if(ep == m_opts.m_sentinel_endpoints[m_sentinel_id]) {
68 auto client = std::make_unique<sentinel::rpc::client>(
69 std::vector<network::endpoint_t>{ep},
72 m_logger->warn(
"Failed to start sentinel client");
74 m_sentinel_clients.emplace_back(std::move(
client));
77 constexpr size_t dist_lower_bound = 0;
78 const size_t dist_upper_bound
79 = m_sentinel_clients.empty() ? 0 : m_sentinel_clients.size() - 1;
80 m_dist =
decltype(m_dist)(dist_lower_bound, dist_upper_bound);
85 m_opts.m_sentinel_endpoints[m_sentinel_id]);
86 if(!rpc_server->init()) {
87 m_logger->error(
"Failed to start sentinel RPC server");
91 m_rpc_server = std::make_unique<decltype(m_rpc_server)::element_type>(
93 std::move(rpc_server));
102 if(validation_err.has_value()) {
117 if(m_opts.m_attestation_threshold > 0) {
118 auto attestation = compact_tx.sign(m_secp.get(), m_privkey);
119 compact_tx.m_attestations.insert(attestation);
122 gather_attestations(tx, std::move(result_callback), compact_tx, {});
128 controller::result_handler(std::optional<bool> res,
129 const execute_result_callback_type& res_cb) {
130 if(res.has_value()) {
139 res_cb(std::nullopt);
147 if(validation_err.has_value()) {
148 result_callback(std::nullopt);
152 auto attestation = compact_tx.sign(m_secp.get(), m_privkey);
153 result_callback(std::move(attestation));
157 void controller::validate_result_handler(
158 validate_result v_res,
160 execute_result_callback_type result_callback,
162 std::unordered_set<size_t> requested) {
163 if(!v_res.has_value()) {
165 "invalid according to remote sentinel");
166 result_callback(std::nullopt);
170 gather_attestations(tx,
171 std::move(result_callback),
173 std::move(requested));
176 void controller::gather_attestations(
177 const transaction::full_tx& tx,
178 execute_result_callback_type result_callback,
179 const transaction::compact_tx& ctx,
180 std::unordered_set<size_t> requested) {
182 auto success =
false;
184 auto sentinel_id = m_dist(m_rand);
185 if(requested.find(sentinel_id) != requested.end()) {
189 = m_sentinel_clients[sentinel_id]->validate_transaction(
193 r.insert(sentinel_id);
194 validate_result_handler(v_res,
204 m_logger->debug(
"Accepted",
to_string(ctx.m_id));
206 send_compact_tx(ctx, std::move(result_callback));
210 controller::send_compact_tx(
const transaction::compact_tx& ctx,
211 execute_result_callback_type result_callback) {
213 [&, res_cb = std::move(result_callback)](std::optional<bool> res) {
214 result_handler(res, res_cb);
224 static constexpr auto retry_delay = std::chrono::milliseconds(100);
225 std::this_thread::sleep_for(retry_delay);
External client for sending new transactions to the system.
auto init() -> bool
Initializes the client.
auto execute_transaction(transaction::compact_tx tx, callback_type result_callback) -> bool override
Requests execution of the given transaction using the coordinator cluster.
Generic asynchronous RPC server.
Implements an RPC server over a TCP socket.
std::optional< cbdc::sentinel::validate_response > validate_result
Result of a validation operation.
std::function< void( std::optional< cbdc::sentinel::execute_response >)> execute_result_callback_type
Callback function for transaction execution result.
std::function< void(validate_result)> validate_result_callback_type
Callback function for providing a transaction validation result.
auto execute_transaction(transaction::full_tx tx, execute_result_callback_type result_callback) -> bool override
Statically validates a transaction, submits it the shard coordinator network, and returns the result ...
auto init() -> bool
Initializes the controller.
auto validate_transaction(transaction::full_tx tx, validate_result_callback_type result_callback) -> bool override
Statically validates a transaction and generates a sentinel attestation if the transaction is valid.
std::variant< execute_response, validate_response > response
Sentinel RPC response type.
@ static_invalid
Statically invalid. Must be fixed and resubmitted.
@ state_invalid
Statically valid, but rejected by the shards for trying to spend inputs either that do not exist or t...
@ confirmed
Executed to completion.
std::variant< execute_request, validate_request > request
Sentinel RPC request type.
auto to_string(cbdc::transaction::validation::tx_error_code err) -> std::string
auto check_tx(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
Runs static validation checks on the given transaction.
auto tx_id(const full_tx &tx) noexcept -> hash_t
Calculates the unique hash of a full transaction.
auto to_string(const hash_t &val) -> std::string
Converts a hash to a hexadecimal string.
auto pubkey_from_privkey(const privkey_t &privkey, secp256k1_context *ctx) -> pubkey_t
Generates a public key from the specified private key.
Project-wide configuration options.
size_t m_attestation_threshold
Number of sentinel attestations needed for a compact transaction.
Sentinel response message.
A condensed, hash-only transaction representation.
std::unordered_map< pubkey_t, signature_t, hashing::null > m_attestations
Signatures from sentinels attesting the compact TX is valid.
hash_t m_id
The hash of the full transaction returned by tx_id.