Files
wownero/tests/fuzz/fuzz_rpc/initialisation.cpp
T
+3 a9145f9d38 FCMP++/Carrot mega commit [9/14/26]
Co-authored-by: jeffro256 <jeffro256@tutanota.com>
Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
Co-authored-by: Lee *!* Clagett <code@leeclagett.com>
Co-authored-by: SyntheticBird45 <someoneelse.is_on.github.rio7x@simplelogin.com>
Co-authored-by: jeetrex17 <jeetrex@tutamail.com>
Co-authored-by: UkoeHB <ukoehb@users.noreply.github.com>
Co-authored-by: MrCyjaneK
Co-authored-by: tobtoht
Co-authored-by: selsta
Co-authored-by: Thomas <thomas.giudici@proton.me>
Co-authored-by: navidR
Co-authored-by: Boog900 <boog900@tutanota.com>
2026-09-17 18:33:24 -07:00

328 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&, std::vector<crypto::hash>&&, 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::transaction tx{};
crypto::hash tx_hash;
if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash))
continue;
cryptonote::tx_verification_context tvc{};
if (!core.handle_incoming_tx(tx_blob, tx, tx_hash, tvc, cryptonote::relay_method::block, true)
&& !tvc.m_added_to_pool)
{
continue;
}
// Store legit hashes
cached_tx_hashes.push_back(tx_hash);
}
return added_block;
}