provsum
Fixed-size digests of error provenance, for checking that a Rust port raises the same errors for the same reasons as the code it replaces.
Construction
Every error-origin, wrap, and pick site in the source gets a 16-byte GUID, carried verbatim across both implementations. Each GUID is hashed to a nonzero point in F_p (p = 2^61−1) per lane (two lanes). The digest of an error is its N[X] provenance polynomial evaluated at those points:
Site is the GUID's 16 bytes in canonical order, not a native-endian integer.
Point derivation reads its two consecutive 8-byte chunks as little-endian, and
digest serialization is little-endian too, so neither operation depends on the
host architecture. A byte-array literal therefore produces the same result on
every supported platform. When starting from textual UUID syntax, use the
parser's canonical/RFC byte sequence in every language; in particular, do not
mix a Windows GUID's field-wise little-endian representation with RFC bytes.
| operation | polynomial | digest |
|---|---|---|
Digest::origin(site) |
x_site |
r_site |
d.wrap(site) |
x_site · d |
r_site · d |
a.merge(b) |
a + b |
a + b |
Digest::pick(site, [d0,d1..]) |
Σ x_{site,i} · d_i |
Σ r_{site,i} · d_i |
pick records an ordered choice: slot 0 is the error that was returned, later
slots were considered and discarded. Order enters the commutative semiring
through per-slot variables, not through the operators, so swapping which error
wins changes the polynomial. Plain wrap order is not recorded (x·y = y·x);
that is intentional — a port that restructures control flow but preserves
which sites contribute and how deeply should digest identically.
Digests are 18 bytes: two 8-byte lanes plus a 2-byte depth bound. Equality
compares lanes only. Two digests of distinct polynomials of depth ≤ d collide
with probability ≤ (d/(p−1))² ≈ 2^(2·log2 d − 122). This is a non-adversarial
construction: it assumes nobody is choosing GUIDs or points to force a
collision. Digest::log2_collision_bound() reports the bound for a given depth.
Diagnostics
provsum::poly::Poly is the expanded polynomial with the same API.
Poly::digest() evaluates it at the same points, so poly.digest() == streaming_digest is a testable invariant, and Poly::diff names the monomials
that differ between two implementations. Keep it behind a debug flag; its size
is the number of distinct derivations, which is fine at a few thousand sites
and shallow depth and unbounded in general.
Cross-language
python/provsum.py is a reference implementation. cargo run --example vectors and python3 python/provsum.py must print identical output;
tests/vectors.rs pins those values. Point derivation is a splitmix64 finalizer
over (seed, lane, slot) then the two GUID halves, rejection-sampled to be
nonzero mod p — deliberately dependency-free so it is trivial to mirror.
Usage sketch
use ;
const PARSE: Site = Site; // real GUIDs in practice
const READ: Site = Site;
const RETRY: Site = Site;
The Python side does the same with Digest.origin(READ).wrap(PARSE) etc.;
the differential harness runs both on the same inputs and compares
prov.to_bytes().