Extend height tolerance to state_change tx "verifivation"

This commit is contained in:
Jason Rhinelander 2019-06-30 23:18:33 -03:00
parent 192ea32326
commit 3bc4f1ac13
3 changed files with 35 additions and 23 deletions

View File

@ -3215,9 +3215,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false;
}
if (!service_nodes::verify_tx_state_change(state_change, get_current_blockchain_height(), tvc.m_vote_ctx, *quorum, hf_version))
if (!service_nodes::verify_tx_state_change(state_change, get_current_blockchain_height(), tvc, *quorum, hf_version))
{
tvc.m_verifivation_failed = true;
// will be set by the above on serious failures (i.e. illegal value), but not for less
// serious ones like state change heights slightly outside of allowed bounds:
//tvc.m_verifivation_failed = true;
MERROR_VER("tx " << get_transaction_hash(tx) << ": state change tx could not be completely verified reason: " << print_vote_verification_context(tvc.m_vote_ctx));
return false;
}

View File

@ -157,39 +157,50 @@ namespace service_nodes
return true;
}
// If we get an incoming vote of state change tx that is outside the acceptable range by this many
// blocks then ignore it but don't trigger a connection drop; the sending side could be a couple
// blocks out of sync and sending something that it thinks is legit.
constexpr uint64_t VERIFY_HEIGHT_BUFFER = 5;
static bool bad_tx(cryptonote::tx_verification_context &tvc) {
tvc.m_verifivation_failed = true;
return false;
}
bool verify_tx_state_change(const cryptonote::tx_extra_service_node_state_change &state_change,
uint64_t latest_height,
cryptonote::vote_verification_context &vvc,
cryptonote::tx_verification_context &tvc,
const service_nodes::testing_quorum &quorum,
const uint8_t hf_version)
{
auto &vvc = tvc.m_vote_ctx;
if (state_change.state != new_state::deregister && hf_version < cryptonote::network_version_12_checkpointing)
{
LOG_PRINT_L1("Non-deregister state changes are invalid before v12");
return false;
return bad_tx(tvc);
}
if (state_change.state > new_state::recommission)
{
LOG_PRINT_L1("Unknown state change to new state: " << static_cast<uint16_t>(state_change.state));
return false;
return bad_tx(tvc);
}
if (state_change.votes.size() < service_nodes::STATE_CHANGE_MIN_VOTES_TO_CHANGE_STATE)
{
LOG_PRINT_L1("Not enough votes");
vvc.m_not_enough_votes = true;
return false;
return bad_tx(tvc);
}
if (state_change.votes.size() > service_nodes::STATE_CHANGE_QUORUM_SIZE)
{
LOG_PRINT_L1("Too many votes");
return false;
return bad_tx(tvc);
}
if (!bounds_check_worker_index(quorum, state_change.service_node_index, &vvc))
return false;
return bad_tx(tvc);
// Check if state_change is too old or too new to hold onto
{
@ -200,11 +211,12 @@ namespace service_nodes
<< ", is newer than current height: " << latest_height
<< " blocks and has been rejected.");
vvc.m_invalid_block_height = true;
if (state_change.block_height >= latest_height + VERIFY_HEIGHT_BUFFER)
tvc.m_verifivation_failed = true;
return false;
}
uint64_t delta_height = latest_height - state_change.block_height;
if (latest_height >= state_change.block_height && delta_height >= service_nodes::STATE_CHANGE_TX_LIFETIME_IN_BLOCKS)
if (latest_height >= state_change.block_height + service_nodes::STATE_CHANGE_TX_LIFETIME_IN_BLOCKS)
{
LOG_PRINT_L1("Received state change tx for height: "
<< state_change.block_height << " and service node: " << state_change.service_node_index
@ -212,6 +224,8 @@ namespace service_nodes
<< " (current height: " << latest_height << ") "
<< "blocks and has been rejected.");
vvc.m_invalid_block_height = true;
if (latest_height >= state_change.block_height + (service_nodes::STATE_CHANGE_TX_LIFETIME_IN_BLOCKS + VERIFY_HEIGHT_BUFFER))
tvc.m_verifivation_failed = true;
return false;
}
}
@ -221,13 +235,13 @@ namespace service_nodes
for (const auto &vote : state_change.votes)
{
if (!bounds_check_validator_index(quorum, vote.validator_index, &vvc))
return false;
return bad_tx(tvc);
if (++validator_set[vote.validator_index] > 1)
{
vvc.m_duplicate_voters = true;
LOG_PRINT_L1("Voter quorum index is duplicated: " << vote.validator_index);
return false;
return bad_tx(tvc);
}
crypto::public_key const &key = quorum.validators[vote.validator_index];
@ -235,7 +249,7 @@ namespace service_nodes
{
LOG_PRINT_L1("Invalid signatures for votes");
vvc.m_signature_not_valid = true;
return false;
return bad_tx(tvc);
}
}
@ -319,22 +333,17 @@ namespace service_nodes
// NOTE: Validate vote age
//
{
// If we get an incoming vote that is outside the acceptable range by this many votes then
// just ignore it instead of dropping the connection; the sending side could be a couple
// blocks out of sync and sending something that it thinks is legit.
constexpr uint64_t VOTE_BUFFER = 5;
bool height_in_buffer = false;
if (latest_height > vote.block_height + VOTE_LIFETIME)
if (latest_height > vote.block_height + VERIFY_HEIGHT_BUFFER)
{
height_in_buffer = latest_height <= vote.block_height + (VOTE_LIFETIME + VOTE_BUFFER);
LOG_PRINT_L1("Received vote for height: " << vote.block_height << ", is older than: " << VOTE_LIFETIME
height_in_buffer = latest_height <= vote.block_height + (VOTE_LIFETIME + VERIFY_HEIGHT_BUFFER);
LOG_PRINT_L1("Received vote for height: " << vote.block_height << ", is older than: " << VERIFY_HEIGHT_BUFFER
<< " blocks and has been rejected.");
vvc.m_invalid_block_height = true;
}
else if (vote.block_height > latest_height)
{
height_in_buffer = vote.block_height <= latest_height + VOTE_BUFFER;
height_in_buffer = vote.block_height <= latest_height + VERIFY_HEIGHT_BUFFER;
LOG_PRINT_L1("Received vote for height: " << vote.block_height << ", is newer than: " << latest_height
<< " (latest block height) and has been rejected.");
vvc.m_invalid_block_height = true;

View File

@ -42,6 +42,7 @@
namespace cryptonote
{
struct tx_verification_context;
struct vote_verification_context;
struct checkpoint_t;
};
@ -101,7 +102,7 @@ namespace service_nodes
quorum_vote_t make_state_change_vote(uint64_t block_height, uint16_t index_in_group, uint16_t worker_index, new_state state, crypto::public_key const &pub_key, crypto::secret_key const &secret_key);
bool verify_checkpoint (cryptonote::checkpoint_t const &checkpoint, service_nodes::testing_quorum const &quorum);
bool verify_tx_state_change (const cryptonote::tx_extra_service_node_state_change& state_change, uint64_t latest_height, cryptonote::vote_verification_context& vvc, const service_nodes::testing_quorum &quorum, uint8_t hf_version);
bool verify_tx_state_change (const cryptonote::tx_extra_service_node_state_change& state_change, uint64_t latest_height, cryptonote::tx_verification_context& vvc, const service_nodes::testing_quorum &quorum, uint8_t hf_version);
bool verify_vote (const quorum_vote_t& vote, uint64_t latest_height, cryptonote::vote_verification_context &vvc, const service_nodes::testing_quorum &quorum);
crypto::signature make_signature_from_vote (quorum_vote_t const &vote, const crypto::public_key& pub, const crypto::secret_key& sec);
crypto::signature make_signature_from_tx_state_change(cryptonote::tx_extra_service_node_state_change const &state_change, crypto::public_key const &pub, crypto::secret_key const &sec);