soft3/crate/src/chain/pool/tests.rs

//! chain.md ยง11 / chain-conformance ยง12: the pool's formats.

use super::*;
use crate::genesis::token_of;

#[test]
fn wide_digest_extends_hemera_and_is_canonical() {
    for x in [&b""[..], b"pussy", &[7u8; 200][..]] {
        let w = wide(x);
        assert_eq!(&w[..32], hemera::hash(x).as_bytes(), "the first four limbs are hemera(x)");
        assert!(canonical(&w));
        assert_eq!(wide_secret(x), w, "constant-time variant, same digest");
    }
    assert!(!canonical(&[0xff; 8]));
    assert!(canonical(&(P - 1).to_le_bytes()));
    assert!(!canonical(&P.to_le_bytes()), "p aliases 0 and is refused");
}

#[test]
fn wide_digest_is_two_squeezes_of_the_xof() {
    let x = b"soft3/pool/test\0";
    let mut xof = {
        let mut h = hemera::Hasher::new();
        h.update(x);
        h.finalize_xof()
    };
    let mut first = [0u8; 32];
    let mut second = [0u8; 32];
    xof.fill(&mut first);
    xof.fill(&mut second);
    let w = wide(x);
    assert_eq!(&w[..32], &first);
    assert_eq!(&w[32..], &second);
}

#[test]
fn tree_node_is_the_tagged_wide_digest() {
    let (a, b) = (wide(b"a"), wide(b"b"));
    assert_eq!(node(&a, &b), wide(&[D_NODE, &a[..], &b[..]].concat()));
    assert_ne!(node(&a, &b), node(&b, &a), "order matters");
    assert!(canonical(&node(&a, &b)));
}

#[test]
fn tree_roots_and_paths() {
    let e = empty_roots();
    assert_eq!(e.len(), 41);
    assert_eq!(e[0], wide(D_EMPTY));
    assert_eq!(e[1], node(&e[0], &e[0]));
    assert_eq!(e[40], node(&e[39], &e[39]));
    assert_eq!(root_of(&[]), e[40]);
    let (_, record, record2, _) = sample();
    let (c0, c1) = (record.commitment(), record2.commitment());
    let mut path: Vec<Wide> = e[..40].to_vec();
    assert_eq!(root_from_path(&c0, 0, &path), Some(root_of(&[c0])));
    let root2 = root_of(&[c0, c1]);
    path[0] = c1;
    assert_eq!(root_from_path(&c0, 0, &path), Some(root2));
    path[0] = c0;
    assert_eq!(root_from_path(&c1, 1, &path), Some(root2), "bit 0 set: sibling on the left");
    assert_ne!(root_from_path(&c1, 0, &path), Some(root2), "wrong side");
    assert_eq!(root_from_path(&c1, 1 << 40, &path), None, "position out of range");
    assert_eq!(root_from_path(&c1, 1, &path[..39]), None);
    assert_ne!(root_of(&[c0, c1]), root_of(&[c1, c0]), "position matters");
}

#[test]
fn record_owner_rho_commitment_nullifier() {
    let (sk, record, record2, _) = sample();
    let bytes = record.encode();
    assert_eq!(bytes.len(), 296);
    assert_eq!(&bytes[..32], &token_of("pussy"));
    assert_eq!(&bytes[32..40], &5u64.to_le_bytes());
    assert_eq!(&bytes[40..104], &owner_of(&sk));
    assert_eq!(&bytes[104..168], &rho_of_deposit(&SAMPLE_DEPOSIT_TXID));
    assert_eq!(Record::decode(&bytes).unwrap(), record);
    assert!(Record::decode(&bytes[..295]).is_err());
    for at in [0, 40, 104, 168, 200, 232] {
        let mut bad = bytes;
        bad[at..at + 8].copy_from_slice(&P.to_le_bytes());
        assert!(Record::decode(&bad).is_err(), "limb >= p at {at}");
    }
    let mut big = bytes;
    big[32..40].copy_from_slice(&u64::MAX.to_le_bytes());
    assert!(Record::decode(&big).is_ok(), "a value is any u64");
    assert_eq!(record.commitment(), wide(&[D_CM, &bytes[..]].concat()));
    assert_eq!(owner_of(&sk), wide(&[D_OWNER, &sk[..]].concat()));
    let nf = nullifier(&SAMPLE_CHAIN, &sk, &record.rho);
    assert_eq!(nf, wide(&[D_NULL, &SAMPLE_CHAIN[..], &sk[..], &record.rho[..]].concat()));
    assert_ne!(nf, nullifier(&[0x45; 32], &sk, &record.rho), "the chain id is in the nullifier");
    assert_eq!(record2.rho, wide(&[D_RHO, &nf[..], &[0u8][..]].concat()));
    assert_ne!(rho_of_output(&nf, 0), rho_of_output(&nf, 1));
    assert_eq!(rho_of_deposit(&SAMPLE_DEPOSIT_TXID), wide(&[D_RHO, &SAMPLE_DEPOSIT_TXID[..], &[0u8][..]].concat()));
    assert_eq!(predicate_of(b"prog"), wide(&[D_PRED, &b"prog"[..]].concat()));
    assert_eq!(turnstile_key(&token_of("pussy")), crate::genesis::hash32(&[D_TURNSTILE, &token_of("pussy")[..]].concat()));
    assert_ne!(turnstile_key(&token_of("pussy")), bbg::state::balance_key(&[0; 32], &token_of("pussy")));
}

