mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
chains/schema/qchain.zap says what a Q block and a Q transaction are — the same offsets the chain stated in Rust, and the same ones the Go chain writes. zapgen turns that into the accessors, so the block and the two transaction shapes are no longer spelled out twice. The runtime goes with them. chains/rust/zap holds one emitted copy for every chain in this directory, so the reader a chain calls is the reader the Go and C++ chains call; quantumvm's own 896-line copy is gone, and xvm's byte- identical twin will follow. What is left in wire.rs is what a Q block MEANS: the transaction set a length list partitions, the bound on how much a peer can make this node allocate, and canonical-or-nothing — re-serializing what was decoded and refusing anything that does not come back the same bytes. The bytes did not move. The two Go-captured golden wires still match, and tests/corpus_bytes.rs now writes back every Q block in the shared corpus and compares it to the hex the Go chain generated: 12 blocks, byte for byte. 110 unit tests, the corpus differential and the byte test all pass. One verdict moved and it moved tighter. A reader that knows an element is four bytes wide now refuses a declared count no buffer that size could hold, so a forged length list is refused where it is read instead of at the allocation behind it. The allocation bound is still there and still catches the counts that fit the run but not the blob; the test now names both.
73 lines
2.6 KiB
Rust
73 lines
2.6 KiB
Rust
// SPDX-License-Identifier: BSD-3-Clause-Eco
|
|
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
|
|
//! Every Q vector in the shared corpus, written back byte for byte.
|
|
//!
|
|
//! The corpus was generated by the Go chain, so a vector's hex is what Go
|
|
//! wrote. Reading one and writing it again has to reproduce it exactly: this
|
|
//! chain's writer and Go's are the same writer or they are not. That is what
|
|
//! makes the corpus a check on the WRITE path and not only on the read path —
|
|
//! a reader can agree about every field of bytes it could never have produced.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use lux_quantumvm::wire;
|
|
|
|
fn corpus() -> String {
|
|
let p = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("../../../conformance/corpus/vectors.tsv");
|
|
std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("{}: {e}", p.display()))
|
|
}
|
|
|
|
fn unhex(s: &str) -> Option<Vec<u8>> {
|
|
if s == "-" || s.len() % 2 != 0 {
|
|
return None;
|
|
}
|
|
(0..s.len() / 2)
|
|
.map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).ok())
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn every_q_vector_is_written_back_exactly_as_it_arrived() {
|
|
let text = corpus();
|
|
let mut blocks = 0;
|
|
let mut txs = 0;
|
|
for line in text.lines() {
|
|
if line.is_empty() || line.starts_with('#') {
|
|
continue;
|
|
}
|
|
let f: Vec<&str> = line.split('\t').collect();
|
|
if f.len() < 5 || f[0] != "V" || f[2] != "Q" {
|
|
continue;
|
|
}
|
|
let (id, op) = (f[1], f[3]);
|
|
let Some(bytes) = unhex(f[4]) else { continue };
|
|
|
|
match op {
|
|
"block" => {
|
|
// parse_block refuses anything that does not come back
|
|
// byte-identical, so a vector that parses IS one this chain
|
|
// writes the same way. Say it here anyway, in the open.
|
|
let Ok(b) = wire::parse_block(&bytes) else {
|
|
continue;
|
|
};
|
|
assert_eq!(b.bytes(), &bytes[..], "{id}: block bytes moved");
|
|
assert_eq!(wire::block_bytes(&b), bytes, "{id}: rebuild moved");
|
|
blocks += 1;
|
|
}
|
|
"tx" => {
|
|
let Ok(tx) = wire::parse_tx_envelope(&bytes) else {
|
|
continue;
|
|
};
|
|
assert_eq!(wire::tx_envelope(&tx), bytes, "{id}: envelope moved");
|
|
txs += 1;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
// A corpus that stopped naming Q vectors would pass every assertion above.
|
|
assert!(blocks > 0, "no Q block vector was read back");
|
|
println!("{blocks} Q blocks and {txs} Q transactions written back byte for byte");
|
|
}
|