From 089c7637a64652c8bcf6c437065237c23266d4a9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 4 Apr 2019 00:15:57 +0000 Subject: [PATCH] cryptonote: rework block blob size sanity check Use the actual block weight limit, assuming that weight is always greater or equal to size --- src/blockchain_db/blockchain_db.h | 14 +++++ src/blockchain_db/lmdb/db_lmdb.cpp | 52 +++++++++++++++++++ src/blockchain_db/lmdb/db_lmdb.h | 3 ++ src/blockchain_db/testdb.h | 3 ++ src/cryptonote_basic/cryptonote_basic.h | 2 + .../cryptonote_basic_impl.cpp | 5 -- src/cryptonote_basic/cryptonote_basic_impl.h | 1 - src/cryptonote_config.h | 4 +- src/cryptonote_core/blockchain.cpp | 6 +++ src/cryptonote_core/cryptonote_core.cpp | 26 +++++++--- 10 files changed, 100 insertions(+), 16 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index d2fe39fc2..2c40b5a78 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1513,6 +1513,20 @@ public: */ virtual bool check_pruning() = 0; + /** + * @brief get the max block size + */ + virtual uint64_t get_max_block_size() = 0; + + /** + * @brief add a new max block size + * + * The max block size will be the maximum of sz and the current block size + * + * @param: sz the block size + */ + + virtual void add_max_block_size(uint64_t sz) = 0; /** * @brief runs a function over all txpool transactions * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 9f71fd068..a07e9ac55 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2513,6 +2513,58 @@ std::vector BlockchainLMDB::get_block_info_64bit_fields(uint64_t start return ret; } +uint64_t BlockchainLMDB::get_max_block_size() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(properties) + MDB_val_str(k, "max_block_size"); + MDB_val v; + int result = mdb_cursor_get(m_cur_properties, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return std::numeric_limits::max(); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to retrieve max block size: ", result).c_str())); + if (v.mv_size != sizeof(uint64_t)) + throw0(DB_ERROR("Failed to retrieve or create max block size: unexpected value size")); + uint64_t max_block_size; + memcpy(&max_block_size, v.mv_data, sizeof(max_block_size)); + TXN_POSTFIX_RDONLY(); + return max_block_size; +} + +void BlockchainLMDB::add_max_block_size(uint64_t sz) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + CURSOR(properties) + + MDB_val_str(k, "max_block_size"); + MDB_val v; + int result = mdb_cursor_get(m_cur_properties, &k, &v, MDB_SET); + if (result && result != MDB_NOTFOUND) + throw0(DB_ERROR(lmdb_error("Failed to retrieve max block size: ", result).c_str())); + uint64_t max_block_size = 0; + if (result == 0) + { + if (v.mv_size != sizeof(uint64_t)) + throw0(DB_ERROR("Failed to retrieve or create max block size: unexpected value size")); + memcpy(&max_block_size, v.mv_data, sizeof(max_block_size)); + } + if (sz > max_block_size) + max_block_size = sz; + v.mv_data = (void*)&max_block_size; + v.mv_size = sizeof(max_block_size); + result = mdb_cursor_put(m_cur_properties, &k, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to set max_block_size: ", result).c_str())); +} + + std::vector BlockchainLMDB::get_block_weights(uint64_t start_height, size_t count) const { return get_block_info_64bit_fields(start_height, count, offsetof(mdb_block_info, bi_weight)); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 2f89b77ac..f6b00817d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -400,6 +400,9 @@ private: std::vector get_block_info_64bit_fields(uint64_t start_height, size_t count, off_t offset) const; + uint64_t get_max_block_size(); + void add_max_block_size(uint64_t sz); + // fix up anything that may be wrong due to past bugs virtual void fixup(); diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 7916364c5..04fad26a4 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -149,6 +149,9 @@ public: virtual bool update_pruning() { return true; } virtual bool check_pruning() { return true; } virtual void prune_outputs(uint64_t amount) {} + + virtual uint64_t get_max_block_size() { return 100000000; } + virtual void add_max_block_size(uint64_t sz) { } }; } diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 03caafbb0..20d92bdf1 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -422,6 +422,8 @@ namespace cryptonote FIELDS(*static_cast(this)) FIELD(miner_tx) FIELD(tx_hashes) + if (tx_hashes.size() > CRYPTONOTE_MAX_TX_PER_BLOCK) + return false; END_SERIALIZE() }; diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index e336cc1d1..d8de65b81 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -76,11 +76,6 @@ namespace cryptonote { return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; } //----------------------------------------------------------------------------------------------- - size_t get_max_block_size() - { - return CRYPTONOTE_MAX_BLOCK_SIZE; - } - //----------------------------------------------------------------------------------------------- size_t get_max_tx_size() { return CRYPTONOTE_MAX_TX_SIZE; diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 036273f0e..c7198a16f 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -87,7 +87,6 @@ namespace cryptonote { /* Cryptonote helper functions */ /************************************************************************/ size_t get_min_block_weight(uint8_t version); - size_t get_max_block_size(); size_t get_max_tx_size(); bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index b6087de22..56b6a63b7 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -37,9 +37,9 @@ #define CRYPTONOTE_DNS_TIMEOUT_MS 20000 #define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000 -#define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! #define CRYPTONOTE_GETBLOCKTEMPLATE_MAX_BLOCK_SIZE 196608 //size of block (bytes) that is the maximum that miners will produce -#define CRYPTONOTE_MAX_TX_SIZE 1000000000 +#define CRYPTONOTE_MAX_TX_SIZE 1000000 +#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000 #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 #define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60 #define CURRENT_TRANSACTION_VERSION 2 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 83d3044f8..5925548d6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3872,6 +3872,8 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti LOG_PRINT_L3("Blockchain::" << __func__); + m_db->block_txn_start(false); + // when we reach this, the last hf version is not yet written to the db const uint64_t db_height = m_db->height(); const uint8_t hf_version = get_current_hard_fork_version(); @@ -3934,6 +3936,10 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti if (long_term_effective_median_block_weight) *long_term_effective_median_block_weight = m_long_term_effective_median_block_weight; + m_db->add_max_block_size(m_current_block_cumul_weight_limit); + + m_db->block_txn_stop(); + return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index da413bbe2..87cfb27e1 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -62,6 +62,9 @@ DISABLE_VS_WARNINGS(4355) #define BAD_SEMANTICS_TXES_MAX_SIZE 100 +// basically at least how many bytes the block itself serializes to without the miner tx +#define BLOCK_SIZE_SANITY_LEEWAY 100 + namespace cryptonote { const command_line::arg_descriptor arg_testnet_on = { @@ -1417,18 +1420,21 @@ namespace cryptonote { TRY_ENTRY(); - // load json & DNS checkpoints every 10min/hour respectively, - // and verify them with respect to what blocks we already have - CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); - bvc = boost::value_initialized(); - if(block_blob.size() > get_max_block_size()) + + if (!check_incoming_block_size(block_blob)) { - LOG_PRINT_L1("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); bvc.m_verifivation_failed = true; return false; } + if (((size_t)-1) <= 0xffffffff && block_blob.size() >= 0x3fffffff) + MWARNING("This block's size is " << block_blob.size() << ", closing on the 32 bit limit"); + + // load json & DNS checkpoints every 10min/hour respectively, + // and verify them with respect to what blocks we already have + CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); + block lb; if (!b) { @@ -1452,9 +1458,13 @@ namespace cryptonote // block_blob bool core::check_incoming_block_size(const blobdata& block_blob) const { - if(block_blob.size() > get_max_block_size()) + // note: we assume block weight is always >= block blob size, so we check incoming + // blob size against the block weight limit, which acts as a sanity check without + // having to parse/weigh first; in fact, since the block blob is the block header + // plus the tx hashes, the weight will typically be much larger than the blob size + if(block_blob.size() > m_blockchain_storage.get_current_cumulative_block_weight_limit() + BLOCK_SIZE_SANITY_LEEWAY) { - LOG_PRINT_L1("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); + LOG_PRINT_L1("WRONG BLOCK BLOB, sanity check failed on size " << block_blob.size() << ", rejected"); return false; } return true;