diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 5447584f9..ce0982ec4 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1598,6 +1598,32 @@ namespace cryptonote 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, const uint64_t height, const uint8_t major_version, @@ -1605,12 +1631,10 @@ namespace cryptonote { crypto::hash res; - if (height == 202612) // block 202612 bug workaround - { - static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; - epee::string_tools::hex_to_pod(longhash_202612, res); - } - else if (major_version >= RX_BLOCK_VERSION) // RandomX + if (height == 202612 && get_block_longhash_202612(block_hashing_blob, res)) + return res; + + if (major_version >= RX_BLOCK_VERSION) // RandomX { crypto::rx_slow_hash(seed_hash.data, block_hashing_blob.data(), block_hashing_blob.size(), res.data); } diff --git a/tests/data/blocks/block_202612_mainnet.bin b/tests/data/blocks/block_202612_mainnet.bin new file mode 100644 index 000000000..b2e81a3e2 Binary files /dev/null and b/tests/data/blocks/block_202612_mainnet.bin differ diff --git a/tests/data/blocks/block_202612_stagenet.bin b/tests/data/blocks/block_202612_stagenet.bin new file mode 100644 index 000000000..a282cc762 Binary files /dev/null and b/tests/data/blocks/block_202612_stagenet.bin differ diff --git a/tests/data/blocks/block_202612_testnet.bin b/tests/data/blocks/block_202612_testnet.bin new file mode 100644 index 000000000..f9745978d Binary files /dev/null and b/tests/data/blocks/block_202612_testnet.bin differ diff --git a/tests/unit_tests/cryptonote_format_utils.cpp b/tests/unit_tests/cryptonote_format_utils.cpp index 1b15305f7..55bc14710 100644 --- a/tests/unit_tests/cryptonote_format_utils.cpp +++ b/tests/unit_tests/cryptonote_format_utils.cpp @@ -30,8 +30,17 @@ #include "crypto/generators.h" #include "cryptonote_basic/cryptonote_format_utils.h" +#include "file_io_utils.h" #include "serialization/binary_utils.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) { @@ -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)); } +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) { const std::vector depths{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 63, 64, 127, 128, 16383, 16384};