Update soft-forking checks to be effective HF13 (#825)

Code review

m_oldest_allowable_alternative_block -> m_immutable_height
This commit is contained in:
Doyle 2019-09-10 10:04:44 +10:00 committed by GitHub
parent d7761fa894
commit 39987729ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 154 deletions

View File

@ -170,36 +170,9 @@ namespace cryptonote
void checkpoints::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
{
uint64_t const height = get_block_height(block);
if (height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL ||
block.major_version < network_version_12_checkpointing)
if (height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL || block.major_version < network_version_12_checkpointing)
return;
if (m_nettype == MAINNET && height == HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
uint64_t start_height = 0;
get_newest_hardcoded_checkpoint(m_nettype, &start_height);
start_height += 1; // Don't start deleting from the hardcoded height
if ((start_height % service_nodes::CHECKPOINT_INTERVAL) > 0)
start_height += (service_nodes::CHECKPOINT_INTERVAL - (start_height % service_nodes::CHECKPOINT_INTERVAL));
for (uint64_t delete_height = start_height;
delete_height <= height;
delete_height += service_nodes::CHECKPOINT_INTERVAL)
{
try
{
m_db->remove_block_checkpoint(delete_height);
}
catch (const std::exception &e)
{
MERROR(
"Deleting historical checkpoints on mainnet soft-fork to checkpointing failed non-trivially at height: "
<< delete_height << ", what = " << e.what());
}
}
}
uint64_t end_cull_height = 0;
{
checkpoint_t immutable_checkpoint;
@ -266,26 +239,27 @@ namespace cryptonote
return height <= top_checkpoint_height;
}
//---------------------------------------------------------------------------
bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool* is_a_checkpoint, bool *rejected_by_service_node) const
bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool* is_a_checkpoint, bool *service_node_checkpoint) const
{
checkpoint_t checkpoint;
bool found = get_checkpoint(height, checkpoint);
if (is_a_checkpoint) *is_a_checkpoint = found;
if (service_node_checkpoint) *service_node_checkpoint = false;
if(!found)
return true;
bool result = checkpoint.check(h);
if (rejected_by_service_node)
*rejected_by_service_node = checkpoint.type == checkpoint_type::service_node && result;
if (service_node_checkpoint)
*service_node_checkpoint = (checkpoint.type == checkpoint_type::service_node);
return result;
}
//---------------------------------------------------------------------------
bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height, bool *rejected_by_service_node)
bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height, bool *service_node_checkpoint)
{
if (rejected_by_service_node)
*rejected_by_service_node = false;
if (service_node_checkpoint)
*service_node_checkpoint = false;
if (0 == block_height)
return false;
@ -301,12 +275,12 @@ namespace cryptonote
if (m_db->get_immutable_checkpoint(&immutable_checkpoint, blockchain_height))
{
immutable_height = immutable_checkpoint.height;
if (rejected_by_service_node)
*rejected_by_service_node = (immutable_checkpoint.type == checkpoint_type::service_node);
if (service_node_checkpoint)
*service_node_checkpoint = (immutable_checkpoint.type == checkpoint_type::service_node);
}
m_oldest_allowable_alternative_block = std::max(immutable_height, m_oldest_allowable_alternative_block);
bool result = block_height > m_oldest_allowable_alternative_block;
m_immutable_height = std::max(immutable_height, m_immutable_height);
bool result = block_height > m_immutable_height;
return result;
}
//---------------------------------------------------------------------------

View File

