mirror of
https://github.com/monero-project/monero.git
synced 2026-01-08 19:31:07 -08:00
Merge pull request #9740
008ba966d blockchain sync: reduce disk writes from 2 to 1 per tx (jeffro256)
This commit is contained in:
@@ -83,7 +83,6 @@ else ()
|
||||
add_subdirectory(crypto)
|
||||
add_subdirectory(functional_tests)
|
||||
add_subdirectory(performance_tests)
|
||||
add_subdirectory(core_proxy)
|
||||
add_subdirectory(unit_tests)
|
||||
add_subdirectory(difficulty)
|
||||
add_subdirectory(block_weight)
|
||||
@@ -161,7 +160,6 @@ set(enabled_tests
|
||||
block_weight
|
||||
hash
|
||||
performance_tests
|
||||
core_proxy
|
||||
fuzz
|
||||
unit_tests)
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# Copyright (c) 2014-2022, 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.
|
||||
|
||||
set(core_proxy_sources
|
||||
core_proxy.cpp)
|
||||
|
||||
set(core_proxy_headers
|
||||
core_proxy.h)
|
||||
|
||||
monero_add_minimal_executable(core_proxy
|
||||
${core_proxy_sources}
|
||||
${core_proxy_headers})
|
||||
target_link_libraries(core_proxy
|
||||
PRIVATE
|
||||
cryptonote_core
|
||||
cryptonote_protocol
|
||||
p2p
|
||||
version
|
||||
epee
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES})
|
||||
set_property(TARGET core_proxy
|
||||
PROPERTY
|
||||
FOLDER "tests")
|
||||
@@ -1,295 +0,0 @@
|
||||
// Copyright (c) 2014-2022, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
// node.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "version.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "console_handler.h"
|
||||
#include "p2p/net_node.h"
|
||||
#include "p2p/net_node.inl"
|
||||
//#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.inl"
|
||||
#include "core_proxy.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace std;
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
using namespace crypto;
|
||||
|
||||
|
||||
BOOST_CLASS_VERSION(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<tests::proxy_core> >, 1);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
#ifdef WIN32
|
||||
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
|
||||
#endif
|
||||
|
||||
TRY_ENTRY();
|
||||
|
||||
tools::on_startup();
|
||||
string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
||||
//set up logging options
|
||||
mlog_configure(mlog_get_default_log_path("core_proxy.log"), true);
|
||||
mlog_set_log_level(2);
|
||||
|
||||
|
||||
po::options_description desc("Allowed options");
|
||||
command_line::add_arg(desc, cryptonote::arg_data_dir);
|
||||
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<tests::proxy_core> >::init_options(desc);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc, [&]()
|
||||
{
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
MGINFO("Module folder: " << argv[0]);
|
||||
MGINFO("Node starting ...");
|
||||
|
||||
|
||||
//create objects and link them
|
||||
tests::proxy_core pr_core;
|
||||
cryptonote::t_cryptonote_protocol_handler<tests::proxy_core> cprotocol(pr_core, NULL);
|
||||
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<tests::proxy_core> > p2psrv {
|
||||
cprotocol
|
||||
};
|
||||
cprotocol.set_p2p_endpoint(&p2psrv);
|
||||
//pr_core.set_cryptonote_protocol(&cprotocol);
|
||||
//daemon_cmmands_handler dch(p2psrv);
|
||||
|
||||
//initialize objects
|
||||
|
||||
MGINFO("Initializing p2p server...");
|
||||
bool res = p2psrv.init(vm);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server.");
|
||||
MGINFO("P2p server initialized OK");
|
||||
|
||||
MGINFO("Initializing cryptonote protocol...");
|
||||
res = cprotocol.init(vm);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol.");
|
||||
MGINFO("Cryptonote protocol initialized OK");
|
||||
|
||||
//initialize core here
|
||||
MGINFO("Initializing proxy core...");
|
||||
res = pr_core.init(vm);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core");
|
||||
MGINFO("Core initialized OK");
|
||||
|
||||
MGINFO("Starting p2p net loop...");
|
||||
p2psrv.run();
|
||||
MGINFO("p2p net loop stopped");
|
||||
|
||||
//deinitialize components
|
||||
MGINFO("Deinitializing core...");
|
||||
pr_core.deinit();
|
||||
MGINFO("Deinitializing cryptonote_protocol...");
|
||||
cprotocol.deinit();
|
||||
MGINFO("Deinitializing p2p...");
|
||||
p2psrv.deinit();
|
||||
|
||||
|
||||
//pr_core.set_cryptonote_protocol(NULL);
|
||||
cprotocol.set_p2p_endpoint(NULL);
|
||||
|
||||
|
||||
MGINFO("Node stopped.");
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
string tx2str(const cryptonote::transaction& tx, const cryptonote::hash256& tx_hash, const cryptonote::hash256& tx_prefix_hash, const cryptonote::blobdata& blob) {
|
||||
stringstream ss;
|
||||
|
||||
ss << "{" << endl;
|
||||
ss << "\tversion:" << tx.version << endl;
|
||||
ss << "\tunlock_time:" << tx.unlock_time << endl;
|
||||
ss << "\t"
|
||||
|
||||
return ss.str();
|
||||
}*/
|
||||
|
||||
bool tests::proxy_core::handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) {
|
||||
if (tx_relay != cryptonote::relay_method::block)
|
||||
return true;
|
||||
|
||||
crypto::hash tx_hash = null_hash;
|
||||
crypto::hash tx_prefix_hash = null_hash;
|
||||
transaction tx;
|
||||
|
||||
if (tx_blob.prunable_hash != crypto::null_hash)
|
||||
{
|
||||
cerr << "WRONG TRANSACTION, pruned blob rejected" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_and_validate_tx_from_blob(tx_blob.blob, tx, tx_hash, tx_prefix_hash)) {
|
||||
cerr << "WRONG TRANSACTION BLOB, Failed to parse, rejected" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
cout << "TX " << endl << endl;
|
||||
cout << tx_hash << endl;
|
||||
cout << tx_prefix_hash << endl;
|
||||
cout << tx_blob.blob.size() << endl;
|
||||
//cout << string_tools::buff_to_hex_nodelimer(tx_blob) << endl << endl;
|
||||
cout << obj_to_json_str(tx) << endl;
|
||||
cout << endl << "ENDTX" << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tests::proxy_core::handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, cryptonote::relay_method tx_relay, bool relayed)
|
||||
{
|
||||
tvc.resize(tx_blobs.size());
|
||||
size_t i = 0;
|
||||
for (const auto &tx_blob: tx_blobs)
|
||||
{
|
||||
if (!handle_incoming_tx(tx_blob, tvc[i], tx_relay, relayed))
|
||||
return false;
|
||||
++i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block_, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate) {
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
|
||||
if(!parse_and_validate_block_from_blob(block_blob, b)) {
|
||||
cerr << "Failed to parse and validate new block" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::hash h;
|
||||
crypto::hash lh;
|
||||
cout << "BLOCK" << endl << endl;
|
||||
cout << (h = get_block_hash(b)) << endl;
|
||||
cout << (lh = get_block_longhash(NULL, b, 0, 0)) << endl;
|
||||
cout << get_transaction_hash(b.miner_tx) << endl;
|
||||
cout << ::get_object_blobsize(b.miner_tx) << endl;
|
||||
//cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl;
|
||||
cout << obj_to_json_str(b) << endl;
|
||||
|
||||
cout << endl << "ENDBLOCK" << endl << endl;
|
||||
|
||||
if (!add_block(h, lh, b, block_blob))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tests::proxy_core::get_short_chain_history(std::list<crypto::hash>& ids) {
|
||||
build_short_history(ids, m_lastblk);
|
||||
return true;
|
||||
}
|
||||
|
||||
void tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) {
|
||||
height = 0;
|
||||
top_id = get_block_hash(m_genesis);
|
||||
}
|
||||
|
||||
bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) {
|
||||
generate_genesis_block(m_genesis, config::GENESIS_TX, config::GENESIS_NONCE);
|
||||
crypto::hash h = get_block_hash(m_genesis);
|
||||
add_block(h, get_block_longhash(NULL, m_genesis, 0, 0), m_genesis, block_to_blob(m_genesis));
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
do {
|
||||
m_history.push_front(cit->first);
|
||||
|
||||
size_t n = 1 << m_history.size();
|
||||
while (m_hash2blkidx.end() != cit && crypto::null_hash != cit->second.blk.prev_id && n > 0) {
|
||||
n--;
|
||||
cit = m_hash2blkidx.find(cit->second.blk.prev_id);
|
||||
}
|
||||
} while (m_hash2blkidx.end() != cit && get_block_hash(cit->second.blk) != cit->first);*/
|
||||
}
|
||||
|
||||
bool tests::proxy_core::add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob) {
|
||||
size_t height = 0;
|
||||
|
||||
if (crypto::null_hash != _blk.prev_id) {
|
||||
std::unordered_map<crypto::hash, tests::block_index>::const_iterator cit = m_hash2blkidx.find(_blk.prev_id);
|
||||
if (m_hash2blkidx.end() == cit) {
|
||||
cerr << "ERROR: can't find previous block with id \"" << _blk.prev_id << "\"" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
height = cit->second.height + 1;
|
||||
}
|
||||
|
||||
m_known_block_list.push_back(_id);
|
||||
|
||||
block_index bi(height, _id, _longhash, _blk, _blob, txes);
|
||||
m_hash2blkidx.insert(std::make_pair(_id, bi));
|
||||
txes.clear();
|
||||
m_lastblk = _id;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright (c) 2014-2022, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_basic/verification_context.h"
|
||||
#include "cryptonote_core/i_core_events.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace tests
|
||||
{
|
||||
struct block_index {
|
||||
size_t height;
|
||||
crypto::hash id;
|
||||
crypto::hash longhash;
|
||||
cryptonote::block blk;
|
||||
cryptonote::blobdata blob;
|
||||
std::list<cryptonote::transaction> txes;
|
||||
|
||||
block_index() : height(0), id(crypto::null_hash), longhash(crypto::null_hash) { }
|
||||
block_index(size_t _height, const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob, const std::list<cryptonote::transaction> &_txes)
|
||||
: height(_height), id(_id), longhash(_longhash), blk(_blk), blob(_blob), txes(_txes) { }
|
||||
};
|
||||
|
||||
class proxy_core : public cryptonote::i_core_events
|
||||
{
|
||||
cryptonote::block m_genesis;
|
||||
std::list<crypto::hash> m_known_block_list;
|
||||
std::unordered_map<crypto::hash, block_index> m_hash2blkidx;
|
||||
|
||||
crypto::hash m_lastblk;
|
||||
std::list<cryptonote::transaction> txes;
|
||||
|
||||
bool add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob);
|
||||
void build_short_history(std::list<crypto::hash> &m_history, const crypto::hash &m_start);
|
||||
|
||||
|
||||
public:
|
||||
virtual bool is_synchronized() const final { return true; }
|
||||
void on_synchronized(){}
|
||||
void safesyncmode(const bool){}
|
||||
virtual uint64_t get_current_blockchain_height() const final {return 1;}
|
||||
void set_target_blockchain_height(uint64_t) {}
|
||||
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, 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);
|
||||
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true);
|
||||
void pause_mine(){}
|
||||
void resume_mine(){}
|
||||
bool on_idle(){return true;}
|
||||
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;}
|
||||
bool handle_get_objects(cryptonote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote::cryptonote_connection_context& context){return true;}
|
||||
cryptonote::Blockchain &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); }
|
||||
bool get_test_drop_download() {return true;}
|
||||
bool get_test_drop_download_height() {return true;}
|
||||
bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks_entry, std::vector<cryptonote::block> &blocks) { return true; }
|
||||
bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; }
|
||||
bool update_checkpoints(const bool skip_dns = false) { return true; }
|
||||
uint64_t get_target_blockchain_height() const { return 1; }
|
||||
size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
|
||||
virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, cryptonote::relay_method tx_relay) {}
|
||||
cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; }
|
||||
bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob, cryptonote::relay_category tx_category) const { return false; }
|
||||
bool pool_has_tx(const crypto::hash &txid) const { return false; }
|
||||
bool get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata, cryptonote::block>>& blocks, std::vector<cryptonote::blobdata>& txs) const { return false; }
|
||||
bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::transaction>& txs, std::vector<crypto::hash>& missed_txs) const { return false; }
|
||||
bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; }
|
||||
uint8_t get_ideal_hard_fork_version() const { return 0; }
|
||||
uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; }
|
||||
uint8_t get_hard_fork_version(uint64_t height) const { return 0; }
|
||||
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; }
|
||||
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
|
||||
bool fluffy_blocks_enabled() const { return false; }
|
||||
uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights) { return 0; }
|
||||
bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; }
|
||||
bool is_within_compiled_block_hash_area(uint64_t height) const { return false; }
|
||||
bool pad_transactions() const { return false; }
|
||||
uint32_t get_blockchain_pruning_seed() const { return 0; }
|
||||
bool prune_blockchain(uint32_t pruning_seed) const { return true; }
|
||||
bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; }
|
||||
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; }
|
||||
crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; }
|
||||
};
|
||||
}
|
||||
@@ -568,7 +568,7 @@ public:
|
||||
|
||||
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
|
||||
size_t pool_size = m_c.get_pool_transactions_count();
|
||||
m_c.handle_incoming_tx({t_serializable_object_to_blob(tx), crypto::null_hash}, tvc, m_tx_relay, false);
|
||||
m_c.handle_incoming_tx(t_serializable_object_to_blob(tx), tvc, m_tx_relay, false);
|
||||
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
|
||||
bool r = m_validator.check_tx_verification_context(tvc, tx_added, m_ev_index, tx);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
|
||||
@@ -579,16 +579,17 @@ public:
|
||||
{
|
||||
log_event("cryptonote::transaction");
|
||||
|
||||
std::vector<cryptonote::tx_blob_entry> tx_blobs;
|
||||
std::vector<cryptonote::blobdata> tx_blobs;
|
||||
std::vector<cryptonote::tx_verification_context> tvcs;
|
||||
cryptonote::tx_verification_context tvc0 = AUTO_VAL_INIT(tvc0);
|
||||
for (const auto &tx: txs)
|
||||
{
|
||||
tx_blobs.push_back({t_serializable_object_to_blob(tx)});
|
||||
tx_blobs.emplace_back(t_serializable_object_to_blob(tx));
|
||||
tvcs.push_back(tvc0);
|
||||
}
|
||||
size_t pool_size = m_c.get_pool_transactions_count();
|
||||
m_c.handle_incoming_txs(tx_blobs, tvcs, m_tx_relay, false);
|
||||
for (size_t i = 0; i < tx_blobs.size(); ++i)
|
||||
m_c.handle_incoming_tx(tx_blobs[i], tvcs[i], m_tx_relay, false);
|
||||
size_t tx_added = m_c.get_pool_transactions_count() - pool_size;
|
||||
bool r = m_validator.check_tx_verification_context_array(tvcs, tx_added, m_ev_index, txs);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
|
||||
|
||||
@@ -44,7 +44,10 @@ class P2PTest():
|
||||
self.create()
|
||||
self.mine(80)
|
||||
self.test_p2p_reorg()
|
||||
self.test_p2p_tx_propagation()
|
||||
txid = self.test_p2p_tx_propagation()
|
||||
self.test_p2p_block_propagation_shared(txid)
|
||||
txid = self.test_p2p_tx_propagation()
|
||||
self.test_p2p_block_propagation_new(txid)
|
||||
|
||||
def reset(self):
|
||||
print('Resetting blockchain')
|
||||
@@ -158,7 +161,6 @@ class P2PTest():
|
||||
loops -= 1
|
||||
assert loops >= 0
|
||||
|
||||
|
||||
def test_p2p_tx_propagation(self):
|
||||
print('Testing P2P tx propagation')
|
||||
daemons = (Daemon(idx=2), Daemon(idx=3))
|
||||
@@ -202,6 +204,107 @@ class P2PTest():
|
||||
npending = len(pending_daemons)
|
||||
assert npending == 0, '%d daemons pending' % npending
|
||||
|
||||
return txid
|
||||
|
||||
def test_p2p_block_propagation_shared(self, mempool_txid):
|
||||
print('Testing P2P block propagation with shared TX')
|
||||
daemon2 = Daemon(idx = 2)
|
||||
daemon3 = Daemon(idx = 3)
|
||||
|
||||
# check precondition: txid in daemon2's and daemon3's mempool
|
||||
res = daemon2.get_transaction_pool_hashes()
|
||||
assert mempool_txid in res.get('tx_hashes', [])
|
||||
|
||||
res = daemon3.get_transaction_pool_hashes()
|
||||
assert mempool_txid in res.get('tx_hashes', [])
|
||||
|
||||
# mine block on daemon2
|
||||
res = daemon2.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
|
||||
block_height = res.height
|
||||
|
||||
# wait until both are synced, or 5 seconds, whichever is first.
|
||||
# the timeout time should be very, very low here since block propagation, unlike tx propagation, is designed
|
||||
# to be as fast as possible.
|
||||
# and since both daemons already have the tx in their mempools, a notification of a new fluffy block, which is
|
||||
# pushed out immediately upon being mined, should result in an instant addition of that block to the chain,
|
||||
# without any round trips.
|
||||
# this test should only fail if you're running it on a potato where PoW verification + check_tx_inputs() takes
|
||||
# more than 5 second.
|
||||
deadline = time.monotonic() + 5
|
||||
result = None
|
||||
while result is None:
|
||||
res2 = daemon2.get_info()
|
||||
res3 = daemon3.get_info()
|
||||
if res2.top_block_hash == res3.top_block_hash:
|
||||
result = True
|
||||
elif time.monotonic() > deadline:
|
||||
result = False
|
||||
else:
|
||||
time.sleep(.25)
|
||||
assert result, "Shared tx block propagation timed out"
|
||||
|
||||
# check the tx is moved to both daemons's blockchains at top block
|
||||
for daemon in [daemon2, daemon3]:
|
||||
res = daemon.get_transaction_pool_hashes()
|
||||
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
|
||||
|
||||
res = daemon.get_transactions([mempool_txid])
|
||||
assert len(res.get('txs', [])) == 1
|
||||
tx_details = res.txs[0]
|
||||
assert ('in_pool' not in tx_details) or (not tx_details.in_pool)
|
||||
assert tx_details.block_height == block_height
|
||||
|
||||
def test_p2p_block_propagation_new(self, mempool_txid):
|
||||
# there's a big problem with this testcase in that there's not yet a way to prevent daemon's from syncing
|
||||
# mempools only, but still allow block propagation. so there's a good chance that the transaction will be synced
|
||||
# between daemons between when daemon2's mempool is flushed and when daemon3 mines a new block. in this
|
||||
# scenario, this testcase basically just degenerates into test_p2p_block_propagation_shared(). however, if this
|
||||
# one ever fails but test_p2p_block_propagation_shared() passes, then we might have actually caught a problem
|
||||
# with block propagation when one of the daemons is missing a tx(s)
|
||||
|
||||
print('Testing P2P block propagation with (possibly) new TX')
|
||||
daemon2 = Daemon(idx = 2)
|
||||
daemon3 = Daemon(idx = 3)
|
||||
|
||||
# check precondition: txid in daemon3's mempool
|
||||
res = daemon3.get_transaction_pool_hashes()
|
||||
assert mempool_txid in res.get('tx_hashes', [])
|
||||
|
||||
# flush daemon2 mempool
|
||||
daemon2.flush_txpool()
|
||||
|
||||
# mine block on daemon3
|
||||
res = daemon3.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
|
||||
block_height = res.height
|
||||
|
||||
# wait until both are synced, or 5 seconds, whichever is first.
|
||||
# the timeout time should be very, very low here since block propagation, unlike tx propagation, is designed
|
||||
# to be as fast as possible. however, it might have to be raised if the daemon actually does make a round trip
|
||||
# to request a missing tx in a fluffy block
|
||||
deadline = time.monotonic() + 5
|
||||
result = None
|
||||
while result is None:
|
||||
res2 = daemon2.get_info()
|
||||
res3 = daemon3.get_info()
|
||||
if res2.top_block_hash == res3.top_block_hash:
|
||||
result = True
|
||||
elif time.monotonic() > deadline:
|
||||
result = False
|
||||
else:
|
||||
time.sleep(.25)
|
||||
assert result, "New tx block propagation timed out"
|
||||
|
||||
# check the tx is moved to both daemons's blockchains at top block
|
||||
for daemon in [daemon2, daemon3]:
|
||||
res = daemon.get_transaction_pool_hashes()
|
||||
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
|
||||
|
||||
res = daemon.get_transactions([mempool_txid])
|
||||
assert len(res.get('txs', [])) == 1
|
||||
tx_details = res.txs[0]
|
||||
assert ('in_pool' not in tx_details) or (not tx_details.in_pool)
|
||||
assert tx_details.block_height == block_height
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
P2PTest().run_test()
|
||||
|
||||
@@ -60,9 +60,10 @@ public:
|
||||
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; }
|
||||
bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; }
|
||||
bool handle_single_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *b, cryptonote::block_verification_context& bvc, cryptonote::pool_supplement& extra_block_txs, bool update_miner_blocktemplate = true) { return true; }
|
||||
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; }
|
||||
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, cryptonote::pool_supplement& extra_block_txs, bool update_miner_blocktemplate = true) { return true; }
|
||||
void pause_mine(){}
|
||||
void resume_mine(){}
|
||||
bool on_idle(){return true;}
|
||||
@@ -73,6 +74,7 @@ public:
|
||||
bool get_test_drop_download_height() const {return true;}
|
||||
bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks_entry, std::vector<cryptonote::block> &blocks) { return true; }
|
||||
bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; }
|
||||
bool check_incoming_block_size(const cryptonote::blobdata& block_blob) const { return true; }
|
||||
bool update_checkpoints(const bool skip_dns = false) { return true; }
|
||||
uint64_t get_target_blockchain_height() const { return 1; }
|
||||
size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
|
||||
@@ -81,6 +83,7 @@ public:
|
||||
bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob, cryptonote::relay_category tx_category) const { return false; }
|
||||
bool pool_has_tx(const crypto::hash &txid) const { return false; }
|
||||
bool get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata, cryptonote::block>>& blocks, std::vector<cryptonote::blobdata>& txs) const { return false; }
|
||||
bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const { return false; }
|
||||
bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::transaction>& txs, std::vector<crypto::hash>& missed_txs) const { return false; }
|
||||
bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; }
|
||||
uint8_t get_ideal_hard_fork_version() const { return 0; }
|
||||
@@ -88,7 +91,6 @@ public:
|
||||
uint8_t get_hard_fork_version(uint64_t height) const { return 0; }
|
||||
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; }
|
||||
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
|
||||
bool fluffy_blocks_enabled() const { return false; }
|
||||
uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights) { return 0; }
|
||||
bool pad_transactions() { return false; }
|
||||
uint32_t get_blockchain_pruning_seed() const { return 0; }
|
||||
|
||||
Reference in New Issue
Block a user