mirror of https://github.com/oxen-io/oxen-core.git
Merge branch 'dev' into AlternativeQuorums
This commit is contained in:
commit
50228e2a0e
|
@ -130,12 +130,21 @@ struct tx_data_t
|
|||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct alt_block_data_1_t
|
||||
{
|
||||
uint64_t height;
|
||||
uint64_t cumulative_weight;
|
||||
uint64_t cumulative_difficulty;
|
||||
uint64_t already_generated_coins;
|
||||
};
|
||||
|
||||
struct alt_block_data_t
|
||||
{
|
||||
uint64_t height;
|
||||
uint64_t cumulative_weight;
|
||||
uint64_t cumulative_difficulty;
|
||||
uint64_t already_generated_coins;
|
||||
uint8_t checkpointed;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,8 +58,13 @@
|
|||
using epee::string_tools::pod_to_hex;
|
||||
using namespace crypto;
|
||||
|
||||
// Increase when the DB structure changes
|
||||
#define VERSION 4
|
||||
enum struct lmdb_version
|
||||
{
|
||||
v4 = 4,
|
||||
v5, // alt_block_data_1_t => alt_block_data_t: Alt block data has boolean for if the block was checkpointed
|
||||
};
|
||||
|
||||
constexpr lmdb_version VERSION = lmdb_version::v5; // Increase when the DB structure changes
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -1457,58 +1462,44 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
|||
LOG_PRINT_L2("Setting m_height to: " << db_stats.ms_entries);
|
||||
uint64_t m_height = db_stats.ms_entries;
|
||||
|
||||
bool compatible = true;
|
||||
|
||||
MDB_val_str(k, "version");
|
||||
MDB_val v;
|
||||
auto get_result = mdb_get(txn, m_properties, &k, &v);
|
||||
if(get_result == MDB_SUCCESS)
|
||||
{
|
||||
const uint32_t db_version = *(const uint32_t*)v.mv_data;
|
||||
if (db_version > VERSION)
|
||||
const uint32_t db_version = *(const uint32_t *)v.mv_data;
|
||||
bool failed = false;
|
||||
if (db_version > static_cast<decltype(db_version)>(VERSION))
|
||||
{
|
||||
MWARNING("Existing lmdb database was made by a later version (" << db_version << "). We don't know how it will change yet.");
|
||||
compatible = false;
|
||||
MFATAL("Existing lmdb database is incompatible with this version.");
|
||||
MFATAL("Please delete the existing database and resync.");
|
||||
failed = true;
|
||||
}
|
||||
#if VERSION > 0
|
||||
else if (db_version < VERSION)
|
||||
else if (db_version < static_cast<decltype(db_version)>(VERSION))
|
||||
{
|
||||
if (mdb_flags & MDB_RDONLY)
|
||||
{
|
||||
txn.abort();
|
||||
mdb_env_close(m_env);
|
||||
m_open = false;
|
||||
MFATAL("Existing lmdb database needs to be converted, which cannot be done on a read-only database.");
|
||||
MFATAL("Please run lokid once to convert the database.");
|
||||
failed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
txn.commit();
|
||||
m_open = true;
|
||||
migrate(db_version);
|
||||
return;
|
||||
}
|
||||
// Note that there was a schema change within version 0 as well.
|
||||
// See commit e5d2680094ee15889934fe28901e4e133cda56f2 2015/07/10
|
||||
// We don't handle the old format previous to that commit.
|
||||
txn.commit();
|
||||
m_open = true;
|
||||
migrate(db_version);
|
||||
}
|
||||
|
||||
if (failed)
|
||||
{
|
||||
txn.abort();
|
||||
mdb_env_close(m_env);
|
||||
m_open = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not found, and the DB is non-empty, this is probably
|
||||
// an "old" version 0, which we don't handle. If the DB is
|
||||
// empty it's fine.
|
||||
if (VERSION > 0 && m_height > 0)
|
||||
compatible = false;
|
||||
}
|
||||
|
||||
if (!compatible)
|
||||
{
|
||||
txn.abort();
|
||||
mdb_env_close(m_env);
|
||||
m_open = false;
|
||||
MFATAL("Existing lmdb database is incompatible with this version.");
|
||||
MFATAL("Please delete the existing database and resync.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(mdb_flags & MDB_RDONLY))
|
||||
|
@ -1517,7 +1508,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
|||
if (m_height == 0)
|
||||
{
|
||||
MDB_val_str(k, "version");
|
||||
MDB_val_copy<uint32_t> v(VERSION);
|
||||
MDB_val_copy<uint32_t> v(static_cast<uint32_t>(VERSION));
|
||||
auto put_result = mdb_put(txn, m_properties, &k, &v, 0);
|
||||
if (put_result != MDB_SUCCESS)
|
||||
{
|
||||
|
@ -1532,7 +1523,6 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
|||
|
||||
// commit the transaction
|
||||
txn.commit();
|
||||
|
||||
m_open = true;
|
||||
// from here, init should be finished
|
||||
}
|
||||
|
@ -1622,7 +1612,7 @@ void BlockchainLMDB::reset()
|
|||
|
||||
// init with current version
|
||||
MDB_val_str(k, "version");
|
||||
MDB_val_copy<uint32_t> v(VERSION);
|
||||
MDB_val_copy<uint32_t> v(static_cast<uint32_t>(VERSION));
|
||||
if (auto result = mdb_put(txn, m_properties, &k, &v, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str()));
|
||||
|
||||
|
@ -5739,6 +5729,74 @@ void BlockchainLMDB::migrate_3_4()
|
|||
txn.commit();
|
||||
}
|
||||
|
||||
void BlockchainLMDB::migrate_4_5()
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
MGINFO_YELLOW("Migrating blockchain from DB version 4 to 5 - this may take a while:");
|
||||
|
||||
mdb_txn_safe txn(false);
|
||||
{
|
||||
int result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result) throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
}
|
||||
|
||||
if (auto res = mdb_dbi_open(txn, LMDB_ALT_BLOCKS, 0, &m_alt_blocks)) return;
|
||||
|
||||
MDB_cursor *cursor;
|
||||
if (auto ret = mdb_cursor_open(txn, m_alt_blocks, &cursor))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for alt blocks: ", ret).c_str()));
|
||||
|
||||
struct entry_t
|
||||
{
|
||||
crypto::hash key;
|
||||
alt_block_data_t data;
|
||||
cryptonote::blobdata blob;
|
||||
};
|
||||
|
||||
std::vector<entry_t> new_entries;
|
||||
for (MDB_cursor_op op = MDB_FIRST;; op = MDB_NEXT)
|
||||
{
|
||||
MDB_val key, val;
|
||||
int ret = mdb_cursor_get(cursor, &key, &val, op);
|
||||
if (ret == MDB_NOTFOUND) break;
|
||||
if (ret) throw0(DB_ERROR(lmdb_error("Failed to enumerate alt blocks: ", ret).c_str()));
|
||||
|
||||
entry_t entry = {};
|
||||
|
||||
if (val.mv_size < sizeof(alt_block_data_1_t)) throw0(DB_ERROR("Record size is less than expected"));
|
||||
const auto *data = (const alt_block_data_1_t *)val.mv_data;
|
||||
entry.blob.assign((const char *)(data + 1), val.mv_size - sizeof(*data));
|
||||
|
||||
entry.key = *(crypto::hash const *)key.mv_data;
|
||||
entry.data.height = data->height;
|
||||
entry.data.cumulative_weight = data->cumulative_weight;
|
||||
entry.data.cumulative_difficulty = data->cumulative_difficulty;
|
||||
entry.data.already_generated_coins = data->already_generated_coins;
|
||||
new_entries.push_back(entry);
|
||||
}
|
||||
|
||||
{
|
||||
int ret = mdb_drop(txn, m_alt_blocks, 0 /*empty the db but don't delete handle*/);
|
||||
if (ret && ret != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_alt_blocks: ", ret).c_str()));
|
||||
}
|
||||
|
||||
for (entry_t const &entry : new_entries)
|
||||
{
|
||||
const size_t val_size = sizeof(entry.data) + entry.blob.size();
|
||||
std::unique_ptr<char[]> val_buf(new char[val_size]);
|
||||
|
||||
memcpy(val_buf.get(), &entry.data, sizeof(entry.data));
|
||||
memcpy(val_buf.get() + sizeof(entry.data), entry.blob.data(), entry.blob.size());
|
||||
|
||||
MDB_val_set(key, entry.key);
|
||||
MDB_val val = {val_size, (void *)val_buf.get()};
|
||||
int ret = mdb_cursor_put(cursor, &key, &val, 0);
|
||||
if (ret) throw0(DB_ERROR(lmdb_error("Failed to re-update alt block data: ", ret).c_str()));
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
void BlockchainLMDB::migrate(const uint32_t oldversion)
|
||||
{
|
||||
switch(oldversion) {
|
||||
|
@ -5750,6 +5808,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion)
|
|||
migrate_2_3(); /* FALLTHRU */
|
||||
case 3:
|
||||
migrate_3_4(); /* FALLTHRU */
|
||||
case 4:
|
||||
migrate_4_5(); /* FALLTHRU */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -439,17 +439,11 @@ private:
|
|||
// migrate from older DB version to current
|
||||
void migrate(const uint32_t oldversion);
|
||||
|
||||
// migrate from DB version 0 to 1
|
||||
void migrate_0_1();
|
||||
|
||||
// migrate from DB version 1 to 2
|
||||
void migrate_1_2();
|
||||
|
||||
// migrate from DB version 2 to 3
|
||||
void migrate_2_3();
|
||||
|
||||
// migrate from DB version 3 to 4
|
||||
void migrate_3_4();
|
||||
void migrate_4_5();
|
||||
|
||||
void cleanup_batch();
|
||||
|
||||
|
|
|
@ -170,39 +170,9 @@ namespace cryptonote
|
|||
bool checkpoints::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs, checkpoint_t const *checkpoint)
|
||||
{
|
||||
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 true;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkpoint)
|
||||
update_checkpoint(*checkpoint);
|
||||
|
||||
uint64_t end_cull_height = 0;
|
||||
{
|
||||
checkpoint_t immutable_checkpoint;
|
||||
|
@ -233,6 +203,9 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
|
||||
if (checkpoint)
|
||||
update_checkpoint(*checkpoint);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -271,26 +244,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;
|
||||
|
@ -306,12 +280,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;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
|
@ -172,11 +172,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
|
||||
|
@ -189,7 +189,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
|
||||
|
@ -209,7 +209,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;
|
||||
};
|
||||
|
||||
|
|
|
@ -69,7 +69,6 @@ void hash_extra_skein(const void *data, size_t length, char *hash);
|
|||
|
||||
void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash);
|
||||
|
||||
#define RX_BLOCK_VERSION 12
|
||||
void rx_slow_hash_allocate_state(void);
|
||||
void rx_slow_hash_free_state(void);
|
||||
uint64_t rx_seedheight(const uint64_t height);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -1037,7 +1037,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
|
|||
//------------------------------------------------------------------
|
||||
// This function attempts to switch to an alternate chain, returning
|
||||
// boolean based on success therein.
|
||||
bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain)
|
||||
bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool keep_disconnected_chain)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
@ -1120,16 +1120,18 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
|
|||
}
|
||||
}
|
||||
|
||||
//pushing old chain as alternative chain
|
||||
for (auto& old_ch_ent : disconnected_chain)
|
||||
if (keep_disconnected_chain) //pushing old chain as alternative chain
|
||||
{
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc, nullptr /*checkpoint*/);
|
||||
if(!r)
|
||||
for (auto &old_ch_ent : disconnected_chain)
|
||||
{
|
||||
MERROR("Failed to push ex-main chain blocks to alternative chain ");
|
||||
// previously this would fail the blockchain switching, but I don't
|
||||
// think this is bad enough to warrant that.
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc, nullptr /*checkpoint*/);
|
||||
if (!r)
|
||||
{
|
||||
MERROR("Failed to push ex-main chain blocks to alternative chain ");
|
||||
// previously this would fail the blockchain switching, but I don't
|
||||
// think this is bad enough to warrant that.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1491,7 +1493,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
|||
std::list<block_extended_info> alt_chain;
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
std::vector<uint64_t> timestamps;
|
||||
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
|
||||
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc, nullptr))
|
||||
return false;
|
||||
|
||||
if (parent_in_main)
|
||||
|
@ -1711,24 +1713,31 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const
|
||||
bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc, int *num_checkpoints) const
|
||||
{
|
||||
//build alternative subchain, front -> mainchain, back -> alternative head
|
||||
cryptonote::alt_block_data_t data;
|
||||
cryptonote::blobdata blob;
|
||||
bool found = m_db->get_alt_block(prev_id, &data, &blob);
|
||||
timestamps.clear();
|
||||
while(found)
|
||||
|
||||
if (num_checkpoints) *num_checkpoints = 0;
|
||||
crypto::hash prev_hash = crypto::null_hash;
|
||||
block_extended_info bei = {};
|
||||
for(bool found = m_db->get_alt_block(prev_id, &data, &blob);
|
||||
found;
|
||||
found = m_db->get_alt_block(prev_hash, &data, &blob))
|
||||
{
|
||||
block_extended_info bei;
|
||||
if (num_checkpoints && data.checkpointed) (*num_checkpoints)++;
|
||||
CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_block_from_blob(blob, bei.bl), false, "Failed to parse alt block");
|
||||
bei.height = data.height;
|
||||
bei.height = data.height;
|
||||
bei.block_cumulative_weight = data.cumulative_weight;
|
||||
bei.cumulative_difficulty = data.cumulative_difficulty;
|
||||
bei.cumulative_difficulty = data.cumulative_difficulty;
|
||||
bei.already_generated_coins = data.already_generated_coins;
|
||||
|
||||
prev_hash = bei.bl.prev_id;
|
||||
timestamps.push_back(bei.bl.timestamp);
|
||||
alt_chain.push_front(std::move(bei));
|
||||
found = m_db->get_alt_block(bei.bl.prev_id, &data, &blob);
|
||||
bei = {};
|
||||
}
|
||||
|
||||
// if block to be added connects to known blocks that aren't part of the
|
||||
|
@ -1784,20 +1793,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1811,6 +1814,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
|
||||
//block is not related with head of main chain
|
||||
//first of all - look in alternative chains container
|
||||
uint64_t const curr_blockchain_height = get_current_blockchain_height();
|
||||
alt_block_data_t prev_data;
|
||||
bool parent_in_alt = m_db->get_alt_block(b.prev_id, &prev_data, NULL);
|
||||
bool parent_in_main = m_db->block_exists(b.prev_id);
|
||||
|
@ -1819,16 +1823,16 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
//we have new block in alternative chain
|
||||
std::list<block_extended_info> alt_chain;
|
||||
std::vector<uint64_t> timestamps;
|
||||
if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
|
||||
int num_checkpoints_on_alt_chain = 0;
|
||||
if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc, &num_checkpoints_on_alt_chain))
|
||||
return false;
|
||||
|
||||
// FIXME: consider moving away from block_extended_info at some point
|
||||
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
||||
bei.bl = b;
|
||||
const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id);
|
||||
bei.height = prev_height + 1;
|
||||
uint64_t block_reward = get_outs_money_amount(b.miner_tx);
|
||||
bei.already_generated_coins = block_reward + (alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
|
||||
bei.bl = b;
|
||||
bei.height = cryptonote::get_block_height(b);
|
||||
uint64_t block_reward = get_outs_money_amount(b.miner_tx);
|
||||
bei.already_generated_coins = block_reward + (alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(bei.height - 1));
|
||||
|
||||
// verify that the block's timestamp is within the acceptable range
|
||||
// (not earlier than the median of the last X blocks)
|
||||
|
@ -1843,7 +1847,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
|
||||
CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
|
||||
crypto::hash proof_of_work = null_hash;
|
||||
if (b.major_version >= RX_BLOCK_VERSION)
|
||||
if (b.major_version >= cryptonote::network_version_12_checkpointing)
|
||||
{
|
||||
crypto::hash seedhash = null_hash;
|
||||
uint64_t seedheight = rx_seedheight(bei.height);
|
||||
|
@ -1862,7 +1866,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
{
|
||||
seedhash = get_block_id_by_height(seedheight);
|
||||
}
|
||||
get_altblock_longhash(bei.bl, proof_of_work, get_current_blockchain_height(), bei.height, seedheight, seedhash);
|
||||
get_altblock_longhash(bei.bl, proof_of_work, curr_blockchain_height, bei.height, seedheight, seedhash);
|
||||
} else
|
||||
{
|
||||
get_block_longhash(this, bei.bl, proof_of_work, bei.height, 0);
|
||||
|
@ -1896,22 +1900,40 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
}
|
||||
bei.cumulative_difficulty += current_diff;
|
||||
|
||||
bool rejected_by_service_node = false;
|
||||
bool is_a_checkpoint = false;
|
||||
if(!checkpoint && !m_checkpoints.check_block(bei.height, id, &is_a_checkpoint, &rejected_by_service_node))
|
||||
// add block to alternate blocks storage,
|
||||
// as well as the current "alt chain" container
|
||||
CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists");
|
||||
cryptonote::alt_block_data_t data = {};
|
||||
data.height = bei.height;
|
||||
data.cumulative_weight = bei.block_cumulative_weight;
|
||||
data.cumulative_difficulty = bei.cumulative_difficulty;
|
||||
data.already_generated_coins = bei.already_generated_coins;
|
||||
data.checkpointed = (checkpoint != nullptr);
|
||||
m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl));
|
||||
alt_chain.push_back(bei);
|
||||
if (data.checkpointed) num_checkpoints_on_alt_chain++;
|
||||
|
||||
// NOTE: Block is within the allowable service node reorg window due to passing is_alternative_block_allowed().
|
||||
// So we don't need to check that this block matches the checkpoint unless it's a hardcoded checkpoint, in which
|
||||
// case it must. Otherwise if it fails a Service Node checkpoint that's fine because we're allowed to replace it in
|
||||
// this window
|
||||
bool service_node_checkpoint = false;
|
||||
if (!checkpoint && !m_checkpoints.check_block(bei.height, id, nullptr, &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;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t last_block_height = alt_chain.back().height;
|
||||
std::vector<checkpoint_t> const checkpoints = m_db->get_checkpoints_range(curr_blockchain_height, last_block_height, num_checkpoints_on_alt_chain + 1);
|
||||
bool alt_chain_has_greater_pow = bei.cumulative_difficulty > main_chain_cumulative_difficulty;
|
||||
bool alt_chain_has_more_checkpoints = (num_checkpoints_on_alt_chain > static_cast<int>(checkpoints.size()));
|
||||
bool alt_chain_has_equal_checkpoints = (num_checkpoints_on_alt_chain == static_cast<int>(checkpoints.size()));
|
||||
|
||||
{
|
||||
std::vector<transaction> txs;
|
||||
std::vector<crypto::hash> missed;
|
||||
|
@ -1928,50 +1950,48 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
}
|
||||
}
|
||||
|
||||
// add block to alternate blocks storage,
|
||||
// as well as the current "alt chain" container
|
||||
CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists");
|
||||
cryptonote::alt_block_data_t data;
|
||||
data.height = bei.height;
|
||||
data.cumulative_weight = bei.block_cumulative_weight;
|
||||
data.cumulative_difficulty = bei.cumulative_difficulty;
|
||||
data.already_generated_coins = bei.already_generated_coins;
|
||||
m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl));
|
||||
alt_chain.push_back(bei);
|
||||
|
||||
bool checkpointed = checkpoint || is_a_checkpoint;
|
||||
if (checkpointed || (main_chain_cumulative_difficulty < bei.cumulative_difficulty)) // check if difficulty bigger then in main chain
|
||||
if (b.major_version >= network_version_13_enforce_checkpoints)
|
||||
{
|
||||
if (checkpointed)
|
||||
if (alt_chain_has_more_checkpoints || (alt_chain_has_greater_pow && alt_chain_has_equal_checkpoints))
|
||||
{
|
||||
bool activate_branch = nettype() != MAINNET;
|
||||
if (nettype() == MAINNET && block_height > HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
|
||||
activate_branch = true;
|
||||
|
||||
if (activate_branch)
|
||||
if (alt_chain_has_more_checkpoints)
|
||||
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 << " 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);
|
||||
|
||||
bool keep_alt_chain = (alt_chain_has_greater_pow && alt_chain_has_equal_checkpoints);
|
||||
bool r = switch_to_alternative_blockchain(alt_chain, keep_alt_chain);
|
||||
if (r)
|
||||
bvc.m_added_to_main_chain = true;
|
||||
else
|
||||
bvc.m_verifivation_failed = true;
|
||||
return r;
|
||||
}
|
||||
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);
|
||||
|
||||
// NOTE: No longer discard chains, because we can reorg the last 2 Service Node checkpoints, so checkpointed chains aren't always final
|
||||
bool r = switch_to_alternative_blockchain(alt_chain);
|
||||
if (r)
|
||||
bvc.m_added_to_main_chain = true;
|
||||
else
|
||||
bvc.m_verifivation_failed = true;
|
||||
return r;
|
||||
{
|
||||
MGINFO_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "difficulty:\t" << current_diff);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MGINFO_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "difficulty:\t" << current_diff);
|
||||
return true;
|
||||
if (alt_chain_has_greater_pow)
|
||||
{
|
||||
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);
|
||||
bool r = switch_to_alternative_blockchain(alt_chain, true);
|
||||
if (r)
|
||||
bvc.m_added_to_main_chain = true;
|
||||
else
|
||||
bvc.m_verifivation_failed = true;
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
MGINFO_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "difficulty:\t" << current_diff);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1979,7 +1999,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
bvc.m_marked_as_orphaned = true;
|
||||
MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height
|
||||
<< ", parent in alt " << parent_in_alt << ", parent in main " << parent_in_main
|
||||
<< " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")");
|
||||
<< " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << curr_blockchain_height << ")");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -3804,11 +3824,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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4037,6 +4061,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())
|
||||
{
|
||||
|
@ -4321,11 +4357,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)
|
||||
{
|
||||
uint64_t blocks_to_pop = m_db->height() - checkpoint.height + 2;
|
||||
crypto::hash const reorg_hash = m_db->get_block_hash_from_height(checkpoint.height - 2);
|
||||
|
|
|
@ -1184,11 +1184,11 @@ namespace cryptonote
|
|||
* the blockchain is reverted to its previous state.
|
||||
*
|
||||
* @param alt_chain the chain to switch to
|
||||
* @param discard_disconnected_chain whether or not to keep the old chain as an alternate
|
||||
* @param keep_disconnected_chain whether or not to keep the old chain as an alternate
|
||||
*
|
||||
* @return false if the reorganization fails, otherwise true
|
||||
*/
|
||||
bool switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain);
|
||||
bool switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool keep_disconnected_chain);
|
||||
|
||||
/**
|
||||
* @brief removes the most recent block from the blockchain
|
||||
|
@ -1251,7 +1251,7 @@ namespace cryptonote
|
|||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const;
|
||||
bool build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc, int *num_checkpoints) const;
|
||||
|
||||
/**
|
||||
* @brief gets the difficulty requirement for a new block on an alternate chain
|
||||
|
|
|
@ -1583,17 +1583,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)
|
||||
{
|
||||
|
@ -1657,19 +1646,14 @@ 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)
|
||||
// TODO(loki): Temporary to make hf12 checkpoints play nicely, but, hf12 checkpoints will be deleted on hf13
|
||||
if (checkpoint && b->major_version < network_version_12_checkpointing)
|
||||
{
|
||||
if (b->major_version < network_version_13)
|
||||
{
|
||||
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;
|
||||
});
|
||||
}
|
||||
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);
|
||||
|
@ -2152,6 +2136,11 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value)
|
||||
{
|
||||
return m_service_node_list.set_storage_server_peer_reachable(pubkey, value);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::update_blockchain_pruning()
|
||||
{
|
||||
return m_blockchain_storage.update_blockchain_pruning();
|
||||
|
|
|
@ -887,6 +887,11 @@ namespace cryptonote
|
|||
*/
|
||||
void record_checkpoint_vote(crypto::public_key const &pubkey, bool voted) { m_service_node_list.record_checkpoint_vote(pubkey, voted); }
|
||||
|
||||
/**
|
||||
* @brief Record the reachability status of node's storage server
|
||||
*/
|
||||
bool set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value);
|
||||
|
||||
/// Time point at which the storage server last pinged us
|
||||
std::atomic<time_t> m_last_storage_server_ping;
|
||||
private:
|
||||
|
|
|
@ -545,7 +545,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
|
||||
|
@ -1995,6 +1995,26 @@ namespace service_nodes
|
|||
info.vote_index = (info.vote_index + 1) % info.votes.size();
|
||||
}
|
||||
|
||||
bool service_node_list::set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value)
|
||||
{
|
||||
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
|
||||
|
||||
auto it = m_state.service_nodes_infos.find(pubkey);
|
||||
if (it == m_state.service_nodes_infos.end()) {
|
||||
LOG_PRINT_L2("No Service Node is known by this pubkey: " << pubkey);
|
||||
return false;
|
||||
} else {
|
||||
|
||||
proof_info &info = *it->second->proof;
|
||||
if (info.storage_server_reachable != value) {
|
||||
info.storage_server_reachable = value;
|
||||
LOG_PRINT_L2("Setting reachability status for node " << pubkey << " as: " << (value ? "true" : "false"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static quorum_manager quorum_for_serialization_to_quorum_manager(service_node_list::quorum_for_serialization const &source)
|
||||
{
|
||||
quorum_manager result = {};
|
||||
|
@ -2482,7 +2502,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)
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace service_nodes
|
|||
std::array<bool, CHECKPOINT_MIN_QUORUMS_NODE_MUST_VOTE_IN_BEFORE_DEREGISTER_CHECK> votes;
|
||||
uint8_t vote_index = 0;
|
||||
std::array<std::pair<uint32_t, uint64_t>, 2> public_ips = {}; // (not serialized)
|
||||
bool storage_server_reachable = true;
|
||||
proof_info() { votes.fill(true); }
|
||||
|
||||
// Called to update both actual and effective timestamp, i.e. when a proof is received
|
||||
|
@ -282,6 +283,8 @@ namespace service_nodes
|
|||
bool handle_uptime_proof (cryptonote::NOTIFY_UPTIME_PROOF::request const &proof);
|
||||
void record_checkpoint_vote (crypto::public_key const &pubkey, bool voted);
|
||||
|
||||
bool set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value);
|
||||
|
||||
struct quorum_for_serialization
|
||||
{
|
||||
uint8_t version;
|
||||
|
|
|
@ -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,6 +82,13 @@ namespace service_nodes
|
|||
result.uptime_proved = 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
|
||||
const auto &ips = proof.public_ips;
|
||||
if (ips[0].first && ips[1].first) {
|
||||
|
@ -109,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,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))
|
||||
{
|
||||
|
@ -266,22 +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;
|
||||
}
|
||||
|
||||
new_state vote_for_state;
|
||||
if (passed) {
|
||||
if (info.is_decommissioned()) {
|
||||
|
@ -387,14 +383,7 @@ namespace service_nodes
|
|||
bool quorum_cop::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs, cryptonote::checkpoint_t const * /*checkpoint*/)
|
||||
{
|
||||
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);
|
||||
return true;
|
||||
|
@ -404,15 +393,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;
|
||||
|
|
|
@ -75,11 +75,12 @@ namespace service_nodes
|
|||
};
|
||||
|
||||
struct service_node_test_results {
|
||||
bool uptime_proved = true;
|
||||
bool single_ip = true;
|
||||
bool voted_in_checkpoints = true;
|
||||
bool uptime_proved = true;
|
||||
bool single_ip = true;
|
||||
bool voted_in_checkpoints = true;
|
||||
bool storage_server_reachable = true;
|
||||
|
||||
bool passed() const { return uptime_proved && voted_in_checkpoints; }
|
||||
bool passed() const { return uptime_proved && voted_in_checkpoints && storage_server_reachable; }
|
||||
};
|
||||
|
||||
class quorum_cop
|
||||
|
@ -102,7 +103,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;
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace service_nodes {
|
|||
// blocks out of sync and sending something that it thinks is legit.
|
||||
constexpr uint64_t VOTE_OR_TX_VERIFY_HEIGHT_BUFFER = 5;
|
||||
|
||||
constexpr std::array<int, 3> MIN_STORAGE_SERVER_VERSION = {1, 0, 5};
|
||||
constexpr std::array<int, 3> MIN_STORAGE_SERVER_VERSION = {1, 0, 6};
|
||||
|
||||
using swarm_id_t = uint64_t;
|
||||
constexpr swarm_id_t UNASSIGNED_SWARM_ID = UINT64_MAX;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -264,7 +264,7 @@ namespace service_nodes
|
|||
for (size_t i = 0; i < checkpoint.signatures.size(); i++)
|
||||
{
|
||||
service_nodes::voter_to_signature const &voter_to_signature = checkpoint.signatures[i];
|
||||
if (hf_version >= cryptonote::network_version_13 && i < (checkpoint.signatures.size() - 1))
|
||||
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints && i < (checkpoint.signatures.size() - 1))
|
||||
{
|
||||
auto curr = checkpoint.signatures[i].voter_index;
|
||||
auto next = checkpoint.signatures[i + 1].voter_index;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -210,7 +210,16 @@ namespace cryptonote
|
|||
res.stagenet = nettype == STAGENET;
|
||||
res.nettype = nettype == MAINNET ? "mainnet" : nettype == TESTNET ? "testnet" : nettype == STAGENET ? "stagenet" : "fakechain";
|
||||
|
||||
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
|
||||
try
|
||||
{
|
||||
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
|
||||
}
|
||||
catch(std::exception const &e)
|
||||
{
|
||||
res.status = "Error retrieving cumulative difficulty at height " + std::to_string(res.height - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
|
||||
res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
|
||||
res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time();
|
||||
|
@ -2740,6 +2749,7 @@ namespace cryptonote
|
|||
entry.service_node_version = {info.proof->version_major, info.proof->version_minor, info.proof->version_patch};
|
||||
entry.public_ip = string_tools::get_ip_string_from_int32(info.proof->public_ip);
|
||||
entry.storage_port = info.proof->storage_port;
|
||||
entry.storage_server_reachable = info.proof->storage_server_reachable;
|
||||
|
||||
entry.contributors.reserve(info.contributors.size());
|
||||
|
||||
|
@ -3142,6 +3152,35 @@ namespace cryptonote
|
|||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_report_peer_storage_server_status(const COMMAND_RPC_REPORT_PEER_SS_STATUS::request& req,
|
||||
COMMAND_RPC_REPORT_PEER_SS_STATUS::response& res,
|
||||
epee::json_rpc::error& error_resp,
|
||||
const connection_context* ctx)
|
||||
{
|
||||
crypto::public_key pubkey;
|
||||
if (!string_tools::hex_to_pod(req.pubkey, pubkey)) {
|
||||
MERROR("Could not parse public key: " << req.pubkey);
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Could not parse public key";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.type == "reachability") {
|
||||
|
||||
if (!m_core.set_storage_server_peer_reachable(pubkey, req.passed)) {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Pubkey not found";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Unknown status type";
|
||||
return false;
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -184,10 +184,11 @@ namespace cryptonote
|
|||
MAP_JON_RPC_WE("get_all_service_nodes_keys", on_get_all_service_nodes_keys, COMMAND_RPC_GET_ALL_SERVICE_NODES_KEYS)
|
||||
MAP_JON_RPC_WE("get_n_service_nodes", on_get_n_service_nodes, COMMAND_RPC_GET_N_SERVICE_NODES)
|
||||
MAP_JON_RPC_WE("get_staking_requirement", on_get_staking_requirement, COMMAND_RPC_GET_STAKING_REQUIREMENT)
|
||||
MAP_JON_RPC_WE("get_checkpoints", on_get_checkpoints, COMMAND_RPC_GET_CHECKPOINTS)
|
||||
MAP_JON_RPC_WE("get_checkpoints", on_get_checkpoints, COMMAND_RPC_GET_CHECKPOINTS)
|
||||
MAP_JON_RPC_WE_IF("perform_blockchain_test", on_perform_blockchain_test, COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST, !m_restricted)
|
||||
MAP_JON_RPC_WE_IF("storage_server_ping", on_storage_server_ping, COMMAND_RPC_STORAGE_SERVER_PING, !m_restricted)
|
||||
MAP_JON_RPC_WE("get_service_nodes_state_changes", on_get_service_nodes_state_changes, COMMAND_RPC_GET_SN_STATE_CHANGES)
|
||||
MAP_JON_RPC_WE_IF("report_peer_storage_server_status", on_report_peer_storage_server_status, COMMAND_RPC_REPORT_PEER_SS_STATUS, !m_restricted)
|
||||
END_JSON_RPC_MAP()
|
||||
END_URI_MAP2()
|
||||
|
||||
|
@ -277,6 +278,7 @@ namespace cryptonote
|
|||
bool on_storage_server_ping(const COMMAND_RPC_STORAGE_SERVER_PING::request& req, COMMAND_RPC_STORAGE_SERVER_PING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_checkpoints(const COMMAND_RPC_GET_CHECKPOINTS::request& req, COMMAND_RPC_GET_CHECKPOINTS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_service_nodes_state_changes(const COMMAND_RPC_GET_SN_STATE_CHANGES::request& req, COMMAND_RPC_GET_SN_STATE_CHANGES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_report_peer_storage_server_status(const COMMAND_RPC_REPORT_PEER_SS_STATUS::request& req, COMMAND_RPC_REPORT_PEER_SS_STATUS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
//-----------------------
|
||||
|
||||
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
|
||||
|
|
|
@ -2763,6 +2763,7 @@ namespace cryptonote
|
|||
std::string operator_address; // The wallet address of the operator to which the operator cut of the staking reward is sent to.
|
||||
std::string public_ip; // The public ip address of the service node
|
||||
uint16_t storage_port; // The port number associated with the storage server
|
||||
bool storage_server_reachable; // Whether the node's storage server has been reported as unreachable for a long time
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(service_node_pubkey)
|
||||
|
@ -2786,6 +2787,7 @@ namespace cryptonote
|
|||
KV_SERIALIZE(operator_address)
|
||||
KV_SERIALIZE(public_ip)
|
||||
KV_SERIALIZE(storage_port)
|
||||
KV_SERIALIZE(storage_server_reachable)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
@ -2843,6 +2845,7 @@ namespace cryptonote
|
|||
bool operator_address;
|
||||
bool public_ip;
|
||||
bool storage_port;
|
||||
bool storage_server_reachable;
|
||||
|
||||
bool block_hash;
|
||||
bool height;
|
||||
|
@ -2871,6 +2874,7 @@ namespace cryptonote
|
|||
KV_SERIALIZE_OPT2(operator_address, false)
|
||||
KV_SERIALIZE_OPT2(public_ip, false)
|
||||
KV_SERIALIZE_OPT2(storage_port, false)
|
||||
KV_SERIALIZE_OPT2(storage_server_reachable, false)
|
||||
KV_SERIALIZE_OPT2(block_hash, false)
|
||||
KV_SERIALIZE_OPT2(height, false)
|
||||
KV_SERIALIZE_OPT2(target_height, false)
|
||||
|
@ -2923,6 +2927,7 @@ namespace cryptonote
|
|||
std::string operator_address; // The wallet address of the operator to which the operator cut of the staking reward is sent to.
|
||||
std::string public_ip; // The public ip address of the service node
|
||||
uint16_t storage_port; // The port number associated with the storage server
|
||||
bool storage_server_reachable; // Whether the node's storage server has been reported as unreachable for a long time
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(service_node_pubkey);
|
||||
|
@ -2946,6 +2951,7 @@ namespace cryptonote
|
|||
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(operator_address);
|
||||
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(public_ip);
|
||||
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_port);
|
||||
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_reachable);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
@ -3233,4 +3239,30 @@ namespace cryptonote
|
|||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
|
||||
LOKI_RPC_DOC_INTROSPECT
|
||||
struct COMMAND_RPC_REPORT_PEER_SS_STATUS
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::string type; // test type (currently used: ["reachability"])
|
||||
std::string pubkey; // service node pubkey
|
||||
bool passed; // whether the node is passing the test
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(type)
|
||||
KV_SERIALIZE(pubkey)
|
||||
KV_SERIALIZE(passed)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::string status; // Generic RPC error code. "OK" is the success value.
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(status)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue