17 std::shared_ptr<logging::log> logger)
18 : m_sentinel_id(sentinel_id),
19 m_opts(std::move(opts)),
20 m_logger(std::move(logger)) {}
23 auto skey = m_opts.m_sentinel_private_keys.find(m_sentinel_id);
24 if(skey == m_opts.m_sentinel_private_keys.end()) {
25 m_logger->error(
"No private key specified");
28 m_privkey = skey->second;
33 m_shard_data.reserve(m_opts.m_shard_endpoints.size());
34 for(
size_t i{0}; i < m_opts.m_shard_endpoints.size(); i++) {
35 const auto& shard = m_opts.m_shard_endpoints[i];
36 m_logger->info(
"Connecting to",
41 auto sock = std::make_unique<network::tcp_socket>();
42 if(!sock->connect(shard)) {
43 m_logger->warn(
"failed to connect");
46 const auto peer_id = m_shard_network.add(std::move(sock));
47 const auto& shard_range = m_opts.m_shard_ranges[i];
48 m_shard_data.push_back(
shard_info{shard_range, peer_id});
50 m_logger->info(
"done");
53 m_shard_dist =
decltype(m_shard_dist)(0, m_shard_data.size() - 1);
55 for(
const auto& ep : m_opts.m_sentinel_endpoints) {
56 if(ep == m_opts.m_sentinel_endpoints[m_sentinel_id]) {
59 auto client = std::make_unique<sentinel::rpc::client>(
60 std::vector<network::endpoint_t>{ep},
63 m_logger->error(
"Failed to start sentinel client");
66 m_sentinel_clients.emplace_back(std::move(
client));
69 m_dist =
decltype(m_dist)(0, m_sentinel_clients.size() - 1);
71 auto rpc_server = std::make_unique<
73 m_opts.m_sentinel_endpoints[m_sentinel_id]);
74 if(!rpc_server->init()) {
75 m_logger->error(
"Failed to start sentinel RPC server");
79 m_rpc_server = std::make_unique<decltype(m_rpc_server)::element_type>(
81 std::move(rpc_server));
87 -> std::optional<cbdc::sentinel::execute_response> {
96 if(!res.has_value()) {
103 if(!res.has_value()) {
104 send_transaction(tx);
112 auto attestation = compact_tx.sign(m_secp.get(), m_privkey);
113 compact_tx.m_attestations.insert(attestation);
115 gather_attestations(tx, compact_tx, {});
119 -> std::optional<validate_response> {
121 if(res.has_value()) {
125 auto attestation = compact_tx.sign(m_secp.get(), m_privkey);
133 std::unordered_set<size_t> requested) {
134 if(!v_res.has_value()) {
136 "invalid according to remote sentinel");
140 gather_attestations(tx, ctx, std::move(requested));
144 controller::gather_attestations(
const transaction::full_tx& tx,
145 const transaction::compact_tx& ctx,
146 std::unordered_set<size_t> requested) {
148 auto success =
false;
150 auto sentinel_id = [&]() {
151 std::unique_lock l(m_rand_mut);
152 return m_dist(m_rand);
154 if(requested.find(sentinel_id) != requested.end()) {
158 = m_sentinel_clients[sentinel_id]->validate_transaction(
162 r.insert(sentinel_id);
163 validate_result_handler(v_res, tx, ctx, r);
169 send_compact_tx(ctx);
172 void controller::send_compact_tx(
const transaction::compact_tx& ctx) {
175 auto offset = [&]() {
176 std::unique_lock l(m_rand_mut);
177 return m_shard_dist(m_rand);
179 auto inputs_sent = std::unordered_set<size_t>();
180 for(
size_t i = 0; i < m_shard_data.size(); i++) {
181 auto idx = (i + offset) % m_shard_data.size();
182 const auto& range = m_shard_data[idx].m_range;
183 const auto& pid = m_shard_data[idx].m_peer_id;
184 if(inputs_sent.size() == ctx.m_inputs.size()) {
190 auto should_send =
false;
191 for(
size_t j = 0; j < ctx.m_inputs.size(); j++) {
192 if(inputs_sent.find(j) != inputs_sent.end()) {
198 inputs_sent.insert(j);
202 m_shard_network.
send(ctx_pkt, pid);
External client for sending new transactions to the system.
auto init() -> bool
Initializes the client.
auto connected(peer_id_t peer_id) -> bool
Determines whether the given peer ID is connected.
void send(const std::shared_ptr< buffer > &data, peer_id_t peer_id)
Sends the provided data to the specified peer.
Implements an RPC server over a TCP socket.
std::optional< cbdc::sentinel::validate_response > validate_result
Result of a validation operation.
auto validate_transaction(transaction::full_tx tx) -> std::optional< validate_response > override
Validate transaction and generate a sentinel attestation if the transaction is valid.
auto init() -> bool
Initializes the controller.
auto execute_transaction(transaction::full_tx tx) -> std::optional< cbdc::sentinel::execute_response > override
Validate transaction, forward it to shards for processing, and return the validation result to send b...
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.
tx_status
Status of the transaction following sentinel processing.
@ static_invalid
Statically invalid. Must be fixed and resubmitted.
@ pending
Statically valid, and the sentinel has submitted the transaction to the network for processing.
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 make_buffer(const T &obj) -> std::enable_if_t< std::is_same_v< B, nuraft::ptr< nuraft::buffer > >, nuraft::ptr< nuraft::buffer > >
Serialize object into nuraft::buffer using a cbdc::nuraft_serializer.
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.
Sentinel-specific representation of shard network information.
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.