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 alias | chain | headers, proof of work | this crate |
|---|---|---|---|
btc, tbtc4 | Bitcoin mainnet, testnet4 | stock 80 bytes, SHA-256d | StockHeader |
xbt, txbt4 | Bitcoin Knots’ BLAKE2b fork of each | 164-byte v2, BLAKE2b | Blake2bV2Header |
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/encodepair; 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 separatepow_hash;Targetwith compactbitsdecoding, andcheck_powwith thepowLimitsemantics siding uses —bitsis pinned topowLimit’s compact form and never retargets (SPEC 3, SPEC 4 step 1);- the BIP-325 block-data preimage over that family’s serialisation
(
signet), sosidestr-coresigns 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 assidestr-coresees 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’sStateOf::from_genesisa typed v2 genesis is held toknots:rule-header-v2-from-fork,-heightand-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::decoderefuses the bytes;family::Stockreports the version as the kernel types it (i32le, so bit 31 is negative) andbtc:rule-header-versionrefuses a typed header, as the kernel does on the same block. - Compact targets Bitcoin Core rejects (negative, overflow) are
rejected by
Target::from_compactwhere the kernel is lenient — unreachable on a valid sidestr chain, wherebitsis pinned topowLimit. - A mirror’s record framing is checked on replay (
sidestr-core’sblockfile::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.jsonldof bitcoin-desktop/schema (AGPL-3.0), at commitb8cbf6337c7450fe14ddc5bce00c7280059aab5d.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 commit2de40bdac4cba01be0864156a553d8287c22e279.- Test vectors: Knots’ own
block_header_v2.jsonand real fork headers captured from a Knots 29.4.1 node on 2026-09-05, both carried by the schema kernel; siding’sblockDataon the livesidestr:dreamlabblock 0 and on kernel-hashed v2 headers, computed with the JavaScript engine as the oracle; and the livesidestr:txbt4-sidingchain (229 blocks as of 2026-09-22) as the oracle for the whole BLAKE2b family throughsidestr-core(tests/core_family.rs).
Re-exports§
pub use family::Blake2bV2;
Modules§
- family
sidestr_core::HeaderFamilyfor both header types (featurecore): the seam through whichsidestr-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§
- Blake2b
V2Header - 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). - Block
Hash - A block hash in display order — the byte order bitcoind prints and a
chain document’s
genesisHashuses; read as a big-endian number it compares directly against aTarget. - Encoded
Header - A header’s wire bytes, sized for the larger family;
as_ref()yields exactly the family’s bytes. - Stock
Header - The 80-byte Bitcoin block header (
btc:BlockHeaderin the kernel’sschema/core.jsonld). - Target
- A 256-bit proof-of-work target, big-endian.
- V2Hash
Stages - 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;hashis 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.
- Header
Family - The two header families of SPEC 3.2, named after the header they carry.
Constants§
- BLAK
E2B_ V2_ POW_ HASH_ NAME - The kernel’s name for this proof-of-work hash (
POW_HASH_NAME). - FLAG_
ASIC_ PROFILE_ MASK flagsbits 0–1 select the ASIC input layout of the second BLAKE2b round.- FLAG_
RESERVED_ MASK flagsbits 6–7 are reserved for future hardforks and must be zero (knots:rule-header-flags-reserved).- FLAG_
USE_ TIME_ OFFSET flagsbit 2: the consensus time istime_on_wire + time_offset(FLAG_USE_TIME_OFFSETincodec/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_FLAGincodec/pow/knots-header-v2.js; the kernel’sstructVariantsselects 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: