Skip to content

Commit

Permalink
Merge pull request #36 from worldcoin/wip/mk/reducedness-check
Browse files Browse the repository at this point in the history
Add a reducedness check to binary reps
  • Loading branch information
kustosz committed Sep 7, 2023
2 parents 4caca9c + 663d8c0 commit bdb3d85
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 10 deletions.
37 changes: 34 additions & 3 deletions prover/circuit_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,35 @@ func VerifyProof(api frontend.API, proofSet, helper []frontend.Variable) fronten
return sum
}

// ReducedModRCheck Checks a little-endian array of bits asserting that it represents a number that
// is less than the field modulus R.
type ReducedModRCheck struct {
Input []frontend.Variable
}

func (r *ReducedModRCheck) DefineGadget(api abstractor.API) []frontend.Variable {
field := api.Compiler().Field()
if len(r.Input) < field.BitLen() {
// input is shorter than the field, so it's definitely reduced
return []frontend.Variable{}
}
var failed frontend.Variable = 0 // we already know number is > R
var succeeded frontend.Variable = 0 // we already know number is < R
for i := len(r.Input) - 1; i >= 0; i-- {
api.AssertIsBoolean(r.Input[i])
if field.Bit(i) == 0 {
// if number is not already < R, a 1 in this position means it's > R
failed = api.Select(succeeded, 0, api.Or(r.Input[i], failed))
} else {
bitNeg := api.Sub(1, r.Input[i])
// if number isn't already > R, a 0 in this position means it's < R
succeeded = api.Select(failed, 0, api.Or(bitNeg, succeeded))
}
}
api.AssertIsEqual(succeeded, 1)
return []frontend.Variable{}
}

// SwapBitArrayEndianness Swaps the endianness of the bit pattern in bits,
// returning the result in newBits.
//
Expand Down Expand Up @@ -73,13 +102,15 @@ func SwapBitArrayEndianness(bits []frontend.Variable) (newBits []frontend.Variab
return newBits, nil
}

// ToBinaryBigEndian converts the provided variable to the corresponding bit
// pattern using big-endian byte ordering.
// ToReducedBinaryBigEndian converts the provided variable to the corresponding bit
// pattern using big-endian byte ordering. It also makes sure to pick the smallest
// binary representation (i.e. one that is reduced modulo scalar field order).
//
// Raises a bitPatternLengthError if the number of bits in variable is not a
// whole number of bytes.
func ToBinaryBigEndian(variable frontend.Variable, size int, api frontend.API) (bitsBigEndian []frontend.Variable, err error) {
func ToReducedBinaryBigEndian(variable frontend.Variable, size int, api frontend.API) (bitsBigEndian []frontend.Variable, err error) {
bitsLittleEndian := api.ToBinary(variable, size)
abstractor.CallGadget(api, &ReducedModRCheck{Input: bitsLittleEndian})
return SwapBitArrayEndianness(bitsLittleEndian)
}

Expand Down
6 changes: 3 additions & 3 deletions prover/deletion_circuit.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ func (circuit *DeletionMbuCircuit) Define(api frontend.API) error {
var err error

for i := 0; i < circuit.BatchSize; i++ {
bits, err = ToBinaryBigEndian(circuit.DeletionIndices[i], 32, api)
bits, err = ToReducedBinaryBigEndian(circuit.DeletionIndices[i], 32, api)
if err != nil {
return err
}
kh.Write(bits...)
}

bits, err = ToBinaryBigEndian(circuit.PreRoot, 256, api)
bits, err = ToReducedBinaryBigEndian(circuit.PreRoot, 256, api)
if err != nil {
return err
}
kh.Write(bits...)

bits, err = ToBinaryBigEndian(circuit.PostRoot, 256, api)
bits, err = ToReducedBinaryBigEndian(circuit.PostRoot, 256, api)
if err != nil {
return err
}
Expand Down
8 changes: 4 additions & 4 deletions prover/insertion_circuit.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,26 @@ func (circuit *InsertionMbuCircuit) Define(api frontend.API) error {
// We convert all the inputs to the keccak hash to use big-endian (network) byte
// ordering so that it agrees with Solidity. This ensures that we don't have to
// perform the conversion inside the contract and hence save on gas.
bits, err = ToBinaryBigEndian(circuit.StartIndex, 32, api)
bits, err = ToReducedBinaryBigEndian(circuit.StartIndex, 32, api)
if err != nil {
return err
}
kh.Write(bits...)

bits, err = ToBinaryBigEndian(circuit.PreRoot, 256, api)
bits, err = ToReducedBinaryBigEndian(circuit.PreRoot, 256, api)
if err != nil {
return err
}
kh.Write(bits...)

bits, err = ToBinaryBigEndian(circuit.PostRoot, 256, api)
bits, err = ToReducedBinaryBigEndian(circuit.PostRoot, 256, api)
if err != nil {
return err
}
kh.Write(bits...)

for i := 0; i < circuit.BatchSize; i++ {
bits, err = ToBinaryBigEndian(circuit.IdComms[i], 256, api)
bits, err = ToReducedBinaryBigEndian(circuit.IdComms[i], 256, api)
if err != nil {
return err
}
Expand Down

0 comments on commit bdb3d85

Please sign in to comment.