mirror of
https://github.com/wownero/wownero.git
synced 2026-10-17 15:41:44 +02:00
Initialize RPC server state used by direct handler calls, let fuzz input choose restricted or unrestricted mode, and skip iterations when the dummy core fails to initialize. Also update the RPC fuzz harness documentation after removal of the old RPC payment setup.
325 lines
11 KiB
C++
325 lines
11 KiB
C++
#include "initialisation.h"
|
|
|
|
#include "crypto/crypto.h"
|
|
#include "cryptonote_config.h"
|
|
#include "cryptonote_basic/cryptonote_basic.h"
|
|
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
|
#include "cryptonote_basic/tx_extra.h"
|
|
#include "cryptonote_core/cryptonote_core.h"
|
|
#include "cryptonote_core/cryptonote_tx_utils.h"
|
|
#include "cryptonote_core/tx_pool.h"
|
|
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
|
|
#include "p2p/net_node.h"
|
|
#include "p2p/net_node.inl"
|
|
#include "ringct/rctTypes.h"
|
|
#include "serialization/binary_utils.h"
|
|
#include "serialization/variant.h"
|
|
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/program_options/variables_map.hpp>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <functional>
|
|
#include <unordered_map>
|
|
|
|
// For storing valid transaction hashes
|
|
std::vector<crypto::hash> cached_tx_hashes;
|
|
|
|
// Configure a dummy protocol to avoid external requests
|
|
bool DummyProtocol::is_synchronized() const {
|
|
return true;
|
|
}
|
|
|
|
bool DummyProtocol::relay_transactions(cryptonote::NOTIFY_NEW_TRANSACTIONS::request&, const boost::uuids::uuid&, epee::net_utils::zone, cryptonote::relay_method) {
|
|
return true;
|
|
}
|
|
|
|
bool DummyProtocol::relay_block(cryptonote::NOTIFY_NEW_FLUFFY_BLOCK::request&, cryptonote::cryptonote_connection_context&) {
|
|
return true;
|
|
}
|
|
|
|
// Function to create and initialise a dummy rpc core object
|
|
std::unique_ptr<CoreEnv> initialise_rpc_core() {
|
|
auto env = std::make_unique<CoreEnv>();
|
|
|
|
// Create fresh pointer for dummy protocol and core
|
|
env->protocol = std::make_unique<DummyProtocol>();
|
|
env->core = std::make_unique<cryptonote::core>(env->protocol.get());
|
|
|
|
// Configure the rpc core object with default settings
|
|
boost::program_options::options_description desc;
|
|
cryptonote::core::init_options(desc);
|
|
boost::program_options::variables_map vm;
|
|
|
|
// Add command line arguments to configure the rpc core object to use regression testing mode (FAKECHAIN)
|
|
// and a throwaway data directory. Enabling FAKECHAIN mode allows skipping validation logic of the authors
|
|
// signature and transactions ID on valid blocks and transactions while keeping other logic.
|
|
const boost::filesystem::path data_dir = boost::filesystem::temp_directory_path() / "monero-fuzz-rpc";
|
|
std::vector<std::string> args = {"fuzz", "--regtest", "--offline", "--data-dir", data_dir.string()};
|
|
std::vector<const char*> argv;
|
|
argv.reserve(args.size());
|
|
for (const auto& arg : args) {
|
|
argv.push_back(arg.c_str());
|
|
}
|
|
boost::program_options::store(boost::program_options::parse_command_line(argv.size(), argv.data(), desc), vm);
|
|
boost::program_options::notify(vm);
|
|
|
|
// Initialise the dummy core with all the above settings
|
|
if (!env->core->init(vm)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return env;
|
|
}
|
|
|
|
// Build the core_rpc_server handler object with the configured dummy core object
|
|
std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core& dummy_core, bool restricted) {
|
|
// Create rpc server bundle object
|
|
auto bundle = std::make_unique<RpcServerBundle>();
|
|
|
|
// Allocate new protocol and node server then create RPC server object
|
|
bundle->proto_handler = std::make_unique<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>(dummy_core, nullptr, true);
|
|
bundle->proto_handler->set_no_sync(false);
|
|
bundle->dummy_p2p = std::make_unique<nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>>(*bundle->proto_handler);
|
|
bundle->rpc = std::make_unique<cryptonote::core_rpc_server>(dummy_core, *bundle->dummy_p2p, restricted);
|
|
|
|
return bundle;
|
|
}
|
|
|
|
bool generate_random_blocks(cryptonote::core& core, FuzzedDataProvider& provider) {
|
|
static std::vector<cryptonote::block> cached_blocks;
|
|
static std::vector<cryptonote::blobdata> cached_txs;
|
|
|
|
bool use_cached_blocks = provider.ConsumeBool();
|
|
bool use_cached_txs = provider.ConsumeBool();
|
|
|
|
if (!use_cached_blocks || cached_blocks.empty()) {
|
|
// Create new cached blocks
|
|
cached_blocks.clear();
|
|
|
|
// Prepare the genesis block (initial base block) in the block chains
|
|
cryptonote::block genesis_block;
|
|
cryptonote::generate_genesis_block(genesis_block, config::GENESIS_TX, config::GENESIS_NONCE);
|
|
cached_blocks.push_back(genesis_block);
|
|
|
|
// Setup initial values
|
|
const size_t median_weight = 300000;
|
|
const size_t block_weight = 100000;
|
|
const uint8_t version = core.get_blockchain_storage().get_current_hard_fork_version();
|
|
|
|
// Accumulate coins for genesis block
|
|
uint64_t coins = 0;
|
|
for (const auto& o : genesis_block.miner_tx.vout) {
|
|
coins += o.amount;
|
|
}
|
|
|
|
// Create random number of miners
|
|
size_t miner_count = provider.ConsumeIntegralInRange<size_t>(1, 4);
|
|
std::vector<cryptonote::account_base> miners(miner_count);
|
|
for (auto& miner : miners) {
|
|
miner.generate();
|
|
}
|
|
|
|
// Create random number of blocks
|
|
size_t total_blocks = provider.ConsumeIntegralInRange<size_t>(2, 15);
|
|
for (size_t i = 0; i < total_blocks; ++i) {
|
|
// Stop generate blocks if no more bytes left
|
|
if (provider.remaining_bytes() <= 0) {
|
|
break;
|
|
}
|
|
|
|
// Randomly link a miner identity to this block
|
|
cryptonote::block blk;
|
|
size_t selected_miner_index = provider.ConsumeIntegralInRange<size_t>(0, miners.size() - 1);
|
|
cryptonote::account_base& miner = miners[selected_miner_index];
|
|
|
|
// Initialise block property with random value
|
|
uint64_t height = cached_blocks.size();
|
|
crypto::hash prev_id = cached_blocks.back().hash;
|
|
blk.major_version = version;
|
|
blk.minor_version = version;
|
|
blk.timestamp = core.get_blockchain_storage().get_adjusted_time(height);
|
|
blk.prev_id = prev_id;
|
|
blk.nonce = provider.ConsumeIntegral<uint32_t>();
|
|
|
|
// Calculate and accumulate reward
|
|
uint64_t base_reward = 0, fee = 0;
|
|
if (!cryptonote::get_block_reward(median_weight, block_weight, coins, base_reward, version)) {
|
|
break;
|
|
}
|
|
|
|
// Link empty miner transaction to block
|
|
cryptonote::transaction miner_tx;
|
|
cryptonote::construct_miner_tx(
|
|
height, median_weight, coins, block_weight,
|
|
fee, miner.get_keys().m_account_address,
|
|
miner_tx, cryptonote::blobdata(),
|
|
1, blk.major_version
|
|
);
|
|
for (const auto& o : miner_tx.vout) {
|
|
coins += o.amount;
|
|
}
|
|
miner_tx.version = 2;
|
|
blk.miner_tx = miner_tx;
|
|
|
|
// Add the block to the cache
|
|
cached_blocks.push_back(blk);
|
|
}
|
|
}
|
|
|
|
if (cached_blocks.empty()) {
|
|
// No random block generated, exit early to next iteration
|
|
return false;
|
|
}
|
|
|
|
if (!use_cached_txs || cached_txs.empty()) {
|
|
// Create new cache transactions
|
|
cached_txs.clear();
|
|
cached_tx_hashes.clear();
|
|
|
|
// Random number of transactions to generate
|
|
const size_t tx_count = provider.ConsumeIntegralInRange<size_t>(1, 4);
|
|
for (size_t i = 0; i < tx_count; ++i) {
|
|
// Stop generate transactions if no more bytes left
|
|
if (provider.remaining_bytes() <= 0) {
|
|
break;
|
|
}
|
|
|
|
// Initialise variables
|
|
cryptonote::transaction tx;
|
|
std::vector<cryptonote::tx_source_entry> sources;
|
|
std::vector<cryptonote::tx_destination_entry> destinations;
|
|
std::vector<uint8_t> extra;
|
|
|
|
// Generate sender account
|
|
cryptonote::account_base sender;
|
|
sender.generate();
|
|
|
|
// Generate receiver account
|
|
cryptonote::account_base receiver;
|
|
receiver.generate();
|
|
|
|
// Generate random transfer amount
|
|
uint64_t transfer_amount = provider.ConsumeIntegralInRange<uint64_t>(10000, 1000000000);
|
|
uint64_t input_amount = transfer_amount + 10000;
|
|
|
|
// Prepare source entry
|
|
cryptonote::tx_source_entry src{};
|
|
src.amount = input_amount;
|
|
src.rct = false;
|
|
src.real_output = 0;
|
|
src.real_output_in_tx_index = 0;
|
|
|
|
// Derive valid public key
|
|
hw::device &hwdev = sender.get_keys().get_device();
|
|
cryptonote::keypair tx_key = cryptonote::keypair::generate(hwdev);
|
|
crypto::key_derivation derivation;
|
|
if (!crypto::generate_key_derivation(sender.get_keys().m_account_address.m_view_public_key, tx_key.sec, derivation)) {
|
|
continue;
|
|
}
|
|
crypto::public_key out_pub_key;
|
|
if (!crypto::derive_public_key(derivation, 0, sender.get_keys().m_account_address.m_spend_public_key, out_pub_key)) {
|
|
continue;
|
|
}
|
|
src.real_out_tx_key = tx_key.pub;
|
|
|
|
// Configure output entry for source_entry
|
|
for (size_t j = 0; j < 10; ++j) {
|
|
cryptonote::tx_source_entry::output_entry oe;
|
|
oe.first = j;
|
|
crypto::public_key pk;
|
|
|
|
if (j == src.real_output) {
|
|
pk = out_pub_key;
|
|
} else {
|
|
cryptonote::keypair fake = cryptonote::keypair::generate(hwdev);
|
|
pk = fake.pub;
|
|
}
|
|
|
|
crypto::view_tag vtag{};
|
|
vtag.data = static_cast<uint8_t>(provider.ConsumeIntegral<uint8_t>());
|
|
|
|
cryptonote::txout_to_tagged_key tagged{};
|
|
tagged.key = pk;
|
|
tagged.view_tag = vtag;
|
|
|
|
oe.second.dest = *reinterpret_cast<const rct::key*>(&tagged.key);
|
|
oe.second.mask = rct::identity();
|
|
src.outputs.push_back(oe);
|
|
}
|
|
sources.push_back(src);
|
|
|
|
// Prepare destination entry
|
|
cryptonote::tx_destination_entry dst;
|
|
dst.amount = transfer_amount;
|
|
dst.addr = receiver.get_keys().m_account_address;
|
|
dst.is_subaddress = false;
|
|
destinations.push_back(dst);
|
|
|
|
// Construct the transaction
|
|
crypto::secret_key tx_secret_key;
|
|
std::vector<crypto::secret_key> additional_tx_keys;
|
|
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
|
subaddresses[sender.get_keys().m_account_address.m_spend_public_key] = {0, 0};
|
|
std::vector<cryptonote::tx_destination_entry> destinations_copy = destinations;
|
|
|
|
// Construct transaction objects
|
|
bool success = cryptonote::construct_tx_and_get_tx_key(
|
|
sender.get_keys(),
|
|
subaddresses,
|
|
sources,
|
|
destinations_copy,
|
|
boost::none,
|
|
extra,
|
|
tx,
|
|
tx_secret_key,
|
|
additional_tx_keys,
|
|
true,
|
|
{rct::RangeProofPaddedBulletproof, 0},
|
|
true
|
|
);
|
|
if (!success) {
|
|
continue;
|
|
}
|
|
|
|
// Force version and unlock_time
|
|
tx.version = 2;
|
|
tx.unlock_time = 0;
|
|
|
|
// Serialise the transaction
|
|
cryptonote::blobdata tx_blob;
|
|
if (!cryptonote::t_serializable_object_to_blob(tx, tx_blob)) {
|
|
continue;
|
|
}
|
|
|
|
// Add the transaction to cache
|
|
cached_txs.push_back(tx_blob);
|
|
}
|
|
}
|
|
|
|
core.get_blockchain_storage().get_db().batch_start();
|
|
|
|
bool added_block = false;
|
|
for (const auto& blk : cached_blocks) {
|
|
cryptonote::block_verification_context bvc{};
|
|
if (core.get_blockchain_storage().add_new_block(blk, bvc)) {
|
|
added_block = true;
|
|
}
|
|
}
|
|
|
|
for (const auto& tx_blob : cached_txs) {
|
|
cryptonote::tx_verification_context tvc;
|
|
bool accepted = core.handle_incoming_tx(tx_blob, tvc, cryptonote::relay_method::block, true);
|
|
if (accepted || tvc.m_added_to_pool) {
|
|
// Store legit hashes
|
|
cryptonote::transaction tx;
|
|
if (cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx)) {
|
|
cached_tx_hashes.push_back(cryptonote::get_transaction_hash(tx));
|
|
}
|
|
}
|
|
}
|
|
|
|
return added_block;
|
|
}
|