From bed9a44e56ebdba9667f3cd2d242afde2b71514d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 1 Sep 2015 09:40:43 +0100 Subject: [PATCH 01/25] blockchain: add a couple missing includes --- src/cryptonote_core/blockchain.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 57763f6ca..7246c1d83 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include "syncobj.h" #include "string_tools.h" From 62b1f74116356653d17b4e2a71988de2d1ada093 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 1 Sep 2015 09:40:33 +0100 Subject: [PATCH 02/25] New hardfork class This keeps track of voting via block version, in order to decide when to enable a particular fork's code. --- src/cryptonote_core/CMakeLists.txt | 6 +- src/cryptonote_core/hardfork.cpp | 266 +++++++++++++++++++ src/cryptonote_core/hardfork.h | 205 +++++++++++++++ tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/hardfork.cpp | 394 +++++++++++++++++++++++++++++ 5 files changed, 871 insertions(+), 3 deletions(-) create mode 100644 src/cryptonote_core/hardfork.cpp create mode 100644 src/cryptonote_core/hardfork.h create mode 100644 tests/unit_tests/hardfork.cpp diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 6315fc4f0..7b0a5017d 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -37,7 +37,8 @@ set(cryptonote_core_sources cryptonote_format_utils.cpp difficulty.cpp miner.cpp - tx_pool.cpp) + tx_pool.cpp + hardfork.cpp) set(cryptonote_core_headers) @@ -60,7 +61,8 @@ set(cryptonote_core_private_headers miner.h tx_extra.h tx_pool.h - verification_context.h) + verification_context.h + hardfork.h) if(PER_BLOCK_CHECKPOINT) set(Blocks "blocks") diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp new file mode 100644 index 000000000..c7a69dadb --- /dev/null +++ b/src/cryptonote_core/hardfork.cpp @@ -0,0 +1,266 @@ +// Copyright (c) 2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include +#include + +#include "cryptonote_core/cryptonote_basic.h" +#include "blockchain_db/blockchain_db.h" +#include "hardfork.h" + +using namespace cryptonote; + +HardFork::HardFork(uint8_t original_version, time_t forked_time, time_t update_time, uint64_t max_history, int threshold_percent, uint64_t checkpoint_period): + original_version(original_version), + forked_time(forked_time), + update_time(update_time), + max_history(max_history), + threshold_percent(threshold_percent), + checkpoint_period(checkpoint_period) +{ + init(); +} + +bool HardFork::add(uint8_t version, uint64_t height, time_t time) +{ + CRITICAL_REGION_LOCAL(lock); + + // add in order + if (version == 0) + return false; + if (!heights.empty()) { + if (version <= heights.back().version) + return false; + if (height <= heights.back().height) + return false; + if (time <= heights.back().time) + return false; + } + heights.push_back({version: version, height: height, time: time}); + return true; +} + +uint8_t HardFork::get_effective_version(const cryptonote::block &block) const +{ + uint8_t version = block.major_version; + if (!heights.empty()) { + uint8_t max_version = heights.back().version; + if (version > max_version) + version = max_version; + } + return version; +} + +bool HardFork::do_check(const cryptonote::block &block) const +{ + return block.major_version >= heights[current_fork_index].version; +} + +bool HardFork::check(const cryptonote::block &block) const +{ + CRITICAL_REGION_LOCAL(lock); + return do_check(block); +} + +bool HardFork::add(const cryptonote::block &block, uint64_t height) +{ + CRITICAL_REGION_LOCAL(lock); + + if (!do_check(block)) + return false; + + const uint8_t version = get_effective_version(block); + + while (versions.size() >= max_history) { + const uint8_t old_version = versions.front(); + last_versions[old_version]--; + assert(last_versions[old_version] >= 0); + versions.pop_front(); + } + + last_versions[version]++; + versions.push_back(version); + + uint8_t voted = get_voted_fork_index(height); + if (voted > current_fork_index) { + for (int v = heights[current_fork_index].version + 1; v <= heights[voted].version; ++v) { + starting[v] = height; + } + current_fork_index = voted; + } + + if (height % checkpoint_period == 0) + checkpoints.push_back(std::make_pair(height, current_fork_index)); + + return true; +} + +void HardFork::init() +{ + CRITICAL_REGION_LOCAL(lock); + versions.clear(); + for (size_t n = 0; n < 256; ++n) + last_versions[n] = 0; + for (size_t n = 0; n < 256; ++n) + starting[n] = std::numeric_limits::max(); + add(original_version, 0, 0); + for (size_t n = 0; n <= original_version; ++n) + starting[n] = 0; + checkpoints.clear(); + current_fork_index = 0; + vote_threshold = (unsigned int)ceilf(max_history * threshold_percent / 100.0f); +} + +bool HardFork::reorganize_from_block_height(const cryptonote::BlockchainDB *db, uint64_t height) +{ + CRITICAL_REGION_LOCAL(lock); + if (!db || height >= db->height()) + return false; + while (!checkpoints.empty() && checkpoints.back().first > height) + checkpoints.pop_back(); + versions.clear(); + + int v; + for (v = 255; v >= 0; --v) { + if (starting[v] <= height) + break; + if (starting[v] != std::numeric_limits::max()) { + starting[v] = std::numeric_limits::max(); + } + } + for (current_fork_index = 0; current_fork_index < heights.size(); ++current_fork_index) { + if (heights[current_fork_index].version == v) + break; + } + for (size_t n = 0; n < 256; ++n) + last_versions[n] = 0; + const uint64_t rescan_height = height >= (max_history - 1) ? height - (max_history - 1) : 0; + 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(b); + last_versions[v]++; + versions.push_back(v); + } + 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); + } + + return true; +} + +bool HardFork::reorganize_from_chain_height(const cryptonote::BlockchainDB *db, uint64_t height) +{ + if (height == 0) + return false; + return reorganize_from_block_height(db, height - 1); +} + +int HardFork::get_voted_fork_index(uint64_t height) const +{ + CRITICAL_REGION_LOCAL(lock); + unsigned int accumulated_votes = 0; + for (unsigned int n = heights.size() - 1; n > current_fork_index; --n) { + uint8_t v = heights[n].version; + accumulated_votes += last_versions[v]; + if (height >= heights[n].height && accumulated_votes >= vote_threshold) { + return n; + } + } + return current_fork_index; +} + +HardFork::State HardFork::get_state(time_t t) const +{ + CRITICAL_REGION_LOCAL(lock); + + // 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; +} + +HardFork::State HardFork::get_state() const +{ + return get_state(time(NULL)); +} + +uint8_t HardFork::get(uint64_t height) const +{ + CRITICAL_REGION_LOCAL(lock); + for (size_t n = 1; n < 256; ++n) { + if (starting[n] > height) + return n - 1; + } + assert(false); + return 255; +} + +uint64_t HardFork::get_start_height(uint8_t version) const +{ + CRITICAL_REGION_LOCAL(lock); + return starting[version]; +} + +uint8_t HardFork::get_current_version() const +{ + CRITICAL_REGION_LOCAL(lock); + return heights[current_fork_index].version; +} + +uint8_t HardFork::get_ideal_version() const +{ + CRITICAL_REGION_LOCAL(lock); + return heights.back().version; +} + +template +void HardFork::serialize(archive_t & ar, const unsigned int version) +{ + CRITICAL_REGION_LOCAL(lock); + ar & forked_time; + ar & update_time; + ar & max_history; + ar & threshold_percent; + ar & original_version; + ar & heights; + ar & last_versions; + ar & starting; + ar & current_fork_index; + ar & vote_threshold; + ar & checkpoint_period; + ar & checkpoints; +} diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h new file mode 100644 index 000000000..b478dd0cf --- /dev/null +++ b/src/cryptonote_core/hardfork.h @@ -0,0 +1,205 @@ +// Copyright (c) 2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include +#include "syncobj.h" +#include "cryptonote_core/cryptonote_basic.h" + +namespace cryptonote +{ + class BlockchainDB; + + class HardFork + { + public: + typedef enum { + LikelyForked, + UpdateNeeded, + Ready, + } State; + + 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_MAX_HISTORY = 50; // supermajority window check length + static const int DEFAULT_THRESHOLD_PERCENT = 80; + static const uint64_t DEFAULT_CHECKPOINT_PERIOD = 1024; // mark a checkpoint every that many blocks + + /** + * @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 max_history the size of the window in blocks to consider for version voting + * @param threshold_percent the size of the majority in percents + */ + HardFork(uint8_t original_version = 1, time_t forked_time = DEFAULT_FORKED_TIME, time_t update_time = DEFAULT_UPDATE_TIME, uint64_t max_history = DEFAULT_MAX_HISTORY, int threshold_percent = DEFAULT_THRESHOLD_PERCENT, uint64_t checkpoint_period = DEFAULT_CHECKPOINT_PERIOD); + + /** + * @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) + */ + bool add(uint8_t version, uint64_t height, time_t time); + + /** + * @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 rescan the blockchain versions past the + * last checkpoint, potentially causing a large number of DB + * operations. + */ + bool check(const cryptonote::block &block) 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(const cryptonote::BlockchainDB *db, uint64_t height); + bool reorganize_from_chain_height(const cryptonote::BlockchainDB *db, uint64_t 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 height of the first block on the fork with th given version + * + * @param version version of the fork to query the starting block for + */ + uint64_t get_start_height(uint8_t version) 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 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; + + template + void serialize(archive_t & ar, const unsigned int version); + + private: + + void init(); + bool do_check(const cryptonote::block &block) const; + int get_voted_fork_index(uint64_t height) const; + uint8_t get_effective_version(const cryptonote::block &block) const; + + private: + + time_t forked_time; + time_t update_time; + uint64_t max_history; + int threshold_percent; + + uint8_t original_version; + + typedef struct { + uint8_t version; + uint64_t height; + time_t time; + } Params; + std::vector heights; + + std::deque versions; /* rolling window of the last N blocks' versions */ + unsigned int last_versions[256]; /* count of the block versions in the lsat N blocks */ + uint64_t starting[256]; /* block height at which each fork starts */ + unsigned int current_fork_index; + unsigned int vote_threshold; + + uint64_t checkpoint_period; + std::vector> checkpoints; + + mutable epee::critical_section lock; + }; + +} // namespace cryptonote + +BOOST_CLASS_VERSION(cryptonote::HardFork, 1) diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 2c6f18653..bbfa4a905 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -46,7 +46,8 @@ set(unit_tests_sources slow_memmem.cpp test_format_utils.cpp test_peerlist.cpp - test_protocol_pack.cpp) + test_protocol_pack.cpp + hardfork.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp new file mode 100644 index 000000000..6b3c332a8 --- /dev/null +++ b/tests/unit_tests/hardfork.cpp @@ -0,0 +1,394 @@ +// Copyright (c) 2014-2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include +#include "gtest/gtest.h" + +#include "blockchain_db/lmdb/db_lmdb.h" +#include "cryptonote_core/hardfork.h" + +using namespace cryptonote; + +#define BLOCKS_PER_YEAR 525960 +#define SECONDS_PER_YEAR 31557600 + +static cryptonote::block mkblock(uint8_t version) +{ + cryptonote::block b; + b.major_version = version; + return b; +} + +TEST(empty_hardforks, Success) +{ + HardFork hf; + + ASSERT_TRUE(hf.get_state(time(NULL)) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); + + ASSERT_EQ(hf.get(0), 1); + ASSERT_EQ(hf.get(1), 1); + ASSERT_EQ(hf.get(100000000), 1); +} + +TEST(ordering, Success) +{ + HardFork hf; + + ASSERT_TRUE(hf.add(2, 2, 1)); + ASSERT_FALSE(hf.add(3, 3, 1)); + ASSERT_FALSE(hf.add(3, 2, 2)); + ASSERT_FALSE(hf.add(2, 3, 2)); + ASSERT_TRUE(hf.add(3, 10, 2)); + ASSERT_TRUE(hf.add(4, 20, 3)); + ASSERT_FALSE(hf.add(5, 5, 4)); +} + +TEST(states, Success) +{ + HardFork hf; + + ASSERT_TRUE(hf.add(2, BLOCKS_PER_YEAR, SECONDS_PER_YEAR)); + + ASSERT_TRUE(hf.get_state(0) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_UPDATE_TIME / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + (HardFork::DEFAULT_UPDATE_TIME + HardFork::DEFAULT_FORKED_TIME) / 2) == HardFork::UpdateNeeded); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_FORKED_TIME * 2) == HardFork::LikelyForked); + + ASSERT_TRUE(hf.add(3, BLOCKS_PER_YEAR * 5, SECONDS_PER_YEAR * 5)); + + ASSERT_TRUE(hf.get_state(0) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_UPDATE_TIME / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + (HardFork::DEFAULT_UPDATE_TIME + HardFork::DEFAULT_FORKED_TIME) / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_FORKED_TIME * 2) == HardFork::Ready); +} + +TEST(steps_asap, Success) +{ + HardFork hf(1,1,1,1); + + // v h t + ASSERT_TRUE(hf.add(4, 2, 1)); + ASSERT_TRUE(hf.add(7, 4, 2)); + ASSERT_TRUE(hf.add(9, 6, 3)); + + for (uint64_t h = 0; h < 10; ++h) + hf.add(mkblock(10), h); + + ASSERT_EQ(hf.get(0), 1); + ASSERT_EQ(hf.get(1), 1); + ASSERT_EQ(hf.get(2), 4); + ASSERT_EQ(hf.get(3), 4); + ASSERT_EQ(hf.get(4), 7); + ASSERT_EQ(hf.get(5), 7); + ASSERT_EQ(hf.get(6), 9); + ASSERT_EQ(hf.get(7), 9); + ASSERT_EQ(hf.get(8), 9); + ASSERT_EQ(hf.get(9), 9); + ASSERT_EQ(hf.get(100000), 9); +} + +TEST(steps_1, Success) +{ + HardFork hf(1,1,1,1); + + // v h t + for (int n = 1 ; n < 10; ++n) + ASSERT_TRUE(hf.add(n+1, n, n)); + + for (uint64_t h = 0; h < 10; ++h) { + hf.add(mkblock(h+1), h); + ASSERT_EQ(hf.get(h), h+1); + } +} + +class TestDB: public BlockchainDB { +public: + virtual void open(const std::string& filename, const int db_flags = 0) {} + virtual void close() {} + virtual void sync() {} + virtual void reset() {} + virtual std::vector get_filenames() const { return std::vector(); } + virtual std::string get_db_name() const { return std::string(); } + virtual bool lock() { return true; } + virtual void unlock() { } + virtual void batch_start(uint64_t batch_num_blocks=0) {} + virtual void batch_stop() {} + virtual void set_batch_transactions(bool) {} + virtual bool block_exists(const crypto::hash& h) const { return false; } + virtual block get_block(const crypto::hash& h) const { return block(); } + virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } + virtual block_header get_block_header(const crypto::hash& h) const { return block_header(); } + virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } + virtual uint64_t get_top_block_timestamp() const { return 0; } + virtual size_t get_block_size(const uint64_t& height) const { return 128; } + virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } + virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } + virtual std::vector get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } + virtual std::vector get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } + virtual crypto::hash top_block_hash() const { return crypto::hash(); } + virtual block get_top_block() const { return block(); } + virtual uint64_t height() const { return blocks.size(); } + virtual bool tx_exists(const crypto::hash& h) const { return false; } + virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } + virtual transaction get_tx(const crypto::hash& h) const { return transaction(); } + virtual uint64_t get_tx_count() const { return 0; } + virtual std::vector get_tx_list(const std::vector& hlist) const { return std::vector(); } + virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } + virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } + virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); } + virtual output_data_t get_output_key(const uint64_t& global_index) const { return output_data_t(); } + virtual tx_out get_output(const crypto::hash& h, const uint64_t& index) const { return tx_out(); } + virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } + virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) { return tx_out_index(); } + virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) {} + virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs) {} + virtual bool can_thread_bulk_indices() const { return false; } + virtual std::vector get_tx_output_indices(const crypto::hash& h) const { return std::vector(); } + virtual std::vector get_tx_amount_output_indices(const crypto::hash& h) const { return std::vector(); } + virtual bool has_key_image(const crypto::key_image& img) const { return false; } + virtual void remove_block() { blocks.pop_back(); } + virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) {} + virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) {} + virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time) {} + virtual void remove_output(const tx_out& tx_output) {} + virtual void add_spent_key(const crypto::key_image& k_image) {} + virtual void remove_spent_key(const crypto::key_image& k_image) {} + + virtual void add_block( const block& blk + , const size_t& block_size + , const difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , const crypto::hash& blk_hash + ) { + blocks.push_back(blk); + } + virtual block get_block_from_height(const uint64_t& height) const { + return blocks[height]; + } +private: + std::vector blocks; +}; + +TEST(reorganize, Same) +{ + for (int history = 1; history <= 12; ++history) { + for (uint64_t checkpoint_period = 1; checkpoint_period <= 16; checkpoint_period++) { + HardFork hf(1, 1, 1, history, 100, checkpoint_period); + TestDB db; + + // v h t + ASSERT_TRUE(hf.add(4, 2, 1)); + ASSERT_TRUE(hf.add(7, 4, 2)); + ASSERT_TRUE(hf.add(9, 6, 3)); + + // index 0 1 2 3 4 5 6 7 8 9 + static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + for (uint64_t h = 0; h < 20; ++h) { + db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); + hf.add(db.get_block_from_height(h), h); + } + + for (uint64_t rh = 0; rh < 20; ++rh) { + hf.reorganize_from_block_height(&db, rh); + for (int hh = 0; hh < 20; ++hh) { + uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; + ASSERT_EQ(hf.get(hh), version); + } + ASSERT_EQ(hf.get(100000), 9); + } + } + } +} + +TEST(reorganize, Changed) +{ + int history = 4; + for (uint64_t checkpoint_period = 1; checkpoint_period <= 16; checkpoint_period++) { + HardFork hf(1, 1, 1, 4, 100, checkpoint_period); + TestDB db; + + // v h t + ASSERT_TRUE(hf.add(4, 2, 1)); + ASSERT_TRUE(hf.add(7, 4, 2)); + ASSERT_TRUE(hf.add(9, 6, 3)); + + // index 0 1 2 3 4 5 6 7 8 9 + static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + for (uint64_t h = 0; h < 20; ++h) { + db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); + hf.add(db.get_block_from_height(h), h); + } + + for (uint64_t rh = 0; rh < 20; ++rh) { + hf.reorganize_from_block_height(&db, rh); + for (int hh = 0; hh < 20; ++hh) { + uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; + ASSERT_EQ(hf.get(hh), version); + } + ASSERT_EQ(hf.get(100000), 9); + } + + // delay a bit for 9, and go back to 1 to check it stays at 9 + static const uint8_t block_versions_new[] = { 1, 1, 4, 4, 7, 7, 4, 7, 7, 7, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1 }; + static const uint8_t expected_versions_new[] = { 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 7, 7, 7, 9, 9, 9, 9, 9, 9, 9 }; + for (uint64_t h = 3; h < 20; ++h) { + db.remove_block(); + } + ASSERT_EQ(db.height(), 3); + hf.reorganize_from_block_height(&db, 2); + for (uint64_t h = 3; h < 20; ++h) { + db.add_block(mkblock(block_versions_new[h]), 0, 0, 0, crypto::hash()); + hf.add(db.get_block_from_height(h), h); + } + for (int hh = 0; hh < 20; ++hh) { + ASSERT_EQ(hf.get(hh), expected_versions_new[hh]); + } + ASSERT_EQ(hf.get(100000), 9); + } +} + +TEST(voting, threshold) +{ + for (int threshold = 87; threshold <= 88; ++threshold) { + HardFork hf(1, 1, 1, 8, threshold, 10); + TestDB db; + + // v h t + ASSERT_TRUE(hf.add(2, 2, 1)); + + for (uint64_t h = 0; h < 10; ++h) { + uint8_t v = 1 + !!(h % 8); + hf.add(mkblock(v), h); + uint8_t expected = threshold == 88 ? 1 : h < 7 ? 1 : 2; + ASSERT_EQ(hf.get(h), expected); + } + } +} + +TEST(new_blocks, denied) +{ + HardFork hf(1, 1, 1, 4, 50, 10); + + // v h t + ASSERT_TRUE(hf.add(2, 2, 1)); + + ASSERT_FALSE(hf.add(mkblock(0), 0)); + ASSERT_TRUE(hf.add(mkblock(1), 0)); + ASSERT_TRUE(hf.add(mkblock(1), 1)); + ASSERT_TRUE(hf.add(mkblock(1), 2)); + ASSERT_TRUE(hf.add(mkblock(2), 3)); + ASSERT_TRUE(hf.add(mkblock(1), 4)); + ASSERT_TRUE(hf.add(mkblock(1), 5)); + ASSERT_TRUE(hf.add(mkblock(1), 6)); + ASSERT_TRUE(hf.add(mkblock(2), 7)); + ASSERT_TRUE(hf.add(mkblock(2), 8)); // we reach 50% of the last 4 + ASSERT_FALSE(hf.add(mkblock(1), 9)); // so this one can't get added + ASSERT_TRUE(hf.add(mkblock(2), 10)); + + ASSERT_EQ(hf.get_start_height(2), 8); +} + +TEST(new_version, early) +{ + HardFork hf(1, 1, 1, 4, 50, 10); + + // v h t + ASSERT_TRUE(hf.add(2, 4, 1)); + + ASSERT_FALSE(hf.add(mkblock(0), 0)); + ASSERT_TRUE(hf.add(mkblock(2), 0)); + ASSERT_TRUE(hf.add(mkblock(2), 1)); // we have enough votes already + ASSERT_TRUE(hf.add(mkblock(2), 2)); + ASSERT_TRUE(hf.add(mkblock(1), 3)); // we accept a previous version because we did not switch, even with all the votes + ASSERT_TRUE(hf.add(mkblock(2), 4)); // but have to wait for the declared height anyway + ASSERT_TRUE(hf.add(mkblock(2), 5)); + ASSERT_FALSE(hf.add(mkblock(1), 6)); // we don't accept 1 anymore + ASSERT_TRUE(hf.add(mkblock(2), 7)); // but we do accept 2 + + ASSERT_EQ(hf.get_start_height(2), 4); +} + +TEST(reorganize, changed) +{ + HardFork hf(1, 1, 1, 4, 50, 10); + TestDB db; + + // v h t + ASSERT_TRUE(hf.add(2, 2, 1)); + ASSERT_TRUE(hf.add(3, 5, 2)); + +#define ADD(v, h, a) \ + do { \ + cryptonote::block b = mkblock(v); \ + db.add_block(b, 0, 0, 0, crypto::hash()); \ + ASSERT_##a(hf.add(b, h)); \ + } while(0) +#define ADD_TRUE(v, h) ADD(v, h, TRUE) +#define ADD_FALSE(v, h) ADD(v, h, FALSE) + + ADD_FALSE(0, 0); + ADD_TRUE(1, 0); + ADD_TRUE(1, 1); + ADD_TRUE(2, 2); + ADD_TRUE(2, 3); // switch to 2 here + ADD_TRUE(2, 4); + ADD_TRUE(2, 5); + ADD_TRUE(2, 6); + ASSERT_EQ(hf.get_current_version(), 2); + ADD_TRUE(3, 7); + ADD_TRUE(4, 8); + ADD_TRUE(4, 9); + ASSERT_EQ(hf.get_start_height(2), 3); + ASSERT_EQ(hf.get_start_height(3), 8); + ASSERT_EQ(hf.get_current_version(), 3); + + // pop a few blocks and check current version goes back down + db.remove_block(); + hf.reorganize_from_block_height(&db, 8); + ASSERT_EQ(hf.get_current_version(), 3); + db.remove_block(); + hf.reorganize_from_block_height(&db, 7); + ASSERT_EQ(hf.get_current_version(), 2); + db.remove_block(); + ASSERT_EQ(hf.get_current_version(), 2); + + // add blocks again, but remaining at 2 + ADD_TRUE(2, 7); + ADD_TRUE(2, 8); + ADD_TRUE(2, 9); + ASSERT_EQ(hf.get_start_height(2), 3); // unchanged + ASSERT_EQ(hf.get_current_version(), 2); // we did not bump to 3 this time + ASSERT_EQ(hf.get_start_height(3), std::numeric_limits::max()); // not yet +} From f85498422d9aec79411c739835ab4a5ed27b1688 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 12 Sep 2015 11:14:00 +0100 Subject: [PATCH 03/25] blockchain: use the new hardfork class --- src/cryptonote_core/blockchain.cpp | 26 +++++++++++++++++++++++--- src/cryptonote_core/blockchain.h | 7 +++++-- src/cryptonote_core/cryptonote_basic.h | 4 +--- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 09f8f8d7f..81612f9a5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -118,6 +118,11 @@ void Blockchain::serialize(archive_t & ar, const unsigned int version) } } + if (version > 12) + { + ar & m_hardfork; + } + LOG_PRINT_L3("Blockchain storage:" << std::endl << "m_blocks: " << m_db->height() << std::endl << "m_blocks_index: " << m_blocks_index.size() << std::endl << "m_transactions: " << m_transactions.size() << std::endl << "dummy_key_images_container: " << dummy_key_images_container.size() << std::endl << "m_alternative_chains: " << m_alternative_chains.size() << std::endl << "m_outputs: " << m_outputs.size() << std::endl << "m_invalid_blocks: " << m_invalid_blocks.size() << std::endl << "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); } //------------------------------------------------------------------ @@ -706,6 +711,8 @@ bool Blockchain::rollback_blockchain_switching(std::list& original_chain, 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(m_db, rollback_height); + LOG_PRINT_L1("Rollback to height " << rollback_height << " was successful."); if (original_chain.size()) { @@ -803,6 +810,8 @@ bool Blockchain::switch_to_alternative_blockchain(std::listheight(), LOG_LEVEL_0); return true; } @@ -972,12 +981,13 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m uint64_t already_generated_coins; CRITICAL_REGION_BEGIN(m_blockchain_lock); - b.major_version = CURRENT_BLOCK_MAJOR_VERSION; - b.minor_version = CURRENT_BLOCK_MINOR_VERSION; + height = m_db->height(); + + b.major_version = m_hardfork.get_ideal_version(); + b.minor_version = 0; b.prev_id = get_tail_id(); b.timestamp = time(NULL); - height = m_db->height(); diffic = get_difficulty_for_next_block(); CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead."); @@ -2245,6 +2255,13 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& CRITICAL_REGION_LOCAL(m_blockchain_lock); TIME_MEASURE_START(t1); + // this is a cheap test + if (!m_hardfork.check(bl)) + { + LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << bl.major_version << std::endl << "current: " << m_hardfork.get_current_version()); + return false; + } + if(bl.prev_id != get_tail_id()) { LOG_PRINT_L1("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << get_tail_id()); @@ -2521,6 +2538,9 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& update_next_cumulative_size_limit(); + // this will not fail since check succeeded above + m_hardfork.add(bl, new_height - 1); + LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); if(m_show_time_stats) { diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 7246c1d83..4693d4164 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -52,6 +52,7 @@ #include "verification_context.h" #include "crypto/hash.h" #include "checkpoints.h" +#include "hardfork.h" #include "blockchain_db/blockchain_db.h" namespace cryptonote @@ -227,6 +228,8 @@ namespace cryptonote std::atomic m_is_blockchain_storing; bool m_enforce_dns_checkpoints; + HardFork m_hardfork; + template inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const; bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, std::vector &output_keys, uint64_t* pmax_related_block_height); @@ -268,10 +271,10 @@ namespace cryptonote /* */ /************************************************************************/ - #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12 + #define CURRENT_BLOCKCHAIN_ARCHIVE_VER 13 //------------------------------------------------------------------ } // namespace cryptonote -BOOST_CLASS_VERSION(cryptonote::Blockchain, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) +BOOST_CLASS_VERSION(cryptonote::Blockchain, CURRENT_BLOCKCHAIN_ARCHIVE_VER) diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 07745bf0d..d3db50068 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -182,7 +182,6 @@ namespace cryptonote FIELD(extra) END_SERIALIZE() - protected: transaction_prefix(){} }; @@ -278,7 +277,7 @@ namespace cryptonote /************************************************************************/ struct block_header { - uint8_t major_version; + uint8_t major_version; // now used as a voting mechanism, rather than how this particular block is built uint8_t minor_version; uint64_t timestamp; crypto::hash prev_id; @@ -286,7 +285,6 @@ namespace cryptonote BEGIN_SERIALIZE() VARINT_FIELD(major_version) - if(major_version > CURRENT_BLOCK_MAJOR_VERSION) return false; VARINT_FIELD(minor_version) VARINT_FIELD(timestamp) FIELD(prev_id) From 6d4ec05f9db84d655f46b93783fc6097a3591df8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Sep 2015 11:27:52 +0100 Subject: [PATCH 04/25] monero-rpc-deprecated: misc fixes/improvements - make error constants actually const - fix client leak when reconnecting after failure to connect - simplify strncpy bound - fix user controlled memory writes in getheight --- src/rpc/daemon_deprecated_rpc.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 6e85a93a1..6dd48279e 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -59,12 +59,12 @@ namespace { // TODO: put right error codes here - int daemon_connection_error = -326701; - int parse_error = -32700; - int invalid_request = -32600; - int invalid_params = -32602; - int internal_error = -32603; - int not_mining_error = -32604; + const int daemon_connection_error = -326701; + const int parse_error = -32700; + const int invalid_request = -32600; + const int invalid_params = -32602; + const int internal_error = -32603; + const int not_mining_error = -32604; RPC::Json_rpc_http_server *server = NULL; wap_client_t *ipc_client = NULL; @@ -90,7 +90,11 @@ namespace } ipc_client = wap_client_new(); wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); - return check_connection_to_daemon(); + if (!check_connection_to_daemon()) { + wap_client_destroy(&ipc_client); // this sets ipc_client to NULL + return false; + } + return true; } /*! @@ -191,8 +195,7 @@ namespace result_json.AddMember("status", "OK", response_json.GetAllocator()); std::string response; construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); + strncpy(buf, response.c_str(), (size_t)len); return response.length(); } @@ -217,7 +220,10 @@ namespace rapidjson::Document request_json; char request_buf[1000]; strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; + size_t zidx = sizeof(request_buf) - 1; + if (req->params[0].len < zidx) + zidx = req->params[0].len; + request_buf[zidx] = '\0'; if (request_json.Parse(request_buf).HasParseError()) { return ns_rpc_create_error(buf, len, req, parse_error, From 05968c53cfdf313e32df548fd31117e30fc93129 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Sep 2015 11:30:26 +0100 Subject: [PATCH 05/25] wap_proto: fix warning casting away const --- src/ipc/wap_proto.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 4a4e52e46..1d0acc444 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -2544,8 +2544,10 @@ wap_proto_test (bool verbose) wap_proto_set_id (self, WAP_PROTO_BLOCKS); zlist_t *blocks_block_ids = zlist_new (); - zlist_append (blocks_block_ids, "Name: Brutus"); - zlist_append (blocks_block_ids, "Age: 43"); + char name[] = "Name: Brutus"; + zlist_append (blocks_block_ids, name); + char age[] = "Age: 43"; + zlist_append (blocks_block_ids, age); wap_proto_set_block_ids (self, &blocks_block_ids); wap_proto_set_start_height (self, 123); // Send twice From 8ffc508cef31002942e5548270f55093a4562eb5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Sep 2015 18:09:57 +0100 Subject: [PATCH 06/25] core: moan when we think an update is needed to get latest hard fork info --- src/cryptonote_core/blockchain.cpp | 5 +++++ src/cryptonote_core/blockchain.h | 2 ++ src/cryptonote_core/cryptonote_core.cpp | 26 +++++++++++++++++++++++++ src/cryptonote_core/cryptonote_core.h | 2 ++ 4 files changed, 35 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 81612f9a5..dfaae85b9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3074,3 +3074,8 @@ void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, m_db_blocks_per_sync = blocks_per_sync; m_max_prepare_blocks_threads = maxthreads; } + +HardFork::State Blockchain::get_hard_fork_state() const +{ + return m_hardfork.get_state(); +} diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 4693d4164..967745c46 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -159,6 +159,8 @@ namespace cryptonote void set_show_time_stats(bool stats) { m_show_time_stats = stats; } + HardFork::State get_hard_fork_state() const; + BlockchainDB& get_db() { return *m_db; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4d90eec1e..0769dbb69 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -830,11 +830,37 @@ namespace cryptonote #else m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage)); #endif + m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; } //----------------------------------------------------------------------------------------------- + bool core::check_fork_time() + { +#if BLOCKCHAIN_DB == DB_LMDB + HardFork::State state = m_blockchain_storage.get_hard_fork_state(); + switch (state) { + case HardFork::LikelyForked: + LOG_PRINT_L0(ENDL + << "**********************************************************************" << ENDL + << "Last scheduled hard fork is too far in the past." << ENDL + << "We are most likely forked from the network. Daemon update needed now." << ENDL + << "**********************************************************************" << ENDL); + break; + case HardFork::UpdateNeeded: + LOG_PRINT_L0(ENDL + << "**********************************************************************" << ENDL + << "Last scheduled hard fork time shows a daemon update is needed now." << ENDL + << "**********************************************************************" << ENDL); + break; + default: + break; + } +#endif + return true; + } + //----------------------------------------------------------------------------------------------- void core::set_target_blockchain_height(uint64_t target_blockchain_height) { m_target_blockchain_height = target_blockchain_height; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index d700b3b47..8e134e080 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -167,6 +167,7 @@ namespace cryptonote bool on_update_blocktemplate_interval(); bool check_tx_inputs_keyimages_diff(const transaction& tx); void graceful_exit(); + bool check_fork_time(); static std::atomic m_fast_exit; bool m_test_drop_download = true; uint64_t m_test_drop_download_height = 0; @@ -185,6 +186,7 @@ namespace cryptonote std::string m_config_folder; cryptonote_protocol_stub m_protocol_stub; epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; + epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; friend class tx_validate_inputs; std::atomic m_starter_message_showed; From a7177610b31a32fde7975af00f0d2c4cfee9ba67 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 19 Sep 2015 11:25:57 +0100 Subject: [PATCH 07/25] core: add consts where appropriate --- src/cryptonote_core/blockchain.cpp | 6 +- src/cryptonote_core/blockchain.h | 6 +- src/cryptonote_core/blockchain_storage.cpp | 6 +- src/cryptonote_core/blockchain_storage.h | 6 +- src/cryptonote_core/cryptonote_core.cpp | 64 +++++++++---------- src/cryptonote_core/cryptonote_core.h | 73 +++++++++++----------- 6 files changed, 82 insertions(+), 79 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index dfaae85b9..7673aa1f1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1632,7 +1632,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container return true; } //------------------------------------------------------------------ -void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) +void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const { LOG_PRINT_L3("Blockchain::" << __func__); std::stringstream ss; @@ -1652,7 +1652,7 @@ void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) LOG_PRINT_L0("Blockchain printed with log level 1"); } //------------------------------------------------------------------ -void Blockchain::print_blockchain_index() +void Blockchain::print_blockchain_index() const { LOG_PRINT_L3("Blockchain::" << __func__); std::stringstream ss; @@ -1670,7 +1670,7 @@ void Blockchain::print_blockchain_index() } //------------------------------------------------------------------ //TODO: remove this function and references to it -void Blockchain::print_blockchain_outs(const std::string& file) +void Blockchain::print_blockchain_outs(const std::string& file) const { LOG_PRINT_L3("Blockchain::" << __func__); return; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 967745c46..6a73faf4a 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -145,9 +145,9 @@ namespace cryptonote bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - void print_blockchain_outs(const std::string& file); + void print_blockchain(uint64_t start_index, uint64_t end_index) const; + void print_blockchain_index() const; + void print_blockchain_outs(const std::string& file) const; void check_against_checkpoints(const checkpoints& points, bool enforce); void set_enforce_dns_checkpoints(bool enforce); diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index ee4263687..4a4d348ba 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -1178,7 +1178,7 @@ double blockchain_storage::get_avg_block_size( size_t count) const return average; } //------------------------------------------------------------------ -void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) +void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) const { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1198,7 +1198,7 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind LOG_PRINT_L0("Blockchain printed with log level 1"); } //------------------------------------------------------------------ -void blockchain_storage::print_blockchain_index() +void blockchain_storage::print_blockchain_index() const { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1208,7 +1208,7 @@ void blockchain_storage::print_blockchain_index() LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); } //------------------------------------------------------------------ -void blockchain_storage::print_blockchain_outs(const std::string& file) +void blockchain_storage::print_blockchain_outs(const std::string& file) const { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index aeeef31b6..2a4cfcd49 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -173,9 +173,9 @@ namespace cryptonote return true; } //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - void print_blockchain_outs(const std::string& file); + void print_blockchain(uint64_t start_index, uint64_t end_index) const; + void print_blockchain_index() const; + void print_blockchain_outs(const std::string& file) const; void check_against_checkpoints(const checkpoints& points, bool enforce); bool update_checkpoints(const std::string& file_path, bool check_dns); void set_enforce_dns_checkpoints(bool enforce_checkpoints); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 0769dbb69..4ade50cde 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -168,37 +168,37 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - uint64_t core::get_current_blockchain_height() + uint64_t core::get_current_blockchain_height() const { return m_blockchain_storage.get_current_blockchain_height(); } //----------------------------------------------------------------------------------------------- - bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) + bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) const { top_id = m_blockchain_storage.get_tail_id(height); return true; } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) + bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) + bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) + bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const { return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list& blocks) + bool core::get_alternative_blocks(std::list& blocks) const { return m_blockchain_storage.get_alternative_blocks(blocks); } //----------------------------------------------------------------------------------------------- - size_t core::get_alternative_blocks_count() + size_t core::get_alternative_blocks_count() const { return m_blockchain_storage.get_alternative_blocks_count(); } @@ -385,12 +385,12 @@ namespace cryptonote m_test_drop_download_height = height; } //----------------------------------------------------------------------------------------------- - bool core::get_test_drop_download() + bool core::get_test_drop_download() const { return m_test_drop_download; } //----------------------------------------------------------------------------------------------- - bool core::get_test_drop_download_height() + bool core::get_test_drop_download_height() const { if (m_test_drop_download_height == 0) return true; @@ -451,7 +451,7 @@ namespace cryptonote return r; } //----------------------------------------------------------------------------------------------- - bool core::get_stat_info(core_stat_info& st_inf) + bool core::get_stat_info(core_stat_info& st_inf) const { st_inf.mining_speed = m_miner.get_speed(); st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); @@ -462,7 +462,7 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------- - bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) + bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) const { if(!tx.vin.size()) { @@ -515,12 +515,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::is_key_image_spent(const crypto::key_image &key_image) + bool core::is_key_image_spent(const crypto::key_image &key_image) const { return m_blockchain_storage.have_tx_keyimg_as_spent(key_image); } //----------------------------------------------------------------------------------------------- - bool core::are_key_images_spent(const std::vector& key_im, std::vector &spent) + bool core::are_key_images_spent(const std::vector& key_im, std::vector &spent) const { spent.clear(); BOOST_FOREACH(auto& ki, key_im) @@ -530,7 +530,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::check_tx_inputs_keyimages_diff(const transaction& tx) + bool core::check_tx_inputs_keyimages_diff(const transaction& tx) const { std::unordered_set ki; BOOST_FOREACH(const auto& in, tx.vin) @@ -551,7 +551,7 @@ namespace cryptonote return add_new_tx(tx, tx_hash, tx_prefix_hash, bl.size(), tvc, keeped_by_block); } //----------------------------------------------------------------------------------------------- - size_t core::get_blockchain_total_transactions() + size_t core::get_blockchain_total_transactions() const { return m_blockchain_storage.get_total_transactions(); } @@ -583,22 +583,22 @@ namespace cryptonote return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) + bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const { return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const { return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count); } //----------------------------------------------------------------------------------------------- - void core::print_blockchain(uint64_t start_index, uint64_t end_index) + void core::print_blockchain(uint64_t start_index, uint64_t end_index) const { m_blockchain_storage.print_blockchain(start_index, end_index); } //----------------------------------------------------------------------------------------------- - void core::print_blockchain_index() + void core::print_blockchain_index() const { m_blockchain_storage.print_blockchain_index(); } @@ -608,12 +608,12 @@ namespace cryptonote m_blockchain_storage.print_blockchain_outs(file); } //----------------------------------------------------------------------------------------------- - bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) + bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const { return m_blockchain_storage.get_random_outs_for_amounts(req, res); } //----------------------------------------------------------------------------------------------- - bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) + bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const { return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); } @@ -728,7 +728,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- // Used by the RPC server to check the size of an incoming // block_blob - bool core::check_incoming_block_size(const blobdata& block_blob) + bool core::check_incoming_block_size(const blobdata& block_blob) const { if(block_blob.size() > get_max_block_size()) { @@ -738,32 +738,32 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - crypto::hash core::get_tail_id() + crypto::hash core::get_tail_id() const { return m_blockchain_storage.get_tail_id(); } //----------------------------------------------------------------------------------------------- - size_t core::get_pool_transactions_count() + size_t core::get_pool_transactions_count() const { return m_mempool.get_transactions_count(); } //----------------------------------------------------------------------------------------------- - bool core::have_block(const crypto::hash& id) + bool core::have_block(const crypto::hash& id) const { return m_blockchain_storage.have_block(id); } //----------------------------------------------------------------------------------------------- - bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) + bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) const { return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); } //----------------------------------------------------------------------------------------------- - bool core::check_tx_syntax(const transaction& tx) + bool core::check_tx_syntax(const transaction& tx) const { return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions(std::list& txs) + bool core::get_pool_transactions(std::list& txs) const { m_mempool.get_transactions(txs); return true; @@ -774,7 +774,7 @@ namespace cryptonote return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos); } //----------------------------------------------------------------------------------------------- - bool core::get_short_chain_history(std::list& ids) + bool core::get_short_chain_history(std::list& ids) const { return m_blockchain_storage.get_short_chain_history(ids); } @@ -784,12 +784,12 @@ namespace cryptonote return m_blockchain_storage.handle_get_objects(arg, rsp); } //----------------------------------------------------------------------------------------------- - crypto::hash core::get_block_id_by_height(uint64_t height) + crypto::hash core::get_block_id_by_height(uint64_t height) const { return m_blockchain_storage.get_block_id_by_height(height); } //----------------------------------------------------------------------------------------------- - bool core::get_block_by_hash(const crypto::hash &h, block &blk) + bool core::get_block_by_hash(const crypto::hash &h, block &blk) const { return m_blockchain_storage.get_block_by_hash(h, blk); } @@ -798,7 +798,7 @@ namespace cryptonote // m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); //} //----------------------------------------------------------------------------------------------- - std::string core::print_pool(bool short_format) + std::string core::print_pool(bool short_format) const { return m_mempool.print_pool(short_format); } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 8e134e080..634340a3f 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -69,7 +69,7 @@ namespace cryptonote bool prepare_handle_incoming_blocks(const std::list &blocks); bool cleanup_handle_incoming_blocks(bool force_sync = false); - bool check_incoming_block_size(const blobdata& block_blob); + bool check_incoming_block_size(const blobdata& block_blob) const; i_cryptonote_protocol* get_protocol(){return m_pprotocol;} //-------------------- i_miner_handler ----------------------- @@ -78,6 +78,7 @@ namespace cryptonote miner& get_miner(){return m_miner;} + const miner& get_miner()const{return m_miner;} static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); bool set_genesis_block(const block& b); @@ -86,55 +87,57 @@ namespace cryptonote static bool get_fast_exit(); void test_drop_download(); void test_drop_download_height(uint64_t height); - bool get_test_drop_download(); - bool get_test_drop_download_height(); - uint64_t get_current_blockchain_height(); - bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); + bool get_test_drop_download() const; + bool get_test_drop_download_height() const; + uint64_t get_current_blockchain_height() const; + bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const; template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const { return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); } - crypto::hash get_block_id_by_height(uint64_t height); - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); - bool get_block_by_hash(const crypto::hash &h, block &blk); + crypto::hash get_block_id_by_height(uint64_t height) const; + bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const; + bool get_block_by_hash(const crypto::hash &h, block &blk) const; //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - bool get_alternative_blocks(std::list& blocks); - size_t get_alternative_blocks_count(); + bool get_alternative_blocks(std::list& blocks) const; + size_t get_alternative_blocks_count() const; void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_checkpoints(checkpoints&& chk_pts); void set_checkpoints_file_path(const std::string& path); void set_enforce_dns_checkpoints(bool enforce_dns); - bool get_pool_transactions(std::list& txs); + bool get_pool_transactions(std::list& txs) const; bool get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const; - size_t get_pool_transactions_count(); - size_t get_blockchain_total_transactions(); + size_t get_pool_transactions_count() const; + size_t get_blockchain_total_transactions() const; //bool get_outs(uint64_t amount, std::list& pkeys); - bool have_block(const crypto::hash& id); - bool get_short_chain_history(std::list& ids); - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - bool get_stat_info(core_stat_info& st_inf); + bool have_block(const crypto::hash& id) const; + bool get_short_chain_history(std::list& ids) const; + bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; + bool get_stat_info(core_stat_info& st_inf) const; //bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); - bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - crypto::hash get_tail_id(); - bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const; + crypto::hash get_tail_id() const; + bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const; void pause_mine(); void resume_mine(); #if BLOCKCHAIN_DB == DB_LMDB Blockchain& get_blockchain_storage(){return m_blockchain_storage;} + const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;} #else blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} + const blockchain_storage& get_blockchain_storage()const{return m_blockchain_storage;} #endif //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - std::string print_pool(bool short_format); + void print_blockchain(uint64_t start_index, uint64_t end_index) const; + void print_blockchain_index() const; + std::string print_pool(bool short_format) const; void print_blockchain_outs(const std::string& file); void on_synchronized(); @@ -145,27 +148,27 @@ namespace cryptonote void stop(); - bool is_key_image_spent(const crypto::key_image& key_im); - bool are_key_images_spent(const std::vector& key_im, std::vector &spent); + bool is_key_image_spent(const crypto::key_image& key_im) const; + bool are_key_images_spent(const std::vector& key_im, std::vector &spent) const; private: bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); bool add_new_block(const block& b, block_verification_context& bvc); bool load_state_data(); - bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); + bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) const; - bool check_tx_syntax(const transaction& tx); + bool check_tx_syntax(const transaction& tx) const; //check correct values, amounts and all lightweight checks not related with database - bool check_tx_semantic(const transaction& tx, bool keeped_by_block); + bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; //check if tx already in memory pool or in main blockchain - bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); - bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig) const; + bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; bool update_miner_block_template(); bool handle_command_line(const boost::program_options::variables_map& vm); bool on_update_blocktemplate_interval(); - bool check_tx_inputs_keyimages_diff(const transaction& tx); + bool check_tx_inputs_keyimages_diff(const transaction& tx) const; void graceful_exit(); bool check_fork_time(); static std::atomic m_fast_exit; From d06713199eda083290d3778786198a02250d97b9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 19 Sep 2015 16:33:39 +0100 Subject: [PATCH 08/25] blockchain: force a hardfork recalculation at load time Since the state isn't actually saved anywhere, as the archive code isn't called in the new DB version. --- src/cryptonote_core/blockchain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7673aa1f1..868006e62 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -355,6 +355,9 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) } #endif + // reinitialize hard fork versions, since they're not saved in the DB + m_hardfork.reorganize_from_chain_height (m_db, 1); + LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); return true; From e546f3724aeb91e72df263c27780a4d149b0a92a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 19 Sep 2015 16:34:29 +0100 Subject: [PATCH 09/25] Add an RPC call and daemon command to get info on hard fork voting --- src/cryptonote_core/blockchain.cpp | 7 ++++- src/cryptonote_core/blockchain.h | 3 +++ src/cryptonote_core/hardfork.cpp | 20 ++++++++++++-- src/cryptonote_core/hardfork.h | 22 ++++++++++++++-- src/daemon/command_parser_executor.cpp | 22 ++++++++++++++++ src/daemon/command_parser_executor.h | 2 ++ src/daemon/command_server.cpp | 5 ++++ src/daemon/rpc_command_executor.cpp | 31 ++++++++++++++++++++++ src/daemon/rpc_command_executor.h | 2 ++ src/rpc/core_rpc_server.cpp | 23 ++++++++++++++++ src/rpc/core_rpc_server.h | 1 + src/rpc/core_rpc_server_commands_defs.h | 35 +++++++++++++++++++++++++ src/rpc/core_rpc_server_error_codes.h | 1 + 13 files changed, 169 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 868006e62..2ac50aefc 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -72,7 +72,7 @@ DISABLE_VS_WARNINGS(4267) //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), -m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true) +m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_hardfork(), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -3082,3 +3082,8 @@ 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, uint8_t &voting) const +{ + return m_hardfork.get_voting_info(version, window, votes, threshold, voting); +} diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6a73faf4a..e549ea2d0 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -160,6 +160,9 @@ namespace cryptonote void set_show_time_stats(bool stats) { m_show_time_stats = stats; } HardFork::State get_hard_fork_state() const; + uint8_t get_current_hard_fork_version() const { return m_hardfork.get_current_version(); } + uint8_t get_ideal_hard_fork_version() const { return m_hardfork.get_ideal_version(); } + bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const; BlockchainDB& get_db() { diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index c7a69dadb..8ef45ab5a 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -135,7 +135,7 @@ void HardFork::init() starting[n] = 0; checkpoints.clear(); current_fork_index = 0; - vote_threshold = (unsigned int)ceilf(max_history * threshold_percent / 100.0f); + vote_threshold = (uint32_t)ceilf(max_history * threshold_percent / 100.0f); } bool HardFork::reorganize_from_block_height(const cryptonote::BlockchainDB *db, uint64_t height) @@ -186,7 +186,7 @@ bool HardFork::reorganize_from_chain_height(const cryptonote::BlockchainDB *db, int HardFork::get_voted_fork_index(uint64_t height) const { CRITICAL_REGION_LOCAL(lock); - unsigned int accumulated_votes = 0; + uint32_t accumulated_votes = 0; for (unsigned int n = heights.size() - 1; n > current_fork_index; --n) { uint8_t v = heights[n].version; accumulated_votes += last_versions[v]; @@ -247,6 +247,22 @@ uint8_t HardFork::get_ideal_version() const return heights.back().version; } +bool HardFork::get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const +{ + CRITICAL_REGION_LOCAL(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 = vote_threshold; + assert((votes >= threshold) == enabled); + voting = heights.back().version; + return enabled; +} + template void HardFork::serialize(archive_t & ar, const unsigned int version) { diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index b478dd0cf..813996b8c 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -162,6 +162,24 @@ namespace cryptonote */ uint8_t get_current_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 + */ + bool get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const; + + /** + * @brief returns the size of the voting window in blocks + */ + uint64_t get_window_size() const { return max_history; } + template void serialize(archive_t & ar, const unsigned int version); @@ -189,10 +207,10 @@ namespace cryptonote std::vector heights; std::deque versions; /* rolling window of the last N blocks' versions */ - unsigned int last_versions[256]; /* count of the block versions in the lsat N blocks */ + unsigned int last_versions[256]; /* count of the block versions in the last N blocks */ uint64_t starting[256]; /* block height at which each fork starts */ unsigned int current_fork_index; - unsigned int vote_threshold; + uint32_t vote_threshold; uint64_t checkpoint_period; std::vector> checkpoints; diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index e6666c443..7b0f4a66b 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -361,5 +361,27 @@ bool t_command_parser_executor::stop_save_graph(const std::vector& return m_executor.stop_save_graph(); } +bool t_command_parser_executor::hard_fork_info(const std::vector& args) +{ + int version; + if (args.size() == 0) { + version = 0; + } + else if (args.size() == 1) { + try { + version = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + if (version <= 0 || version > 255) + return false; + } + else { + return false; + } + return m_executor.hard_fork_info(version); +} + } // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index ddc207cfe..f900c72bd 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -102,6 +102,8 @@ public: bool start_save_graph(const std::vector& args); bool stop_save_graph(const std::vector& args); + + bool hard_fork_info(const std::vector& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 047b52c3e..446379558 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -184,6 +184,11 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1) , "Stop save data for dr monero" ); + m_command_lookup.set_handler( + "hard_fork_info" + , std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1) + , "Print hard fork voting information" + ); } bool t_command_server::process_command_str(const std::string& cmd) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 62f254c76..c4fe642e8 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -946,4 +946,35 @@ bool t_rpc_command_executor::stop_save_graph() return true; } +bool t_rpc_command_executor::hard_fork_info(uint8_t version) +{ + cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req; + cryptonote::COMMAND_RPC_HARD_FORK_INFO::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + req.version = version; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "/hard_fork_info", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_hard_fork_info(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + 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; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index b40a67bf8..9ad849434 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -120,6 +120,8 @@ public: bool start_save_graph(); bool stop_save_graph(); + + bool hard_fork_info(uint8_t version); }; } // namespace daemonize diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 80bd7e6cd..f9ff632ac 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -812,6 +812,29 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + +#if BLOCKCHAIN_DB == DB_LMDB + const Blockchain &blockchain = m_core.get_blockchain_storage(); + uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_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.voting); + res.state = blockchain.get_hard_fork_state(); + return true; +#else + error_resp.code = CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC; + error_resp.message = "Hard fork inoperative in memory mode."; + return false; +#endif + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_fast_exit(const COMMAND_RPC_FAST_EXIT::request& req, COMMAND_RPC_FAST_EXIT::response& res) { cryptonote::core::set_fast_exit(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3213e6b1c..60e7d00d5 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -138,6 +138,7 @@ namespace cryptonote bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp); bool on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp); bool on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp); + bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp); //----------------------- private: diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index b2fdd9930..a806cbae9 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -815,5 +815,40 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_HARD_FORK_INFO + { + struct request + { + uint8_t version; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint8_t version; + bool enabled; + uint32_t window; + uint32_t votes; + uint32_t threshold; + uint8_t voting; + uint32_t state; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + KV_SERIALIZE(enabled) + KV_SERIALIZE(window) + KV_SERIALIZE(votes) + KV_SERIALIZE(threshold) + KV_SERIALIZE(voting) + KV_SERIALIZE(state) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 91659eb2a..72c72b94b 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -40,5 +40,6 @@ #define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 #define CORE_RPC_ERROR_CODE_CORE_BUSY -9 #define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10 +#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11 From 0a54c3a55304cab9f7da53347a9d564d9b473a77 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 20 Sep 2015 10:05:15 +0100 Subject: [PATCH 10/25] hardfork: remove the "parts are copyright cryptonote" notices I coded the whole thing from scratch. --- src/cryptonote_core/hardfork.cpp | 2 -- src/cryptonote_core/hardfork.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index 8ef45ab5a..3003e74c8 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -25,8 +25,6 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // 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. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include #include diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index 813996b8c..3bcf46cff 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -25,8 +25,6 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // 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. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once From 5b11a89a76cf44c0fb98d98b05eb1c9e4ddce0c4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 20 Sep 2015 18:41:38 +0100 Subject: [PATCH 11/25] hardfork: most state now saved to the DB There will be a delay on first load of an existing blockchain as it gets reparsed for this state data. --- src/blockchain_db/berkeleydb/db_bdb.cpp | 75 +++++ src/blockchain_db/berkeleydb/db_bdb.h | 10 + src/blockchain_db/blockchain_db.h | 6 + src/blockchain_db/lmdb/db_lmdb.cpp | 124 ++++++++ src/blockchain_db/lmdb/db_lmdb.h | 9 + src/cryptonote_core/blockchain.cpp | 41 ++- src/cryptonote_core/blockchain.h | 6 +- src/cryptonote_core/hardfork.cpp | 109 +++---- src/cryptonote_core/hardfork.h | 41 ++- tests/unit_tests/hardfork.cpp | 374 ++++++++++++++---------- 10 files changed, 537 insertions(+), 258 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index a990d7aaf..2a9e9a7f8 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -118,6 +118,9 @@ const char* const BDB_OUTPUT_KEYS = "output_keys"; const char* const BDB_SPENT_KEYS = "spent_keys"; +const char* const BDB_HF_STARTING_HEIGHTS = "hf_starting_heights"; +const char* const BDB_HF_VERSIONS = "hf_versions"; + const unsigned int MB = 1024 * 1024; // ND: FIXME: db keeps running out of locks when doing full syncs. Possible bug??? Set it to 5K for now. const unsigned int DB_MAX_LOCKS = 5000; @@ -667,6 +670,9 @@ void BlockchainBDB::open(const std::string& filename, const int db_flags) m_spent_keys = new Db(m_env, 0); + m_hf_starting_heights = new Db(m_env, 0); + m_hf_versions = new Db(m_env, 0); + // Tell DB about Dbs that need duplicate support // Note: no need to tell about sorting, // as the default is insertion order, which we want @@ -684,6 +690,9 @@ void BlockchainBDB::open(const std::string& filename, const int db_flags) m_output_indices->set_re_len(sizeof(uint64_t)); m_output_keys->set_re_len(sizeof(output_data_t)); + m_hf_starting_heights->set_re_len(sizeof(uint64_t)); + m_hf_versions->set_re_len(sizeof(uint8_t)); + //TODO: Find out if we need to do Db::set_flags(DB_RENUMBER) // for the RECNO databases. We shouldn't as we're only // inserting/removing from the end, but we'll see. @@ -713,6 +722,9 @@ void BlockchainBDB::open(const std::string& filename, const int db_flags) m_spent_keys->open(txn, BDB_SPENT_KEYS, NULL, DB_HASH, DB_CREATE, 0); + m_hf_starting_heights->open(txn, BDB_HF_STARTING_HEIGHTS, NULL, DB_RECNO, DB_CREATE, 0); + m_hf_versions->open(txn, BDB_HF_VERSIONS, NULL, DB_RECNO, DB_CREATE, 0); + txn.commit(); DB_BTREE_STAT* stats; @@ -785,6 +797,9 @@ void BlockchainBDB::sync() m_output_keys->sync(0); m_spent_keys->sync(0); + + m_hf_starting_heights->sync(0); + m_hf_versions->sync(0); } catch (const std::exception& e) { @@ -859,6 +874,12 @@ std::vector BlockchainBDB::get_filenames() const m_spent_keys->get_dbname(pfname, pdbname); filenames.push_back(fname); + m_hf_starting_heights->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_hf_versions->get_dbname(pfname, pdbname); + filenames.push_back(fname); + std::vector full_paths; for (auto& filename : filenames) @@ -1839,6 +1860,60 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const std::v LOG_PRINT_L3("db3: " << db3); } +void BlockchainBDB::set_hard_fork_starting_height(uint8_t version, uint64_t height) +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + Dbt_copy val_key(version); + Dbt_copy val(height); + if (m_hf_starting_heights->put(DB_DEFAULT_TX, &val_key, &val, 0)) + throw1(DB_ERROR("Error adding hard fork starting height to db transaction.")); +} + +uint64_t BlockchainBDB::get_hard_fork_starting_height(uint8_t version) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + Dbt_copy key(version); + Dbt_copy result; + + auto get_result = m_hf_starting_heights->get(DB_DEFAULT_TX, &key, &result, 0); + if (get_result == DB_NOTFOUND) + return std::numeric_limits::max(); + else if (get_result) + throw0(DB_ERROR("Error attempting to retrieve hard fork starting height from the db")); + + return result; +} + +void BlockchainBDB::set_hard_fork_version(uint64_t height, uint8_t version) +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + Dbt_copy val_key(height); + Dbt_copy val(version); + if (m_hf_versions->put(DB_DEFAULT_TX, &val_key, &val, 0)) + throw1(DB_ERROR("Error adding hard fork version to db transaction.")); +} + +uint8_t BlockchainBDB::get_hard_fork_version(uint64_t height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + Dbt_copy key(height); + Dbt_copy result; + + auto get_result = m_hf_versions->get(DB_DEFAULT_TX, &key, &result, 0); + if (get_result == DB_NOTFOUND || get_result) + throw0(DB_ERROR("Error attempting to retrieve hard fork version from the db")); + + return result; +} + void BlockchainBDB::checkpoint_worker() const { LOG_PRINT_L0("Entering BDB checkpoint thread.") diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index f92bbef68..54edcf0ad 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -374,6 +374,13 @@ private: virtual void remove_spent_key(const crypto::key_image& k_image); void get_output_global_indices(const uint64_t& amount, const std::vector &offsets, std::vector &global_indices); + + // Hard fork related storage + virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height); + virtual uint64_t get_hard_fork_starting_height(uint8_t version) const; + virtual void set_hard_fork_version(uint64_t height, uint8_t version); + virtual uint8_t get_hard_fork_version(uint64_t height) const; + /** * @brief convert a tx output to a blob for storage * @@ -430,6 +437,9 @@ private: Db* m_spent_keys; + Db* m_hf_starting_heights; + Db* m_hf_versions; + uint64_t m_height; uint64_t m_num_outputs; std::string m_folder; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 25a34fc09..24bf4024d 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -493,6 +493,12 @@ public: // returns true if key image is present in spent key images storage virtual bool has_key_image(const crypto::key_image& img) const = 0; + // Hard fork related storage + virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height) = 0; + virtual uint64_t get_hard_fork_starting_height(uint8_t version) const = 0; + virtual void set_hard_fork_version(uint64_t height, uint8_t version) = 0; + virtual uint8_t get_hard_fork_version(uint64_t height) const = 0; + void set_auto_remove_logs(bool auto_remove) { m_auto_remove_logs = auto_remove; } bool m_open; diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 1583a0c06..2695d83f8 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -131,6 +131,15 @@ auto compare_uint64 = [](const MDB_val *a, const MDB_val *b) else return 1; }; +auto compare_uint8 = [](const MDB_val *a, const MDB_val *b) +{ + const uint8_t va = *(const uint8_t*)a->mv_data; + const uint8_t vb = *(const uint8_t*)b->mv_data; + if (va < vb) return -1; + else if (va == vb) return 0; + else return 1; +}; + int compare_hash32(const MDB_val *a, const MDB_val *b) { uint32_t *va = (uint32_t*) a->mv_data; @@ -166,6 +175,9 @@ const char* const LMDB_OUTPUTS = "outputs"; const char* const LMDB_OUTPUT_GINDICES = "output_gindices"; const char* const LMDB_SPENT_KEYS = "spent_keys"; +const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights"; +const char* const LMDB_HF_VERSIONS = "hf_versions"; + inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi, const std::string& error_string) { if (mdb_dbi_open(txn, name, flags, &dbi)) @@ -1022,6 +1034,9 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags) lmdb_db_open(txn, LMDB_SPENT_KEYS, MDB_CREATE, m_spent_keys, "Failed to open db handle for m_spent_keys"); + lmdb_db_open(txn, LMDB_HF_STARTING_HEIGHTS, MDB_CREATE, m_hf_starting_heights, "Failed to open db handle for m_hf_starting_heights"); + lmdb_db_open(txn, LMDB_HF_VERSIONS, MDB_CREATE, m_hf_versions, "Failed to open db handle for m_hf_versions"); + mdb_set_dupsort(txn, m_output_amounts, compare_uint64); mdb_set_dupsort(txn, m_tx_outputs, compare_uint64); mdb_set_compare(txn, m_spent_keys, compare_hash32); @@ -1029,6 +1044,8 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags) mdb_set_compare(txn, m_txs, compare_hash32); mdb_set_compare(txn, m_tx_unlocks, compare_hash32); mdb_set_compare(txn, m_tx_heights, compare_hash32); + mdb_set_compare(txn, m_hf_starting_heights, compare_uint8); + mdb_set_compare(txn, m_hf_versions, compare_uint64); // get and keep current height MDB_stat db_stats; @@ -2347,4 +2364,111 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std:: LOG_PRINT_L3("db3: " << db3); } +void BlockchainLMDB::set_hard_fork_starting_height(uint8_t version, uint64_t height) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + mdb_txn_safe txn; + mdb_txn_safe* txn_ptr = &txn; + if (m_batch_active) + txn_ptr = m_write_txn; + else + { + if (mdb_txn_begin(m_env, NULL, 0, txn)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + txn_ptr = &txn; + } + + MDB_val_copy val_key(version); + MDB_val_copy val_value(height); + if (auto result = mdb_put(*txn_ptr, m_hf_starting_heights, &val_key, &val_value, 0)) + throw1(DB_ERROR(std::string("Error adding hard fork starting height to db transaction: ").append(mdb_strerror(result)).c_str())); + + if (!m_batch_active) + txn.commit(); +} + +uint64_t BlockchainLMDB::get_hard_fork_starting_height(uint8_t version) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + mdb_txn_safe txn; + mdb_txn_safe* txn_ptr = &txn; + if (m_batch_active) + txn_ptr = m_write_txn; + else + { + if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + txn_ptr = &txn; + } + + MDB_val_copy val_key(version); + MDB_val val_ret; + auto result = mdb_get(*txn_ptr, m_hf_starting_heights, &val_key, &val_ret); + if (result == MDB_NOTFOUND) + return std::numeric_limits::max(); + if (result) + throw0(DB_ERROR("Error attempting to retrieve a hard fork starting height from the db")); + + if (!m_batch_active) + txn.commit(); + return *(const uint64_t*)val_ret.mv_data; +} + +void BlockchainLMDB::set_hard_fork_version(uint64_t height, uint8_t version) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + +//LOG_PRINT_L1("BlockchainLMDB::set_hard_fork_version: batch " << m_batch_active << ", height " << height << ", version " << (int)version); + mdb_txn_safe txn; + mdb_txn_safe* txn_ptr = &txn; + if (m_batch_active) + txn_ptr = m_write_txn; + else + { + if (mdb_txn_begin(m_env, NULL, 0, txn)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + txn_ptr = &txn; + } + + MDB_val_copy val_key(height); + MDB_val_copy val_value(version); + if (auto result = mdb_put(*txn_ptr, m_hf_versions, &val_key, &val_value, 0)) + throw1(DB_ERROR(std::string("Error adding hard fork version to db transaction: ").append(mdb_strerror(result)).c_str())); + + if (!m_batch_active) + txn.commit(); +} + +uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + mdb_txn_safe txn; + mdb_txn_safe* txn_ptr = &txn; + if (m_batch_active) + txn_ptr = m_write_txn; + else + { + if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + txn_ptr = &txn; + } + + MDB_val_copy val_key(height); + MDB_val val_ret; + auto result = mdb_get(*txn_ptr, m_hf_versions, &val_key, &val_ret); + if (result == MDB_NOTFOUND || result) + throw0(DB_ERROR("Error attempting to retrieve a hard fork version from the db")); + + if (!m_batch_active) + txn.commit(); + return *(const uint8_t*)val_ret.mv_data; +} + } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index b4c197803..380954295 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -238,6 +238,12 @@ private: virtual void remove_spent_key(const crypto::key_image& k_image); + // Hard fork + virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height); + virtual uint64_t get_hard_fork_starting_height(uint8_t version) const; + virtual void set_hard_fork_version(uint64_t height, uint8_t version); + virtual uint8_t get_hard_fork_version(uint64_t height) const; + /** * @brief convert a tx output to a blob for storage * @@ -292,6 +298,9 @@ private: MDB_dbi m_spent_keys; + MDB_dbi m_hf_starting_heights; + MDB_dbi m_hf_versions; + uint64_t m_height; uint64_t m_num_outputs; std::string m_folder; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 2ac50aefc..65d07dcf7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -69,10 +69,22 @@ extern "C" void slow_hash_free_state(); DISABLE_VS_WARNINGS(4267) +static const struct { + uint8_t version; + uint64_t height; + time_t time; +} hard_forks[] = { + // version 1 from the start of the blockchain + { 1, 1, 1341378000 }, + + // version 2 can start from block 1009827, setup on the 20th of september + { 2, 1009827, 1442763710 }, +}; + //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), -m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_hardfork(), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true) +m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -120,7 +132,7 @@ void Blockchain::serialize(archive_t & ar, const unsigned int version) if (version > 12) { - ar & m_hardfork; + ar & *m_hardfork; } LOG_PRINT_L3("Blockchain storage:" << std::endl << "m_blocks: " << m_db->height() << std::endl << "m_blocks_index: " << m_blocks_index.size() << std::endl << "m_transactions: " << m_transactions.size() << std::endl << "dummy_key_images_container: " << dummy_key_images_container.size() << std::endl << "m_alternative_chains: " << m_alternative_chains.size() << std::endl << "m_outputs: " << m_outputs.size() << std::endl << "m_invalid_blocks: " << m_invalid_blocks.size() << std::endl << "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); @@ -275,6 +287,11 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) m_db = db; + m_hardfork = new HardFork(*db); + for (size_t n = 0; n < sizeof(hard_forks) / sizeof(hard_forks[0]); ++n) + m_hardfork->add(hard_forks[n].version, hard_forks[n].height, hard_forks[n].time); + m_hardfork->init(); + // if the blockchain is new, add the genesis block // this feels kinda kludgy to do it this way, but can be looked at later. // TODO: add function to create and store genesis block, @@ -355,9 +372,6 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) } #endif - // reinitialize hard fork versions, since they're not saved in the DB - m_hardfork.reorganize_from_chain_height (m_db, 1); - LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); return true; @@ -424,6 +438,7 @@ bool Blockchain::deinit() LOG_PRINT_L0("There was an issue closing/storing the blockchain, shutting down now to prevent issues!"); } + delete m_hardfork; delete m_db; return true; } @@ -714,7 +729,7 @@ bool Blockchain::rollback_blockchain_switching(std::list& original_chain, 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(m_db, rollback_height); + m_hardfork->reorganize_from_chain_height(rollback_height); LOG_PRINT_L1("Rollback to height " << rollback_height << " was successful."); if (original_chain.size()) @@ -813,7 +828,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::listreorganize_from_chain_height(split_height); LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height(), LOG_LEVEL_0); return true; @@ -986,7 +1001,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m CRITICAL_REGION_BEGIN(m_blockchain_lock); height = m_db->height(); - b.major_version = m_hardfork.get_ideal_version(); + b.major_version = m_hardfork->get_ideal_version(); b.minor_version = 0; b.prev_id = get_tail_id(); b.timestamp = time(NULL); @@ -2259,9 +2274,9 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& TIME_MEASURE_START(t1); // this is a cheap test - if (!m_hardfork.check(bl)) + if (!m_hardfork->check(bl)) { - LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << bl.major_version << std::endl << "current: " << m_hardfork.get_current_version()); + LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << bl.major_version << std::endl << "current: " << m_hardfork->get_current_version()); return false; } @@ -2542,7 +2557,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& update_next_cumulative_size_limit(); // this will not fail since check succeeded above - m_hardfork.add(bl, new_height - 1); + m_hardfork->add(bl, new_height - 1); LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); if(m_show_time_stats) @@ -3080,10 +3095,10 @@ void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, HardFork::State Blockchain::get_hard_fork_state() const { - return m_hardfork.get_state(); + return m_hardfork->get_state(); } bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const { - return m_hardfork.get_voting_info(version, window, votes, threshold, voting); + return m_hardfork->get_voting_info(version, window, votes, threshold, voting); } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index e549ea2d0..a248682fc 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -160,8 +160,8 @@ namespace cryptonote void set_show_time_stats(bool stats) { m_show_time_stats = stats; } HardFork::State get_hard_fork_state() const; - uint8_t get_current_hard_fork_version() const { return m_hardfork.get_current_version(); } - uint8_t get_ideal_hard_fork_version() const { return m_hardfork.get_ideal_version(); } + uint8_t get_current_hard_fork_version() const { return m_hardfork->get_current_version(); } + uint8_t get_ideal_hard_fork_version() const { return m_hardfork->get_ideal_version(); } bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const; BlockchainDB& get_db() @@ -233,7 +233,7 @@ namespace cryptonote std::atomic m_is_blockchain_storing; bool m_enforce_dns_checkpoints; - HardFork m_hardfork; + HardFork *m_hardfork; template inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const; diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index 3003e74c8..d2e95b58e 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -35,15 +35,14 @@ using namespace cryptonote; -HardFork::HardFork(uint8_t original_version, time_t forked_time, time_t update_time, uint64_t max_history, int threshold_percent, uint64_t checkpoint_period): +HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, time_t forked_time, time_t update_time, uint64_t window_size, int threshold_percent): + db(db), original_version(original_version), forked_time(forked_time), update_time(update_time), - max_history(max_history), - threshold_percent(threshold_percent), - checkpoint_period(checkpoint_period) + window_size(window_size), + threshold_percent(threshold_percent) { - init(); } bool HardFork::add(uint8_t version, uint64_t height, time_t time) @@ -96,7 +95,7 @@ bool HardFork::add(const cryptonote::block &block, uint64_t height) const uint8_t version = get_effective_version(block); - while (versions.size() >= max_history) { + while (versions.size() >= window_size) { const uint8_t old_version = versions.front(); last_versions[old_version]--; assert(last_versions[old_version] >= 0); @@ -109,13 +108,12 @@ bool HardFork::add(const cryptonote::block &block, uint64_t height) uint8_t voted = get_voted_fork_index(height); if (voted > current_fork_index) { for (int v = heights[current_fork_index].version + 1; v <= heights[voted].version; ++v) { - starting[v] = height; + db.set_hard_fork_starting_height(v, height); } current_fork_index = voted; } - if (height % checkpoint_period == 0) - checkpoints.push_back(std::make_pair(height, current_fork_index)); + db.set_hard_fork_version(height, heights[current_fork_index].version); return true; } @@ -126,59 +124,66 @@ void HardFork::init() versions.clear(); for (size_t n = 0; n < 256; ++n) last_versions[n] = 0; - for (size_t n = 0; n < 256; ++n) - starting[n] = std::numeric_limits::max(); - add(original_version, 0, 0); - for (size_t n = 0; n <= original_version; ++n) - starting[n] = 0; - checkpoints.clear(); current_fork_index = 0; - vote_threshold = (uint32_t)ceilf(max_history * threshold_percent / 100.0f); + vote_threshold = (uint32_t)ceilf(window_size * threshold_percent / 100.0f); + + // restore state from DB + uint64_t height = db.height(); + if (height > window_size) + height -= window_size; + else + height = 1; + + bool populate = db.get_hard_fork_starting_height(original_version) == std::numeric_limits::max(); + if (populate) { + LOG_PRINT_L0("The DB has no hard fork info, reparsing from start"); + height = 1; + } + LOG_PRINT_L1("reorganizing from " << height); + reorganize_from_chain_height(height); + if (populate) { + // reorg will not touch the genesis block, use this as a flag for populating done + db.set_hard_fork_version(0, original_version); + db.set_hard_fork_starting_height(original_version, 0); + } + LOG_PRINT_L1("reorganization done"); } -bool HardFork::reorganize_from_block_height(const cryptonote::BlockchainDB *db, uint64_t height) +bool HardFork::reorganize_from_block_height(uint64_t height) { CRITICAL_REGION_LOCAL(lock); - if (!db || height >= db->height()) + if (height >= db.height()) return false; - while (!checkpoints.empty() && checkpoints.back().first > height) - checkpoints.pop_back(); + versions.clear(); - int v; - for (v = 255; v >= 0; --v) { - if (starting[v] <= height) - break; - if (starting[v] != std::numeric_limits::max()) { - starting[v] = std::numeric_limits::max(); - } - } - for (current_fork_index = 0; current_fork_index < heights.size(); ++current_fork_index) { - if (heights[current_fork_index].version == v) - break; - } for (size_t n = 0; n < 256; ++n) last_versions[n] = 0; - const uint64_t rescan_height = height >= (max_history - 1) ? height - (max_history - 1) : 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 (heights[current_fork_index].version > start_version) { + db.set_hard_fork_starting_height(heights[current_fork_index].version, std::numeric_limits::max()); + --current_fork_index; + } for (uint64_t h = rescan_height; h <= height; ++h) { - cryptonote::block b = db->get_block_from_height(h); + cryptonote::block b = db.get_block_from_height(h); const uint8_t v = get_effective_version(b); last_versions[v]++; versions.push_back(v); } - const uint64_t bc_height = db->height(); + 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); + add(db.get_block_from_height(h), h); } return true; } -bool HardFork::reorganize_from_chain_height(const cryptonote::BlockchainDB *db, uint64_t height) +bool HardFork::reorganize_from_chain_height(uint64_t height) { if (height == 0) return false; - return reorganize_from_block_height(db, height - 1); + return reorganize_from_block_height(height - 1); } int HardFork::get_voted_fork_index(uint64_t height) const @@ -219,18 +224,17 @@ HardFork::State HardFork::get_state() const uint8_t HardFork::get(uint64_t height) const { CRITICAL_REGION_LOCAL(lock); - for (size_t n = 1; n < 256; ++n) { - if (starting[n] > height) - return n - 1; + if (height > db.height()) { + assert(false); + return 255; } - assert(false); - return 255; + return db.get_hard_fork_version(height); } uint64_t HardFork::get_start_height(uint8_t version) const { CRITICAL_REGION_LOCAL(lock); - return starting[version]; + return db.get_hard_fork_starting_height(version); } uint8_t HardFork::get_current_version() const @@ -261,20 +265,3 @@ bool HardFork::get_voting_info(uint8_t version, uint32_t &window, uint32_t &vote return enabled; } -template -void HardFork::serialize(archive_t & ar, const unsigned int version) -{ - CRITICAL_REGION_LOCAL(lock); - ar & forked_time; - ar & update_time; - ar & max_history; - ar & threshold_percent; - ar & original_version; - ar & heights; - ar & last_versions; - ar & starting; - ar & current_fork_index; - ar & vote_threshold; - ar & checkpoint_period; - ar & checkpoints; -} diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index 3bcf46cff..bdac87f2c 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -28,8 +28,6 @@ #pragma once -#include -#include #include "syncobj.h" #include "cryptonote_core/cryptonote_basic.h" @@ -48,9 +46,8 @@ namespace cryptonote 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_MAX_HISTORY = 50; // supermajority window check length + static const uint64_t DEFAULT_WINDOW_SIZE = 50; // supermajority window check length static const int DEFAULT_THRESHOLD_PERCENT = 80; - static const uint64_t DEFAULT_CHECKPOINT_PERIOD = 1024; // mark a checkpoint every that many blocks /** * @brief creates a new HardFork object @@ -58,10 +55,10 @@ namespace cryptonote * @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 max_history the size of the window in blocks to consider for version voting + * @param window_size the size of the window in blocks to consider for version voting * @param threshold_percent the size of the majority in percents */ - HardFork(uint8_t original_version = 1, time_t forked_time = DEFAULT_FORKED_TIME, time_t update_time = DEFAULT_UPDATE_TIME, uint64_t max_history = DEFAULT_MAX_HISTORY, int threshold_percent = DEFAULT_THRESHOLD_PERCENT, uint64_t checkpoint_period = DEFAULT_CHECKPOINT_PERIOD); + 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, int threshold_percent = DEFAULT_THRESHOLD_PERCENT); /** * @brief add a new hardfork height @@ -74,6 +71,13 @@ namespace cryptonote */ bool add(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 * @@ -91,9 +95,7 @@ namespace cryptonote * 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 rescan the blockchain versions past the - * last checkpoint, potentially causing a large number of DB - * operations. + * hardfork object to reorganize. */ bool check(const cryptonote::block &block) const; @@ -117,8 +119,8 @@ namespace cryptonote * @param blockchain the blockchain * @param height of the last block kept from the previous blockchain */ - bool reorganize_from_block_height(const cryptonote::BlockchainDB *db, uint64_t height); - bool reorganize_from_chain_height(const cryptonote::BlockchainDB *db, uint64_t height); + bool reorganize_from_block_height(uint64_t height); + bool reorganize_from_chain_height(uint64_t height); /** * @brief returns current state at the given time @@ -176,23 +178,21 @@ namespace cryptonote /** * @brief returns the size of the voting window in blocks */ - uint64_t get_window_size() const { return max_history; } - - template - void serialize(archive_t & ar, const unsigned int version); + uint64_t get_window_size() const { return window_size; } private: - void init(); bool do_check(const cryptonote::block &block) const; int get_voted_fork_index(uint64_t height) const; uint8_t get_effective_version(const cryptonote::block &block) const; private: + BlockchainDB &db; + time_t forked_time; time_t update_time; - uint64_t max_history; + uint64_t window_size; int threshold_percent; uint8_t original_version; @@ -206,16 +206,11 @@ namespace cryptonote std::deque versions; /* rolling window of the last N blocks' versions */ unsigned int last_versions[256]; /* count of the block versions in the last N blocks */ - uint64_t starting[256]; /* block height at which each fork starts */ - unsigned int current_fork_index; + uint32_t current_fork_index; uint32_t vote_threshold; - uint64_t checkpoint_period; - std::vector> checkpoints; - mutable epee::critical_section lock; }; } // namespace cryptonote -BOOST_CLASS_VERSION(cryptonote::HardFork, 1) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 6b3c332a8..ad00ed60f 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -39,101 +39,13 @@ using namespace cryptonote; #define BLOCKS_PER_YEAR 525960 #define SECONDS_PER_YEAR 31557600 -static cryptonote::block mkblock(uint8_t version) -{ - cryptonote::block b; - b.major_version = version; - return b; -} - -TEST(empty_hardforks, Success) -{ - HardFork hf; - - ASSERT_TRUE(hf.get_state(time(NULL)) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); - - ASSERT_EQ(hf.get(0), 1); - ASSERT_EQ(hf.get(1), 1); - ASSERT_EQ(hf.get(100000000), 1); -} - -TEST(ordering, Success) -{ - HardFork hf; - - ASSERT_TRUE(hf.add(2, 2, 1)); - ASSERT_FALSE(hf.add(3, 3, 1)); - ASSERT_FALSE(hf.add(3, 2, 2)); - ASSERT_FALSE(hf.add(2, 3, 2)); - ASSERT_TRUE(hf.add(3, 10, 2)); - ASSERT_TRUE(hf.add(4, 20, 3)); - ASSERT_FALSE(hf.add(5, 5, 4)); -} - -TEST(states, Success) -{ - HardFork hf; - - ASSERT_TRUE(hf.add(2, BLOCKS_PER_YEAR, SECONDS_PER_YEAR)); - - ASSERT_TRUE(hf.get_state(0) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR / 2) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_UPDATE_TIME / 2) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + (HardFork::DEFAULT_UPDATE_TIME + HardFork::DEFAULT_FORKED_TIME) / 2) == HardFork::UpdateNeeded); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_FORKED_TIME * 2) == HardFork::LikelyForked); - - ASSERT_TRUE(hf.add(3, BLOCKS_PER_YEAR * 5, SECONDS_PER_YEAR * 5)); - - ASSERT_TRUE(hf.get_state(0) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR / 2) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_UPDATE_TIME / 2) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + (HardFork::DEFAULT_UPDATE_TIME + HardFork::DEFAULT_FORKED_TIME) / 2) == HardFork::Ready); - ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_FORKED_TIME * 2) == HardFork::Ready); -} - -TEST(steps_asap, Success) -{ - HardFork hf(1,1,1,1); - - // v h t - ASSERT_TRUE(hf.add(4, 2, 1)); - ASSERT_TRUE(hf.add(7, 4, 2)); - ASSERT_TRUE(hf.add(9, 6, 3)); - - for (uint64_t h = 0; h < 10; ++h) - hf.add(mkblock(10), h); - - ASSERT_EQ(hf.get(0), 1); - ASSERT_EQ(hf.get(1), 1); - ASSERT_EQ(hf.get(2), 4); - ASSERT_EQ(hf.get(3), 4); - ASSERT_EQ(hf.get(4), 7); - ASSERT_EQ(hf.get(5), 7); - ASSERT_EQ(hf.get(6), 9); - ASSERT_EQ(hf.get(7), 9); - ASSERT_EQ(hf.get(8), 9); - ASSERT_EQ(hf.get(9), 9); - ASSERT_EQ(hf.get(100000), 9); -} - -TEST(steps_1, Success) -{ - HardFork hf(1,1,1,1); - - // v h t - for (int n = 1 ; n < 10; ++n) - ASSERT_TRUE(hf.add(n+1, n, n)); - - for (uint64_t h = 0; h < 10; ++h) { - hf.add(mkblock(h+1), h); - ASSERT_EQ(hf.get(h), h+1); - } -} class TestDB: public BlockchainDB { public: - virtual void open(const std::string& filename, const int db_flags = 0) {} + virtual void open(const std::string& filename, const int db_flags = 0) { + for (size_t n = 0; n < 256; ++n) + starting_height[n] = std::numeric_limits::max(); + } virtual void close() {} virtual void sync() {} virtual void reset() {} @@ -197,36 +109,164 @@ public: virtual block get_block_from_height(const uint64_t& height) const { return blocks[height]; } + virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height) { + starting_height[version] = height; + } + virtual uint64_t get_hard_fork_starting_height(uint8_t version) const { + return starting_height[version]; + } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) { + printf("set_hard_fork_version(%lu, %u)\n", (unsigned long)height, version); + if (versions.size() <= height) versions.resize(height+1); versions[height] = version; + } + virtual uint8_t get_hard_fork_version(uint64_t height) const { + printf("get_hard_fork_version(%lu)\n", (unsigned long)height); + return versions[height]; + } + private: std::vector blocks; + uint64_t starting_height[256]; + std::deque versions; }; +static cryptonote::block mkblock(uint8_t version) +{ + cryptonote::block b; + b.major_version = version; + return b; +} + +TEST(empty_hardforks, Success) +{ + TestDB db; + HardFork hf(db); + + ASSERT_TRUE(hf.add(1, 0, 0)); + hf.init(); + ASSERT_TRUE(hf.get_state(time(NULL)) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); + + for (uint64_t h = 0; h <= 10; ++h) { + db.add_block(mkblock(1), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + ASSERT_EQ(hf.get(0), 1); + ASSERT_EQ(hf.get(1), 1); + ASSERT_EQ(hf.get(10), 1); +} + +TEST(ordering, Success) +{ + TestDB db; + HardFork hf(db); + + ASSERT_TRUE(hf.add(2, 2, 1)); + ASSERT_FALSE(hf.add(3, 3, 1)); + ASSERT_FALSE(hf.add(3, 2, 2)); + ASSERT_FALSE(hf.add(2, 3, 2)); + ASSERT_TRUE(hf.add(3, 10, 2)); + ASSERT_TRUE(hf.add(4, 20, 3)); + ASSERT_FALSE(hf.add(5, 5, 4)); +} + +TEST(states, Success) +{ + TestDB db; + HardFork hf(db); + + ASSERT_TRUE(hf.add(1, 0, 0)); + ASSERT_TRUE(hf.add(2, BLOCKS_PER_YEAR, SECONDS_PER_YEAR)); + + ASSERT_TRUE(hf.get_state(0) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_UPDATE_TIME / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + (HardFork::DEFAULT_UPDATE_TIME + HardFork::DEFAULT_FORKED_TIME) / 2) == HardFork::UpdateNeeded); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_FORKED_TIME * 2) == HardFork::LikelyForked); + + ASSERT_TRUE(hf.add(3, BLOCKS_PER_YEAR * 5, SECONDS_PER_YEAR * 5)); + + ASSERT_TRUE(hf.get_state(0) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_UPDATE_TIME / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + (HardFork::DEFAULT_UPDATE_TIME + HardFork::DEFAULT_FORKED_TIME) / 2) == HardFork::Ready); + ASSERT_TRUE(hf.get_state(SECONDS_PER_YEAR + HardFork::DEFAULT_FORKED_TIME * 2) == HardFork::Ready); +} + +TEST(steps_asap, Success) +{ + TestDB db; + HardFork hf(db, 1,1,1,1); + + // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); + ASSERT_TRUE(hf.add(4, 2, 1)); + ASSERT_TRUE(hf.add(7, 4, 2)); + ASSERT_TRUE(hf.add(9, 6, 3)); + hf.init(); + + for (uint64_t h = 0; h < 10; ++h) { + db.add_block(mkblock(10), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + ASSERT_EQ(hf.get(0), 1); + ASSERT_EQ(hf.get(1), 1); + ASSERT_EQ(hf.get(2), 4); + ASSERT_EQ(hf.get(3), 4); + ASSERT_EQ(hf.get(4), 7); + ASSERT_EQ(hf.get(5), 7); + ASSERT_EQ(hf.get(6), 9); + ASSERT_EQ(hf.get(7), 9); + ASSERT_EQ(hf.get(8), 9); + ASSERT_EQ(hf.get(9), 9); +} + +TEST(steps_1, Success) +{ + TestDB db; + HardFork hf(db, 1,1,1,1); + + ASSERT_TRUE(hf.add(1, 0, 0)); + for (int n = 1 ; n < 10; ++n) + ASSERT_TRUE(hf.add(n+1, n, n)); + hf.init(); + + for (uint64_t h = 0 ; h < 10; ++h) { + db.add_block(mkblock(h+1), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 0; h < 10; ++h) { + ASSERT_EQ(hf.get(h), h+1); + } +} + TEST(reorganize, Same) { for (int history = 1; history <= 12; ++history) { - for (uint64_t checkpoint_period = 1; checkpoint_period <= 16; checkpoint_period++) { - HardFork hf(1, 1, 1, history, 100, checkpoint_period); - TestDB db; + TestDB db; + HardFork hf(db, 1, 1, 1, history, 100); - // v h t - ASSERT_TRUE(hf.add(4, 2, 1)); - ASSERT_TRUE(hf.add(7, 4, 2)); - ASSERT_TRUE(hf.add(9, 6, 3)); + // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); + ASSERT_TRUE(hf.add(4, 2, 1)); + ASSERT_TRUE(hf.add(7, 4, 2)); + ASSERT_TRUE(hf.add(9, 6, 3)); + hf.init(); - // index 0 1 2 3 4 5 6 7 8 9 - static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; - for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); - hf.add(db.get_block_from_height(h), h); - } + // index 0 1 2 3 4 5 6 7 8 9 + static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + for (uint64_t h = 0; h < 20; ++h) { + db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } - for (uint64_t rh = 0; rh < 20; ++rh) { - hf.reorganize_from_block_height(&db, rh); - for (int hh = 0; hh < 20; ++hh) { - uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; - ASSERT_EQ(hf.get(hh), version); - } - ASSERT_EQ(hf.get(100000), 9); + for (uint64_t rh = 0; rh < 20; ++rh) { + hf.reorganize_from_block_height(rh); + for (int hh = 0; hh < 20; ++hh) { + uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; + ASSERT_EQ(hf.get(hh), version); } } } @@ -235,74 +275,87 @@ TEST(reorganize, Same) TEST(reorganize, Changed) { int history = 4; - for (uint64_t checkpoint_period = 1; checkpoint_period <= 16; checkpoint_period++) { - HardFork hf(1, 1, 1, 4, 100, checkpoint_period); - TestDB db; + TestDB db; + HardFork hf(db, 1, 1, 1, 4, 100); - // v h t - ASSERT_TRUE(hf.add(4, 2, 1)); - ASSERT_TRUE(hf.add(7, 4, 2)); - ASSERT_TRUE(hf.add(9, 6, 3)); + // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); + ASSERT_TRUE(hf.add(4, 2, 1)); + ASSERT_TRUE(hf.add(7, 4, 2)); + ASSERT_TRUE(hf.add(9, 6, 3)); + hf.init(); - // index 0 1 2 3 4 5 6 7 8 9 - static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; - for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); - hf.add(db.get_block_from_height(h), h); - } + // index 0 1 2 3 4 5 6 7 8 9 + static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + for (uint64_t h = 0; h < 16; ++h) { + db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); + ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); + } - for (uint64_t rh = 0; rh < 20; ++rh) { - hf.reorganize_from_block_height(&db, rh); - for (int hh = 0; hh < 20; ++hh) { - uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; - ASSERT_EQ(hf.get(hh), version); - } - ASSERT_EQ(hf.get(100000), 9); + for (uint64_t rh = 0; rh < 16; ++rh) { + hf.reorganize_from_block_height(rh); + for (int hh = 0; hh < 16; ++hh) { + uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; + ASSERT_EQ(hf.get(hh), version); } + } - // delay a bit for 9, and go back to 1 to check it stays at 9 - static const uint8_t block_versions_new[] = { 1, 1, 4, 4, 7, 7, 4, 7, 7, 7, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1 }; - static const uint8_t expected_versions_new[] = { 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 7, 7, 7, 9, 9, 9, 9, 9, 9, 9 }; - for (uint64_t h = 3; h < 20; ++h) { - db.remove_block(); - } - ASSERT_EQ(db.height(), 3); - hf.reorganize_from_block_height(&db, 2); - for (uint64_t h = 3; h < 20; ++h) { - db.add_block(mkblock(block_versions_new[h]), 0, 0, 0, crypto::hash()); - hf.add(db.get_block_from_height(h), h); - } - for (int hh = 0; hh < 20; ++hh) { - ASSERT_EQ(hf.get(hh), expected_versions_new[hh]); - } - ASSERT_EQ(hf.get(100000), 9); + // delay a bit for 9, and go back to 1 to check it stays at 9 + static const uint8_t block_versions_new[] = { 1, 1, 4, 4, 7, 7, 4, 7, 7, 7, 9, 9, 9, 9, 9, 1 }; + static const uint8_t expected_versions_new[] = { 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 7, 7, 7, 9, 9, 9 }; + for (uint64_t h = 3; h < 16; ++h) { + db.remove_block(); + } + ASSERT_EQ(db.height(), 3); + hf.reorganize_from_block_height(2); + for (uint64_t h = 3; h < 16; ++h) { + db.add_block(mkblock(block_versions_new[h]), 0, 0, 0, crypto::hash()); + bool ret = hf.add(db.get_block_from_height(h), h); + ASSERT_EQ (ret, h < 15); + } + db.remove_block(); // last block added to the blockchain, but not hf + ASSERT_EQ(db.height(), 15); + for (int hh = 0; hh < 15; ++hh) { + ASSERT_EQ(hf.get(hh), expected_versions_new[hh]); } } TEST(voting, threshold) { for (int threshold = 87; threshold <= 88; ++threshold) { - HardFork hf(1, 1, 1, 8, threshold, 10); TestDB db; + HardFork hf(db, 1, 1, 1, 8, threshold); // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); ASSERT_TRUE(hf.add(2, 2, 1)); + hf.init(); - for (uint64_t h = 0; h < 10; ++h) { + for (uint64_t h = 0; h <= 8; ++h) { uint8_t v = 1 + !!(h % 8); - hf.add(mkblock(v), h); - uint8_t expected = threshold == 88 ? 1 : h < 7 ? 1 : 2; - ASSERT_EQ(hf.get(h), expected); + db.add_block(mkblock(v), 0, 0, 0, crypto::hash()); + bool ret = hf.add(db.get_block_from_height(h), h); + if (h >= 8 && threshold == 87) { + ASSERT_FALSE(ret); + } + else { + ASSERT_TRUE(ret); + uint8_t expected = threshold == 88 ? 1 : h < 7 ? 1 : 2; + ASSERT_EQ(hf.get(h), expected); + } } } } TEST(new_blocks, denied) { - HardFork hf(1, 1, 1, 4, 50, 10); + TestDB db; + HardFork hf(db, 1, 1, 1, 4, 50); // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); ASSERT_TRUE(hf.add(2, 2, 1)); + hf.init(); ASSERT_FALSE(hf.add(mkblock(0), 0)); ASSERT_TRUE(hf.add(mkblock(1), 0)); @@ -322,10 +375,13 @@ TEST(new_blocks, denied) TEST(new_version, early) { - HardFork hf(1, 1, 1, 4, 50, 10); + TestDB db; + HardFork hf(db, 1, 1, 1, 4, 50); // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); ASSERT_TRUE(hf.add(2, 4, 1)); + hf.init(); ASSERT_FALSE(hf.add(mkblock(0), 0)); ASSERT_TRUE(hf.add(mkblock(2), 0)); @@ -342,12 +398,14 @@ TEST(new_version, early) TEST(reorganize, changed) { - HardFork hf(1, 1, 1, 4, 50, 10); TestDB db; + HardFork hf(db, 1, 1, 1, 4, 50); // v h t + ASSERT_TRUE(hf.add(1, 0, 0)); ASSERT_TRUE(hf.add(2, 2, 1)); ASSERT_TRUE(hf.add(3, 5, 2)); + hf.init(); #define ADD(v, h, a) \ do { \ @@ -376,10 +434,10 @@ TEST(reorganize, changed) // pop a few blocks and check current version goes back down db.remove_block(); - hf.reorganize_from_block_height(&db, 8); + hf.reorganize_from_block_height(8); ASSERT_EQ(hf.get_current_version(), 3); db.remove_block(); - hf.reorganize_from_block_height(&db, 7); + hf.reorganize_from_block_height(7); ASSERT_EQ(hf.get_current_version(), 2); db.remove_block(); ASSERT_EQ(hf.get_current_version(), 2); From 8d67a9abd45c02355b62d53dcc334a6b06f9d747 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 21 Sep 2015 17:11:40 +0100 Subject: [PATCH 12/25] tests: remove leftover debug traces in hardfork test --- tests/unit_tests/hardfork.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index ad00ed60f..66431e7a8 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -116,11 +116,9 @@ public: return starting_height[version]; } virtual void set_hard_fork_version(uint64_t height, uint8_t version) { - printf("set_hard_fork_version(%lu, %u)\n", (unsigned long)height, version); if (versions.size() <= height) versions.resize(height+1); versions[height] = version; } virtual uint8_t get_hard_fork_version(uint64_t height) const { - printf("get_hard_fork_version(%lu)\n", (unsigned long)height); return versions[height]; } From 9fa0f4aa4c92d9f273316e79c8036e24096855a3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 22 Sep 2015 20:35:19 +0100 Subject: [PATCH 13/25] blockchain: use different hard fork settings for testnet and mainnet --- src/cryptonote_core/blockchain.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 81627bb85..f415b01f4 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -73,7 +73,7 @@ static const struct { uint8_t version; uint64_t height; time_t time; -} hard_forks[] = { +} mainnet_hard_forks[] = { // version 1 from the start of the blockchain { 1, 1, 1341378000 }, @@ -81,6 +81,15 @@ static const struct { { 2, 1009827, 1442763710 }, }; +static const struct { + uint8_t version; + uint64_t height; + time_t time; +} testnet_hard_forks[] = { + // version 1 from the start of the blockchain + { 1, 1, 1341378000 }, +}; + //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), @@ -288,8 +297,15 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) m_db = db; m_hardfork = new HardFork(*db); - for (size_t n = 0; n < sizeof(hard_forks) / sizeof(hard_forks[0]); ++n) - m_hardfork->add(hard_forks[n].version, hard_forks[n].height, hard_forks[n].time); + if (testnet) { + for (size_t n = 0; n < sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); ++n) + m_hardfork->add(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].time); + } + else + { + for (size_t n = 0; n < sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); ++n) + m_hardfork->add(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].time); + } m_hardfork->init(); // if the blockchain is new, add the genesis block From a803befcd313c3b643eaa58d5d97050f40fa7c88 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Sep 2015 13:25:22 +0100 Subject: [PATCH 14/25] hardfork: change window semantics to not count the newly added block This allows knowing the hard fork a block must obey in order to be added to the blockchain. The previous semantics would use that new block's version vote to determine this hard fork, which made it impossible to use the rules to validate transactions entering the tx pool (and made it impossible to validate a block before adding it to the blockchain). --- src/cryptonote_core/hardfork.cpp | 25 ++++++++++++++++++++----- tests/unit_tests/hardfork.cpp | 23 +++++++++++++---------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index d2e95b58e..f4df1f9c6 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -43,6 +43,10 @@ HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, time_ window_size(window_size), threshold_percent(threshold_percent) { + if (window_size == 0) + throw "window_size needs to be strictly positive"; + if (threshold_percent > 100) + throw "threshold_percent needs to be between 0 and 100"; } bool HardFork::add(uint8_t version, uint64_t height, time_t time) @@ -93,6 +97,8 @@ bool HardFork::add(const cryptonote::block &block, uint64_t height) if (!do_check(block)) return false; + db.set_hard_fork_version(height, heights[current_fork_index].version); + const uint8_t version = get_effective_version(block); while (versions.size() >= window_size) { @@ -105,16 +111,15 @@ bool HardFork::add(const cryptonote::block &block, uint64_t height) last_versions[version]++; versions.push_back(version); - uint8_t voted = get_voted_fork_index(height); + uint8_t voted = get_voted_fork_index(height + 1); if (voted > current_fork_index) { for (int v = heights[current_fork_index].version + 1; v <= heights[voted].version; ++v) { - db.set_hard_fork_starting_height(v, height); + // we reached the vote threshold with this block, next one will be forked + db.set_hard_fork_starting_height(v, height + 1); } current_fork_index = voted; } - db.set_hard_fork_version(height, heights[current_fork_index].version); - return true; } @@ -159,7 +164,7 @@ bool HardFork::reorganize_from_block_height(uint64_t height) 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 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 (heights[current_fork_index].version > start_version) { db.set_hard_fork_starting_height(heights[current_fork_index].version, std::numeric_limits::max()); @@ -171,6 +176,16 @@ bool HardFork::reorganize_from_block_height(uint64_t height) last_versions[v]++; versions.push_back(v); } + + uint8_t voted = get_voted_fork_index(height + 1); + if (voted > current_fork_index) { + for (int v = heights[current_fork_index].version + 1; v <= heights[voted].version; ++v) { + // we reached the vote threshold with this block, next one will be forked + db.set_hard_fork_starting_height(v, height + 1); + } + 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); diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 66431e7a8..1fd179950 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -236,7 +236,7 @@ TEST(steps_1, Success) } for (uint64_t h = 0; h < 10; ++h) { - ASSERT_EQ(hf.get(h), h+1); + ASSERT_EQ(hf.get(h), std::max(1,(int)h)); } } @@ -263,7 +263,7 @@ TEST(reorganize, Same) for (uint64_t rh = 0; rh < 20; ++rh) { hf.reorganize_from_block_height(rh); for (int hh = 0; hh < 20; ++hh) { - uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; + uint8_t version = hh >= history ? block_versions[hh - history] : 1; ASSERT_EQ(hf.get(hh), version); } } @@ -283,8 +283,10 @@ TEST(reorganize, Changed) ASSERT_TRUE(hf.add(9, 6, 3)); hf.init(); + // fork 4 7 9 // index 0 1 2 3 4 5 6 7 8 9 static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 16; ++h) { db.add_block(mkblock(block_versions[h]), 0, 0, 0, crypto::hash()); ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); @@ -293,14 +295,13 @@ TEST(reorganize, Changed) for (uint64_t rh = 0; rh < 16; ++rh) { hf.reorganize_from_block_height(rh); for (int hh = 0; hh < 16; ++hh) { - uint8_t version = hh >= (history-1) ? block_versions[hh - (history-1)] : 1; - ASSERT_EQ(hf.get(hh), version); + ASSERT_EQ(hf.get(hh), expected_versions[hh]); } } // delay a bit for 9, and go back to 1 to check it stays at 9 static const uint8_t block_versions_new[] = { 1, 1, 4, 4, 7, 7, 4, 7, 7, 7, 9, 9, 9, 9, 9, 1 }; - static const uint8_t expected_versions_new[] = { 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 7, 7, 7, 9, 9, 9 }; + static const uint8_t expected_versions_new[] = { 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 7, 7, 7, 9, 9 }; for (uint64_t h = 3; h < 16; ++h) { db.remove_block(); } @@ -334,11 +335,13 @@ TEST(voting, threshold) db.add_block(mkblock(v), 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); if (h >= 8 && threshold == 87) { + // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1 ASSERT_FALSE(ret); } else { + // for threshold 88, we never reach the threshold ASSERT_TRUE(ret); - uint8_t expected = threshold == 88 ? 1 : h < 7 ? 1 : 2; + uint8_t expected = threshold == 88 ? 1 : h < 8 ? 1 : 2; ASSERT_EQ(hf.get(h), expected); } } @@ -368,7 +371,7 @@ TEST(new_blocks, denied) ASSERT_FALSE(hf.add(mkblock(1), 9)); // so this one can't get added ASSERT_TRUE(hf.add(mkblock(2), 10)); - ASSERT_EQ(hf.get_start_height(2), 8); + ASSERT_EQ(hf.get_start_height(2), 9); } TEST(new_version, early) @@ -426,8 +429,8 @@ TEST(reorganize, changed) ADD_TRUE(3, 7); ADD_TRUE(4, 8); ADD_TRUE(4, 9); - ASSERT_EQ(hf.get_start_height(2), 3); - ASSERT_EQ(hf.get_start_height(3), 8); + ASSERT_EQ(hf.get_start_height(2), 4); // reaches threshold 2 at height 3, so height 4 forks + ASSERT_EQ(hf.get_start_height(3), 9); ASSERT_EQ(hf.get_current_version(), 3); // pop a few blocks and check current version goes back down @@ -444,7 +447,7 @@ TEST(reorganize, changed) ADD_TRUE(2, 7); ADD_TRUE(2, 8); ADD_TRUE(2, 9); - ASSERT_EQ(hf.get_start_height(2), 3); // unchanged + ASSERT_EQ(hf.get_start_height(2), 4); // unchanged ASSERT_EQ(hf.get_current_version(), 2); // we did not bump to 3 this time ASSERT_EQ(hf.get_start_height(3), std::numeric_limits::max()); // not yet } From 969c2c886784a2cbbfa4f028a82874293d38a56e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 22 Sep 2015 20:43:19 +0100 Subject: [PATCH 15/25] blockchain: on hardfork 2, allow miners to claim less money than allowed So they can avoid dust if they so wish --- src/cryptonote_core/blockchain.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f415b01f4..979c82e40 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -969,10 +969,14 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl LOG_PRINT_L1("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); return false; } - if(base_reward + fee != money_in_use) + // From hard fork 2, we allow a miner to claim less block reward than is allowed, in case a miner wants less dust + if (m_hardfork->get_current_version() < 2) { - LOG_PRINT_L1("coinbase transaction doesn't use full amount of block reward: spent: " << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")"); - return false; + if(base_reward + fee != money_in_use) + { + LOG_PRINT_L1("coinbase transaction doesn't use full amount of block reward: spent: " << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")"); + return false; + } } return true; } From d9236396def1e902233dd7c54f31681d9f87f221 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Sep 2015 14:06:00 +0100 Subject: [PATCH 16/25] hardfork: remove use of GNU extension for initializing object --- src/cryptonote_core/hardfork.cpp | 4 ++-- src/cryptonote_core/hardfork.h | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index f4df1f9c6..30c5d94c9 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -64,7 +64,7 @@ bool HardFork::add(uint8_t version, uint64_t height, time_t time) if (time <= heights.back().time) return false; } - heights.push_back({version: version, height: height, time: time}); + heights.push_back(Params(version, height, time)); return true; } @@ -239,7 +239,7 @@ HardFork::State HardFork::get_state() const uint8_t HardFork::get(uint64_t height) const { CRITICAL_REGION_LOCAL(lock); - if (height > db.height()) { + if (height >= db.height()) { assert(false); return 255; } diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index bdac87f2c..946e5febc 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -197,11 +197,12 @@ namespace cryptonote uint8_t original_version; - typedef struct { + struct Params { uint8_t version; uint64_t height; time_t time; - } Params; + Params(uint8_t version, uint64_t height, time_t time): version(version), height(height), time(time) {} + }; std::vector heights; std::deque versions; /* rolling window of the last N blocks' versions */ From 4cf3028ba5dea16a33653090f0e371f0fbf5817a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Sep 2015 16:22:57 +0100 Subject: [PATCH 17/25] hardfork: rescan speedup Add a block height before which version 1 is assumed Use DB transactions --- src/cryptonote_core/blockchain.cpp | 5 +++- src/cryptonote_core/hardfork.cpp | 41 ++++++++++++++++++++++-------- src/cryptonote_core/hardfork.h | 10 +++++--- tests/unit_tests/hardfork.cpp | 16 ++++++------ 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 979c82e40..3ab4e43c3 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -80,6 +80,7 @@ static const struct { // version 2 can start from block 1009827, setup on the 20th of september { 2, 1009827, 1442763710 }, }; +static const uint64_t mainnet_hard_fork_version_1_till = 750000; static const struct { uint8_t version; @@ -89,6 +90,7 @@ static const struct { // version 1 from the start of the blockchain { 1, 1, 1341378000 }, }; +static const uint64_t testnet_hard_fork_version_1_till = 540000; //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : @@ -296,13 +298,14 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) m_db = db; - m_hardfork = new HardFork(*db); if (testnet) { + m_hardfork = new HardFork(*db, 1, testnet_hard_fork_version_1_till); for (size_t n = 0; n < sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); ++n) m_hardfork->add(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].time); } else { + m_hardfork = new HardFork(*db, 1, mainnet_hard_fork_version_1_till); for (size_t n = 0; n < sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); ++n) m_hardfork->add(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].time); } diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index 30c5d94c9..4b40a4cf3 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -35,9 +35,10 @@ using namespace cryptonote; -HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, time_t forked_time, time_t update_time, uint64_t window_size, int threshold_percent): +HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, uint64_t original_version_till_height, time_t forked_time, time_t update_time, uint64_t window_size, int threshold_percent): db(db), original_version(original_version), + original_version_till_height(original_version_till_height), forked_time(forked_time), update_time(update_time), window_size(window_size), @@ -68,9 +69,8 @@ bool HardFork::add(uint8_t version, uint64_t height, time_t time) return true; } -uint8_t HardFork::get_effective_version(const cryptonote::block &block) const +uint8_t HardFork::get_effective_version(uint8_t version) const { - uint8_t version = block.major_version; if (!heights.empty()) { uint8_t max_version = heights.back().version; if (version > max_version) @@ -79,27 +79,27 @@ uint8_t HardFork::get_effective_version(const cryptonote::block &block) const return version; } -bool HardFork::do_check(const cryptonote::block &block) const +bool HardFork::do_check(uint8_t version) const { - return block.major_version >= heights[current_fork_index].version; + return version >= heights[current_fork_index].version; } bool HardFork::check(const cryptonote::block &block) const { CRITICAL_REGION_LOCAL(lock); - return do_check(block); + return do_check(block.major_version); } -bool HardFork::add(const cryptonote::block &block, uint64_t height) +bool HardFork::add(uint8_t block_version, uint64_t height) { CRITICAL_REGION_LOCAL(lock); - if (!do_check(block)) + if (!do_check(block_version)) return false; db.set_hard_fork_version(height, heights[current_fork_index].version); - const uint8_t version = get_effective_version(block); + const uint8_t version = get_effective_version(block_version); while (versions.size() >= window_size) { const uint8_t old_version = versions.front(); @@ -123,6 +123,11 @@ bool HardFork::add(const cryptonote::block &block, uint64_t height) return true; } +bool HardFork::add(const cryptonote::block &block, uint64_t height) +{ + return add(block.major_version, height); +} + void HardFork::init() { CRITICAL_REGION_LOCAL(lock); @@ -154,12 +159,24 @@ void HardFork::init() LOG_PRINT_L1("reorganization done"); } +uint8_t HardFork::get_block_version(uint64_t height) const +{ + if (height <= original_version_till_height) + return original_version; + + const cryptonote::block &block = db.get_block_from_height(height); + return block.major_version; +} + bool HardFork::reorganize_from_block_height(uint64_t height) { CRITICAL_REGION_LOCAL(lock); if (height >= db.height()) return false; + //db.set_batch_transactions(true); + //db.batch_start(); + versions.clear(); for (size_t n = 0; n < 256; ++n) @@ -172,7 +189,7 @@ bool HardFork::reorganize_from_block_height(uint64_t height) } 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(b); + const uint8_t v = get_effective_version(b.major_version); last_versions[v]++; versions.push_back(v); } @@ -188,9 +205,11 @@ bool HardFork::reorganize_from_block_height(uint64_t height) 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); + add(get_block_version(h), h); } + //db.batch_stop(); + return true; } diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index 946e5febc..2fcc2d539 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -44,6 +44,7 @@ namespace cryptonote 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 = 50; // supermajority window check length @@ -58,7 +59,7 @@ namespace cryptonote * @param window_size the size of the window in blocks to consider for version voting * @param 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, int threshold_percent = DEFAULT_THRESHOLD_PERCENT); + HardFork(cryptonote::BlockchainDB &db, uint8_t original_version = 1, uint64_t original_version_till_height = DEFAULT_ORIGINAL_VERSION_TILL_HEIGHT, time_t forked_time = DEFAULT_FORKED_TIME, time_t update_time = DEFAULT_UPDATE_TIME, uint64_t window_size = DEFAULT_WINDOW_SIZE, int threshold_percent = DEFAULT_THRESHOLD_PERCENT); /** * @brief add a new hardfork height @@ -182,9 +183,11 @@ namespace cryptonote private: - bool do_check(const cryptonote::block &block) const; + uint8_t get_block_version(uint64_t height) const; + bool do_check(uint8_t version) const; int get_voted_fork_index(uint64_t height) const; - uint8_t get_effective_version(const cryptonote::block &block) const; + uint8_t get_effective_version(uint8_t version) const; + bool add(uint8_t block_version, uint64_t height); private: @@ -196,6 +199,7 @@ namespace cryptonote int threshold_percent; uint8_t original_version; + uint64_t original_version_till_height; struct Params { uint8_t version; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 1fd179950..a8d2d381f 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -194,7 +194,7 @@ TEST(states, Success) TEST(steps_asap, Success) { TestDB db; - HardFork hf(db, 1,1,1,1); + HardFork hf(db, 1,0,1,1,1); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); @@ -223,7 +223,7 @@ TEST(steps_asap, Success) TEST(steps_1, Success) { TestDB db; - HardFork hf(db, 1,1,1,1); + HardFork hf(db, 1,0,1,1,1); ASSERT_TRUE(hf.add(1, 0, 0)); for (int n = 1 ; n < 10; ++n) @@ -244,7 +244,7 @@ TEST(reorganize, Same) { for (int history = 1; history <= 12; ++history) { TestDB db; - HardFork hf(db, 1, 1, 1, history, 100); + HardFork hf(db, 1, 0, 1, 1, history, 100); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); @@ -274,7 +274,7 @@ TEST(reorganize, Changed) { int history = 4; TestDB db; - HardFork hf(db, 1, 1, 1, 4, 100); + HardFork hf(db, 1, 0, 1, 1, 4, 100); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); @@ -323,7 +323,7 @@ TEST(voting, threshold) { for (int threshold = 87; threshold <= 88; ++threshold) { TestDB db; - HardFork hf(db, 1, 1, 1, 8, threshold); + HardFork hf(db, 1, 0, 1, 1, 8, threshold); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); @@ -351,7 +351,7 @@ TEST(voting, threshold) TEST(new_blocks, denied) { TestDB db; - HardFork hf(db, 1, 1, 1, 4, 50); + HardFork hf(db, 1, 0, 1, 1, 4, 50); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); @@ -377,7 +377,7 @@ TEST(new_blocks, denied) TEST(new_version, early) { TestDB db; - HardFork hf(db, 1, 1, 1, 4, 50); + HardFork hf(db, 1, 0, 1, 1, 4, 50); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); @@ -400,7 +400,7 @@ TEST(new_version, early) TEST(reorganize, changed) { TestDB db; - HardFork hf(db, 1, 1, 1, 4, 50); + HardFork hf(db, 1, 0, 1, 1, 4, 50); // v h t ASSERT_TRUE(hf.add(1, 0, 0)); From 82a38d0d3be33254c8f7aa78a0dc8ec64fe11e58 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Sep 2015 17:56:43 +0100 Subject: [PATCH 18/25] hardfork: make the voting window a week --- src/cryptonote_core/hardfork.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index 2fcc2d539..6b98b9fa1 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -47,7 +47,7 @@ namespace cryptonote 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 = 50; // supermajority window check length + static const uint64_t DEFAULT_WINDOW_SIZE = 10080; // supermajority window check length - a week static const int DEFAULT_THRESHOLD_PERCENT = 80; /** From d1c3c3ba59a3a7d131a7b1e79ecd3aa81a2e9f1c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Sep 2015 17:57:01 +0100 Subject: [PATCH 19/25] blockchain: on hardfork 2, require mixin 2 at least if possible --- src/cryptonote_core/blockchain.cpp | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3ab4e43c3..4609fc137 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2006,6 +2006,43 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_bloc return true; } + // from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others + // if one output cannot mix with 2 others, we accept at most 1 output that can mix + if (m_hardfork->get_current_version() >= 2) + { + size_t n_unmixable = 0, n_mixable = 0; + size_t mixin = std::numeric_limits::max(); + for (const auto& txin : tx.vin) + { + // non txin_to_key inputs will be rejected below + if (txin.type() == typeid(txin_to_key)) + { + const txin_to_key& in_to_key = boost::get(txin); + uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount); + // n_outputs includes the output we're considering + if (n_outputs <= 2) + ++n_unmixable; + else + ++n_mixable; + if (in_to_key.key_offsets.size() - 1 < mixin) + mixin = in_to_key.key_offsets.size() - 1; + } + } + if (mixin < 2) + { + if (n_unmixable == 0) + { + LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and no unmixable inputs"); + return false; + } + if (n_mixable > 1) + { + LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and more than one mixable input with unmixable inputs"); + return false; + } + } + } + auto it = m_check_txin_table.find(tx_prefix_hash); if(it == m_check_txin_table.end()) { From 1c3643ef352effce2eb3c51856197dde918e4297 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Sep 2015 17:30:40 +0100 Subject: [PATCH 20/25] dns_utils: fix wrong asserts Braino. --- src/common/dns_utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 5cb5ed2de..2ae5d9624 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -102,7 +102,7 @@ namespace tools // fuck it, I'm tired of dealing with getnameinfo()/inet_ntop/etc std::string ipv4_to_string(const char* src, size_t len) { - assert(memchr(src, 0, len)); + assert(len >= 4); std::stringstream ss; unsigned int bytes[4]; @@ -122,7 +122,7 @@ std::string ipv4_to_string(const char* src, size_t len) // stop-gap measure and to make the tests pass at least... std::string ipv6_to_string(const char* src, size_t len) { - assert(memchr(src, 0, len)); + assert(len >= 8); std::stringstream ss; unsigned int bytes[8]; From bd675d1782d678995f3d932e8f51d4b451cc1b74 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 4 Oct 2015 12:50:10 +0100 Subject: [PATCH 21/25] gpg_keys: update my key with a later expiration date --- utils/gpg_keys/moneromooo.asc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/utils/gpg_keys/moneromooo.asc b/utils/gpg_keys/moneromooo.asc index 80cb7ae0b..e30809608 100644 --- a/utils/gpg_keys/moneromooo.asc +++ b/utils/gpg_keys/moneromooo.asc @@ -1,5 +1,5 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v2 +Version: GnuPG v1 mQINBFQym34BEADDKCspvziDW0f+T9i6iOewFO9m2XTWKXlQutCPgTkIlZZUrcTR K+ApsfPxk+PBWgucQDPv/nJVs0CNaSzqewxk7Swjsf8+YjvRmxSSg/NQEgsiBx/s @@ -13,19 +13,19 @@ XCGtzeiNJSRoklNSTpUo+qQ+bLjETjfogUvq8HpYU+oGi4qaHf+Hkmp4c5Mv3Gfh p7gDvxXOGxzq0sqfPTWTBdCj1OPfunHbbeH8ypwBlNpwVG40fJdya+Dqjwu25qX6 Xh5vxLzeJTBmlawa97MCliPvzzJgW9qHRVCa9lLloGVYLiUOS0N+dZ/r/QARAQAB tD5tb25lcm9tb29vLW1vbmVybyA8bW9uZXJvbW9vby1tb25lcm9AdXNlcnMubm9y -ZXBseS5naXRodWIuY29tPokCPwQTAQIAKQUCVDKfzgIbAwUJAdqcAAcLCQgHAwIB -BhUIAgkKCwQWAgMBAh4BAheAAAoJEGhvB0VNbO/DUvMQAJrOo9JZKD8gm1TvSE8r -ERwO59CaDk6IwZXhXFncUEJzgOCNNYUc0ulI2Y1R/abJLarrjcKZQ+yxGPsh6gVL -gRckMpRix694tAND85ebl/y2XTcblwp971QJJZm0RltdpXykymo+P2AFw/5KLhcT -XHrzFkrMXKcY+JhyczhLNMg/BMtiaV0gu8G6pq3i2L9B51wBEJaB0qFbowYvXZA0 -mF7Mi8+jlWH8K1szdkff9YtRtxTPWQmJaOB3mV6jWlZM2Cmq5Jk0U9aEbN1I20Iy -k9r9Vinno/Tv2J8QWtqNmBRWTWSQnkLrp9/rN6jTi0h3rkllI5mjm8Vtuxntkd6a -9wKog8ElPLH6zlkm0FztUt5yL9TI/nH9iFUEcxd0QjF4OyshHW1KNSgmHucXrEXg -fggLh1IMgBcmqfZJw2QgcWAyKQ8LKSC7Y0Tv2kPuCW3Z9OA8pi7AbsmCJUyUKqQl -+a6zPW5qMPmPmbX5BiI5jfiIrduQ+4fURDbrWBw78zg6Dy/Z0qA51AnqZGTSyAr1 -ctOYh2Ju0u6Ph/h7Gk1jsYsVTZ9SXG//jOCvLxESuiLD5rsaX5q092CQEuLDpemk -2bjEREygtKa6bckl5Ny0jijaT1/AK9yHbWOIhWpJWDW4rijWuLHdpvZY49il5JcS -rKwuZnvKtNXD6W6DYIRAdQHOuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L +ZXBseS5naXRodWIuY29tPokCPwQTAQIAKQIbAwcLCQgHAwIBBhUIAgkKCwQWAgMB +Ah4BAheABQJWERAMBQkFoNuFAAoJEGhvB0VNbO/DhEcP/2+CnIe4k+WPknVPDcfU +45eryg/mzTFbgzYxbIDfoILtWLziKJFMap5VxNgN/679UQPZihukVnSnhb2C5Osz +j8hZ8zKLb579xVJbHqrBunykGEpchcUc7/wKwSkBPyJOFluDj/FL43o3fiPgnkls +6vAP143LoLP9QSQfi2LzpnuDrxZ4kDPChL5TytqHtbQjwcYPgfKiJrE2J9jFk5fS +/QCthaE7SkxOUQneL6CCdL95r07SVnN0dldOgOJPmSpo5vmATL5nmb4AyRwyjdsP +TwvwYC0QufvP5P+bPXN7WxVMETwZf5NMpqAoxySDE4kiO7jZ7XZBjn/6NPDSl6r7 +lbTMl7ghrBEmjXOBREE6K5b5sUfye1qnTMF9oULWkR0edDBiSsnNFb4p+bmcAv++ +iTSj+4dz0VTcU2WUuiKAcVoDcHMx/JmR8RHms20f3Mt4XZyXJQfUYMWTOjzKRZGG +m6aJQxTyB8qX0U+FptxD8UxUgrYqNotwVcGOGoZl7MwDcDB5lGfVZyMh6wR1gURx +cB5xQi/q293q8iXTID85xbL/3ONvdprFCDg0ZZjRXPizPZKRnNcd+//hRwN1V6g6 +G4Y10AYm/JmaMZcYbeBsjTydXiYEj1dBZ7lvPwO0Kt4dsbcxYfPR8TfHyOZtHYxX +ASCj+NiQCDV8H6RgLh5wZ9F4uQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L xSSopLSk7/sdLWTc2ERmjDId7dKmqrL1Kh2kqAtHY3Rq8Y839LGmbJCzI1kJyOHF o9jkEI93sqXcztLjizPVukqClOZNt3NV/nvefH6JSdqWcnC4V1mQr2Ztl0j+51i+ NYVwGjlsOMlBER+LW/s7egRqAQonrcEB5vsSAzd8mOlNKjRAnDCV+C21GDKxzb80 @@ -48,5 +48,5 @@ pfGV5kuLJEVxpGsGPJTirh+1nbOss0vr7pngy1MeH5Wqk1+LBZPRgJ2KNaOcCiNT POY/RnsFrhJjUV9DSF0rm2o2RBtV+TVrIrtEOAjof5jcjDJeU7hV7AK6zOYzksJ/ l5ajwVyde4plP3/R0PJTYFe2tCDXhhCYc79kgIZO5df7mTygeMU20sTMgs3RMWlg 42Yz4PoruQRT3YeUC1Bsgr0ONqQShlM+68NbCe+3uhZm9WbNg6+oqcNt -=itPR +=7OLW -----END PGP PUBLIC KEY BLOCK----- From c89e88eaa136de45e67f0e7cae64ec1ea378ea2f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 4 Oct 2015 12:37:59 +0100 Subject: [PATCH 22/25] hardfork: move an assert so it actually works An unsigned quantity is always >= 0 --- src/cryptonote_core/hardfork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index 4b40a4cf3..3a8584b7f 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -103,8 +103,8 @@ bool HardFork::add(uint8_t block_version, uint64_t height) while (versions.size() >= window_size) { const uint8_t old_version = versions.front(); + assert(last_versions[old_version] >= 1); last_versions[old_version]--; - assert(last_versions[old_version] >= 0); versions.pop_front(); } From 61ea5468e311bb88afa5cfbe02e1fad24dea865d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 4 Oct 2015 19:00:37 +0100 Subject: [PATCH 23/25] db_bdb: fix hard fork keys Berkeley DB requires RECNO keys to be 32 bits, and forbids a key value of 0. --- src/blockchain_db/berkeleydb/db_bdb.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 5228c8f9b..c3db06d8f 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1865,7 +1865,7 @@ void BlockchainBDB::set_hard_fork_starting_height(uint8_t version, uint64_t heig LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); - Dbt_copy val_key(version); + Dbt_copy val_key(version + 1); Dbt_copy val(height); if (m_hf_starting_heights->put(DB_DEFAULT_TX, &val_key, &val, 0)) throw1(DB_ERROR("Error adding hard fork starting height to db transaction.")); @@ -1876,7 +1876,7 @@ uint64_t BlockchainBDB::get_hard_fork_starting_height(uint8_t version) const LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); - Dbt_copy key(version); + Dbt_copy key(version + 1); Dbt_copy result; auto get_result = m_hf_starting_heights->get(DB_DEFAULT_TX, &key, &result, 0); @@ -1893,7 +1893,7 @@ void BlockchainBDB::set_hard_fork_version(uint64_t height, uint8_t version) LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); - Dbt_copy val_key(height); + Dbt_copy val_key(height + 1); Dbt_copy val(version); if (m_hf_versions->put(DB_DEFAULT_TX, &val_key, &val, 0)) throw1(DB_ERROR("Error adding hard fork version to db transaction.")); @@ -1904,7 +1904,7 @@ uint8_t BlockchainBDB::get_hard_fork_version(uint64_t height) const LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); - Dbt_copy key(height); + Dbt_copy key(height + 1); Dbt_copy result; auto get_result = m_hf_versions->get(DB_DEFAULT_TX, &key, &result, 0); From 70e9f180c8718086218c37d727736bbdba2d2125 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 4 Oct 2015 19:01:33 +0100 Subject: [PATCH 24/25] build: default to Berkeley DB for 32 bit and ARM --- CMakeLists.txt | 9 +++++++++ src/daemon/command_line_args.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4dbd9457..8cf66db97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,8 +218,17 @@ if (DATABASE STREQUAL "lmdb") endif() endif() endif() + + if (BERKELEY_DB AND (ARCH_WIDTH STREQUAL "32" OR ARM6 OR ARM7)) + message(STATUS "Using Berkeley DB as default DB type") + add_definitions("-DDEFAULT_DB_TYPE=\"berkeley\"") + else() + message(STATUS "Using LMDB as default DB type") + add_definitions("-DDEFAULT_DB_TYPE=\"lmdb\"") + endif() elseif (DATABASE STREQUAL "memory") set(BLOCKCHAIN_DB DB_MEMORY) + add_definitions("-DDEFAULT_DB_TYPE=\"memory\"") else() die("Invalid database type: ${DATABASE}") endif() diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index ba98a6ea1..8da470c86 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -73,7 +73,7 @@ namespace daemon_args const command_line::arg_descriptor arg_db_type = { "db-type" , "Specify database type" - , "lmdb" + , DEFAULT_DB_TYPE }; const command_line::arg_descriptor arg_prep_blocks_threads = { "prep-blocks-threads" From defcb28c689340df21714b8408a49ec554aa83f8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Oct 2015 20:57:52 +0100 Subject: [PATCH 25/25] unbound: do not try to link against libevent It is now not needed anymore, as we use minievent instead --- external/unbound/CMakeLists.txt | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/external/unbound/CMakeLists.txt b/external/unbound/CMakeLists.txt index 72e2addd6..47010e1b3 100644 --- a/external/unbound/CMakeLists.txt +++ b/external/unbound/CMakeLists.txt @@ -40,7 +40,7 @@ if (WIN32) set(USE_WINSOCK 1) else () find_package(PkgConfig REQUIRED) - pkg_check_modules(LIBEVENT2 REQUIRED libevent) + set(USE_MINI_EVENT 1) endif () set(RETSIGTYPE void) @@ -181,10 +181,6 @@ set(libunbound_src include_directories("${CMAKE_CURRENT_SOURCE_DIR}") include_directories("${CMAKE_CURRENT_BINARY_DIR}") include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) -if (LIBEVENT2_FOUND) - include_directories(SYSTEM ${LIBEVENT2_INCLUDE_DIRS}) - link_directories(${LIBEVENT2_LIBRARY_DIRS}) -endif () add_library(unbound ${common_src} ${sldns_src} @@ -194,11 +190,6 @@ target_link_libraries(unbound LINK_PRIVATE ${OPENSSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) -if (LIBEVENT2_FOUND) - target_link_libraries(unbound - LINK_PRIVATE - ${LIBEVENT2_LIBRARIES}) -endif () if (WIN32) target_link_libraries(unbound @@ -207,11 +198,6 @@ if (WIN32) ws2_32) endif () -# XXX: Hack for static builds. -set(LIBEVENT2_LIBDIR - "${LIBEVENT2_LIBDIR}" - PARENT_SCOPE) - if (MINGW) # There is no variable for this (probably due to the fact that the pthread # library is implicit with a link in msys).