Enforce ordering of votes in HF13

This commit is contained in:
Doyle 2019-08-14 14:52:51 +10:00
parent d973d42250
commit a6cbad1701
8 changed files with 61 additions and 17 deletions

View File

@ -1246,6 +1246,7 @@ namespace cryptonote
bufPtr += snprintf(bufPtr, bufEnd - bufPtr, ", ");
}
if (vvc.m_invalid_vote_type) bufPtr += snprintf(bufPtr, bufEnd - bufPtr, "Vote type has invalid value: %s, ", vote ? std::to_string((uint8_t)vote->type).c_str() : "??");
if (vvc.m_votes_not_sorted) bufPtr += snprintf(bufPtr, bufEnd - bufPtr, "Votes are not stored in ascending order");
if (bufPtr != buf)
{

View File

@ -48,6 +48,7 @@ namespace cryptonote
bool m_not_enough_votes;
bool m_incorrect_voting_group;
bool m_invalid_vote_type;
bool m_votes_not_sorted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_verification_failed)
@ -60,6 +61,7 @@ namespace cryptonote
KV_SERIALIZE(m_not_enough_votes)
KV_SERIALIZE(m_incorrect_voting_group)
KV_SERIALIZE(m_invalid_vote_type)
KV_SERIALIZE(m_votes_not_sorted)
END_KV_SERIALIZE_MAP()
};

View File

@ -1630,7 +1630,7 @@ namespace cryptonote
}
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_block(const blobdata& block_blob, const block *b, block_verification_context& bvc, checkpoint_t const *checkpoint, bool update_miner_blocktemplate)
bool core::handle_incoming_block(const blobdata& block_blob, const block *b, block_verification_context& bvc, checkpoint_t *checkpoint, bool update_miner_blocktemplate)
{
TRY_ENTRY();
bvc = boost::value_initialized<block_verification_context>();
@ -1658,6 +1658,40 @@ namespace cryptonote
}
b = &lb;
}
// TODO(loki): This check should be redundant and included in
// verify_checkpoints once we enable it. It is not enabled until alternate
// quorums are implemented and merged
if (checkpoint)
{
if (b->major_version >= network_version_13)
{
if (checkpoint->signatures.size() > 1)
{
for (size_t i = 0; i < (checkpoint->signatures.size() - 1); i++)
{
auto curr = checkpoint->signatures[i].voter_index;
auto next = checkpoint->signatures[i + 1].voter_index;
if (curr >= next)
{
LOG_PRINT_L1("Voters in checkpoints are not given in ascending order, block failed");
bvc.m_verifivation_failed = true;
return false;
}
}
}
}
else
{
std::sort(checkpoint->signatures.begin(),
checkpoint->signatures.end(),
[](service_nodes::voter_to_signature const &lhs, service_nodes::voter_to_signature const &rhs) {
return lhs.voter_index < rhs.voter_index;
});
}
}
add_new_block(*b, bvc, checkpoint);
if(update_miner_blocktemplate && bvc.m_added_to_main_chain)
m_miner.on_block_chain_update();

View File

@ -166,7 +166,7 @@ namespace cryptonote
* @return false if loading new checkpoints fails, or the block is not
* added, otherwise true
*/
bool handle_incoming_block(const blobdata& block_blob, const block *b, block_verification_context& bvc, checkpoint_t const *checkpoint, bool update_miner_blocktemplate = true);
bool handle_incoming_block(const blobdata& block_blob, const block *b, block_verification_context& bvc, checkpoint_t *checkpoint, bool update_miner_blocktemplate = true);
/**
* @copydoc Blockchain::prepare_handle_incoming_blocks

View File

@ -206,8 +206,21 @@ namespace service_nodes
crypto::hash const hash = make_state_change_vote_hash(state_change.block_height, state_change.service_node_index, state_change.state);
std::array<int, service_nodes::STATE_CHANGE_QUORUM_SIZE> validator_set = {};
uint64_t validator_index_tracker = -1;
for (const auto &vote : state_change.votes)
{
if (hf_version >= cryptonote::network_version_13) // NOTE: After HF13, votes must be stored in ascending order
{
if (validator_index_tracker >= vote.validator_index)
{
vvc.m_votes_not_sorted = true;
LOG_PRINT_L1("Vote validator index is not stored in ascending order, prev validator index: "
<< validator_index_tracker << ", curr index: " << vote.validator_index);
return bad_tx(tvc);
}
validator_index_tracker = vote.validator_index;
}
if (!bounds_check_validator_index(quorum, vote.validator_index, &vvc))
return bad_tx(tvc);
@ -482,14 +495,15 @@ namespace service_nodes
// return: True if the vote was unique
static bool add_vote_to_pool_if_unique(std::vector<pool_vote_entry> &votes, quorum_vote_t const &vote)
{
auto vote_it = std::find_if(votes.begin(), votes.end(), [&vote](pool_vote_entry const &pool_entry) {
assert(pool_entry.vote.group == vote.group);
return (pool_entry.vote.index_in_group == vote.index_in_group);
});
auto vote_it = std::lower_bound(
votes.begin(), votes.end(), vote, [](pool_vote_entry const &pool_entry, quorum_vote_t const &vote) {
assert(pool_entry.vote.group == vote.group);
return pool_entry.vote.index_in_group < vote.index_in_group;
});
if (vote_it == votes.end())
if (vote_it == votes.end() || vote_it->vote.index_in_group != vote.index_in_group)
{
votes.push_back({vote});
votes.insert(vote_it, {vote});
return true;
}

View File

@ -1321,13 +1321,6 @@ namespace cryptonote
return false;
}
std::sort(
checkpoint->signatures.begin(),
checkpoint->signatures.end(),
[](service_nodes::voter_to_signature const &lhs, service_nodes::voter_to_signature const &rhs) {
return lhs.voter_index < rhs.voter_index;
});
// TODO(doyle): If we are receiving alternative blocks, we won't
// have the quorum for the alternative chain meaning we will not
// be able to verify the checkpoint. For now always accept

View File

@ -197,7 +197,7 @@ bool tests::proxy_core::handle_incoming_txs(const std::vector<blobdata>& tx_blob
return true;
}
bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block_, cryptonote::block_verification_context& bvc, cryptonote::checkpoint_t const *checkpoint, bool update_miner_blocktemplate) {
bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block_, cryptonote::block_verification_context& bvc, cryptonote::checkpoint_t *checkpoint, bool update_miner_blocktemplate) {
block b = AUTO_VAL_INIT(b);
if(!parse_and_validate_block_from_blob(block_blob, b)) {

View File

@ -78,7 +78,7 @@ namespace tests
void get_blockchain_top(uint64_t& height, crypto::hash& top_id);
bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
bool handle_incoming_txs(const std::vector<cryptonote::blobdata>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, cryptonote::checkpoint_t const *checkpoint, bool update_miner_blocktemplate = true);
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, cryptonote::checkpoint_t *checkpoint, bool update_miner_blocktemplate = true);
bool handle_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof);
void pause_mine(){}
void resume_mine(){}