@@ -1156,7 +1156,12 @@ MiningInterface::build_coinbase_parts(
11561156 }
11571157
11581158 // ScriptSig: height + mm_commitment + tag + state_root (NO extranonce!)
1159- const int script_total = height_bytes + mm_bytes + tag_bytes + state_root_bytes;
1159+ // Each element (mm, tag) gets a 1-byte push opcode prefix (matching create_push_script)
1160+ // state_root is raw (no push opcode, like coinbaseflags in p2pool)
1161+ const int mm_push_overhead = (mm_bytes > 0 ) ? 1 : 0 ;
1162+ const int tag_push_overhead = (tag_bytes > 0 ) ? 1 : 0 ;
1163+ const int script_total = height_bytes + mm_push_overhead + mm_bytes
1164+ + tag_push_overhead + tag_bytes + state_root_bytes;
11601165
11611166 // Build coinb1: entire coinbase TX up to and including ref_hash in OP_RETURN
11621167 std::ostringstream coinb1;
@@ -1167,10 +1172,17 @@ MiningInterface::build_coinbase_parts(
11671172 << std::hex << std::setfill (' 0' ) << std::setw (2 ) << script_total
11681173 << height_hex;
11691174
1170- // scriptSig: mm + tag + state_root (no en1/en2 — those go into last_txout_nonce)
1171- if (!mm_hex.empty ())
1172- coinb1 << mm_hex;
1173- coinb1 << tag_hex;
1175+ // scriptSig elements with push opcodes (matching p2pool's create_push_script):
1176+ // Each datum gets its own length-prefix push opcode.
1177+ auto emit_push = [&](std::ostringstream& os, const std::string& data_hex) {
1178+ size_t len = data_hex.size () / 2 ;
1179+ if (len > 0 && len < 76 )
1180+ os << std::hex << std::setfill (' 0' ) << std::setw (2 ) << len;
1181+ os << data_hex;
1182+ };
1183+ if (!mm_hex.empty ()) emit_push (coinb1, mm_hex);
1184+ if (!tag_hex.empty ()) emit_push (coinb1, tag_hex);
1185+ // state_root appended raw (like p2pool's coinbaseflags)
11741186 if (!state_root_hex.empty ())
11751187 coinb1 << state_root_hex;
11761188 coinb1 << " ffffffff" ; // sequence = 0xFFFFFFFF
@@ -1331,9 +1343,22 @@ MiningInterface::build_connection_coinbase(
13311343 }
13321344 }
13331345
1334- // ScriptSig: height + mm + tag + state_root (NO extranonce!)
1335- // Must match what build_coinbase_parts produces in the actual coinbase.
1336- std::string scriptsig_hex = height_hex + mm_hex + tag_hex + state_root_hex;
1346+ // ScriptSig: height_push + mm_push + tag_push + state_root (NO extranonce!)
1347+ // Must match p2pool's create_push_script([height, mm_data, coinb_texts]):
1348+ // Each element gets its own push opcode prefix (PUSH_N for N < 76 bytes).
1349+ // state_root is appended as raw bytes (like coinbaseflags in p2pool).
1350+ auto push_prefix = [&](const std::string& data_hex) -> std::string {
1351+ size_t len = data_hex.size () / 2 ;
1352+ if (len == 0 ) return " " ;
1353+ char buf[3 ];
1354+ snprintf (buf, sizeof (buf), " %02x" , static_cast <unsigned >(len));
1355+ return std::string (buf) + data_hex;
1356+ };
1357+ std::string scriptsig_hex = height_hex;
1358+ if (!mm_hex.empty ()) scriptsig_hex += push_prefix (mm_hex);
1359+ if (!tag_hex.empty ()) scriptsig_hex += push_prefix (tag_hex);
1360+ // state_root appended raw (like p2pool's coinbaseflags + COINBASEEXT)
1361+ scriptsig_hex += state_root_hex;
13371362
13381363 // Decode to bytes for ref_hash computation
13391364 std::vector<unsigned char > scriptsig_bytes;
0 commit comments