Skip to main content

Crate sidestr_header

Crate sidestr_header 

Source
Expand description

sidestr-header — block headers for sidestr sidechains, in both families the parent table allows.

A sidestr chain is an overlay on a Bitcoin-family parent, and SPEC 3 is explicit that the overlay names no header format and no hash: “everything the overlay does not set is inherited from the parent: header format and proof-of-work hash … Nothing in the document names a header format or a hash; the parent decides both.” SPEC 3.2’s parents table then fixes two families:

parent aliaschainheaders, proof of workthis crate
btc, tbtc4Bitcoin mainnet, testnet4stock 80 bytes, SHA-256dStockHeader
xbt, txbt4Bitcoin Knots’ BLAKE2b fork of each164-byte v2, BLAKE2bBlake2bV2Header

ltc and vtc are reserved upstream and absent here. The crate is a port of the reference JavaScript — the bitcoin-desktop/schema kernel’s header codec, Knots proof of work and Knots overlay rules, and Melvin Carvalho’s siding for how a sidestr block shapes its header and what its signature covers — under the same AGPL-3.0 licence (agentbox ADR-2106). Without the core feature it is #![no_std], allocates nothing, and takes every primitive from RustCrypto (sha2, blake2) with no bitcoin crate dependency (ADR-2096 D2): the header and its proof of work are the part of consensus that the parent’s serialisation library does not own.

With core (default) the family module implements sidestr_core::HeaderFamily for both header types, so sidestr_core::StateOf<Blake2bV2> and ChainOf<Blake2bV2> validate and produce a chain beside xbt or txbt4 end to end — the v2 header, the BLAKE2b proof of work, the Knots overlay’s rules and Knots’ unified sighash on every spend — proven by replaying the live sidestr:txbt4-siding chain. The edge points from this crate to sidestr-core, never back.

§What it gives a validator

Per family, through HeaderFamily and the enum Header or the two structs directly:

  • the wire size and a strict decode / encode pair;
  • hash(), the block id, which in both families is the proof-of-work hash (SHA-256d of the 80 bytes; the v2 pipeline for Knots) — so there is no separate pow_hash;
  • Target with compact bits decoding, and check_pow with the powLimit semantics siding uses — bits is pinned to powLimit’s compact form and never retargets (SPEC 3, SPEC 4 step 1);
  • the BIP-325 block-data preimage over that family’s serialisation (signet), so sidestr-core signs and verifies blocks without knowing the layout;
  • the version-bit-31 rule: bit 31 selects the family, so a stock header with it set and a v2 header without it are both refused at decode;
  • the fork activation constants of SPEC 3.2 (fork);
  • with core, the two families as sidestr-core sees them (Blake2bV2, family::Stock).

§Where this port departs from the reference

  • The genesis is judged under the family’s rules. The reference applies block 0 on its hash; through sidestr-core’s StateOf::from_genesis a typed v2 genesis is held to knots:rule-header-v2-from-fork, -height and -flags-reserved, the proof of work and the signature before its hash is compared to the document, so a header the decoder would refuse cannot enter as a struct either (tests/audit_regressions.rs).
  • Version bit 31 is refused on every stock path. StockHeader::decode refuses the bytes; family::Stock reports the version as the kernel types it (i32le, so bit 31 is negative) and btc:rule-header-version refuses a typed header, as the kernel does on the same block.
  • Compact targets Bitcoin Core rejects (negative, overflow) are rejected by Target::from_compact where the kernel is lenient — unreachable on a valid sidestr chain, where bits is pinned to powLimit.
  • A mirror’s record framing is checked on replay (sidestr-core’s blockfile::read_block): the [u32 height][u32 size] prefix of every record must agree with the index entry, and the entry must lie within the file.

§The v2 header, field by field

The 164-byte Knots v2 header keeps the classic 80-byte prefix (version with bit 31 set, previous hash, merkle root, time on wire, bits, nonce) and appends 84 bytes: two more nonces, a 128-bit extranonce, a time offset, the committed transaction count, a flags byte (ASIC profile in bits 0–1, time offset in bit 2, bits 6–7 reserved), the XOR-mask clear count, a 128-bit XOR key, the committed height, and a 32-byte merge-mining hook. The table with offsets is on Blake2bV2Header. Its hash is not a hash of the bytes but a commitment tree: two BIP-340 tagged-hash rounds over the fields, then two BLAKE2b-256 rounds whose second input layout the ASIC profile selects, then an XOR mask derived from the key (see Blake2bV2Header::hash).

§The signet preimage

Siding’s blockData hashes the first 72 header bytes — version, previous hash, merkle root, time on wire — with the merkle root recomputed over the coinbase stripped of its solution. Those 72 bytes have the same layout in both families, so Header::block_data is one function.

§Example

use sidestr_header::{Header, HeaderFamily, Target};

