Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down
198 changes: 198 additions & 0 deletions src/impl/dash/messages.hpp
Original file line number Diff line number Diff line change
@@ -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 <string>

#include <core/message.hpp>
#include <core/message_macro.hpp>
#include <core/netaddress.hpp>
#include <sharechain/share.hpp>

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<addr_record_t>, m_addrs)
)
{
READWRITE(obj.m_addrs);
}
END_MESSAGE()

// message_shares
BEGIN_MESSAGE(shares)
MESSAGE_FIELDS
(
(std::vector<chain::RawShare>, m_shares)
)
{
READWRITE(obj.m_shares);
}
END_MESSAGE()

// message_sharereq
BEGIN_MESSAGE(sharereq)
MESSAGE_FIELDS
(
(uint256, m_id),
(std::vector<uint256>, m_hashes),
(uint64_t, m_parents),
(std::vector<uint256>, 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<chain::RawShare>, m_shares)
)
{
READWRITE(obj.m_id, Using<EnumType<CompactFormat>>(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<uint256>, m_tx_hashes)
)
{
READWRITE(obj.m_tx_hashes);
}
END_MESSAGE()

// message_losing_tx
BEGIN_MESSAGE(losing_tx)
MESSAGE_FIELDS
(
(std::vector<uint256>, m_tx_hashes)
)
{
READWRITE(obj.m_tx_hashes);
}
END_MESSAGE()

// message_forget_tx
BEGIN_MESSAGE(forget_tx)
MESSAGE_FIELDS
(
(std::vector<uint256>, m_tx_hashes)
)
{
READWRITE(obj.m_tx_hashes);
}
END_MESSAGE()

// message_remember_tx
BEGIN_MESSAGE(remember_tx)
MESSAGE_FIELDS
(
(std::vector<uint256>, m_tx_hashes),
(std::vector<coin::MutableTransaction>, 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
9 changes: 9 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
142 changes: 142 additions & 0 deletions test/test_dash_poolnode_messages.cpp
Original file line number Diff line number Diff line change
@@ -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 <gtest/gtest.h>

#include <impl/dash/messages.hpp>

using namespace dash;

namespace {

// Non-destructive byte view of a PackStream (does not advance the read cursor).
static std::vector<unsigned char> bytes_of(PackStream& ps) {
auto sp = ps.get_span();
auto* p = reinterpret_cast<const unsigned char*>(sp.data());
return std::vector<unsigned char>(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<addr_record_t> 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<chain::RawShare> shares{
chain::RawShare(16u, BaseScript(std::vector<unsigned char>{0xde, 0xad, 0xbe, 0xef})),
chain::RawShare(36u, BaseScript(std::vector<unsigned char>{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<unsigned char>{0xde, 0xad, 0xbe, 0xef}));
}

TEST(DashPoolNodeMessages, Message_Sharereq_RoundTrip) {
auto rmsg = message_sharereq::make_raw(
uint256(0xaaull),
std::vector<uint256>{uint256(0x01ull), uint256(0x02ull)},
5ull,
std::vector<uint256>{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<chain::RawShare> shares{
chain::RawShare(16u, BaseScript(std::vector<unsigned char>{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<uint256> 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);
}

Loading