diff --git a/src/stratifier.c b/src/stratifier.c index fc46efdd..588f708c 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -40,6 +40,7 @@ #ifdef HAVE_SV2 #include "sv2_strat.h" #include "sv2_jd.h" +#include "sv2_tx.h" #endif /* Consistent across all pool instances */ @@ -6988,7 +6989,6 @@ bool stratifier_sv2_merkle_root(int64_t instance_id, uint8_t merkle_root_le[32], stratum_instance_t *client; sdata_t *sdata = ckpool.sdata; int cblen, i, en1len; - uint32_t *data32, *swap32; if (!stratifier_sv2_snapshot_work(&snap, instance_id)) return false; @@ -7016,9 +7016,11 @@ bool stratifier_sv2_merkle_root(int64_t instance_id, uint8_t merkle_root_le[32], gen_hash(merkle_sha, merkle_root, 64); memcpy(merkle_sha, merkle_root, 32); } - data32 = (uint32_t *)merkle_sha; - swap32 = (uint32_t *)merkle_root_le; - flip_32(swap32, data32); + /* SV2 U256 fields carry the raw SHA256d digest bytes in little-endian + * integer order. gen_hash() already returns those bytes; applying + * CKPool's internal flip_32() here would word-swap the Merkle root and + * cause miners to hash a different header than the pool validates. */ + sv2_merkle_root_to_u256_le(merkle_sha, merkle_root_le); if (wb_id_out) *wb_id_out = snap.wb_id; diff --git a/src/sv2_tx.c b/src/sv2_tx.c index 04001823..fed638e7 100644 --- a/src/sv2_tx.c +++ b/src/sv2_tx.c @@ -270,6 +270,11 @@ void sv2_merkle_root_from_path(const uint8_t coinbase_txid[32], memcpy(root, pair, 32); } +void sv2_merkle_root_to_u256_le(const uint8_t raw_hash[32], uint8_t out[32]) +{ + memcpy(out, raw_hash, 32); +} + double sv2_diff_from_nbits(uint32_t nbits) { uint32_t be = htobe32(nbits); diff --git a/src/sv2_tx.h b/src/sv2_tx.h index d11280e6..006045a2 100644 --- a/src/sv2_tx.h +++ b/src/sv2_tx.h @@ -65,6 +65,10 @@ void sv2_merkle_root_from_path(const uint8_t coinbase_txid[32], const uint8_t (*path)[32], int steps, uint8_t root[32]); +/* A SHA256d digest is already the little-endian byte representation required + * by the SV2 U256 merkle_root field. */ +void sv2_merkle_root_to_u256_le(const uint8_t raw_hash[32], uint8_t out[32]); + /* * Network difficulty of an nbits value held as a host-order U32 — the form SV2 * carries it in, and the form that goes little-endian into a wire header. diff --git a/test/sv2_tx.c b/test/sv2_tx.c index 2b53212d..f45cc084 100644 --- a/test/sv2_tx.c +++ b/test/sv2_tx.c @@ -282,6 +282,21 @@ static void test_merkle(void) expect(!memcmp(root, want, 32), "path fold reproduces the tree root"); } +static void test_merkle_u256_wire_order(void) +{ + static const uint8_t raw[32] = { + 0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, + 0x20, 0x21, 0x22, 0x23, 0x30, 0x31, 0x32, 0x33, + 0x40, 0x41, 0x42, 0x43, 0x50, 0x51, 0x52, 0x53, + 0x60, 0x61, 0x62, 0x63, 0x70, 0x71, 0x72, 0x73, + }; + uint8_t wire[32]; + + sv2_merkle_root_to_u256_le(raw, wire); + expect(!memcmp(raw, wire, 32), + "SV2 U256 merkle root preserves raw digest byte order"); +} + /* * nbits → difficulty. The byte order matters more than the arithmetic: the * little-endian bytes of a wire header fed to libckpool's diff_from_nbits() @@ -317,6 +332,7 @@ int main(void) test_witness_tx(); test_walk_sequence(); test_merkle(); + test_merkle_u256_wire_order(); test_nbits_diff(); if (failures) {