#[test]
fn spend_encoding_is_canonical_and_strict() {
    let (_, _, _, spend) = sample();
    let enc = spend.encode().unwrap();
    assert_eq!(enc.len(), 32 + 64 + 1 + 64 + 1 + 64 + 1 + 136 + 1 + 32 + 8 + 128 + 1);
    assert_eq!(enc.len(), 533);
    assert_eq!(SpendPublic::decode(&enc).unwrap(), spend);
    assert_eq!(spend.id().unwrap(), wide(&[D_SPEND, &enc[..]].concat()));
    let mut t = enc.clone();
    t.push(0);
    assert!(SpendPublic::decode(&t).is_err(), "trailing byte");
    assert!(SpendPublic::decode(&enc[..enc.len() - 1]).is_err(), "truncated");
    let mut n5 = enc.clone();
    n5[96] = 5;
    assert!(SpendPublic::decode(&n5).is_err(), "5 inputs");
    let mut n0 = enc.clone();
    n0[96] = 0;
    assert!(SpendPublic::decode(&n0).is_err(), "0 inputs");
    let mut anchor = enc.clone();
    anchor[32..40].copy_from_slice(&P.to_le_bytes());
    assert!(SpendPublic::decode(&anchor).is_err(), "anchor limb >= p");
    let bad = |f: &dyn Fn(&mut SpendPublic), why: &str| {
        let mut s = spend.clone();
        f(&mut s);
        assert!(s.encode().is_err(), "{why}");
    };
    bad(&|s| s.nullifiers.push(s.nullifiers[0]), "duplicate nullifier");
    bad(&|s| {
        s.commitments.push(s.commitments[0]);
        s.predicates.push([0; 64]);
    }, "duplicate commitment");
    bad(&|s| s.links[0].q = s.links[0].p, "p == q");
    bad(&|s| s.notes = vec![vec![0; MAX_NOTE_BYTES + 1]], "note over the cap");
    bad(&|s| s.notes = vec![vec![1], vec![2]], "more notes than outputs");
    bad(&|s| s.predicates.pop().map(|_| ()).unwrap_or(()), "predicate count");
    bad(&|s| s.withdrawal = Some(Withdrawal { to: [0x77; 32], token: token_of("pussy"), amount: 0 }), "withdrawal of 0");
    bad(&|s| s.links[0].payout[..8].copy_from_slice(&u64::MAX.to_le_bytes()), "payout limb >= p");
    bad(&|s| s.nullifiers = vec![], "no input");
    let w = sample_withdrawal();
    let enc2 = w.encode().unwrap();
    assert_eq!(enc2.len(), 533 - 136 + 72 + 2 + 3);
    assert_eq!(SpendPublic::decode(&enc2).unwrap(), w);
    let mut flag = enc2.clone();
    flag[32 + 64 + 1 + 64 + 1 + 64 + 1] = 2;
    assert!(SpendPublic::decode(&flag).is_err(), "withdrawal flag 2");
    assert_eq!((DEPOSIT_BODY_BYTES, DEPOSIT_BYTES), (280, 384));
    assert_eq!((MAX_SPEND_BYTES, MAX_SPEND_TX_BYTES), (9_981, 9_985));
    let full = SpendPublic {
        nullifiers: (1..=4).map(|i| [i; 64]).collect(),
        commitments: (5..=8).map(|i| [i; 64]).collect(),
        links: (0..4).map(|i| Link { p: [0x10 + i; 32], q: [0x20 + i; 32], weight: 1, payout: [0x30; 64] }).collect(),
        withdrawal: Some(Withdrawal { to: [0x77; 32], token: token_of("pussy"), amount: 1 }),
        predicates: vec![[0; 64]; 8],
        notes: vec![vec![0; MAX_NOTE_BYTES]; 4],
        ..spend.clone()
    };
    assert_eq!(full.encode().unwrap().len(), MAX_SPEND_BYTES);
}


Homonyms

soft3/radio/iroh-blobs/src/tests.rs
neural/trident/src/neural/tests.rs
soft3/lens/cli/src/tests.rs
neural/trident/src/deploy/tests.rs
warriors/trisha/rs/lower/tests.rs
soft3/crate/src/wallet/tests.rs
cyb/honeycrisp/aruminium/src/tests.rs
soft3/crate/src/genesis/tests.rs
neural/trident/src/package/registry/tests.rs
neural/trident/src/syntax/grammar/tests.rs
neural/trident/src/config/resolve/tests.rs
cyb/wysm/crates/collections/src/tests.rs
neural/trident/src/verify/equiv/tests.rs
soft3/crate/src/chain/economy/tests.rs
cyb/honeycrisp/unimem/src/block/tests.rs
neural/trident/src/verify/smt/tests.rs
neural/trident/src/verify/synthesize/tests.rs
neural/trident/src/verify/sym/tests.rs
neural/trident/src/package/hash/tests.rs
neural/trident/src/verify/report/tests.rs
neural/trident/src/lsp/semantic/tests.rs
soft3/radio/cyber-bao/src/tree/tests.rs
cyb/wysm/crates/cli/src/tests.rs
neural/trident/src/config/scaffold/tests.rs
neural/trident/src/lsp/util/tests.rs
soft3/radio/cyber-bao/src/group/tests.rs
soft3/radio/quinn/quinn-udp/tests/tests.rs
neural/trident/src/package/store/tests.rs
neural/trident/src/verify/solve/tests.rs
warriors/trisha/rs/cost/stack_verifier/tests.rs
cyb/wysm/crates/wasmi/src/tests.rs
soft3/radio/quinn/quinn/src/tests.rs
neural/trident/src/syntax/lexer/tests.rs
warriors/trisha/rs/lower/legalize/tests.rs
neural/trident/src/syntax/format/tests.rs
neural/trident/src/package/manifest/tests.rs
cyb/honeycrisp/aruminium/src/render/tests.rs
warriors/erga/rs/mine-bench/src/texture_ffi/tests.rs
warriors/erga/rs/rtable-bench/src/table/tests.rs
neural/trident/src/neural/data/tir_graph/tests.rs
soft3/zheng/rs/src/ccs/universal/tests.rs
cyb/wysm/crates/core/src/table/tests.rs
soft3/crate/src/chain/net/kernel/tests.rs
cyb/evy/forks/bevy_ecs/src/bundle/tests.rs
warriors/erga/rs/blake-bench/src/batch/tests.rs
warriors/erga/rs/rtable-bench/src/probe/tests.rs
neural/trident/src/ir/tir/stack/tests.rs
warriors/erga/rs/mine-bench/src/owner/tests.rs
soft3/radio/quinn/quinn-proto/src/range_set/tests.rs
neural/trident/src/ir/tir/optimize/tests.rs
cyb/wysm/crates/wasmi/src/instance/tests.rs
cyb/wysm/crates/core/src/memory/tests.rs
cyb/evy/forks/naga/src/front/wgsl/tests.rs
warriors/erga/rs/mine-bench/src/cli/timed/tests.rs
cyb/wysm/crates/wasmi/src/module/instantiate/tests.rs
cyb/wysm/crates/wasmi/src/engine/limits/tests.rs
soft3/bbg/rs/src/storage/application/transfer/tests.rs

Graph