Verification
Every round can be checked from public data. These tools run the same checks in your browser, and the scripts below let you reproduce any derivation with nothing but SHA-256.
Select a round and run the checks. Each one recomputes a value from public inputs and compares it with the record. Rounds that are still sealed or scheduled can't be checked past the snapshot, because their outcome doesn't exist yet.
Round record
Verification
SHA-256(canonical_json(snapshot)) = root
test vector: SHA-256("oomi:test-vector:R-0001")
SHA-256(domain‖epoch‖identity‖root‖rule‖entropy)
per-trait draws over rule table oomi-rules/0.3
Example rounds use public test-vector entropy so anyone can reproduce them. A live round adds checks for the QRNG receipt hash, the commit H(q) = qc, and the drand signature for round B.
About these rounds
SHA-256("oomi:test-vector:<round>")) so anyone can reproduce them. They are not physical quantum measurements and not on-chain records.Both scripts are complete and use only the standard library. Run either one and you should get the pinned seed 0c583ec8…f908.
import hashlib, struct
DOMAIN = "oomi:mutation:v1"
RULE = "oomi-rules/0.3"
TRAITS = [ # order defines trait index i
("aura", [("Signal Cyan", 34), ("Deep Plasma", 26), ("Dusk Violet", 18), ("Tidal Teal", 14), ("Halo Gold", 8)]),
("earGlow", [("Dim", 22), ("Soft", 34), ("Bright", 28), ("Radiant", 16)]),
("droplets", [("Still", 30), ("Drifting", 38), ("Orbiting", 20), ("Swarming", 12)]),
("pattern", [("Marbled", 36), ("Rippled", 26), ("Crystalline", 22), ("Static", 16)]),
("temperament", [("Curious", 30), ("Playful", 24), ("Serene", 20), ("Mischievous", 16), ("Focused", 10)]),
]
def lp(s: str) -> bytes:
b = s.encode()
return struct.pack(">I", len(b)) + b
def seed(epoch: int, identity: str, root_hex: str, entropy_hex: str, rule: str = RULE) -> bytes:
pre = lp(DOMAIN) + struct.pack(">I", epoch) + lp(identity) + bytes.fromhex(root_hex) + lp(rule) + bytes.fromhex(entropy_hex)
return hashlib.sha256(pre).digest()
def derive(epoch, identity, root_hex, entropy_hex):
s = seed(epoch, identity, root_hex, entropy_hex)
out = {}
for i, (name, opts) in enumerate(TRAITS):
u = struct.unpack(">I", hashlib.sha256(s + struct.pack(">I", i)).digest()[:4])[0] / 2**32
total, acc = sum(w for _, w in opts), 0.0
for value, w in opts:
acc += w / total
if u < acc:
break
out[name] = value
return s.hex(), out
if __name__ == "__main__":
h = lambda t: hashlib.sha256(t.encode()).hexdigest()
print(derive(7, "oomi:flagship", h("snapshot"), h("entropy")))
import { createHash } from "node:crypto";
const sha = (b) => createHash("sha256").update(b).digest();
const u32 = (n) => { const b = Buffer.alloc(4); b.writeUInt32BE(n); return b; };
const lp = (s) => { const b = Buffer.from(s, "utf8"); return Buffer.concat([u32(b.length), b]); };
const TRAITS = [
["aura", [["Signal Cyan", 34], ["Deep Plasma", 26], ["Dusk Violet", 18], ["Tidal Teal", 14], ["Halo Gold", 8]]],
["earGlow", [["Dim", 22], ["Soft", 34], ["Bright", 28], ["Radiant", 16]]],
["droplets", [["Still", 30], ["Drifting", 38], ["Orbiting", 20], ["Swarming", 12]]],
["pattern", [["Marbled", 36], ["Rippled", 26], ["Crystalline", 22], ["Static", 16]]],
["temperament", [["Curious", 30], ["Playful", 24], ["Serene", 20], ["Mischievous", 16], ["Focused", 10]]],
];
export function derive(epoch, identity, rootHex, entropyHex, rule = "oomi-rules/0.3") {
const seed = sha(Buffer.concat([lp("oomi:mutation:v1"), u32(epoch), lp(identity), Buffer.from(rootHex, "hex"), lp(rule), Buffer.from(entropyHex, "hex")]));
const traits = {};
TRAITS.forEach(([name, opts], i) => {
const u = sha(Buffer.concat([seed, u32(i)])).readUInt32BE(0) / 2 ** 32;
const total = opts.reduce((s, [, w]) => s + w, 0);
let acc = 0;
traits[name] = opts.find(([, w]) => (acc += w / total) > u)?.[0] ?? opts.at(-1)[0];
});
return { seed: seed.toString("hex"), traits };
}
const h = (t) => sha(Buffer.from(t)).toString("hex");
console.log(derive(7, "oomi:flagship", h("snapshot"), h("entropy")));
Download: oomi_derive.py · derive.mjs
Any implementation (the planned Rust program, a third-party checker, these scripts) must match every row.
| Vector | Epoch | Seed | Traits |
|---|---|---|---|
| pinned | 7 | 0c583ec8ee6228344fb07a2fdd0508d70062a5c58289826043a96f2999b2f908 | Halo Gold · Soft · Drifting · Marbled · Mischievous |
| R-0001 | 1 | 77de4fc4b3e68107c86aeb4e42ed2608cf97fa1e9b37e53f15d46c4ec782f285 | Signal Cyan · Dim · Still · Marbled · Curious |
| R-0002 | 2 | 8512c7de8d7919d8d5c21ff0fb9fe49c1a4267472c172c16aef16bff2d8f9415 | Deep Plasma · Bright · Drifting · Static · Playful |
| R-0003 | 3 | 49b2a0ddbd8da904716ccd34db89bb6d37a86444cb3b4563df03e03ec4a51294 | Dusk Violet · Bright · Swarming · Marbled · Curious |
| R-0004 | 4 | ac9440b0d65566aa76c99bbc9c1807d9911d7df2742934b5a8d018e77d2b1954 | Signal Cyan · Soft · Still · Rippled · Mischievous |
| R-0005 | 5 | 213f4932b2c9abd75164059ebb7d233578936c94ccbaa1744bc0aee2f8dfd5c3 | Deep Plasma · Radiant · Still · Crystalline · Curious |
| R-0006 | 6 | cfc3bba6cf06d3ad4f4e0ed4986590ffd5126818b39dc05779f8d5a2d6025e33 | Halo Gold · Bright · Drifting · Rippled · Curious |
A hash turns any input into 256 bits, and changing a single character flips about half of them. That's why no one can nudge an input toward a trait they want.
SHA-256(A)
SHA-256(B)
Bits that differ
139/256
Change one character and roughly half the bits flip. That's why nobody can nudge an input towards a trait they want.
| Trait | from A | from B |
|---|---|---|
| Aura | Signal Cyan | Tidal Teal |
| Ear glow | Soft | Soft |
| Droplets | Still | Drifting |
| Pattern | Static | Static |
| Temperament | Curious | Curious |
| Check | How |
|---|---|
| Snapshot published = committed | SHA-256(snapshot.json) equals Round.snapshot_root on-chain |
| Commit order | commit_snapshot slot < commit_qrng slot < reveal slot |
| QRNG commitment | SHA-256("oomi:qrng-commit:v1" ‖ round_id ‖ q) = Round.qrng_commit |
| Beacon round | B = first drand round with t ≥ time(S) + Δ; verify σB with drand's public key |
| Entropy | SHA-256("oomi:entropy:v1" ‖ round_id ‖ q ‖ σB) = Round.entropy |
| Outcomes | derive() for every identity; Merkle root = Outcomes.outcomes_root |