diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 7 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 38 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 23 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 51 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 15 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 6 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 6 |
7 files changed, 114 insertions, 32 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 72844db66..231489fdb 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -41,12 +41,6 @@ set(cryptonote_core_private_headers tx_pool.h cryptonote_tx_utils.h) -if(PER_BLOCK_CHECKPOINT) - set(Blocks "blocks") -else() - set(Blocks "") -endif() - monero_private_headers(cryptonote_core ${cryptonote_core_private_headers}) monero_add_library(cryptonote_core @@ -69,5 +63,4 @@ target_link_libraries(cryptonote_core ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE - ${Blocks} ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 1ec2366e4..77b6d0b69 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -53,9 +53,6 @@ #include "ringct/rctSigs.h" #include "common/perf_timer.h" #include "common/notify.h" -#if defined(PER_BLOCK_CHECKPOINT) -#include "blocks/blocks.h" -#endif #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "blockchain" @@ -341,7 +338,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty) +bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback& get_checkpoints/* = nullptr*/) { LOG_PRINT_L3("Blockchain::" << __func__); @@ -442,7 +439,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline #if defined(PER_BLOCK_CHECKPOINT) if (m_nettype != FAKECHAIN) - load_compiled_in_block_hashes(); + load_compiled_in_block_hashes(get_checkpoints); #endif MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block()); @@ -886,6 +883,15 @@ difficulty_type Blockchain::get_difficulty_for_next_block() return diff; } //------------------------------------------------------------------ +std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const +{ + std::vector<time_t> timestamps(blocks); + uint64_t height = m_db->height(); + while (blocks--) + timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1); + return timestamps; +} +//------------------------------------------------------------------ // This function removes blocks from the blockchain until it gets to the // position where the blockchain switch started and then re-adds the blocks // that had been removed. @@ -4407,19 +4413,21 @@ void Blockchain::cancel() #if defined(PER_BLOCK_CHECKPOINT) static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511"; -void Blockchain::load_compiled_in_block_hashes() +void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { - const bool testnet = m_nettype == TESTNET; - const bool stagenet = m_nettype == STAGENET; - if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0) + if (get_checkpoints == nullptr || !m_fast_sync) { - MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)"); - + return; + } + const epee::span<const unsigned char> &checkpoints = get_checkpoints(m_nettype); + if (!checkpoints.empty()) + { + MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)"); if (m_nettype == MAINNET) { // first check hash crypto::hash hash; - if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash)) + if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash)) { MERROR("Failed to hash precomputed blocks data"); return; @@ -4439,9 +4447,9 @@ void Blockchain::load_compiled_in_block_hashes() } } - if (get_blocks_dat_size(testnet, stagenet) > 4) + if (checkpoints.size() > 4) { - const unsigned char *p = get_blocks_dat_start(testnet, stagenet); + const unsigned char *p = checkpoints.data(); const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24); if (nblocks > (std::numeric_limits<uint32_t>::max() - 4) / sizeof(hash)) { @@ -4449,7 +4457,7 @@ void Blockchain::load_compiled_in_block_hashes() return; } const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); - if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed) + if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index ab66fac8b..f140d7719 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -38,9 +38,11 @@ #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/member.hpp> #include <atomic> +#include <functional> #include <unordered_map> #include <unordered_set> +#include "span.h" #include "syncobj.h" #include "string_tools.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -73,6 +75,15 @@ namespace cryptonote db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O) }; + /** + * @brief Callback routine that returns checkpoints data for specific network type + * + * @param network network type + * + * @return checkpoints data, empty span if there ain't any checkpoints for specific network type + */ + typedef std::function<const epee::span<const unsigned char>(cryptonote::network_type network)> GetCheckpointsCallback; + /************************************************************************/ /* */ /************************************************************************/ @@ -117,10 +128,11 @@ namespace cryptonote * @param offline true if running offline, else false * @param test_options test parameters * @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled + * @param get_checkpoints if set, will be called to get checkpoints data * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0); + bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr); /** * @brief Initialize the Blockchain state @@ -952,6 +964,11 @@ namespace cryptonote */ void on_new_tx_from_block(const cryptonote::transaction &tx); + /** + * @brief returns the timestamps of the last N blocks + */ + std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const; + private: // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage @@ -1369,8 +1386,10 @@ namespace cryptonote * A (possibly empty) set of block hashes can be compiled into the * monero daemon binary. This function loads those hashes into * a useful state. + * + * @param get_checkpoints if set, will be called to get checkpoints data */ - void load_compiled_in_block_hashes(); + void load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints); /** * @brief expands v2 transaction data from blockchain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2fec6b613..4b806c282 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -389,7 +389,7 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options) + bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */) { start_time = std::time(nullptr); @@ -567,7 +567,7 @@ namespace cryptonote regtest_hard_forks }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); - r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); + r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); @@ -1494,6 +1494,7 @@ namespace cryptonote m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); + m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1682,6 +1683,52 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + double factorial(unsigned int n) + { + if (n <= 1) + return 1.0; + double f = n; + while (n-- > 1) + f *= n; + return f; + } + //----------------------------------------------------------------------------------------------- + static double probability(unsigned int blocks, unsigned int expected) + { + // https://www.umass.edu/wsp/resources/poisson/#computing + return pow(expected, blocks) / (factorial(blocks) * exp(expected)); + } + //----------------------------------------------------------------------------------------------- + bool core::check_block_rate() + { + if (m_offline || m_target_blockchain_height > get_current_blockchain_height()) + { + MDEBUG("Not checking block rate, offline or syncing"); + return true; + } + + static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days + + const time_t now = time(NULL); + const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60); + + static const unsigned int seconds[] = { 5400, 1800, 600 }; + for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) + { + unsigned int b = 0; + for (time_t ts: timestamps) b += ts >= now - static_cast<time_t>(seconds[n]); + const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2); + MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); + if (p < threshold) + { + MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck."); + break; // no need to look further + } + } + + 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 58fe5b7b5..4eca2a57b 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -117,7 +117,7 @@ namespace cryptonote * @param relayed whether or not the transaction was relayed to us * @param do_not_relay whether to prevent the transaction from being relayed * - * @return true if the transaction made it to the transaction pool, otherwise false + * @return true if the transaction was accepted, false otherwise */ bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); @@ -133,7 +133,7 @@ namespace cryptonote * @param relayed whether or not the transactions were relayed to us * @param do_not_relay whether to prevent the transactions from being relayed * - * @return true if the transactions made it to the transaction pool, otherwise false + * @return true if the transactions were accepted, false otherwise */ bool handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); @@ -244,10 +244,11 @@ namespace cryptonote * @param vm command line parameters * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing + * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL); + bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr); /** * @copydoc Blockchain::reset_and_set_genesis_block @@ -945,6 +946,13 @@ namespace cryptonote */ bool check_disk_space(); + /** + * @brief checks block rate, and warns if it's too slow + * + * @return true on success, false otherwise + */ + bool check_block_rate(); + bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so @@ -969,6 +977,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space + epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4bc33b56b..4fc2736a6 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -38,6 +38,7 @@ using namespace epee; #include "cryptonote_tx_utils.h" #include "cryptonote_config.h" #include "cryptonote_basic/miner.h" +#include "cryptonote_basic/tx_extra.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" @@ -84,6 +85,8 @@ namespace cryptonote if(!extra_nonce.empty()) if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) return false; + if (!sort_tx_extra(tx.extra, tx.extra)) + return false; txin_gen in; in.height = height; @@ -434,6 +437,9 @@ namespace cryptonote add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); } + if (!sort_tx_extra(tx.extra, tx.extra)) + return false; + //check money if(summary_outs_money > summary_inputs_money ) { diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 553a22298..70f7e8328 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -379,11 +379,11 @@ namespace cryptonote return; } // remove first, in case this throws, so key images aren't removed - MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); + MINFO("Pruning tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first); m_blockchain.remove_txpool_tx(txid); - m_txpool_weight -= it->first.second; + m_txpool_weight -= meta.weight; remove_transaction_keyimages(tx, txid); - MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); + MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first); m_txs_by_fee_and_receive_time.erase(it--); changed = true; } |