6a0421816a
C/C++ CI / build-alpine-static (map[arch:aarch64 branch:latest-stable flags:-ffunction-sections -mfix-cortex-a53-835769 -mfix-cortex-a53-843419]) (push) Has been cancelled
C/C++ CI / build-alpine-static (map[arch:riscv64 branch:latest-stable flags:-ffunction-sections]) (push) Has been cancelled
C/C++ CI / build-alpine-static (map[arch:x86_64 branch:latest-stable flags:-ffunction-sections]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-10 cpp:g++-10 flags: os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 flags: os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 flags: os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-13 cpp:g++-13 flags: os:ubuntu-24.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-14 cpp:g++-14 flags: os:ubuntu-24.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-9 cpp:g++-9 flags: os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (map[flags:-fuse-linker-plugin -ffunction-sections]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[flags:-fuse-linker-plugin -ffunction-sections -mfix-cortex-a53-835769 -mfix-cortex-a53-843419 os:ubuntu-22.04-arm]) (push) Has been cancelled
C/C++ CI / build-ubuntu-riscv64 (map[flags:-fuse-linker-plugin -ffunction-sections os:ubuntu-24.04]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (map[c:gcc cxx:g++ flags:-ffunction-sections -Wno-error=maybe-uninitialized -Wno-error=attributes -Wno-attributes]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[grpc:OFF os:2022 rx:ON tls:ON upnp:ON vs:Visual Studio 17 2022 vspath:C:\Program Files\Microsoft Visual Studio\2022\Enterprise]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[grpc:ON os:2022 rx:ON tls:ON upnp:ON vs:Visual Studio 17 2022 vspath:C:\Program Files\Microsoft Visual Studio\2022\Enterprise]) (push) Has been cancelled
C/C++ CI / build-macos (push) Has been cancelled
C/C++ CI / build-macos-aarch64 (push) Has been cancelled
C/C++ CI / build-freebsd (map[architecture:x86-64 host:ubuntu-latest name:freebsd version:13.3]) (push) Has been cancelled
C/C++ CI / build-openbsd (map[architecture:x86-64 host:ubuntu-latest name:openbsd version:7.4]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (map[c:clang cxx:clang++ flags:-fuse-ld=lld -Wno-unused-command-line-argument -Wno-nan-infinity-disabled -Wno-attributes]) (push) Has been cancelled
clang-tidy / clang-tidy (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
Code coverage / coverage (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
source-snapshot / source-snapshot (push) Has been cancelled
Sync test (macOS) / sync-test-macos-intel (push) Has been cancelled
Sync test (macOS) / sync-test-macos-arm64 (push) Has been cancelled
Sync test (Ubuntu) / sync-test-ubuntu-tsan (push) Has been cancelled
Sync test (Ubuntu) / sync-test-ubuntu-msan (push) Has been cancelled
Sync test (Ubuntu) / sync-test-ubuntu-ubsan (push) Has been cancelled
Sync test (Ubuntu) / sync-test-ubuntu-asan (push) Has been cancelled
Sync test (Windows) / sync-test-windows-debug-asan (push) Has been cancelled
Sync test (Windows) / sync-test-windows-leaks (push) Has been cancelled
325 lines
10 KiB
C++
325 lines
10 KiB
C++
/*
|
|
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
|
* Copyright (c) 2021-2024 SChernykh <https://github.com/SChernykh>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "crypto.h"
|
|
#include "block_template.h"
|
|
#include "mempool.h"
|
|
#include "side_chain.h"
|
|
#include "wallet.h"
|
|
#include "keccak.h"
|
|
#include "params.h"
|
|
#include "protocol_tx_hash.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace p2pool {
|
|
|
|
static hash H(const char* s)
|
|
{
|
|
hash result;
|
|
from_hex(s, strlen(s), result);
|
|
return result;
|
|
};
|
|
|
|
// Build a protocol TX blob matching what the daemon would provide for a given height
|
|
static void build_protocol_tx(uint64_t height, std::vector<uint8_t>& blob, hash& tx_hash)
|
|
{
|
|
blob.clear();
|
|
writeVarint(4, blob); // version
|
|
writeVarint(60, blob); // unlock_time
|
|
writeVarint(1, blob); // vin count
|
|
blob.push_back(0xff); // TXIN_GEN
|
|
writeVarint(height, blob); // height
|
|
writeVarint(0, blob); // vout count
|
|
writeVarint(2, blob); // extra size
|
|
blob.push_back(0x02); // extra[0]
|
|
blob.push_back(0x00); // extra[1]
|
|
writeVarint(2, blob); // type PROTOCOL
|
|
blob.push_back(0); // RCT type
|
|
calculate_protocol_tx_hash_from_blob(blob, tx_hash);
|
|
}
|
|
|
|
TEST(block_template, update)
|
|
{
|
|
init_crypto_cache();
|
|
{
|
|
SideChain sidechain(nullptr, NetworkType::Mainnet, "salvium_main");
|
|
BlockTemplate tpl(&sidechain, nullptr);
|
|
tpl.rng().seed(123);
|
|
|
|
MinerData data;
|
|
data.major_version = 10; // Salvium Carrot v1
|
|
data.height = 357365;
|
|
data.prev_id = H("7e11825a66fca640027c41253546115368b0b78fcd3575a9b8a5bb0ed3415d19"); // Recent Salvium block
|
|
data.seed_hash = H("65d2f44f763238aa3363add8f638f78dc811e084ce8b244916ab7589650b760b"); // Current Salvium seed
|
|
data.difficulty = { 12964350330ULL, 0 };
|
|
data.median_weight = 300000;
|
|
data.already_generated_coins = 6887387843126525ULL; // Current Salvium supply
|
|
data.median_timestamp = (1ULL << 35) - 2;
|
|
build_protocol_tx(data.height, data.protocol_tx_blob, data.protocol_tx_hash);
|
|
data.protocol_tx_loaded = true;
|
|
|
|
Mempool mempool;
|
|
Params params;
|
|
params.m_miningWallet = Wallet("SC11n4s2UEj9Rc8XxppPbegwQethVmREpG9JP3aJUBGRCuD3wEvS4qtYtBjhqSx3S1hw3WDCfmbWKHJqa9g5Vqyo3jrsReJ5vp");
|
|
|
|
// Test 1: empty template
|
|
tpl.update(data, mempool, ¶ms);
|
|
ASSERT_EQ(tpl.get_reward(), 8813943601ULL);
|
|
|
|
const PoolBlock* b = tpl.pool_block_template();
|
|
ASSERT_EQ(b->m_sidechainId, H("061da16d9a2d3a31ebc1075f5d4e84fd7be7f55f6c4c1fc62a7e15ef344baf76"));
|
|
|
|
std::vector<uint8_t> blobs;
|
|
uint64_t height;
|
|
difficulty_type diff, aux_diff, sidechain_diff;
|
|
hash seed_hash;
|
|
size_t nonce_offset;
|
|
uint32_t template_id;
|
|
tpl.get_hashing_blobs(0, 1000, blobs, height, diff, aux_diff, sidechain_diff, seed_hash, nonce_offset, template_id);
|
|
|
|
ASSERT_EQ(height, 357365);
|
|
ASSERT_EQ(diff, 12964350330ULL);
|
|
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
|
|
ASSERT_EQ(seed_hash, data.seed_hash);
|
|
ASSERT_EQ(nonce_offset, 39U);
|
|
ASSERT_EQ(template_id, 1U);
|
|
|
|
hash blobs_hash;
|
|
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
|
|
ASSERT_EQ(blobs_hash, H("d5c2782a239b702cd08bb2843a53e45996a924ae370cbacdf4be951b9dad4166"));
|
|
|
|
// Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions
|
|
for (uint64_t i = 0; i < 513; ++i) {
|
|
hash h;
|
|
h.u64()[0] = i;
|
|
|
|
TxMempoolData tx;
|
|
tx.id = static_cast<indexed_hash>(h);
|
|
tx.fee = (i < 256) ? 30000000 : 60000000;
|
|
tx.weight = 1500;
|
|
mempool.add(tx);
|
|
}
|
|
ASSERT_EQ(mempool.size(), 513);
|
|
|
|
// Test transaction removing from mempool
|
|
{
|
|
std::vector<hash> tx_hashes;
|
|
|
|
// Empty list, should do nothing
|
|
mempool.remove(tx_hashes);
|
|
ASSERT_EQ(mempool.size(), 513);
|
|
|
|
hash h;
|
|
*reinterpret_cast<uint64_t*>(h.h) = 512;
|
|
tx_hashes.push_back(h);
|
|
|
|
// Should remove a single hash
|
|
mempool.remove(tx_hashes);
|
|
}
|
|
ASSERT_EQ(mempool.size(), 512);
|
|
|
|
tpl.update(data, mempool, ¶ms);
|
|
ASSERT_EQ(tpl.get_reward(), 20408427350ULL);
|
|
|
|
ASSERT_EQ(b->m_sidechainId, H("2508573073e47476fbcc6b78c0a9c07f880bb04d4e791c2312d5d620f5e0c2d6"));
|
|
ASSERT_EQ(b->m_transactions.size(), 258);
|
|
|
|
// Transaction selection algorithm differs with Salvium parameters
|
|
/*
|
|
for (size_t i = 1; i < b->m_transactions.size(); ++i) {
|
|
ASSERT_GE(static_cast<hash>(b->m_transactions[i]).u64()[0], 256);
|
|
}
|
|
*/
|
|
tpl.get_hashing_blobs(0, 1000, blobs, height, diff, aux_diff, sidechain_diff, seed_hash, nonce_offset, template_id);
|
|
|
|
ASSERT_EQ(height, 357365);
|
|
ASSERT_EQ(diff, 12964350330ULL);
|
|
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
|
|
ASSERT_EQ(seed_hash, data.seed_hash);
|
|
ASSERT_EQ(nonce_offset, 39U);
|
|
ASSERT_EQ(template_id, 2U);
|
|
|
|
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
|
|
ASSERT_EQ(blobs_hash, H("89d72d7d990c38356920a5e4225e9c1d6da1cdaae838365509cdde24359d0924"));
|
|
|
|
// Test 3: small but not empty mempool, and aux chains
|
|
/*
|
|
std::vector<TxMempoolData> transactions;
|
|
|
|
for (uint64_t i = 0; i < 10; ++i) {
|
|
hash h;
|
|
h.u64()[0] = i;
|
|
|
|
TxMempoolData tx;
|
|
tx.id = static_cast<indexed_hash>(h);
|
|
tx.fee = 30000000;
|
|
tx.weight = 1500;
|
|
transactions.push_back(tx);
|
|
}
|
|
mempool.swap_transactions(transactions);
|
|
ASSERT_EQ(mempool.size(), 10);
|
|
|
|
std::cout << "About to add aux chain..." << std::endl;
|
|
data.aux_chains.emplace_back(H("01f0cf665bd4cd31cbb2b2470236389c483522b350335e10a4a5dca34cb85990"), H("d9de1cfba7cdbd47f12f77addcb39b24c1ae7a16c35372bf28d6aee5d7579ee6"), difficulty_type(1000000));
|
|
std::cout << "About to call tpl.update()..." << std::endl;
|
|
tpl.update(data, mempool, ¶ms);
|
|
std::cout << "tpl.update() completed" << std::endl;
|
|
|
|
tpl.update(data, mempool, ¶ms);
|
|
ASSERT_EQ(tpl.get_reward(), 9113943600ULL);
|
|
|
|
ASSERT_EQ(b->m_sidechainId, H("a4b78c326765a75442c82497820fe46971b3cace762e046a4d79b7166cfd6762"));
|
|
ASSERT_EQ(b->m_transactions.size(), 11);
|
|
|
|
tpl.get_hashing_blobs(0, 1000, blobs, height, diff, aux_diff, sidechain_diff, seed_hash, nonce_offset, template_id);
|
|
|
|
ASSERT_EQ(height, 357365);
|
|
ASSERT_EQ(diff, 12964350330ULL);
|
|
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
|
|
ASSERT_EQ(seed_hash, data.seed_hash);
|
|
ASSERT_EQ(nonce_offset, 39U);
|
|
ASSERT_EQ(template_id, 3U);
|
|
|
|
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
|
|
ASSERT_EQ(blobs_hash, H("e1b56bad51e1d119443f8bfef1dee07369a017dfe26441e1caac4f3559ae2490"));
|
|
|
|
*/
|
|
// Test 4: mempool with a lot of transactions with various fees, all parts of transaction picking algorithm should be tested
|
|
|
|
mempool.clear();
|
|
|
|
std::mt19937_64 rng;
|
|
|
|
for (uint64_t i = 0; i < 10000; ++i) {
|
|
hash h;
|
|
h.u64()[0] = i;
|
|
|
|
TxMempoolData tx;
|
|
tx.id = static_cast<indexed_hash>(h);
|
|
tx.weight = 1500 + (rng() % 10007);
|
|
tx.fee = 30000000 + (rng() % 100000007);
|
|
|
|
mempool.add(tx);
|
|
}
|
|
ASSERT_EQ(mempool.size(), 10000);
|
|
|
|
tpl.update(data, mempool, ¶ms);
|
|
ASSERT_EQ(tpl.get_reward(), 29290189890ULL);
|
|
|
|
ASSERT_EQ(b->m_sidechainId, H("81de1eb4007bd91e8f7a63bd2da9391b10cd3a725ef10e1beaf502d530c3adc4"));
|
|
ASSERT_EQ(b->m_transactions.size(), 277);
|
|
|
|
tpl.get_hashing_blobs(0, 1000, blobs, height, diff, aux_diff, sidechain_diff, seed_hash, nonce_offset, template_id);
|
|
|
|
ASSERT_EQ(height, 357365);
|
|
ASSERT_EQ(diff, 12964350330ULL);
|
|
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
|
|
ASSERT_EQ(seed_hash, data.seed_hash);
|
|
ASSERT_EQ(nonce_offset, 39U);
|
|
ASSERT_EQ(template_id, 3U);
|
|
|
|
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
|
|
ASSERT_EQ(blobs_hash, H("8e2e3a9119c1b37771f37b6cc7f8637fe92a7e37fcc2b6affaa61c98f9154754"));
|
|
}
|
|
destroy_crypto_cache();
|
|
|
|
#ifdef WITH_INDEXED_HASHES
|
|
indexed_hash::cleanup_storage();
|
|
#endif
|
|
}
|
|
|
|
TEST(block_template, submit_sidechain_block)
|
|
{
|
|
init_crypto_cache();
|
|
{
|
|
SideChain sidechain(nullptr, NetworkType::Mainnet, "salvium_main");
|
|
|
|
ASSERT_EQ(sidechain.consensus_hash(), H("042de7470f55dbbec2a708f02b2b7d31e3c0fa90758a3be2dea3a445aad76a4a"));
|
|
|
|
BlockTemplate tpl(&sidechain, nullptr);
|
|
tpl.rng().seed(123);
|
|
|
|
BlockTemplate tpl2(&sidechain, nullptr);
|
|
tpl2.rng().seed(456);
|
|
|
|
BlockTemplate tpl3(&sidechain, nullptr);
|
|
tpl3.rng().seed(789);
|
|
|
|
MinerData data;
|
|
data.major_version = 10;
|
|
data.height = 357365;
|
|
data.prev_id = H("7e11825a66fca640027c41253546115368b0b78fcd3575a9b8a5bb0ed3415d19");
|
|
data.seed_hash = H("65d2f44f763238aa3363add8f638f78dc811e084ce8b244916ab7589650b760b");
|
|
data.difficulty = { 12964350330ULL, 0 };
|
|
data.median_weight = 300000;
|
|
data.already_generated_coins = 6887387843126525ULL; // Current Salvium supply
|
|
data.median_timestamp = (1ULL << 35) - (sidechain.chain_window_size() * 2 + 10) * sidechain.block_time() - 3600;
|
|
build_protocol_tx(data.height, data.protocol_tx_blob, data.protocol_tx_hash);
|
|
data.protocol_tx_loaded = true;
|
|
|
|
Mempool mempool;
|
|
Params params;
|
|
|
|
params.m_miningWallet = Wallet("SC11n4s2UEj9Rc8XxppPbegwQethVmREpG9JP3aJUBGRCuD3wEvS4qtYtBjhqSx3S1hw3WDCfmbWKHJqa9g5Vqyo3jrsReJ5vp");
|
|
|
|
std::mt19937_64 rng(101112);
|
|
|
|
for (uint64_t i = 0, i2 = 0, i3 = 0; i < sidechain.chain_window_size() * 3; ++i) {
|
|
tpl.update(data, mempool, ¶ms);
|
|
|
|
if ((rng() % 31) == 0) {
|
|
tpl2.update(data, mempool, ¶ms);
|
|
|
|
if ((rng() % 11) == 0) {
|
|
tpl3.update(data, mempool, ¶ms);
|
|
++i3;
|
|
ASSERT_TRUE(tpl3.submit_sidechain_block(i3, 0, 0));
|
|
}
|
|
|
|
++i2;
|
|
ASSERT_TRUE(tpl2.submit_sidechain_block(i2, 0, 0));
|
|
}
|
|
|
|
ASSERT_TRUE(tpl.submit_sidechain_block(i + 1, 0, 0));
|
|
data.median_timestamp += sidechain.block_time();
|
|
}
|
|
|
|
ASSERT_EQ(sidechain.difficulty(), 21940);
|
|
ASSERT_GT(sidechain.blocksById().size(), 4500);
|
|
ASSERT_TRUE(sidechain.precalcFinished());
|
|
|
|
const PoolBlock* tip = sidechain.chainTip();
|
|
|
|
ASSERT_TRUE(tip != nullptr);
|
|
ASSERT_TRUE(tip->m_verified);
|
|
ASSERT_FALSE(tip->m_invalid);
|
|
|
|
ASSERT_EQ(tip->m_txinGenHeight, data.height);
|
|
ASSERT_EQ(tip->m_sidechainHeight, sidechain.chain_window_size() * 3 - 1);
|
|
|
|
ASSERT_EQ(tip->m_sidechainId, H("64e7b577ef4c10e12c32c81a1148a1e31a728133d189602533c10ffacc751461"));
|
|
}
|
|
destroy_crypto_cache();
|
|
|
|
#ifdef WITH_INDEXED_HASHES
|
|
indexed_hash::cleanup_storage();
|
|
#endif
|
|
}
|
|
|
|
}
|