mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
Pulse: Add random value hash stage
This commit is contained in:
parent
b140f1542b
commit
937a2fd39d
|
@ -28,6 +28,9 @@ enum struct round_state
|
|||
|
||||
submit_block_template,
|
||||
wait_for_block_template,
|
||||
|
||||
submit_random_value_hash,
|
||||
wait_for_random_value_hashes,
|
||||
};
|
||||
|
||||
constexpr std::string_view round_state_string(round_state state)
|
||||
|
@ -47,6 +50,9 @@ constexpr std::string_view round_state_string(round_state state)
|
|||
|
||||
case round_state::submit_block_template: return "Submit Block Template"sv;
|
||||
case round_state::wait_for_block_template: return "Wait For Block Template"sv;
|
||||
|
||||
case round_state::submit_random_value_hash: return "Submit Random Value Hash"sv;
|
||||
case round_state::wait_for_random_value_hashes: return "Wait For Random Value Hash"sv;
|
||||
}
|
||||
|
||||
return "Invalid2"sv;
|
||||
|
@ -107,6 +113,17 @@ struct round_context
|
|||
pulse::time_point end_time;
|
||||
} wait_for_block_template;
|
||||
|
||||
struct
|
||||
{
|
||||
cryptonote::pulse_random_value value;
|
||||
} submit_random_value_hash;
|
||||
|
||||
struct
|
||||
{
|
||||
std::array<std::pair<crypto::hash, bool>, service_nodes::PULSE_QUORUM_NUM_VALIDATORS> hashes;
|
||||
pulse::time_point end_time;
|
||||
} wait_for_random_value_hashes;
|
||||
|
||||
round_state state;
|
||||
};
|
||||
|
||||
|
@ -147,12 +164,49 @@ bool msg_time_check(round_context const &context, pulse::message const &msg, pul
|
|||
return true;
|
||||
}
|
||||
|
||||
crypto::hash make_message_signature_hash(round_context const &context, pulse::message const &msg)
|
||||
{
|
||||
assert(context.state >= round_state::wait_for_next_block);
|
||||
crypto::hash result = {};
|
||||
switch(msg.type)
|
||||
{
|
||||
case pulse::message_type::invalid:
|
||||
assert("Invalid Code Path" == nullptr);
|
||||
break;
|
||||
|
||||
case pulse::message_type::handshake:
|
||||
{
|
||||
auto buf = tools::memcpy_le(context.wait_for_next_block.top_hash.data, msg.quorum_position);
|
||||
result = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
}
|
||||
break;
|
||||
|
||||
case pulse::message_type::handshake_bitset:
|
||||
{
|
||||
auto buf = tools::memcpy_le(msg.handshakes.validator_bitset, context.wait_for_next_block.top_hash.data, msg.quorum_position);
|
||||
result = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
}
|
||||
break;
|
||||
|
||||
case pulse::message_type::block_template:
|
||||
result = crypto::cn_fast_hash(msg.block_template.blob.data(), msg.block_template.blob.size());
|
||||
break;
|
||||
|
||||
case pulse::message_type::random_value_hash:
|
||||
{
|
||||
auto buf = tools::memcpy_le(context.wait_for_next_block.top_hash.data, msg.quorum_position, msg.random_value_hash.hash.data);
|
||||
result = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool message_signature_check(pulse::message const &msg, service_nodes::quorum const &quorum)
|
||||
{
|
||||
// Generate hash that was signed to a signature
|
||||
// Get Service Node Key
|
||||
crypto::public_key const *key = nullptr;
|
||||
crypto::hash hash = {};
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
case pulse::message_type::invalid:
|
||||
|
@ -164,7 +218,8 @@ bool message_signature_check(pulse::message const &msg, service_nodes::quorum co
|
|||
break;
|
||||
|
||||
case pulse::message_type::handshake: /* FALLTHRU */
|
||||
case pulse::message_type::handshake_bitset:
|
||||
case pulse::message_type::handshake_bitset: /* FALLTHRU */
|
||||
case pulse::message_type::random_value_hash:
|
||||
{
|
||||
if (msg.quorum_position >= static_cast<int>(quorum.validators.size()))
|
||||
{
|
||||
|
@ -173,16 +228,6 @@ bool message_signature_check(pulse::message const &msg, service_nodes::quorum co
|
|||
}
|
||||
|
||||
key = &quorum.validators[msg.quorum_position];
|
||||
if (msg.type == pulse::message_type::handshake)
|
||||
{
|
||||
auto buf = tools::memcpy_le(context.wait_for_next_block.top_hash.data, msg.quorum_position);
|
||||
hash = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto buf = tools::memcpy_le(msg.handshakes.validator_bitset, context.wait_for_next_block.top_hash.data, msg.quorum_position);
|
||||
hash = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -194,14 +239,13 @@ bool message_signature_check(pulse::message const &msg, service_nodes::quorum co
|
|||
return false;
|
||||
}
|
||||
|
||||
key = &context.prepare_for_round.quorum.workers[0];
|
||||
hash = crypto::cn_fast_hash(msg.block_template.blob.data(), msg.block_template.blob.size());
|
||||
key = &context.prepare_for_round.quorum.workers[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Verify hash with signature
|
||||
if (!crypto::check_signature(hash, *key, msg.signature))
|
||||
// Verify
|
||||
if (!crypto::check_signature(make_message_signature_hash(context, msg), *key, msg.signature))
|
||||
{
|
||||
MERROR(log_prefix(context) << "Signature from pulse handshake bit does not validate with node " << msg.quorum_position << ":" << lokimq::to_hex(tools::view_guts(*key)) << ", at height " << context.wait_for_next_block.height << "; Node signing outdated height or bad handshake data");
|
||||
return false;
|
||||
|
@ -287,6 +331,21 @@ void pulse::handle_message(void *quorumnet_state, pulse::message const &msg)
|
|||
|
||||
}
|
||||
break;
|
||||
|
||||
case pulse::message_type::random_value_hash:
|
||||
{
|
||||
if (!msg_time_check(context, msg, pulse::clock::now(), context.wait_for_handshakes.start_time, context.wait_for_random_value_hashes.end_time))
|
||||
return;
|
||||
|
||||
auto &[hash, received] = context.wait_for_random_value_hashes.hashes[msg.quorum_position];
|
||||
if (received)
|
||||
return; // Already received their hash
|
||||
|
||||
received = true;
|
||||
relay_message = true;
|
||||
hash = msg.random_value_hash.hash;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (relay_message)
|
||||
|
@ -457,12 +516,20 @@ Yes +-----[Insufficient Bitsets]
|
|||
|
||||
*/
|
||||
|
||||
|
||||
enum struct event_loop
|
||||
{
|
||||
keep_running,
|
||||
return_to_caller,
|
||||
};
|
||||
|
||||
event_loop goto_preparing_for_next_round(round_context &context)
|
||||
{
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
return event_loop::keep_running;
|
||||
}
|
||||
|
||||
event_loop wait_for_next_block(uint64_t hf16_height, round_context &context, cryptonote::Blockchain const &blockchain)
|
||||
{
|
||||
//
|
||||
|
@ -540,6 +607,8 @@ event_loop prepare_for_round(round_context &context, service_nodes::service_node
|
|||
context.wait_for_handshake_bitsets = {};
|
||||
context.submit_block_template = {};
|
||||
context.wait_for_block_template = {};
|
||||
context.submit_random_value_hash = {};
|
||||
context.wait_for_random_value_hashes = {};
|
||||
|
||||
if (context.prepare_for_round.queue_for_next_round)
|
||||
{
|
||||
|
@ -568,10 +637,11 @@ event_loop prepare_for_round(round_context &context, service_nodes::service_node
|
|||
}
|
||||
|
||||
auto start_time = context.wait_for_next_block.round_0_start_time + (context.prepare_for_round.round * service_nodes::PULSE_ROUND_TIME);
|
||||
context.wait_for_handshakes.start_time = start_time;
|
||||
context.wait_for_handshakes.end_time = start_time + service_nodes::PULSE_WAIT_FOR_HANDSHAKES_DURATION;
|
||||
context.wait_for_handshake_bitsets.end_time = context.wait_for_handshakes.end_time + service_nodes::PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION;
|
||||
context.wait_for_block_template.end_time = context.wait_for_handshake_bitsets.end_time + service_nodes::PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION;
|
||||
context.wait_for_handshakes.start_time = start_time;
|
||||
context.wait_for_handshakes.end_time = start_time + service_nodes::PULSE_WAIT_FOR_HANDSHAKES_DURATION;
|
||||
context.wait_for_handshake_bitsets.end_time = context.wait_for_handshakes.end_time + service_nodes::PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION;
|
||||
context.wait_for_block_template.end_time = context.wait_for_handshake_bitsets.end_time + service_nodes::PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION;
|
||||
context.wait_for_random_value_hashes.end_time = context.wait_for_block_template.end_time + service_nodes::PULSE_WAIT_FOR_RANDOM_VALUE_HASH_DURATION;
|
||||
|
||||
context.prepare_for_round.quorum =
|
||||
service_nodes::generate_pulse_quorum(blockchain.nettype(),
|
||||
|
@ -618,14 +688,10 @@ event_loop prepare_for_round(round_context &context, service_nodes::service_node
|
|||
if (context.prepare_for_round.participant == sn_type::none)
|
||||
{
|
||||
MINFO(log_prefix(context) << "We are not a pulse validator. Waiting for next pulse round or block.");
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.state = round_state::wait_for_round;
|
||||
return goto_preparing_for_next_round(context);
|
||||
}
|
||||
|
||||
context.state = round_state::wait_for_round;
|
||||
return event_loop::keep_running;
|
||||
}
|
||||
|
||||
|
@ -672,8 +738,7 @@ event_loop submit_handshakes(round_context &context, void *quorumnet_state)
|
|||
catch (std::exception const &e)
|
||||
{
|
||||
MERROR(log_prefix(context) << "Attempting to invoke and send a Pulse participation handshake unexpectedly failed. " << e.what());
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
return goto_preparing_for_next_round(context);
|
||||
}
|
||||
|
||||
return event_loop::return_to_caller;
|
||||
|
@ -718,8 +783,7 @@ event_loop submit_handshake_bitset(round_context &context, void *quorumnet_state
|
|||
catch(std::exception const &e)
|
||||
{
|
||||
MERROR(log_prefix(context) << "Attempting to invoke and send a Pulse validator bitset unexpectedly failed. " << e.what());
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
return goto_preparing_for_next_round(context);
|
||||
}
|
||||
|
||||
return event_loop::keep_running;
|
||||
|
@ -772,21 +836,18 @@ event_loop wait_for_handshake_bitsets(round_context &context)
|
|||
<< max_bitsets << ", waiting for next round.");
|
||||
}
|
||||
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
return goto_preparing_for_next_round(context);
|
||||
}
|
||||
|
||||
std::bitset<8 * sizeof(most_common_bitset)> bitset = most_common_bitset;
|
||||
context.submit_block_template.validator_bitset = most_common_bitset;
|
||||
|
||||
MINFO(log_prefix(context) << count << "/" << max_bitsets << " validators agreed on the participating nodes in the quorum " << bitset << (context.prepare_for_round.participant == sn_type::producer ? "" : ". Awaiting block template from block producer"));
|
||||
|
||||
if (context.prepare_for_round.participant == sn_type::producer)
|
||||
context.state = round_state::submit_block_template;
|
||||
else
|
||||
{
|
||||
std::bitset<8 * sizeof(most_common_bitset)> bitset = most_common_bitset;
|
||||
context.submit_block_template.validator_bitset = most_common_bitset;
|
||||
|
||||
MINFO(log_prefix(context) << count << "/" << max_bitsets << " validators agreed on the participating nodes in the quorum " << bitset << (context.prepare_for_round.participant == sn_type::producer ? "" : ". Awaiting block template from block producer"));
|
||||
|
||||
if (context.prepare_for_round.participant == sn_type::producer)
|
||||
context.state = round_state::submit_block_template;
|
||||
else
|
||||
context.state = round_state::wait_for_block_template;
|
||||
}
|
||||
context.state = round_state::wait_for_block_template;
|
||||
|
||||
return event_loop::keep_running;
|
||||
}
|
||||
|
@ -802,9 +863,7 @@ event_loop submit_block_template(round_context &context, service_nodes::service_
|
|||
if (list_state.empty())
|
||||
{
|
||||
MINFO(log_prefix(context) << "Block producer (us) is not available on the service node list, waiting until next round");
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
return event_loop::keep_running;
|
||||
return goto_preparing_for_next_round(context);
|
||||
}
|
||||
|
||||
// TODO(doyle): These checks can be done earlier?
|
||||
|
@ -812,9 +871,7 @@ event_loop submit_block_template(round_context &context, service_nodes::service_
|
|||
if (!info->is_active())
|
||||
{
|
||||
MINFO(log_prefix(context) << "Block producer (us) is not an active service node, waiting until next round");
|
||||
context.state = round_state::prepare_for_round;
|
||||
context.prepare_for_round.queue_for_next_round = true;
|
||||
return event_loop::keep_running;
|
||||
return goto_preparing_for_next_round(context);
|
||||
}
|
||||
|
||||
service_nodes::payout block_producer_payouts = service_nodes::service_node_info_to_payout(key.pub, *info);
|
||||
|
@ -867,7 +924,71 @@ event_loop wait_for_block_template(round_context &context)
|
|||
MINFO(log_prefix(context) << "Timed out, block template was not received");
|
||||
}
|
||||
|
||||
context.state = round_state::submit_random_value_hash;
|
||||
return event_loop::keep_running;
|
||||
}
|
||||
|
||||
return event_loop::return_to_caller;
|
||||
}
|
||||
|
||||
event_loop submit_random_value_hash(round_context &context, void *quorumnet_state, service_nodes::service_node_keys const &key)
|
||||
{
|
||||
assert(context.prepare_for_round.participant == sn_type::validator);
|
||||
|
||||
// Random Value
|
||||
crypto::generate_random_bytes_thread_safe(sizeof(context.submit_random_value_hash.value.data), context.submit_random_value_hash.value.data);
|
||||
|
||||
// Message
|
||||
pulse::message msg = {};
|
||||
msg.type = pulse::message_type::random_value_hash;
|
||||
msg.quorum_position = context.prepare_for_round.my_quorum_position;
|
||||
msg.random_value_hash.hash = crypto::cn_fast_hash(context.submit_random_value_hash.value.data, sizeof(context.submit_random_value_hash.value.data));
|
||||
crypto::generate_signature(make_message_signature_hash(context, msg), key.pub, key.key, msg.signature);
|
||||
|
||||
// Send
|
||||
cryptonote::quorumnet_pulse_relay_message_to_quorum(quorumnet_state, msg, context.prepare_for_round.quorum, false /*block_producer*/);
|
||||
context.state = round_state::wait_for_random_value_hashes;
|
||||
return event_loop::return_to_caller;
|
||||
}
|
||||
|
||||
event_loop wait_for_random_value_hashes(round_context &context)
|
||||
{
|
||||
bool timed_out = pulse::clock::now() >= context.wait_for_block_template.end_time;
|
||||
|
||||
int received_hashes = 0;
|
||||
int expected_hashes = 0;
|
||||
uint16_t received_hashes_bitset = 0;
|
||||
uint16_t validator_bitset = context.wait_for_block_template.block.pulse.validator_bitset;
|
||||
for (size_t i = 0; i < service_nodes::PULSE_QUORUM_NUM_VALIDATORS; i++)
|
||||
{
|
||||
auto &[hash, received] = context.wait_for_random_value_hashes.hashes[i];
|
||||
uint16_t bit = (1 << i);
|
||||
|
||||
if (validator_bitset & bit) // This validator is locked in for this round
|
||||
{
|
||||
received_hashes_bitset |= bit;
|
||||
received_hashes += received;
|
||||
expected_hashes++;
|
||||
}
|
||||
}
|
||||
|
||||
if (expected_hashes == 0)
|
||||
{
|
||||
auto block_bitset = std::bitset<sizeof(validator_bitset) * 8>(context.wait_for_block_template.block.pulse.validator_bitset);
|
||||
auto our_bitset = std::bitset<sizeof(validator_bitset) * 8>(context.submit_block_template.validator_bitset);
|
||||
MERROR(log_prefix(context) << "Internal error, unexpected block validator bitset is empty " << block_bitset << ", our bitset was " << our_bitset);
|
||||
return event_loop::keep_running;
|
||||
}
|
||||
|
||||
// TODO(doyle): Does this need to be == expected hashes or can it just be > min validator nodes for signing, i.e. 7.
|
||||
if (timed_out || received_hashes == expected_hashes)
|
||||
{
|
||||
if (timed_out && received_hashes != expected_hashes)
|
||||
return goto_preparing_for_next_round(context);
|
||||
|
||||
context.state = round_state::wait_for_next_block;
|
||||
auto received_bitset = std::bitset<sizeof(validator_bitset) * 8>(context.submit_block_template.validator_bitset);
|
||||
MINFO(log_prefix(context) << "Received " << received_hashes << " random value hashes from " << received_bitset << (timed_out ? ". We timed out and some hashes are missing" : ""));
|
||||
return event_loop::keep_running;
|
||||
}
|
||||
|
||||
|
@ -937,6 +1058,14 @@ void pulse::main(void *quorumnet_state, cryptonote::core &core)
|
|||
case round_state::wait_for_block_template:
|
||||
loop = wait_for_block_template(context);
|
||||
break;
|
||||
|
||||
case round_state::submit_random_value_hash:
|
||||
loop = submit_random_value_hash(context, quorumnet_state, key);
|
||||
break;
|
||||
|
||||
case round_state::wait_for_random_value_hashes:
|
||||
loop = wait_for_random_value_hashes(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ enum struct message_type : uint8_t
|
|||
handshake,
|
||||
handshake_bitset,
|
||||
block_template,
|
||||
random_value_hash,
|
||||
};
|
||||
|
||||
constexpr std::string_view message_type_string(message_type type)
|
||||
|
@ -42,6 +43,7 @@ constexpr std::string_view message_type_string(message_type type)
|
|||
case message_type::handshake: return "Handshake"sv;
|
||||
case message_type::handshake_bitset: return "Handshake Bitset"sv;
|
||||
case message_type::block_template: return "Block Template"sv;
|
||||
case message_type::random_value_hash: return "Random Value Hash"sv;
|
||||
}
|
||||
return "Invalid2"sv;
|
||||
}
|
||||
|
@ -61,6 +63,11 @@ struct message
|
|||
{
|
||||
std::string blob;
|
||||
} block_template;
|
||||
|
||||
struct
|
||||
{
|
||||
crypto::hash hash;
|
||||
} random_value_hash;
|
||||
};
|
||||
|
||||
void main(void *quorumnet_state, cryptonote::core &core);
|
||||
|
|
|
@ -9,24 +9,19 @@ namespace service_nodes {
|
|||
constexpr size_t PULSE_QUORUM_ENTROPY_LAG = 21; // How many blocks back from the tip of the Blockchain to source entropy for the Pulse quorums.
|
||||
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
|
||||
constexpr auto PULSE_ROUND_TIME = 20s;
|
||||
constexpr auto PULSE_WAIT_FOR_HANDSHAKES_DURATION = 7s;
|
||||
constexpr auto PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 7s;
|
||||
constexpr auto PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 6s;
|
||||
constexpr auto PULSE_WAIT_FOR_HANDSHAKES_DURATION = 5s;
|
||||
constexpr auto PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 5s;
|
||||
constexpr auto PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 5s;
|
||||
constexpr auto PULSE_WAIT_FOR_RANDOM_VALUE_HASH_DURATION = 5s;
|
||||
|
||||
constexpr size_t PULSE_QUORUM_NUM_VALIDATORS = 0;
|
||||
constexpr size_t PULSE_BLOCK_REQUIRED_SIGNATURES = 0;
|
||||
#else
|
||||
#if 0
|
||||
constexpr auto PULSE_ROUND_TIME = 60s;
|
||||
constexpr auto PULSE_WAIT_FOR_HANDSHAKES_DURATION = 20s;
|
||||
constexpr auto PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 20s;
|
||||
constexpr auto PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 20s;
|
||||
#else
|
||||
constexpr auto PULSE_ROUND_TIME = 20s;
|
||||
constexpr auto PULSE_WAIT_FOR_HANDSHAKES_DURATION = 7s;
|
||||
constexpr auto PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 7s;
|
||||
constexpr auto PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 6s;
|
||||
#endif
|
||||
constexpr auto PULSE_WAIT_FOR_HANDSHAKES_DURATION = 15s;
|
||||
constexpr auto PULSE_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 15s;
|
||||
constexpr auto PULSE_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 15s;
|
||||
constexpr auto PULSE_WAIT_FOR_RANDOM_VALUE_HASH_DURATION = 15s;
|
||||
|
||||
constexpr size_t PULSE_QUORUM_NUM_VALIDATORS = 7;
|
||||
constexpr size_t PULSE_BLOCK_REQUIRED_SIGNATURES = 7; // A block must have exactly N signatures to be considered properly
|
||||
|
|
|
@ -1084,7 +1084,7 @@ void extract_signature_values(bt_dict_consumer& data, std::string_view key, std:
|
|||
crypto::signature convert_string_view_bytes_to_signature(std::string_view sig_str)
|
||||
{
|
||||
if (sig_str.size() != sizeof(crypto::signature))
|
||||
throw std::invalid_argument("Invalid signature data size: " + std::to_string(sizeof(crypto::signature)));
|
||||
throw std::invalid_argument("Invalid signature data size: " + std::to_string(sig_str.size()));
|
||||
|
||||
crypto::signature result;
|
||||
std::memcpy(&result, sig_str.data(), sizeof(crypto::signature));
|
||||
|
@ -1421,18 +1421,21 @@ void handle_blink_success(Message& m) {
|
|||
//
|
||||
// Pulse
|
||||
//
|
||||
const std::string PULSE_TAG_VALIDATOR_BITSET = "b";
|
||||
const std::string PULSE_TAG_QUORUM_POSITION = "q";
|
||||
const std::string PULSE_TAG_SIGNATURE = "s";
|
||||
const std::string PULSE_TAG_BLOCK_TEMPLATE = "t";
|
||||
const std::string PULSE_TAG_VALIDATOR_BITSET = "b";
|
||||
const std::string PULSE_TAG_QUORUM_POSITION = "q";
|
||||
const std::string PULSE_TAG_SIGNATURE = "s";
|
||||
const std::string PULSE_TAG_BLOCK_TEMPLATE = "t";
|
||||
const std::string PULSE_TAG_RANDOM_VALUE_HASH = "#";
|
||||
|
||||
const std::string PULSE_CMD_CATEGORY = "pulse";
|
||||
const std::string PULSE_CMD_VALIDATOR_BITSET = "validator_bitset";
|
||||
const std::string PULSE_CMD_VALIDATOR_BIT = "validator_bit";
|
||||
const std::string PULSE_CMD_BLOCK_TEMPLATE = "block_template";
|
||||
const std::string PULSE_CMD_SEND_VALIDATOR_BITSET = PULSE_CMD_CATEGORY + "." + PULSE_CMD_VALIDATOR_BITSET;
|
||||
const std::string PULSE_CMD_SEND_VALIDATOR_BIT = PULSE_CMD_CATEGORY + "." + PULSE_CMD_VALIDATOR_BIT;
|
||||
const std::string PULSE_CMD_SEND_BLOCK_TEMPLATE = PULSE_CMD_CATEGORY + "." + PULSE_CMD_BLOCK_TEMPLATE;
|
||||
const std::string PULSE_CMD_CATEGORY = "pulse";
|
||||
const std::string PULSE_CMD_VALIDATOR_BITSET = "validator_bitset";
|
||||
const std::string PULSE_CMD_VALIDATOR_BIT = "validator_bit";
|
||||
const std::string PULSE_CMD_BLOCK_TEMPLATE = "block_template";
|
||||
const std::string PULSE_CMD_RANDOM_VALUE_HASH = "random_value_hash";
|
||||
const std::string PULSE_CMD_SEND_VALIDATOR_BITSET = PULSE_CMD_CATEGORY + "." + PULSE_CMD_VALIDATOR_BITSET;
|
||||
const std::string PULSE_CMD_SEND_VALIDATOR_BIT = PULSE_CMD_CATEGORY + "." + PULSE_CMD_VALIDATOR_BIT;
|
||||
const std::string PULSE_CMD_SEND_BLOCK_TEMPLATE = PULSE_CMD_CATEGORY + "." + PULSE_CMD_BLOCK_TEMPLATE;
|
||||
const std::string PULSE_CMD_SEND_RANDOM_VALUE_HASH = PULSE_CMD_CATEGORY + "." + PULSE_CMD_RANDOM_VALUE_HASH;
|
||||
|
||||
bt_dict pulse_serialize_message(pulse::message const &msg)
|
||||
{
|
||||
|
@ -1440,7 +1443,6 @@ bt_dict pulse_serialize_message(pulse::message const &msg)
|
|||
|
||||
switch(msg.type)
|
||||
{
|
||||
default:
|
||||
case pulse::message_type::invalid:
|
||||
{
|
||||
assert(!"Invalid Code Path");
|
||||
|
@ -1468,6 +1470,14 @@ bt_dict pulse_serialize_message(pulse::message const &msg)
|
|||
{PULSE_TAG_BLOCK_TEMPLATE, msg.block_template.blob}};
|
||||
}
|
||||
break;
|
||||
|
||||
case pulse::message_type::random_value_hash:
|
||||
{
|
||||
result = {{PULSE_TAG_QUORUM_POSITION, msg.quorum_position},
|
||||
{PULSE_TAG_SIGNATURE, tools::view_guts(msg.signature)},
|
||||
{PULSE_TAG_RANDOM_VALUE_HASH, tools::view_guts(msg.random_value_hash.hash)}};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1481,12 +1491,12 @@ void pulse_relay_message_to_quorum(void *self, pulse::message const &msg, servic
|
|||
std::string_view command = {};
|
||||
switch(msg.type)
|
||||
{
|
||||
default: break;
|
||||
case pulse::message_type::invalid:
|
||||
assert("Invalid Code Path" == nullptr);
|
||||
break;
|
||||
|
||||
case pulse::message_type::block_template:
|
||||
{
|
||||
command = PULSE_CMD_SEND_BLOCK_TEMPLATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case pulse::message_type::handshake: /* FALLTHRU */
|
||||
|
@ -1506,6 +1516,10 @@ void pulse_relay_message_to_quorum(void *self, pulse::message const &msg, servic
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case pulse::message_type::random_value_hash:
|
||||
command = PULSE_CMD_SEND_RANDOM_VALUE_HASH;
|
||||
break;
|
||||
}
|
||||
|
||||
QnetState &qnet = *static_cast<QnetState *>(self);
|
||||
|
@ -1687,6 +1701,51 @@ void handle_pulse_block_template(Message &m, QnetState &qnet)
|
|||
qnet.lmq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
|
||||
}
|
||||
|
||||
void handle_pulse_random_value_hash(Message &m, QnetState &qnet)
|
||||
{
|
||||
if (m.data.size() != 1)
|
||||
throw std::runtime_error(std::string("Rejecting pulse block template expected one data entry not ") + std::to_string(m.data.size()));
|
||||
|
||||
bt_dict_consumer data{m.data[0]};
|
||||
|
||||
int quorum_position = -1;
|
||||
crypto::hash random_value_hash = {};
|
||||
crypto::signature signature = {};
|
||||
{
|
||||
std::string_view INVALID_ARG_PREFIX = "Invalid pulse random value hash: missing required field '"sv;
|
||||
if (auto const &tag = PULSE_TAG_RANDOM_VALUE_HASH; data.skip_until(tag)) {
|
||||
auto str = data.consume_string_view();
|
||||
if (str.size() != sizeof(random_value_hash))
|
||||
throw std::invalid_argument("Invalid hash data size: " + std::to_string(str.size()));
|
||||
|
||||
std::memcpy(random_value_hash.data, str.data(), str.size());
|
||||
} else {
|
||||
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + std::string(tag) + "'");
|
||||
}
|
||||
|
||||
if (auto const &tag = PULSE_TAG_QUORUM_POSITION; data.skip_until(tag))
|
||||
quorum_position = data.consume_integer<int>();
|
||||
else
|
||||
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + std::string(tag) + "'");
|
||||
|
||||
if (auto const &tag = PULSE_TAG_SIGNATURE; data.skip_until(tag)) {
|
||||
auto sig_str = data.consume_string_view();
|
||||
signature = convert_string_view_bytes_to_signature(sig_str);
|
||||
} else {
|
||||
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + std::string(tag) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
pulse::message msg = {};
|
||||
msg.type = pulse::message_type::random_value_hash;
|
||||
msg.quorum_position = quorum_position;
|
||||
msg.signature = signature;
|
||||
msg.random_value_hash.hash = random_value_hash;
|
||||
|
||||
auto *self = reinterpret_cast<void *>(&qnet);
|
||||
qnet.lmq.job([self, data = std::move(msg)]() { pulse::handle_message(self, data); }, qnet.core.pulse_thread_id());
|
||||
}
|
||||
|
||||
} // end empty namespace
|
||||
|
||||
|
||||
|
@ -1746,6 +1805,7 @@ void setup_endpoints(QnetState& qnet) {
|
|||
.add_command(PULSE_CMD_VALIDATOR_BIT, [&qnet](Message& m) { handle_pulse_participation_bit_or_bitset(m, qnet, false /*bitset*/); })
|
||||
.add_command(PULSE_CMD_VALIDATOR_BITSET, [&qnet](Message& m) { handle_pulse_participation_bit_or_bitset(m, qnet, true /*bitset*/); })
|
||||
.add_command(PULSE_CMD_BLOCK_TEMPLATE, [&qnet](Message& m) { handle_pulse_block_template(m, qnet); })
|
||||
.add_command(PULSE_CMD_RANDOM_VALUE_HASH, [&qnet](Message& m) { handle_pulse_random_value_hash(m, qnet); })
|
||||
;
|
||||
|
||||
// Compatibility aliases. No longer used since 7.1.4, but can still be received from previous
|
||||
|
|
Loading…
Reference in a new issue