Pulse: Setup the mainnet to work around difficulty

- Pulse blocks will forcibly get the difficulty set to
  1'000'000 * TARGET_BLOCK_TIME throughout time
- When PoW is required again, the past window of blocks will use these
  difficulties, i.e. setup the chain for mining at 1'000'000 difficulty
  which is easily mineable to continue the network and continue to pull
  difficulties from the new-er mined blocks until the network is ready
  for Pulse again.
- Difficulty is still necessary for falling back to mining when Pulse
  fails. Switching between the two systems seamlessly can be done by
  continuing to set the difficulty for Pulse blocks.
This commit is contained in:
Doyle 2020-08-31 17:45:33 +10:00 committed by Jason Rhinelander
parent 9bec861f4d
commit 79ae34e361
13 changed files with 289 additions and 152 deletions

View File

@ -415,4 +415,54 @@ uint64_t BlockchainDB::get_tx_block_height(const crypto::hash &h) const
return result;
}
void BlockchainDB::fill_timestamps_and_difficulties_for_pow(cryptonote::network_type nettype,
std::vector<uint64_t> &timestamps,
std::vector<uint64_t> &difficulties,
uint64_t chain_height,
uint64_t timestamps_difficulty_height) const
{
constexpr uint64_t MIN_HEIGHT = 2;
if (chain_height < MIN_HEIGHT)
return;
const uint64_t top_block_height = chain_height - 1;
static const uint64_t hf16_height = HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_16);
bool const before_hf16 = top_block_height < hf16_height;
uint64_t const block_count = DIFFICULTY_BLOCKS_COUNT(before_hf16);
timestamps.reserve(block_count);
difficulties.reserve(block_count);
if (timestamps_difficulty_height == 0 ||
(chain_height - timestamps_difficulty_height) != 1 ||
timestamps.size() != block_count ||
difficulties.size() != block_count)
{
// Cache invalidated.
timestamps.clear();
difficulties.clear();
// Fill missing timestamps/difficulties, up to one before the latest (latest is added below).
uint64_t start_height = chain_height - std::min<size_t>(chain_height, block_count);
start_height = std::max<uint64_t>(start_height, MIN_HEIGHT);
for (uint64_t block_height = start_height; block_height < (chain_height - 1); block_height++)
{
timestamps.push_back(get_block_timestamp(block_height));
difficulties.push_back(get_block_cumulative_difficulty(block_height));
}
}
// Add latest timestamp/difficulty
timestamps.push_back(get_block_timestamp(top_block_height));
difficulties.push_back(get_block_cumulative_difficulty(top_block_height));
// Trim down arrays
while (timestamps.size() > block_count)
timestamps.erase(timestamps.begin());
while (difficulties.size() > block_count)
difficulties.erase(difficulties.begin());
}
} // namespace cryptonote

View File

@ -1817,9 +1817,9 @@ public:
*/
struct fixup_context
{
cryptonote::network_type nettype;
struct
{
uint64_t hf12_height;
uint64_t start_height;
} recalc_diff;
};
@ -1845,6 +1845,33 @@ public:
/// found, false if not found.
virtual bool remove_service_node_proof(const crypto::public_key &pubkey) = 0;
// This function accepts an empty timestamps/difficulties array to fill, or
// a prior timestamps/difficulties array that was filled by a previous call to
// this same function in which case it will optimally insert and remove the
// new data instead of reconstructing the entrie array.
//
// timestamps: On return, timestamps of the last loaded
// 'DIFFICULTY_WINDOW' blocks starting from top_block_height stored in
// ascending block height order.
//
// difficulties: On return, cumulative difficulties of the last loaded
// DIFFICULTY_WINDOW blocks starting from 'top_block_height' stored in
// ascending block height order
//
// chain_height: The blockchain height, (next height a block will be added
// at). The input arrays will be filled with the prior `timestamps` and
// `difficulties` DIFFICULTY_WINDOW worth of values.
//
// timestamps_difficulty_height: The last 'chain_height' that this function
// was invoked and loaded historical timestamp/difficulties into (allowing
// this function to be called iteratively on the same input arrays over time).
// This should be set to 0 on the initial call.
void fill_timestamps_and_difficulties_for_pow(cryptonote::network_type nettype,
std::vector<uint64_t> &timestamps,
std::vector<uint64_t> &difficulties,
uint64_t chain_height,
uint64_t timestamps_difficulty_height) const;
/**
* @brief set whether or not to automatically remove logs
*

View File

@ -394,6 +394,11 @@ struct mdb_block_info_2 : mdb_block_info_1
uint64_t bi_cum_rct;
};
struct mdb_block_info_3 : mdb_block_info_2
{
uint8_t bi_pulse;
};
struct mdb_block_info : mdb_block_info_2
{
uint64_t bi_long_term_block_weight;
@ -2544,11 +2549,18 @@ uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const
return ret;
}
block_header BlockchainLMDB::get_block_header_by_height(uint64_t height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
// block_header object is automatically cast from block object
return get_block_from_height(height);
}
block_header BlockchainLMDB::get_block_header(const crypto::hash& h) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
// block_header object is automatically cast from block object
return get_block(h);
}
@ -4736,23 +4748,11 @@ void BlockchainLMDB::fixup(fixup_context const context)
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
{
uint64_t offset = start_height - std::min<size_t>(start_height, static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
if (offset == 0)
offset = 1;
if (start_height > offset)
{
timestamps.reserve (start_height - offset);
difficulties.reserve(start_height - offset);
}
for (; offset < start_height; offset++)
{
timestamps.push_back (get_block_timestamp(offset));
difficulties.push_back(get_block_cumulative_difficulty(offset));
}
}
BlockchainDB::fill_timestamps_and_difficulties_for_pow(context.nettype,
timestamps,
difficulties,
context.recalc_diff.start_height + 1 /*chain_height*/,
0 /*timestamps_difficulty_height*/);
try
{
@ -4772,12 +4772,19 @@ void BlockchainLMDB::fixup(fixup_context const context)
for (uint64_t block_index = 0; block_index < blocks_in_batch; block_index++)
{
uint64_t const curr_height = (start_height + (batch_index * BLOCKS_PER_BATCH) + block_index);
uint8_t version = get_hard_fork_version(curr_height);
difficulty_type diff =
next_difficulty_v2(timestamps,
difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(version, curr_height, context.recalc_diff.hf12_height));
difficulty_type diff = {};
block_header header = get_block_header_by_height(curr_height);
if (block_header_has_pulse_components(header))
{
diff = PULSE_FIXED_DIFFICULTY;
}
else
{
diff = next_difficulty_v2(timestamps,
difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(context.nettype, header.major_version, curr_height));
}
MDB_val_set(key, curr_height);
@ -4810,8 +4817,10 @@ void BlockchainLMDB::fixup(fixup_context const context)
return;
}
while (timestamps.size() > DIFFICULTY_BLOCKS_COUNT) timestamps.erase(timestamps.begin());
while (difficulties.size() > DIFFICULTY_BLOCKS_COUNT) difficulties.erase(difficulties.begin());
static const uint64_t hf16_height = HardFork::get_hardcoded_hard_fork_height(context.nettype, cryptonote::network_version_16);
bool before_hf16 = curr_height < hf16_height;
while (timestamps.size() > DIFFICULTY_BLOCKS_COUNT(before_hf16)) timestamps.erase(timestamps.begin());
while (difficulties.size() > DIFFICULTY_BLOCKS_COUNT(before_hf16)) difficulties.erase(difficulties.begin());
}
block_wtxn_stop();
@ -5954,10 +5963,9 @@ void BlockchainLMDB::migrate_4_5(cryptonote::network_type nettype)
txn.commit();
// NOTE: Rescan chain difficulty to mitigate difficulty problem pre hardfork v12
uint64_t hf12_height = HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_12_checkpointing);
fixup_context context = {};
context.recalc_diff.start_height = hf12_height;
context.recalc_diff.hf12_height = hf12_height;
context.nettype = nettype;
context.recalc_diff.start_height = HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_12_checkpointing);
uint64_t constexpr FUDGE = BLOCKS_EXPECTED_IN_DAYS(1);
context.recalc_diff.start_height = (context.recalc_diff.start_height < FUDGE) ? 0 : context.recalc_diff.start_height - FUDGE;

View File

@ -205,6 +205,8 @@ public:
block_header get_block_header(const crypto::hash& h) const override;
block_header get_block_header_by_height(uint64_t height) const;
cryptonote::blobdata get_block_blob(const crypto::hash& h) const override;
cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const override;

View File

@ -64,14 +64,22 @@ namespace cryptonote {
/* Cryptonote helper functions */
/************************************************************************/
//-----------------------------------------------------------------------------------------------
bool block_has_pulse_components(block const &blk)
bool block_header_has_pulse_components(block_header const &blk_header)
{
constexpr cryptonote::pulse_random_value empty_random_value = {};
bool bitset = blk_header.pulse.validator_bitset > 0;
bool random_value = !(blk_header.pulse.random_value == empty_random_value);
uint8_t hf_version = blk_header.major_version;
bool result = hf_version >= cryptonote::network_version_16 && (bitset || random_value);
return result;
}
//-----------------------------------------------------------------------------------------------
bool block_has_pulse_components(block const &blk)
{
bool signatures = blk.signatures.size();
bool bitset = blk.pulse.validator_bitset > 0;
bool random_value = !(blk.pulse.random_value == empty_random_value);
uint8_t hf_version = blk.major_version;
bool result = hf_version >= cryptonote::network_version_16 && (signatures || bitset || random_value);
bool result =
(hf_version >= cryptonote::network_version_16 && signatures) || block_header_has_pulse_components(blk);
return result;
}
//-----------------------------------------------------------------------------------------------

View File

@ -114,6 +114,7 @@ namespace cryptonote {
/************************************************************************/
/* Cryptonote helper functions */
/************************************************************************/
bool block_header_has_pulse_components(block_header const &blk_header);
bool block_has_pulse_components(block const &blk);
size_t get_min_block_weight(uint8_t version);
size_t get_max_tx_size();

View File

@ -34,8 +34,8 @@
#include "common/loki.h"
#include "int-util.h"
#include "crypto/hash.h"
#include "cryptonote_config.h"
#include "difficulty.h"
#include "hardfork.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "difficulty"
@ -140,9 +140,11 @@ namespace cryptonote {
// be reduced from 60*60*2 to 500 seconds to prevent timestamp manipulation from miner's with
// > 50% hash power. If this is too small, it can be increased to 1000 at a cost in protection.
difficulty_calc_mode difficulty_mode(uint8_t hf_version, uint64_t height, uint64_t hf12_height)
difficulty_calc_mode difficulty_mode(cryptonote::network_type nettype, uint8_t hf_version, uint64_t height)
{
auto result = difficulty_calc_mode::post_pulse;
static const uint64_t hf12_height = cryptonote::HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_12_checkpointing);
auto result = difficulty_calc_mode::normal;
if (hf_version <= cryptonote::network_version_9_service_nodes)
{
result = difficulty_calc_mode::use_old_lwma;
@ -154,10 +156,6 @@ namespace cryptonote {
{
result = difficulty_calc_mode::hf12_override;
}
else if (hf_version <= cryptonote::network_version_15_lns)
{
result = difficulty_calc_mode::pre_pulse;
}
return result;
}
@ -167,10 +165,8 @@ namespace cryptonote {
size_t target_seconds,
difficulty_calc_mode mode)
{
const int64_t T = static_cast<int64_t>(target_seconds);
size_t N = DIFFICULTY_WINDOW - 1;
size_t N = DIFFICULTY_WINDOW - 1;
// Return a difficulty of 1 for first 4 blocks if it's the start of the chain.
if (timestamps.size() < 4) {
@ -180,13 +176,13 @@ namespace cryptonote {
else if ( timestamps.size()-1 < N ) {
N = timestamps.size() - 1;
}
// Otherwise make sure timestamps and cumulative_difficulties are correct size.
else {
// TODO: put asserts here, so that the difficulty algorithm is never called with an oversized window
// OR make this use the last N+1 timestamps and cum_diff, not the first.
else
{
// Otherwise make sure timestamps and cumulative_difficulties are correct size.
timestamps.resize(N+1);
cumulative_difficulties.resize(N+1);
}
// To get an average solvetime to within +/- ~0.1%, use an adjustment factor.
// adjust=0.999 for 80 < N < 120(?)
const double adjust = 0.998;
@ -228,7 +224,7 @@ namespace cryptonote {
// that 30MH/s seems more or less right, so we cap it there for the first WINDOW blocks to
// prevent too-long blocks right after the fork.
if (mode == difficulty_calc_mode::hf12_override)
return std::min(next_difficulty, 30000000 * uint64_t(target_seconds));
return std::min(next_difficulty, 30'000'000 * uint64_t(target_seconds));
return next_difficulty;
}

View File

@ -33,6 +33,8 @@
#include <cstdint>
#include <cstddef>
#include <vector>
#include "cryptonote_config.h"
#include "common/util.h"
namespace crypto { struct hash; }
@ -54,15 +56,15 @@ namespace cryptonote
*/
bool check_hash(const crypto::hash &hash, difficulty_type difficulty);
constexpr difficulty_type PULSE_FIXED_DIFFICULTY = 1'000'000 * tools::to_seconds(TARGET_BLOCK_TIME);
enum struct difficulty_calc_mode
{
use_old_lwma,
hf12_override,
pre_pulse,
post_pulse,
normal,
};
difficulty_calc_mode difficulty_mode(uint8_t hf_version, uint64_t height, uint64_t hf12_height);
difficulty_calc_mode difficulty_mode(network_type nettype, uint8_t hf_version, uint64_t height);
difficulty_type next_difficulty_v2(std::vector<std::uint64_t> timestamps,
std::vector<difficulty_type> cumulative_difficulties,

View File

@ -93,8 +93,46 @@ static_assert(STAKING_PORTIONS % 12 == 0, "Use a multiple of twelve, so that it
#define DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT_V12 ((uint64_t)240000) // Only v12 (v13 switches back)
constexpr auto TARGET_BLOCK_TIME = 2min;
constexpr auto DIFFICULTY_WINDOW = 60;
constexpr uint64_t DIFFICULTY_BLOCKS_COUNT = (DIFFICULTY_WINDOW + 1); // added +1 to make N=N
constexpr uint64_t DIFFICULTY_WINDOW = 60;
constexpr uint64_t DIFFICULTY_BLOCKS_COUNT(bool before_hf16)
{
// NOTE(loki): next_difficulty_v2(...) calculates the next block difficulty
// by examining the past N window of blocks i.e.
//
// N = (DIFFICULTY_WINDOW - 1)
//
// and resizes the timestamps/difficulty arrays to
//
// array.resize(N+1) or in otherwords array.resize(DIFFICULTY_WINDOW)
//
// However all the code responsible for filling the timestamps/difficulty
// arrays prior to HF16 used DIFFICULTY_BLOCKS_COUNT which was defined as,
//
// constexpr uint64_t DIFFICULTY_BLOCKS_COUNT = (DIFFICULTY_WINDOW + 1); // added +1 to make N=N
//
// The comment suggests that +1 makes N=N, assuming the N refers to
// next_difficulty_v2's N variable. Adding +1 here does not make
// DIFFICULTY_BLOCKS_COUNT be equal to N.
//
// N = N1 = (DIFFICULTY_WINDOW - 1)
// N2 = DIFFICULTY_BLOCKS_COUNT = (DIFFICULTY_WINDOW + 1)
//
// What this means in practice is that, we fill DIFFICULTY_BLOCKS_COUNT
// worth of values and resize to DIFFICULTY_WINDOW size (chopping off the
// latest 2 blocks).
//
// This for example means when calculating the difficulty for a block, we
// 1. Fill the timestamps/difficulties with the latest DIFFICULTY_BLOCKS_COUNT data
// 2. Resize the timestamps/difficulties to DIFFICULTY_WINDOW. This steps
// chops off the last 2 blocks from consideration.
//
// So prior to HF16, we revert to the incorrect value, and afterwards we
// calculate correctly. With Pulse in HF16, this is largely a non-issue, but
// for future correctness (when the network falls back to PoW stalled) and
// purpose of documentation this is fixed and addressed in this function.
uint64_t result = (before_hf16) ? DIFFICULTY_WINDOW + 1 : DIFFICULTY_WINDOW;
return result;
}
constexpr uint64_t BLOCKS_EXPECTED_IN_HOURS(int hours) { return (1h / TARGET_BLOCK_TIME) * hours; }
constexpr uint64_t BLOCKS_EXPECTED_IN_DAYS(int days) { return BLOCKS_EXPECTED_IN_HOURS(24) * days; }

View File

@ -110,14 +110,12 @@ Blockchain::block_extended_info::block_extended_info(const alt_block_data_t &src
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool, service_nodes::service_node_list& service_node_list):
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
m_long_term_effective_median_block_weight(0),
m_long_term_block_weights_cache_tip_hash(crypto::null_hash),
m_long_term_block_weights_cache_rolling_median(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1),
m_service_node_list(service_node_list),
m_btc_valid(false),
m_batch_success(true),
@ -494,7 +492,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *lns_db, const network_type nett
if (m_nettype != FAKECHAIN)
{
cryptonote::BlockchainDB::fixup_context context = {};
context.recalc_diff.hf12_height = HardFork::get_hardcoded_hard_fork_height(m_nettype, cryptonote::network_version_12_checkpointing);
context.nettype = m_nettype;
m_db->fixup(context);
}
@ -518,6 +516,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *lns_db, const network_type nett
load_compiled_in_block_hashes(get_checkpoints);
#endif
// TODO(doyle): I'm not sure that we want to cache difficulty right here
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
rtxn_guard.stop();
@ -564,7 +563,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *lns_db, const network_type nett
}
if (num_popped_blocks > 0)
{
m_timestamps_and_difficulties_height = 0;
m_cache.m_timestamps_and_difficulties_height = 0;
m_hardfork->reorganize_from_chain_height(get_current_blockchain_height());
m_tx_pool.on_blockchain_dec();
}
@ -734,7 +733,7 @@ block Blockchain::pop_block_from_blockchain()
LOG_PRINT_L3("Blockchain::" << __func__);
std::unique_lock lock{*this};
m_timestamps_and_difficulties_height = 0;
m_cache.m_timestamps_and_difficulties_height = 0;
block popped_block;
std::vector<transaction> popped_txs;
@ -809,7 +808,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
{
LOG_PRINT_L3("Blockchain::" << __func__);
std::unique_lock lock{*this};
m_timestamps_and_difficulties_height = 0;
m_cache.m_timestamps_and_difficulties_height = 0;
invalidate_block_template_cache();
m_db->reset();
m_db->drop_alt_blocks();
@ -948,89 +947,69 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph
}
//------------------------------------------------------------------
// This function aggregates the cumulative difficulties and timestamps of the
// last DIFFICULTY_BLOCKS_COUNT blocks and passes them to next_difficulty,
// last DIFFICULTY_WINDOW blocks and passes them to next_difficulty,
// returning the result of that call. Ignores the genesis block, and can use
// less blocks than desired if there aren't enough.
difficulty_type Blockchain::get_difficulty_for_next_block()
{
LOG_PRINT_L3("Blockchain::" << __func__);
if (m_fixed_difficulty)
{
return m_db->height() ? m_fixed_difficulty : 1;
}
LOG_PRINT_L3("Blockchain::" << __func__);
uint8_t const hf_version = get_current_hard_fork_version();
crypto::hash top_hash = get_tail_id();
{
std::unique_lock diff_lock{m_difficulty_lock};
std::unique_lock diff_lock{m_cache.m_difficulty_lock};
// we can call this without the blockchain lock, it might just give us
// something a bit out of date, but that's fine since anything which
// requires the blockchain lock will have acquired it in the first place,
// and it will be unlocked only when called from the getinfo RPC
if (top_hash == m_difficulty_for_next_block_top_hash)
return m_difficulty_for_next_block;
if (top_hash == m_cache.m_difficulty_for_next_block_top_hash)
{
if (hf_version >= cryptonote::network_version_16 && pulse::clock::now() < m_cache.m_pulse_timings.miner_fallback_timestamp)
return PULSE_FIXED_DIFFICULTY;
else
return m_cache.m_difficulty_for_next_block;
}
}
std::unique_lock lock{*this};
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
uint64_t height;
uint8_t version = get_current_hard_fork_version();
top_hash = get_tail_id(height); // get it again now that we have the lock
++height; // top block height to blockchain height
// ND: Speedup
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
// then when the next block difficulty is queried, push the latest height data and
// pop the oldest one from the list. This only requires 1x read per height instead
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT)
uint64_t top_block_height = 0;
top_hash = get_tail_id(top_block_height); // get it again now that we have the lock
uint64_t chain_height = top_block_height + 1;
bool pulse_override_difficulty = false;
if (hf_version >= cryptonote::network_version_16)
{
uint64_t index = height - 1;
m_timestamps.push_back(m_db->get_block_timestamp(index));
m_difficulties.push_back(m_db->get_block_cumulative_difficulty(index));
pulse::get_round_timings(*this, chain_height, m_cache.m_pulse_timings);
pulse_override_difficulty = (pulse::clock::now() < m_cache.m_pulse_timings.miner_fallback_timestamp);
}
while (m_timestamps.size() > DIFFICULTY_BLOCKS_COUNT)
m_timestamps.erase(m_timestamps.begin());
while (m_difficulties.size() > DIFFICULTY_BLOCKS_COUNT)
m_difficulties.erase(m_difficulties.begin());
difficulty_type diff = {};
m_cache.m_timestamps_and_difficulties_height = chain_height;
m_timestamps_and_difficulties_height = height;
timestamps = m_timestamps;
difficulties = m_difficulties;
if (pulse_override_difficulty)
{
// All blocks generated by a Quorum in Pulse have difficulty fixed to
// 1'000'000 such that, when we have to fallback to PoW difficulty is
// a reasonable value to allow continuing the network onwards.
diff = PULSE_FIXED_DIFFICULTY;
}
else
{
uint64_t offset = height - std::min < size_t > (height, static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
if (offset == 0)
++offset;
timestamps.clear();
difficulties.clear();
if (height > offset)
{
timestamps.reserve(height - offset);
difficulties.reserve(height - offset);
}
for (; offset < height; offset++)
{
timestamps.push_back(m_db->get_block_timestamp(offset));
difficulties.push_back(m_db->get_block_cumulative_difficulty(offset));
}
m_timestamps_and_difficulties_height = height;
m_timestamps = timestamps;
m_difficulties = difficulties;
m_db->fill_timestamps_and_difficulties_for_pow(
m_nettype, m_cache.m_timestamps, m_cache.m_difficulties, chain_height, m_cache.m_timestamps_and_difficulties_height);
diff = next_difficulty_v2(m_cache.m_timestamps,
m_cache.m_difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(m_nettype, hf_version, chain_height));
}
uint64_t hf12_height = m_hardfork->get_earliest_ideal_height_for_version(network_version_12_checkpointing);
difficulty_type diff = next_difficulty_v2(timestamps,
difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(version, height, hf12_height));
std::unique_lock diff_lock{m_difficulty_lock};
m_difficulty_for_next_block_top_hash = top_hash;
m_difficulty_for_next_block = diff;
std::unique_lock diff_lock{m_cache.m_difficulty_lock};
m_cache.m_difficulty_for_next_block_top_hash = top_hash;
m_cache.m_difficulty_for_next_block = diff;
return diff;
}
//------------------------------------------------------------------
@ -1059,7 +1038,7 @@ bool Blockchain::rollback_blockchain_switching(const std::list<block_and_checkpo
return true;
}
m_timestamps_and_difficulties_height = 0;
m_cache.m_timestamps_and_difficulties_height = 0;
// remove blocks from blockchain until we get back to where we should be.
while (m_db->height() != rollback_height)
@ -1110,7 +1089,7 @@ bool Blockchain::switch_to_alternative_blockchain(const std::list<block_extended
LOG_PRINT_L3("Blockchain::" << __func__);
std::unique_lock lock{*this};
m_timestamps_and_difficulties_height = 0;
m_cache.m_timestamps_and_difficulties_height = 0;
// if empty alt chain passed (not sure how that could happen), return false
CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
@ -1221,18 +1200,32 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
}
LOG_PRINT_L3("Blockchain::" << __func__);
uint64_t block_count = 0;
{
bool before_hf16 = false;
if (alt_chain.size())
before_hf16 = alt_chain.back().bl.major_version < network_version_16;
else
{
static const uint64_t hf16_height = HardFork::get_hardcoded_hard_fork_height(m_nettype, cryptonote::network_version_16);
before_hf16 = get_current_blockchain_height() < hf16_height;
}
block_count = DIFFICULTY_BLOCKS_COUNT(before_hf16);
}
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> cumulative_difficulties;
// if the alt chain isn't long enough to calculate the difficulty target
// based on its blocks alone, need to get more blocks from the main chain
if(alt_chain.size() < DIFFICULTY_BLOCKS_COUNT)
if(alt_chain.size() < block_count)
{
std::unique_lock lock{*this};
// Figure out start and stop offsets for main chain blocks
size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front().height : alt_block_height;
size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
size_t main_chain_count = block_count - std::min(static_cast<size_t>(block_count), alt_chain.size());
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
@ -1247,7 +1240,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
}
// make sure we haven't accidentally grabbed too many blocks...maybe don't need this check?
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT);
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= block_count, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << block_count);
for (const auto &bei : alt_chain)
{
@ -1259,8 +1252,8 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
// and timestamps from it alone
else
{
timestamps.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
cumulative_difficulties.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
timestamps.resize(static_cast<size_t>(block_count));
cumulative_difficulties.resize(static_cast<size_t>(block_count));
size_t count = 0;
size_t max_i = timestamps.size()-1;
// get difficulties and timestamps from most recent blocks in alt chain
@ -1270,19 +1263,17 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
timestamps[max_i - count] = bei.bl.timestamp;
cumulative_difficulties[max_i - count] = bei.cumulative_difficulty;
count++;
if(count >= DIFFICULTY_BLOCKS_COUNT)
if(count >= block_count)
break;
}
}
uint64_t hf12_height = m_hardfork->get_earliest_ideal_height_for_version(network_version_12_checkpointing);
uint64_t height = (alt_chain.size() ? alt_chain.front().height : alt_block_height) + alt_chain.size() + 1;
// calculate the difficulty target for the block and return it
uint64_t height = (alt_chain.size() ? alt_chain.front().height : alt_block_height) + alt_chain.size() + 1;
return next_difficulty_v2(timestamps,
cumulative_difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(get_current_hard_fork_version(), height, hf12_height));
difficulty_mode(m_nettype, get_current_hard_fork_version(), height));
}
//------------------------------------------------------------------
// This function does a sanity check on basic things that all miner
@ -1598,12 +1589,12 @@ bool Blockchain::create_block_template_internal(block& b, const crypto::hash *fr
}
else
{
height = m_db->height();
b.major_version = m_hardfork->get_current_version();
b.minor_version = m_hardfork->get_ideal_version();
b.prev_id = get_tail_id();
median_weight = m_current_block_cumul_weight_limit / 2;
diffic = get_difficulty_for_next_block();
height = m_db->height();
b.major_version = m_hardfork->get_current_version();
b.minor_version = m_hardfork->get_ideal_version();
b.prev_id = get_tail_id();
median_weight = m_current_block_cumul_weight_limit / 2;
diffic = get_difficulty_for_next_block();
already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
}
b.timestamp = time(NULL);
@ -1899,7 +1890,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
}
// NOTE: Reset timestamp/difficulty cache
m_timestamps_and_difficulties_height = 0;
m_cache.m_timestamps_and_difficulties_height = 0;
// NOTE: Build the alternative chain for checking reorg-ability
std::list<block_extended_info> alt_chain;
@ -4303,7 +4294,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
if (nettype() == MAINNET && (block_height % BLOCKS_EXPECTED_IN_DAYS(1) == 0))
{
cryptonote::BlockchainDB::fixup_context context = {};
context.recalc_diff.hf12_height = HardFork::get_hardcoded_hard_fork_height(m_nettype, cryptonote::network_version_12_checkpointing);
context.nettype = m_nettype;
context.recalc_diff.start_height = block_height - BLOCKS_EXPECTED_IN_DAYS(1);
m_db->fixup(context);
}
@ -4353,8 +4344,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
++m_sync_counter;
m_tx_pool.on_blockchain_inc(bl);
if (!pulse_block)
get_difficulty_for_next_block(); // just to cache it
get_difficulty_for_next_block(); // just to cache it
invalidate_block_template_cache();
if (notify)

View File

@ -57,6 +57,7 @@
#include "cryptonote_basic/hardfork.h"
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_core/loki_name_system.h"
#include "pulse.h"
struct sqlite3;
namespace service_nodes { class service_node_list; };
@ -1134,17 +1135,31 @@ namespace cryptonote
uint64_t m_fake_scan_time;
uint64_t m_sync_counter;
uint64_t m_bytes_to_sync;
std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height;
uint64_t m_long_term_block_weights_window;
uint64_t m_long_term_effective_median_block_weight;
mutable crypto::hash m_long_term_block_weights_cache_tip_hash;
mutable epee::misc_utils::rolling_median_t<uint64_t> m_long_term_block_weights_cache_rolling_median;
std::mutex m_difficulty_lock;
crypto::hash m_difficulty_for_next_block_top_hash;
difficulty_type m_difficulty_for_next_block;
// NOTE: PoW/Difficulty Cache
// Before HF16, we use timestamps and difficulties only.
// After HF16, we check if the state of block producing in Pulse and return
// the PoW difficulty or the fixed Pulse difficulty if we're still eligible
// for Pulse blocks.
struct
{
std::mutex m_difficulty_lock;
// NOTE: PoW Difficulty Calculation Metadata
std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties;
// NOTE: Cache Invalidation Checks
uint64_t m_timestamps_and_difficulties_height{0};
crypto::hash m_difficulty_for_next_block_top_hash{crypto::null_hash};
difficulty_type m_difficulty_for_next_block{1};
pulse::timings m_pulse_timings{};
} m_cache;
boost::asio::io_service m_async_service;
std::thread m_async_thread;

View File

@ -45,7 +45,7 @@ namespace
for (size_t i = 0; i < new_block_count; ++i)
{
block blk_next;
difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::pre_pulse);
difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::normal);
if (!generator.construct_block_manually(blk_next, blk_prev, miner_account,
test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic))
return false;
@ -175,7 +175,7 @@ bool gen_block_invalid_nonce::generate(std::vector<test_event_entry>& events) co
return false;
// Create invalid nonce
difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::pre_pulse);
difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::normal);
assert(1 < diffic);
const block& blk_last = std::get<block>(events.back());
uint64_t timestamp = blk_last.timestamp;
@ -639,7 +639,7 @@ bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& ev
do
{
blk_last = std::get<block>(events.back());
diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::pre_pulse);
diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::normal);
if (!lift_up_difficulty(events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account))
return false;
std::cout << "Block #" << events.size() << ", difficulty: " << diffic << std::endl;
@ -654,7 +654,7 @@ bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& ev
std::vector<crypto::hash> tx_hashes;
tx_hashes.push_back(get_transaction_hash(tx_0));
size_t txs_weight = get_transaction_weight(tx_0);
diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::pre_pulse);
diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME), cryptonote::difficulty_calc_mode::normal);
if (!generator.construct_block_manually(blk_test, blk_last, miner_account,
test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp,
crypto::hash(), diffic, transaction(), tx_hashes, txs_weight))

View File

@ -69,7 +69,7 @@ int main(int argc, char *argv[]) {
std::vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end),
std::vector<uint64_t>(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end),
tools::to_seconds(TARGET_BLOCK_TIME),
cryptonote::difficulty_calc_mode::pre_pulse);
cryptonote::difficulty_calc_mode::normal);
if (res != difficulty) {
std::cerr << "Wrong difficulty for block " << n
<< "\nExpected: " << difficulty