Skip to content

Commit 47a0dc3

Browse files
authored
Merge branch 'main' into Soumya8898/extend-issuer-wallet
2 parents 3397064 + eec69e9 commit 47a0dc3

1 file changed

Lines changed: 48 additions & 35 deletions

File tree

  • token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof

token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof/ipa.go

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -478,12 +478,29 @@ func CloneGenerators(LeftGenerators, RightGenerators []*mathlib.G1) ([]*mathlib.
478478
return leftGen, rightGen
479479
}
480480

481-
// ComputeSVector computes the s vector and its entry-wise inverse of s as detailed below:
482-
// b(i,j) = 1 if (logn - j)^{th} bit of i-1 is 1, else its -1
483-
// s[i] = \prod_{j=1}^{\log n} x_j^{bits(i,j)}
484-
// sInv[i] = s[i].Inverse
485-
// Input: n, challenges = [x_1,\ldots,x_{\log n}]
486-
// Returns (s, sInv) where sInv[i] = s[i]^{-1}, computed via batch inversion.
481+
// ComputeSVector computes the s vector and its entry-wise inverse using
482+
// a dual-butterfly (doubling) recurrence in O(n) multiplications.
483+
//
484+
// Each entry is defined as:
485+
//
486+
// s[i] = ∏_{r=0}^{k-1} (bit(i, k-1-r) == 1 ? x_r : x_r^{-1})
487+
// sInv[i] = s[i]^{-1}
488+
//
489+
// where k = log₂(n) and x_r = challenges[r].
490+
//
491+
// The butterfly builds both vectors simultaneously:
492+
//
493+
// Round r: for each existing entry i in [0, 2^r):
494+
// s[i + 2^r] = s[i] · x_{k-1-r} (bit set → challenge)
495+
// s[i] = s[i] · x_{k-1-r}^{-1} (bit unset → inverse)
496+
// sInv[i + 2^r] = sInv[i] · x_{k-1-r}^{-1} (swapped)
497+
// sInv[i] = sInv[i] · x_{k-1-r} (swapped)
498+
//
499+
// This replaces the previous O(n·log n) nested-loop implementation and
500+
// eliminates the final BatchInverse call for sInv.
501+
//
502+
// Input: n, challenges = [x_0, …, x_{k-1}] where n = 2^k.
503+
// Returns (s, sInv) where sInv[i] = s[i]^{-1}.
487504
func ComputeSVector(n int, challenges []*mathlib.Zr, curve *mathlib.Curve) ([]*mathlib.Zr, []*mathlib.Zr) {
488505
log2n := len(challenges)
489506

@@ -492,40 +509,36 @@ func ComputeSVector(n int, challenges []*mathlib.Zr, curve *mathlib.Curve) ([]*m
492509
panic("n must equal 2^(number of challenges)")
493510
}
494511

495-
// Precompute challenge inverses (log2n inversions instead of O(n*log2n))
512+
// Precompute challenge inverses: O(log n) with a single field inversion.
496513
challengeInvs := math.BatchInverse(challenges, curve)
497514

515+
// Pre-allocate both vectors. Entries [1..n-1] are initialised to zero
516+
// so that ModMulInPlace can write into them without prior allocation.
498517
s := make([]*mathlib.Zr, n)
499-
500-
for i := 1; i <= n; i++ {
501-
// Start with s_i = 1
502-
si := math.One(curve)
503-
504-
// Compute product over j=1 to log2(n).
505-
// At round j the generator fold splits on bit (log2n - j) of the
506-
// 0-based index: first half (bit=0) picks up x_j^{-1}, second
507-
// half (bit=1) picks up x_j — matching reduceGenerators.
508-
for j := 1; j <= log2n; j++ {
509-
bitPosition := log2n - j
510-
iMinus1 := i - 1
511-
bitIsSet := (iMinus1 >> bitPosition) & 1
512-
513-
var factor *mathlib.Zr
514-
if bitIsSet == 1 {
515-
// second half at round j => x_j
516-
factor = challenges[j-1]
517-
} else {
518-
// first half at round j => x_j^{-1}
519-
factor = challengeInvs[j-1]
520-
}
521-
522-
// Multiply: s_i *= factor
523-
si = curve.ModMul(si, factor, curve.GroupOrder)
518+
sInv := make([]*mathlib.Zr, n)
519+
s[0] = math.One(curve)
520+
sInv[0] = math.One(curve)
521+
for i := 1; i < n; i++ {
522+
s[i] = curve.NewZrFromInt(0)
523+
sInv[i] = curve.NewZrFromInt(0)
524+
}
525+
526+
// Dual butterfly: O(n) total multiplications.
527+
for r := range log2n {
528+
halfLen := 1 << r
529+
c := challenges[log2n-1-r]
530+
cInv := challengeInvs[log2n-1-r]
531+
for i := range halfLen {
532+
// s: bit set → challenge, bit unset → inverse.
533+
// Compute s[i+halfLen] BEFORE mutating s[i].
534+
curve.ModMulInPlace(s[i+halfLen], s[i], c, curve.GroupOrder)
535+
s[i] = curve.ModMul(s[i], cInv, curve.GroupOrder)
536+
537+
// sInv: factors are swapped relative to s.
538+
curve.ModMulInPlace(sInv[i+halfLen], sInv[i], cInv, curve.GroupOrder)
539+
sInv[i] = curve.ModMul(sInv[i], c, curve.GroupOrder)
524540
}
525-
526-
s[i-1] = si
527541
}
528-
sInv := math.BatchInverse(s, curve)
529542

530543
return s, sInv
531544
}

0 commit comments

Comments
 (0)