diff options
author | Riccardo Spagni <ric@spagni.net> | 2015-09-22 14:49:22 +0200 |
---|---|---|
committer | Riccardo Spagni <ric@spagni.net> | 2015-09-22 14:49:25 +0200 |
commit | 0193bfce4bcee87f202ac1154f017d57bb5aa08d (patch) | |
tree | 8f6b2d879f7e8b5c224a4592373bd37afc2a9957 /src/cryptonote_core | |
parent | Merge pull request #394 (diff) | |
parent | hardfork: most state now saved to the DB (diff) | |
download | monero-0193bfce4bcee87f202ac1154f017d57bb5aa08d.tar.xz |
Merge pull request #405
5b11a89 hardfork: most state now saved to the DB (moneromooo-monero)
0a54c3a hardfork: remove the "parts are copyright cryptonote" notices (moneromooo-monero)
e546f37 Add an RPC call and daemon command to get info on hard fork voting (moneromooo-monero)
d067131 blockchain: force a hardfork recalculation at load time (moneromooo-monero)
a717761 core: add consts where appropriate (moneromooo-monero)
8ffc508 core: moan when we think an update is needed to get latest hard fork info (moneromooo-monero)
f854984 blockchain: use the new hardfork class (moneromooo-monero)
62b1f74 New hardfork class (moneromooo-monero)
bed9a44 blockchain: add a couple missing includes (moneromooo-monero)
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 60 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 20 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain_storage.cpp | 6 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain_storage.h | 6 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_basic.h | 4 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 90 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 75 | ||||
-rw-r--r-- | src/cryptonote_core/hardfork.cpp | 267 | ||||
-rw-r--r-- | src/cryptonote_core/hardfork.h | 216 |
10 files changed, 661 insertions, 89 deletions
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/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 09f8f8d7f..65d07dcf7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -69,6 +69,18 @@ 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), @@ -118,6 +130,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); } //------------------------------------------------------------------ @@ -270,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, @@ -416,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; } @@ -706,6 +729,8 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& 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(rollback_height); + LOG_PRINT_L1("Rollback to height " << rollback_height << " was successful."); if (original_chain.size()) { @@ -803,6 +828,8 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: m_alternative_chains.erase(ch_ent); } + m_hardfork->reorganize_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; } @@ -972,12 +999,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."); @@ -1622,7 +1650,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; @@ -1642,7 +1670,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; @@ -1660,7 +1688,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; @@ -2245,6 +2273,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 +2556,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) { @@ -3054,3 +3092,13 @@ 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(); +} + +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 57763f6ca..a248682fc 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -38,6 +38,8 @@ #include <boost/multi_index/member.hpp> #include <boost/foreach.hpp> #include <atomic> +#include <unordered_map> +#include <unordered_set> #include "syncobj.h" #include "string_tools.h" @@ -50,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 @@ -142,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); @@ -156,6 +159,11 @@ 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() { return *m_db; @@ -225,6 +233,8 @@ namespace cryptonote std::atomic<bool> m_is_blockchain_storing; bool m_enforce_dns_checkpoints; + HardFork *m_hardfork; + template<class visitor_t> 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<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height); @@ -266,10 +276,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/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_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) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4d90eec1e..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<block>& blocks, std::list<transaction>& txs) + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& 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<block>& blocks) + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const { return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list<block>& blocks) + bool core::get_alternative_blocks(std::list<block>& 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<crypto::key_image>& key_im, std::vector<bool> &spent) + bool core::are_key_images_spent(const std::vector<crypto::key_image>& key_im, std::vector<bool> &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<crypto::key_image> 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<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) + bool core::find_blockchain_supplement(const std::list<crypto::hash>& 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<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& 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<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& 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<uint64_t>& indexs) + bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& 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<transaction>& txs) + bool core::get_pool_transactions(std::list<transaction>& 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<crypto::hash>& ids) + bool core::get_short_chain_history(std::list<crypto::hash>& 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); } @@ -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..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<block_complete_entry> &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<block>& blocks, std::list<transaction>& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& 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<block>& blocks, std::list<transaction>& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const; template<class t_ids_container, class t_blocks_container, class t_missed_container> - 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<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& 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<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const; + bool get_block_by_hash(const crypto::hash &h, block &blk) const; //void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid); - bool get_alternative_blocks(std::list<block>& blocks); - size_t get_alternative_blocks_count(); + bool get_alternative_blocks(std::list<block>& 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<transaction>& txs); + bool get_pool_transactions(std::list<transaction>& txs) const; bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& 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<crypto::public_key>& pkeys); - bool have_block(const crypto::hash& id); - bool get_short_chain_history(std::list<crypto::hash>& ids); - bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& 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<crypto::hash>& ids) const; + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& 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<size_t>& sizes, size_t count); - bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& 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<uint64_t>& 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,28 +148,29 @@ namespace cryptonote void stop(); - bool is_key_image_spent(const crypto::key_image& key_im); - bool are_key_images_spent(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent); + bool is_key_image_spent(const crypto::key_image& key_im) const; + bool are_key_images_spent(const std::vector<crypto::key_image>& key_im, std::vector<bool> &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<crypto::signature>& 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<crypto::signature>& 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<bool> m_fast_exit; bool m_test_drop_download = true; uint64_t m_test_drop_download_height = 0; @@ -185,6 +189,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<bool> m_starter_message_showed; diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp new file mode 100644 index 000000000..d2e95b58e --- /dev/null +++ b/src/cryptonote_core/hardfork.cpp @@ -0,0 +1,267 @@ +// 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. + +#include <algorithm> +#include <cstdio> + +#include "cryptonote_core/cryptonote_basic.h" +#include "blockchain_db/blockchain_db.h" +#include "hardfork.h" + +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): + db(db), + original_version(original_version), + forked_time(forked_time), + update_time(update_time), + window_size(window_size), + threshold_percent(threshold_percent) +{ +} + +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() >= window_size) { + 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) { + db.set_hard_fork_starting_height(v, height); + } + current_fork_index = voted; + } + + db.set_hard_fork_version(height, heights[current_fork_index].version); + + return true; +} + +void HardFork::init() +{ + CRITICAL_REGION_LOCAL(lock); + versions.clear(); + for (size_t n = 0; n < 256; ++n) + last_versions[n] = 0; + current_fork_index = 0; + 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<uint64_t>::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(uint64_t height) +{ + CRITICAL_REGION_LOCAL(lock); + if (height >= db.height()) + return false; + + versions.clear(); + + for (size_t n = 0; n < 256; ++n) + last_versions[n] = 0; + const uint64_t rescan_height = height >= (window_size - 1) ? height - (window_size - 1) : 0; + const uint8_t start_version = height == 0 ? original_version : db.get_hard_fork_version(height); + while (heights[current_fork_index].version > start_version) { + db.set_hard_fork_starting_height(heights[current_fork_index].version, std::numeric_limits<uint64_t>::max()); + --current_fork_index; + } + for (uint64_t h = rescan_height; h <= height; ++h) { + cryptonote::block b = db.get_block_from_height(h); + const uint8_t v = get_effective_version(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(uint64_t height) +{ + if (height == 0) + return false; + return reorganize_from_block_height(height - 1); +} + +int HardFork::get_voted_fork_index(uint64_t height) const +{ + CRITICAL_REGION_LOCAL(lock); + 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]; + 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); + if (height > db.height()) { + 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 db.get_hard_fork_starting_height(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; +} + +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; +} + diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h new file mode 100644 index 000000000..bdac87f2c --- /dev/null +++ b/src/cryptonote_core/hardfork.h @@ -0,0 +1,216 @@ +// 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. + +#pragma once + +#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_WINDOW_SIZE = 50; // supermajority window check length + static const int DEFAULT_THRESHOLD_PERCENT = 80; + + /** + * @brief creates a new HardFork object + * + * @param original_version the block version for blocks 0 through to the first fork + * @param forked_time the time in seconds before thinking we're forked + * @param update_time the time in seconds before thinking we need to update + * @param window_size the size of the window in blocks to consider for version voting + * @param 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); + + /** + * @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 initialize the object + * + * Must be done after adding all the required hardforks via add above + */ + void init(); + + /** + * @brief check whether a new block would be accepted + * + * returns true if the block is accepted, false otherwise + * + * @param block the new block + * + * This check is made by add. It is exposed publicly to allow + * the caller to inexpensively check whether a block would be + * accepted or rejected by its version number. Indeed, if this + * check could only be done as part of add, the caller would + * either have to add the block to the blockchain first, then + * call add, then have to pop the block from the blockchain if + * its version did not satisfy the hard fork requirements, or + * call add first, then, if the hard fork requirements are met, + * add the block to the blockchain, upon which a failure (the + * block being invalid, double spending, etc) would cause the + * hardfork object to reorganize. + */ + bool check(const cryptonote::block &block) const; + + /** + * @brief add a new block + * + * returns true if no error, false otherwise + * + * @param block the new block + */ + bool add(const cryptonote::block &block, uint64_t height); + + /** + * @brief called when the blockchain is reorganized + * + * This will rescan the blockchain to determine which hard forks + * have been triggered + * + * returns true if no error, false otherwise + * + * @param blockchain the blockchain + * @param height of the last block kept from the previous blockchain + */ + bool reorganize_from_block_height(uint64_t height); + bool reorganize_from_chain_height(uint64_t height); + + /** + * @brief 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; + + /** + * @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 window_size; } + + private: + + 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 window_size; + int threshold_percent; + + uint8_t original_version; + + typedef struct { + uint8_t version; + uint64_t height; + time_t time; + } Params; + std::vector<Params> heights; + + std::deque<uint8_t> versions; /* rolling window of the last N blocks' versions */ + unsigned int last_versions[256]; /* count of the block versions in the last N blocks */ + uint32_t current_fork_index; + uint32_t vote_threshold; + + mutable epee::critical_section lock; + }; + +} // namespace cryptonote + |