// sidestr:dreamlab block 0, beside tbtc4: stock family.
let bytes = hex::decode(
    "0000002000000000000000000000000000000000000000000000000000000000000000003a87d59ecf60ab58ee75948cc39d1bb44ac4285747e64b5a1e7a960d37764cb40e67b26affff7f2002000000",
).unwrap();
let header = HeaderFamily::Stock.decode(&bytes).unwrap();
assert_eq!(header.hash().to_string(),
           "4db37517728bd509c0cb96ee5a2e3e2a77f9e965a092e9f67948b413d453dbc0");

// SPEC 4 step 1 against the chain document's powLimit.
let pow_limit = Target::from_hex(
    "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
assert!(header.check_pow(&pow_limit).is_ok());

// The same 80 bytes are not a v2 header, and 81 bytes are not a header.
assert!(HeaderFamily::Blake2bV2.decode(&bytes).is_err());
assert!(HeaderFamily::Stock.decode(&bytes[..79]).is_err());

// Re-encoding is the identity.
assert_eq!(header.encode().as_ref(), bytes.as_slice());

§Provenance

  • codec/pow/knots-header-v2.js, codec/pow/blake2b.js, codec/hash.js, codec/codec.js, codec/headers.js, codec/overlays/knots-blake2b.js (the overlay’s header and block checks), schema/overlays/knots-blake2b.jsonld of bitcoin-desktop/schema (AGPL-3.0), at commit b8cbf6337c7450fe14ddc5bce00c7280059aab5d.
  • siding/lib/block.mjs, siding/lib/parents.mjs, siding/lib/chain.mjs, siding/lib/overlay.mjs (blake2bHeight: 0, unifiedSighashParam), SPEC.md §3, §3.2, §4 of sidestr/spec (AGPL-3.0, Melvin Carvalho), at commit 2de40bdac4cba01be0864156a553d8287c22e279.
  • Test vectors: Knots’ own block_header_v2.json and real fork headers captured from a Knots 29.4.1 node on 2026-09-05, both carried by the schema kernel; siding’s blockData on the live sidestr:dreamlab block 0 and on kernel-hashed v2 headers, computed with the JavaScript engine as the oracle; and the live sidestr:txbt4-siding chain (229 blocks as of 2026-09-22) as the oracle for the whole BLAKE2b family through sidestr-core (tests/core_family.rs).

Re-exports§

pub use family::Blake2bV2;

Modules§

family
sidestr_core::HeaderFamily for both header types (feature core): the seam through which sidestr-core’s rules, state and chain run over this crate’s headers without knowing their layout.
fork
The two BLAKE2b forks’ activation points, from SPEC 3.2’s parents table.
hash
The hash primitives both families are built from, as thin wrappers over RustCrypto: SHA-256, SHA-256d, the BIP-340 tagged hash and BLAKE2b-256.
signet
BIP-325’s block data over a sidestr header, family-agnostic.

Structs§

Blake2bV2Header
The 164-byte header used from the BLAKE2b fork height — on a sidestr chain beside a BLAKE2b parent, from height 0 (siding/lib/overlay.mjs: blake2bHeight: 0).
BlockHash
A block hash in display order — the byte order bitcoind prints and a chain document’s genesisHash uses; read as a big-endian number it compares directly against a Target.
EncodedHeader
A header’s wire bytes, sized for the larger family; as_ref() yields exactly the family’s bytes.
StockHeader
The 80-byte Bitcoin block header (btc:BlockHeader in the kernel’s schema/core.jsonld).
Target
A 256-bit proof-of-work target, big-endian.
V2HashStages
Every intermediate of the v2 pipeline, for tests and debugging — the kernel’s hashHeaderV2Detailed. Digests are in the byte order the pipeline feeds them onward; hash is the display-order block hash.

Enums§

Error
Why a header could not be decoded or does not satisfy a rule.
Header
A header of either family, with the operations a sidestr validator needs dispatched to the right one.
HeaderFamily
The two header families of SPEC 3.2, named after the header they carry.

Constants§

BLAKE2B_V2_POW_HASH_NAME
The kernel’s name for this proof-of-work hash (POW_HASH_NAME).
FLAG_ASIC_PROFILE_MASK
flags bits 0–1 select the ASIC input layout of the second BLAKE2b round.
FLAG_RESERVED_MASK
flags bits 6–7 are reserved for future hardforks and must be zero (knots:rule-header-flags-reserved).
FLAG_USE_TIME_OFFSET
flags bit 2: the consensus time is time_on_wire + time_offset (FLAG_USE_TIME_OFFSET in codec/pow/knots-header-v2.js).
SHA256D_POW_HASH_NAME
The kernel’s name for the stock family’s proof-of-work hash.
VERSION_HEADER_V2_FLAG
Version bit 31: set on every v2 header, clear on every stock header (VERSION_HEADER_V2_FLAG in codec/pow/knots-header-v2.js; the kernel’s structVariants selects the v2 struct on it).

Functions§

check_pow
SPEC 4 step 1, “the header meets powLimit. No difficulty adjustment, no minimum-difficulty window”, as siding and the kernel enforce it together: