Merge branch 'dev' into AlternativeQuorums

This commit is contained in:
Doyle 2019-09-10 12:31:03 +10:00
commit 50228e2a0e
21 changed files with 398 additions and 259 deletions

View File

@ -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;
};
/**

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}
//---------------------------------------------------------------------------

View File

@ -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;
};

View File

@ -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);

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

@ -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> &timestamps, 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> &timestamps, 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);

View File

@ -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> &timestamps, 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> &timestamps, block_verification_context& bvc, int *num_checkpoints) const;
/**
* @brief gets the difficulty requirement for a new block on an alternate chain

View File

@ -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();

View File

@ -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:

View File

@ -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)

View File

@ -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;

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,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;

View File

@ -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;

View File

@ -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;

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)
{
@ -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;

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))

View File

@ -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

View File

@ -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)

View File

@ -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()
};
};
}