module std.crypto.poseidon2
use vm.io.mem
// Poseidon2 hash function over the Goldilocks field (p = 2^64 - 2^32 + 1).
//
// Implements the Poseidon2 permutation (Grassi et al., 2023) with:
// - State width t = 8, rate = 4, capacity = 4
// - S-box: x^7
// - 8 full rounds (4 initial + 4 final) and 22 partial rounds
// - External linear layer: circulant(2,1,1,...,1)
// - Internal linear layer: diag(2,3,5,9,17,33,65,129) + ones_matrix
// - Round constants: deterministic from BLAKE3-seeded generation
//
// This is the SNARK-friendly hash used for content addressing throughout
// the Trident toolchain. Matching parameters to src/poseidon2.rs ensures
// that hashes computed in Trident programs can be verified against
// compiler-generated content hashes on-chain.
// ---------------------------------------------------------------------------
// Poseidon2 state: 8 field elements
// ---------------------------------------------------------------------------
pub struct State {
s0: Field,
s1: Field,
s2: Field,
s3: Field,
s4: Field,
s5: Field,
s6: Field,
s7: Field,
}
pub fn zero_state() -> State {
State { s0: 0, s1: 0, s2: 0, s3: 0, s4: 0, s5: 0, s6: 0, s7: 0 }
}
// ---------------------------------------------------------------------------
// S-box: x^7 over the Goldilocks field
// ---------------------------------------------------------------------------
// gcd(7, p-1) = 1 for p = 2^64 - 2^32 + 1, so x^7 is a permutation.
fn sbox(x: Field) -> Field {
let x2: Field = x * x
let x3: Field = x2 * x
let x6: Field = x3 * x3
x6 * x
}
// ---------------------------------------------------------------------------
// External linear layer: circulant(2,1,1,...,1)
// new[i] = state[i] + sum(state)
// This equals 2*state[i] + sum(others).
// ---------------------------------------------------------------------------
fn external_linear(st: State) -> State {
let sum: Field = st.s0 + st.s1 + st.s2 + st.s3 + st.s4 + st.s5 + st.s6 + st.s7
State { s0: st.s0 + sum, s1: st.s1 + sum, s2: st.s2 + sum, s3: st.s3 + sum, s4: st.s4 + sum, s5: st.s5 + sum, s6: st.s6 + sum, s7: st.s7 + sum }
}
// ---------------------------------------------------------------------------
// Internal linear layer: diag(d_0,...,d_7) + ones_matrix
// new[i] = d_i * state[i] + sum(state)
// d_i = 1 + 2^i: [2, 3, 5, 9, 17, 33, 65, 129]
// ---------------------------------------------------------------------------
fn internal_linear(st: State) -> State {
let sum: Field = st.s0 + st.s1 + st.s2 + st.s3 + st.s4 + st.s5 + st.s6 + st.s7
State { s0: st.s0 * 2 + sum, s1: st.s1 * 3 + sum, s2: st.s2 * 5 + sum, s3: st.s3 * 9 + sum, s4: st.s4 * 17 + sum, s5: st.s5 * 33 + sum, s6: st.s6 * 65 + sum, s7: st.s7 * 129 + sum }
}
// ---------------------------------------------------------------------------
// Full round: add round constants + sbox all elements + external linear
// ---------------------------------------------------------------------------
fn full_round(
st: State,
rc0: Field,
rc1: Field,
rc2: Field,
rc3: Field,
rc4: Field,
rc5: Field,
rc6: Field,
rc7: Field
) -> State {
let t: State = State { s0: sbox(st.s0 + rc0), s1: sbox(st.s1 + rc1), s2: sbox(st.s2 + rc2), s3: sbox(st.s3 + rc3), s4: sbox(st.s4 + rc4), s5: sbox(st.s5 + rc5), s6: sbox(st.s6 + rc6), s7: sbox(st.s7 + rc7) }
external_linear(t)
}
// ---------------------------------------------------------------------------
// Partial round: add round constant to s0 only + sbox s0 only + internal linear
// ---------------------------------------------------------------------------
fn partial_round(st: State, rc: Field) -> State {
let new_s0: Field = sbox(st.s0 + rc)
let t: State = State { s0: new_s0, s1: st.s1, s2: st.s2, s3: st.s3, s4: st.s4, s5: st.s5, s6: st.s6, s7: st.s7 }
internal_linear(t)
}
// ---------------------------------------------------------------------------
// Round constants
// ---------------------------------------------------------------------------
// These are the deterministic round constants matching src/poseidon2.rs.
// Generated from BLAKE3("Poseidon2-Goldilocks-t8-RF8-RP22-{round}-{element}"),
// taking the first 8 bytes as a little-endian u64, reduced mod p.
//
// Layout: 4 full rounds (8 constants each) = 32,
// 22 partial rounds (1 constant each) = 22,
// 4 full rounds (8 constants each) = 32.
// Total: 86 round constants.
//
// For efficiency in a constrained environment, these constants are
// embedded as literals. The Rust implementation (src/poseidon2.rs)
// generates them identically at runtime.
//
// Note: The actual constant values depend on the BLAKE3 derivation.
// We use a parametric approach: the permutation function accepts
// all round constants as arguments, allowing the caller to supply
// the correct values. This avoids hardcoding 86 field constants
// in source and enables verification against the Rust implementation.
// ---------------------------------------------------------------------------
// Permutation: 4 full + 22 partial + 4 full rounds
// ---------------------------------------------------------------------------
// The permutation takes the state and all 86 round constants.
// This is the core cryptographic primitive.
//
// For practical use, wrap this with functions that supply the
// correct constants for a given security parameter set.
// Apply 4 full rounds with 32 round constants (rc[0..32]).
fn apply_full_rounds_4(
st: State,
c0: Field,
c1: Field,
c2: Field,
c3: Field,
c4: Field,
c5: Field,
c6: Field,
c7: Field,
c8: Field,
c9: Field,
c10: Field,
c11: Field,
c12: Field,
c13: Field,
c14: Field,
c15: Field,
c16: Field,
c17: Field,
c18: Field,
c19: Field,
c20: Field,
c21: Field,
c22: Field,
c23: Field,
c24: Field,
c25: Field,
c26: Field,
c27: Field,
c28: Field,
c29: Field,
c30: Field,
c31: Field
) -> State {
let s1: State = full_round(st, c0, c1, c2, c3, c4, c5, c6, c7)
let s2: State = full_round(s1, c8, c9, c10, c11, c12, c13, c14, c15)
let s3: State = full_round(s2, c16, c17, c18, c19, c20, c21, c22, c23)
full_round(s3, c24, c25, c26, c27, c28, c29, c30, c31)
}
// Apply 11 partial rounds with 11 round constants.
fn apply_partial_rounds_11(
st: State,
c0: Field,
c1: Field,
c2: Field,
c3: Field,
c4: Field,
c5: Field,
c6: Field,
c7: Field,
c8: Field,
c9: Field,
c10: Field
) -> State {
let s1: State = partial_round(st, c0)
let s2: State = partial_round(s1, c1)
let s3: State = partial_round(s2, c2)
let s4: State = partial_round(s3, c3)
let s5: State = partial_round(s4, c4)
let s6: State = partial_round(s5, c5)
let s7: State = partial_round(s6, c6)
let s8: State = partial_round(s7, c7)
let s9: State = partial_round(s8, c8)
let s10: State = partial_round(s9, c9)
partial_round(s10, c10)
}
// Full Poseidon2 permutation.
//
// Takes the 8-element state and 86 round constants:
// - first_full[0..31]: 32 constants for 4 initial full rounds
// - partial[0..21]: 22 constants for 22 partial rounds
// - last_full[0..31]: 32 constants for 4 final full rounds
//
// Returns the permuted state.
pub fn permute(
st: State,
ff0: Field,
ff1: Field,
ff2: Field,
ff3: Field,
ff4: Field,
ff5: Field,
ff6: Field,
ff7: Field,
ff8: Field,
ff9: Field,
ff10: Field,
ff11: Field,
ff12: Field,
ff13: Field,
ff14: Field,
ff15: Field,
ff16: Field,
ff17: Field,
ff18: Field,
ff19: Field,
ff20: Field,
ff21: Field,
ff22: Field,
ff23: Field,
ff24: Field,
ff25: Field,
ff26: Field,
ff27: Field,
ff28: Field,
ff29: Field,
ff30: Field,
ff31: Field,
p0: Field,
p1: Field,
p2: Field,
p3: Field,
p4: Field,
p5: Field,
p6: Field,
p7: Field,
p8: Field,
p9: Field,
p10: Field,
p11: Field,
p12: Field,
p13: Field,
p14: Field,
p15: Field,
p16: Field,
p17: Field,
p18: Field,
p19: Field,
p20: Field,
p21: Field,
lf0: Field,
lf1: Field,
lf2: Field,
lf3: Field,
lf4: Field,
lf5: Field,
lf6: Field,
lf7: Field,
lf8: Field,
lf9: Field,
lf10: Field,
lf11: Field,
lf12: Field,
lf13: Field,
lf14: Field,
lf15: Field,
lf16: Field,
lf17: Field,
lf18: Field,
lf19: Field,
lf20: Field,
lf21: Field,
lf22: Field,
lf23: Field,
lf24: Field,
lf25: Field,
lf26: Field,
lf27: Field,
lf28: Field,
lf29: Field,
lf30: Field,
lf31: Field
) -> State {
// First 4 full rounds (32 constants)
// 22 partial rounds (22 constants)
// Last 4 full rounds (32 constants)
// First 4 full rounds
let s1: State = apply_full_rounds_4(
st,
ff0,
ff1,
ff2,
ff3,
ff4,
ff5,
ff6,
ff7,
ff8,
ff9,
ff10,
ff11,
ff12,
ff13,
ff14,
ff15,
ff16,
ff17,
ff18,
ff19,
ff20,
ff21,
ff22,
ff23,
ff24,
ff25,
ff26,
ff27,
ff28,
ff29,
ff30,
ff31
)
// First 11 partial rounds
let s2: State = apply_partial_rounds_11(
s1,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7,
p8,
p9,
p10
)
// Remaining 11 partial rounds
let s3: State = apply_partial_rounds_11(
s2,
p11,
p12,
p13,
p14,
p15,
p16,
p17,
p18,
p19,
p20,
p21
)
// Last 4 full rounds
apply_full_rounds_4(
s3,
lf0,
lf1,
lf2,
lf3,
lf4,
lf5,
lf6,
lf7,
lf8,
lf9,
lf10,
lf11,
lf12,
lf13,
lf14,
lf15,
lf16,
lf17,
lf18,
lf19,
lf20,
lf21,
lf22,
lf23,
lf24,
lf25,
lf26,
lf27,
lf28,
lf29,
lf30,
lf31
)
}
// ---------------------------------------------------------------------------
// Sponge construction: absorb-squeeze API
// ---------------------------------------------------------------------------
// The sponge operates on the state with rate = 4 (elements s0..s3)
// and capacity = 4 (elements s4..s7).
// Absorb 4 field elements into the rate portion by addition.
pub fn absorb4(st: State, a: Field, b: Field, c: Field, d: Field) -> State {
State { s0: st.s0 + a, s1: st.s1 + b, s2: st.s2 + c, s3: st.s3 + d, s4: st.s4, s5: st.s5, s6: st.s6, s7: st.s7 }
}
// Absorb 1 field element (padded with zeros in rate positions 1-3).
pub fn absorb1(st: State, a: Field) -> State {
State { s0: st.s0 + a, s1: st.s1, s2: st.s2, s3: st.s3, s4: st.s4, s5: st.s5, s6: st.s6, s7: st.s7 }
}
// Absorb 2 field elements (padded with zeros in rate positions 2-3).
pub fn absorb2(st: State, a: Field, b: Field) -> State {
State { s0: st.s0 + a, s1: st.s1 + b, s2: st.s2, s3: st.s3, s4: st.s4, s5: st.s5, s6: st.s6, s7: st.s7 }
}
// Squeeze the rate portion: returns the 4 rate elements.
pub fn squeeze4(st: State) -> (Field, Field, Field, Field) {
(st.s0, st.s1, st.s2, st.s3)
}
// Squeeze a single element from the rate portion.
pub fn squeeze1(st: State) -> Field {
st.s0
}
// ---------------------------------------------------------------------------
// High-level hash functions
// ---------------------------------------------------------------------------
// These convenience functions compute a Poseidon2 hash of a small number
// of field elements. The caller must supply the 86 round constants.
//
// For compact call sites, a program would typically define a wrapper
// that captures the constants and delegates to these functions.
// Hash 1 field element -> 1 field element.
// Domain separation: capacity element s4 = 1 (single-input domain tag).
pub fn hash1(
input: Field,
ff0: Field,
ff1: Field,
ff2: Field,
ff3: Field,
ff4: Field,
ff5: Field,
ff6: Field,
ff7: Field,
ff8: Field,
ff9: Field,
ff10: Field,
ff11: Field,
ff12: Field,
ff13: Field,
ff14: Field,
ff15: Field,
ff16: Field,
ff17: Field,
ff18: Field,
ff19: Field,
ff20: Field,
ff21: Field,
ff22: Field,
ff23: Field,
ff24: Field,
ff25: Field,
ff26: Field,
ff27: Field,
ff28: Field,
ff29: Field,
ff30: Field,
ff31: Field,
p0: Field,
p1: Field,
p2: Field,
p3: Field,
p4: Field,
p5: Field,
p6: Field,
p7: Field,
p8: Field,
p9: Field,
p10: Field,
p11: Field,
p12: Field,
p13: Field,
p14: Field,
p15: Field,
p16: Field,
p17: Field,
p18: Field,
p19: Field,
p20: Field,
p21: Field,
lf0: Field,
lf1: Field,
lf2: Field,
lf3: Field,
lf4: Field,
lf5: Field,
lf6: Field,
lf7: Field,
lf8: Field,
lf9: Field,
lf10: Field,
lf11: Field,
lf12: Field,
lf13: Field,
lf14: Field,
lf15: Field,
lf16: Field,
lf17: Field,
lf18: Field,
lf19: Field,
lf20: Field,
lf21: Field,
lf22: Field,
lf23: Field,
lf24: Field,
lf25: Field,
lf26: Field,
lf27: Field,
lf28: Field,
lf29: Field,
lf30: Field,
lf31: Field
) -> Field {
// 86 round constants (same signature groups as permute)
// Initialize state: input in s0, domain tag 1 in s4
let st: State = State { s0: input, s1: 0, s2: 0, s3: 0, s4: 1, s5: 0, s6: 0, s7: 0 }
let result: State = permute(
st,
ff0,
ff1,
ff2,
ff3,
ff4,
ff5,
ff6,
ff7,
ff8,
ff9,
ff10,
ff11,
ff12,
ff13,
ff14,
ff15,
ff16,
ff17,
ff18,
ff19,
ff20,
ff21,
ff22,
ff23,
ff24,
ff25,
ff26,
ff27,
ff28,
ff29,
ff30,
ff31,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7,
p8,
p9,
p10,
p11,
p12,
p13,
p14,
p15,
p16,
p17,
p18,
p19,
p20,
p21,
lf0,
lf1,
lf2,
lf3,
lf4,
lf5,
lf6,
lf7,
lf8,
lf9,
lf10,
lf11,
lf12,
lf13,
lf14,
lf15,
lf16,
lf17,
lf18,
lf19,
lf20,
lf21,
lf22,
lf23,
lf24,
lf25,
lf26,
lf27,
lf28,
lf29,
lf30,
lf31
)
squeeze1(result)
}
// Hash 2 field elements -> 1 field element.
// Domain separation: capacity element s4 = 2.
pub fn hash2(
a: Field,
b: Field,
ff0: Field,
ff1: Field,
ff2: Field,
ff3: Field,
ff4: Field,
ff5: Field,
ff6: Field,
ff7: Field,
ff8: Field,
ff9: Field,
ff10: Field,
ff11: Field,
ff12: Field,
ff13: Field,
ff14: Field,
ff15: Field,
ff16: Field,
ff17: Field,
ff18: Field,
ff19: Field,
ff20: Field,
ff21: Field,
ff22: Field,
ff23: Field,
ff24: Field,
ff25: Field,
ff26: Field,
ff27: Field,
ff28: Field,
ff29: Field,
ff30: Field,
ff31: Field,
p0: Field,
p1: Field,
p2: Field,
p3: Field,
p4: Field,
p5: Field,
p6: Field,
p7: Field,
p8: Field,
p9: Field,
p10: Field,
p11: Field,
p12: Field,
p13: Field,
p14: Field,
p15: Field,
p16: Field,
p17: Field,
p18: Field,
p19: Field,
p20: Field,
p21: Field,
lf0: Field,
lf1: Field,
lf2: Field,
lf3: Field,
lf4: Field,
lf5: Field,
lf6: Field,
lf7: Field,
lf8: Field,
lf9: Field,
lf10: Field,
lf11: Field,
lf12: Field,
lf13: Field,
lf14: Field,
lf15: Field,
lf16: Field,
lf17: Field,
lf18: Field,
lf19: Field,
lf20: Field,
lf21: Field,
lf22: Field,
lf23: Field,
lf24: Field,
lf25: Field,
lf26: Field,
lf27: Field,
lf28: Field,
lf29: Field,
lf30: Field,
lf31: Field
) -> Field {
let st: State = State { s0: a, s1: b, s2: 0, s3: 0, s4: 2, s5: 0, s6: 0, s7: 0 }
let result: State = permute(
st,
ff0,
ff1,
ff2,
ff3,
ff4,
ff5,
ff6,
ff7,
ff8,
ff9,
ff10,
ff11,
ff12,
ff13,
ff14,
ff15,
ff16,
ff17,
ff18,
ff19,
ff20,
ff21,
ff22,
ff23,
ff24,
ff25,
ff26,
ff27,
ff28,
ff29,
ff30,
ff31,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7,
p8,
p9,
p10,
p11,
p12,
p13,
p14,
p15,
p16,
p17,
p18,
p19,
p20,
p21,
lf0,
lf1,
lf2,
lf3,
lf4,
lf5,
lf6,
lf7,
lf8,
lf9,
lf10,
lf11,
lf12,
lf13,
lf14,
lf15,
lf16,
lf17,
lf18,
lf19,
lf20,
lf21,
lf22,
lf23,
lf24,
lf25,
lf26,
lf27,
lf28,
lf29,
lf30,
lf31
)
squeeze1(result)
}
// Hash 4 field elements -> 4 field elements (full rate input/output).
// Domain separation: capacity element s4 = 4.
pub fn hash4(
a: Field,
b: Field,
c: Field,
d: Field,
ff0: Field,
ff1: Field,
ff2: Field,
ff3: Field,
ff4: Field,
ff5: Field,
ff6: Field,
ff7: Field,
ff8: Field,
ff9: Field,
ff10: Field,
ff11: Field,
ff12: Field,
ff13: Field,
ff14: Field,
ff15: Field,
ff16: Field,
ff17: Field,
ff18: Field,
ff19: Field,
ff20: Field,
ff21: Field,
ff22: Field,
ff23: Field,
ff24: Field,
ff25: Field,
ff26: Field,
ff27: Field,
ff28: Field,
ff29: Field,
ff30: Field,
ff31: Field,
p0: Field,
p1: Field,
p2: Field,
p3: Field,
p4: Field,
p5: Field,
p6: Field,
p7: Field,
p8: Field,
p9: Field,
p10: Field,
p11: Field,
p12: Field,
p13: Field,
p14: Field,
p15: Field,
p16: Field,
p17: Field,
p18: Field,
p19: Field,
p20: Field,
p21: Field,
lf0: Field,
lf1: Field,
lf2: Field,
lf3: Field,
lf4: Field,
lf5: Field,
lf6: Field,
lf7: Field,
lf8: Field,
lf9: Field,
lf10: Field,
lf11: Field,
lf12: Field,
lf13: Field,
lf14: Field,
lf15: Field,
lf16: Field,
lf17: Field,
lf18: Field,
lf19: Field,
lf20: Field,
lf21: Field,
lf22: Field,
lf23: Field,
lf24: Field,
lf25: Field,
lf26: Field,
lf27: Field,
lf28: Field,
lf29: Field,
lf30: Field,
lf31: Field
) -> (Field, Field, Field, Field) {
let st: State = State { s0: a, s1: b, s2: c, s3: d, s4: 4, s5: 0, s6: 0, s7: 0 }
let result: State = permute(
st,
ff0,
ff1,
ff2,
ff3,
ff4,
ff5,
ff6,
ff7,
ff8,
ff9,
ff10,
ff11,
ff12,
ff13,
ff14,
ff15,
ff16,
ff17,
ff18,
ff19,
ff20,
ff21,
ff22,
ff23,
ff24,
ff25,
ff26,
ff27,
ff28,
ff29,
ff30,
ff31,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7,
p8,
p9,
p10,
p11,
p12,
p13,
p14,
p15,
p16,
p17,
p18,
p19,
p20,
p21,
lf0,
lf1,
lf2,
lf3,
lf4,
lf5,
lf6,
lf7,
lf8,
lf9,
lf10,
lf11,
lf12,
lf13,
lf14,
lf15,
lf16,
lf17,
lf18,
lf19,
lf20,
lf21,
lf22,
lf23,
lf24,
lf25,
lf26,
lf27,
lf28,
lf29,
lf30,
lf31
)
squeeze4(result)
}
// ---------------------------------------------------------------------------
// RAM-based variants
// ---------------------------------------------------------------------------
// Read 86 round constants from RAM and call permute.
// Layout: rc_addr+0..rc_addr+31 = first full (32),
// rc_addr+32..rc_addr+53 = partial (22),
// rc_addr+54..rc_addr+85 = last full (32).
//
// This enables Poseidon2 in pipelines where passing 86 parameters
// through the call chain is impractical. The STARK proof authenticates
// all RAM reads through consistency.
pub fn permute_from_ram(st: State, rc_addr: Field) -> State {
permute(
st,
mem.read(rc_addr), mem.read(rc_addr + 1), mem.read(rc_addr + 2), mem.read(rc_addr + 3),
mem.read(rc_addr + 4), mem.read(rc_addr + 5), mem.read(rc_addr + 6), mem.read(rc_addr + 7),
mem.read(rc_addr + 8), mem.read(rc_addr + 9), mem.read(rc_addr + 10), mem.read(rc_addr + 11),
mem.read(rc_addr + 12), mem.read(rc_addr + 13), mem.read(rc_addr + 14), mem.read(rc_addr + 15),
mem.read(rc_addr + 16), mem.read(rc_addr + 17), mem.read(rc_addr + 18), mem.read(rc_addr + 19),
mem.read(rc_addr + 20), mem.read(rc_addr + 21), mem.read(rc_addr + 22), mem.read(rc_addr + 23),
mem.read(rc_addr + 24), mem.read(rc_addr + 25), mem.read(rc_addr + 26), mem.read(rc_addr + 27),
mem.read(rc_addr + 28), mem.read(rc_addr + 29), mem.read(rc_addr + 30), mem.read(rc_addr + 31),
mem.read(rc_addr + 32), mem.read(rc_addr + 33), mem.read(rc_addr + 34), mem.read(rc_addr + 35),
mem.read(rc_addr + 36), mem.read(rc_addr + 37), mem.read(rc_addr + 38), mem.read(rc_addr + 39),
mem.read(rc_addr + 40), mem.read(rc_addr + 41), mem.read(rc_addr + 42), mem.read(rc_addr + 43),
mem.read(rc_addr + 44), mem.read(rc_addr + 45), mem.read(rc_addr + 46), mem.read(rc_addr + 47),
mem.read(rc_addr + 48), mem.read(rc_addr + 49), mem.read(rc_addr + 50), mem.read(rc_addr + 51),
mem.read(rc_addr + 52), mem.read(rc_addr + 53),
mem.read(rc_addr + 54), mem.read(rc_addr + 55), mem.read(rc_addr + 56), mem.read(rc_addr + 57),
mem.read(rc_addr + 58), mem.read(rc_addr + 59), mem.read(rc_addr + 60), mem.read(rc_addr + 61),
mem.read(rc_addr + 62), mem.read(rc_addr + 63), mem.read(rc_addr + 64), mem.read(rc_addr + 65),
mem.read(rc_addr + 66), mem.read(rc_addr + 67), mem.read(rc_addr + 68), mem.read(rc_addr + 69),
mem.read(rc_addr + 70), mem.read(rc_addr + 71), mem.read(rc_addr + 72), mem.read(rc_addr + 73),
mem.read(rc_addr + 74), mem.read(rc_addr + 75), mem.read(rc_addr + 76), mem.read(rc_addr + 77),
mem.read(rc_addr + 78), mem.read(rc_addr + 79), mem.read(rc_addr + 80), mem.read(rc_addr + 81),
mem.read(rc_addr + 82), mem.read(rc_addr + 83), mem.read(rc_addr + 84), mem.read(rc_addr + 85)
)
}
// Hash 4 field elements using round constants from RAM.
// Domain separation: capacity element s4 = 4.
pub fn hash4_from_ram(
a: Field,
b: Field,
c: Field,
d: Field,
rc_addr: Field
) -> (Field, Field, Field, Field) {
let st: State = State { s0: a, s1: b, s2: c, s3: d, s4: 4, s5: 0, s6: 0, s7: 0 }
let result: State = permute_from_ram(st, rc_addr)
squeeze4(result)
}
// Hash 4 field elements, return single digest (first rate element).
pub fn hash4_to_digest(
a: Field,
b: Field,
c: Field,
d: Field,
rc_addr: Field
) -> Field {
let st: State = State { s0: a, s1: b, s2: c, s3: d, s4: 4, s5: 0, s6: 0, s7: 0 }
let result: State = permute_from_ram(st, rc_addr)
squeeze1(result)
}
trident/std/crypto/poseidon2.tri
ฯ 0.0%