Files
zeekay e72a706541 The Q-chain in Rust: a stamp per transaction, a quorum per block, on a disk
Q is the post-quantum lane of the parallel-witness finality model (LP-020), and
it was unported in both non-Go runtimes. This is the port, against the node's
own seam: `lux_node::vm` is named, never restated, and an id is
`lux_consensus::finality::Id`, so a block this chain builds is named the way the
node that certifies it names it.

Two signatures live here and they are not the same thing. A transaction stamp is
one validator's ML-DSA-65 signature over one transaction, bound to the moment it
was made, and it rides beside the transaction on the wire. A round witness is a
quorum of the committee signing one block, and it is deliberately NOT a field of
the block: the block id is the hash of the block's own bytes, so a signature
inside it would give two honest nodes two ids for one block.

The wire is canonical or nothing. Parse re-encodes what it decoded and refuses
anything that does not come back byte-identical, because a ZAP message declares
its own size and its own root offset — padding after the content, a relocated
root and a root pointed at the header all decode to the same block under
different names.

The chain is on a disk, not in a map. `Store::commit` takes the whole batch, so
the block, its height index entry and the tip pointer move together or not at
all; `store::Log` extends that across a crash, dropping a torn tail whole. A
read that FAILED is reported as a failure rather than as an empty chain, which
is what keeps one transient error from committing genesis over a live tip.

Q-Chain sells no blockspace: `issue_tx` refuses every amount (LP-0130 §6), and
the chain advances only through consensus-internal aggregation.

Held to the reference, not to itself. `tests/vectors/q_reference.json` is five
vectors whose every byte came out of the Go chain — its encoder's wires and its
parser's verdicts, ids included — and `src/wire.rs` asserts the Go encoder's own
bytes for a preimage and an unsigned envelope as constants.
`src/bin/conformance.rs` answers the cross-language corpus in the runner's own
format; over the four Q vectors, Go and Rust agree on every compared field,
including this chain's genesis id.

118 tests, no stubs. Two places are narrower than Go and say so rather than
faking: ML-DSA-44 and -87 cannot be signed under here because libluxcrypto
exposes only -65 (asking is an error, never a silent downgrade), and batch
verification is CPU-parallel because the accelerator's ML-DSA kernel is
reachable from Go only.

`/bin/` in .gitignore is anchored to the repo root: unanchored it also swallowed
`chains/rust/*/src/bin/`, which is where the differential's Rust evaluators live.
2026-09-04 20:41:35 -07:00

22 lines
981 B
Rust

// SPDX-License-Identifier: BSD-3-Clause-Eco
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
//! Re-emit the loader path `libluxcrypto` was resolved at.
//!
//! `cargo:rustc-link-arg` reaches only the binaries of the crate that emitted
//! it, so the rpath `lux-pq` set for its own tests does not reach this chain's
//! test binaries. `links` metadata does reach here — `DEP_LUXPQ_LIB_DIR` is
//! `lux-pq` passing on what the ABI crate resolved — so the path is found once,
//! down there, and repeated here rather than searched for a second time.
//!
//! Nothing is emitted when the variable is absent: the library may be on the
//! system loader path already, and inventing a directory would only be a second
//! place to look.
fn main() {
if let Some(dir) = std::env::var_os("DEP_LUXPQ_LIB_DIR") {
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", dir.to_string_lossy());
}
println!("cargo:rerun-if-env-changed=DEP_LUXPQ_LIB_DIR");
}