sidestr-core 0.2.2

sidestr user-activated sidechains beside a Bitcoin-family parent: the chain document, the parents table, signed blocks generic over the header family (BIP-325 challenge, no subsidy), the peg-in claim and peg-out burn rules, Knots' unified sighash, the block file and an in-memory validating chain. A port of siding by Melvin Carvalho.
Documentation
sidestr-core-0.2.2 has been yanked.

sidestr-core

sidestr user-activated sidechains beside a Bitcoin-family parent, in Rust: the chain document, the parents table, signed blocks in either header family (a BIP 325 challenge, no subsidy), the peg-in claim and peg-out burn rules, the block file, and an in-memory validating chain with a producer's mempool.

A sidestr chain runs beside a Bitcoin-family chain with Bitcoin's transaction rules, blocks that are valid because they are signed rather than mined, no subsidy, and every coin on it a coin locked on the parent. Signers decide the order of blocks; they do not decide the rules.

[dependencies]
sidestr-core = "0.2"
# and, for a chain beside a BLAKE2b parent (xbt, txbt4):
sidestr-header = "0.2"

The rules, state and chain are generic over the header family (sidestr_core::block::HeaderFamily): State / Chain are the stock instantiation (btc, tbtc4), and StateOf<Blake2bV2> / ChainOf<Blake2bV2> with sidestr_header::Blake2bV2 validate a chain beside Knots' BLAKE2b fork. The dependency edge runs from sidestr-header to this crate, never back.

Attribution

This crate is a port of siding, the reference implementation of sidestr by Melvin Carvalho — github.com/sidestr/spec, AGPL-3.0 — ported from commit 2de40bdac4cba01be0864156a553d8287c22e279 (siding/lib/{parents,block,chain,overlay,marker,records,address,checkpoint}.mjs, bin/siding.mjs, and the tests in siding/test/). Two parts come from the engine siding loads, by the same author and under the same licence:

  • the block, header and spending checks, and Knots' unified sighash, from bitcoin-desktop/schema (codec/blocks.js, codec/headers.js, codec/interpreter.js sighashUnified, schema/validate.jsonld);
  • the block file format and the chain state machine from bitcoin-blake/blaketestnode (lib/blockfile.mjs, lib/node.mjs).

SPEC.md in the sidestr repository is the design; the crate documentation cites its sections, and every ported function names its original.

What changed in the port

  • Bitcoin's consensus serialisation, hashes, merkle roots, the taproot sighash and BIP-340 Schnorr come from rust-bitcoin and its secp256k1; bech32 from the bech32 crate. Nothing cryptographic is hand-rolled.
  • Script verification fails closed: taproot key-path spends are verified, every other script type is refused rather than skipped. The reference lets a witness version it cannot verify through.
  • The solution's witness decoder refuses truncation, trailing bytes, non-minimal sizes and more than 256 items; the reference reads what it can.
  • The mempool judges a spend's sighash type by the family's block rule (unified only beside a BLAKE2b parent); the reference mempool accepts unified on every family while its block rule does not.
  • Claim and burn records commit only when a block is applied, not while it is being judged.
  • Blocks are a pure function of their inputs and the key: BIP 340 auxiliary randomness is zero for every block, as siding sets it for the genesis.
  • A document naming the assets, pool or evm rules is refused, because this version does not carry them.
  • Level 2 carries the pure parts of federation.mjs and not the round (that is sidestr-round): the script path is verified for exactly the multi_a(k, …) leaf, an unknown leaf version is refused rather than skipped, and template_id names what signers authorise separately from the sealed hash (ADR-2101 review).
  • No I/O in the rules: the filesystem and the clock are behind the std feature (blockfile, chain).
  • The genesis is judged, not trusted: from_genesis runs every rule that applies at height 0 (the family's header rules, the signature against the challenge, the pegs as the one subsidy, sidestr:rule-genesis-document) before the hash is held to the document's genesisHash; siding applies block 0 on the hash alone. There is no trusted import.
  • A stock header with version bit 31 set is refused at decode and, on the typed path, by btc:rule-header-version — the rule the kernel names, which reads a stock version as i32le.
  • A mirror's blocks.dat record framing ([u32 height][u32 size]) is held to blocks.json and to the file's length on every read (Error::BlockFile).
  • Markers are written with a canonical push (OP_PUSHDATA1 above 75 bytes) and read exactly as siding's opReturnData reads them — a bare length byte or an OP_PUSHDATA1 prefix, minimal or not — because that is the burn rule's grammar and a burn a reference wallet wrote must be paid. Their text is decoded as siding's TextDecoder decodes it, one leading UTF-8 byte-order mark dropped, at exactly the readers that text-decode there (burns, claims, the peg-in remainder's hex-form decision, records) and not at the two compared as bytes (the parent peg-out record, the checkpoint) — so a BOM-led burn is recorded, or refused, alike. tests/audit_regressions_records.rs holds every marker case and every block of an audit corpus to identical derived lists in both engines.

