cryptonote: scope 202612 PoW workaround

The block 202612 PoW workaround previously applied to any block at
height 202612. Restrict it to the object hashes of the known historical
mainnet, testnet, and stagenet block hashing blobs so new chains do not
inherit the fixed PoW hash, which would prevent sync or mining past that
height if their difficulty is higher.

Reported-by: DataHoarder
Co-authored-by: jeffro256 <jeffro256@tutanota.com>
This commit is contained in:
selsta
2026-07-03 14:59:41 +02:00
co-authored by jeffro256
parent ee50ba98c9
commit dc8bc028a6
5 changed files with 141 additions and 6 deletions
@@ -1598,6 +1598,32 @@ namespace cryptonote
return get_tx_tree_hash(txs_ids); return get_tx_tree_hash(txs_ids);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
static bool get_block_longhash_202612(const blobdata_ref block_hashing_blob, crypto::hash &res)
{
static constexpr const char longhash_202612[] = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
// Object hashes of get_block_hashing_blob(block). The mainnet value differs
// from the public block id because calculate_block_hash() replaces that id.
static constexpr const char * const block_hashing_blob_ids_202612[] =
{
"426d16cff04c71f8b16340b722dc4010a2dd3831c22041431f772547ba6e331a", // mainnet
"248fde4b96b829c4ddbd00e3f76d35b03d01257898bc1b5578bc9e04b379a676", // testnet
"f3449e658b5f880c4b0e69007ed5d092c9c883ac3a518166fa652d5cc505e7b1", // stagenet
};
crypto::hash block_id;
const blobdata block_hashing_blob_copy(block_hashing_blob.data(), block_hashing_blob.size());
get_object_hash(block_hashing_blob_copy, block_id);
const std::string block_id_hex = string_tools::pod_to_hex(block_id);
for (const char * const expected_block_id: block_hashing_blob_ids_202612)
{
if (block_id_hex == expected_block_id)
{
return epee::string_tools::hex_to_pod(longhash_202612, res);
}
}
return false;
}
//---------------------------------------------------------------
crypto::hash get_block_longhash(const blobdata_ref block_hashing_blob, crypto::hash get_block_longhash(const blobdata_ref block_hashing_blob,
const uint64_t height, const uint64_t height,
const uint8_t major_version, const uint8_t major_version,
@@ -1605,12 +1631,10 @@ namespace cryptonote
{ {
crypto::hash res; crypto::hash res;
if (height == 202612) // block 202612 bug workaround if (height == 202612 && get_block_longhash_202612(block_hashing_blob, res))
{ return res;
static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
epee::string_tools::hex_to_pod(longhash_202612, res); if (major_version >= RX_BLOCK_VERSION) // RandomX
}
else if (major_version >= RX_BLOCK_VERSION) // RandomX
{ {
crypto::rx_slow_hash(seed_hash.data, block_hashing_blob.data(), block_hashing_blob.size(), res.data); crypto::rx_slow_hash(seed_hash.data, block_hashing_blob.data(), block_hashing_blob.size(), res.data);
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -30,8 +30,17 @@
#include "crypto/generators.h" #include "crypto/generators.h"
#include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/cryptonote_format_utils.h"
#include "file_io_utils.h"
#include "serialization/binary_utils.h" #include "serialization/binary_utils.h"
#include "serialization/string.h" #include "serialization/string.h"
#include "string_tools.h"
#include "unit_tests_utils.h"
namespace
{
static constexpr const char EXISTING_BLOCK_POW_HASH_202612[] =
"84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
} //anonymous namespace
TEST(cn_format_utils, add_extra_nonce_to_tx_extra) TEST(cn_format_utils, add_extra_nonce_to_tx_extra)
{ {
@@ -160,6 +169,108 @@ TEST(cn_format_utils, payment_id_tx_extra_nonce_rejects_wrong_tag_or_size)
EXPECT_FALSE(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(wrong_size, parsed_encrypted_payment_id)); EXPECT_FALSE(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(wrong_size, parsed_encrypted_payment_id));
} }
TEST(cn_format_utils, block_longhash_202612_arbitrary_blob)
{
const cryptonote::blobdata blob = "not the historical block 202612 hashing blob";
const crypto::hash pow_hash = cryptonote::get_block_longhash(blob, 202612, 1, crypto::null_hash);
ASSERT_NE(EXISTING_BLOCK_POW_HASH_202612, epee::string_tools::pod_to_hex(pow_hash));
}
TEST(cn_format_utils, block_longhash_202612_mainnet_blob)
{
// load existing mainnet block 202612 from file
const auto block_202612_blob_path = unit_test::data_dir / "blocks" / "block_202612_mainnet.bin";
cryptonote::blobdata block_blob;
ASSERT_TRUE(epee::file_io_utils::load_file_to_string(block_202612_blob_path.string(), block_blob));
// parse and validate existing mainnet block 202612 and check block ID (you can see bbd604d2... on an explorer)
cryptonote::block block;
crypto::hash block_id = crypto::null_hash;
ASSERT_TRUE(cryptonote::parse_and_validate_block_from_blob(block_blob, block, &block_id));
ASSERT_EQ("bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698", epee::string_tools::pod_to_hex(block_id));
// get hashing blob for 202612 and check raw hash (you can see 426d16cf... in cryptonote::get_block_longhash() impl)
const cryptonote::blobdata hashing_blob = cryptonote::get_block_hashing_blob(block);
ASSERT_TRUE(cryptonote::get_object_hash(hashing_blob, block_id));
ASSERT_EQ("426d16cff04c71f8b16340b722dc4010a2dd3831c22041431f772547ba6e331a", epee::string_tools::pod_to_hex(block_id));
// call get_block_longhash() and check that we get the existing PoW hash
const crypto::hash pow_hash = cryptonote::get_block_longhash(hashing_blob, 202612, 1, crypto::null_hash);
ASSERT_EQ(EXISTING_BLOCK_POW_HASH_202612, epee::string_tools::pod_to_hex(pow_hash));
}
TEST(cn_format_utils, block_longhash_202612_testnet_blob)
{
// load existing testnet block 202612 from file
const auto block_202612_blob_path = unit_test::data_dir / "blocks" / "block_202612_testnet.bin";
cryptonote::blobdata block_blob;
ASSERT_TRUE(epee::file_io_utils::load_file_to_string(block_202612_blob_path.string(), block_blob));
// parse and validate existing testnet block 202612 and check block ID (you can see 248fde4b... on an explorer)
cryptonote::block block;
crypto::hash block_id;
ASSERT_TRUE(cryptonote::parse_and_validate_block_from_blob(block_blob, block, block_id));
ASSERT_EQ("248fde4b96b829c4ddbd00e3f76d35b03d01257898bc1b5578bc9e04b379a676", epee::string_tools::pod_to_hex(block_id));
// get hashing blob for 202612 and check raw hash (you can see 248fde4b... in cryptonote::get_block_longhash() impl)
const cryptonote::blobdata hashing_blob = cryptonote::get_block_hashing_blob(block);
ASSERT_TRUE(cryptonote::get_object_hash(hashing_blob, block_id));
ASSERT_EQ("248fde4b96b829c4ddbd00e3f76d35b03d01257898bc1b5578bc9e04b379a676", epee::string_tools::pod_to_hex(block_id));
// call get_block_longhash() and check that we get the existing PoW hash
const crypto::hash pow_hash = cryptonote::get_block_longhash(hashing_blob, 202612, 9, crypto::null_hash);
ASSERT_EQ(EXISTING_BLOCK_POW_HASH_202612, epee::string_tools::pod_to_hex(pow_hash));
}
TEST(cn_format_utils, block_longhash_202612_stagenet_blob)
{
// load existing stagenet block 202612 from file
const auto block_202612_blob_path = unit_test::data_dir / "blocks" / "block_202612_stagenet.bin";
cryptonote::blobdata block_blob;
ASSERT_TRUE(epee::file_io_utils::load_file_to_string(block_202612_blob_path.string(), block_blob));
// parse and validate existing stagenet block 202612 and check block ID (you can see f3449e65... on an explorer)
cryptonote::block block;
crypto::hash block_id;
ASSERT_TRUE(cryptonote::parse_and_validate_block_from_blob(block_blob, block, block_id));
ASSERT_EQ("f3449e658b5f880c4b0e69007ed5d092c9c883ac3a518166fa652d5cc505e7b1", epee::string_tools::pod_to_hex(block_id));
// get hashing blob for 202612 and check raw hash (you can see f3449e65... in cryptonote::get_block_longhash() impl)
const cryptonote::blobdata hashing_blob = cryptonote::get_block_hashing_blob(block);
ASSERT_TRUE(cryptonote::get_object_hash(hashing_blob, block_id));
ASSERT_EQ("f3449e658b5f880c4b0e69007ed5d092c9c883ac3a518166fa652d5cc505e7b1", epee::string_tools::pod_to_hex(block_id));
// call get_block_longhash() and check that we get the existing PoW hash
const crypto::hash pow_hash = cryptonote::get_block_longhash(hashing_blob, 202612, 9, crypto::null_hash);
ASSERT_EQ(EXISTING_BLOCK_POW_HASH_202612, epee::string_tools::pod_to_hex(pow_hash));
}
TEST(cn_format_utils, block_longhash_reconstruct_existing_202612)
{
// load existing mainnet block 202612 from file
const auto block_202612_blob_path = unit_test::data_dir / "blocks" / "block_202612_mainnet.bin";
cryptonote::blobdata block_blob;
ASSERT_TRUE(epee::file_io_utils::load_file_to_string(block_202612_blob_path.string(), block_blob));
// parse and validate existing mainnet block 202612
cryptonote::block block;
ASSERT_TRUE(cryptonote::parse_and_validate_block_from_blob(block_blob, block));
ASSERT_EQ(513, block.tx_hashes.size());
// modify block and hashing blob s.t. it matches buggy behavior of tree hash before counting fix
block.tx_hashes.pop_back();
block.tx_hashes.pop_back();
cryptonote::blobdata hashing_blob = cryptonote::get_block_hashing_blob(block);
ASSERT_EQ(77, hashing_blob.size());
hashing_blob.at(75) = (char) -126; // mangle total tx count from 512 to 514
// check that we can reconstruct the existing PoW hash
crypto::hash pow_hash;
crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), pow_hash, /*variant=*/0, /*height=*/202612);
ASSERT_EQ(EXISTING_BLOCK_POW_HASH_202612, epee::string_tools::pod_to_hex(pow_hash));
}
TEST(cn_format_utils, add_mm_merkle_root_to_tx_extra) TEST(cn_format_utils, add_mm_merkle_root_to_tx_extra)
{ {
const std::vector<std::uint64_t> depths{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 63, 64, 127, 128, 16383, 16384}; const std::vector<std::uint64_t> depths{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 63, 64, 127, 128, 16383, 16384};