From edade8dc818e2fcb7af082bb6c5f619dedd4748a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Nov 2015 14:04:33 +0000 Subject: [PATCH 1/6] hardfork: fix actual/voting confusion --- src/cryptonote_core/hardfork.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index edc2f33a9..836378050 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -46,6 +46,11 @@ static uint8_t get_block_vote(const cryptonote::block &b) return b.minor_version; } +static uint8_t get_block_version(const cryptonote::block &b) +{ + return b.major_version; +} + 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, uint8_t default_threshold_percent): db(db), original_version(original_version), @@ -105,7 +110,7 @@ bool HardFork::do_check(uint8_t version) const bool HardFork::check(const cryptonote::block &block) const { CRITICAL_REGION_LOCAL(lock); - return do_check(get_block_vote(block)); + return do_check(::get_block_version(block)); } bool HardFork::add(uint8_t block_version, uint64_t height) @@ -143,7 +148,7 @@ bool HardFork::add(uint8_t block_version, uint64_t height) bool HardFork::add(const cryptonote::block &block, uint64_t height) { - return add(get_block_vote(block), height); + return add(::get_block_version(block), height); } void HardFork::init() @@ -185,7 +190,7 @@ uint8_t HardFork::get_block_version(uint64_t height) const return original_version; const cryptonote::block &block = db.get_block_from_height(height); - return get_block_vote(block); + return ::get_block_version(block); } bool HardFork::reorganize_from_block_height(uint64_t height) From 53c75ab4a0e21fe3e6a5958ae76b241b5c2fcd45 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Nov 2015 15:53:59 +0000 Subject: [PATCH 2/6] blockchain: log versions as numbers, not characters --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8bb63ad09..3b04bf941 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2338,7 +2338,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // 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()); + LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)m_hardfork->get_current_version()); return false; } From 9c64b12320ba8b4954b47bdc93a146594a48cb09 Mon Sep 17 00:00:00 2001 From: Javier Smooth Date: Sat, 1 Aug 2015 19:19:42 -0700 Subject: [PATCH 3/6] quiet down p2p logging a bit --- src/p2p/net_node.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index d92b4bb2a..371cbc462 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -866,7 +866,7 @@ namespace nodetool continue; } - LOG_PRINT_L1("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) + LOG_PRINT_L2("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << "[white=" << use_white_list << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); From 4cea2b13b221bed66023254dd3356f2548a08df4 Mon Sep 17 00:00:00 2001 From: Javier Smooth Date: Mon, 23 Nov 2015 17:34:55 +0000 Subject: [PATCH 4/6] Add IP blocking for misbehaving nodes (adapted from Boolberry) With minor cleanup and fixes (spelling, indent) by moneromooo --- .../epee/include/net/abstract_tcp_server2.inl | 4 +- src/cryptonote_config.h | 5 ++ .../cryptonote_protocol_handler.inl | 4 ++ src/p2p/net_node.h | 19 +++++- src/p2p/net_node.inl | 68 +++++++++++++++++++ src/p2p/net_node_common.h | 10 +++ src/p2p/net_peerlist.h | 3 +- 7 files changed, 108 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 612e2b417..b75a9ecaa 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -899,9 +899,7 @@ POP_WARNINGS boost::bind(&boosted_tcp_server::handle_accept, this, boost::asio::placeholders::error)); - bool r = conn->start(true, 1 < m_threads_count); - if (!r) - _erro("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sock_count); + conn->start(true, 1 < m_threads_count); conn->save_dbg_log(); }else { diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6053387ac..33005660a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -105,6 +105,11 @@ #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +#define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour +#define P2P_IP_BLOCKTIME (60*60*24) //24 hour +#define P2P_IP_FAILS_BEFORE_BLOCK 10 +#define P2P_IDLE_CONNECTION_KILL_INTERVAL (5*60) //5 minutes + #define ALLOW_DEBUG_COMMANDS #define CRYPTONOTE_NAME "bitmonero" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 83c7233b1..a6761101f 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -571,6 +571,7 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); m_core.cleanup_handle_incoming_blocks(); return 1; } @@ -578,6 +579,7 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L1("Block received at sync phase was marked as orphaned, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); m_core.cleanup_handle_incoming_blocks(); return 1; } @@ -728,6 +730,7 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } @@ -736,6 +739,7 @@ namespace cryptonote LOG_ERROR_CCONTEXT("sent m_block_ids starting from unknown id: " << epee::string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 3eb125208..4aaac813e 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "cryptonote_config.h" #include "warnings.h" @@ -66,7 +67,8 @@ namespace nodetool template class node_server: public epee::levin::levin_commands_handler >, - public i_p2p_endpoint + public i_p2p_endpoint, + public epee::net_utils::i_connection_filter { struct by_conn_id{}; struct by_peer_id{}; @@ -169,6 +171,10 @@ namespace nodetool virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); virtual void for_each_connection(std::function f); + virtual bool block_ip(uint32_t adress); + virtual bool add_ip_fail(uint32_t address); + //----------------- i_connection_filter -------------------------------------------------------- + virtual bool is_remote_ip_allowed(uint32_t adress); //----------------------------------------------------------------------------------------------- bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line( @@ -196,6 +202,8 @@ namespace nodetool template bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool make_expected_connections_count(bool white_list, size_t expected_connections); + void cache_connect_fail_info(const net_address& addr); + bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); template @@ -282,6 +290,15 @@ namespace nodetool //keep connections to initiate some interactions net_server m_net_server; boost::uuids::uuid m_network_id; + + std::map m_conn_fails_cache; + epee::critical_section m_conn_fails_cache_lock; + + epee::critical_section m_blocked_ips_lock; + std::map m_blocked_ips; + + epee::critical_section m_ip_fails_score_lock; + std::map m_ip_fails_score; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 371cbc462..11df7ee49 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -28,6 +28,8 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +// IP blocking adapted from Boolberry + #pragma once #include @@ -161,6 +163,22 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::is_remote_ip_allowed(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + auto it = m_blocked_ips.find(addr); + if(it == m_blocked_ips.end()) + return true; + if(time(nullptr) - it->second > P2P_IP_BLOCKTIME ) + { + m_blocked_ips.erase(it); + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << "is unblocked.", LOG_LEVEL_0); + return true; + } + return false; + } + //----------------------------------------------------------------------------------- + template bool node_server::make_default_config() { m_config.m_peer_id = crypto::rand(); @@ -168,6 +186,31 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::block_ip(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + m_blocked_ips[addr] = time(nullptr); + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked.", LOG_LEVEL_0); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::add_ip_fail(uint32_t address) + { + CRITICAL_REGION_LOCAL(m_ip_fails_score_lock); + uint64_t fails = ++m_ip_fails_score[address]; + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(address) << " fail score=" << fails, LOG_LEVEL_1); + if(fails > P2P_IP_FAILS_BEFORE_BLOCK) + { + auto it = m_ip_fails_score.find(address); + CHECK_AND_ASSERT_MES(it != m_ip_fails_score.end(), false, "internal error"); + it->second = P2P_IP_FAILS_BEFORE_BLOCK/2; + block_ip(address); + } + return true; + } + //----------------------------------------------------------------------------------- + template bool node_server::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) { return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); @@ -428,6 +471,7 @@ namespace nodetool m_net_server.set_threads_prefix("P2P"); m_net_server.get_config_object().m_pcommands_handler = this; m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + m_net_server.set_connection_filter(this); //try to bind LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); @@ -624,6 +668,7 @@ namespace nodetool if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); + add_ip_fail(context.m_remote_ip); return; } hsh_result = true; @@ -685,6 +730,7 @@ namespace nodetool { LOG_ERROR_CCONTEXT("COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); m_net_server.get_config_object().close(context.m_connection_id ); + add_ip_fail(context.m_remote_ip); } if(!context.m_is_income) m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); @@ -829,6 +875,20 @@ namespace nodetool #undef LOG_PRINT_CC_PRIORITY_NODE + //----------------------------------------------------------------------------------- + template + bool node_server::is_addr_recently_failed(const net_address& addr) + { + CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); + auto it = m_conn_fails_cache.find(addr); + if(it == m_conn_fails_cache.end()) + return false; + + if(time(NULL) - it->second > P2P_FAILED_ADDR_FORGET_SECONDS) + return false; + else + return true; + } //----------------------------------------------------------------------------------- template bool node_server::make_new_connection_from_peerlist(bool use_white_list) @@ -866,6 +926,12 @@ namespace nodetool continue; } + if(!is_remote_ip_allowed(pe.adr.ip)) + continue; + + if(is_addr_recently_failed(pe.adr)) + continue; + LOG_PRINT_L2("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << "[white=" << use_white_list @@ -1270,6 +1336,7 @@ namespace nodetool LOG_PRINT_CCONTEXT_L1("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); drop_connection(context); + add_ip_fail(context.m_remote_ip); return 1; } @@ -1277,6 +1344,7 @@ namespace nodetool { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); + add_ip_fail(context.m_remote_ip); return 1; } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index a7b8bf6f3..93b29deb2 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -50,6 +50,8 @@ namespace nodetool virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_connections_count()=0; virtual void for_each_connection(std::function f)=0; + virtual bool block_ip(uint32_t adress)=0; + virtual bool add_ip_fail(uint32_t adress)=0; }; template @@ -84,5 +86,13 @@ namespace nodetool { return false; } + virtual bool block_ip(uint32_t adress) + { + return true; + } + virtual bool add_ip_fail(uint32_t adress) + { + return true; + } }; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index f738c68f6..3d8b08ce6 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -53,6 +53,7 @@ #include "net_peerlist_boost_serialization.h" +#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 4 namespace nodetool { @@ -394,4 +395,4 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- } -BOOST_CLASS_VERSION(nodetool::peerlist_manager, 4) +BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER) From 3b47ca2d7ded0e886d7fb299ebd421f3afd373c9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Nov 2015 21:12:55 +0000 Subject: [PATCH 5/6] hardfork: fix rescan on load --- 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 836378050..3d1bca32d 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -263,7 +263,7 @@ bool HardFork::rescan_from_block_height(uint64_t height) versions.push_back(v); } - uint8_t lastv = db.get_hard_fork_version(height); + uint8_t lastv = db.get_hard_fork_version(db.height() - 1); current_fork_index = 0; while (current_fork_index + 1 < heights.size() && heights[current_fork_index].version != lastv) ++current_fork_index; From d887c18e333aefb74b168931f23edcaf21b642d9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Nov 2015 20:46:10 +0000 Subject: [PATCH 6/6] hardfork: fix more major/minor issues Also add some more tests, and rename some instances of "version" and "add" for clarity. NOTE: the starting height values are sometimes wrong. I suspect this is due to the hard fork reorg code being buggy, since they're good when syncing after the fact. However, they're not actually used by the consensus code, so I'm ignoring this for now, but this needs debugging. --- src/cryptonote_core/blockchain.cpp | 4 +- src/cryptonote_core/hardfork.cpp | 35 +++++----- src/cryptonote_core/hardfork.h | 11 ++-- tests/unit_tests/hardfork.cpp | 101 ++++++++++++++++------------- 4 files changed, 83 insertions(+), 68 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3b04bf941..af7713972 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -257,13 +257,13 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) 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].threshold, testnet_hard_forks[n].time); + m_hardfork->add_fork(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].threshold, 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].threshold, mainnet_hard_forks[n].time); + m_hardfork->add_fork(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].threshold, mainnet_hard_forks[n].time); } m_hardfork->init(); diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index 3d1bca32d..2a2e25635 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -66,7 +66,7 @@ HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, uint6 throw "default_threshold_percent needs to be between 0 and 100"; } -bool HardFork::add(uint8_t version, uint64_t height, uint8_t threshold, time_t time) +bool HardFork::add_fork(uint8_t version, uint64_t height, uint8_t threshold, time_t time) { CRITICAL_REGION_LOCAL(lock); @@ -87,42 +87,43 @@ bool HardFork::add(uint8_t version, uint64_t height, uint8_t threshold, time_t t return true; } -bool HardFork::add(uint8_t version, uint64_t height, time_t time) +bool HardFork::add_fork(uint8_t version, uint64_t height, time_t time) { - return add(version, height, default_threshold_percent, time); + return add_fork(version, height, default_threshold_percent, time); } -uint8_t HardFork::get_effective_version(uint8_t version) const +uint8_t HardFork::get_effective_version(uint8_t voting_version) const { if (!heights.empty()) { uint8_t max_version = heights.back().version; - if (version > max_version) - version = max_version; + if (voting_version > max_version) + voting_version = max_version; } - return version; + return voting_version; } -bool HardFork::do_check(uint8_t version) const +bool HardFork::do_check(uint8_t block_version, uint8_t voting_version) const { - return version >= heights[current_fork_index].version; + return block_version >= heights[current_fork_index].version + && voting_version >= heights[current_fork_index].version; } bool HardFork::check(const cryptonote::block &block) const { CRITICAL_REGION_LOCAL(lock); - return do_check(::get_block_version(block)); + return do_check(::get_block_version(block), ::get_block_vote(block)); } -bool HardFork::add(uint8_t block_version, uint64_t height) +bool HardFork::add(uint8_t block_version, uint8_t voting_version, uint64_t height) { CRITICAL_REGION_LOCAL(lock); - if (!do_check(block_version)) + if (!do_check(block_version, voting_version)) return false; db.set_hard_fork_version(height, heights[current_fork_index].version); - const uint8_t version = get_effective_version(block_version); + voting_version = get_effective_version(voting_version); while (versions.size() >= window_size) { const uint8_t old_version = versions.front(); @@ -131,8 +132,8 @@ bool HardFork::add(uint8_t block_version, uint64_t height) versions.pop_front(); } - last_versions[version]++; - versions.push_back(version); + last_versions[voting_version]++; + versions.push_back(voting_version); uint8_t voted = get_voted_fork_index(height + 1); if (voted > current_fork_index) { @@ -148,7 +149,7 @@ bool HardFork::add(uint8_t block_version, uint64_t height) bool HardFork::add(const cryptonote::block &block, uint64_t height) { - return add(::get_block_version(block), height); + return add(::get_block_version(block), ::get_block_vote(block), height); } void HardFork::init() @@ -230,7 +231,7 @@ 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(get_block_version(h), h); + add(db.get_block_from_height(h), h); } db.batch_stop(); diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index 6800749da..6d2a3c55b 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -71,7 +71,7 @@ namespace cryptonote * @param threshold The threshold of votes needed for this fork (0-100) * @param time Approximate time of the hardfork (seconds since epoch) */ - bool add(uint8_t version, uint64_t height, uint8_t threshold, time_t time); + bool add_fork(uint8_t version, uint64_t height, uint8_t threshold, time_t time); /** * @brief add a new hardfork height @@ -79,10 +79,11 @@ namespace cryptonote * returns true if no error, false otherwise * * @param version the major block version for the fork + * @param voting_version the minor block version for the fork, used for voting * @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); + bool add_fork(uint8_t version, uint64_t height, time_t time); /** * @brief initialize the object @@ -203,10 +204,10 @@ namespace cryptonote private: uint8_t get_block_version(uint64_t height) const; - bool do_check(uint8_t version) const; + bool do_check(uint8_t block_version, uint8_t voting_version) const; int get_voted_fork_index(uint64_t height) const; - uint8_t get_effective_version(uint8_t version) const; - bool add(uint8_t block_version, uint64_t height); + uint8_t get_effective_version(uint8_t voting_version) const; + bool add(uint8_t block_version, uint8_t voting_version, uint64_t height); bool rescan_from_block_height(uint64_t height); bool rescan_from_chain_height(uint64_t height); diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 1f6ef5bf0..254dece54 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -135,6 +135,7 @@ private: static cryptonote::block mkblock(uint8_t version) { cryptonote::block b; + b.major_version = version; b.minor_version = version; return b; } @@ -144,7 +145,7 @@ TEST(empty_hardforks, Success) TestDB db; HardFork hf(db); - ASSERT_TRUE(hf.add(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(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); @@ -163,13 +164,13 @@ 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)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); + ASSERT_FALSE(hf.add_fork(3, 3, 1)); + ASSERT_FALSE(hf.add_fork(3, 2, 2)); + ASSERT_FALSE(hf.add_fork(2, 3, 2)); + ASSERT_TRUE(hf.add_fork(3, 10, 2)); + ASSERT_TRUE(hf.add_fork(4, 20, 3)); + ASSERT_FALSE(hf.add_fork(5, 5, 4)); } TEST(states, Success) @@ -177,8 +178,8 @@ 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.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(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); @@ -186,7 +187,7 @@ TEST(states, Success) 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.add_fork(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); @@ -201,10 +202,10 @@ TEST(steps_asap, Success) HardFork hf(db, 1,0,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)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(4, 2, 1)); + ASSERT_TRUE(hf.add_fork(7, 4, 2)); + ASSERT_TRUE(hf.add_fork(9, 6, 3)); hf.init(); for (uint64_t h = 0; h < 10; ++h) { @@ -229,9 +230,9 @@ TEST(steps_1, Success) TestDB db; HardFork hf(db, 1,0,1,1,1); - ASSERT_TRUE(hf.add(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); for (int n = 1 ; n < 10; ++n) - ASSERT_TRUE(hf.add(n+1, n, n)); + ASSERT_TRUE(hf.add_fork(n+1, n, n)); hf.init(); for (uint64_t h = 0 ; h < 10; ++h) { @@ -251,10 +252,10 @@ TEST(reorganize, Same) HardFork hf(db, 1, 0, 1, 1, history, 100); // 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)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(4, 2, 1)); + ASSERT_TRUE(hf.add_fork(7, 4, 2)); + ASSERT_TRUE(hf.add_fork(9, 6, 3)); hf.init(); // index 0 1 2 3 4 5 6 7 8 9 @@ -270,6 +271,10 @@ TEST(reorganize, Same) uint8_t version = hh >= history ? block_versions[hh - history] : 1; ASSERT_EQ(hf.get(hh), version); } + ASSERT_EQ(hf.get_start_height(1), 0); + ASSERT_EQ(hf.get_start_height(4), 2 + history); + ASSERT_EQ(hf.get_start_height(7), 4 + history); + ASSERT_EQ(hf.get_start_height(9), 6 + history); } } } @@ -281,15 +286,15 @@ TEST(reorganize, Changed) HardFork hf(db, 1, 0, 1, 1, 4, 100); // 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)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(4, 2, 1)); + ASSERT_TRUE(hf.add_fork(7, 4, 2)); + ASSERT_TRUE(hf.add_fork(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 }; + // 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()); @@ -301,6 +306,10 @@ TEST(reorganize, Changed) for (int hh = 0; hh < 16; ++hh) { ASSERT_EQ(hf.get(hh), expected_versions[hh]); } + ASSERT_EQ(hf.get_start_height(1), 0); + ASSERT_EQ(hf.get_start_height(4), 6); + ASSERT_EQ(hf.get_start_height(7), 8); + ASSERT_EQ(hf.get_start_height(9), 10); } // delay a bit for 9, and go back to 1 to check it stays at 9 @@ -321,6 +330,10 @@ TEST(reorganize, Changed) for (int hh = 0; hh < 15; ++hh) { ASSERT_EQ(hf.get(hh), expected_versions_new[hh]); } + ASSERT_EQ(hf.get_start_height(1), 0); + ASSERT_EQ(hf.get_start_height(4), 6); + ASSERT_EQ(hf.get_start_height(7), 11); + ASSERT_EQ(hf.get_start_height(9), 14); } TEST(voting, threshold) @@ -330,8 +343,8 @@ TEST(voting, threshold) HardFork hf(db, 1, 0, 1, 1, 8, threshold); // v h t - ASSERT_TRUE(hf.add(1, 0, 0)); - ASSERT_TRUE(hf.add(2, 2, 1)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); hf.init(); for (uint64_t h = 0; h <= 8; ++h) { @@ -359,10 +372,10 @@ TEST(voting, different_thresholds) HardFork hf(db, 1, 0, 1, 1, 4, 50); // window size 4 // v h t - ASSERT_TRUE(hf.add(1, 0, 0)); - ASSERT_TRUE(hf.add(2, 5, 0, 1)); // asap - ASSERT_TRUE(hf.add(3, 10, 100, 2)); // all votes - ASSERT_TRUE(hf.add(4, 15, 3)); // default 50% votes + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 5, 0, 1)); // asap + ASSERT_TRUE(hf.add_fork(3, 10, 100, 2)); // all votes + ASSERT_TRUE(hf.add_fork(4, 15, 3)); // default 50% votes hf.init(); // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 @@ -386,8 +399,8 @@ TEST(new_blocks, denied) HardFork hf(db, 1, 0, 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_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); hf.init(); ASSERT_TRUE(hf.add(mkblock(1), 0)); @@ -411,8 +424,8 @@ TEST(new_version, early) HardFork hf(db, 1, 0, 1, 1, 4, 50); // v h t - ASSERT_TRUE(hf.add(1, 0, 0)); - ASSERT_TRUE(hf.add(2, 4, 1)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 4, 1)); hf.init(); ASSERT_TRUE(hf.add(mkblock(2), 0)); @@ -433,9 +446,9 @@ TEST(reorganize, changed) HardFork hf(db, 1, 0, 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)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); + ASSERT_TRUE(hf.add_fork(3, 5, 2)); hf.init(); #define ADD(v, h, a) \ @@ -487,9 +500,9 @@ TEST(get, higher) HardFork hf(db, 1, 0, 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)); + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); + ASSERT_TRUE(hf.add_fork(3, 5, 2)); hf.init(); ASSERT_EQ(hf.get_ideal_version(0), 1);