Skip to content

Commit 4e8a7bc

Browse files
committed
dash: add sharechain pool-node wire-message layer (S8 pool-node leaf 2)
DASH lacked the p2pool sharechain pool-node wire layer that btc/dgb/ltc all carry. This is leaf 2 (after peer #580): the message envelope itself. src/impl/dash/messages.hpp — namespace dash port of the shared p2pool-family messages layer (src/impl/ltc/messages.hpp): version/ping/addrme/getaddrs/ addrs/shares/sharereq/sharereply/have_tx BEGIN_MESSAGE definitions + the pool-node dispatch type-list. The envelope is byte-parity with the 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 the analog; no new contract invented. test/test_dash_poolnode_messages.cpp — 10 KATs: per-message pack/make round-trips (version field-by-field, ping empty, addrme LE-layout pin, getaddrs, addrs, v16+v36 shares envelope, sharereq, sharereply enum, have_tx) + Handler type-list instantiation. 10/10 green, non-hollow. Strictly fenced to src/impl/dash/ + additive test wiring (CMakeLists add_executable/link + build.yml allowlist x2); no bitcoin_family/src/core/ other-coin touch. peer.hpp coverage stays in #580 (test_dash_peer).
1 parent 414cf41 commit 4e8a7bc

4 files changed

Lines changed: 351 additions & 2 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282
test_threading test_weights \
8383
test_header_chain test_mempool test_template_builder \
8484
test_doge_chain test_compact_blocks test_dash_x11_kat \
85-
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 \
85+
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_poolnode_messages \
8686
test_multiaddress_pplns test_pplns_stress \
8787
test_hash_link test_decay_pplns \
8888
test_pplns_consensus \
@@ -226,7 +226,7 @@ jobs:
226226
test_threading test_weights \
227227
test_header_chain test_mempool test_template_builder \
228228
test_doge_chain test_compact_blocks test_dash_x11_kat \
229-
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 \
229+
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_poolnode_messages \
230230
test_hash_link test_decay_pplns \
231231
test_pplns_consensus \
232232
test_v36_script_sorting test_v36_cross_impl_refhash \

src/impl/dash/messages.hpp

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#pragma once
2+
3+
// V36 Dash-X11 sharechain pool wire-message layer — namespace-only divergence
4+
// from src/impl/ltc/messages.hpp; message envelope is byte-parity with the
5+
// shared p2pool family layer. Share VERSION/transition (16->36) lives inside
6+
// chain::RawShare, not the message frame, so the pool-node wire contract is
7+
// coin-uniform. Mirrors btc/dgb (match the analog; do not invent a contract).
8+
9+
#include "coin/block.hpp"
10+
#include "coin/transaction.hpp"
11+
12+
#include <string>
13+
14+
#include <core/message.hpp>
15+
#include <core/message_macro.hpp>
16+
#include <core/netaddress.hpp>
17+
#include <sharechain/share.hpp>
18+
19+
namespace dash
20+
{
21+
22+
// message_version
23+
BEGIN_MESSAGE(version)
24+
MESSAGE_FIELDS
25+
(
26+
(uint32_t, m_version),
27+
(uint64_t, m_services),
28+
(addr_t , m_addr_to),
29+
(addr_t, m_addr_from),
30+
(uint64_t, m_nonce),
31+
(std::string, m_subversion),
32+
(uint32_t, m_mode), //# always 1 for legacy compatibility
33+
(uint256, m_best_share)
34+
)
35+
{
36+
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);
37+
}
38+
END_MESSAGE()
39+
40+
// message_ping
41+
BEGIN_MESSAGE(ping)
42+
WITHOUT_MESSAGE_FIELDS() { }
43+
END_MESSAGE()
44+
45+
// message_addrme
46+
BEGIN_MESSAGE(addrme)
47+
MESSAGE_FIELDS
48+
(
49+
(uint16_t, m_port)
50+
)
51+
{
52+
READWRITE(obj.m_port);
53+
}
54+
END_MESSAGE()
55+
56+
// message_getaddrs
57+
BEGIN_MESSAGE(getaddrs)
58+
MESSAGE_FIELDS
59+
(
60+
(uint32_t, m_count)
61+
)
62+
{
63+
READWRITE(obj.m_count);
64+
}
65+
END_MESSAGE()
66+
67+
// message_addrs
68+
BEGIN_MESSAGE(addrs)
69+
MESSAGE_FIELDS
70+
(
71+
(std::vector<addr_record_t>, m_addrs)
72+
)
73+
{
74+
READWRITE(obj.m_addrs);
75+
}
76+
END_MESSAGE()
77+
78+
// message_shares
79+
BEGIN_MESSAGE(shares)
80+
MESSAGE_FIELDS
81+
(
82+
(std::vector<chain::RawShare>, m_shares)
83+
)
84+
{
85+
READWRITE(obj.m_shares);
86+
}
87+
END_MESSAGE()
88+
89+
// message_sharereq
90+
BEGIN_MESSAGE(sharereq)
91+
MESSAGE_FIELDS
92+
(
93+
(uint256, m_id),
94+
(std::vector<uint256>, m_hashes),
95+
(uint64_t, m_parents),
96+
(std::vector<uint256>, m_stops)
97+
)
98+
{
99+
READWRITE(obj.m_id, obj.m_hashes, VarInt(obj.m_parents), obj.m_stops);
100+
}
101+
END_MESSAGE()
102+
103+
enum ShareReplyResult
104+
{
105+
good = 0,
106+
too_long = 1,
107+
unk2 = 2,
108+
unk3 = 3,
109+
unk4 = 4,
110+
unk5 = 5,
111+
unk6 = 6
112+
};
113+
114+
// message_sharereply
115+
BEGIN_MESSAGE(sharereply)
116+
MESSAGE_FIELDS
117+
(
118+
(uint256, m_id),
119+
(ShareReplyResult, m_result),
120+
(std::vector<chain::RawShare>, m_shares)
121+
)
122+
{
123+
READWRITE(obj.m_id, Using<EnumType<CompactFormat>>(obj.m_result), obj.m_shares);
124+
}
125+
END_MESSAGE()
126+
127+
// message_bestblock
128+
BEGIN_MESSAGE(bestblock)
129+
MESSAGE_FIELDS
130+
(
131+
(coin::BlockHeaderType, m_header)
132+
)
133+
{
134+
READWRITE(obj.m_header);
135+
}
136+
END_MESSAGE()
137+
138+
// message_have_tx
139+
BEGIN_MESSAGE(have_tx)
140+
MESSAGE_FIELDS
141+
(
142+
(std::vector<uint256>, m_tx_hashes)
143+
)
144+
{
145+
READWRITE(obj.m_tx_hashes);
146+
}
147+
END_MESSAGE()
148+
149+
// message_losing_tx
150+
BEGIN_MESSAGE(losing_tx)
151+
MESSAGE_FIELDS
152+
(
153+
(std::vector<uint256>, m_tx_hashes)
154+
)
155+
{
156+
READWRITE(obj.m_tx_hashes);
157+
}
158+
END_MESSAGE()
159+
160+
// message_forget_tx
161+
BEGIN_MESSAGE(forget_tx)
162+
MESSAGE_FIELDS
163+
(
164+
(std::vector<uint256>, m_tx_hashes)
165+
)
166+
{
167+
READWRITE(obj.m_tx_hashes);
168+
}
169+
END_MESSAGE()
170+
171+
// message_remember_tx
172+
BEGIN_MESSAGE(remember_tx)
173+
MESSAGE_FIELDS
174+
(
175+
(std::vector<uint256>, m_tx_hashes),
176+
(std::vector<coin::MutableTransaction>, m_txs)
177+
)
178+
{
179+
READWRITE(obj.m_tx_hashes, coin::TX_WITH_WITNESS(obj.m_txs));
180+
}
181+
END_MESSAGE()
182+
183+
using Handler = MessageHandler<
184+
message_ping,
185+
message_addrme,
186+
message_getaddrs,
187+
message_addrs,
188+
message_shares,
189+
message_sharereq,
190+
message_sharereply,
191+
message_bestblock,
192+
message_have_tx,
193+
message_losing_tx,
194+
message_forget_tx,
195+
message_remember_tx
196+
>;
197+
198+
} // namespace dash

