From d61babb4e699066896682b6c5af1acd80efd218d Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Mon, 8 Jun 2026 00:26:24 +0100 Subject: [PATCH 01/12] test stuff --- .../poseidon2_test.go | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go index de22520..14b88c7 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go @@ -804,3 +804,98 @@ func TestHashPair(t *testing.T) { } } + +func externalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { + // Load state into locals (register-resident, no pointer-indexed scratch). + v0 := g.AsUInt128(s[0]) + v1 := g.AsUInt128(s[1]) + v2 := g.AsUInt128(s[2]) + v3 := g.AsUInt128(s[3]) + v4 := g.AsUInt128(s[4]) + v5 := g.AsUInt128(s[5]) + v6 := g.AsUInt128(s[6]) + v7 := g.AsUInt128(s[7]) + v8 := g.AsUInt128(s[8]) + v9 := g.AsUInt128(s[9]) + v10 := g.AsUInt128(s[10]) + v11 := g.AsUInt128(s[11]) + + // --- M4 on each 4-lane chunk: out = circ(2,3,1,1) * in --- + // chunk 0 + t01 := AddUInt128(v0, v1) + t23 := AddUInt128(v2, v3) + t := AddUInt128(t01, t23) + n0 := AddUInt128(AddUInt128(t, t01), v1) + n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) + n2 := AddUInt128(AddUInt128(t, t23), v3) + n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) + + // chunk 1 + t01 = AddUInt128(v4, v5) + t23 = AddUInt128(v6, v7) + t = AddUInt128(t01, t23) + n4 := AddUInt128(AddUInt128(t, t01), v5) + n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) + n6 := AddUInt128(AddUInt128(t, t23), v7) + n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) + + // chunk 2 + t01 = AddUInt128(v8, v9) + t23 = AddUInt128(v10, v11) + t = AddUInt128(t01, t23) + n8 := AddUInt128(AddUInt128(t, t01), v9) + n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) + n10 := AddUInt128(AddUInt128(t, t23), v11) + n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) + + // --- cross-chunk circulant: add the per-position sum across the 3 chunks --- + sum0 := AddUInt128(n0, AddUInt128(n4, n8)) + sum1 := AddUInt128(n1, AddUInt128(n5, n9)) + sum2 := AddUInt128(n2, AddUInt128(n6, n10)) + sum3 := AddUInt128(n3, AddUInt128(n7, n11)) + + // Single 96-bit reduction per lane (max intermediate < 2^70, well within 96 bits). + s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) + s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) + s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) + s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) + s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) + s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) + s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) + s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) + s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) + s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) +} + +func BenchmarkExternalLayer_2(b *testing.B) { + len := 100000 + s := make([][WIDTH]g.GoldilocksField, len) + for i := 0; i < len; i++ { + for j := 0; j < 12; j++ { + s[i][j] = getRandomGoldilocks() + } + } + id := 0 + b.Run("Original-ExternalLayer", func(b *testing.B) { + for i := 0; i < b.N; i++ { + externalLinearLayer(&s[id]) + id++ + if id == len { + id -= len + } + } + }) + + id = 0 + b.Run("Inlined-Version", func(b *testing.B) { + for i := 0; i < b.N; i++ { + externalLinearLayer_2(&s[id]) + id++ + if id == len { + id -= len + } + } + }) +} From 3bfff6779d94317d0d6a68093665c47353cd5654 Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Mon, 8 Jun 2026 14:58:47 +0100 Subject: [PATCH 02/12] sped up things --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 269 ++++++++++++------ .../poseidon2_test.go | 102 ++++++- 2 files changed, 275 insertions(+), 96 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index c403f51..30da748 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -219,113 +219,202 @@ func fullRounds(state *[WIDTH]g.GoldilocksField, start int) { func partialRounds(state *[WIDTH]g.GoldilocksField) { for r := 0; r < ROUNDS_P; r++ { - addRCI(state, r) - + // addRCI + state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[r])) // Manual-inlining for sboxP p := state[0] p2 := g.SquareF(p) // x^2 p4 := g.SquareF(p2) // x^4 p = g.MulF(p, p2) // x^3 state[0] = g.MulF(p, p4) // x^7 - - internalLinearLayer(state) + // Internal Linear Layer - inlined + v0 := g.AsUInt128(state[0]) + v1 := g.AsUInt128(state[1]) + v2 := g.AsUInt128(state[2]) + v3 := g.AsUInt128(state[3]) + v4 := g.AsUInt128(state[4]) + v5 := g.AsUInt128(state[5]) + v6 := g.AsUInt128(state[6]) + v7 := g.AsUInt128(state[7]) + v8 := g.AsUInt128(state[8]) + v9 := g.AsUInt128(state[9]) + v10 := g.AsUInt128(state[10]) + v11 := g.AsUInt128(state[11]) + + // chunk 0 + t01 := AddUInt128(v0, v1) + t23 := AddUInt128(v2, v3) + t := AddUInt128(t01, t23) + n0 := AddUInt128(AddUInt128(t, t01), v1) + n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) + n2 := AddUInt128(AddUInt128(t, t23), v3) + n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) + + // chunk 1 + t01 = AddUInt128(v4, v5) + t23 = AddUInt128(v6, v7) + t = AddUInt128(t01, t23) + n4 := AddUInt128(AddUInt128(t, t01), v5) + n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) + n6 := AddUInt128(AddUInt128(t, t23), v7) + n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) + + // chunk 2 + t01 = AddUInt128(v8, v9) + t23 = AddUInt128(v10, v11) + t = AddUInt128(t01, t23) + n8 := AddUInt128(AddUInt128(t, t01), v9) + n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) + n10 := AddUInt128(AddUInt128(t, t23), v11) + n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) + + sum0 := AddUInt128(n0, AddUInt128(n4, n8)) + sum1 := AddUInt128(n1, AddUInt128(n5, n9)) + sum2 := AddUInt128(n2, AddUInt128(n6, n10)) + sum3 := AddUInt128(n3, AddUInt128(n7, n11)) + + state[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) + state[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) + state[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) + + state[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) + state[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) + state[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + + state[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) + state[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) + state[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + + state[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) + state[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) + state[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) } } func externalLinearLayer(s *[WIDTH]g.GoldilocksField) { - s128 := [WIDTH]UInt128{} - for i := 0; i < WIDTH; i++ { - s128[i] = g.AsUInt128(s[i]) - } + v0 := g.AsUInt128(s[0]) + v1 := g.AsUInt128(s[1]) + v2 := g.AsUInt128(s[2]) + v3 := g.AsUInt128(s[3]) + v4 := g.AsUInt128(s[4]) + v5 := g.AsUInt128(s[5]) + v6 := g.AsUInt128(s[6]) + v7 := g.AsUInt128(s[7]) + v8 := g.AsUInt128(s[8]) + v9 := g.AsUInt128(s[9]) + v10 := g.AsUInt128(s[10]) + v11 := g.AsUInt128(s[11]) - externalLinearLayer128(&s128) + // chunk 0 + t01 := AddUInt128(v0, v1) + t23 := AddUInt128(v2, v3) + t := AddUInt128(t01, t23) + n0 := AddUInt128(AddUInt128(t, t01), v1) + n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) + n2 := AddUInt128(AddUInt128(t, t23), v3) + n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) - for i := 0; i < WIDTH; i++ { - s[i] = g.Reduce96Bit(s128[i]) - } -} + // chunk 1 + t01 = AddUInt128(v4, v5) + t23 = AddUInt128(v6, v7) + t = AddUInt128(t01, t23) + n4 := AddUInt128(AddUInt128(t, t01), v5) + n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) + n6 := AddUInt128(AddUInt128(t, t23), v7) + n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) + + // chunk 2 + t01 = AddUInt128(v8, v9) + t23 = AddUInt128(v10, v11) + t = AddUInt128(t01, t23) + n8 := AddUInt128(AddUInt128(t, t01), v9) + n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) + n10 := AddUInt128(AddUInt128(t, t23), v11) + n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) + + sum0 := AddUInt128(n0, AddUInt128(n4, n8)) + sum1 := AddUInt128(n1, AddUInt128(n5, n9)) + sum2 := AddUInt128(n2, AddUInt128(n6, n10)) + sum3 := AddUInt128(n3, AddUInt128(n7, n11)) + + s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) + s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) + s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) + + s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) + s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) + s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + + s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) + s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) + s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + + s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) + s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) + s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) +} + +func internalLinearLayer(s *[WIDTH]g.GoldilocksField) { + v0 := g.AsUInt128(s[0]) + v1 := g.AsUInt128(s[1]) + v2 := g.AsUInt128(s[2]) + v3 := g.AsUInt128(s[3]) + v4 := g.AsUInt128(s[4]) + v5 := g.AsUInt128(s[5]) + v6 := g.AsUInt128(s[6]) + v7 := g.AsUInt128(s[7]) + v8 := g.AsUInt128(s[8]) + v9 := g.AsUInt128(s[9]) + v10 := g.AsUInt128(s[10]) + v11 := g.AsUInt128(s[11]) -func externalLinearLayer128(s *[WIDTH]UInt128) { // chunk 0 - x0, x1, x2, x3 := s[0], s[1], s[2], s[3] - t01 := AddUInt128(x0, x1) - t23 := AddUInt128(x2, x3) - t0123 := AddUInt128(t01, t23) - s[0] = AddUInt128(AddUInt128(t0123, t01), x1) - s[1] = AddUInt128(AddUInt128(t0123, x1), AddUInt128(x2, x2)) - s[2] = AddUInt128(AddUInt128(t0123, t23), x3) - s[3] = AddUInt128(AddUInt128(t0123, x3), AddUInt128(x0, x0)) + t01 := AddUInt128(v0, v1) + t23 := AddUInt128(v2, v3) + t := AddUInt128(t01, t23) + n0 := AddUInt128(AddUInt128(t, t01), v1) + n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) + n2 := AddUInt128(AddUInt128(t, t23), v3) + n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) // chunk 1 - x0, x1, x2, x3 = s[4], s[5], s[6], s[7] - t01 = AddUInt128(x0, x1) - t23 = AddUInt128(x2, x3) - t0123 = AddUInt128(t01, t23) - s[4] = AddUInt128(AddUInt128(t0123, t01), x1) - s[5] = AddUInt128(AddUInt128(t0123, x1), AddUInt128(x2, x2)) - s[6] = AddUInt128(AddUInt128(t0123, t23), x3) - s[7] = AddUInt128(AddUInt128(t0123, x3), AddUInt128(x0, x0)) + t01 = AddUInt128(v4, v5) + t23 = AddUInt128(v6, v7) + t = AddUInt128(t01, t23) + n4 := AddUInt128(AddUInt128(t, t01), v5) + n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) + n6 := AddUInt128(AddUInt128(t, t23), v7) + n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) // chunk 2 - x0, x1, x2, x3 = s[8], s[9], s[10], s[11] - t01 = AddUInt128(x0, x1) - t23 = AddUInt128(x2, x3) - t0123 = AddUInt128(t01, t23) - s[8] = AddUInt128(AddUInt128(t0123, t01), x1) - s[9] = AddUInt128(AddUInt128(t0123, x1), AddUInt128(x2, x2)) - s[10] = AddUInt128(AddUInt128(t0123, t23), x3) - s[11] = AddUInt128(AddUInt128(t0123, x3), AddUInt128(x0, x0)) - - sums := [4]UInt128{} - sums[0] = AddUInt128(s[0], AddUInt128(s[4], s[8])) - sums[1] = AddUInt128(s[1], AddUInt128(s[5], s[9])) - sums[2] = AddUInt128(s[2], AddUInt128(s[6], s[10])) - sums[3] = AddUInt128(s[3], AddUInt128(s[7], s[11])) - - s[0] = AddUInt128(s[0], sums[0]) - s[4] = AddUInt128(s[4], sums[0]) - s[8] = AddUInt128(s[8], sums[0]) - - s[1] = AddUInt128(s[1], sums[1]) - s[5] = AddUInt128(s[5], sums[1]) - s[9] = AddUInt128(s[9], sums[1]) - - s[2] = AddUInt128(s[2], sums[2]) - s[6] = AddUInt128(s[6], sums[2]) - s[10] = AddUInt128(s[10], sums[2]) - - s[3] = AddUInt128(s[3], sums[3]) - s[7] = AddUInt128(s[7], sums[3]) - s[11] = AddUInt128(s[11], sums[3]) -} + t01 = AddUInt128(v8, v9) + t23 = AddUInt128(v10, v11) + t = AddUInt128(t01, t23) + n8 := AddUInt128(AddUInt128(t, t01), v9) + n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) + n10 := AddUInt128(AddUInt128(t, t23), v11) + n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) + + sum0 := AddUInt128(n0, AddUInt128(n4, n8)) + sum1 := AddUInt128(n1, AddUInt128(n5, n9)) + sum2 := AddUInt128(n2, AddUInt128(n6, n10)) + sum3 := AddUInt128(n3, AddUInt128(n7, n11)) + + s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) + s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) + s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) -func internalLinearLayer(state *[WIDTH]g.GoldilocksField) { - sum := g.AsUInt128(state[0]) - sum = AddUInt128(sum, g.AsUInt128(state[1])) - sum = AddUInt128(sum, g.AsUInt128(state[2])) - sum = AddUInt128(sum, g.AsUInt128(state[3])) - sum = AddUInt128(sum, g.AsUInt128(state[4])) - sum = AddUInt128(sum, g.AsUInt128(state[5])) - sum = AddUInt128(sum, g.AsUInt128(state[6])) - sum = AddUInt128(sum, g.AsUInt128(state[7])) - sum = AddUInt128(sum, g.AsUInt128(state[8])) - sum = AddUInt128(sum, g.AsUInt128(state[9])) - sum = AddUInt128(sum, g.AsUInt128(state[10])) - sum = AddUInt128(sum, g.AsUInt128(state[11])) - sumF := g.Reduce96Bit(sum) - - state[0] = g.MulAccF(sumF, state[0], MATRIX_DIAG_12_U64[0]) - state[1] = g.MulAccF(sumF, state[1], MATRIX_DIAG_12_U64[1]) - state[2] = g.MulAccF(sumF, state[2], MATRIX_DIAG_12_U64[2]) - state[3] = g.MulAccF(sumF, state[3], MATRIX_DIAG_12_U64[3]) - state[4] = g.MulAccF(sumF, state[4], MATRIX_DIAG_12_U64[4]) - state[5] = g.MulAccF(sumF, state[5], MATRIX_DIAG_12_U64[5]) - state[6] = g.MulAccF(sumF, state[6], MATRIX_DIAG_12_U64[6]) - state[7] = g.MulAccF(sumF, state[7], MATRIX_DIAG_12_U64[7]) - state[8] = g.MulAccF(sumF, state[8], MATRIX_DIAG_12_U64[8]) - state[9] = g.MulAccF(sumF, state[9], MATRIX_DIAG_12_U64[9]) - state[10] = g.MulAccF(sumF, state[10], MATRIX_DIAG_12_U64[10]) - state[11] = g.MulAccF(sumF, state[11], MATRIX_DIAG_12_U64[11]) + s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) + s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) + s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + + s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) + s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) + s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + + s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) + s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) + s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) } func addRC(state *[WIDTH]g.GoldilocksField, externalRound int) { @@ -334,10 +423,6 @@ func addRC(state *[WIDTH]g.GoldilocksField, externalRound int) { } } -func addRCI(state *[WIDTH]g.GoldilocksField, round int) { - state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[round])) -} - func sbox(state *[WIDTH]g.GoldilocksField) { // group 0-5 p0_2 := g.SquareF(state[0]) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go index 14b88c7..4f5d2f4 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go @@ -806,7 +806,6 @@ func TestHashPair(t *testing.T) { } func externalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { - // Load state into locals (register-resident, no pointer-indexed scratch). v0 := g.AsUInt128(s[0]) v1 := g.AsUInt128(s[1]) v2 := g.AsUInt128(s[2]) @@ -820,7 +819,6 @@ func externalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { v10 := g.AsUInt128(s[10]) v11 := g.AsUInt128(s[11]) - // --- M4 on each 4-lane chunk: out = circ(2,3,1,1) * in --- // chunk 0 t01 := AddUInt128(v0, v1) t23 := AddUInt128(v2, v3) @@ -848,22 +846,23 @@ func externalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { n10 := AddUInt128(AddUInt128(t, t23), v11) n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) - // --- cross-chunk circulant: add the per-position sum across the 3 chunks --- sum0 := AddUInt128(n0, AddUInt128(n4, n8)) sum1 := AddUInt128(n1, AddUInt128(n5, n9)) sum2 := AddUInt128(n2, AddUInt128(n6, n10)) sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - // Single 96-bit reduction per lane (max intermediate < 2^70, well within 96 bits). s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) + s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) @@ -899,3 +898,98 @@ func BenchmarkExternalLayer_2(b *testing.B) { } }) } + +func internalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { + // Load state into locals (register-resident, no pointer-indexed scratch). + v0 := g.AsUInt128(s[0]) + v1 := g.AsUInt128(s[1]) + v2 := g.AsUInt128(s[2]) + v3 := g.AsUInt128(s[3]) + v4 := g.AsUInt128(s[4]) + v5 := g.AsUInt128(s[5]) + v6 := g.AsUInt128(s[6]) + v7 := g.AsUInt128(s[7]) + v8 := g.AsUInt128(s[8]) + v9 := g.AsUInt128(s[9]) + v10 := g.AsUInt128(s[10]) + v11 := g.AsUInt128(s[11]) + + // --- M4 on each 4-lane chunk: out = circ(2,3,1,1) * in --- + // chunk 0 + t01 := AddUInt128(v0, v1) + t23 := AddUInt128(v2, v3) + t := AddUInt128(t01, t23) + n0 := AddUInt128(AddUInt128(t, t01), v1) + n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) + n2 := AddUInt128(AddUInt128(t, t23), v3) + n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) + + // chunk 1 + t01 = AddUInt128(v4, v5) + t23 = AddUInt128(v6, v7) + t = AddUInt128(t01, t23) + n4 := AddUInt128(AddUInt128(t, t01), v5) + n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) + n6 := AddUInt128(AddUInt128(t, t23), v7) + n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) + + // chunk 2 + t01 = AddUInt128(v8, v9) + t23 = AddUInt128(v10, v11) + t = AddUInt128(t01, t23) + n8 := AddUInt128(AddUInt128(t, t01), v9) + n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) + n10 := AddUInt128(AddUInt128(t, t23), v11) + n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) + + sum0 := AddUInt128(n0, AddUInt128(n4, n8)) + sum1 := AddUInt128(n1, AddUInt128(n5, n9)) + sum2 := AddUInt128(n2, AddUInt128(n6, n10)) + sum3 := AddUInt128(n3, AddUInt128(n7, n11)) + + s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) + s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) + s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) + + s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) + s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) + s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + + s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) + s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) + s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) + s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) + s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) +} + +func BenchmarkInternalLinearLayer(b *testing.B) { + len := 100000 + s := make([][WIDTH]g.GoldilocksField, len) + for i := 0; i < len; i++ { + for j := 0; j < 12; j++ { + s[i][j] = getRandomGoldilocks() + } + } + id := 0 + b.Run("Original-InternalLayer", func(b *testing.B) { + for i := 0; i < b.N; i++ { + partialRounds(&s[id]) + id++ + if id == len { + id -= len + } + } + }) + + id = 0 + b.Run("Inlined-Version", func(b *testing.B) { + for i := 0; i < b.N; i++ { + // partialRounds_2(&s[id]) + id++ + if id == len { + id -= len + } + } + }) +} From 83c09368de2d87ac488340153896b7ecfd98f93c Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Mon, 8 Jun 2026 15:32:09 +0100 Subject: [PATCH 03/12] finale --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 65 +------------ .../poseidon2_test.go | 95 ------------------- 2 files changed, 2 insertions(+), 158 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index 30da748..51e5cf0 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -221,12 +221,14 @@ func partialRounds(state *[WIDTH]g.GoldilocksField) { for r := 0; r < ROUNDS_P; r++ { // addRCI state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[r])) + // Manual-inlining for sboxP p := state[0] p2 := g.SquareF(p) // x^2 p4 := g.SquareF(p2) // x^4 p = g.MulF(p, p2) // x^3 state[0] = g.MulF(p, p4) // x^7 + // Internal Linear Layer - inlined v0 := g.AsUInt128(state[0]) v1 := g.AsUInt128(state[1]) @@ -354,69 +356,6 @@ func externalLinearLayer(s *[WIDTH]g.GoldilocksField) { s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) } -func internalLinearLayer(s *[WIDTH]g.GoldilocksField) { - v0 := g.AsUInt128(s[0]) - v1 := g.AsUInt128(s[1]) - v2 := g.AsUInt128(s[2]) - v3 := g.AsUInt128(s[3]) - v4 := g.AsUInt128(s[4]) - v5 := g.AsUInt128(s[5]) - v6 := g.AsUInt128(s[6]) - v7 := g.AsUInt128(s[7]) - v8 := g.AsUInt128(s[8]) - v9 := g.AsUInt128(s[9]) - v10 := g.AsUInt128(s[10]) - v11 := g.AsUInt128(s[11]) - - // chunk 0 - t01 := AddUInt128(v0, v1) - t23 := AddUInt128(v2, v3) - t := AddUInt128(t01, t23) - n0 := AddUInt128(AddUInt128(t, t01), v1) - n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) - n2 := AddUInt128(AddUInt128(t, t23), v3) - n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) - - // chunk 1 - t01 = AddUInt128(v4, v5) - t23 = AddUInt128(v6, v7) - t = AddUInt128(t01, t23) - n4 := AddUInt128(AddUInt128(t, t01), v5) - n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) - n6 := AddUInt128(AddUInt128(t, t23), v7) - n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) - - // chunk 2 - t01 = AddUInt128(v8, v9) - t23 = AddUInt128(v10, v11) - t = AddUInt128(t01, t23) - n8 := AddUInt128(AddUInt128(t, t01), v9) - n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) - n10 := AddUInt128(AddUInt128(t, t23), v11) - n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) - - sum0 := AddUInt128(n0, AddUInt128(n4, n8)) - sum1 := AddUInt128(n1, AddUInt128(n5, n9)) - sum2 := AddUInt128(n2, AddUInt128(n6, n10)) - sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - - s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) - s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) - s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) - - s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) - s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) - s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) - - s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) - s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) - s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) - - s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) - s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) - s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) -} - func addRC(state *[WIDTH]g.GoldilocksField, externalRound int) { for i := 0; i < WIDTH; i++ { state[i] = g.AddCanonicalUint64(state[i], uint64(EXTERNAL_CONSTANTS[externalRound][i])) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go index 4f5d2f4..54d4995 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go @@ -898,98 +898,3 @@ func BenchmarkExternalLayer_2(b *testing.B) { } }) } - -func internalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { - // Load state into locals (register-resident, no pointer-indexed scratch). - v0 := g.AsUInt128(s[0]) - v1 := g.AsUInt128(s[1]) - v2 := g.AsUInt128(s[2]) - v3 := g.AsUInt128(s[3]) - v4 := g.AsUInt128(s[4]) - v5 := g.AsUInt128(s[5]) - v6 := g.AsUInt128(s[6]) - v7 := g.AsUInt128(s[7]) - v8 := g.AsUInt128(s[8]) - v9 := g.AsUInt128(s[9]) - v10 := g.AsUInt128(s[10]) - v11 := g.AsUInt128(s[11]) - - // --- M4 on each 4-lane chunk: out = circ(2,3,1,1) * in --- - // chunk 0 - t01 := AddUInt128(v0, v1) - t23 := AddUInt128(v2, v3) - t := AddUInt128(t01, t23) - n0 := AddUInt128(AddUInt128(t, t01), v1) - n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) - n2 := AddUInt128(AddUInt128(t, t23), v3) - n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) - - // chunk 1 - t01 = AddUInt128(v4, v5) - t23 = AddUInt128(v6, v7) - t = AddUInt128(t01, t23) - n4 := AddUInt128(AddUInt128(t, t01), v5) - n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) - n6 := AddUInt128(AddUInt128(t, t23), v7) - n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) - - // chunk 2 - t01 = AddUInt128(v8, v9) - t23 = AddUInt128(v10, v11) - t = AddUInt128(t01, t23) - n8 := AddUInt128(AddUInt128(t, t01), v9) - n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) - n10 := AddUInt128(AddUInt128(t, t23), v11) - n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) - - sum0 := AddUInt128(n0, AddUInt128(n4, n8)) - sum1 := AddUInt128(n1, AddUInt128(n5, n9)) - sum2 := AddUInt128(n2, AddUInt128(n6, n10)) - sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - - s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) - s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) - s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) - - s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) - s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) - s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) - - s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) - s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) - s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) - s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) - s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) - s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) -} - -func BenchmarkInternalLinearLayer(b *testing.B) { - len := 100000 - s := make([][WIDTH]g.GoldilocksField, len) - for i := 0; i < len; i++ { - for j := 0; j < 12; j++ { - s[i][j] = getRandomGoldilocks() - } - } - id := 0 - b.Run("Original-InternalLayer", func(b *testing.B) { - for i := 0; i < b.N; i++ { - partialRounds(&s[id]) - id++ - if id == len { - id -= len - } - } - }) - - id = 0 - b.Run("Inlined-Version", func(b *testing.B) { - for i := 0; i < b.N; i++ { - // partialRounds_2(&s[id]) - id++ - if id == len { - id -= len - } - } - }) -} From 213facd9e66dc6eb2a30838e0975dbf18b3b3d50 Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Mon, 8 Jun 2026 15:53:37 +0100 Subject: [PATCH 04/12] fix --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 99 ++++++++----------- 1 file changed, 39 insertions(+), 60 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index 51e5cf0..dea6262 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -230,66 +230,45 @@ func partialRounds(state *[WIDTH]g.GoldilocksField) { state[0] = g.MulF(p, p4) // x^7 // Internal Linear Layer - inlined - v0 := g.AsUInt128(state[0]) - v1 := g.AsUInt128(state[1]) - v2 := g.AsUInt128(state[2]) - v3 := g.AsUInt128(state[3]) - v4 := g.AsUInt128(state[4]) - v5 := g.AsUInt128(state[5]) - v6 := g.AsUInt128(state[6]) - v7 := g.AsUInt128(state[7]) - v8 := g.AsUInt128(state[8]) - v9 := g.AsUInt128(state[9]) - v10 := g.AsUInt128(state[10]) - v11 := g.AsUInt128(state[11]) - - // chunk 0 - t01 := AddUInt128(v0, v1) - t23 := AddUInt128(v2, v3) - t := AddUInt128(t01, t23) - n0 := AddUInt128(AddUInt128(t, t01), v1) - n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) - n2 := AddUInt128(AddUInt128(t, t23), v3) - n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) - - // chunk 1 - t01 = AddUInt128(v4, v5) - t23 = AddUInt128(v6, v7) - t = AddUInt128(t01, t23) - n4 := AddUInt128(AddUInt128(t, t01), v5) - n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) - n6 := AddUInt128(AddUInt128(t, t23), v7) - n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) - - // chunk 2 - t01 = AddUInt128(v8, v9) - t23 = AddUInt128(v10, v11) - t = AddUInt128(t01, t23) - n8 := AddUInt128(AddUInt128(t, t01), v9) - n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) - n10 := AddUInt128(AddUInt128(t, t23), v11) - n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) - - sum0 := AddUInt128(n0, AddUInt128(n4, n8)) - sum1 := AddUInt128(n1, AddUInt128(n5, n9)) - sum2 := AddUInt128(n2, AddUInt128(n6, n10)) - sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - - state[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) - state[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) - state[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) - - state[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) - state[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) - state[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) - - state[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) - state[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) - state[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) - - state[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) - state[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) - state[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) + s0 := state[0] + s1 := state[1] + s2 := state[2] + s3 := state[3] + s4 := state[4] + s5 := state[5] + s6 := state[6] + s7 := state[7] + s8 := state[8] + s9 := state[9] + s10 := state[10] + s11 := state[11] + + sum := g.AsUInt128(s0) + sum = AddUInt128(sum, g.AsUInt128(s1)) + sum = AddUInt128(sum, g.AsUInt128(s2)) + sum = AddUInt128(sum, g.AsUInt128(s3)) + sum = AddUInt128(sum, g.AsUInt128(s4)) + sum = AddUInt128(sum, g.AsUInt128(s5)) + sum = AddUInt128(sum, g.AsUInt128(s6)) + sum = AddUInt128(sum, g.AsUInt128(s7)) + sum = AddUInt128(sum, g.AsUInt128(s8)) + sum = AddUInt128(sum, g.AsUInt128(s9)) + sum = AddUInt128(sum, g.AsUInt128(s10)) + sum = AddUInt128(sum, g.AsUInt128(s11)) + sumF := g.Reduce96Bit(sum) + + state[0] = g.MulAccF(sumF, s0, MATRIX_DIAG_12_U64[0]) + state[1] = g.MulAccF(sumF, s1, MATRIX_DIAG_12_U64[1]) + state[2] = g.MulAccF(sumF, s2, MATRIX_DIAG_12_U64[2]) + state[3] = g.MulAccF(sumF, s3, MATRIX_DIAG_12_U64[3]) + state[4] = g.MulAccF(sumF, s4, MATRIX_DIAG_12_U64[4]) + state[5] = g.MulAccF(sumF, s5, MATRIX_DIAG_12_U64[5]) + state[6] = g.MulAccF(sumF, s6, MATRIX_DIAG_12_U64[6]) + state[7] = g.MulAccF(sumF, s7, MATRIX_DIAG_12_U64[7]) + state[8] = g.MulAccF(sumF, s8, MATRIX_DIAG_12_U64[8]) + state[9] = g.MulAccF(sumF, s9, MATRIX_DIAG_12_U64[9]) + state[10] = g.MulAccF(sumF, s10, MATRIX_DIAG_12_U64[10]) + state[11] = g.MulAccF(sumF, s11, MATRIX_DIAG_12_U64[11]) } } From 4b7238832866119cddfb710b5e8dec1f6403cc1f Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Tue, 9 Jun 2026 01:19:34 +0100 Subject: [PATCH 05/12] . --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 99 ++++++++++++++++--- 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index dea6262..114f782 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -203,18 +203,27 @@ func HashPair(left, right [32]byte) [32]byte { } func Permute(input *[WIDTH]g.GoldilocksField) { + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[0]) + sbox(input) + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[1]) + sbox(input) + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[2]) + sbox(input) + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[3]) + sbox(input) externalLinearLayer(input) - fullRounds(input, 0) + partialRounds(input) - fullRounds(input, ROUNDS_F_HALF) -} -func fullRounds(state *[WIDTH]g.GoldilocksField, start int) { - for r := start; r < start+ROUNDS_F_HALF; r++ { - addRC(state, r) - sbox(state) - externalLinearLayer(state) - } + addRC(input, 4) + sbox(input) + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[5]) + sbox(input) + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[6]) + sbox(input) + externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[7]) + sbox(input) + externalLinearLayer(input) } func partialRounds(state *[WIDTH]g.GoldilocksField) { @@ -224,13 +233,12 @@ func partialRounds(state *[WIDTH]g.GoldilocksField) { // Manual-inlining for sboxP p := state[0] - p2 := g.SquareF(p) // x^2 - p4 := g.SquareF(p2) // x^4 - p = g.MulF(p, p2) // x^3 - state[0] = g.MulF(p, p4) // x^7 + p2 := g.SquareF(p) // x^2 + p4 := g.SquareF(p2) // x^4 + p = g.MulF(p, p2) // x^3 + s0 := g.MulF(p, p4) // Internal Linear Layer - inlined - s0 := state[0] s1 := state[1] s2 := state[2] s3 := state[3] @@ -335,6 +343,69 @@ func externalLinearLayer(s *[WIDTH]g.GoldilocksField) { s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) } +func externalLinearLayerRC(s *[WIDTH]g.GoldilocksField, rc *[WIDTH]g.GoldilocksField) { + v0 := g.AsUInt128(s[0]) + v1 := g.AsUInt128(s[1]) + v2 := g.AsUInt128(s[2]) + v3 := g.AsUInt128(s[3]) + v4 := g.AsUInt128(s[4]) + v5 := g.AsUInt128(s[5]) + v6 := g.AsUInt128(s[6]) + v7 := g.AsUInt128(s[7]) + v8 := g.AsUInt128(s[8]) + v9 := g.AsUInt128(s[9]) + v10 := g.AsUInt128(s[10]) + v11 := g.AsUInt128(s[11]) + + // chunk 0 + t01 := AddUInt128(v0, v1) + t23 := AddUInt128(v2, v3) + t := AddUInt128(t01, t23) + n0 := AddUInt128(AddUInt128(t, t01), v1) + n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) + n2 := AddUInt128(AddUInt128(t, t23), v3) + n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) + + // chunk 1 + t01 = AddUInt128(v4, v5) + t23 = AddUInt128(v6, v7) + t = AddUInt128(t01, t23) + n4 := AddUInt128(AddUInt128(t, t01), v5) + n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) + n6 := AddUInt128(AddUInt128(t, t23), v7) + n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) + + // chunk 2 + t01 = AddUInt128(v8, v9) + t23 = AddUInt128(v10, v11) + t = AddUInt128(t01, t23) + n8 := AddUInt128(AddUInt128(t, t01), v9) + n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) + n10 := AddUInt128(AddUInt128(t, t23), v11) + n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) + + sum0 := AddUInt128(n0, AddUInt128(n4, n8)) + sum1 := AddUInt128(n1, AddUInt128(n5, n9)) + sum2 := AddUInt128(n2, AddUInt128(n6, n10)) + sum3 := AddUInt128(n3, AddUInt128(n7, n11)) + + s[0] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n0, sum0)), uint64(rc[0])) + s[4] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n4, sum0)), uint64(rc[4])) + s[8] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n8, sum0)), uint64(rc[8])) + + s[1] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n1, sum1)), uint64(rc[1])) + s[5] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n5, sum1)), uint64(rc[5])) + s[9] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n9, sum1)), uint64(rc[9])) + + s[2] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n2, sum2)), uint64(rc[2])) + s[6] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n6, sum2)), uint64(rc[6])) + s[10] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n10, sum2)), uint64(rc[10])) + + s[3] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n3, sum3)), uint64(rc[3])) + s[7] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n7, sum3)), uint64(rc[7])) + s[11] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n11, sum3)), uint64(rc[11])) +} + func addRC(state *[WIDTH]g.GoldilocksField, externalRound int) { for i := 0; i < WIDTH; i++ { state[i] = g.AddCanonicalUint64(state[i], uint64(EXTERNAL_CONSTANTS[externalRound][i])) From d3138721cac62e09d7ca2335f8abef1152c6dc99 Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Tue, 9 Jun 2026 18:31:22 +0100 Subject: [PATCH 06/12] finale? --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index 114f782..fa6bb25 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -251,19 +251,17 @@ func partialRounds(state *[WIDTH]g.GoldilocksField) { s10 := state[10] s11 := state[11] - sum := g.AsUInt128(s0) - sum = AddUInt128(sum, g.AsUInt128(s1)) - sum = AddUInt128(sum, g.AsUInt128(s2)) - sum = AddUInt128(sum, g.AsUInt128(s3)) - sum = AddUInt128(sum, g.AsUInt128(s4)) - sum = AddUInt128(sum, g.AsUInt128(s5)) - sum = AddUInt128(sum, g.AsUInt128(s6)) - sum = AddUInt128(sum, g.AsUInt128(s7)) - sum = AddUInt128(sum, g.AsUInt128(s8)) - sum = AddUInt128(sum, g.AsUInt128(s9)) - sum = AddUInt128(sum, g.AsUInt128(s10)) - sum = AddUInt128(sum, g.AsUInt128(s11)) - sumF := g.Reduce96Bit(sum) + // Summing in a tree structure + r0 := AddUInt128(g.AsUInt128(s1), g.AsUInt128(s2)) + r1 := AddUInt128(g.AsUInt128(s3), g.AsUInt128(s4)) + r2 := AddUInt128(g.AsUInt128(s5), g.AsUInt128(s6)) + r3 := AddUInt128(g.AsUInt128(s7), g.AsUInt128(s8)) + r4 := AddUInt128(g.AsUInt128(s9), g.AsUInt128(s10)) + q0 := AddUInt128(r0, r1) + q1 := AddUInt128(r2, r3) + q2 := AddUInt128(r4, g.AsUInt128(s11)) + restSum := AddUInt128(AddUInt128(q0, q1), q2) + sumF := g.Reduce96Bit(AddUInt128(restSum, g.AsUInt128(s0))) state[0] = g.MulAccF(sumF, s0, MATRIX_DIAG_12_U64[0]) state[1] = g.MulAccF(sumF, s1, MATRIX_DIAG_12_U64[1]) @@ -413,13 +411,11 @@ func addRC(state *[WIDTH]g.GoldilocksField, externalRound int) { } func sbox(state *[WIDTH]g.GoldilocksField) { - // group 0-5 + // group 0-3 p0_2 := g.SquareF(state[0]) p1_2 := g.SquareF(state[1]) p2_2 := g.SquareF(state[2]) p3_2 := g.SquareF(state[3]) - p4_2 := g.SquareF(state[4]) - p5_2 := g.SquareF(state[5]) p0_3 := g.MulF(state[0], p0_2) p0_4 := g.SquareF(p0_2) p1_3 := g.MulF(state[1], p1_2) @@ -428,28 +424,34 @@ func sbox(state *[WIDTH]g.GoldilocksField) { p2_4 := g.SquareF(p2_2) p3_3 := g.MulF(state[3], p3_2) p3_4 := g.SquareF(p3_2) - p4_3 := g.MulF(state[4], p4_2) - p4_4 := g.SquareF(p4_2) - p5_3 := g.MulF(state[5], p5_2) - p5_4 := g.SquareF(p5_2) state[0] = g.MulF(p0_3, p0_4) state[1] = g.MulF(p1_3, p1_4) state[2] = g.MulF(p2_3, p2_4) state[3] = g.MulF(p3_3, p3_4) - state[4] = g.MulF(p4_3, p4_4) - state[5] = g.MulF(p5_3, p5_4) - // group 6-11 + // group 4-7 + p4_2 := g.SquareF(state[4]) + p5_2 := g.SquareF(state[5]) p6_2 := g.SquareF(state[6]) p7_2 := g.SquareF(state[7]) - p8_2 := g.SquareF(state[8]) - p9_2 := g.SquareF(state[9]) - p10_2 := g.SquareF(state[10]) - p11_2 := g.SquareF(state[11]) + p4_3 := g.MulF(state[4], p4_2) + p4_4 := g.SquareF(p4_2) + p5_3 := g.MulF(state[5], p5_2) + p5_4 := g.SquareF(p5_2) p6_3 := g.MulF(state[6], p6_2) p6_4 := g.SquareF(p6_2) p7_3 := g.MulF(state[7], p7_2) p7_4 := g.SquareF(p7_2) + state[4] = g.MulF(p4_3, p4_4) + state[5] = g.MulF(p5_3, p5_4) + state[6] = g.MulF(p6_3, p6_4) + state[7] = g.MulF(p7_3, p7_4) + + // group 8-11 + p8_2 := g.SquareF(state[8]) + p9_2 := g.SquareF(state[9]) + p10_2 := g.SquareF(state[10]) + p11_2 := g.SquareF(state[11]) p8_3 := g.MulF(state[8], p8_2) p8_4 := g.SquareF(p8_2) p9_3 := g.MulF(state[9], p9_2) @@ -458,8 +460,6 @@ func sbox(state *[WIDTH]g.GoldilocksField) { p10_4 := g.SquareF(p10_2) p11_3 := g.MulF(state[11], p11_2) p11_4 := g.SquareF(p11_2) - state[6] = g.MulF(p6_3, p6_4) - state[7] = g.MulF(p7_3, p7_4) state[8] = g.MulF(p8_3, p8_4) state[9] = g.MulF(p9_3, p9_4) state[10] = g.MulF(p10_3, p10_4) From 4443756f696f351688ec7f9b9a2081ec755db7a3 Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Tue, 9 Jun 2026 22:38:04 +0100 Subject: [PATCH 07/12] wow --- field/goldilocks/goldilocks_plonky2.go | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/field/goldilocks/goldilocks_plonky2.go b/field/goldilocks/goldilocks_plonky2.go index b1725f1..8a159f3 100644 --- a/field/goldilocks/goldilocks_plonky2.go +++ b/field/goldilocks/goldilocks_plonky2.go @@ -55,8 +55,8 @@ func (z GoldilocksField) ToCanonicalUint64() uint64 { // lhs, rhs in non-canonical form func AddF(lhs, rhs GoldilocksField) GoldilocksField { sum, over := bits.Add64(uint64(lhs), uint64(rhs), 0) - sum, over = bits.Add64(sum, over*EPSILON, 0) - sum += EPSILON * over // this can't overflow + sum, over = bits.Add64(sum, (EPSILON & -over), 0) + sum += (EPSILON & -over) // this can't overflow return GoldilocksField(sum) } @@ -65,7 +65,7 @@ func AddF(lhs, rhs GoldilocksField) GoldilocksField { func AddCanonicalUint64(lhs GoldilocksField, rhs uint64) GoldilocksField { sum, over := bits.Add64(uint64(lhs), rhs, 0) // if overflowed, sum := lhs + rhs - 2^64 => sum + EPSILON = lhs + rhs - 2^64 + 2^32 -1 = lhs + rhs - ORDER < ORDER + 2^64 - ORDER = 2^64, so there is no overflow in this case. - return GoldilocksField(sum + over*EPSILON) + return GoldilocksField(sum + (EPSILON & -over)) } func DoubleF(lhs GoldilocksField) GoldilocksField { @@ -75,8 +75,8 @@ func DoubleF(lhs GoldilocksField) GoldilocksField { // lhs, rhs in non-canonical form func SubF(lhs, rhs GoldilocksField) GoldilocksField { diff, borrow := bits.Sub64(uint64(lhs), uint64(rhs), 0) - diff, borrow = bits.Sub64(diff, borrow*EPSILON, 0) - diff -= EPSILON * borrow // this can't underflow + diff, borrow = bits.Sub64(diff, (EPSILON & -borrow), 0) + diff -= (EPSILON & -borrow) // this can't underflow return GoldilocksField(diff) } @@ -89,12 +89,12 @@ func MulF(lhs, rhs GoldilocksField) GoldilocksField { x_hi_lo := x_hi & EPSILON t0, borrow := bits.Sub64(x_lo, x_hi_hi, 0) - t0 -= EPSILON * borrow + t0 -= (EPSILON & -borrow) t1 := x_hi_lo * EPSILON sum, over := bits.Add64(t0, t1, 0) - t2 := sum + EPSILON*over + t2 := sum + (EPSILON & -over) return GoldilocksField(t2) } @@ -105,12 +105,12 @@ func SquareF(x GoldilocksField) GoldilocksField { x_hi_lo := x_hi & EPSILON t0, borrow := bits.Sub64(x_lo, x_hi_hi, 0) - t0 -= EPSILON * borrow + t0 -= (EPSILON & -borrow) t1 := x_hi_lo * EPSILON sum, over := bits.Add64(t0, t1, 0) - t2 := sum + EPSILON*over + t2 := sum + (EPSILON & -over) return GoldilocksField(t2) } @@ -121,9 +121,9 @@ func MulAccF(self, x, y GoldilocksField) GoldilocksField { hi += c t0, borrow := bits.Sub64(lo, hi>>32, 0) - t0 -= EPSILON * borrow + t0 -= (EPSILON & -borrow) resWrapped, c := bits.Add64(t0, (hi&EPSILON)*EPSILON, 0) - return GoldilocksField(resWrapped + EPSILON*c) + return GoldilocksField(resWrapped + (EPSILON & -c)) } func ExpPowerOf2(x GoldilocksField, n uint) GoldilocksField { @@ -189,7 +189,7 @@ func Reduce96Bit(x UInt128) GoldilocksField { t1 := x.Hi * EPSILON resWrapped, carry := bits.Add64(x.Lo, t1, 0) - return GoldilocksField(resWrapped) + GoldilocksField(carry*EPSILON) + return GoldilocksField(resWrapped) + GoldilocksField((EPSILON & -carry)) } func Reduce128Bit(x UInt128) GoldilocksField { @@ -197,12 +197,12 @@ func Reduce128Bit(x UInt128) GoldilocksField { x_hi_lo := x.Hi & EPSILON t0, borrow := bits.Sub64(x.Lo, x_hi_hi, 0) - t0 -= EPSILON * borrow + t0 -= (EPSILON & -borrow) t1 := x_hi_lo * EPSILON resWrapped, carry := bits.Add64(t0, t1, 0) - t2 := resWrapped + EPSILON*carry + t2 := resWrapped + (EPSILON & -carry) return GoldilocksField(t2) } From 699bf2ab3eee388b87fe6a9a64f27b58736a3e72 Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Tue, 9 Jun 2026 23:46:40 +0100 Subject: [PATCH 08/12] .. --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 92 +++++++++++-------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index fa6bb25..2511194 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -215,7 +215,6 @@ func Permute(input *[WIDTH]g.GoldilocksField) { partialRounds(input) - addRC(input, 4) sbox(input) externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[5]) sbox(input) @@ -227,10 +226,12 @@ func Permute(input *[WIDTH]g.GoldilocksField) { } func partialRounds(state *[WIDTH]g.GoldilocksField) { - for r := 0; r < ROUNDS_P; r++ { - // addRCI - state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[r])) + // Only the first internal round constant is added explicitly. + // Each later one is folded into the previous round's lane-0 accumulator, and the round-4 external + // constants are folded into the last round's accumulators, so no constant add sits on the critical path. + state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[0])) + for r := 0; r < ROUNDS_P; r++ { // Manual-inlining for sboxP p := state[0] p2 := g.SquareF(p) // x^2 @@ -261,20 +262,43 @@ func partialRounds(state *[WIDTH]g.GoldilocksField) { q1 := AddUInt128(r2, r3) q2 := AddUInt128(r4, g.AsUInt128(s11)) restSum := AddUInt128(AddUInt128(q0, q1), q2) - sumF := g.Reduce96Bit(AddUInt128(restSum, g.AsUInt128(s0))) - - state[0] = g.MulAccF(sumF, s0, MATRIX_DIAG_12_U64[0]) - state[1] = g.MulAccF(sumF, s1, MATRIX_DIAG_12_U64[1]) - state[2] = g.MulAccF(sumF, s2, MATRIX_DIAG_12_U64[2]) - state[3] = g.MulAccF(sumF, s3, MATRIX_DIAG_12_U64[3]) - state[4] = g.MulAccF(sumF, s4, MATRIX_DIAG_12_U64[4]) - state[5] = g.MulAccF(sumF, s5, MATRIX_DIAG_12_U64[5]) - state[6] = g.MulAccF(sumF, s6, MATRIX_DIAG_12_U64[6]) - state[7] = g.MulAccF(sumF, s7, MATRIX_DIAG_12_U64[7]) - state[8] = g.MulAccF(sumF, s8, MATRIX_DIAG_12_U64[8]) - state[9] = g.MulAccF(sumF, s9, MATRIX_DIAG_12_U64[9]) - state[10] = g.MulAccF(sumF, s10, MATRIX_DIAG_12_U64[10]) - state[11] = g.MulAccF(sumF, s11, MATRIX_DIAG_12_U64[11]) + // The 12-lane sum stays unreduced (< 2^68); it is added into each lane's 128-bit product and + // reduced once per lane. Product hi < 2^64 - 2^32 since the diagonal is canonical, so no overflow. + sum := AddUInt128(restSum, g.AsUInt128(s0)) + + var rc0 uint64 + if r+1 < ROUNDS_P { + rc0 = uint64(INTERNAL_CONSTANTS[r+1]) + } else { + rc0 = uint64(EXTERNAL_CONSTANTS[4][0]) + } + state[0] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s0), uint64(MATRIX_DIAG_12_U64[0])), sum), rc0)) + + if r+1 < ROUNDS_P { + state[1] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s1), uint64(MATRIX_DIAG_12_U64[1])), sum)) + state[2] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s2), uint64(MATRIX_DIAG_12_U64[2])), sum)) + state[3] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s3), uint64(MATRIX_DIAG_12_U64[3])), sum)) + state[4] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s4), uint64(MATRIX_DIAG_12_U64[4])), sum)) + state[5] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s5), uint64(MATRIX_DIAG_12_U64[5])), sum)) + state[6] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s6), uint64(MATRIX_DIAG_12_U64[6])), sum)) + state[7] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s7), uint64(MATRIX_DIAG_12_U64[7])), sum)) + state[8] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s8), uint64(MATRIX_DIAG_12_U64[8])), sum)) + state[9] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s9), uint64(MATRIX_DIAG_12_U64[9])), sum)) + state[10] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s10), uint64(MATRIX_DIAG_12_U64[10])), sum)) + state[11] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s11), uint64(MATRIX_DIAG_12_U64[11])), sum)) + } else { + state[1] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s1), uint64(MATRIX_DIAG_12_U64[1])), sum), uint64(EXTERNAL_CONSTANTS[4][1]))) + state[2] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s2), uint64(MATRIX_DIAG_12_U64[2])), sum), uint64(EXTERNAL_CONSTANTS[4][2]))) + state[3] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s3), uint64(MATRIX_DIAG_12_U64[3])), sum), uint64(EXTERNAL_CONSTANTS[4][3]))) + state[4] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s4), uint64(MATRIX_DIAG_12_U64[4])), sum), uint64(EXTERNAL_CONSTANTS[4][4]))) + state[5] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s5), uint64(MATRIX_DIAG_12_U64[5])), sum), uint64(EXTERNAL_CONSTANTS[4][5]))) + state[6] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s6), uint64(MATRIX_DIAG_12_U64[6])), sum), uint64(EXTERNAL_CONSTANTS[4][6]))) + state[7] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s7), uint64(MATRIX_DIAG_12_U64[7])), sum), uint64(EXTERNAL_CONSTANTS[4][7]))) + state[8] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s8), uint64(MATRIX_DIAG_12_U64[8])), sum), uint64(EXTERNAL_CONSTANTS[4][8]))) + state[9] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s9), uint64(MATRIX_DIAG_12_U64[9])), sum), uint64(EXTERNAL_CONSTANTS[4][9]))) + state[10] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s10), uint64(MATRIX_DIAG_12_U64[10])), sum), uint64(EXTERNAL_CONSTANTS[4][10]))) + state[11] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s11), uint64(MATRIX_DIAG_12_U64[11])), sum), uint64(EXTERNAL_CONSTANTS[4][11]))) + } } } @@ -387,27 +411,23 @@ func externalLinearLayerRC(s *[WIDTH]g.GoldilocksField, rc *[WIDTH]g.GoldilocksF sum2 := AddUInt128(n2, AddUInt128(n6, n10)) sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - s[0] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n0, sum0)), uint64(rc[0])) - s[4] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n4, sum0)), uint64(rc[4])) - s[8] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n8, sum0)), uint64(rc[8])) + // The round constant is folded into the 128-bit accumulator so each lane needs a single reduction. + // Accumulator stays < 2^70, well within Reduce96Bit's contract. + s[0] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n0, sum0), uint64(rc[0]))) + s[4] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n4, sum0), uint64(rc[4]))) + s[8] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n8, sum0), uint64(rc[8]))) - s[1] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n1, sum1)), uint64(rc[1])) - s[5] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n5, sum1)), uint64(rc[5])) - s[9] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n9, sum1)), uint64(rc[9])) + s[1] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n1, sum1), uint64(rc[1]))) + s[5] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n5, sum1), uint64(rc[5]))) + s[9] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n9, sum1), uint64(rc[9]))) - s[2] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n2, sum2)), uint64(rc[2])) - s[6] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n6, sum2)), uint64(rc[6])) - s[10] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n10, sum2)), uint64(rc[10])) + s[2] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n2, sum2), uint64(rc[2]))) + s[6] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n6, sum2), uint64(rc[6]))) + s[10] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n10, sum2), uint64(rc[10]))) - s[3] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n3, sum3)), uint64(rc[3])) - s[7] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n7, sum3)), uint64(rc[7])) - s[11] = g.AddCanonicalUint64(g.Reduce96Bit(AddUInt128(n11, sum3)), uint64(rc[11])) -} - -func addRC(state *[WIDTH]g.GoldilocksField, externalRound int) { - for i := 0; i < WIDTH; i++ { - state[i] = g.AddCanonicalUint64(state[i], uint64(EXTERNAL_CONSTANTS[externalRound][i])) - } + s[3] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n3, sum3), uint64(rc[3]))) + s[7] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n7, sum3), uint64(rc[7]))) + s[11] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n11, sum3), uint64(rc[11]))) } func sbox(state *[WIDTH]g.GoldilocksField) { From 7179b4be6f780aa560c460264f942fd44a3b2fde Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Wed, 10 Jun 2026 21:55:40 +0100 Subject: [PATCH 09/12] no need to push those --- .../poseidon2_test.go | 94 ------------------- 1 file changed, 94 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go index 54d4995..de22520 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2_test.go @@ -804,97 +804,3 @@ func TestHashPair(t *testing.T) { } } - -func externalLinearLayer_2(s *[WIDTH]g.GoldilocksField) { - v0 := g.AsUInt128(s[0]) - v1 := g.AsUInt128(s[1]) - v2 := g.AsUInt128(s[2]) - v3 := g.AsUInt128(s[3]) - v4 := g.AsUInt128(s[4]) - v5 := g.AsUInt128(s[5]) - v6 := g.AsUInt128(s[6]) - v7 := g.AsUInt128(s[7]) - v8 := g.AsUInt128(s[8]) - v9 := g.AsUInt128(s[9]) - v10 := g.AsUInt128(s[10]) - v11 := g.AsUInt128(s[11]) - - // chunk 0 - t01 := AddUInt128(v0, v1) - t23 := AddUInt128(v2, v3) - t := AddUInt128(t01, t23) - n0 := AddUInt128(AddUInt128(t, t01), v1) - n1 := AddUInt128(AddUInt128(t, v1), AddUInt128(v2, v2)) - n2 := AddUInt128(AddUInt128(t, t23), v3) - n3 := AddUInt128(AddUInt128(t, v3), AddUInt128(v0, v0)) - - // chunk 1 - t01 = AddUInt128(v4, v5) - t23 = AddUInt128(v6, v7) - t = AddUInt128(t01, t23) - n4 := AddUInt128(AddUInt128(t, t01), v5) - n5 := AddUInt128(AddUInt128(t, v5), AddUInt128(v6, v6)) - n6 := AddUInt128(AddUInt128(t, t23), v7) - n7 := AddUInt128(AddUInt128(t, v7), AddUInt128(v4, v4)) - - // chunk 2 - t01 = AddUInt128(v8, v9) - t23 = AddUInt128(v10, v11) - t = AddUInt128(t01, t23) - n8 := AddUInt128(AddUInt128(t, t01), v9) - n9 := AddUInt128(AddUInt128(t, v9), AddUInt128(v10, v10)) - n10 := AddUInt128(AddUInt128(t, t23), v11) - n11 := AddUInt128(AddUInt128(t, v11), AddUInt128(v8, v8)) - - sum0 := AddUInt128(n0, AddUInt128(n4, n8)) - sum1 := AddUInt128(n1, AddUInt128(n5, n9)) - sum2 := AddUInt128(n2, AddUInt128(n6, n10)) - sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - - s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) - s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) - s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) - - s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) - s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) - s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) - - s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) - s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) - s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) - - s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) - s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) - s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) -} - -func BenchmarkExternalLayer_2(b *testing.B) { - len := 100000 - s := make([][WIDTH]g.GoldilocksField, len) - for i := 0; i < len; i++ { - for j := 0; j < 12; j++ { - s[i][j] = getRandomGoldilocks() - } - } - id := 0 - b.Run("Original-ExternalLayer", func(b *testing.B) { - for i := 0; i < b.N; i++ { - externalLinearLayer(&s[id]) - id++ - if id == len { - id -= len - } - } - }) - - id = 0 - b.Run("Inlined-Version", func(b *testing.B) { - for i := 0; i < b.N; i++ { - externalLinearLayer_2(&s[id]) - id++ - if id == len { - id -= len - } - } - }) -} From d0cf4548263f766dc574b4b77d9af7b1c656068d Mon Sep 17 00:00:00 2001 From: Mychecksdead Date: Thu, 11 Jun 2026 00:35:25 +0100 Subject: [PATCH 10/12] finale. --- .../poseidon2_goldilocks_plonky2/poseidon2.go | 218 +++++++++--------- 1 file changed, 113 insertions(+), 105 deletions(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index 2511194..2e35dd7 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -202,6 +202,8 @@ func HashPair(left, right [32]byte) [32]byte { return output } +// Permute runs the Poseidon2 permutation: 4 external rounds, 22 partial rounds, 4 external rounds. +// EXTERNAL_CONSTANTS[4] is not applied here because partialRounds folds it into its last round. func Permute(input *[WIDTH]g.GoldilocksField) { externalLinearLayerRC(input, &EXTERNAL_CONSTANTS[0]) sbox(input) @@ -225,84 +227,9 @@ func Permute(input *[WIDTH]g.GoldilocksField) { externalLinearLayer(input) } -func partialRounds(state *[WIDTH]g.GoldilocksField) { - // Only the first internal round constant is added explicitly. - // Each later one is folded into the previous round's lane-0 accumulator, and the round-4 external - // constants are folded into the last round's accumulators, so no constant add sits on the critical path. - state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[0])) - - for r := 0; r < ROUNDS_P; r++ { - // Manual-inlining for sboxP - p := state[0] - p2 := g.SquareF(p) // x^2 - p4 := g.SquareF(p2) // x^4 - p = g.MulF(p, p2) // x^3 - s0 := g.MulF(p, p4) - - // Internal Linear Layer - inlined - s1 := state[1] - s2 := state[2] - s3 := state[3] - s4 := state[4] - s5 := state[5] - s6 := state[6] - s7 := state[7] - s8 := state[8] - s9 := state[9] - s10 := state[10] - s11 := state[11] - - // Summing in a tree structure - r0 := AddUInt128(g.AsUInt128(s1), g.AsUInt128(s2)) - r1 := AddUInt128(g.AsUInt128(s3), g.AsUInt128(s4)) - r2 := AddUInt128(g.AsUInt128(s5), g.AsUInt128(s6)) - r3 := AddUInt128(g.AsUInt128(s7), g.AsUInt128(s8)) - r4 := AddUInt128(g.AsUInt128(s9), g.AsUInt128(s10)) - q0 := AddUInt128(r0, r1) - q1 := AddUInt128(r2, r3) - q2 := AddUInt128(r4, g.AsUInt128(s11)) - restSum := AddUInt128(AddUInt128(q0, q1), q2) - // The 12-lane sum stays unreduced (< 2^68); it is added into each lane's 128-bit product and - // reduced once per lane. Product hi < 2^64 - 2^32 since the diagonal is canonical, so no overflow. - sum := AddUInt128(restSum, g.AsUInt128(s0)) - - var rc0 uint64 - if r+1 < ROUNDS_P { - rc0 = uint64(INTERNAL_CONSTANTS[r+1]) - } else { - rc0 = uint64(EXTERNAL_CONSTANTS[4][0]) - } - state[0] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s0), uint64(MATRIX_DIAG_12_U64[0])), sum), rc0)) - - if r+1 < ROUNDS_P { - state[1] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s1), uint64(MATRIX_DIAG_12_U64[1])), sum)) - state[2] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s2), uint64(MATRIX_DIAG_12_U64[2])), sum)) - state[3] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s3), uint64(MATRIX_DIAG_12_U64[3])), sum)) - state[4] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s4), uint64(MATRIX_DIAG_12_U64[4])), sum)) - state[5] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s5), uint64(MATRIX_DIAG_12_U64[5])), sum)) - state[6] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s6), uint64(MATRIX_DIAG_12_U64[6])), sum)) - state[7] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s7), uint64(MATRIX_DIAG_12_U64[7])), sum)) - state[8] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s8), uint64(MATRIX_DIAG_12_U64[8])), sum)) - state[9] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s9), uint64(MATRIX_DIAG_12_U64[9])), sum)) - state[10] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s10), uint64(MATRIX_DIAG_12_U64[10])), sum)) - state[11] = g.Reduce128Bit(AddUInt128(MulUInt64(uint64(s11), uint64(MATRIX_DIAG_12_U64[11])), sum)) - } else { - state[1] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s1), uint64(MATRIX_DIAG_12_U64[1])), sum), uint64(EXTERNAL_CONSTANTS[4][1]))) - state[2] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s2), uint64(MATRIX_DIAG_12_U64[2])), sum), uint64(EXTERNAL_CONSTANTS[4][2]))) - state[3] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s3), uint64(MATRIX_DIAG_12_U64[3])), sum), uint64(EXTERNAL_CONSTANTS[4][3]))) - state[4] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s4), uint64(MATRIX_DIAG_12_U64[4])), sum), uint64(EXTERNAL_CONSTANTS[4][4]))) - state[5] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s5), uint64(MATRIX_DIAG_12_U64[5])), sum), uint64(EXTERNAL_CONSTANTS[4][5]))) - state[6] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s6), uint64(MATRIX_DIAG_12_U64[6])), sum), uint64(EXTERNAL_CONSTANTS[4][6]))) - state[7] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s7), uint64(MATRIX_DIAG_12_U64[7])), sum), uint64(EXTERNAL_CONSTANTS[4][7]))) - state[8] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s8), uint64(MATRIX_DIAG_12_U64[8])), sum), uint64(EXTERNAL_CONSTANTS[4][8]))) - state[9] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s9), uint64(MATRIX_DIAG_12_U64[9])), sum), uint64(EXTERNAL_CONSTANTS[4][9]))) - state[10] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s10), uint64(MATRIX_DIAG_12_U64[10])), sum), uint64(EXTERNAL_CONSTANTS[4][10]))) - state[11] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s11), uint64(MATRIX_DIAG_12_U64[11])), sum), uint64(EXTERNAL_CONSTANTS[4][11]))) - } - } -} - -func externalLinearLayer(s *[WIDTH]g.GoldilocksField) { +// externalLinearLayer combined with addRC in the end +func externalLinearLayerRC(s *[WIDTH]g.GoldilocksField, rc *[WIDTH]g.GoldilocksField) { + // Load the current state v0 := g.AsUInt128(s[0]) v1 := g.AsUInt128(s[1]) v2 := g.AsUInt128(s[2]) @@ -348,24 +275,27 @@ func externalLinearLayer(s *[WIDTH]g.GoldilocksField) { sum2 := AddUInt128(n2, AddUInt128(n6, n10)) sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) - s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) - s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) + // Putting sums back to the state & addRC + s[0] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n0, sum0), uint64(rc[0]))) + s[4] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n4, sum0), uint64(rc[4]))) + s[8] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n8, sum0), uint64(rc[8]))) - s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) - s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) - s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) + s[1] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n1, sum1), uint64(rc[1]))) + s[5] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n5, sum1), uint64(rc[5]))) + s[9] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n9, sum1), uint64(rc[9]))) - s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) - s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) - s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) + s[2] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n2, sum2), uint64(rc[2]))) + s[6] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n6, sum2), uint64(rc[6]))) + s[10] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n10, sum2), uint64(rc[10]))) - s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) - s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) - s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) + s[3] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n3, sum3), uint64(rc[3]))) + s[7] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n7, sum3), uint64(rc[7]))) + s[11] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n11, sum3), uint64(rc[11]))) } -func externalLinearLayerRC(s *[WIDTH]g.GoldilocksField, rc *[WIDTH]g.GoldilocksField) { +// externalLinearLayer is externalLinearLayerRC without the addRC +func externalLinearLayer(s *[WIDTH]g.GoldilocksField) { + // Load from the current state v0 := g.AsUInt128(s[0]) v1 := g.AsUInt128(s[1]) v2 := g.AsUInt128(s[2]) @@ -411,23 +341,101 @@ func externalLinearLayerRC(s *[WIDTH]g.GoldilocksField, rc *[WIDTH]g.GoldilocksF sum2 := AddUInt128(n2, AddUInt128(n6, n10)) sum3 := AddUInt128(n3, AddUInt128(n7, n11)) - // The round constant is folded into the 128-bit accumulator so each lane needs a single reduction. - // Accumulator stays < 2^70, well within Reduce96Bit's contract. - s[0] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n0, sum0), uint64(rc[0]))) - s[4] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n4, sum0), uint64(rc[4]))) - s[8] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n8, sum0), uint64(rc[8]))) + // Put the sums back to the current state + s[0] = g.Reduce96Bit(AddUInt128(n0, sum0)) + s[4] = g.Reduce96Bit(AddUInt128(n4, sum0)) + s[8] = g.Reduce96Bit(AddUInt128(n8, sum0)) - s[1] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n1, sum1), uint64(rc[1]))) - s[5] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n5, sum1), uint64(rc[5]))) - s[9] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n9, sum1), uint64(rc[9]))) + s[1] = g.Reduce96Bit(AddUInt128(n1, sum1)) + s[5] = g.Reduce96Bit(AddUInt128(n5, sum1)) + s[9] = g.Reduce96Bit(AddUInt128(n9, sum1)) - s[2] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n2, sum2), uint64(rc[2]))) - s[6] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n6, sum2), uint64(rc[6]))) - s[10] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n10, sum2), uint64(rc[10]))) + s[2] = g.Reduce96Bit(AddUInt128(n2, sum2)) + s[6] = g.Reduce96Bit(AddUInt128(n6, sum2)) + s[10] = g.Reduce96Bit(AddUInt128(n10, sum2)) - s[3] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n3, sum3), uint64(rc[3]))) - s[7] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n7, sum3), uint64(rc[7]))) - s[11] = g.Reduce96Bit(AddUint128AndUint64(AddUInt128(n11, sum3), uint64(rc[11]))) + s[3] = g.Reduce96Bit(AddUInt128(n3, sum3)) + s[7] = g.Reduce96Bit(AddUInt128(n7, sum3)) + s[11] = g.Reduce96Bit(AddUInt128(n11, sum3)) +} + +func partialRounds(state *[WIDTH]g.GoldilocksField) { + // addRCI (just the first one) + state[0] = g.AddCanonicalUint64(state[0], uint64(INTERNAL_CONSTANTS[0])) + + for r := 0; r < ROUNDS_P; r++ { + // sboxP inlined: s0 = state[0]^7 + p := state[0] + p2 := g.SquareF(p) + p4 := g.SquareF(p2) + p = g.MulF(p, p2) + s0 := g.MulF(p, p4) + + // internalLinearLayer inlined + u1 := uint64(state[1]) + u2 := uint64(state[2]) + u3 := uint64(state[3]) + u4 := uint64(state[4]) + u5 := uint64(state[5]) + u6 := uint64(state[6]) + u7 := uint64(state[7]) + u8 := uint64(state[8]) + u9 := uint64(state[9]) + u10 := uint64(state[10]) + u11 := uint64(state[11]) + + // summing up in a tree-way + r0 := AddUint64(u1, u2) + r1 := AddUint64(u3, u4) + r2 := AddUint64(u5, u6) + r3 := AddUint64(u7, u8) + r4 := AddUint64(u9, u10) + q0 := AddUInt128(r0, r1) + q1 := AddUInt128(r2, r3) + q2 := AddUint128AndUint64(r4, u11) + restSum := AddUInt128(AddUInt128(q0, q1), q2) + sum := AddUint128AndUint64(restSum, uint64(s0)) + // sum < 2^68 + + var rc0 uint64 + if r+1 < ROUNDS_P { + rc0 = uint64(INTERNAL_CONSTANTS[r+1]) + } else { + rc0 = uint64(EXTERNAL_CONSTANTS[4][0]) + } + // combining addRCI and add in the internalLinearLayer together + // state[0] = (state[0] * MATRIX_DIAG_12_U64[0] + sum) + rc0 + // no overflows since: sum+rc0 = 68-bit + 64-bit < (2^128 - F*F) = 2^128 - (2^128-2^97+2^64) = 2^97-2^64 + state[0] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s0), uint64(MATRIX_DIAG_12_U64[0])), sum), rc0)) + + // each lane: state[i] = state[i] * MATRIX_DIAG_12_U64[i] + sum + if r+1 < ROUNDS_P { + state[1] = g.Reduce128Bit(AddUInt128(MulUInt64(u1, uint64(MATRIX_DIAG_12_U64[1])), sum)) + state[2] = g.Reduce128Bit(AddUInt128(MulUInt64(u2, uint64(MATRIX_DIAG_12_U64[2])), sum)) + state[3] = g.Reduce128Bit(AddUInt128(MulUInt64(u3, uint64(MATRIX_DIAG_12_U64[3])), sum)) + state[4] = g.Reduce128Bit(AddUInt128(MulUInt64(u4, uint64(MATRIX_DIAG_12_U64[4])), sum)) + state[5] = g.Reduce128Bit(AddUInt128(MulUInt64(u5, uint64(MATRIX_DIAG_12_U64[5])), sum)) + state[6] = g.Reduce128Bit(AddUInt128(MulUInt64(u6, uint64(MATRIX_DIAG_12_U64[6])), sum)) + state[7] = g.Reduce128Bit(AddUInt128(MulUInt64(u7, uint64(MATRIX_DIAG_12_U64[7])), sum)) + state[8] = g.Reduce128Bit(AddUInt128(MulUInt64(u8, uint64(MATRIX_DIAG_12_U64[8])), sum)) + state[9] = g.Reduce128Bit(AddUInt128(MulUInt64(u9, uint64(MATRIX_DIAG_12_U64[9])), sum)) + state[10] = g.Reduce128Bit(AddUInt128(MulUInt64(u10, uint64(MATRIX_DIAG_12_U64[10])), sum)) + state[11] = g.Reduce128Bit(AddUInt128(MulUInt64(u11, uint64(MATRIX_DIAG_12_U64[11])), sum)) + } else { + // only works in the last round to combine addRC too + state[1] = g.Reduce128Bit(AddUInt128(MulUInt64(u1, uint64(MATRIX_DIAG_12_U64[1])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][1])))) + state[2] = g.Reduce128Bit(AddUInt128(MulUInt64(u2, uint64(MATRIX_DIAG_12_U64[2])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][2])))) + state[3] = g.Reduce128Bit(AddUInt128(MulUInt64(u3, uint64(MATRIX_DIAG_12_U64[3])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][3])))) + state[4] = g.Reduce128Bit(AddUInt128(MulUInt64(u4, uint64(MATRIX_DIAG_12_U64[4])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][4])))) + state[5] = g.Reduce128Bit(AddUInt128(MulUInt64(u5, uint64(MATRIX_DIAG_12_U64[5])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][5])))) + state[6] = g.Reduce128Bit(AddUInt128(MulUInt64(u6, uint64(MATRIX_DIAG_12_U64[6])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][6])))) + state[7] = g.Reduce128Bit(AddUInt128(MulUInt64(u7, uint64(MATRIX_DIAG_12_U64[7])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][7])))) + state[8] = g.Reduce128Bit(AddUInt128(MulUInt64(u8, uint64(MATRIX_DIAG_12_U64[8])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][8])))) + state[9] = g.Reduce128Bit(AddUInt128(MulUInt64(u9, uint64(MATRIX_DIAG_12_U64[9])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][9])))) + state[10] = g.Reduce128Bit(AddUInt128(MulUInt64(u10, uint64(MATRIX_DIAG_12_U64[10])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][10])))) + state[11] = g.Reduce128Bit(AddUInt128(MulUInt64(u11, uint64(MATRIX_DIAG_12_U64[11])), AddUint128AndUint64(sum, uint64(EXTERNAL_CONSTANTS[4][11])))) + } + } } func sbox(state *[WIDTH]g.GoldilocksField) { From 0915e9c536b70df84e792a01c6c23f116e99c861 Mon Sep 17 00:00:00 2001 From: mert koksal <85784378+Mychecksdead@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:56:47 +0300 Subject: [PATCH 11/12] Updated the comment --- hash/poseidon2_goldilocks_plonky2/poseidon2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hash/poseidon2_goldilocks_plonky2/poseidon2.go b/hash/poseidon2_goldilocks_plonky2/poseidon2.go index 2e35dd7..9cfe123 100644 --- a/hash/poseidon2_goldilocks_plonky2/poseidon2.go +++ b/hash/poseidon2_goldilocks_plonky2/poseidon2.go @@ -405,7 +405,7 @@ func partialRounds(state *[WIDTH]g.GoldilocksField) { } // combining addRCI and add in the internalLinearLayer together // state[0] = (state[0] * MATRIX_DIAG_12_U64[0] + sum) + rc0 - // no overflows since: sum+rc0 = 68-bit + 64-bit < (2^128 - F*F) = 2^128 - (2^128-2^97+2^64) = 2^97-2^64 + // no overflows since: sum+rc0 = 68-bit + 64-bit < (2^128 - 2^64*F) = 2^128 - (2^128-2^96+2^64) = 2^96-2^64 state[0] = g.Reduce128Bit(AddUint128AndUint64(AddUInt128(MulUInt64(uint64(s0), uint64(MATRIX_DIAG_12_U64[0])), sum), rc0)) // each lane: state[i] = state[i] * MATRIX_DIAG_12_U64[i] + sum From e630450157845e4a51371695b6547c82adb9baa9 Mon Sep 17 00:00:00 2001 From: mert koksal <85784378+Mychecksdead@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:41:03 +0300 Subject: [PATCH 12/12] Schnorr Signature Verification Speed Up (#37) Signature verification went down from 290 microseconds to 130~ (combining with #36). (ECgFp5Point's was using lot of divisions, and there weren't any other way to calculate it other than Fermat, which was taking lot of field operations.) After #36, it was 235 microseconds. - Main optimization is passing to AffinePoints for Verification (this brought it down to 175). - Precomputing the generator's affine windows also helped another 10-15 microseconds. - Delaying carries did the last 30 microseconds. There is probably a bit more room, maybe 10%~. --- curve/ecgfp5/point.go | 113 +++++++++ .../goldilocks_quintic_extension.go | 231 ++++++++++++------ signature/schnorr/schnorr.go | 4 +- 3 files changed, 269 insertions(+), 79 deletions(-) diff --git a/curve/ecgfp5/point.go b/curve/ecgfp5/point.go index 9cfcf92..04b9a7d 100644 --- a/curve/ecgfp5/point.go +++ b/curve/ecgfp5/point.go @@ -1,6 +1,8 @@ package ecgfp5 import ( + "fmt" + g "github.com/elliottech/poseidon_crypto/field/goldilocks" gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension" ) @@ -65,6 +67,73 @@ var ( u: gFp5.FP5_ONE, t: gFp5.Element{4, 0, 0, 0, 0}, } + + GeneratorWindowAffine = []AffinePoint{ + { + x: gFp5.Element{12883135586176881569, 4356519642755055268, 5248930565894896907, 2165973894480315022, 2448410071095648785}, + u: gFp5.Element{13835058052060938241, 18446744069414584321, 18446744069414584321, 18446744069414584321, 18446744069414584321}, + }, + { + x: gFp5.Element{16517537419581740386, 6962630169123120981, 12147752690379666704, 16637325971742264607, 2335078582315237010}, + u: gFp5.Element{8457587110646932172, 138591869800252458, 3187444967472352324, 18179149801168653736, 9453003655195557048}, + }, + { + x: gFp5.Element{4546139357324501584, 1393728687664685160, 15208040286522119521, 7903224051455420834, 12463930627278381774}, + u: gFp5.Element{16373828487211693378, 5899455736915524900, 17616512450102495476, 17643201028570366669, 2833280130550676525}, + }, + { + x: gFp5.Element{4341836049185169731, 9111482874850194930, 7798994609726992878, 12619124383509403661, 13047834166950680886}, + u: gFp5.Element{3584786391427904733, 1717626083626375072, 16549008311909030594, 17550175197111849143, 18374971670674568416}, + }, + { + x: gFp5.Element{18121072711119258927, 3394315639035318724, 2648370499809919556, 13348924736921714137, 3428166646246873447}, + u: gFp5.Element{9264305576790077869, 7426254234280836405, 5107777768036114824, 9390769538758625122, 9788182195111344062}, + }, + { + x: gFp5.Element{11080635543643017332, 3122290570793204485, 16632474826839786439, 14883711538614796285, 10396852362099782295}, + u: gFp5.Element{14253916706639980511, 15728038457561632290, 3947138785484546318, 4740958322851071718, 17384736114265519442}, + }, + { + x: gFp5.Element{4763058716218401568, 17879823368956058516, 13578954599286698938, 8634670560943921567, 13706660844700767685}, + u: gFp5.Element{3354778288360932917, 13842278303693121409, 4717821645259836467, 7978743897613094276, 10118963888992569394}, + }, + { + x: gFp5.Element{4026958896735257282, 13595990041314210204, 11499471878438064392, 10019455879458851233, 11986847968355927330}, + u: gFp5.Element{14532821659997761913, 9582789969382797985, 3082219099923033594, 2859656980617778370, 3746047816071136016}, + }, + { + x: gFp5.Element{15935900828168308224, 8668680449802005535, 491315506768012688, 6584881037682113026, 12386385009372860460}, + u: gFp5.Element{13217832923050551864, 51671271962049328, 15400792709153778477, 6752203529649104660, 2855313280735340066}, + }, + { + x: gFp5.Element{8473506523195244465, 2446964921175324878, 17962771942831363202, 6949608686158330138, 9315492999547366751}, + u: gFp5.Element{5171814696081600409, 3025466154945175207, 453302446979841822, 14135305892339872079, 2556388051049291052}, + }, + { + x: gFp5.Element{3960231187580500028, 3695840168764199059, 2914577777792670911, 9249939676680902688, 17553522813502241416}, + u: gFp5.Element{3015152305907361949, 10730034543155667220, 3314242046485170944, 1984395553885795852, 13781645774758249860}, + }, + { + x: gFp5.Element{11575997426281090678, 1534495174840625570, 7539338128385981583, 10393042019577161985, 10667466219175771157}, + u: gFp5.Element{16681365912970185037, 11287896019745355117, 11069899752345274504, 15487604769605237513, 13467978440572613228}, + }, + { + x: gFp5.Element{11192179397773394280, 3555953455665397909, 5346523552109387121, 4514445299325204396, 3932728981135688453}, + u: gFp5.Element{5421638117266109845, 204299445119713184, 6067390115784997081, 16191134954342419157, 4139938600224417293}, + }, + { + x: gFp5.Element{13189785832536261642, 8777097377506996162, 17497140949916325738, 15140279769427597032, 15517274717131999881}, + u: gFp5.Element{1040464435413162742, 9262701069034606854, 2990438819650713743, 18129195737333990255, 12490074042478236606}, + }, + { + x: gFp5.Element{17716508479149156535, 14351380558651795729, 3644546258883003807, 5171318241596472386, 294806796132518330}, + u: gFp5.Element{7535225611936271281, 14682077054502188499, 784215514926156349, 5280586574139275596, 14407528916988559545}, + }, + { + x: gFp5.Element{8681294642569802563, 7751765660802747503, 16382129702876313971, 7447155060842833278, 6859908403876474879}, + u: gFp5.Element{9674486254207846385, 5248970165164951259, 3611784478790504991, 18437168019170350173, 3537959913875671086}, + }, + } ) func (p ECgFp5Point) Equals(rhs ECgFp5Point) bool { @@ -435,6 +504,26 @@ func BatchToAffine(src []ECgFp5Point) []AffinePoint { return res } +// To pre-compute G, 2G, 3G...15G +func GenerateG() { + p := GENERATOR_ECgFp5Point + tmp := make([]ECgFp5Point, WIN_SIZE) + tmp[0] = p + for i := 1; i < WIN_SIZE; i++ { + if (i & 1) == 0 { + tmp[i] = tmp[i-1].Add(p) + } else { + tmp[i] = tmp[i>>1].Double() + } + } + res := BatchToAffine(tmp) + for i := 0; i < 16; i++ { + elx := fmt.Sprintf("gFp5.Element{%d, %d, %d, %d, %d}", res[i].x[0], res[i].x[1], res[i].x[2], res[i].x[3], res[i].x[4]) + elu := fmt.Sprintf("gFp5.Element{%d, %d, %d, %d, %d}", res[i].u[0], res[i].u[1], res[i].u[2], res[i].u[3], res[i].u[4]) + fmt.Printf("AffinePoint{\n x : %s,\n u : %s,\n},\n", elx, elu) + } +} + func (p ECgFp5Point) MakeWindowAffine() []AffinePoint { tmp := make([]ECgFp5Point, WIN_SIZE) tmp[0] = p @@ -466,3 +555,27 @@ func (r ECgFp5Point) Mul(s ECgFp5Scalar) ECgFp5Point { return p } + +// Returns G*scalarA + b*scalarB +func MulAddG(b ECgFp5Point, scalarA, scalarB ECgFp5Scalar) ECgFp5Point { + winA := GeneratorWindowAffine + winB := b.MakeWindowAffine() + + digitsA := make([]int32, (319+WINDOW)/WINDOW) + scalarA.RecodeSigned(digitsA, int32(WINDOW)) + digitsB := make([]int32, (319+WINDOW)/WINDOW) + scalarB.RecodeSigned(digitsB, int32(WINDOW)) + + n := len(digitsA) // == len(digitsB) + + // Top window digit of each scalar, combined into the running point. + p := LookupVarTime(winA, digitsA[n-1]).ToPoint() + p = p.AddAffine(LookupVarTime(winB, digitsB[n-1])) + + for i := n - 2; i >= 0; i-- { + p.SetMDouble(uint32(WINDOW)) // ×2^WINDOW, shared by both scalars + p = p.AddAffine(LookupVarTime(winA, digitsA[i])) + p = p.AddAffine(LookupVarTime(winB, digitsB[i])) + } + return p +} diff --git a/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go b/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go index f4b2907..2ba3c86 100644 --- a/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go +++ b/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go @@ -2,6 +2,7 @@ package goldilocks_quintic_extension import ( "errors" + "math/bits" g "github.com/elliottech/poseidon_crypto/field/goldilocks" ) @@ -96,52 +97,103 @@ func Sub(a, b Element) Element { } } +// reduce128 reduces hi*2^64 + lo modulo the Goldilocks prime. +func reduce128(lo, hi uint64) uint64 { + x_hi_hi := hi >> 32 + x_hi_lo := hi & g.EPSILON + + t0, borrow := bits.Sub64(lo, x_hi_hi, 0) + t0 -= (g.EPSILON & -borrow) + + t1 := x_hi_lo * g.EPSILON + + resWrapped, carry := bits.Add64(t0, t1, 0) + t2 := resWrapped + (g.EPSILON & -carry) + return t2 +} + +// acc192 is a 192-bit accumulator of unreduced products +type acc192 struct{ lo, mid, hi uint64 } + +func (s acc192) addProduct(a, b uint64) (res acc192) { // s += a*b (no reduction) + res = s + hi, lo := bits.Mul64(a, b) + var c uint64 + res.lo, c = bits.Add64(s.lo, lo, 0) + res.mid, c = bits.Add64(s.mid, hi, c) + res.hi += c + return +} + +func (s acc192) addProduct3(a, b uint64) (res acc192) { // s += 3*a*b (the X^5 = 3 fold) + res = s + hi, lo := bits.Mul64(a, b) + d0 := lo << 1 // 3*(hi:lo) = (hi:lo)<<1 + (hi:lo) + d1 := (hi << 1) | (lo >> 63) + d2 := hi >> 63 + var c uint64 + d0, c = bits.Add64(d0, lo, 0) + d1, c = bits.Add64(d1, hi, c) + d2 += c + res.lo, c = bits.Add64(s.lo, d0, 0) + res.mid, c = bits.Add64(s.mid, d1, c) + res.hi += d2 + c + return +} + +func (s acc192) reduce() uint64 { + // value = (lo + mid*2^64) + hi*2^128 ; 2^128 ≡ -2^32 (mod p) + rLow := reduce128(s.lo, s.mid) + r2 := reduce128(s.hi<<32, s.hi>>32) // = hi*2^32 mod p + res, borrow := bits.Sub64(rLow, r2, 0) + return res - (g.EPSILON & -borrow) +} + +// Mul multiplies two F_{p^5} elements with lazy reduction func Mul(a, b Element) Element { - w := FP5_W + a0, a1, a2, a3, a4 := uint64(a[0]), uint64(a[1]), uint64(a[2]), uint64(a[3]), uint64(a[4]) + b0, b1, b2, b3, b4 := uint64(b[0]), uint64(b[1]), uint64(b[2]), uint64(b[3]), uint64(b[4]) + + var s0, s1, s2, s3, s4 acc192 + + // c0 = a0b0 + 3(a1b4 + a2b3 + a3b2 + a4b1) + s0 = s0.addProduct(a0, b0) + s0 = s0.addProduct3(a1, b4) + s0 = s0.addProduct3(a2, b3) + s0 = s0.addProduct3(a3, b2) + s0 = s0.addProduct3(a4, b1) + // c1 = a0b1 + a1b0 + 3(a2b4 + a3b3 + a4b2) + s1 = s1.addProduct(a0, b1) + s1 = s1.addProduct(a1, b0) + s1 = s1.addProduct3(a2, b4) + s1 = s1.addProduct3(a3, b3) + s1 = s1.addProduct3(a4, b2) + // c2 = a0b2 + a1b1 + a2b0 + 3(a3b4 + a4b3) + s2 = s2.addProduct(a0, b2) + s2 = s2.addProduct(a1, b1) + s2 = s2.addProduct(a2, b0) + s2 = s2.addProduct3(a3, b4) + s2 = s2.addProduct3(a4, b3) + // c3 = a0b3 + a1b2 + a2b1 + a3b0 + 3 a4b4 + s3 = s3.addProduct(a0, b3) + s3 = s3.addProduct(a1, b2) + s3 = s3.addProduct(a2, b1) + s3 = s3.addProduct(a3, b0) + s3 = s3.addProduct3(a4, b4) + // c4 = a0b4 + a1b3 + a2b2 + a3b1 + a4b0 + s4 = s4.addProduct(a0, b4) + s4 = s4.addProduct(a1, b3) + s4 = s4.addProduct(a2, b2) + s4 = s4.addProduct(a3, b1) + s4 = s4.addProduct(a4, b0) - a0b0 := g.MulF(a[0], b[0]) - a1b4 := g.MulF(a[1], b[4]) - a2b3 := g.MulF(a[2], b[3]) - a3b2 := g.MulF(a[3], b[2]) - a4b1 := g.MulF(a[4], b[1]) - added := g.AddF(g.AddF(a1b4, a2b3), g.AddF(a3b2, a4b1)) - muld := g.MulF(w, added) - c0 := g.AddF(a0b0, muld) - - a0b1 := g.MulF(a[0], b[1]) - a1b0 := g.MulF(a[1], b[0]) - a2b4 := g.MulF(a[2], b[4]) - a3b3 := g.MulF(a[3], b[3]) - a4b2 := g.MulF(a[4], b[2]) - added = g.AddF(g.AddF(a2b4, a3b3), a4b2) - muld = g.MulF(w, added) - c1 := g.AddF(g.AddF(a0b1, a1b0), muld) - - a0b2 := g.MulF(a[0], b[2]) - a1b1 := g.MulF(a[1], b[1]) - a2b0 := g.MulF(a[2], b[0]) - a3b4 := g.MulF(a[3], b[4]) - a4b3 := g.MulF(a[4], b[3]) - added = g.AddF(a3b4, a4b3) - muld = g.MulF(w, added) - c2 := g.AddF(g.AddF(a0b2, a1b1), g.AddF(a2b0, muld)) - - a0b3 := g.MulF(a[0], b[3]) - a1b2 := g.MulF(a[1], b[2]) - a2b1 := g.MulF(a[2], b[1]) - a3b0 := g.MulF(a[3], b[0]) - a4b4 := g.MulF(a[4], b[4]) - muld = g.MulF(w, a4b4) - c3 := g.AddF(g.AddF(g.AddF(a0b3, a1b2), g.AddF(a2b1, a3b0)), muld) - - a0b4 := g.MulF(a[0], b[4]) - a1b3 := g.MulF(a[1], b[3]) - a2b2 := g.MulF(a[2], b[2]) - a3b1 := g.MulF(a[3], b[1]) - a4b0 := g.MulF(a[4], b[0]) - c4 := g.AddF(g.AddF(g.AddF(a0b4, a1b3), g.AddF(a2b2, a3b1)), a4b0) - - return Element{c0, c1, c2, c3, c4} + return Element{ + g.GoldilocksField(s0.reduce()), + g.GoldilocksField(s1.reduce()), + g.GoldilocksField(s2.reduce()), + g.GoldilocksField(s3.reduce()), + g.GoldilocksField(s4.reduce()), + } } // Returns a / b. Panics if b == 0. @@ -162,40 +214,65 @@ func ExpPowerOf2(x Element, power int) Element { return res } +// add3 adds the 3-limb value v2:v1:v0 into the accumulator. +func (s acc192) add3(v0, v1, v2 uint64) (res acc192) { + res = s + var c uint64 + res.lo, c = bits.Add64(s.lo, v0, 0) + res.mid, c = bits.Add64(s.mid, v1, c) + res.hi += v2 + c + return +} + +func (s acc192) addProduct2(a, b uint64) acc192 { // s += 2*a*b (shift the product left 1) + hi, lo := bits.Mul64(a, b) + return s.add3(lo<<1, (hi<<1)|(lo>>63), hi>>63) +} + +func (s acc192) addProduct6(a, b uint64) acc192 { // s += 6*a*b = 4*ab + 2*ab + hi, lo := bits.Mul64(a, b) + q0, q1, q2 := lo<<2, (hi<<2)|(lo>>62), hi>>62 // 4x + r0, r1, r2 := lo<<1, (hi<<1)|(lo>>63), hi>>63 // 2x + var c uint64 + q0, c = bits.Add64(q0, r0, 0) + q1, c = bits.Add64(q1, r1, c) + q2 += r2 + c + return s.add3(q0, q1, q2) +} + +// Square squares an F_{p^5} element with lazy reduction func Square(a Element) Element { - w := FP5_W - double_w := g.AddF(w, w) - - a0s := g.MulF(a[0], a[0]) - a1a4 := g.MulF(a[1], a[4]) - a2a3 := g.MulF(a[2], a[3]) - added := g.AddF(a1a4, a2a3) - muld := g.MulF(double_w, added) - c0 := g.AddF(a0s, muld) - - a0Double := g.AddF(a[0], a[0]) - a0Doublea1 := g.MulF(a0Double, a[1]) - a2a4DoubleW := g.MulF(g.MulF(a[2], a[4]), double_w) - a3a3w := g.MulF(g.MulF(a[3], a[3]), w) - c1 := g.AddF(g.AddF(a0Doublea1, a2a4DoubleW), a3a3w) - - a0Doublea2 := g.MulF(a0Double, a[2]) - a1Square := g.MulF(a[1], a[1]) - a4a3DoubleW := g.MulF(g.MulF(a[4], a[3]), double_w) - c2 := g.AddF(g.AddF(a0Doublea2, a1Square), a4a3DoubleW) - - a1Double := g.AddF(a[1], a[1]) - a0Doublea3 := g.MulF(a0Double, a[3]) - a1Doublea2 := g.MulF(a1Double, a[2]) - a4SquareW := g.MulF(g.MulF(a[4], a[4]), w) - c3 := g.AddF(g.AddF(a0Doublea3, a1Doublea2), a4SquareW) - - a0Doublea4 := g.MulF(a0Double, a[4]) - a1Doublea3 := g.MulF(a1Double, a[3]) - a2Square := g.MulF(a[2], a[2]) - c4 := g.AddF(g.AddF(a0Doublea4, a1Doublea3), a2Square) - - return Element{c0, c1, c2, c3, c4} + a0, a1, a2, a3, a4 := uint64(a[0]), uint64(a[1]), uint64(a[2]), uint64(a[3]), uint64(a[4]) + var s0, s1, s2, s3, s4 acc192 + + // c0 = a0^2 + 6 a1a4 + 6 a2a3 + s0 = s0.addProduct(a0, a0) + s0 = s0.addProduct6(a1, a4) + s0 = s0.addProduct6(a2, a3) + // c1 = 2 a0a1 + 6 a2a4 + 3 a3^2 + s1 = s1.addProduct2(a0, a1) + s1 = s1.addProduct6(a2, a4) + s1 = s1.addProduct3(a3, a3) + // c2 = 2 a0a2 + a1^2 + 6 a3a4 + s2 = s2.addProduct2(a0, a2) + s2 = s2.addProduct(a1, a1) + s2 = s2.addProduct6(a3, a4) + // c3 = 2 a0a3 + 2 a1a2 + 3 a4^2 + s3 = s3.addProduct2(a0, a3) + s3 = s3.addProduct2(a1, a2) + s3 = s3.addProduct3(a4, a4) + // c4 = 2 a0a4 + 2 a1a3 + a2^2 + s4 = s4.addProduct2(a0, a4) + s4 = s4.addProduct2(a1, a3) + s4 = s4.addProduct(a2, a2) + + return Element{ + g.GoldilocksField(s0.reduce()), + g.GoldilocksField(s1.reduce()), + g.GoldilocksField(s2.reduce()), + g.GoldilocksField(s3.reduce()), + g.GoldilocksField(s4.reduce()), + } } func Triple(a Element) Element { diff --git a/signature/schnorr/schnorr.go b/signature/schnorr/schnorr.go index 25e463c..b18b345 100644 --- a/signature/schnorr/schnorr.go +++ b/signature/schnorr/schnorr.go @@ -192,12 +192,12 @@ func IsSchnorrSignatureValid(pubKey, hashedMsg gFp5.Element, sig Signature) bool // Decode public key (canonical decoding automatically ensures valid group element) // No subgroup check needed due to prime order! - pubKeyWs, ok := curve.DecodeFp5AsWeierstrass(pubKey) + pubKeyWs, ok := curve.Decode(pubKey) if !ok { return false } - rV := curve.MulAdd2(curve.GENERATOR_WEIERSTRASS, pubKeyWs, sig.S, sig.E).Encode() // r_v = s*G + e*pk + rV := curve.MulAddG(pubKeyWs, sig.S, sig.E).Encode() // r_v = s*G + e*pk preImage := make([]g.GoldilocksField, 5+5) copy(preImage[:5], rV[:])