protocol: more sanity checks in new chain block hashes

This commit is contained in:
moneromooo-monero 2020-12-31 21:12:30 +00:00
parent a1eca8ca7e
commit 0aa541b361
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
8 changed files with 90 additions and 21 deletions

View file

@ -2758,32 +2758,44 @@ void Blockchain::flush_invalid_blocks()
m_invalid_blocks.clear();
}
//------------------------------------------------------------------
bool Blockchain::have_block(const crypto::hash& id) const
bool Blockchain::have_block_unlocked(const crypto::hash& id, int *where) const
{
// WARNING: this function does not take m_blockchain_lock, and thus should only call read only
// m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as
// well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must
// lock if it is otherwise needed.
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(m_db->block_exists(id))
{
LOG_PRINT_L2("block " << id << " found in main chain");
if (where) *where = HAVE_BLOCK_MAIN_CHAIN;
return true;
}
if(m_db->get_alt_block(id, NULL, NULL))
{
LOG_PRINT_L2("block " << id << " found in alternative chains");
if (where) *where = HAVE_BLOCK_ALT_CHAIN;
return true;
}
if(m_invalid_blocks.count(id))
{
LOG_PRINT_L2("block " << id << " found in m_invalid_blocks");
if (where) *where = HAVE_BLOCK_INVALID;
return true;
}
return false;
}
//------------------------------------------------------------------
bool Blockchain::have_block(const crypto::hash& id, int *where) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return have_block_unlocked(id, where);
}
//------------------------------------------------------------------
bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc, bool notify/* = true*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);

View file

@ -377,10 +377,12 @@ namespace cryptonote
* for a block with the given hash
*
* @param id the hash to search for
* @param where the type of block, if non NULL
*
* @return true if the block is known, else false
*/
bool have_block(const crypto::hash& id) const;
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const;
bool have_block(const crypto::hash& id, int *where = NULL) const;
/**
* @brief gets the total number of transactions on the main chain

View file

@ -1625,9 +1625,14 @@ namespace cryptonote
return m_mempool.get_transactions_count(include_sensitive_txes);
}
//-----------------------------------------------------------------------------------------------
bool core::have_block(const crypto::hash& id) const
bool core::have_block_unlocked(const crypto::hash& id, int *where) const
{
return m_blockchain_storage.have_block(id);
return m_blockchain_storage.have_block_unlocked(id, where);
}
//-----------------------------------------------------------------------------------------------
bool core::have_block(const crypto::hash& id, int *where) const
{
return m_blockchain_storage.have_block(id, where);
}
//-----------------------------------------------------------------------------------------------
bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob) const

View file

@ -55,6 +55,8 @@
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4355)
enum { HAVE_BLOCK_MAIN_CHAIN, HAVE_BLOCK_ALT_CHAIN, HAVE_BLOCK_INVALID };
namespace cryptonote
{
struct test_options {
@ -543,7 +545,8 @@ namespace cryptonote
*
* @note see Blockchain::have_block
*/
bool have_block(const crypto::hash& id) const;
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const;
bool have_block(const crypto::hash& id, int *where = NULL) const;
/**
* @copydoc Blockchain::get_short_chain_history

View file

@ -2570,17 +2570,6 @@ skip:
return 1;
}
std::unordered_set<crypto::hash> hashes;
for (const auto &h: arg.m_block_ids)
{
if (!hashes.insert(h).second)
{
LOG_ERROR_CCONTEXT("sent duplicate block, dropping connection");
drop_connection(context, true, false);
return 1;
}
}
uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights);
if (n_use_blocks == 0 || n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size())
{
@ -2592,18 +2581,69 @@ skip:
context.m_needed_objects.clear();
uint64_t added = 0;
std::unordered_set<crypto::hash> blocks_found;
bool first = true;
bool expect_unknown = false;
for (size_t i = 0; i < arg.m_block_ids.size(); ++i)
{
if (!blocks_found.insert(arg.m_block_ids[i]).second)
{
LOG_ERROR_CCONTEXT("Duplicate blocks in chain entry response, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
int where;
const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where);
if (first && !have_block)
{
LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
if (!first)
{
// after the first, blocks may be known or unknown, but if they are known,
// they should be at the same height if on the main chain
if (have_block)
{
switch (where)
{
default:
case HAVE_BLOCK_INVALID:
LOG_ERROR_CCONTEXT("Block is invalid or known without known type, dropping connection");
drop_connection(context, true, false);
return 1;
case HAVE_BLOCK_MAIN_CHAIN:
if (expect_unknown)
{
LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
if (m_core.get_block_id_by_height(arg.start_height + i) != arg.m_block_ids[i])
{
LOG_ERROR_CCONTEXT("Block is on the main chain, but not at the expected height, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
break;
case HAVE_BLOCK_ALT_CHAIN:
if (expect_unknown)
{
LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
break;
}
}
else
expect_unknown = true;
}
const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i];
context.m_needed_objects.push_back(std::make_pair(arg.m_block_ids[i], block_weight));
if (++added == n_use_blocks)
break;
first = false;
}
context.m_last_response_height -= arg.m_block_ids.size() - n_use_blocks;

View file

@ -245,12 +245,17 @@ bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/
return true;
}
bool tests::proxy_core::have_block(const crypto::hash& id) {
bool tests::proxy_core::have_block_unlocked(const crypto::hash& id, int *where) {
if (m_hash2blkidx.end() == m_hash2blkidx.find(id))
return false;
if (where) *where = HAVE_BLOCK_MAIN_CHAIN;
return true;
}
bool tests::proxy_core::have_block(const crypto::hash& id, int *where) {
return have_block_unlocked(id, where);
}
void tests::proxy_core::build_short_history(std::list<crypto::hash> &m_history, const crypto::hash &m_start) {
m_history.push_front(get_block_hash(m_genesis));
/*std::unordered_map<crypto::hash, tests::block_index>::const_iterator cit = m_hash2blkidx.find(m_lastblk);

View file

@ -74,7 +74,8 @@ namespace tests
bool init(const boost::program_options::variables_map& vm);
bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids);
bool have_block(const crypto::hash& id);
bool have_block(const crypto::hash& id, int *where = NULL);
bool have_block_unlocked(const crypto::hash& id, int *where = NULL);
void get_blockchain_top(uint64_t& height, crypto::hash& top_id);
bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed);
bool handle_incoming_txs(const std::vector<cryptonote::tx_blob_entry>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, cryptonote::relay_method tx_relay, bool relayed);

View file

@ -55,7 +55,8 @@ public:
bool init(const boost::program_options::variables_map& vm) {return true ;}
bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids) const { return true; }
bool have_block(const crypto::hash& id) const {return true;}
bool have_block(const crypto::hash& id, int *where = NULL) const {return false;}
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const {return false;}
void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;}
bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; }
bool handle_incoming_txs(const std::vector<cryptonote::tx_blob_entry>& tx_blob, std::vector<cryptonote::tx_verification_context>& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; }