test/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,15 @@ if (BUILD_TESTING AND GTest_FOUND)
446446
)
447447
target_link_libraries(test_dash_p2p_messages PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
448448
gtest_add_tests(test_dash_p2p_messages "" AUTO)
449+
add_executable(test_dash_poolnode_messages test_dash_poolnode_messages.cpp)
450+
target_link_libraries(test_dash_poolnode_messages PRIVATE
451+
GTest::gtest_main GTest::gtest
452+
dash_x11 core
453+
nlohmann_json::nlohmann_json
454+
${Boost_LIBRARIES}
455+
)
456+
target_link_libraries(test_dash_poolnode_messages PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
457+
gtest_add_tests(test_dash_poolnode_messages "" AUTO)
449458

450459
add_executable(test_dash_p2p_connection test_dash_p2p_connection.cpp)
451460
target_link_libraries(test_dash_p2p_connection PRIVATE
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// KAT for the Dash sharechain pool-node wire layer (src/impl/dash/messages.hpp).
2+
// Proves each pool-node BEGIN_MESSAGE round-trips byte-for-byte and the type-list
3+
// instantiates — the btc/dgb/ltc analog ported namespace-only to dash.
4+
// Mirrors test_dash_p2p_messages.cpp idiom.
5+
#include <gtest/gtest.h>
6+
7+
#include <impl/dash/messages.hpp>
8+
9+
using namespace dash;
10+
11+
namespace {
12+
13+
// Non-destructive byte view of a PackStream (does not advance the read cursor).
14+
static std::vector<unsigned char> bytes_of(PackStream& ps) {
15+
auto sp = ps.get_span();
16+
auto* p = reinterpret_cast<const unsigned char*>(sp.data());
17+
return std::vector<unsigned char>(p, p + sp.size());
18+
}
19+
20+
static addr_t make_addr(uint16_t port) {
21+
return addr_t(1u, NetService("192.168.1.1", port));
22+
}
23+
24+
} // namespace
25+
26+
TEST(DashPoolNodeMessages, Message_Version_RoundTrip) {
27+
auto rmsg = message_version::make_raw(
28+
70238u, 1u, make_addr(9999), make_addr(8888), uint64_t(0xABCDEF),
29+
std::string("c2pool-dash"), 1u, uint256(0x11ull));
30+
EXPECT_EQ(rmsg->m_command, "version");
31+
32+
auto parsed = message_version::make(rmsg->m_data);
33+
EXPECT_EQ(parsed->m_version, 70238u);
34+
EXPECT_EQ(parsed->m_services, 1u);
35+
EXPECT_EQ(parsed->m_nonce, uint64_t(0xABCDEF));
36+
EXPECT_EQ(parsed->m_subversion, std::string("c2pool-dash"));
37+
EXPECT_EQ(parsed->m_mode, 1u);
38+
EXPECT_EQ(parsed->m_addr_to.m_endpoint.port(), 9999);
39+
EXPECT_EQ(parsed->m_addr_from.m_endpoint.port(), 8888);
40+
EXPECT_EQ(parsed->m_best_share, uint256(0x11ull));
41+
}
42+
43+
TEST(DashPoolNodeMessages, Message_Ping_Empty) {
44+
auto rmsg = message_ping::make_raw();
45+
EXPECT_EQ(rmsg->m_command, "ping");
46+
EXPECT_EQ(bytes_of(rmsg->m_data).size(), 0u);
47+
}
48+
49+
TEST(DashPoolNodeMessages, Message_Addrme_LayoutPinned) {
50+
auto rmsg = message_addrme::make_raw(uint16_t(0x1234));
51+
EXPECT_EQ(rmsg->m_command, "addrme");
52+
// uint16_t port, little-endian: 2 bytes 0x34 0x12
53+
auto b = bytes_of(rmsg->m_data);
54+
ASSERT_EQ(b.size(), 2u);
55+
EXPECT_EQ(b[0], 0x34);
56+
EXPECT_EQ(b[1], 0x12);
57+
58+
auto parsed = message_addrme::make(rmsg->m_data);
59+
EXPECT_EQ(parsed->m_port, 0x1234);
60+
}
61+
62+
TEST(DashPoolNodeMessages, Message_Getaddrs_RoundTrip) {
63+
auto rmsg = message_getaddrs::make_raw(42u);
64+
EXPECT_EQ(rmsg->m_command, "getaddrs");
65+
auto parsed = message_getaddrs::make(rmsg->m_data);
66+
EXPECT_EQ(parsed->m_count, 42u);
67+
}
68+
69+
TEST(DashPoolNodeMessages, Message_Addrs_RoundTrip) {
70+
std::vector<addr_record_t> addrs{
71+
addr_record_t(make_addr(19999), 1710000000ull),
72+
addr_record_t(make_addr(29999), 1710000001ull),
73+
};
74+
auto rmsg = message_addrs::make_raw(addrs);
75+
EXPECT_EQ(rmsg->m_command, "addrs");
76+
auto parsed = message_addrs::make(rmsg->m_data);
77+
ASSERT_EQ(parsed->m_addrs.size(), 2u);
78+
EXPECT_EQ(parsed->m_addrs[0].m_endpoint.port(), 19999);
79+
EXPECT_EQ(parsed->m_addrs[0].m_timestamp, 1710000000ull);
80+
EXPECT_EQ(parsed->m_addrs[1].m_endpoint.port(), 29999);
81+
}
82+
83+
TEST(DashPoolNodeMessages, Message_Shares_RoundTrip_V16) {
84+
// Dash sharechain carries v16 shares in the envelope (transition 16->36).
85+
std::vector<chain::RawShare> shares{
86+
chain::RawShare(16u, BaseScript(std::vector<unsigned char>{0xde, 0xad, 0xbe, 0xef})),
87+
chain::RawShare(36u, BaseScript(std::vector<unsigned char>{0x01, 0x02})),
88+
};
89+
auto rmsg = message_shares::make_raw(shares);
90+
EXPECT_EQ(rmsg->m_command, "shares");
91+
auto parsed = message_shares::make(rmsg->m_data);
92+
ASSERT_EQ(parsed->m_shares.size(), 2u);
93+
EXPECT_EQ(parsed->m_shares[0].type, 16u);
94+
EXPECT_EQ(parsed->m_shares[1].type, 36u);
95+
EXPECT_EQ(parsed->m_shares[0].contents.m_data,
96+
(std::vector<unsigned char>{0xde, 0xad, 0xbe, 0xef}));
97+
}
98+
99+
TEST(DashPoolNodeMessages, Message_Sharereq_RoundTrip) {
100+
auto rmsg = message_sharereq::make_raw(
101+
uint256(0xaaull),
102+
std::vector<uint256>{uint256(0x01ull), uint256(0x02ull)},
103+
5ull,
104+
std::vector<uint256>{uint256(0xffull)});
105+
EXPECT_EQ(rmsg->m_command, "sharereq");
106+
auto parsed = message_sharereq::make(rmsg->m_data);
107+
EXPECT_EQ(parsed->m_id, uint256(0xaaull));
108+
ASSERT_EQ(parsed->m_hashes.size(), 2u);
109+
EXPECT_EQ(parsed->m_parents, 5ull);
110+
ASSERT_EQ(parsed->m_stops.size(), 1u);
111+
EXPECT_EQ(parsed->m_stops[0], uint256(0xffull));
112+
}
113+
114+
TEST(DashPoolNodeMessages, Message_Sharereply_EnumResult) {
115+
std::vector<chain::RawShare> shares{
116+
chain::RawShare(16u, BaseScript(std::vector<unsigned char>{0x42})),
117+
};
118+
auto rmsg = message_sharereply::make_raw(uint256(0xccull), good, shares);
119+
EXPECT_EQ(rmsg->m_command, "sharereply");
120+
auto parsed = message_sharereply::make(rmsg->m_data);
121+
EXPECT_EQ(parsed->m_id, uint256(0xccull));
122+
EXPECT_EQ(parsed->m_result, good);
123+
ASSERT_EQ(parsed->m_shares.size(), 1u);
124+
}
125+
126+
TEST(DashPoolNodeMessages, Message_HaveTx_RoundTrip) {
127+
std::vector<uint256> hashes{uint256(0x07ull), uint256(0x08ull)};
128+
auto rmsg = message_have_tx::make_raw(hashes);
129+
EXPECT_EQ(rmsg->m_command, "have_tx");
130+
auto parsed = message_have_tx::make(rmsg->m_data);
131+
ASSERT_EQ(parsed->m_tx_hashes.size(), 2u);
132+
EXPECT_EQ(parsed->m_tx_hashes[1], uint256(0x08ull));
133+
}
134+
135+
TEST(DashPoolNodeMessages, Handler_TypeList_Compiles) {
136+
// The pool-node dispatch type-list must instantiate (variadic pack valid).
137+
// Type-only check: avoids ODR-using MessageHandler::m_handlers static
138+
// storage, which is defined out-of-line in the (not-yet-landed) node.cpp.
139+
using HandlerResult = Handler::result_t;
140+
EXPECT_GT(sizeof(HandlerResult), 0u);
141+
}
142+

0 commit comments

Comments
 (0)