@@ -867,98 +867,19 @@ MergedMiningManager::build_merged_header_info() const
867867 std::string bits_hex = tmpl.value (" bits" , std::string (" 1d00ffff" ));
868868
869869 try {
870- // Build full block hex (reuses existing tested code)
871870 uint256 state_root;
872871 if (m_state_root_provider) state_root = m_state_root_provider ();
873- // Need a dummy auxpow — but we don't have the parent data yet.
874- // The header doesn't include auxpow — it's just the first 80 bytes.
875- // We can build coinbase + compute merkle root + assemble header separately.
876-
877- // Build coinbase TX hex (same logic as build_multiaddress_block)
878- std::string coinbase_hex;
879- {
880- std::ostringstream coinb;
881- coinb << " 01000000" << " 01"
882- << " 0000000000000000000000000000000000000000000000000000000000000000"
883- << " ffffffff" ;
884- // scriptSig: BIP34 height + tag
885- std::ostringstream sig2;
886- { uint32_t h2 = info.block_height ;
887- // BIP34 height push: 03 + LE bytes (matches encode_height_pushdata)
888- uint8_t hb[4 ]; for (int i=0 ;i<4 ;++i) hb[i]=(h2>>(8 *i))&0xff ;
889- if (h2 < 0x100 ) { sig2 << " 02" << to_hex (hb, 1 ) << " 00" ; }
890- else if (h2 < 0x10000 ) { sig2 << " 03" << to_hex (hb, 2 ) << " 00" ; }
891- else { sig2 << " 04" << to_hex (hb, 4 ); }
892- }
893- const std::string ctag = " /c2pool/" ;
894- for (char ch : ctag) { uint8_t b2 = uint8_t (ch); sig2 << to_hex (&b2, 1 ); }
895- if (!state_root.IsNull ()) sig2 << to_hex (state_root.data (), 32 );
896- std::string sig_hex2 = sig2.str ();
897- coinb << varint_hex (sig_hex2.size ()/2 ) << sig_hex2 << " ffffffff" ;
898- // Outputs
899- static const std::vector<unsigned char > CDON = {
900- 0xa9 ,0x14 ,0x8c ,0x62 ,0x72 ,0x62 ,0x1d ,0x89 ,0xe8 ,0xfa ,
901- 0x52 ,0x6d ,0xd8 ,0x6a ,0xcf ,0xf6 ,0x0c ,0x71 ,0x36 ,0xbe ,0x8e ,0x85 ,0x87 };
902- std::vector<std::pair<std::vector<unsigned char >,uint64_t >> mouts;
903- uint64_t don = 0 ;
904- for (auto & [s,a] : payouts) { if (s == CDON ) don += a; else if (a > 0 ) mouts.emplace_back (s,a); }
905- if (don < 1 && !mouts.empty ()) { mouts.back ().second -= 1 ; don += 1 ; }
906- std::string optext = " c2pool merged mining" ;
907- coinb << varint_hex (mouts.size () + 2 );
908- for (auto & [s,a] : mouts) {
909- uint8_t v[8 ]; for (int i=0 ;i<8 ;++i) v[i]=(a>>(8 *i))&0xFF ;
910- coinb << to_hex (v,8 ) << varint_hex (s.size ()) << to_hex (s.data (), s.size ());
911- }
912- { uint8_t z[8 ]={}; coinb << to_hex (z,8 ); // OP_RETURN value=0
913- std::vector<uint8_t > ops; ops.push_back (0x6a ); ops.push_back (uint8_t (optext.size ()));
914- ops.insert (ops.end (), optext.begin (), optext.end ());
915- coinb << varint_hex (ops.size ()) << to_hex (ops.data (), ops.size ()); }
916- { uint8_t v[8 ]; for (int i=0 ;i<8 ;++i) v[i]=(don>>(8 *i))&0xFF ;
917- coinb << to_hex (v,8 ) << varint_hex (CDON .size ()) << to_hex (CDON .data (), CDON .size ()); }
918- coinb << " 00000000" ; // locktime
919- coinbase_hex = coinb.str ();
920- }
872+
873+ // Build coinbase, compute merkle root + link using shared helpers
874+ std::string coinbase_hex = build_pplns_coinbase_hex (
875+ info.block_height , payouts, state_root);
921876 if (coinbase_hex.empty ()) continue ;
922877
923878 auto coinbase_bytes = from_hex (coinbase_hex);
924879 uint256 cb_hash = Hash (coinbase_bytes);
925-
926- // Tx hashes
927- std::vector<uint256> tx_hashes;
928- tx_hashes.push_back (cb_hash);
929- if (tmpl.contains (" transactions" ) && tmpl[" transactions" ].is_array ()) {
930- for (const auto & tx : tmpl[" transactions" ]) {
931- std::string txid_str = tx.value (" txid" , " " );
932- if (!txid_str.empty ()) { uint256 h; h.SetHex (txid_str); tx_hashes.push_back (h); }
933- else { auto raw = from_hex (tx.value (" data" , " " )); tx_hashes.push_back (Hash (raw)); }
934- }
935- }
936-
937- // Merkle root
938- auto layer = tx_hashes;
939- while (layer.size () > 1 ) {
940- if (layer.size () % 2 ) layer.push_back (layer.back ());
941- std::vector<uint256> next;
942- for (size_t i = 0 ; i < layer.size (); i += 2 )
943- next.push_back (Hash (layer[i], layer[i+1 ]));
944- layer = std::move (next);
945- }
946- uint256 merkle_root = layer.empty () ? cb_hash : layer[0 ];
947-
948- // Coinbase merkle link (branches for proof: coinbase → root)
949- {
950- auto all = tx_hashes;
951- size_t idx = 0 ; // coinbase is at index 0
952- while (all.size () > 1 ) {
953- if (all.size () % 2 ) all.push_back (all.back ());
954- info.coinbase_merkle_branches .push_back (all[idx ^ 1 ]);
955- std::vector<uint256> next;
956- for (size_t i = 0 ; i < all.size (); i += 2 )
957- next.push_back (Hash (all[i], all[i+1 ]));
958- all = std::move (next);
959- idx >>= 1 ;
960- }
961- }
880+ auto tx_hashes = collect_tx_hashes (cb_hash, tmpl);
881+ uint256 merkle_root = compute_tx_merkle_root (tx_hashes);
882+ info.coinbase_merkle_branches = compute_merkle_link (tx_hashes, 0 );
962883
963884 // 80-byte header
964885 uint256 prev_hash;
@@ -1013,6 +934,131 @@ void MergedMiningManager::set_block_relay_fn(BlockRelayFn fn)
1013934// The coinbase transaction is built fresh with the supplied payout outputs.
1014935// Template transactions are included verbatim from getblocktemplate.
1015936
937+ // ─── Shared helpers ──────────────────────────────────────────────────────────
938+
939+ /* static*/ std::string MergedMiningManager::build_pplns_coinbase_hex (
940+ int height,
941+ const std::vector<std::pair<std::vector<unsigned char >, uint64_t >>& payouts,
942+ const uint256& the_state_root)
943+ {
944+ if (payouts.empty ()) return {};
945+
946+ static const std::vector<unsigned char > COMBINED_DONATION = {
947+ 0xa9 , 0x14 , 0x8c , 0x62 , 0x72 , 0x62 , 0x1d , 0x89 , 0xe8 , 0xfa ,
948+ 0x52 , 0x6d , 0xd8 , 0x6a , 0xcf , 0xf6 , 0x0c , 0x71 , 0x36 , 0xbe ,
949+ 0x8e , 0x85 , 0x87
950+ };
951+
952+ std::ostringstream cb;
953+ cb << " 01000000" << " 01"
954+ << " 0000000000000000000000000000000000000000000000000000000000000000"
955+ << " ffffffff" ;
956+
957+ // scriptSig: BIP34 height + "/c2pool/" + THE state root
958+ std::ostringstream sig;
959+ if (height > 0 && height <= 16 ) {
960+ uint8_t op = static_cast <uint8_t >(0x50 + height);
961+ sig << to_hex (&op, 1 );
962+ } else {
963+ std::vector<uint8_t > hbytes;
964+ uint32_t h = static_cast <uint32_t >(height);
965+ while (h > 0 ) { hbytes.push_back (h & 0xff ); h >>= 8 ; }
966+ if (!hbytes.empty () && (hbytes.back () & 0x80 )) hbytes.push_back (0 );
967+ uint8_t push_len = static_cast <uint8_t >(hbytes.size ());
968+ sig << to_hex (&push_len, 1 ) << to_hex (hbytes.data (), hbytes.size ());
969+ }
970+ const std::string ctag = " /c2pool/" ;
971+ for (char c : ctag) { uint8_t b = static_cast <uint8_t >(c); sig << to_hex (&b, 1 ); }
972+ if (!the_state_root.IsNull ()) sig << to_hex (the_state_root.data (), 32 );
973+ std::string sig_hex = sig.str ();
974+ cb << varint_hex (sig_hex.size () / 2 ) << sig_hex << " ffffffff" ;
975+
976+ // Separate donation from miner outputs
977+ std::vector<std::pair<std::vector<unsigned char >, uint64_t >> miner_outs;
978+ uint64_t donation_amount = 0 ;
979+ for (const auto & [script, amount] : payouts) {
980+ if (script == COMBINED_DONATION ) donation_amount += amount;
981+ else if (amount > 0 ) miner_outs.emplace_back (script, amount);
982+ }
983+ if (donation_amount < 1 && !miner_outs.empty ()) {
984+ miner_outs.back ().second -= 1 ;
985+ donation_amount += 1 ;
986+ }
987+
988+ const std::string op_return_text = " c2pool merged mining" ;
989+ std::vector<unsigned char > op_return_script;
990+ op_return_script.push_back (0x6a );
991+ op_return_script.push_back (static_cast <unsigned char >(op_return_text.size ()));
992+ op_return_script.insert (op_return_script.end (), op_return_text.begin (), op_return_text.end ());
993+
994+ cb << varint_hex (miner_outs.size () + 2 );
995+ for (const auto & [script, amount] : miner_outs) {
996+ uint8_t vbuf[8 ]; for (int i = 0 ; i < 8 ; ++i) vbuf[i] = (amount >> (8 *i)) & 0xFF ;
997+ cb << to_hex (vbuf, 8 ) << varint_hex (script.size ()) << to_hex (script.data (), script.size ());
998+ }
999+ { uint8_t z[8 ] = {}; cb << to_hex (z, 8 )
1000+ << varint_hex (op_return_script.size ()) << to_hex (op_return_script.data (), op_return_script.size ()); }
1001+ { uint8_t vbuf[8 ]; for (int i = 0 ; i < 8 ; ++i) vbuf[i] = (donation_amount >> (8 *i)) & 0xFF ;
1002+ cb << to_hex (vbuf, 8 ) << varint_hex (COMBINED_DONATION .size ())
1003+ << to_hex (COMBINED_DONATION .data (), COMBINED_DONATION .size ()); }
1004+ cb << " 00000000" ;
1005+ return cb.str ();
1006+ }
1007+
1008+ /* static*/ uint256 MergedMiningManager::compute_tx_merkle_root (
1009+ const std::vector<uint256>& tx_hashes)
1010+ {
1011+ if (tx_hashes.empty ()) return uint256 ();
1012+ auto layer = tx_hashes;
1013+ while (layer.size () > 1 ) {
1014+ if (layer.size () % 2 ) layer.push_back (layer.back ());
1015+ std::vector<uint256> next;
1016+ for (size_t i = 0 ; i < layer.size (); i += 2 )
1017+ next.push_back (Hash (layer[i], layer[i+1 ]));
1018+ layer = std::move (next);
1019+ }
1020+ return layer[0 ];
1021+ }
1022+
1023+ /* static*/ std::vector<uint256> MergedMiningManager::compute_merkle_link (
1024+ const std::vector<uint256>& tx_hashes, size_t leaf_index)
1025+ {
1026+ std::vector<uint256> branches;
1027+ auto all = tx_hashes;
1028+ size_t idx = leaf_index;
1029+ while (all.size () > 1 ) {
1030+ if (all.size () % 2 ) all.push_back (all.back ());
1031+ branches.push_back (all[idx ^ 1 ]);
1032+ std::vector<uint256> next;
1033+ for (size_t i = 0 ; i < all.size (); i += 2 )
1034+ next.push_back (Hash (all[i], all[i+1 ]));
1035+ all = std::move (next);
1036+ idx >>= 1 ;
1037+ }
1038+ return branches;
1039+ }
1040+
1041+ /* static*/ std::vector<uint256> MergedMiningManager::collect_tx_hashes (
1042+ const uint256& coinbase_hash, const nlohmann::json& tmpl)
1043+ {
1044+ std::vector<uint256> tx_hashes;
1045+ tx_hashes.push_back (coinbase_hash);
1046+ if (tmpl.contains (" transactions" ) && tmpl[" transactions" ].is_array ()) {
1047+ for (const auto & tx : tmpl[" transactions" ]) {
1048+ std::string txid_str = tx.value (" txid" , " " );
1049+ if (!txid_str.empty ()) {
1050+ uint256 h; h.SetHex (txid_str); tx_hashes.push_back (h);
1051+ } else {
1052+ auto raw = from_hex (tx.value (" data" , " " ));
1053+ tx_hashes.push_back (Hash (raw));
1054+ }
1055+ }
1056+ }
1057+ return tx_hashes;
1058+ }
1059+
1060+ // ─── build_multiaddress_block (refactored to use shared helpers) ─────────────
1061+
10161062std::string MergedMiningManager::build_multiaddress_block (
10171063 const nlohmann::json& tmpl,
10181064 const std::vector<std::pair<std::vector<unsigned char >, uint64_t >>& payouts,
@@ -1022,19 +1068,20 @@ std::string MergedMiningManager::build_multiaddress_block(
10221068 if (payouts.empty () || !tmpl.contains (" previousblockhash" ))
10231069 return {};
10241070
1025- // --- Parse template fields ---
1026- // Set AuxPoW flag (bit 8) so the aux daemon recognizes this as a
1027- // merge-mined block and parses the AuxPoW proof after the header.
1028- // Reference: p2pool work.py: version=template['version'] | (1 << 8)
10291071 uint32_t version = tmpl.value (" version" , 0x20000002u ) | 0x100 ;
10301072 std::string prev_hash_hex = tmpl.value (" previousblockhash" , " " );
10311073 uint32_t curtime = tmpl.value (" curtime" , 0u );
10321074 std::string bits_hex = tmpl.value (" bits" , " 1d00ffff" );
10331075 int height = tmpl.value (" height" , 0 );
10341076
1035- // --- Build coinbase TX ---
1036- std::ostringstream cb;
1077+ // --- Build coinbase TX using shared helper ---
1078+ std::string coinbase_hex = build_pplns_coinbase_hex (height, payouts, the_state_root);
1079+ if (coinbase_hex.empty ()) return {};
10371080
1081+ // Skip old inline coinbase — now built by build_pplns_coinbase_hex above.
1082+ // Jump directly to merkle root + block assembly.
1083+ if (false ) { // ---- BEGIN DEAD CODE (replaced by shared helper) ----
1084+ std::ostringstream cb;
10381085 // tx version (1, LE)
10391086 cb << " 01000000" ;
10401087
@@ -1147,49 +1194,20 @@ std::string MergedMiningManager::build_multiaddress_block(
11471194 // locktime
11481195 cb << " 00000000" ;
11491196
1150- std::string coinbase_hex = cb.str ();
1197+ std::string coinbase_hex_DEAD = cb.str ();
1198+ } // ---- END DEAD CODE ----
11511199
1152- // --- Coinbase txid (double-SHA256 of serialized coinbase ) ---
1200+ // --- Coinbase txid + tx hashes + merkle root (shared helpers ) ---
11531201 auto coinbase_bytes = from_hex (coinbase_hex);
11541202 uint256 cb_hash = Hash (coinbase_bytes);
1203+ auto tx_hashes = collect_tx_hashes (cb_hash, tmpl);
1204+ uint256 merkle_root = compute_tx_merkle_root (tx_hashes);
11551205
1156- // --- Collect template transaction hashes ---
1157- std::vector<uint256> tx_hashes;
1158- tx_hashes.push_back (cb_hash);
1159-
1206+ // Collect raw tx data for block assembly
11601207 std::vector<std::string> tx_datas;
11611208 if (tmpl.contains (" transactions" ) && tmpl[" transactions" ].is_array ())
1162- {
11631209 for (const auto & tx : tmpl[" transactions" ])
1164- {
1165- std::string txdata = tx.value (" data" , " " );
1166- tx_datas.push_back (txdata);
1167-
1168- std::string txid_str = tx.value (" txid" , " " );
1169- if (!txid_str.empty ()) {
1170- uint256 txid;
1171- txid.SetHex (txid_str);
1172- tx_hashes.push_back (txid);
1173- } else {
1174- // Compute txid from data
1175- auto raw = from_hex (txdata);
1176- tx_hashes.push_back (Hash (raw));
1177- }
1178- }
1179- }
1180-
1181- // --- Merkle root ---
1182- auto merkle_layer = tx_hashes;
1183- while (merkle_layer.size () > 1 )
1184- {
1185- if (merkle_layer.size () % 2 != 0 )
1186- merkle_layer.push_back (merkle_layer.back ());
1187- std::vector<uint256> next;
1188- for (size_t i = 0 ; i < merkle_layer.size (); i += 2 )
1189- next.push_back (Hash (merkle_layer[i], merkle_layer[i + 1 ]));
1190- merkle_layer = std::move (next);
1191- }
1192- uint256 merkle_root = merkle_layer.empty () ? cb_hash : merkle_layer[0 ];
1210+ tx_datas.push_back (tx.value (" data" , " " ));
11931211
11941212 // --- Block header (80 bytes) ---
11951213 std::ostringstream hdr;
0 commit comments