Status — 0.2.2

Level 1 (one signer), both header families, end to end: genesis from the document, block production, validation, the mempool policy, the block file. Proven against the reference:

  • the genesis of a throwaway chain rebuilt from its document and key is byte-identical to the one siding wrote (tests/oracle.rs);
  • the estate's sealed sidestr:dreamlab genesis replays to its documented hash;
  • blocks produced here are accepted by siding and blocks siding produces are accepted here (tests/interop.rs, needs the reference checkouts);
  • with sidestr-header's Blake2bV2, Melvin Carvalho's live sidestr:txbt4-siding chain replays from its genesis to its tip with every rule on, spends signed with Knots' unified sighash included (sidestr-header/tests/core_family.rs);
  • a 2-of-3 federation's challenge, its genesis and two blocks sealed by three different pairs are byte-identical to siding's (tests/federation.rs, fixtures/fedtest), every subset and both parities are property-tested (tests/federation_prop.rs), and Bitcoin Core's interpreter agrees with the multi_a verifier on 161 differential cases (cargo test --features consensus-oracle);
  • the five counter-examples of the independent 0.2 audit (an unsigned genesis accepted on its hash, a stock block with version bit 31 replayed, a corrupt record prefix replayed, a u32 overflow in claimable, and the typed v2 genesis bypassing the family rules) are fixed and pinned as regressions (tests/audit_regressions.rs, both crates).

0.2 made the rules, state and chain generic over HeaderFamily (with an associated header and block type), added Knots' unified sighash and the family's own rules, tightened the witness decoder, and added level 2's pure parts (federation: NUMS key, leaf, partial signatures, witness assembly, sealing, the multi_a verifier, template_id, Chain::open_sealed). State, Chain and every 0.1 name keep their meaning as the stock instantiation. The parent view (parent) is behind two traits — the chain read-only, the peg wallet — with every decision pure (peg-ins found in decoded blocks, what to claim and lock, the burn payment and the checkpoint as send outputs, reconciliation) and Bitcoin Core's JSON-RPC as the one implementation behind the rpc feature; tests/parent_live.rs (ignored, SIDESTR_PARENT_RPC) finds the estate's peg-wallet funding on a testnet4 node without sending anything.

Elsewhere in the stack: the level-2 co-signing round is sidestr-round, tips and transactions over Nostr are sidestr-nostr, spending is sidestr-wallet. Not yet: the assets and pool rules, a full script interpreter, and the Byzantine-tolerant consensus protocol above the signature (ADR-2101 review), which is a later crate.

Running the checks

cargo test                                   # unit, ported siding suites, fixtures, doctests
cargo test --features consensus-oracle       # plus Bitcoin Core's interpreter as a differential oracle (needs a C++ toolchain)
SIDESTR_PARENT_RPC=http://<node>:48332/ cargo test --features rpc --test parent_live -- --ignored   # a testnet4 node, read-only
cargo run --example siding -- replay --chain fixtures/dreamlab/chain.json --dir <dir with blocks.dat>
cargo run --example siding -- genesis --chain chain.json --dir state --key-file signer.key
# the JS interop test, with the reference checkouts:
SIDESTR_SIDING=<sidestr/spec>/siding SCHEMA=<bitcoin-desktop/schema> BLAKETESTNODE=<bitcoin-blake/blaketestnode> \
  cargo test --test interop
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps
cargo clippy --all-targets -- -D warnings && cargo fmt --check

fixtures/trial is a throwaway chain with its disposable signer key, carried for the byte-for-byte test. fixtures/dreamlab is a sealed genesis without its key. Keys are files, never arguments; nothing here prints one.

Licence

AGPL-3.0-only, as a derivative work of siding. See LICENSE. This crate is not dual-licensed: a crate that links it is AGPL-3.0 in effect and should say so.