Add snode revision soft forks & drop hard fork voting code

Snode revisions are a secondary version that let us put out a mandatory
update for snodes that isn't a hardfork (and so isn't mandatory for
wallets/exchanges/etc.).

The main point of this is to let us make a 9.2.0 release that includes
new mandatory minimums of future versions of storage server (2.2.0) and
lokinet (0.9.4) to bring upgrades to the network.

This slightly changes the HF7 blocks to 0 (instead of 1) because,
apparently, we weren't properly checking the HF value of the
pre-first-hf genesis block at all before.  (In practice this changes
nothing because genesis blocks are v7 anyway).

This also changes (slightly) how we check for hard forks: now if we skip
some hard forks then we still want to know the height when a hard fork
triggers.  For example, if the hf tables contains {7,14} then we still
need to know that the HF14 block height also is the height that
activates HF9, 10, etc.
This commit is contained in:
Jason Rhinelander 2021-06-17 20:06:51 -03:00
parent a4cd3dda3f
commit 7b00cb251b
32 changed files with 370 additions and 1256 deletions

View File

@ -27,6 +27,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_core/service_node_rules.h"
#include "checkpoints/checkpoints.h"
#include "epee/string_tools.h"
@ -222,18 +223,11 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
TIME_MEASURE_FINISH(time1);
time_add_block1 += time1;
m_hardfork->add(blk, prev_height);
++num_calls;
return prev_height;
}
void BlockchainDB::set_hard_fork(HardFork* hf)
{
m_hardfork = hf;
}
void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
{
blk = get_top_block();
@ -438,8 +432,7 @@ void BlockchainDB::fill_timestamps_and_difficulties_for_pow(cryptonote::network_
return;
uint64_t const top_block_height = chain_height - 1;
static const uint64_t hf16_height = HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_16_pulse);
bool const before_hf16 = chain_height < hf16_height;
bool const before_hf16 = !is_hard_fork_at_least(nettype, network_version_16_pulse, chain_height);
uint64_t const block_count = DIFFICULTY_BLOCKS_COUNT(before_hf16);
timestamps.reserve(block_count);

View File

@ -39,7 +39,6 @@
#include "cryptonote_basic/blobdatatype.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/difficulty.h"
#include "cryptonote_basic/hardfork.h"
/** \file
* Cryptonote Blockchain Database Interface
@ -549,14 +548,12 @@ protected:
uint64_t time_commit1 = 0; //!< a performance metric
bool m_auto_remove_logs = true; //!< whether or not to automatically remove old logs
HardFork* m_hardfork;
public:
/**
* @brief An empty constructor.
*/
BlockchainDB(): m_hardfork(NULL), m_open(false) { }
BlockchainDB() = default;
/**
* @brief An empty destructor.
@ -796,8 +793,6 @@ public:
virtual void block_rtxn_stop() const = 0;
virtual void block_rtxn_abort() const = 0;
virtual void set_hard_fork(HardFork* hf);
// adds a block with the given metadata to the top of the blockchain, returns the new height
/**
* @brief handles the addition of a new block to BlockchainDB
@ -1775,38 +1770,6 @@ public:
*/
virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *block_blob, const cryptonote::blobdata *checkpoint_blob)> f, bool include_blob = false) const = 0;
//
// Hard fork related storage
//
/**
* @brief sets which hardfork version a height is on
*
* @param height the height
* @param version the version
*/
virtual void set_hard_fork_version(uint64_t height, uint8_t version) = 0;
/**
* @brief checks which hardfork version a height is on
*
* @param height the height
*
* @return the version
*/
virtual uint8_t get_hard_fork_version(uint64_t height) const = 0;
/**
* @brief verify hard fork info in database
*/
virtual void check_hard_fork_info() = 0;
/**
* @brief delete hard fork info from database
*/
virtual void drop_hard_fork_info() = 0;
/**
* @brief return a histogram of outputs on the blockchain
*
@ -1897,7 +1860,7 @@ public:
*/
void set_auto_remove_logs(bool auto_remove) { m_auto_remove_logs = auto_remove; }
bool m_open; //!< Whether or not the BlockchainDB is open/ready for use
bool m_open = false; //!< Whether or not the BlockchainDB is open/ready for use
}; // class BlockchainDB

View File

@ -36,6 +36,7 @@
#include <type_traits>
#include <variant>
#include "cryptonote_basic/hardfork.h"
#include "epee/string_tools.h"
#include "common/file.h"
#include "common/pruning.h"
@ -49,7 +50,6 @@
#include "cryptonote_core/service_node_rules.h"
#include "cryptonote_core/service_node_list.h"
#include "cryptonote_core/uptime_proof.h"
#include "cryptonote_basic/hardfork.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "blockchain.db.lmdb"
@ -1392,8 +1392,6 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB()
m_cum_count = 0;
// reset may also need changing when initialize things here
m_hardfork = nullptr;
}
void BlockchainLMDB::open(const fs::path& filename, cryptonote::network_type nettype, const int db_flags)
@ -4573,64 +4571,6 @@ void BlockchainLMDB::add_output_blacklist(std::vector<uint64_t> const &blacklist
throw0(DB_ERROR(lmdb_error("Failed to add blacklisted output to db transaction: ", ret).c_str()));
}
void BlockchainLMDB::check_hard_fork_info()
{
}
void BlockchainLMDB::drop_hard_fork_info()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_PREFIX(0);
auto result = mdb_drop(*txn_ptr, m_hf_starting_heights, 1);
if (result)
throw1(DB_ERROR(lmdb_error("Error dropping hard fork starting heights db: ", result).c_str()));
result = mdb_drop(*txn_ptr, m_hf_versions, 1);
if (result)
throw1(DB_ERROR(lmdb_error("Error dropping hard fork versions db: ", result).c_str()));
TXN_POSTFIX_SUCCESS();
}
void BlockchainLMDB::set_hard_fork_version(uint64_t height, uint8_t version)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_BLOCK_PREFIX(0);
MDB_val_copy<uint64_t> val_key(height);
MDB_val_copy<uint8_t> val_value(version);
int result;
result = mdb_put(*txn_ptr, m_hf_versions, &val_key, &val_value, MDB_APPEND);
if (result == MDB_KEYEXIST)
result = mdb_put(*txn_ptr, m_hf_versions, &val_key, &val_value, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Error adding hard fork version to db transaction: ", result).c_str()));
TXN_BLOCK_POSTFIX_SUCCESS();
}
uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_PREFIX_RDONLY();
RCURSOR(hf_versions);
MDB_val_copy<uint64_t> val_key(height);
MDB_val val_ret;
auto result = mdb_cursor_get(m_cur_hf_versions, &val_key, &val_ret, MDB_SET);
if (result == MDB_NOTFOUND || result)
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve a hard fork version at height " + std::to_string(height) + " from the db: ", result).c_str()));
uint8_t ret = *(const uint8_t*)val_ret.mv_data;
return ret;
}
void BlockchainLMDB::add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &block, cryptonote::blobdata const *checkpoint)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -4805,8 +4745,8 @@ void BlockchainLMDB::fixup(cryptonote::network_type nettype)
add_timestamp_and_difficulty(nettype, curr_chain_height, timestamps, difficulties, curr_timestamp, curr_cumulative_diff);
// NOTE: Calculate next block difficulty
uint8_t const hf_version = get_hard_fork_version(curr_height);
if (hf_version >= cryptonote::network_version_16_pulse && block_header_has_pulse_components(get_block_header_from_height(curr_height)))
if (is_hard_fork_at_least(nettype, cryptonote::network_version_16_pulse, curr_height)
&& block_header_has_pulse_components(get_block_header_from_height(curr_height)))
{
diff = PULSE_FIXED_DIFFICULTY;
}
@ -4815,7 +4755,7 @@ void BlockchainLMDB::fixup(cryptonote::network_type nettype)
diff = next_difficulty_v2(timestamps,
difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(nettype, hf_version, curr_height + 1));
difficulty_mode(nettype, curr_height + 1));
}
}

View File

@ -403,12 +403,6 @@ private:
uint64_t num_outputs() const;
// Hard fork
void set_hard_fork_version(uint64_t height, uint8_t version) override;
uint8_t get_hard_fork_version(uint64_t height) const override;
void check_hard_fork_info() override;
void drop_hard_fork_info() override;
inline void check_open() const;
bool prune_worker(int mode, uint32_t pruning_seed);

View File

@ -65,7 +65,6 @@ public:
virtual void block_rtxn_stop() const override {}
virtual void block_rtxn_abort() const override {}
virtual void drop_hard_fork_info() override {}
virtual bool block_exists(const crypto::hash& h, uint64_t *height) const override { return false; }
virtual cryptonote::blobdata get_block_blob_from_height(uint64_t height) const override { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); }
virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const override { return cryptonote::blobdata(); }

View File

@ -549,7 +549,6 @@ int main(int argc, char* argv[])
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
const command_line::arg_descriptor<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size};
const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks};
const command_line::arg_descriptor<bool> arg_drop_hf = {"drop-hard-fork", "Drop hard fork subdbs", false};
const command_line::arg_descriptor<bool> arg_count_blocks = {
"count-blocks"
, "Count blocks in bootstrap file and exit"
@ -569,7 +568,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_only, arg_count_blocks);
command_line::add_arg(desc_cmd_only, arg_pop_blocks);
command_line::add_arg(desc_cmd_only, arg_drop_hf);
command_line::add_arg(desc_cmd_only, command_line::arg_help);
command_line::add_arg(desc_cmd_only, arg_recalculate_difficulty);
@ -722,14 +720,6 @@ int main(int argc, char* argv[])
return 0;
}
if (!command_line::is_arg_defaulted(vm, arg_drop_hf))
{
MINFO("Dropping hard fork tables...");
core.get_blockchain_storage().get_db().drop_hard_fork_info();
core.deinit();
return 0;
}
if (command_line::get_arg(vm, arg_recalculate_difficulty))
core.get_blockchain_storage().get_db().fixup(core.get_nettype());

View File

@ -32,6 +32,7 @@
#include <algorithm>
#include "common/oxen.h"
#include "cryptonote_config.h"
#include "epee/int-util.h"
#include "crypto/hash.h"
#include "difficulty.h"
@ -127,8 +128,7 @@ namespace cryptonote {
timestamps.push_back(timestamp);
difficulties.push_back(cumulative_difficulty);
static const uint64_t hf16_height = HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_16_pulse);
bool const before_hf16 = chain_height < hf16_height;
bool before_hf16 = !is_hard_fork_at_least(nettype, network_version_16_pulse, chain_height);
// Trim down arrays
while (timestamps.size() > DIFFICULTY_BLOCKS_COUNT(before_hf16))
@ -163,27 +163,20 @@ 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(cryptonote::network_type nettype, uint8_t hf_version, uint64_t height)
difficulty_calc_mode difficulty_mode(cryptonote::network_type nettype, uint64_t height)
{
static const uint64_t hf12_height = cryptonote::HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_12_checkpointing);
static const uint64_t hf16_height = cryptonote::HardFork::get_hardcoded_hard_fork_height(nettype, cryptonote::network_version_16_pulse);
auto result = difficulty_calc_mode::normal;
if (hf_version <= cryptonote::network_version_9_service_nodes)
{
if (!is_hard_fork_at_least(nettype, cryptonote::network_version_10_bulletproofs, height))
result = difficulty_calc_mode::use_old_lwma;
}
// HF12 switches to RandomX with a likely drastically reduced hashrate versus Turtle, so override
// difficulty for the first difficulty window blocks:
else if (height >= hf12_height &&
height < hf12_height + (DIFFICULTY_WINDOW + 1))
{
else if (auto randomx_start_height = get_hard_fork_heights(nettype, network_version_12_checkpointing).first;
randomx_start_height && height >= *randomx_start_height && height <= *randomx_start_height + DIFFICULTY_WINDOW)
result = difficulty_calc_mode::hf12_override;
}
else if (nettype == MAINNET && height >= hf16_height && height < hf16_height + (DIFFICULTY_WINDOW + 1))
{
else if (auto pulse_start_height = get_hard_fork_heights(nettype, network_version_16_pulse).first;
nettype == MAINNET && pulse_start_height && height >= *pulse_start_height && height <= *pulse_start_height + DIFFICULTY_WINDOW)
result = difficulty_calc_mode::hf16_override;
}
return result;
}

View File

@ -75,7 +75,7 @@ namespace cryptonote
normal,
};
difficulty_calc_mode difficulty_mode(network_type nettype, uint8_t hf_version, uint64_t height);
difficulty_calc_mode difficulty_mode(network_type nettype, uint64_t height);
difficulty_type next_difficulty_v2(std::vector<std::uint64_t> timestamps,
std::vector<difficulty_type> cumulative_difficulties,

View File

@ -1,5 +1,4 @@
// Copyright (c) 2014-2019, The Monero Project
// Copyright (c) 2018, The Loki Project
// Copyright (c) 2018-2021, The Loki Project
//
// All rights reserved.
//
@ -27,468 +26,145 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <cstdio>
#include <array>
#include "common/oxen.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "blockchain_db/blockchain_db.h"
#include "hardfork.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "hardfork"
using namespace cryptonote;
static uint8_t get_block_vote(const cryptonote::block &b)
{
// Pre-hardfork blocks have a minor version hardcoded to 0.
// For the purposes of voting, we consider 0 to refer to
// version number 1, which is what all blocks from the genesis
// block are. It makes things simpler.
if (b.minor_version == 0)
return 1;
return b.minor_version;
}
static uint8_t get_block_version(const cryptonote::block &b)
{
return b.major_version;
}
// TODO(oxen): Re-evaluate Hardfork as a class. Originally designed to
// handle voting, hardforks are now locked in, maybe we just need helper
// functions on the hardcoded table instead of hiding everything behind
// a class.
namespace cryptonote {
// version 7 from the start of the blockchain, inhereted from Monero mainnet
static constexpr HardFork::Params mainnet_hard_forks[] =
static constexpr std::array mainnet_hard_forks =
{
{ network_version_7, 1, 0, 1503046577 },
{ network_version_8, 64324, 0, 1533006000 },
{ network_version_9_service_nodes, 101250, 0, 1537444800 },
{ network_version_10_bulletproofs, 161849, 0, 1544743800 }, // 2018-12-13 23:30UTC
{ network_version_11_infinite_staking, 234767, 0, 1554170400 }, // 2019-03-26 13:00AEDT
{ network_version_12_checkpointing, 321467, 0, 1563940800 }, // 2019-07-24 14:00AEDT
{ network_version_13_enforce_checkpoints, 385824, 0, 1571850000 }, // 2019-10-23 19:00AEDT
{ network_version_14_blink, 442333, 0, 1578528000 }, // 2020-01-09 00:00UTC
{ network_version_15_ons, 496969, 0, 1585105200 }, // 2020-03-25 14:00AEDT (03:00UTC)
{ network_version_16_pulse, 641111, 0, 1602464400 }, // 2020-10-12 12:00AEDT (01:00UTC)
{ network_version_17, 770711, 0, 1618016400 }, // Saturday, April 10, 2021 1:00:00 UTC
{ network_version_18, 785000, 0, 1619736143 }, // Thursday, April 29, 2021 22:42:23 UTC
hard_fork{7, 0, 0, 1503046577 }, // Loki 0.1: Loki is born
hard_fork{8, 0, 64324, 1533006000 /*2018-07-31 03:00 UTC*/ }, // Loki 0.2: New emissions schedule
hard_fork{9, 0, 101250, 1537444800 /*2018-09-20 12:00 UTC*/ }, // Loki 1: Service nodes launched
hard_fork{10, 0, 161849, 1544743800 /*2018-12-13 23:30 UTC*/ }, // Loki 2: Bulletproofs, gov fee batching
hard_fork{11, 0, 234767, 1554170400 /*2019-03-26 13:00 AEDT*/ }, // Loki 3: Infinite staking, CN-Turtle
hard_fork{12, 0, 321467, 1563940800 /*2019-07-24 14:00 AEDT*/ }, // Loki 4: Checkpointing, RandomXL, decommissioning, Storage Server launched
hard_fork{13, 0, 385824, 1571850000 /*2019-10-23 19:00 AEDT*/ }, // Loki 5: Checkpointing enforced
hard_fork{14, 0, 442333, 1578528000 /*2020-01-09 00:00 UTC*/ }, // Loki 6: Blink, Lokinet launched on mainnet
hard_fork{15, 0, 496969, 1585105200 /*2020-03-25 14:00 AEDT (03:00 UTC)*/ }, // Loki 7: ONS (Session)
hard_fork{16, 0, 641111, 1602464400 /*2020-10-12 12:00 AEDT (01:00 UTC)*/ }, // Loki 8: Pulse
hard_fork{17, 0, 770711, 1618016400 /*Saturday, April 10, 2021 1:00:00 UTC*/ }, // Oxen 8: Eliminate 6/block emissions after 180 days (not a separate release)
hard_fork{18, 0, 785000, 1619736143 /*Thursday, April 29, 2021 22:42:23 UTC*/ }, // Oxen 9: Timesync, new proofs, reasons, wallet ONS
hard_fork{18, 1, 840433, 1626388200 /*Thursday, July 15, 2021 22:30:00 UTC */ }, // Oxen 9.2: mandatory SS 2.2.0 & lokinet 0.9.4 updates
};
static constexpr HardFork::Params testnet_hard_forks[] =
static constexpr std::array testnet_hard_forks =
{
{ network_version_7, 1, 0, 1533631121 },
{ network_version_8, 2, 0, 1533631122 },
{ network_version_9_service_nodes, 3, 0, 1533631123 },
{ network_version_10_bulletproofs, 4, 0, 1542681077 },
{ network_version_11_infinite_staking, 5, 0, 1551223964 },
{ network_version_12_checkpointing, 75471, 0, 1561608000 }, // 2019-06-28 14:00AEDT
{ network_version_13_enforce_checkpoints, 127028, 0, 1568440800 }, // 2019-09-13 16:00AEDT
{ network_version_14_blink, 174630, 0, 1575075600 }, // 2019-11-30 07:00UTC
{ network_version_15_ons, 244777, 0, 1583940000 }, // 2020-03-11 15:20UTC
{ network_version_16_pulse, 382222, 0, 1600468200 }, // 2020-09-18 22:30UTC
{ network_version_17, 447275, 0, 1608276840 }, // 2020-12-18 05:34UTC
{ network_version_18, 501750, 0, 1616631051 }, // 2021-03-25 12:10UTC
hard_fork{7, 0, 0, 1533631121 }, // Testnet was rebooted during Loki 3 development
hard_fork{8, 0, 2, 1533631122 },
hard_fork{9, 0, 3, 1533631123 },
hard_fork{10, 0, 4, 1542681077 },
hard_fork{11, 0, 5, 1551223964 },
hard_fork{12, 0, 75471, 1561608000 }, // 2019-06-28 14:00 AEDT
hard_fork{13, 0, 127028, 1568440800 }, // 2019-09-13 16:00 AEDT
hard_fork{14, 0, 174630, 1575075600 }, // 2019-11-30 07:00 UTC
hard_fork{15, 0, 244777, 1583940000 }, // 2020-03-11 15:20 UTC
hard_fork{16, 0, 382222, 1600468200 }, // 2020-09-18 22:30 UTC
hard_fork{17, 0, 447275, 1608276840 }, // 2020-12-18 05:34 UTC
hard_fork{18, 0, 501750, 1616631051 }, // 2021-03-25 12:10 UTC
hard_fork{18, 1, 578637, 1624040400 }, // 2021-06-18 18:20 UTC
};
static constexpr HardFork::Params devnet_hard_forks[] =
static constexpr std::array devnet_hard_forks =
{
{ network_version_7, 1, 0, 1599848400 },
{ network_version_11_infinite_staking, 2, 0, 1599848400 },
{ network_version_12_checkpointing, 3, 0, 1599848400 },
{ network_version_13_enforce_checkpoints, 4, 0, 1599848400 },
{ network_version_15_ons, 5, 0, 1599848400 },
{ network_version_16_pulse, 99, 0, 1599848400 }, // 2020-09-11 18:20 UTC
hard_fork{ 7, 0, 0, 1599848400 },
hard_fork{ 11, 0, 2, 1599848400 },
hard_fork{ 12, 0, 3, 1599848400 },
hard_fork{ 13, 0, 4, 1599848400 },
hard_fork{ 15, 0, 5, 1599848400 },
hard_fork{ 16, 0, 99, 1599848400 },
};
uint64_t HardFork::get_hardcoded_hard_fork_height(network_type nettype, cryptonote::network_version version)
{
uint64_t result = INVALID_HF_VERSION_HEIGHT;
for (const auto &record : cryptonote::HardFork::get_hardcoded_hard_forks(nettype))
{
if (record.version >= version)
{
result = record.height;
break;
}
template <size_t N>
static constexpr bool is_ordered(const std::array<hard_fork, N>& forks) {
if (N == 0 || forks[0].version < 7)
return false;
for (size_t i = 1; i < N; i++) {
auto& hf = forks[i];
auto& prev = forks[i-1];
if ( // [major,snoderevision] pair must be strictly increasing (lexicographically)
std::make_pair(hf.version, hf.snode_revision) <= std::make_pair(prev.version, prev.snode_revision)
// height must be strictly increasing; time must be weakly increasing
|| hf.height <= prev.height || hf.time < prev.time)
return false;
}
return result;
return true;
}
HardFork::ParamsIterator HardFork::get_hardcoded_hard_forks(network_type nettype)
static_assert(is_ordered(mainnet_hard_forks),
"Invalid mainnet hard forks: version must start at 7, major versions and heights must be strictly increasing, and timestamps must be non-decreasing");
static_assert(is_ordered(testnet_hard_forks),
"Invalid testnet hard forks: version must start at 7, versions and heights must be strictly increasing, and timestamps must be non-decreasing");
static_assert(is_ordered(devnet_hard_forks),
"Invalid devnet hard forks: version must start at 7, versions and heights must be strictly increasing, and timestamps must be non-decreasing");
std::vector<hard_fork> fakechain_hardforks;
std::pair<const hard_fork*, const hard_fork*> get_hard_forks(network_type type)
{
if (nettype == MAINNET) return {mainnet_hard_forks, std::end(mainnet_hard_forks)};
else if (nettype == TESTNET) return {testnet_hard_forks, std::end(testnet_hard_forks)};
else if (nettype == DEVNET) return {devnet_hard_forks, std::end(devnet_hard_forks)};
if (type == network_type::MAINNET) return {&mainnet_hard_forks[0], &mainnet_hard_forks[mainnet_hard_forks.size()]};
if (type == network_type::TESTNET) return {&testnet_hard_forks[0], &testnet_hard_forks[testnet_hard_forks.size()]};
if (type == network_type::DEVNET) return {&devnet_hard_forks[0], &devnet_hard_forks[devnet_hard_forks.size()]};
if (type == network_type::FAKECHAIN) return {fakechain_hardforks.data(), fakechain_hardforks.data() + fakechain_hardforks.size()};
return {nullptr, nullptr};
}
HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, time_t forked_time, time_t update_time, uint64_t window_size, uint8_t default_threshold_percent):
db(db),
original_version(original_version),
forked_time(forked_time),
update_time(update_time),
window_size(window_size),
default_threshold_percent(default_threshold_percent),
current_fork_index(0)
{
if (window_size == 0)
throw std::logic_error{"window_size needs to be strictly positive"};
if (default_threshold_percent > 100)
throw std::logic_error{"default_threshold_percent needs to be between 0 and 100"};
}
void HardFork::add_fork(uint8_t version, uint64_t height, uint8_t threshold, time_t time)
{
std::unique_lock l{lock};
// add in order
if (version == 0)
throw std::runtime_error{"Cannot add a hard fork with HF version 0"};
if (!heights.empty()) {
const auto& [v, h, _thresh, t] = heights.back();
if (version <= v)
throw std::runtime_error{"Cannot add hard fork: version(" + std::to_string(version) + ") must be > previous HF version(" + std::to_string(v) + ")"};
if (height <= h)
throw std::runtime_error{"Cannot add hard fork: height(" + std::to_string(height) + ") must be > previous HF height(" + std::to_string(h) + ")"};
if (time < t)
throw std::runtime_error{"Cannot add hard fork: timestamp(" + std::to_string(time) + ") must be >= previous HF timestamp(" + std::to_string(t) + ")"};
}
if (threshold > 100)
throw std::runtime_error{"Cannot add hard fork: invalid threshold (" + std::to_string(threshold) + ")"};
heights.push_back({version, height, threshold, time});
}
void HardFork::add_fork(uint8_t version, uint64_t height, time_t time)
{
add_fork(version, height, default_threshold_percent, time);
}
uint8_t HardFork::get_effective_version(uint8_t voting_version) const
{
if (!heights.empty()) {
uint8_t max_version = heights.back().version;
if (voting_version > max_version)
voting_version = max_version;
}
return voting_version;
}
bool HardFork::do_check(uint8_t block_version, uint8_t voting_version) const
{
return block_version == heights[current_fork_index].version
&& voting_version >= heights[current_fork_index].version;
}
bool HardFork::check(const cryptonote::block &block) const
{
std::unique_lock l{lock};
return do_check(::get_block_version(block), ::get_block_vote(block));
}
bool HardFork::do_check_for_height(uint8_t block_version, uint8_t voting_version, uint64_t height) const
{
int fork_index = get_voted_fork_index(height);
return block_version == heights[fork_index].version
&& voting_version >= heights[fork_index].version;
}
bool HardFork::check_for_height(const cryptonote::block &block, uint64_t height) const
{
std::unique_lock l{lock};
return do_check_for_height(::get_block_version(block), ::get_block_vote(block), height);
}
bool HardFork::add(uint8_t block_version, uint8_t voting_version, uint64_t height)
{
std::unique_lock l{lock};
if (!do_check(block_version, voting_version))
return false;
db.set_hard_fork_version(height, heights[current_fork_index].version);
voting_version = get_effective_version(voting_version);
while (versions.size() >= window_size) {
const uint8_t old_version = versions.front();
assert(last_versions[old_version] >= 1);
last_versions[old_version]--;
versions.pop_front();
}
last_versions[voting_version]++;
versions.push_back(voting_version);
uint8_t voted = get_voted_fork_index(height + 1);
if (voted > current_fork_index) {
current_fork_index = voted;
}
return true;
}
bool HardFork::add(const cryptonote::block &block, uint64_t height)
{
return add(::get_block_version(block), ::get_block_vote(block), height);
}
void HardFork::init()
{
std::unique_lock l{lock};
// add a placeholder for the default version, to avoid special cases
if (heights.empty())
heights.push_back({original_version, 0, 0, 0});
versions.clear();
for (size_t n = 0; n < 256; ++n)
last_versions[n] = 0;
current_fork_index = 0;
// restore state from DB
uint64_t height = db.height();
if (height > window_size)
height -= window_size - 1;
else
height = 1;
rescan_from_chain_height(height);
MDEBUG("init done");
}
uint8_t HardFork::get_block_version(uint64_t height) const
{
const cryptonote::block &block = db.get_block_from_height(height);
return ::get_block_version(block);
}
bool HardFork::reorganize_from_block_height(uint64_t height)
{
std::unique_lock l{lock};
if (height >= db.height())
return false;
bool stop_batch = db.batch_start();
versions.clear();
for (size_t n = 0; n < 256; ++n)
last_versions[n] = 0;
const uint64_t rescan_height = height >= (window_size - 1) ? height - (window_size -1) : 0;
const uint8_t start_version = height == 0 ? original_version : db.get_hard_fork_version(height);
while (current_fork_index > 0 && heights[current_fork_index].version > start_version) {
--current_fork_index;
}
for (uint64_t h = rescan_height; h <= height; ++h) {
cryptonote::block b = db.get_block_from_height(h);
const uint8_t v = get_effective_version(get_block_vote(b));
last_versions[v]++;
versions.push_back(v);
}
uint8_t voted = get_voted_fork_index(height + 1);
if (voted > current_fork_index) {
current_fork_index = voted;
}
const uint64_t bc_height = db.height();
for (uint64_t h = height + 1; h < bc_height; ++h) {
add(db.get_block_from_height(h), h);
}
if (stop_batch)
db.batch_stop();
return true;
}
bool HardFork::reorganize_from_chain_height(uint64_t height)
{
if (height == 0)
return false;
return reorganize_from_block_height(height - 1);
}
bool HardFork::rescan_from_block_height(uint64_t height)
{
std::unique_lock l{lock};
db_rtxn_guard rtxn_guard(&db);
if (height >= db.height())
return false;
versions.clear();
for (size_t n = 0; n < 256; ++n)
last_versions[n] = 0;
for (uint64_t h = height; h < db.height(); ++h) {
cryptonote::block b = db.get_block_from_height(h);
const uint8_t v = get_effective_version(get_block_vote(b));
last_versions[v]++;
versions.push_back(v);
}
uint8_t lastv = db.get_hard_fork_version(db.height() - 1);
current_fork_index = 0;
while (current_fork_index + 1 < heights.size() && heights[current_fork_index].version != lastv)
++current_fork_index;
uint8_t voted = get_voted_fork_index(db.height());
if (voted > current_fork_index) {
current_fork_index = voted;
}
return true;
}
bool HardFork::rescan_from_chain_height(uint64_t height)
{
if (height == 0)
return false;
return rescan_from_block_height(height - 1);
}
void HardFork::on_block_popped(uint64_t nblocks)
{
CHECK_AND_ASSERT_THROW_MES(nblocks > 0, "nblocks must be greater than 0");
std::unique_lock l{lock};
const uint64_t new_chain_height = db.height();
const uint64_t old_chain_height = new_chain_height + nblocks;
uint8_t version;
for (uint64_t height = old_chain_height - 1; height >= new_chain_height; --height)
{
version = versions.back();
last_versions[version]--;
versions.pop_back();
version = db.get_hard_fork_version(height);
versions.push_front(version);
last_versions[version]++;
}
// does not take voting into account
for (current_fork_index = heights.size() - 1; current_fork_index > 0; --current_fork_index)
if (new_chain_height >= heights[current_fork_index].height)
std::pair<std::optional<uint64_t>, std::optional<uint64_t>>
get_hard_fork_heights(network_type nettype, uint8_t version) {
std::pair<std::optional<uint64_t>, std::optional<uint64_t>> found;
for (auto [it, end] = get_hard_forks(nettype); it != end; it++) {
if (it->version > version) { // This (and anything else) are in the future
if (found.first) // Found something suitable in the previous iteration, so one before this hf is the max
found.second = it->height - 1;
break;
}
int HardFork::get_voted_fork_index(uint64_t height) const
{
std::unique_lock l{lock};
uint32_t accumulated_votes = 0;
for (int n = heights.size() - 1; n >= 0; --n) {
uint8_t v = heights[n].version;
accumulated_votes += last_versions[v];
uint32_t threshold = (window_size * heights[n].threshold + 99) / 100;
if (height >= heights[n].height && accumulated_votes >= threshold) {
return n;
} else if (it->version == version) {
found.first = it->height;
}
}
return current_fork_index;
return found;
}
HardFork::State HardFork::get_state(time_t t) const
{
std::unique_lock l{lock};
uint8_t hard_fork_ceil(network_type nettype, uint8_t version) {
auto [it, end] = get_hard_forks(nettype);
for (; it != end; it++)
if (it->version >= version)
return it->version;
// no hard forks setup yet
if (heights.size() <= 1)
return Ready;
time_t t_last_fork = heights.back().time;
if (t >= t_last_fork + forked_time)
return LikelyForked;
if (t >= t_last_fork + update_time)
return UpdateNeeded;
return Ready;
return version;
}
HardFork::State HardFork::get_state() const
{
return get_state(time(NULL));
}
uint8_t HardFork::get(uint64_t height) const
{
std::unique_lock l{lock};
if (height > db.height()) {
assert(false);
return INVALID_HF_VERSION;
}
if (height == db.height()) {
return get_current_version();
}
return db.get_hard_fork_version(height);
}
uint8_t HardFork::get_current_version() const
{
std::unique_lock l{lock};
return heights[current_fork_index].version;
}
uint8_t HardFork::get_ideal_version() const
{
std::unique_lock l{lock};
return heights.back().version;
}
uint8_t HardFork::get_ideal_version(uint64_t height) const
{
std::unique_lock l{lock};
for (unsigned int n = heights.size() - 1; n > 0; --n) {
if (height >= heights[n].height) {
return heights[n].version;
}
}
return original_version;
}
uint64_t HardFork::get_earliest_ideal_height_for_version(uint8_t version) const
{
uint64_t height = std::numeric_limits<uint64_t>::max();
for (auto i = heights.rbegin(); i != heights.rend(); ++i) {
if (i->version >= version) {
height = i->height;
} else {
std::pair<uint8_t, uint8_t>
get_network_version_revision(network_type nettype, uint64_t height) {
std::pair<uint8_t, uint8_t> result;
for (auto [it, end] = get_hard_forks(nettype); it != end; it++) {
if (it->height <= height)
result = {it->version, it->snode_revision};
else
break;
}
}
return height;
return result;
}
uint8_t HardFork::get_next_version() const
bool is_hard_fork_at_least(network_type type, uint8_t version, uint64_t height) {
return get_network_version(type, height) >= version;
}
std::pair<uint8_t, uint8_t>
get_ideal_block_version(network_type nettype, uint64_t height)
{
std::unique_lock l{lock};
uint64_t height = db.height();
for (auto i = heights.rbegin(); i != heights.rend(); ++i) {
if (height >= i->height) {
return (i == heights.rbegin() ? i : (i - 1))->version;
}
std::pair<uint8_t, uint8_t> result;
for (auto [it, end] = get_hard_forks(nettype); it != end; it++) {
if (it->height <= height)
result.first = it->version;
result.second = it->version;
}
return original_version;
return result;
}
bool HardFork::get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const
{
std::unique_lock l{lock};
const uint8_t current_version = heights[current_fork_index].version;
const bool enabled = current_version >= version;
window = versions.size();
votes = 0;
for (size_t n = version; n < 256; ++n)
votes += last_versions[n];
threshold = (window * heights[current_fork_index].threshold + 99) / 100;
//assert((votes >= threshold) == enabled);
earliest_height = get_earliest_ideal_height_for_version(version);
voting = heights.back().version;
return enabled;
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2019, The Monero Project
// Copyright (c) 2018-2021, The Loki Project
//
// All rights reserved.
//
@ -29,263 +29,72 @@
#pragma once
#include "cryptonote_basic/cryptonote_basic.h"
#include <mutex>
namespace cryptonote
{
class BlockchainDB;
class HardFork
{
public:
struct Params
{
uint8_t version;
uint64_t height;
uint8_t threshold;
time_t time;
};
constexpr static uint8_t INVALID_HF_VERSION = 255;
constexpr static uint64_t INVALID_HF_VERSION_HEIGHT = static_cast<uint64_t>(-1);
typedef enum {
LikelyForked,
UpdateNeeded,
Ready,
} State;
static const uint64_t DEFAULT_ORIGINAL_VERSION_TILL_HEIGHT = 0; // <= actual height
static const time_t DEFAULT_FORKED_TIME = 31557600; // a year in seconds
static const time_t DEFAULT_UPDATE_TIME = 31557600 / 2;
static const uint64_t DEFAULT_WINDOW_SIZE = 10080; // supermajority window check length - a week
static const uint8_t DEFAULT_THRESHOLD_PERCENT = 80;
struct ParamsIterator
{
const Params *begin_, *end_;
constexpr Params const *begin() { return begin_; };
constexpr Params const *end() { return end_; };
};
// NOTE: Returns INVALID_HF_VERSION_HEIGHT if version not specified for nettype
static uint64_t get_hardcoded_hard_fork_height(network_type nettype, cryptonote::network_version version);
static ParamsIterator get_hardcoded_hard_forks(network_type nettype);
/**
* @brief creates a new HardFork object
*
* @param original_version the block version for blocks 0 through to the first fork
* @param forked_time the time in seconds before thinking we're forked
* @param update_time the time in seconds before thinking we need to update
* @param window_size the size of the window in blocks to consider for version voting
* @param default_threshold_percent the size of the majority in percents
*/
HardFork(cryptonote::BlockchainDB &db, uint8_t original_version = 1, time_t forked_time = DEFAULT_FORKED_TIME, time_t update_time = DEFAULT_UPDATE_TIME, uint64_t window_size = DEFAULT_WINDOW_SIZE, uint8_t default_threshold_percent = DEFAULT_THRESHOLD_PERCENT);
/**
* @brief add a new hardfork height
*
* returns true if no error, false otherwise
*
* @param version the major block version for the fork, must be > 0 and > the last-added hf
* @param height The height the hardfork takes effect; must be > the last-added hf
* @param threshold The threshold of votes needed for this fork (0-100)
* @param time Approximate time of the hardfork (seconds since epoch); must be >= the timestamp
* of the last-added hf
*
* @throws std::invalid_argument if any parameters are invalid
*/
void add_fork(uint8_t version, uint64_t height, uint8_t threshold, time_t time);
/**
* @brief add a new hardfork height
*
* returns true if no error, false otherwise
*
* @param version the major block version for the fork
* @param height The height the hardfork takes effect
* @param time Approximate time of the hardfork (seconds since epoch)
*
* @throws std::invalid_argument if any parameters are invalid
*/
void add_fork(uint8_t version, uint64_t height, time_t time);
/**
* @brief initialize the object
*
* Must be done after adding all the required hardforks via add above
*/
void init();
/**
* @brief check whether a new block would be accepted
*
* returns true if the block is accepted, false otherwise
*
* @param block the new block
*
* This check is made by add. It is exposed publicly to allow
* the caller to inexpensively check whether a block would be
* accepted or rejected by its version number. Indeed, if this
* check could only be done as part of add, the caller would
* either have to add the block to the blockchain first, then
* call add, then have to pop the block from the blockchain if
* its version did not satisfy the hard fork requirements, or
* call add first, then, if the hard fork requirements are met,
* add the block to the blockchain, upon which a failure (the
* block being invalid, double spending, etc) would cause the
* hardfork object to reorganize.
*/
bool check(const cryptonote::block &block) const;
/**
* @brief same as check, but for a particular height, rather than the top
*
* NOTE: this does not play well with voting, and relies on voting to be
* disabled (that is, forks happen on the scheduled date, whether or not
* enough blocks have voted for the fork).
*
* returns true if no error, false otherwise
*
* @param block the new block
* @param height which height to check for
*/
bool check_for_height(const cryptonote::block &block, uint64_t height) const;
/**
* @brief add a new block
*
* returns true if no error, false otherwise
*
* @param block the new block
*/
bool add(const cryptonote::block &block, uint64_t height);
/**
* @brief called when the blockchain is reorganized
*
* This will rescan the blockchain to determine which hard forks
* have been triggered
*
* returns true if no error, false otherwise
*
* @param blockchain the blockchain
* @param height of the last block kept from the previous blockchain
*/
bool reorganize_from_block_height(uint64_t height);
bool reorganize_from_chain_height(uint64_t height);
/**
* @brief called when one or more blocks are popped from the blockchain
*
* The current fork will be updated by looking up the db,
* which is much cheaper than recomputing everything
*
* @param new_chain_height the height of the chain after popping
*/
void on_block_popped(uint64_t new_chain_height);
/**
* @brief returns current state at the given time
*
* Based on the approximate time of the last known hard fork,
* estimate whether we need to update, or if we're way behind
*
* @param t the time to consider
*/
State get_state(time_t t) const;
State get_state() const;
/**
* @brief returns the hard fork version for the given block height
*
* @param height height of the block to check
*/
uint8_t get(uint64_t height) const;
/**
* @brief returns the latest "ideal" version
*
* This is the latest version that's been scheduled
*/
uint8_t get_ideal_version() const;
/**
* @brief returns the "ideal" version for a given height
*
* @param height height of the block to check
*/
uint8_t get_ideal_version(uint64_t height) const;
/**
* @brief returns the next version
*
* This is the version which will we fork to next
*/
uint8_t get_next_version() const;
/**
* @brief returns the current version
*
* This is the latest version that's past its trigger date and had enough votes
* at one point in the past.
*/
uint8_t get_current_version() const;
/**
* @brief returns the earliest block a given version may activate
*/
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const;
/**
* @brief returns information about current voting state
*
* returns true if the given version is enabled (ie, the current version
* is at least the passed version), false otherwise
*
* @param version the version to check voting for
* @param window the number of blocks considered in voting
* @param votes number of votes for next version
* @param threshold number of votes needed to switch to next version
* @param earliest_height earliest height at which the version can take effect
*/
bool get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const;
/**
* @brief returns the size of the voting window in blocks
*/
uint64_t get_window_size() const { return window_size; }
private:
uint8_t get_block_version(uint64_t height) const;
bool do_check(uint8_t block_version, uint8_t voting_version) const;
bool do_check_for_height(uint8_t block_version, uint8_t voting_version, uint64_t height) const;
int get_voted_fork_index(uint64_t height) const;
uint8_t get_effective_version(uint8_t voting_version) const;
bool add(uint8_t block_version, uint8_t voting_version, uint64_t height);
bool rescan_from_block_height(uint64_t height);
bool rescan_from_chain_height(uint64_t height);
private:
BlockchainDB &db;
time_t forked_time;
time_t update_time;
uint64_t window_size;
uint8_t default_threshold_percent;
uint8_t original_version;
std::vector<Params> heights;
std::deque<uint8_t> versions; /* rolling window of the last N blocks' versions */
unsigned int last_versions[256]; /* count of the block versions in the last N blocks */
uint32_t current_fork_index;
mutable std::recursive_mutex lock;
// Defines where hard fork (i.e. new minimum network versions) begin
struct hard_fork {
uint8_t version; // Blockchain major version
uint8_t snode_revision; // Snode revision for enforcing non-blockchain-breaking mandatory service node updates
uint64_t height;
time_t time;
};
// Stick your fake hard forks in here if you're into that sort of thing.
extern std::vector<hard_fork> fakechain_hardforks;
// Returns an iteratable range over hard fork values for the given network.
std::pair<const hard_fork*, const hard_fork*> get_hard_forks(network_type type);
// Returns the height range for which the given block/network version is valid. Returns a pair of
// heights {A, B} where A/B is the first/last height at which the version is acceptable. Returns
// nullopt for A if the version indicates a hardfork we do not know about (i.e. we are likely
// outdated), and returns nullopt for B if the version indicates that top network version we know
// about (i.e. there is no subsequent hardfork scheduled).
std::pair<std::optional<uint64_t>, std::optional<uint64_t>>
get_hard_fork_heights(network_type type, uint8_t version);
// Returns the lowest network version >= the given version, that is, it rounds up missing hf table
// entries to the next largest entry. Typically this returns the network version itself, but if
// some versions are skipped (particularly on testnet/devnet/fakechain) then this will return the
// next version that does exist in the hard fork list. If there is no >= value in the hard fork
// table then this returns the given hard fork value itself.
//
// For example, if the HF list contains hf versions {7,8,14} then:
// hard_fork_ceil(7) == 7
// hard_fork_ceil(8) == 8
// hard_fork_ceil(9) == 14
// ...
// hard_fork_ceil(14) == 14
// hard_fork_ceil(15) == 15
uint8_t hard_fork_ceil(network_type type, uint8_t version);
// Returns true if the given height is sufficiently high to be at or after the given hard fork
// version.
bool is_hard_fork_at_least(network_type type, uint8_t version, uint64_t height);
// Returns the active network version and snode revision for the given height.
std::pair<uint8_t, uint8_t>
get_network_version_revision(network_type nettype, uint64_t height);
// Returns the network (i.e. block) version for the given height.
inline uint8_t get_network_version(network_type nettype, uint64_t height) {
return get_network_version_revision(nettype, height).first;
}
// Returns the first height at which the given network version rules become active. This is
// a shortcut for `get_hard_fork_heights(type, hard_fork_ceil(type, version)).first`, i.e. it
// returns the first height at which `version` rules become active (even if they became active at
// a hard fork > the given value).
inline std::optional<uint64_t> hard_fork_begins(network_type type, uint8_t version) {
return get_hard_fork_heights(type, hard_fork_ceil(type, version)).first;
}
// Returns the "ideal" network version that we want to use on blocks we create, which is to use
// the required major version for major version and the maximum major version we know about as
// minor version. If this seems a bit silly, it is, and will be changed in the future.
std::pair<uint8_t, uint8_t> get_ideal_block_version(network_type nettype, uint64_t height);
} // namespace cryptonote

View File

@ -30,6 +30,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <boost/endian/conversion.hpp>
@ -37,6 +38,7 @@
#include "common/hex.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "ringct/rctTypes.h"
#include "tx_pool.h"
@ -109,7 +111,7 @@ 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_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_db(), m_tx_pool(tx_pool), 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),
@ -298,8 +300,8 @@ uint64_t Blockchain::get_current_blockchain_height(bool lock) const
//------------------------------------------------------------------
bool Blockchain::load_missing_blocks_into_oxen_subsystems()
{
uint64_t const snl_height = std::max(m_hardfork->get_earliest_ideal_height_for_version(network_version_9_service_nodes), m_service_node_list.height() + 1);
uint64_t const ons_height = std::max(m_hardfork->get_earliest_ideal_height_for_version(network_version_15_ons), m_ons_db.height() + 1);
uint64_t const snl_height = std::max(hard_fork_begins(m_nettype, network_version_9_service_nodes).value_or(0), m_service_node_list.height() + 1);
uint64_t const ons_height = std::max(hard_fork_begins(m_nettype, network_version_15_ons).value_or(0), m_ons_db.height() + 1);
uint64_t const end_height = m_db->height();
uint64_t const start_height = std::min(end_height, std::min(ons_height, snl_height));
@ -444,28 +446,9 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *ons_db, const network_type nett
m_offline = offline;
m_fixed_difficulty = fixed_difficulty;
if (m_hardfork == nullptr)
m_hardfork = new HardFork(*db, 7);
if (test_options) // Fakechain mode or in integration testing mode we're overriding hardfork dates
{
for (auto n = 0u; n < test_options->hard_forks.size(); ++n)
{
const auto& hf = test_options->hard_forks.at(n);
m_hardfork->add_fork(hf.first, hf.second, 0, n + 1);
}
}
else
{
for (const auto &record : HardFork::get_hardcoded_hard_forks(m_nettype))
{
m_hardfork->add_fork(record.version, record.height, record.threshold, record.time);
}
}
m_hardfork->init();
m_db->set_hard_fork(m_hardfork);
if (test_options) // Fakechain mode or in integration testing mode we're overriding hardfork heights
fakechain_hardforks = test_options->hard_forks;
// if the blockchain is new, add the genesis block
// this feels kinda kludgy to do it this way, but can be looked at later.
@ -519,7 +502,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *ons_db, const network_type nett
uint64_t top_height;
const crypto::hash top_id = m_db->top_block_hash(&top_height);
const block top_block = m_db->get_top_block();
const uint8_t ideal_hf_version = get_ideal_hard_fork_version(top_height);
const uint8_t ideal_hf_version = get_network_version(top_height);
if (ideal_hf_version <= 1 || ideal_hf_version == top_block.major_version)
{
if (num_popped_blocks > 0)
@ -556,7 +539,6 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *ons_db, const network_type nett
if (num_popped_blocks > 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();
}
@ -592,16 +574,6 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *ons_db, const network_type nett
return true;
}
//------------------------------------------------------------------
bool Blockchain::init(BlockchainDB* db, HardFork*& hf, sqlite3 *ons_db, const network_type nettype, bool offline)
{
if (hf != nullptr)
m_hardfork = hf;
bool res = init(db, ons_db, nettype, offline, NULL);
if (hf == nullptr)
hf = m_hardfork;
return res;
}
//------------------------------------------------------------------
bool Blockchain::store_blockchain()
{
LOG_PRINT_L3("Blockchain::" << __func__);
@ -663,10 +635,8 @@ bool Blockchain::deinit()
LOG_ERROR("There was an issue closing/storing the blockchain, shutting down now to prevent issues!");
}
delete m_hardfork;
m_hardfork = NULL;
delete m_db;
m_db = NULL;
m_db = nullptr;
return true;
}
//------------------------------------------------------------------
@ -749,8 +719,6 @@ block Blockchain::pop_block_from_blockchain()
throw;
}
// make sure the hard fork object updates its current version
m_hardfork->on_block_popped(1);
m_ons_db.block_detach(*this, m_db->height());
// return transactions from popped block to the tx_pool
@ -766,10 +734,7 @@ block Blockchain::pop_block_from_blockchain()
{
cryptonote::tx_verification_context tvc{};
// FIXME: HardFork
// Besides the below, popping a block should also remove the last entry
// in hf_versions.
uint8_t version = get_ideal_hard_fork_version(m_db->height());
uint8_t version = get_network_version(m_db->height());
// We assume that if they were in a block, the transactions are already
// known to the network as a whole. However, if we had mined that block,
@ -804,7 +769,6 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
invalidate_block_template_cache();
m_db->reset();
m_db->drop_alt_blocks();
m_hardfork->init();
for (InitHook* hook : m_init_hooks)
hook->init();
@ -967,7 +931,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block(bool pulse)
if (pulse)
return PULSE_FIXED_DIFFICULTY;
uint8_t const hf_version = get_current_hard_fork_version();
uint8_t const hf_version = get_network_version();
crypto::hash top_hash = get_tail_id();
{
std::unique_lock diff_lock{m_cache.m_difficulty_lock};
@ -989,7 +953,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block(bool pulse)
uint64_t 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));
difficulty_mode(m_nettype, chain_height));
m_cache.m_timestamps_and_difficulties_height = chain_height;
@ -1037,9 +1001,6 @@ bool Blockchain::rollback_blockchain_switching(const std::list<block_and_checkpo
hook->blockchain_detached(rollback_height, false /*by_pop_blocks*/);
load_missing_blocks_into_oxen_subsystems();
// make sure the hard fork object updates its current version
m_hardfork->reorganize_from_chain_height(rollback_height);
//return back original chain
for (auto& entry : original_chain)
{
@ -1048,7 +1009,6 @@ bool Blockchain::rollback_blockchain_switching(const std::list<block_and_checkpo
CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!");
}
m_hardfork->reorganize_from_chain_height(rollback_height);
MINFO("Rollback to height " << rollback_height << " was successful.");
if (!original_chain.empty())
{
@ -1159,7 +1119,6 @@ bool Blockchain::switch_to_alternative_blockchain(const std::list<block_extended
m_db->remove_alt_block(cryptonote::get_block_hash(bei.bl));
}
m_hardfork->reorganize_from_chain_height(split_height);
get_block_longhash_reorg(split_height);
std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify;
@ -1196,10 +1155,7 @@ difficulty_type Blockchain::get_difficulty_for_alternative_chain(const std::list
if (alt_chain.size())
before_hf16 = alt_chain.back().bl.major_version < network_version_16_pulse;
else
{
static const uint64_t hf16_height = HardFork::get_hardcoded_hard_fork_height(m_nettype, cryptonote::network_version_16_pulse);
before_hf16 = get_current_blockchain_height() < hf16_height;
}
before_hf16 = !is_hard_fork_at_least(m_nettype, cryptonote::network_version_16_pulse, get_current_blockchain_height());
block_count = DIFFICULTY_BLOCKS_COUNT(before_hf16);
}
@ -1262,7 +1218,7 @@ difficulty_type Blockchain::get_difficulty_for_alternative_chain(const std::list
return next_difficulty_v2(timestamps,
cumulative_difficulties,
tools::to_seconds(TARGET_BLOCK_TIME),
difficulty_mode(m_nettype, get_current_hard_fork_version(), height));
difficulty_mode(m_nettype, height));
}
//------------------------------------------------------------------
// This function does a sanity check on basic things that all miner
@ -1551,8 +1507,9 @@ bool Blockchain::create_block_template_internal(block& b, const crypto::hash *fr
{
height = alt_chain.back().height + 1;
}
b.major_version = m_hardfork->get_ideal_version(height);
b.minor_version = m_hardfork->get_ideal_version();
auto [maj, min] = get_ideal_block_version(m_nettype, height);
b.major_version = maj;
b.minor_version = min;
b.prev_id = *from_block;
// cheat and use the weight of the block we start from, virtually certain to be acceptable
@ -1578,8 +1535,9 @@ 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();
auto [maj, min] = get_ideal_block_version(m_nettype, height);
b.major_version = maj;
b.minor_version = min;
b.prev_id = get_tail_id();
median_weight = m_current_block_cumul_weight_limit / 2;
diffic = get_difficulty_for_next_block(!info.is_miner);
@ -2862,6 +2820,12 @@ bool Blockchain::add_block_as_invalid(cryptonote::block const &block)
MINFO("BLOCK ADDED AS INVALID: " << (*i_res.first) << std::endl << ", prev_id=" << block.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size());
return true;
}
uint8_t Blockchain::get_network_version(std::optional<uint64_t> height) const {
if (!height) height = get_current_blockchain_height();
return cryptonote::get_network_version(m_nettype, *height);
}
//------------------------------------------------------------------
void Blockchain::flush_invalid_blocks()
{
@ -3067,7 +3031,8 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
return true;
// from v10, allow bulletproofs
const uint8_t hf_version = m_hardfork->get_current_version();
auto height = get_current_blockchain_height();
const uint8_t hf_version = get_network_version(height);
if (hf_version < network_version_10_bulletproofs) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
@ -3077,27 +3042,17 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
return false;
}
}
else
else if (rct::is_rct_borromean(tx.rct_signatures.type))
{
const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type);
if (borromean)
// The HF10 block height itself was allowed to (and did) have a Borromean tx as an exception
// to the HF10 rules so that a borderline tx didn't end up unmineable, hence the strict `>`
// here:
if (auto hf10_height = hard_fork_begins(m_nettype, network_version_10_bulletproofs);
hf10_height && height > *hf10_height)
{
uint64_t hf10_height = m_hardfork->get_earliest_ideal_height_for_version(network_version_10_bulletproofs);
uint64_t curr_height = this->get_current_blockchain_height();
if (curr_height == hf10_height)
{
// NOTE(oxen): Allow the hardforking block to contain a borromean proof
// incase there were some transactions in the TX Pool that were
// generated pre-HF10 rules. Note, this isn't bulletproof. If there were
// more than 1 blocks worth of borromean proof TX's sitting in the pool
// this isn't going to work.
}
else
{
MERROR_VER("Borromean range proofs are not allowed after v10");
tvc.m_invalid_output = true;
return false;
}
MERROR_VER("Borromean range proofs are not allowed after v10");
tvc.m_invalid_output = true;
return false;
}
}
@ -3136,10 +3091,11 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// Require CLSAGs starting 10 blocks after the CLSAG-enabling hard fork (the 10 block buffer is to
// allow staggling txes around fork time to still make it into a block).
// NB: there *are* such txes on mainnet in this 10-block window so this code has to stay.
if (hf_version >= HF_VERSION_CLSAG
&& tx.rct_signatures.type < rct::RCTType::CLSAG
&& tx.version >= txversion::v4_tx_types && tx.is_transfer()
&& (hf_version > HF_VERSION_CLSAG || get_current_blockchain_height() >= 10 + m_hardfork->get_earliest_ideal_height_for_version(HF_VERSION_CLSAG)))
&& (hf_version > HF_VERSION_CLSAG || height >= 10 + *hard_fork_begins(m_nettype, HF_VERSION_CLSAG)))
{
MERROR_VER("Ringct type " << (unsigned)tx.rct_signatures.type << " is not allowed from v" << HF_VERSION_CLSAG);
tvc.m_invalid_output = true;
@ -3256,7 +3212,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
pmax_used_block_height = &max_used_block_height;
*pmax_used_block_height = 0;
const auto hf_version = m_hardfork->get_current_version();
const auto hf_version = get_network_version();
// Min/Max Type/Version Check
{
@ -3743,7 +3699,7 @@ byte_and_output_fees Blockchain::get_dynamic_base_fee(uint64_t block_reward, siz
//------------------------------------------------------------------
bool Blockchain::check_fee(size_t tx_weight, size_t tx_outs, uint64_t fee, uint64_t burned, const tx_pool_options &opts) const
{
const uint8_t version = get_current_hard_fork_version();
const uint8_t version = get_network_version();
const uint64_t blockchain_height = get_current_blockchain_height();
uint64_t median = m_current_block_cumul_weight_limit / 2;
@ -3802,7 +3758,7 @@ bool Blockchain::check_fee(size_t tx_weight, size_t tx_outs, uint64_t fee, uint6
//------------------------------------------------------------------
byte_and_output_fees Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
const uint8_t version = get_network_version();
const uint64_t db_height = m_db->height();
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
@ -3964,7 +3920,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
//------------------------------------------------------------------
void Blockchain::return_tx_to_pool(std::vector<std::pair<transaction, blobdata>> &txs)
{
uint8_t version = get_current_hard_fork_version();
uint8_t version = get_network_version();
for (auto& tx : txs)
{
cryptonote::tx_verification_context tvc{};
@ -4020,7 +3976,7 @@ Blockchain::block_pow_verified Blockchain::verify_block_pow(cryptonote::block co
// Hence this hack: starting at that block until the next hard fork, we allow a slight grace
// (0.2%) on the required difficulty (but we don't *change* the actual difficulty value used for
// diff calculation).
if (cryptonote::get_block_height(blk) >= 526483 && m_hardfork->get_current_version() < network_version_16_pulse)
if (cryptonote::get_block_height(blk) >= 526483 && get_network_version() < network_version_16_pulse)
difficulty = (difficulty * 998) / 1000;
CHECK_AND_ASSERT_MES(difficulty, result, "!!!!!!!!! difficulty overhead !!!!!!!!!");
@ -4102,7 +4058,7 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block
const crypto::hash blk_hash = cryptonote::get_block_hash(blk);
const uint64_t blk_height = cryptonote::get_block_height(blk);
const uint64_t chain_height = get_current_blockchain_height();
const uint8_t hf_version = get_current_hard_fork_version();
const uint8_t hf_version = get_network_version();
if (alt_block)
{
@ -4119,9 +4075,11 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block
}
// this is a cheap test
if (!m_hardfork->check_for_height(blk, blk_height))
// HF19 TODO: remove the requirement that minor_version must be >= network version
if (auto v = get_network_version(blk_height); blk.major_version != v || blk.minor_version < v)
{
LOG_PRINT_L1("Block with id: " << blk_hash << ", has old version: " << +blk.major_version << ", current: " << +hf_version << " for height " << blk_height);
LOG_PRINT_L1("Block with id: " << blk_hash << ", has invalid version " << +blk.major_version << "." << +blk.minor_version <<
"; current: " << +v << "." << +v << " for height " << blk_height);
return false;
}
}
@ -4134,21 +4092,28 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block
return false;
}
for (static bool seen_future_version = false;
!seen_future_version && blk.major_version > m_hardfork->get_ideal_version();
seen_future_version = true)
auto required_major_version = get_network_version();
if (blk.major_version > required_major_version)
{
const el::Level level = el::Level::Warning;
MCLOG_RED(level, "global", "**********************************************************************");
MCLOG_RED(level, "global", "A block was seen on the network with a version higher than the last");
MCLOG_RED(level, "global", "known one. This may be an old version of the daemon, and a software");
MCLOG_RED(level, "global", "update may be required to sync further. Try running: update check");
MCLOG_RED(level, "global", "**********************************************************************");
// Show a warning at most once every 5 minutes if we are receiving future hf blocks
std::lock_guard lock{last_outdated_warning_mutex};
if (auto now = std::chrono::steady_clock::now(); now > last_outdated_warning + 5min)
{
last_outdated_warning = now;
const el::Level level = el::Level::Warning;
MCLOG_RED(level, "global", "**********************************************************************");
MCLOG_RED(level, "global", "A block was seen on the network with a version higher than the last");
MCLOG_RED(level, "global", "known one. This may be an old version of the daemon, and a software");
MCLOG_RED(level, "global", "update may be required to sync further. Try running: update check");
MCLOG_RED(level, "global", "**********************************************************************");
}
}
if (!m_hardfork->check(blk))
// HF19 TODO: remove the requirement that minor_version must be >= network version
if (blk.major_version != required_major_version || blk.minor_version < required_major_version)
{
MGINFO_RED("Block with id: " << blk_hash << ", has old version: " << +blk.major_version << ", current: " << (unsigned)m_hardfork->get_current_version());
MGINFO_RED("Block with id: " << blk_hash << ", has invalid version " << +blk.major_version << "." << +blk.minor_version <<
"; current: " << +required_major_version << "." << +required_major_version << " for height " << blk_height);
return false;
}
@ -4365,7 +4330,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
TIME_MEASURE_START(vmt);
uint64_t base_reward = 0;
uint64_t already_generated_coins = chain_height ? m_db->get_block_already_generated_coins(chain_height - 1) : 0;
if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, m_hardfork->get_current_version()))
if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, get_network_version()))
{
MGINFO_RED("Block " << (chain_height - 1) << " with id: " << id << " has incorrect miner transaction");
bvc.m_verifivation_failed = true;
@ -4552,8 +4517,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
const uint64_t db_height = m_db->height();
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
const uint8_t hf_version = get_current_hard_fork_version();
if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
if (!is_hard_fork_at_least(m_nettype, HF_VERSION_LONG_TERM_BLOCK_WEIGHT, get_current_blockchain_height()))
return block_weight;
uint64_t long_term_median = get_long_term_block_weight_median(db_height - nblocks, nblocks);
@ -4573,7 +4537,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
// when we reach this, the last hf version is not yet written to the db
const uint64_t db_height = m_db->height();
const uint8_t hf_version = get_current_hard_fork_version();
const uint8_t hf_version = get_network_version();
uint64_t full_reward_zone = get_min_block_weight(hf_version);
if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
@ -4976,7 +4940,7 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &reward) const
{
reward = 0;
auto hard_fork_version = get_ideal_hard_fork_version(height);
auto hard_fork_version = get_network_version(height);
if (hard_fork_version <= network_version_9_service_nodes)
{
return true;
@ -5453,16 +5417,6 @@ void Blockchain::safesyncmode(const bool onoff)
}
}
HardFork::State Blockchain::get_hard_fork_state() const
{
return m_hardfork->get_state();
}
bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const
{
return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting);
}
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const
{
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count);

View File

@ -61,7 +61,6 @@
#include "cryptonote_basic/verification_context.h"
#include "crypto/hash.h"
#include "checkpoints/checkpoints.h"
#include "cryptonote_basic/hardfork.h"
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_core/oxen_name_system.h"
#include "pulse.h"
@ -149,19 +148,7 @@ namespace cryptonote
*
* @return true on success, false if any initialization steps fail
*/
bool init(BlockchainDB* db, sqlite3 *ons_db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @brief Initialize the Blockchain state
*
* @param db a pointer to the backing store to use for the blockchain
* @param hf a structure containing hardfork information
* @param nettype network type
* @param offline true if running offline, else false
*
* @return true on success, false if any initialization steps fail
*/
bool init(BlockchainDB* db, HardFork*& hf, sqlite3 *ons_db, const network_type nettype = MAINNET, bool offline = false);
bool init(BlockchainDB* db, sqlite3 *ons_db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = nullptr, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @brief Uninitializes the blockchain state
@ -822,72 +809,12 @@ namespace cryptonote
void set_show_time_stats(bool stats) { m_show_time_stats = stats; }
/**
* @brief gets the hardfork voting state object
*
* @return the HardFork object
*/
HardFork::State get_hard_fork_state() const;
/**
* @brief gets the current hardfork version in use/voted for
* @brief gets the network hard fork version of the blockchain at the given height.
* If height is omitted, uses the current blockchain height.
*
* @return the version
*/
uint8_t get_current_hard_fork_version() const { return m_hardfork->get_current_version(); }
/**
* @brief returns the newest hardfork version known to the blockchain
*
* @return the version
*/
uint8_t get_ideal_hard_fork_version() const { return m_hardfork->get_ideal_version(); }
/**
* @brief returns the next hardfork version
*
* @return the version
*/
uint8_t get_next_hard_fork_version() const { return m_hardfork->get_next_version(); }
/**
* @brief returns the newest hardfork version voted to be enabled
* as of a certain height
*
* @param height the height for which to check version info
*
* @return the version
*/
uint8_t get_ideal_hard_fork_version(uint64_t height) const { return m_hardfork->get_ideal_version(height); }
/**
* @brief returns the actual hardfork version for a given block height
*
* @param height the height for which to check version info
*
* @return the version
*/
uint8_t get_hard_fork_version(uint64_t height) const { return m_hardfork->get(height); }
/**
* @brief returns the earliest block a given version may activate
*
* @return the height
*/
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return m_hardfork->get_earliest_ideal_height_for_version(version); }
/**
* @brief get information about hardfork voting for a version
*
* @param version the version in question
* @param window the size of the voting window
* @param votes the number of votes to enable <version>
* @param threshold the number of votes required to enable <version>
* @param earliest_height the earliest height at which <version> is allowed
* @param voting which version this node is voting for/using
*
* @return whether the version queried is enabled
*/
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const;
uint8_t get_network_version(std::optional<uint64_t> height = std::nullopt) const;
/**
* @brief remove transactions from the transaction pool (if present)
@ -1194,7 +1121,6 @@ namespace cryptonote
std::vector<AltBlockAddedHook*> m_alt_block_added_hooks;
checkpoints m_checkpoints;
HardFork *m_hardfork;
network_type m_nettype;
bool m_offline;
@ -1222,6 +1148,9 @@ namespace cryptonote
uint64_t m_prepare_nblocks;
std::vector<block> *m_prepare_blocks;
std::chrono::steady_clock::time_point last_outdated_warning = {};
std::mutex last_outdated_warning_mutex;
/**
* @brief collects the keys for all outputs being "spent" as an input
*

View File

@ -60,6 +60,7 @@ extern "C" {
#include "epee/warnings.h"
#include "crypto/crypto.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/hardfork.h"
#include "epee/misc_language.h"
#include <csignal>
#include "checkpoints/checkpoints.h"
@ -791,13 +792,14 @@ namespace cryptonote
MERROR("Failed to parse block rate notify spec");
}
std::vector<std::pair<uint8_t, uint64_t>> regtest_hard_forks;
for (uint8_t hf = cryptonote::network_version_7; hf < cryptonote::network_version_count; hf++)
regtest_hard_forks.emplace_back(hf, regtest_hard_forks.size() + 1);
const cryptonote::test_options regtest_test_options = {
std::move(regtest_hard_forks),
0
};
cryptonote::test_options regtest_test_options{};
for (auto [it, end] = get_hard_forks(network_type::MAINNET);
it != end;
it++) {
regtest_test_options.hard_forks.push_back(hard_fork{
it->version, it->snode_revision, regtest_test_options.hard_forks.size(), std::time(nullptr)});
}
// Service Nodes
{
@ -832,7 +834,7 @@ namespace cryptonote
// now that we have a valid m_blockchain_storage, we can clean out any
// transactions in the pool that do not conform to the current fork
m_mempool.validate(m_blockchain_storage.get_current_hard_fork_version());
m_mempool.validate(m_blockchain_storage.get_network_version());
bool show_time_stats = command_line::get_arg(vm, arg_show_time_stats) != 0;
m_blockchain_storage.set_show_time_stats(show_time_stats);
@ -1346,7 +1348,7 @@ namespace cryptonote
{
// Caller needs to do this around both this *and* parse_incoming_txs
//auto lock = incoming_tx_lock();
uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
uint8_t version = m_blockchain_storage.get_network_version();
bool ok = true;
bool tx_pool_changed = false;
if (blink_rollback_height)
@ -1415,7 +1417,7 @@ namespace cryptonote
auto &new_blinks = results.first;
auto &missing_txs = results.second;
if (m_blockchain_storage.get_current_hard_fork_version() < HF_VERSION_BLINK)
if (m_blockchain_storage.get_network_version() < HF_VERSION_BLINK)
return results;
std::vector<uint8_t> want(blinks.size(), false); // Really bools, but std::vector<bool> is broken.
@ -1870,7 +1872,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const
{
const uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
const uint8_t version = m_blockchain_storage.get_network_version();
if (version >= 6)
{
for(const auto& in: tx.vin)
@ -1995,7 +1997,7 @@ namespace cryptonote
bool core::relay_service_node_votes()
{
auto height = get_current_blockchain_height();
auto hf_version = get_hard_fork_version(height);
auto hf_version = get_network_version(m_nettype, height);
auto quorum_votes = m_quorum_cop.get_relayable_votes(height, hf_version, true);
auto p2p_votes = m_quorum_cop.get_relayable_votes(height, hf_version, false);
@ -2387,7 +2389,6 @@ namespace cryptonote
m_starter_message_showed = true;
}
m_fork_moaner.do_call([this] { return check_fork_time(); });
m_txpool_auto_relayer.do_call([this] { return relay_txpool_transactions(); });
m_service_node_vote_relayer.do_call([this] { return relay_service_node_votes(); });
m_check_disk_space_interval.do_call([this] { return check_disk_space(); });
@ -2415,48 +2416,6 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_fork_time()
{
if (m_nettype == FAKECHAIN)
return true;
HardFork::State state = m_blockchain_storage.get_hard_fork_state();
const el::Level level = el::Level::Warning;
switch (state) {
case HardFork::LikelyForked:
MCLOG_RED(level, "global", "**********************************************************************");
MCLOG_RED(level, "global", "Last scheduled hard fork is too far in the past.");
MCLOG_RED(level, "global", "We are most likely forked from the network. Daemon update needed now.");
MCLOG_RED(level, "global", "**********************************************************************");
break;
case HardFork::UpdateNeeded:
break;
default:
break;
}
return true;
}
//-----------------------------------------------------------------------------------------------
uint8_t core::get_ideal_hard_fork_version() const
{
return get_blockchain_storage().get_ideal_hard_fork_version();
}
//-----------------------------------------------------------------------------------------------
uint8_t core::get_ideal_hard_fork_version(uint64_t height) const
{
return get_blockchain_storage().get_ideal_hard_fork_version(height);
}
//-----------------------------------------------------------------------------------------------
uint8_t core::get_hard_fork_version(uint64_t height) const
{
return get_blockchain_storage().get_hard_fork_version(height);
}
//-----------------------------------------------------------------------------------------------
uint64_t core::get_earliest_ideal_height_for_version(uint8_t version) const
{
return get_blockchain_storage().get_earliest_ideal_height_for_version(version);
}
//-----------------------------------------------------------------------------------------------
bool core::check_disk_space()
{
uint64_t free_space = get_free_space();

View File

@ -39,6 +39,7 @@
#include <boost/program_options/variables_map.hpp>
#include <oxenmq/oxenmq.h>
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "epee/storages/portable_storage_template_helper.h"
#include "common/command_line.h"
@ -60,7 +61,7 @@ DISABLE_VS_WARNINGS(4355)
namespace cryptonote
{
struct test_options {
std::vector<std::pair<uint8_t, uint64_t>> hard_forks;
std::vector<hard_fork> hard_forks;
size_t long_term_block_weight_window;
};
@ -729,34 +730,6 @@ namespace cryptonote
*/
uint64_t get_target_blockchain_height() const;
/**
* @brief returns the newest hardfork version known to the blockchain
*
* @return the version
*/
uint8_t get_ideal_hard_fork_version() const;
/**
* @brief return the ideal hard fork version for a given block height
*
* @return what it says above
*/
uint8_t get_ideal_hard_fork_version(uint64_t height) const;
/**
* @brief return the hard fork version for a given block height
*
* @return what it says above
*/
uint8_t get_hard_fork_version(uint64_t height) const;
/**
* @brief return the earliest block a given version may activate
*
* @return what it says above
*/
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const;
/**
* @brief gets start_time
*
@ -1107,18 +1080,6 @@ namespace cryptonote
*/
bool check_tx_inputs_keyimages_domain(const transaction& tx) const;
/**
* @brief checks HardFork status and prints messages about it
*
* Checks the status of HardFork and logs/prints if an update to
* the daemon is necessary.
*
* @note see Blockchain::get_hard_fork_state and HardFork::State
*
* @return true
*/
bool check_fork_time();
/**
* @brief checks free disk space
*
@ -1215,8 +1176,6 @@ namespace cryptonote
std::mutex m_sn_timestamp_mutex;
service_nodes::participation_history<service_nodes::timesync_entry, 30> m_sn_times;
tools::periodic_task m_store_blockchain_interval{12h, false}; //!< interval for manual storing of Blockchain, if enabled
tools::periodic_task m_fork_moaner{2h}; //!< interval for checking HardFork status
tools::periodic_task m_txpool_auto_relayer{2min, false}; //!< interval for checking re-relaying txpool transactions
tools::periodic_task m_check_disk_space_interval{10min}; //!< interval for checking for disk space
tools::periodic_task m_check_uptime_proof_interval{30s}; //!< interval for checking our own uptime proof (will be set to get_net_config().UPTIME_PROOF_CHECK_INTERVAL after init)

View File

@ -734,15 +734,12 @@ bool pulse::convert_time_to_round(pulse::time_point const &time, pulse::time_poi
bool pulse::get_round_timings(cryptonote::Blockchain const &blockchain, uint64_t block_height, uint64_t prev_timestamp, pulse::timings &times)
{
times = {};
static uint64_t const hf16_height = blockchain.get_earliest_ideal_height_for_version(cryptonote::network_version_16_pulse);
if (hf16_height == std::numeric_limits<uint64_t>::max())
return false;
if (blockchain.get_current_blockchain_height() < hf16_height)
auto hf16 = hard_fork_begins(blockchain.nettype(), cryptonote::network_version_16_pulse);
if (!hf16 || blockchain.get_current_blockchain_height() < *hf16)
return false;
cryptonote::block genesis_block;
if (!blockchain.get_block_by_height(hf16_height - 1, genesis_block))
if (!blockchain.get_block_by_height(*hf16 - 1, genesis_block))
return false;
uint64_t const delta_height = block_height - cryptonote::get_block_height(genesis_block);
@ -1165,7 +1162,7 @@ round_state prepare_for_round(round_context &context, service_nodes::service_nod
std::vector<crypto::hash> const entropy = service_nodes::get_pulse_entropy_for_next_block(blockchain.get_db(), context.wait_for_next_block.top_hash, context.prepare_for_round.round);
auto const active_node_list = blockchain.get_service_node_list().active_service_nodes_infos();
uint8_t const hf_version = blockchain.get_current_hard_fork_version();
uint8_t const hf_version = blockchain.get_network_version();
crypto::public_key const &block_leader = blockchain.get_service_node_list().get_block_leader().key;
context.prepare_for_round.quorum =
@ -1685,18 +1682,18 @@ void pulse::main(void *quorumnet_state, cryptonote::core &core)
//
// NOTE: Early exit if too early
//
static uint64_t const hf16_height = cryptonote::HardFork::get_hardcoded_hard_fork_height(blockchain.nettype(), cryptonote::network_version_16_pulse);
if (hf16_height == cryptonote::HardFork::INVALID_HF_VERSION_HEIGHT)
auto hf16 = hard_fork_begins(core.get_nettype(), cryptonote::network_version_16_pulse);
if (!hf16)
{
for (static bool once = true; once; once = !once)
MERROR("Pulse: HF16 is not defined, pulse worker waiting");
return;
}
if (uint64_t height = blockchain.get_current_blockchain_height(true /*lock*/); height < hf16_height)
if (uint64_t height = blockchain.get_current_blockchain_height(true /*lock*/); height < *hf16)
{
for (static bool once = true; once; once = !once)
MDEBUG("Pulse: Network at block " << height << " is not ready for Pulse until block " << hf16_height << ", waiting");
MDEBUG("Pulse: Network at block " << height << " is not ready for Pulse until block " << *hf16 << ", waiting");
return;
}
@ -1713,7 +1710,7 @@ void pulse::main(void *quorumnet_state, cryptonote::core &core)
break;
case round_state::wait_for_next_block:
context.state = wait_for_next_block(hf16_height, context, blockchain);
context.state = wait_for_next_block(*hf16, context, blockchain);
break;
case round_state::prepare_for_round:

View File

@ -93,7 +93,7 @@ namespace service_nodes
void service_node_list::init()
{
std::lock_guard lock(m_sn_mutex);
if (m_blockchain.get_current_hard_fork_version() < 9)
if (m_blockchain.get_network_version() < cryptonote::network_version_9_service_nodes)
{
reset(true);
return;
@ -471,7 +471,7 @@ namespace service_nodes
hw::device &hwdev = hw::get_device("default");
contribution->transferred = 0;
bool stake_decoded = true;
if (hf_version >= cryptonote::network_version_11_infinite_staking || hf_version == cryptonote::HardFork::INVALID_HF_VERSION)
if (hf_version >= cryptonote::network_version_11_infinite_staking)
{
// In Infinite Staking, we lock the key image that would be generated if
// you tried to send your stake and prevent it from being transacted on
@ -552,7 +552,7 @@ namespace service_nodes
}
}
if (hf_version < cryptonote::network_version_11_infinite_staking || (hf_version == cryptonote::HardFork::INVALID_HF_VERSION && !stake_decoded))
if (hf_version < cryptonote::network_version_11_infinite_staking)
{
// Pre Infinite Staking, we only need to prove the amount sent is
// sufficient to become a contributor to the Service Node and that there
@ -889,7 +889,7 @@ namespace service_nodes
// check the initial contribution exists
uint64_t staking_requirement = get_staking_requirement(nettype, block_height, hf_version);
uint64_t staking_requirement = get_staking_requirement(nettype, block_height);
cryptonote::account_public_address address;
staking_components stake = {};
@ -2666,7 +2666,7 @@ namespace service_nodes
if (!m_blockchain.has_db())
return false; // Haven't been initialized yet
uint8_t hf_version = m_blockchain.get_current_hard_fork_version();
uint8_t hf_version = m_blockchain.get_network_version();
if (hf_version < cryptonote::network_version_9_service_nodes)
return true;
@ -2907,7 +2907,7 @@ namespace service_nodes
//TODO remove after HF18
bool service_node_list::handle_uptime_proof(cryptonote::NOTIFY_UPTIME_PROOF::request const &proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey)
{
uint8_t const hf_version = m_blockchain.get_current_hard_fork_version();
uint8_t const hf_version = m_blockchain.get_network_version();
auto& netconf = get_config(m_blockchain.nettype());
auto now = std::chrono::system_clock::now();
@ -3000,7 +3000,7 @@ namespace service_nodes
bool service_node_list::handle_btencoded_uptime_proof(std::unique_ptr<uptime_proof::Proof> proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey)
{
uint8_t const hf_version = m_blockchain.get_current_hard_fork_version();
uint8_t const hf_version = m_blockchain.get_network_version();
auto& netconf = get_config(m_blockchain.nettype());
auto now = std::chrono::system_clock::now();
@ -3322,7 +3322,7 @@ namespace service_nodes
if (info.version < version_t::v1_add_registration_hf_version)
{
info.version = version_t::v1_add_registration_hf_version;
info.registration_hf_version = sn_list->m_blockchain.get_hard_fork_version(pubkey_info.info->registration_height);
info.registration_hf_version = sn_list->m_blockchain.get_network_version(pubkey_info.info->registration_height);
}
if (info.version < version_t::v4_noproofs)
{
@ -3552,13 +3552,7 @@ namespace service_nodes
m_blockchain.get_db().clear_service_node_data();
}
uint64_t hardfork_9_from_height = 0;
{
uint32_t window, votes, threshold;
uint8_t voting;
m_blockchain.get_hard_fork_voting_info(9, window, votes, threshold, hardfork_9_from_height, voting);
}
m_state.height = hardfork_9_from_height - 1;
m_state.height = hard_fork_begins(m_blockchain.nettype(), cryptonote::network_version_9_service_nodes).value_or(1) - 1;
}
size_t service_node_info::total_num_locked_contributions() const

View File

@ -32,6 +32,7 @@
#include "uptime_proof.h"
#include "cryptonote_config.h"
#include "cryptonote_core.h"
#include "cryptonote_basic/hardfork.h"
#include "version.h"
#include "common/oxen.h"
#include "common/util.h"
@ -183,7 +184,7 @@ namespace service_nodes
void quorum_cop::blockchain_detached(uint64_t height, bool by_pop_blocks)
{
uint8_t hf_version = m_core.get_hard_fork_version(height);
uint8_t hf_version = get_network_version(m_core.get_nettype(), height);
uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= cryptonote::network_version_12_checkpointing)
? REORG_SAFETY_BUFFER_BLOCKS_POST_HF12
: REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12;
@ -280,7 +281,7 @@ 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++)
{
uint8_t const obligations_height_hf_version = m_core.get_hard_fork_version(m_obligations_height);
uint8_t const obligations_height_hf_version = get_network_version(m_core.get_nettype(), m_obligations_height);
if (obligations_height_hf_version < cryptonote::network_version_9_service_nodes) continue;
// NOTE: Count checkpoints for other nodes, irrespective of being
@ -476,7 +477,7 @@ namespace service_nodes
m_last_checkpointed_height <= height;
m_last_checkpointed_height += CHECKPOINT_INTERVAL)
{
uint8_t checkpointed_height_hf_version = m_core.get_hard_fork_version(m_last_checkpointed_height);
uint8_t checkpointed_height_hf_version = get_network_version(m_core.get_nettype(), m_last_checkpointed_height);
if (checkpointed_height_hf_version <= cryptonote::network_version_11_infinite_staking)
continue;
@ -536,21 +537,21 @@ namespace service_nodes
return true;
}
uint8_t const hf_version = core.get_blockchain_storage().get_current_hard_fork_version();
auto net = core.get_blockchain_storage().get_network_version();
// NOTE: Verify state change is still valid or have we processed some other state change already that makes it invalid
{
crypto::public_key const &service_node_pubkey = quorum.workers[vote.state_change.worker_index];
auto service_node_infos = core.get_service_node_list_state({service_node_pubkey});
if (!service_node_infos.size() ||
!service_node_infos[0].info->can_transition_to_state(hf_version, vote.block_height, vote.state_change.state))
!service_node_infos[0].info->can_transition_to_state(net, vote.block_height, vote.state_change.state))
// NOTE: Vote is valid but is invalidated because we cannot apply the change to a service node or it is not on the network anymore
// So don't bother generating a state change tx.
return true;
}
using version_t = cryptonote::tx_extra_service_node_state_change::version_t;
auto ver = hf_version >= HF_VERSION_PROOF_BTENC ? version_t::v4_reasons : version_t::v0;
auto ver = net >= HF_VERSION_PROOF_BTENC ? version_t::v4_reasons : version_t::v0;
cryptonote::tx_extra_service_node_state_change state_change{
ver,
vote.state_change.state,
@ -569,9 +570,9 @@ namespace service_nodes
}
cryptonote::transaction state_change_tx{};
if (cryptonote::add_service_node_state_change_to_tx_extra(state_change_tx.extra, state_change, hf_version))
if (cryptonote::add_service_node_state_change_to_tx_extra(state_change_tx.extra, state_change, net))
{
state_change_tx.version = cryptonote::transaction::get_max_version_for_hf(hf_version);
state_change_tx.version = cryptonote::transaction::get_max_version_for_hf(net);
state_change_tx.type = cryptonote::txtype::state_change;
cryptonote::tx_verification_context tvc{};
@ -677,7 +678,7 @@ namespace service_nodes
return false;
}
if (!verify_vote_signature(m_core.get_hard_fork_version(vote.block_height), vote, vvc, *quorum))
if (!verify_vote_signature(get_network_version(m_core.get_nettype(), vote.block_height), vote, vvc, *quorum))
return false;
std::vector<pool_vote_entry> votes = m_vote_pool.add_pool_vote_if_unique(vote, vvc);

View File

@ -1,4 +1,5 @@
#include "cryptonote_config.h"
#include "cryptonote_basic/hardfork.h"
#include "common/oxen.h"
#include "epee/int-util.h"
#include <boost/endian/conversion.hpp>
@ -12,15 +13,15 @@
namespace service_nodes {
// TODO(oxen): Move to oxen_economy, this will also need access to oxen::exp2
uint64_t get_staking_requirement(cryptonote::network_type m_nettype, uint64_t height, uint8_t hf_version)
uint64_t get_staking_requirement(cryptonote::network_type nettype, uint64_t height)
{
if (m_nettype == cryptonote::TESTNET || m_nettype == cryptonote::FAKECHAIN || m_nettype == cryptonote::DEVNET)
if (nettype == cryptonote::TESTNET || nettype == cryptonote::FAKECHAIN || nettype == cryptonote::DEVNET)
return COIN * 100;
if (hf_version >= cryptonote::network_version_16_pulse)
if (is_hard_fork_at_least(nettype, cryptonote::network_version_16_pulse, height))
return 15000'000000000;
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints)
if (is_hard_fork_at_least(nettype, cryptonote::network_version_13_enforce_checkpoints, height))
{
constexpr int64_t heights[] = {
385824,
@ -69,7 +70,7 @@ uint64_t get_staking_requirement(cryptonote::network_type m_nettype, uint64_t he
uint64_t height_adjusted = height - hardfork_height;
uint64_t base = 0, variable = 0;
std::fesetround(FE_TONEAREST);
if (hf_version >= cryptonote::network_version_11_infinite_staking)
if (is_hard_fork_at_least(nettype, cryptonote::network_version_11_infinite_staking, height))
{
base = 15000 * COIN;
variable = (25007.0 * COIN) / oxen::exp2(height_adjusted/129600.0);

View File

@ -280,7 +280,7 @@ uint64_t get_min_node_contribution_in_portions(uint8_t version, uint64_t staking
// available contribution room, which allows slight overstaking but disallows larger overstakes.
uint64_t get_max_node_contribution(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved);
uint64_t get_staking_requirement(cryptonote::network_type nettype, uint64_t height, uint8_t hf_version);
uint64_t get_staking_requirement(cryptonote::network_type nettype, uint64_t height);
uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement);

View File

@ -497,7 +497,7 @@ namespace cryptonote
}
bool approved = blink.approved();
auto hf_version = m_blockchain.get_ideal_hard_fork_version(blink.height);
auto hf_version = m_blockchain.get_network_version(blink.height);
bool result = add_tx(tx, tvc, tx_pool_options::new_blink(approved, hf_version), hf_version);
if (result && approved)
{

View File

@ -43,6 +43,7 @@
#include <chrono>
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_basic/verification_context.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/tx_pool.h"
@ -342,13 +343,14 @@ namespace cryptonote
if(context.m_state == cryptonote_connection_context::state_synchronizing)
return true;
// from v6, if the peer advertises a top block version, reject if it's not what it should be (will only work if no voting)
// if the peer advertises a top block version, reject if it's not what it should be
if (hshd.current_height > 0)
{
const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1);
if (version >= 6 && version != hshd.top_version)
auto nettype = m_core.get_nettype();
const uint8_t version = get_network_version(nettype, hshd.current_height - 1);
if (version != hshd.top_version)
{
if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version())
if (version < hshd.top_version && version == get_network_version(nettype, m_core.get_current_blockchain_height()))
MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" <<
(unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version <<
") - we may be forked from the network and a software upgrade may be needed");
@ -385,7 +387,7 @@ namespace cryptonote
context.m_need_blink_sync = false;
// Check for any blink txes being advertised that we don't know about
if (m_core.get_blockchain_storage().get_current_hard_fork_version() >= HF_VERSION_BLINK)
if (is_hard_fork_at_least(m_core.get_nettype(), HF_VERSION_BLINK, curr_height))
{
if (hshd.blink_blocks.size() != hshd.blink_hash.size())
{
@ -527,7 +529,7 @@ namespace cryptonote
bool t_cryptonote_protocol_handler<t_core>::get_payload_sync_data(CORE_SYNC_DATA& hshd)
{
m_core.get_blockchain_top(hshd.current_height, hshd.top_id);
hshd.top_version = m_core.get_ideal_hard_fork_version(hshd.current_height);
hshd.top_version = get_network_version(m_core.get_nettype(), hshd.current_height);
hshd.cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height);
hshd.current_height +=1;
hshd.pruning_seed = m_core.get_blockchain_pruning_seed();
@ -2593,7 +2595,7 @@ skip:
// blink data that got sent to us (we may have additional blink info, or may have rejected some
// of the incoming blink data).
arg.blinks.clear();
if (m_core.get_blockchain_storage().get_current_hard_fork_version() >= HF_VERSION_BLINK)
if (is_hard_fork_at_least(m_core.get_nettype(), HF_VERSION_BLINK, m_core.get_current_blockchain_height()))
{
auto &pool = m_core.get_pool();
auto lock = pool.blink_shared_lock();

View File

@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "quorumnet.h"
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/service_node_voting.h"
#include "cryptonote_core/service_node_rules.h"
@ -853,7 +854,9 @@ void handle_blink(oxenmq::Message& m, QnetState& qnet) {
auto tag = get_or<uint64_t>(data, "!", 0);
auto hf_version = qnet.core.get_blockchain_storage().get_current_hard_fork_version();
auto local_height = qnet.core.get_current_blockchain_height();
auto hf_version = get_network_version(qnet.core.get_nettype(), local_height);
if (hf_version < HF_VERSION_BLINK) {
MWARNING("Rejecting blink message: blink is not available for hardfork " << (int) hf_version);
if (tag)
@ -863,7 +866,6 @@ void handle_blink(oxenmq::Message& m, QnetState& qnet) {
// verify that height is within-2 of current height
auto blink_height = get_int<uint64_t>(data.at("h"));
auto local_height = qnet.core.get_current_blockchain_height();
if (blink_height < local_height - 2) {
MINFO("Rejecting blink tx because blink auth height is too low (" << blink_height << " vs. " << local_height << ")");

View File

@ -686,28 +686,6 @@ bool command_parser_executor::in_peers(const std::vector<std::string>& args)
return m_executor.in_peers(set, limit);
}
bool command_parser_executor::hard_fork_info(const std::vector<std::string>& args)
{
int version;
if (args.size() == 0) {
version = 0;
}
else if (args.size() == 1) {
try {
version = std::stoi(args[0]);
}
catch(const std::exception& ex) {
return false;
}
if (version <= 0 || version > 255)
return false;
}
else {
return false;
}
return m_executor.hard_fork_info(version);
}
bool command_parser_executor::show_bans(const std::vector<std::string>& args)
{
if (!args.empty()) return false;

View File

@ -118,8 +118,6 @@ public:
bool in_peers(const std::vector<std::string>& args);
bool hard_fork_info(const std::vector<std::string>& args);
bool show_bans(const std::vector<std::string>& args);
bool ban(const std::vector<std::string>& args);

View File

@ -262,11 +262,6 @@ void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server)
, "in_peers <max_number>"
, "Set the <max_number> of in peers."
);
m_command_lookup.set_handler(
"hard_fork_info"
, [this](const auto &x) { return m_parser.hard_fork_info(x); }
, "Print the hard fork voting information."
);
m_command_lookup.set_handler(
"bans"
, [this](const auto &x) { return m_parser.show_bans(x); }

View File

@ -532,12 +532,8 @@ bool rpc_command_executor::show_status() {
str << ", v" << (ires.version.empty() ? "?.?.?" : ires.version);
str << "(net v" << +hfres.version << ')';
print_fork_extra_info(str, hfres.earliest_height, net_height, ires.target);
str << ", " << (
hfres.state == cryptonote::HardFork::Ready ? "up to date" :
hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" :
"out of date, likely forked");
if (hfres.earliest_height)
print_fork_extra_info(str, *hfres.earliest_height, net_height, ires.target);
std::time_t now = std::time(nullptr);
@ -1196,20 +1192,6 @@ bool rpc_command_executor::in_peers(bool set, uint32_t limit)
return true;
}
bool rpc_command_executor::hard_fork_info(uint8_t version)
{
HARD_FORK_INFO::response res{};
if (!invoke<HARD_FORK_INFO>({version}, res, "Failed to retrieve hard fork info"))
return false;
version = version > 0 ? version : res.voting;
tools::msg_writer() << "version " << (uint32_t)version << " " << (res.enabled ? "enabled" : "not enabled") <<
", " << res.votes << "/" << res.window << " votes, threshold " << res.threshold;
tools::msg_writer() << "current version " << (uint32_t)res.version << ", voting for version " << (uint32_t)res.voting;
return true;
}
bool rpc_command_executor::print_bans()
{
GETBANS::response res{};
@ -2058,8 +2040,8 @@ bool rpc_command_executor::prepare_registration(bool force_registration)
}
const uint64_t staking_requirement =
std::max(service_nodes::get_staking_requirement(nettype, block_height, hf_version),
service_nodes::get_staking_requirement(nettype, block_height + 30 * 24, hf_version)); // allow 1 day
std::max(service_nodes::get_staking_requirement(nettype, block_height),
service_nodes::get_staking_requirement(nettype, block_height + 30 * 24)); // allow 1 day
// anything less than DUST will be added to operator stake
const uint64_t DUST = MAX_NUMBER_OF_CONTRIBUTORS;

View File

@ -155,8 +155,6 @@ public:
bool in_peers(bool set, uint32_t limit);
bool hard_fork_info(uint8_t version);
bool print_bans();
bool ban(const std::string &address, time_t seconds, bool clear_ban = false);

View File

@ -40,6 +40,7 @@
#include <variant>
#include <oxenmq/base64.h>
#include "crypto/crypto.h"
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/oxen_name_system.h"
#include "cryptonote_core/pulse.h"
@ -1024,7 +1025,7 @@ namespace cryptonote { namespace rpc {
}
if (req.stake_info) {
auto hf_version = m_core.get_hard_fork_version(e.in_pool ? m_core.get_current_blockchain_height() : e.block_height);
auto hf_version = get_network_version(nettype(), e.in_pool ? m_core.get_current_blockchain_height() : e.block_height);
service_nodes::staking_components sc;
if (service_nodes::tx_get_staking_components_and_amounts(nettype(), hf_version, t, e.block_height, &sc)
&& sc.transferred > 0)
@ -1312,7 +1313,7 @@ namespace cryptonote { namespace rpc {
const account_public_address& lMiningAdr = lMiner.get_mining_address();
if (lMiner.is_mining())
res.address = get_account_address_as_str(nettype(), false, lMiningAdr);
const uint8_t major_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
const uint8_t major_version = m_core.get_blockchain_storage().get_network_version();
res.pow_algorithm =
major_version >= network_version_12_checkpointing ? "RandomX (OXEN variant)" :
@ -1466,14 +1467,14 @@ namespace cryptonote { namespace rpc {
std::function<void(const transaction& tx, tx_info& txi)> load_extra;
if (req.tx_extra || req.stake_info)
load_extra = [this, &req](const transaction& tx, tx_info& txi) {
load_extra = [this, &req, net=nettype()](const transaction& tx, tx_info& txi) {
if (req.tx_extra)
load_tx_extra_data(txi.extra.emplace(), tx, nettype());
load_tx_extra_data(txi.extra.emplace(), tx, net);
if (req.stake_info) {
auto height = m_core.get_current_blockchain_height();
auto hf_version = m_core.get_hard_fork_version(height);
auto hf_version = get_network_version(net, height);
service_nodes::staking_components sc;
if (service_nodes::tx_get_staking_components_and_amounts(nettype(), hf_version, tx, height, &sc)
if (service_nodes::tx_get_staking_components_and_amounts(net, hf_version, tx, height, &sc)
&& sc.transferred > 0)
txi.stake_amount = sc.transferred;
}
@ -2104,10 +2105,15 @@ namespace cryptonote { namespace rpc {
return res;
const Blockchain &blockchain = m_core.get_blockchain_storage();
uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version();
res.version = blockchain.get_current_hard_fork_version();
res.enabled = blockchain.get_hard_fork_voting_info(version, res.window, res.votes, res.threshold, res.earliest_height, res.voting);
res.state = blockchain.get_hard_fork_state();
uint8_t version =
req.version > 0 ? req.version :
req.height > 0 ? blockchain.get_network_version(req.height) :
blockchain.get_network_version();
res.version = version;
res.enabled = blockchain.get_network_version() >= version;
auto heights = get_hard_fork_heights(m_core.get_nettype(), version);
res.earliest_height = heights.first;
res.last_height = heights.second;
res.status = STATUS_OK;
return res;
}
@ -2844,10 +2850,10 @@ namespace cryptonote { namespace rpc {
bool at_least_one_succeeded = false;
res.quorums.reserve(std::min((uint64_t)16, count));
auto net = nettype();
for (size_t height = start; height != end;)
{
uint8_t hf_version = m_core.get_hard_fork_version(height);
if (hf_version != HardFork::INVALID_HF_VERSION)
uint8_t hf_version = get_network_version(net, height);
{
auto start_quorum_iterator = static_cast<service_nodes::quorum_type>(0);
auto end_quorum_iterator = service_nodes::max_quorum_type_for_hf(hf_version);
@ -2886,7 +2892,7 @@ namespace cryptonote { namespace rpc {
}
if (uint8_t hf_version; add_curr_pulse
&& (hf_version = m_core.get_hard_fork_version(curr_height)) >= network_version_16_pulse)
&& (hf_version = get_network_version(nettype(), curr_height)) >= network_version_16_pulse)
{
cryptonote::Blockchain const &blockchain = m_core.get_blockchain_storage();
cryptonote::block_header const &top_header = blockchain.get_db().get_block_header_from_height(curr_height - 1);
@ -2940,7 +2946,7 @@ namespace cryptonote { namespace rpc {
if (!m_core.service_node())
throw rpc_error{ERROR_WRONG_PARAM, "Daemon has not been started in service node mode, please relaunch with --service-node flag."};
uint8_t hf_version = m_core.get_hard_fork_version(m_core.get_current_blockchain_height());
uint8_t hf_version = get_network_version(nettype(), m_core.get_current_blockchain_height());
if (!service_nodes::make_registration_cmd(m_core.get_nettype(), hf_version, req.staking_requirement, req.args, m_core.get_service_keys(), res.registration_cmd, req.make_friendly))
throw rpc_error{ERROR_INTERNAL, "Failed to make registration command"};
@ -2957,7 +2963,7 @@ namespace cryptonote { namespace rpc {
std::vector<std::string> args;
uint64_t const curr_height = m_core.get_current_blockchain_height();
uint64_t staking_requirement = service_nodes::get_staking_requirement(m_core.get_nettype(), curr_height, m_core.get_hard_fork_version(curr_height));
uint64_t staking_requirement = service_nodes::get_staking_requirement(nettype(), curr_height);
{
uint64_t portions_cut;
@ -3142,7 +3148,9 @@ namespace cryptonote { namespace rpc {
res.height = m_core.get_current_blockchain_height() - 1;
res.target_height = m_core.get_target_blockchain_height();
res.block_hash = tools::type_to_hex(m_core.get_block_id_by_height(res.height));
res.hardfork = m_core.get_hard_fork_version(res.height);
auto [hf, snode_rev] = get_network_version_revision(nettype(), res.height);
res.hardfork = hf;
res.snode_revision = snode_rev;
if (!req.poll_block_hash.empty()) {
res.polling_mode = true;
@ -3284,7 +3292,7 @@ namespace cryptonote { namespace rpc {
PERF_TIMER(on_get_staking_requirement);
res.height = req.height > 0 ? req.height : m_core.get_current_blockchain_height();
res.staking_requirement = service_nodes::get_staking_requirement(m_core.get_nettype(), res.height, m_core.get_hard_fork_version(res.height));
res.staking_requirement = service_nodes::get_staking_requirement(nettype(), res.height);
res.status = STATUS_OK;
return res;
}
@ -3483,7 +3491,7 @@ namespace cryptonote { namespace rpc {
check_quantity_limit(req.entries.size(), ONS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES);
std::optional<uint64_t> height = m_core.get_current_blockchain_height();
uint8_t hf_version = m_core.get_hard_fork_version(*height);
uint8_t hf_version = get_network_version(nettype(), *height);
if (req.include_expired) height = std::nullopt;
std::vector<ons::mapping_type> types;
@ -3605,7 +3613,7 @@ namespace cryptonote { namespace rpc {
throw rpc_error{ERROR_WRONG_PARAM, "Unable to resolve ONS address: invalid 'name_hash' value '" + req.name_hash + "'"};
uint8_t hf_version = m_core.get_hard_fork_version(m_core.get_current_blockchain_height());
uint8_t hf_version = m_core.get_blockchain_storage().get_network_version();
auto type = static_cast<ons::mapping_type>(req.type);
if (!ons::mapping_type_allowed(hf_version, type))
throw rpc_error{ERROR_WRONG_PARAM, "Invalid lokinet type '" + std::to_string(req.type) + "'"};

View File

@ -727,18 +727,15 @@ KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(HARD_FORK_INFO::request)
KV_SERIALIZE(version)
KV_SERIALIZE(height)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(HARD_FORK_INFO::response)
KV_SERIALIZE(version)
KV_SERIALIZE(enabled)
KV_SERIALIZE(window)
KV_SERIALIZE(votes)
KV_SERIALIZE(threshold)
KV_SERIALIZE(voting)
KV_SERIALIZE(state)
KV_SERIALIZE(earliest_height)
KV_SERIALIZE(last_height)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
KV_SERIALIZE_MAP_CODE_END()
@ -1116,6 +1113,7 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_SERVICE_NODES::requested_fields_t)
KV_SERIALIZE(height)
KV_SERIALIZE(target_height)
KV_SERIALIZE(hardfork)
KV_SERIALIZE(snode_revision)
KV_SERIALIZE(last_uptime_proof)
KV_SERIALIZE(storage_server_reachable)
@ -1200,6 +1198,7 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_SERVICE_NODES::response)
if (fields.target_height || fields.all) KV_SERIALIZE(target_height)
if (fields.block_hash || fields.all || (polling_mode && !unchanged)) KV_SERIALIZE(block_hash)
if (fields.hardfork || fields.all) KV_SERIALIZE(hardfork)
if (fields.snode_revision || fields.all) KV_SERIALIZE(snode_revision)
if (!as_json.empty()) KV_SERIALIZE(as_json)
if (polling_mode) KV_SERIALIZE(unchanged);
KV_SERIALIZE_MAP_CODE_END()

View File

@ -1429,7 +1429,8 @@ namespace rpc {
struct request
{
uint8_t version; // The major block version for the fork.
uint8_t version; // The major block version for the fork (only one of `version` and `height` may be given).
uint64_t height; // Request hard fork info about this height (only one of `version` and `height` may be given).
KV_MAP_SERIALIZABLE
};
@ -1437,13 +1438,9 @@ namespace rpc {
struct response
{
uint8_t version; // The major block version for the fork.
bool enabled; // Tells if hard fork is enforced.
uint32_t window; // Number of blocks over which current votes are cast. Default is 10080 blocks.
uint32_t votes; // Number of votes towards hard fork.
uint32_t threshold; // Minimum percent of votes to trigger hard fork. Default is 80.
uint8_t voting; // Hard fork voting status.
uint32_t state; // Current hard fork state: 0 (There is likely a hard fork), 1 (An update is needed to fork properly), or 2 (Everything looks good).
uint64_t earliest_height; // Block height at which hard fork would be enabled if voted in.
bool enabled; // Indicates whether hard fork is enforced (that is, at or above the requested hardfork)
std::optional<uint64_t> earliest_height; // Block height at which hard fork will be enabled.
std::optional<uint64_t> last_height; // The last block height at which this hard fork will be active; will be omitted if this oxend is not aware of any future hard fork.
std::string status; // General RPC error code. "OK" means everything looks good.
bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`).
@ -2068,6 +2065,7 @@ namespace rpc {
bool height;
bool target_height;
bool hardfork;
bool snode_revision;
KV_MAP_SERIALIZABLE
};
@ -2146,6 +2144,7 @@ namespace rpc {
std::string block_hash; // Current block's hash.
bool unchanged; // Will be true (and `service_node_states` omitted) if you gave the current block hash to poll_block_hash
uint8_t hardfork; // Current hardfork version.
uint8_t snode_revision; // snode revision for non-hardfork but mandatory snode updates
std::string status; // Generic RPC error code. "OK" is the success value.
std::string as_json; // If `include_json` is set in the request, this contains the json representation of the `entry` data structure

View File

@ -161,7 +161,9 @@ bool NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_heigh
req_t.version = version;
try {
auto resp_t = invoke_json_rpc<rpc::HARD_FORK_INFO>(req_t);
m_earliest_height[version] = resp_t.earliest_height;
if (!resp_t.earliest_height)
return false;
m_earliest_height[version] = *resp_t.earliest_height;
} catch (...) { return false; }
}

View File

@ -8389,7 +8389,7 @@ wallet2::register_service_node_result wallet2::create_register_service_node_tx(c
}
}
staking_requirement = service_nodes::get_staking_requirement(nettype(), bc_height, *hf_version);
staking_requirement = service_nodes::get_staking_requirement(nettype(), bc_height);
std::vector<std::string> const args(local_args.begin(), local_args.begin() + local_args.size() - 3);
contributor_args = service_nodes::convert_registration_args(nettype(), args, staking_requirement, *hf_version);