@@ -311,6 +311,45 @@ struct AuxPow {
311311
312312 bool IsNull () const { return parent_header.IsNull (); }
313313
314+ // / Canonical Namecoin CAuxPow wire layout, fenced to the NMC tree (only
315+ // / core/* + nmc::coin types -- no btc/ or bitcoin_family/ include). A
316+ // / byte-faithful port of Namecoin CAuxPow / classic Bitcoin auxpow: the
317+ // / CMerkleTx coinbase leg, then the chain-merkle leg, then the parent
318+ // / header, in daemon field order:
319+ // / 1. parent_coinbase -- parent (BTC) coinbase CTransaction,
320+ // / serialized WITNESS-STRIPPED (TX_NO_WITNESS) so the bytes match the
321+ // / txid the parent tx-merkle tree commits (see parent_coinbase_txid)
322+ // / and the daemon auxpow encoding -- NOT the wtxid form;
323+ // / 2. parent_block_hash -- CMerkleTx::hashBlock;
324+ // / 3. parent_coinbase_branch -- CMerkleTx::vMerkleBranch;
325+ // / 4. parent_coinbase_index -- CMerkleTx::nIndex (int32 LE);
326+ // / 5. chain_merkle_branch -- CAuxPow::vChainMerkleBranch;
327+ // / 6. chain_merkle_index -- CAuxPow::nChainIndex (int32 LE);
328+ // / 7. parent_header -- CAuxPow::parentBlock (80-byte header).
329+ // / Hand-written (not C2POOL_SERIALIZE_METHODS) because the parent coinbase
330+ // / needs the explicit TX_NO_WITNESS param wrapper. Backs the P1f on-disk
331+ // / auxpow blob and is reused by PD dual-target wire path.
332+ template <typename Stream>
333+ void Serialize (Stream& s) const {
334+ ::Serialize (s, TX_NO_WITNESS (parent_coinbase));
335+ ::Serialize (s, parent_block_hash);
336+ ::Serialize (s, parent_coinbase_branch);
337+ ::Serialize (s, parent_coinbase_index);
338+ ::Serialize (s, chain_merkle_branch);
339+ ::Serialize (s, chain_merkle_index);
340+ ::Serialize (s, parent_header);
341+ }
342+ template <typename Stream>
343+ void Unserialize (Stream& s) {
344+ ::Unserialize (s, TX_NO_WITNESS (parent_coinbase));
345+ ::Unserialize (s, parent_block_hash);
346+ ::Unserialize (s, parent_coinbase_branch);
347+ ::Unserialize (s, parent_coinbase_index);
348+ ::Unserialize (s, chain_merkle_branch);
349+ ::Unserialize (s, chain_merkle_index);
350+ ::Unserialize (s, parent_header);
351+ }
352+
314353 // / Result of a (future) AuxPow verification.
315354 enum class CheckResult {
316355 NOT_IMPLEMENTED_P0 , // !< P0 leaf — verification path not built yet
@@ -561,20 +600,20 @@ struct IndexEntry {
561600// / status (as uint32_t), then the AuxPow presence flag (uint8_t: 1 if the entry
562601// / carried a merge-mining proof, else 0).
563602// /
564- // / P1f-DEFER: auxpow blob persistence -- next sub-leg. nmc::coin::AuxPow itself
565- // / has NO ::Serialize/::Unserialize / C2POOL_SERIALIZE_METHODS, so per the
566- // / fence we do NOT hand-roll its wire layout here. This leg persists ONLY the
567- // / presence flag; on load the AuxPow is reconstructed as std::nullopt, but the
568- // / entry's status (HEADER_VALID_CHAIN for a merge-mined header) is preserved
569- // / faithfully so the restored chain's validation state is not lost. The actual
570- // / proof blob lands in the next sub-leg behind this same flag.
603+ // / P1f(a): the AuxPow proof blob is persisted behind has_auxpow using the
604+ // / canonical Namecoin CAuxPow wire layout (AuxPow::Serialize, fenced to
605+ // / src/impl/nmc/). When has_auxpow == 1 the blob trails the flag and is
606+ // / restored verbatim on load; when 0, nothing follows. The entry's status
607+ // / (HEADER_VALID_CHAIN for a merge-mined header) is preserved either way, so
608+ // / the restored chain's validation state is never lost.
571609struct IndexEntryDiskV1 {
572610 BlockHeaderType header;
573611 uint256 block_hash; // SHA256d(header)
574612 uint32_t height{0 };
575613 uint256 chain_work; // cumulative work up to this header
576614 HeaderStatus status{HEADER_VALID_UNKNOWN };
577- uint8_t has_auxpow{0 }; // presence flag (blob deferred -- see above)
615+ uint8_t has_auxpow{0 }; // presence flag for the trailing blob
616+ std::optional<AuxPow> auxpow; // P1f(a): proof blob, present iff has_auxpow
578617
579618 template <typename Stream>
580619 void Serialize (Stream& s) const {
@@ -584,9 +623,9 @@ struct IndexEntryDiskV1 {
584623 ::Serialize (s, chain_work);
585624 ::Serialize (s, static_cast <uint32_t >(status));
586625 ::Serialize (s, has_auxpow);
587- // P1f-DEFER: auxpow blob persistence -- next sub-leg. Nothing follows the
588- // flag yet; when AuxPow gains serialization the blob is written here iff
589- // has_auxpow == 1.
626+ // P1f(a): the canonical CAuxPow blob trails the flag iff present.
627+ if (has_auxpow)
628+ ::Serialize (s, *auxpow);
590629 }
591630 template <typename Stream>
592631 void Unserialize (Stream& s) {
@@ -598,8 +637,14 @@ struct IndexEntryDiskV1 {
598637 ::Unserialize (s, st);
599638 status = static_cast <HeaderStatus>(st);
600639 ::Unserialize (s, has_auxpow);
601- // P1f-DEFER: auxpow blob persistence -- next sub-leg. The blob is not on
602- // disk yet, so there is nothing further to read behind the flag.
640+ // P1f(a): read the canonical CAuxPow blob iff the flag says it is there.
641+ if (has_auxpow) {
642+ AuxPow ap;
643+ ::Unserialize (s, ap);
644+ auxpow = std::move (ap);
645+ } else {
646+ auxpow = std::nullopt ;
647+ }
603648 }
604649
605650 // / Materialize the slim in-memory form. auxpow is reconstructed as nullopt
@@ -611,7 +656,7 @@ struct IndexEntryDiskV1 {
611656 e.height = height;
612657 e.chain_work = chain_work;
613658 e.status = status;
614- e.auxpow = std:: nullopt ; // P1f-DEFER : blob not persisted yet
659+ e.auxpow = auxpow ; // P1f(a) : blob restored behind has_auxpow
615660 return e;
616661 }
617662
@@ -624,6 +669,7 @@ struct IndexEntryDiskV1 {
624669 d.chain_work = e.chain_work ;
625670 d.status = e.status ;
626671 d.has_auxpow = e.auxpow .has_value () ? 1 : 0 ;
672+ d.auxpow = e.auxpow ; // P1f(a): carry the proof blob too
627673 return d;
628674 }
629675};
0 commit comments