@ -164,11 +164,11 @@ namespace cryptonote
* true if the passed parameters match the stored checkpoint,
* false otherwise
*/
bool check_block(uint64_t height, const crypto::hash& h, bool *is_a_checkpoint = nullptr, bool *rejected_by_service_node = nullptr) const;
bool check_block(uint64_t height, const crypto::hash& h, bool *is_a_checkpoint = nullptr, bool *service_node_checkpoint = nullptr) const;
/**
* @brief checks if alternate chain blocks should be kept for a given height and updates
* m_oldest_allowable_alternative_block based on the available checkpoints
* m_immutable_height based on the available checkpoints
*
* this basically says if the blockchain is smaller than the first
* checkpoint then alternate blocks are allowed. Alternatively, if the
@ -181,7 +181,7 @@ namespace cryptonote
* @return true if alternate blocks are allowed given the parameters,
* otherwise false
*/
bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height, bool *rejected_by_service_node = nullptr);
bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height, bool *service_node_checkpoint = nullptr);
/**
* @brief gets the highest checkpoint height
@ -201,7 +201,7 @@ namespace cryptonote
private:
network_type m_nettype = UNDEFINED;
uint64_t m_last_cull_height = 0;
uint64_t m_oldest_allowable_alternative_block = 0;
uint64_t m_immutable_height = 0;
BlockchainDB *m_db;
};

View File

@ -160,8 +160,7 @@ static_assert(STAKING_PORTIONS % 3 == 0, "Use a multiple of three, so that it di
#define HF_VERSION_SMALLER_BP cryptonote::network_version_11_infinite_staking
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT cryptonote::network_version_11_infinite_staking
#define HF_VERSION_INCREASE_FEE cryptonote::network_version_12_checkpointing
#define HF_VERSION_PER_OUTPUT_FEE cryptonote::network_version_13
#define HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT ((uint64_t)(-1))
#define HF_VERSION_PER_OUTPUT_FEE cryptonote::network_version_13_enforce_checkpoints
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
@ -260,7 +259,7 @@ namespace cryptonote
network_version_10_bulletproofs, // Bulletproofs, Service Node Grace Registration Period, Batched Governance
network_version_11_infinite_staking, // Infinite Staking, CN-Turtle
network_version_12_checkpointing, // Checkpointing, Relaxed Deregistration, RandomXL, Loki Storage Server
network_version_13,
network_version_13_enforce_checkpoints,
network_version_count,
};

View File

@ -1787,20 +1787,14 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return false;
}
bool service_node_checkpoint = false;
if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height, &service_node_checkpoint))
{
bool rejected_by_service_node = false;
if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height, &rejected_by_service_node))
if (!service_node_checkpoint || b.major_version >= cryptonote::network_version_13_enforce_checkpoints)
{
if (rejected_by_service_node && nettype() == MAINNET && block_height < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
LOG_PRINT_L1("HF12 Checkpointing Pre-Soft Fork: Block with id: " << id << std::endl << " would NOT have been accepted for alternative chain, block height: " << block_height << std::endl << " blockchain height: " << get_current_blockchain_height());
}
else
{
MERROR_VER("Block with id: " << id << std::endl << " can't be accepted for alternative chain, block height: " << block_height << std::endl << " blockchain height: " << get_current_blockchain_height());
bvc.m_verifivation_failed = true;
return false;
}
MERROR_VER("Block with id: " << id << std::endl << " can't be accepted for alternative chain, block height: " << block_height << std::endl << " blockchain height: " << get_current_blockchain_height());
bvc.m_verifivation_failed = true;
return false;
}
}
@ -1910,17 +1904,13 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl));
alt_chain.push_back(bei);
bool rejected_by_service_node = false;
bool is_a_checkpoint = false;
if(!has_checkpoint && !m_checkpoints.check_block(bei.height, id, &is_a_checkpoint, &rejected_by_service_node))
bool service_node_checkpoint = false;
bool is_a_checkpoint = false;
if(!has_checkpoint && !m_checkpoints.check_block(bei.height, id, &is_a_checkpoint, &service_node_checkpoint))
{
if (rejected_by_service_node && nettype() == MAINNET && block_height < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
if (!service_node_checkpoint || b.major_version >= cryptonote::network_version_13_enforce_checkpoints)
{
LOG_PRINT_L1("HF12 Checkpointing Pre-Soft Fork: CHECKPOINT VALIDATION would have FAILED");
}
else
{
LOG_ERROR("CHECKPOINT VALIDATION FAILED");
LOG_ERROR("CHECKPOINT VALIDATION FAILED FOR ALT BLOCK");
bvc.m_verifivation_failed = true;
return false;
}
@ -1930,19 +1920,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
if (checkpointed || (main_chain_cumulative_difficulty < bei.cumulative_difficulty)) // check if difficulty bigger then in main chain
{
if (checkpointed)
{
bool activate_branch = nettype() != MAINNET;
if (nettype() == MAINNET && block_height > HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
activate_branch = true;
if (activate_branch)
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
else
{
MGINFO_GREEN("HF12 Checkpointing Pre-Soft Fork: ###### Would have REORGANIZED on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
return true;
}
}
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
else
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty);
@ -3791,11 +3769,15 @@ leave:
// is correct.
if(m_checkpoints.is_in_checkpoint_zone(blockchain_height))
{
if(!m_checkpoints.check_block(blockchain_height, id))
bool service_node_checkpoint = false;
if(!m_checkpoints.check_block(blockchain_height, id, nullptr, &service_node_checkpoint))
{
LOG_ERROR("CHECKPOINT VALIDATION FAILED");
bvc.m_verifivation_failed = true;
goto leave;
if (!service_node_checkpoint || (service_node_checkpoint && bl.major_version >= cryptonote::network_version_13_enforce_checkpoints))
{
LOG_ERROR("CHECKPOINT VALIDATION FAILED");
bvc.m_verifivation_failed = true;
goto leave;
}
}
}
@ -4017,6 +3999,18 @@ leave:
TIME_MEASURE_FINISH(addblock);
// TODO(loki): Temporary forking code. We have invalid checkpoints from the soft forking period we should cull
{
static uint64_t hf13_height = m_hardfork->get_earliest_ideal_height_for_version(network_version_13_enforce_checkpoints);
static uint64_t hf12_height = m_hardfork->get_earliest_ideal_height_for_version(network_version_12_checkpointing);
if (hf13_height == get_block_height(bl))
{
std::vector<checkpoint_t> checkpoints = m_db->get_checkpoints_range(hf13_height - 1, hf12_height);
for (cryptonote::checkpoint_t const &checkpoint : checkpoints)
m_db->remove_block_checkpoint(checkpoint.height);
}
}
// do this after updating the hard fork state since the weight limit may change due to fork
if (!update_next_cumulative_weight_limit())
{
@ -4304,11 +4298,8 @@ bool Blockchain::update_checkpoint(cryptonote::checkpoint_t const &checkpoint)
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if (checkpoint.height < m_db->height() && !checkpoint.check(m_db->get_block_hash_from_height(checkpoint.height)))
{
if (nettype() == MAINNET && (m_db->height() - 1) < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
LOG_PRINT_L1("HF12 Checkpointing Pre-Soft Fork: Local blockchain failed to pass a checkpoint in: " << __func__);
}
else
uint8_t hf_version = get_current_hard_fork_version();
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints)
{
// roll back to a couple of blocks before the checkpoint
LOG_ERROR("Local blockchain failed to pass a checkpoint in: " << __func__ << ", rolling back!");

View File

@ -1582,17 +1582,6 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::add_new_block(const block& b, block_verification_context& bvc, checkpoint_t const *checkpoint)
{
// TODO(loki): Temporary soft-fork code can be removed
uint64_t latest_height = std::max(get_current_blockchain_height(), get_target_blockchain_height());
if (get_nettype() == cryptonote::MAINNET &&
latest_height >= HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT &&
get_block_height(b) < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
// NOTE: After the soft fork, ignore all checkpoints from before the fork so we
// only create and enforce checkpoints from after the soft-fork.
checkpoint = nullptr;
}
bool result = m_blockchain_storage.add_new_block(b, bvc, checkpoint);
if (result)
{
@ -1661,7 +1650,7 @@ namespace cryptonote
// quorums are implemented and merged
if (checkpoint)
{
if (b->major_version >= network_version_13)
if (b->major_version >= network_version_13_enforce_checkpoints)
{
if (checkpoint->signatures.size() > 1)
{

View File

@ -489,7 +489,7 @@ namespace service_nodes
info.last_decommission_height = block_height;
info.decommission_count++;
if (hf_version >= cryptonote::network_version_13) {
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints) {
// Assigning invalid swarm id effectively kicks the node off
// its current swarm; it will be assigned a new swarm id when it
// gets recommissioned. Prior to HF13 this step was incorrectly
@ -2234,7 +2234,7 @@ namespace service_nodes
bool service_node_info::can_transition_to_state(uint8_t hf_version, uint64_t height, new_state proposed_state) const
{
if (hf_version >= cryptonote::network_version_13 && !can_be_voted_on(height))
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints && !can_be_voted_on(height))
return false;
if (proposed_state == new_state::deregister)

View File

@ -58,7 +58,7 @@ namespace service_nodes
// Perform service node tests -- this returns true is the server node is in a good state, that is,
// has submitted uptime proofs, participated in required quorums, etc.
service_node_test_results quorum_cop::check_service_node(const crypto::public_key &pubkey, const service_node_info &info) const
service_node_test_results quorum_cop::check_service_node(uint8_t hf_version, const crypto::public_key &pubkey, const service_node_info &info) const
{
service_node_test_results result; // Defaults to true for individual tests
uint64_t now = time(nullptr);
@ -82,9 +82,11 @@ namespace service_nodes
result.uptime_proved = false;
}
if (!info.proof->storage_server_reachable) {
LOG_PRINT_L1("Service Node is not reachable for node: " << pubkey);
result.storage_server_reachable = false;
if (!info.proof->storage_server_reachable)
{
LOG_PRINT_L1("Service Node storage server is not reachable for node: " << pubkey);
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints)
result.storage_server_reachable = false;
}
// IP change checks
@ -114,7 +116,8 @@ namespace service_nodes
<< CHECKPOINT_MAX_MISSABLE_VOTES << " checkpoint votes from: "
<< CHECKPOINT_MIN_QUORUMS_NODE_MUST_VOTE_IN_BEFORE_DEREGISTER_CHECK
<< " quorums that they were required to participate in.");
result.voted_in_checkpoints = false;
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints)
result.voted_in_checkpoints = false;
}
}
@ -219,11 +222,12 @@ namespace service_nodes
m_obligations_height = std::max(m_obligations_height, start_voting_from_height);
for (; m_obligations_height < (height - REORG_SAFETY_BUFFER_BLOCKS); m_obligations_height++)
{
if (m_core.get_hard_fork_version(m_obligations_height) < cryptonote::network_version_9_service_nodes) continue;
uint8_t obligations_height_hf_version = m_core.get_hard_fork_version(m_obligations_height);
if (obligations_height_hf_version < cryptonote::network_version_9_service_nodes) continue;
// NOTE: Update the number of expected checkpoint votes at the (obligation) height so we can check that
// service nodes have fulfilled their checkpointing work
if (m_core.get_hard_fork_version(m_obligations_height) >= cryptonote::network_version_12_checkpointing)
if (obligations_height_hf_version >= cryptonote::network_version_12_checkpointing)
{
if (std::shared_ptr<const testing_quorum> quorum = m_core.get_testing_quorum(quorum_type::checkpointing, m_obligations_height))
{
@ -271,34 +275,9 @@ namespace service_nodes
if (!info.can_be_voted_on(m_obligations_height))
continue;
auto test_results = check_service_node(node_key, info);
auto test_results = check_service_node(obligations_height_hf_version, node_key, info);
bool passed = test_results.passed();
if (test_results.uptime_proved &&
!test_results.voted_in_checkpoints &&
m_core.get_nettype() == cryptonote::MAINNET &&
m_obligations_height < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
LOG_PRINT_L1("HF12 Checkpointing Pre-Soft Fork: Service node: "
<< node_key
<< " failed to participate in checkpointing quorum at height: " << m_obligations_height
<< ", it would have entered the "
"decommission phase");
passed = true;
}
if (test_results.uptime_proved && !test_results.storage_server_reachable &&
m_core.get_nettype() == cryptonote::MAINNET &&
m_obligations_height < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
LOG_PRINT_L1("HF12 Checkpointing Pre-Soft Fork: Service node: "
<< node_key
<< " has unreachable storage server, it would have entered the "
"decommission phase");
passed = true;
}
new_state vote_for_state;
if (passed) {
if (info.is_decommissioned()) {
@ -419,14 +398,7 @@ namespace service_nodes
void quorum_cop::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
{
process_quorums(block);
uint64_t const height = cryptonote::get_block_height(block) + 1; // chain height = new top block height + 1
// TODO(loki): Implicit knowledge of how remove_expired_votes subtracts VOTE_LIFETIME, but this is temporary code
// that will be removed post soft-fork.
if (m_core.get_nettype() == cryptonote::MAINNET && (height - 1) == HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
m_vote_pool.remove_expired_votes(height + VOTE_LIFETIME - 1);
m_vote_pool.remove_expired_votes(height);
m_vote_pool.remove_used_votes(txs, block.major_version);
}
@ -435,15 +407,6 @@ namespace service_nodes
{
vvc = {};
uint64_t curr_height = m_core.get_blockchain_storage().get_current_blockchain_height();
if (m_core.get_nettype() == cryptonote::MAINNET &&
curr_height >= HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT &&
vote.block_height < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
// NOTE: After the soft fork, ignore all votes from before the fork so we
// only create and enforce checkpoints from after the soft-fork.
return true;
}
uint64_t const latest_height = std::max(m_core.get_current_blockchain_height(), m_core.get_target_blockchain_height());
if (!verify_vote_age(vote, latest_height, vvc))
return false;

View File

@ -93,7 +93,7 @@ namespace service_nodes
private:
void process_quorums(cryptonote::block const &block);
service_node_test_results check_service_node(const crypto::public_key &pubkey, const service_node_info &info) const;
service_node_test_results check_service_node(uint8_t hf_version, const crypto::public_key &pubkey, const service_node_info &info) const;
cryptonote::core& m_core;
voting_pool m_vote_pool;

View File

@ -204,7 +204,7 @@ namespace service_nodes
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 (hf_version >= cryptonote::network_version_13_enforce_checkpoints) // NOTE: After HF13, votes must be stored in ascending order
{
if (validator_index_tracker >= vote.validator_index)
{

View File

@ -1189,7 +1189,7 @@ namespace cryptonote
// Once we hard fork to v13 we can just use the blk major version
// whole sale.
uint8_t enforce_hf_version = std::max((uint8_t)cryptonote::network_version_13, blk.major_version);
uint8_t enforce_hf_version = std::max((uint8_t)cryptonote::network_version_13_enforce_checkpoints, blk.major_version);
if (service_node_array.empty() ||
!service_node_array[0].info->can_transition_to_state(enforce_hf_version, state_change.block_height, state_change.state))