diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b684fb2aa..cb6e92776 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,7 +88,7 @@ jobs: test_threading test_weights \ test_header_chain test_mempool test_template_builder \ test_doge_chain test_compact_blocks test_dash_x11_kat \ - test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_cb_payee \ + test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_cb_payee test_dash_poolnode_messages \ test_multiaddress_pplns test_pplns_stress \ test_hash_link test_decay_pplns \ test_pplns_consensus \ @@ -238,7 +238,7 @@ jobs: test_threading test_weights \ test_header_chain test_mempool test_template_builder \ test_doge_chain test_compact_blocks test_dash_x11_kat \ - test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_cb_payee \ + test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_cb_payee test_dash_poolnode_messages \ test_hash_link test_decay_pplns \ test_pplns_consensus \ test_v36_script_sorting test_v36_cross_impl_refhash \ diff --git a/src/impl/dash/messages.hpp b/src/impl/dash/messages.hpp new file mode 100644 index 000000000..8b09dbd3b --- /dev/null +++ b/src/impl/dash/messages.hpp @@ -0,0 +1,198 @@ +#pragma once + +// V36 Dash-X11 sharechain pool wire-message layer — namespace-only divergence +// from src/impl/ltc/messages.hpp; message envelope is byte-parity with the +// shared p2pool family layer. Share VERSION/transition (16->36) lives inside +// chain::RawShare, not the message frame, so the pool-node wire contract is +// coin-uniform. Mirrors btc/dgb (match the analog; do not invent a contract). + +#include "coin/block.hpp" +#include "coin/transaction.hpp" + +#include + +#include +#include +#include +#include + +namespace dash +{ + +// message_version +BEGIN_MESSAGE(version) + MESSAGE_FIELDS + ( + (uint32_t, m_version), + (uint64_t, m_services), + (addr_t , m_addr_to), + (addr_t, m_addr_from), + (uint64_t, m_nonce), + (std::string, m_subversion), + (uint32_t, m_mode), //# always 1 for legacy compatibility + (uint256, m_best_share) + ) + { + READWRITE(obj.m_version, obj.m_services, obj.m_addr_to, obj.m_addr_from, obj.m_nonce, obj.m_subversion, obj.m_mode, obj.m_best_share); + } +END_MESSAGE() + +// message_ping +BEGIN_MESSAGE(ping) + WITHOUT_MESSAGE_FIELDS() { } +END_MESSAGE() + +// message_addrme +BEGIN_MESSAGE(addrme) + MESSAGE_FIELDS + ( + (uint16_t, m_port) + ) + { + READWRITE(obj.m_port); + } +END_MESSAGE() + +// message_getaddrs +BEGIN_MESSAGE(getaddrs) + MESSAGE_FIELDS + ( + (uint32_t, m_count) + ) + { + READWRITE(obj.m_count); + } +END_MESSAGE() + +// message_addrs +BEGIN_MESSAGE(addrs) + MESSAGE_FIELDS + ( + (std::vector, m_addrs) + ) + { + READWRITE(obj.m_addrs); + } +END_MESSAGE() + +// message_shares +BEGIN_MESSAGE(shares) + MESSAGE_FIELDS + ( + (std::vector, m_shares) + ) + { + READWRITE(obj.m_shares); + } +END_MESSAGE() + +// message_sharereq +BEGIN_MESSAGE(sharereq) + MESSAGE_FIELDS + ( + (uint256, m_id), + (std::vector, m_hashes), + (uint64_t, m_parents), + (std::vector, m_stops) + ) + { + READWRITE(obj.m_id, obj.m_hashes, VarInt(obj.m_parents), obj.m_stops); + } +END_MESSAGE() + +enum ShareReplyResult +{ + good = 0, + too_long = 1, + unk2 = 2, + unk3 = 3, + unk4 = 4, + unk5 = 5, + unk6 = 6 +}; + +// message_sharereply +BEGIN_MESSAGE(sharereply) + MESSAGE_FIELDS + ( + (uint256, m_id), + (ShareReplyResult, m_result), + (std::vector, m_shares) + ) + { + READWRITE(obj.m_id, Using>(obj.m_result), obj.m_shares); + } +END_MESSAGE() + +// message_bestblock +BEGIN_MESSAGE(bestblock) + MESSAGE_FIELDS + ( + (coin::BlockHeaderType, m_header) + ) + { + READWRITE(obj.m_header); + } +END_MESSAGE() + +// message_have_tx +BEGIN_MESSAGE(have_tx) + MESSAGE_FIELDS + ( + (std::vector, m_tx_hashes) + ) + { + READWRITE(obj.m_tx_hashes); + } +END_MESSAGE() + +// message_losing_tx +BEGIN_MESSAGE(losing_tx) + MESSAGE_FIELDS + ( + (std::vector, m_tx_hashes) + ) + { + READWRITE(obj.m_tx_hashes); + } +END_MESSAGE() + +// message_forget_tx +BEGIN_MESSAGE(forget_tx) + MESSAGE_FIELDS + ( + (std::vector, m_tx_hashes) + ) + { + READWRITE(obj.m_tx_hashes); + } +END_MESSAGE() + +// message_remember_tx +BEGIN_MESSAGE(remember_tx) + MESSAGE_FIELDS + ( + (std::vector, m_tx_hashes), + (std::vector, m_txs) + ) + { + READWRITE(obj.m_tx_hashes, coin::TX_WITH_WITNESS(obj.m_txs)); + } +END_MESSAGE() + +using Handler = MessageHandler< + message_ping, + message_addrme, + message_getaddrs, + message_addrs, + message_shares, + message_sharereq, + message_sharereply, + message_bestblock, + message_have_tx, + message_losing_tx, + message_forget_tx, + message_remember_tx +>; + +} // namespace dash diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5409ea3b9..9e288d9b8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -446,6 +446,15 @@ if (BUILD_TESTING AND GTest_FOUND) ) target_link_libraries(test_dash_p2p_messages PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39) gtest_add_tests(test_dash_p2p_messages "" AUTO) + add_executable(test_dash_poolnode_messages test_dash_poolnode_messages.cpp) + target_link_libraries(test_dash_poolnode_messages PRIVATE + GTest::gtest_main GTest::gtest + dash_x11 core + nlohmann_json::nlohmann_json + ${Boost_LIBRARIES} + ) + target_link_libraries(test_dash_poolnode_messages PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39) + gtest_add_tests(test_dash_poolnode_messages "" AUTO) add_executable(test_dash_p2p_connection test_dash_p2p_connection.cpp) target_link_libraries(test_dash_p2p_connection PRIVATE diff --git a/test/test_dash_poolnode_messages.cpp b/test/test_dash_poolnode_messages.cpp new file mode 100644 index 000000000..8d34f0464 --- /dev/null +++ b/test/test_dash_poolnode_messages.cpp @@ -0,0 +1,142 @@ +// KAT for the Dash sharechain pool-node wire layer (src/impl/dash/messages.hpp). +// Proves each pool-node BEGIN_MESSAGE round-trips byte-for-byte and the type-list +// instantiates — the btc/dgb/ltc analog ported namespace-only to dash. +// Mirrors test_dash_p2p_messages.cpp idiom. +#include + +#include + +using namespace dash; + +namespace { + +// Non-destructive byte view of a PackStream (does not advance the read cursor). +static std::vector bytes_of(PackStream& ps) { + auto sp = ps.get_span(); + auto* p = reinterpret_cast(sp.data()); + return std::vector(p, p + sp.size()); +} + +static addr_t make_addr(uint16_t port) { + return addr_t(1u, NetService("192.168.1.1", port)); +} + +} // namespace + +TEST(DashPoolNodeMessages, Message_Version_RoundTrip) { + auto rmsg = message_version::make_raw( + 70238u, 1u, make_addr(9999), make_addr(8888), uint64_t(0xABCDEF), + std::string("c2pool-dash"), 1u, uint256(0x11ull)); + EXPECT_EQ(rmsg->m_command, "version"); + + auto parsed = message_version::make(rmsg->m_data); + EXPECT_EQ(parsed->m_version, 70238u); + EXPECT_EQ(parsed->m_services, 1u); + EXPECT_EQ(parsed->m_nonce, uint64_t(0xABCDEF)); + EXPECT_EQ(parsed->m_subversion, std::string("c2pool-dash")); + EXPECT_EQ(parsed->m_mode, 1u); + EXPECT_EQ(parsed->m_addr_to.m_endpoint.port(), 9999); + EXPECT_EQ(parsed->m_addr_from.m_endpoint.port(), 8888); + EXPECT_EQ(parsed->m_best_share, uint256(0x11ull)); +} + +TEST(DashPoolNodeMessages, Message_Ping_Empty) { + auto rmsg = message_ping::make_raw(); + EXPECT_EQ(rmsg->m_command, "ping"); + EXPECT_EQ(bytes_of(rmsg->m_data).size(), 0u); +} + +TEST(DashPoolNodeMessages, Message_Addrme_LayoutPinned) { + auto rmsg = message_addrme::make_raw(uint16_t(0x1234)); + EXPECT_EQ(rmsg->m_command, "addrme"); + // uint16_t port, little-endian: 2 bytes 0x34 0x12 + auto b = bytes_of(rmsg->m_data); + ASSERT_EQ(b.size(), 2u); + EXPECT_EQ(b[0], 0x34); + EXPECT_EQ(b[1], 0x12); + + auto parsed = message_addrme::make(rmsg->m_data); + EXPECT_EQ(parsed->m_port, 0x1234); +} + +TEST(DashPoolNodeMessages, Message_Getaddrs_RoundTrip) { + auto rmsg = message_getaddrs::make_raw(42u); + EXPECT_EQ(rmsg->m_command, "getaddrs"); + auto parsed = message_getaddrs::make(rmsg->m_data); + EXPECT_EQ(parsed->m_count, 42u); +} + +TEST(DashPoolNodeMessages, Message_Addrs_RoundTrip) { + std::vector addrs{ + addr_record_t(make_addr(19999), 1710000000ull), + addr_record_t(make_addr(29999), 1710000001ull), + }; + auto rmsg = message_addrs::make_raw(addrs); + EXPECT_EQ(rmsg->m_command, "addrs"); + auto parsed = message_addrs::make(rmsg->m_data); + ASSERT_EQ(parsed->m_addrs.size(), 2u); + EXPECT_EQ(parsed->m_addrs[0].m_endpoint.port(), 19999); + EXPECT_EQ(parsed->m_addrs[0].m_timestamp, 1710000000ull); + EXPECT_EQ(parsed->m_addrs[1].m_endpoint.port(), 29999); +} + +TEST(DashPoolNodeMessages, Message_Shares_RoundTrip_V16) { + // Dash sharechain carries v16 shares in the envelope (transition 16->36). + std::vector shares{ + chain::RawShare(16u, BaseScript(std::vector{0xde, 0xad, 0xbe, 0xef})), + chain::RawShare(36u, BaseScript(std::vector{0x01, 0x02})), + }; + auto rmsg = message_shares::make_raw(shares); + EXPECT_EQ(rmsg->m_command, "shares"); + auto parsed = message_shares::make(rmsg->m_data); + ASSERT_EQ(parsed->m_shares.size(), 2u); + EXPECT_EQ(parsed->m_shares[0].type, 16u); + EXPECT_EQ(parsed->m_shares[1].type, 36u); + EXPECT_EQ(parsed->m_shares[0].contents.m_data, + (std::vector{0xde, 0xad, 0xbe, 0xef})); +} + +TEST(DashPoolNodeMessages, Message_Sharereq_RoundTrip) { + auto rmsg = message_sharereq::make_raw( + uint256(0xaaull), + std::vector{uint256(0x01ull), uint256(0x02ull)}, + 5ull, + std::vector{uint256(0xffull)}); + EXPECT_EQ(rmsg->m_command, "sharereq"); + auto parsed = message_sharereq::make(rmsg->m_data); + EXPECT_EQ(parsed->m_id, uint256(0xaaull)); + ASSERT_EQ(parsed->m_hashes.size(), 2u); + EXPECT_EQ(parsed->m_parents, 5ull); + ASSERT_EQ(parsed->m_stops.size(), 1u); + EXPECT_EQ(parsed->m_stops[0], uint256(0xffull)); +} + +TEST(DashPoolNodeMessages, Message_Sharereply_EnumResult) { + std::vector shares{ + chain::RawShare(16u, BaseScript(std::vector{0x42})), + }; + auto rmsg = message_sharereply::make_raw(uint256(0xccull), good, shares); + EXPECT_EQ(rmsg->m_command, "sharereply"); + auto parsed = message_sharereply::make(rmsg->m_data); + EXPECT_EQ(parsed->m_id, uint256(0xccull)); + EXPECT_EQ(parsed->m_result, good); + ASSERT_EQ(parsed->m_shares.size(), 1u); +} + +TEST(DashPoolNodeMessages, Message_HaveTx_RoundTrip) { + std::vector hashes{uint256(0x07ull), uint256(0x08ull)}; + auto rmsg = message_have_tx::make_raw(hashes); + EXPECT_EQ(rmsg->m_command, "have_tx"); + auto parsed = message_have_tx::make(rmsg->m_data); + ASSERT_EQ(parsed->m_tx_hashes.size(), 2u); + EXPECT_EQ(parsed->m_tx_hashes[1], uint256(0x08ull)); +} + +TEST(DashPoolNodeMessages, Handler_TypeList_Compiles) { + // The pool-node dispatch type-list must instantiate (variadic pack valid). + // Type-only check: avoids ODR-using MessageHandler::m_handlers static + // storage, which is defined out-of-line in the (not-yet-landed) node.cpp. + using HandlerResult = Handler::result_t; + EXPECT_GT(sizeof(HandlerResult), 0u); +} +