From 7d615f212d97eac6f7157b5eeb91dc296e4a08e1 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 6 Oct 2026 08:33:18 +0000 Subject: [PATCH 1/2] test: add regression test for AssertFinalExpIsOne for BW6-761 Signed-off-by: Ivo Kubjas --- .../emulated/sw_bw6761/pairing_test.go | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index ea462a2b55..fb870a7bf7 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -126,6 +126,38 @@ func TestFinalExponentiationIsOneSoundness(t *testing.T) { assert.Error(err) } +// assertFinalExponentiationIsOneDirectCircuit asserts the final-exponentiation +// property of a witness-supplied accumulator, as in +// https://eprint.iacr.org/2024/640.pdf Section 4 where the Miller loop output +// is a prover input. +type assertFinalExponentiationIsOneDirectCircuit struct { + InGt GTEl +} + +func (c *assertFinalExponentiationIsOneDirectCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + pairing.AssertFinalExponentiationIsOne(&c.InGt) + return nil +} + +// TestFinalExponentiationIsOneRejectsZeroAccumulator is a regression for the +// zero-residue-witness underconstraint: the check x == residueWitness^Λ is +// homogeneous in the hint output, so for a zero accumulator (x = 0, which is +// not in the multiplicative target group — its final exponentiation is 0, not +// 1) the all-zero residue witness degenerated the exponentiation chain and the +// unchecked division to 0 == 0 and was accepted. The invertibility anchor +// (residueWitness·residueWitness⁻¹ == 1) now rejects it. +func TestFinalExponentiationIsOneRejectsZeroAccumulator(t *testing.T) { + assert := test.NewAssert(t) + var zero bw6761.GT // = 0 + witness := assertFinalExponentiationIsOneDirectCircuit{InGt: NewGTEl(zero)} + err := test.IsSolved(&assertFinalExponentiationIsOneDirectCircuit{}, &witness, ecc.BN254.ScalarField()) + assert.Error(err, "zero accumulator must be rejected: Inverse(0) anchor is unsatisfiable") +} + type FinalExponentiationCircuit struct { InGt GTEl Res GTEl From 347f228dedca60203fc8809a458b8def7488e631 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 6 Oct 2026 08:33:36 +0000 Subject: [PATCH 2/2] fix: ensure hinted residueWitness is invertible Signed-off-by: Ivo Kubjas --- std/algebra/emulated/sw_bw6761/pairing.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index b54d2f6360..919cfe8fb6 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -145,6 +145,15 @@ func (pr *Pairing) AssertFinalExponentiationIsOne(x *GTEl) { A5: *res[5], } + // Constrain residueWitness to be invertible (non-zero). The check below, + // x == residueWitness^Λ, is homogeneous in the hint output, so for x == 0 + // the all-zero residue witness would satisfy it: the exponentiation chain + // degenerates to 0 and DivUnchecked(0, 0) only enforces 0 = quotient·0, + // leaving the quotient free. But 0 is not in the multiplicative target + // group and its final exponentiation is not one. Inverse asserts + // residueWitness·residueWitness⁻¹ == 1, ruling that out. + pr.Ext6.Inverse(&residueWitness) + // Check that x == residueWitness^Λ // where Λ = x₀+1+p(x₀³-x₀²-x₀) and residueWitness from the hint.