Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions frontend/cs/r1cs/api_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,18 @@ func (builder *builder[E]) AssertIsBoolean(i1 frontend.Variable) {
}

func (builder *builder[E]) AssertIsCrumb(i1 frontend.Variable) {
i1 = builder.MulAcc(builder.Mul(-3, i1), i1, i1)
i1 = builder.MulAcc(builder.Mul(2, i1), i1, i1)
builder.AssertIsEqual(i1, 0)
if c, ok := builder.constantValue(i1); ok {
if i, ok := builder.cs.Uint64(c); ok && i < 4 {
return
}
panic(fmt.Sprintf("AssertIsCrumb constant input %s is not a crumb", builder.cs.String(c)))
}

// i1 (i1-1) (i1-2) (i1-3) = (i1² - 3i1) (i1² - 3i1 + 2)
// take X := i1² - 3i1 and we get X (X+2) = 0
x := builder.MulAcc(builder.Mul(-3, i1), i1, i1)
x = builder.MulAcc(builder.Mul(2, x), x, x)
builder.AssertIsEqual(x, 0)
}

// AssertIsLessOrEqual adds assertion in constraint builder (v ⩽ bound)
Expand Down
11 changes: 7 additions & 4 deletions frontend/cs/scs/api_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,21 +119,24 @@ func (builder *builder[E]) AssertIsBoolean(i1 frontend.Variable) {
}

func (builder *builder[E]) AssertIsCrumb(i1 frontend.Variable) {
const errorMsg = "AssertIsCrumb: input is not a crumb"
if c, ok := builder.constantValue(i1); ok {
if i, ok := builder.cs.Uint64(c); ok && i < 4 {
return
}
panic(errorMsg)
panic(fmt.Sprintf("AssertIsCrumb constant input %s is not a crumb", builder.cs.String(c)))
}

// i1 (i1-1) (i1-2) (i1-3) = (i1² - 3i1) (i1² - 3i1 + 2)
// take X := i1² - 3i1 and we get X (X+2) = 0

// usually MulAcc is a composition in PLONK, unless we have the condition
// a/c == const. This holds here so this is only a single constraint.
x := builder.MulAcc(builder.Mul(-3, i1), i1, i1).(expr.Term[E])

// TODO @Tabaie Ideally this entire function would live in std/math/bits as it is quite specialized;
// however using two generic MulAccs and an AssertIsEqual results in three constraints rather than two.
// usually bit assertions are defined in [std/math/bits] package, but we
// already have it and want to keep it backwards compatible. By doing it
// directly we can avoid a constraint as we do 2X + X^2 == 0 in a single
// constraint.
builder.addPlonkConstraint(sparseR1C[E]{
xa: x.VID,
xb: x.VID,
Expand Down
14 changes: 14 additions & 0 deletions internal/stats/latest_stats.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
circuit,curve,backend,nbConstraints,nbWires
api/AssertIsCrumb,bn254,groth16,3,2
api/AssertIsCrumb,bls12_377,groth16,3,2
api/AssertIsCrumb,bls12_381,groth16,3,2
api/AssertIsCrumb,bls24_315,groth16,3,2
api/AssertIsCrumb,bls24_317,groth16,3,2
api/AssertIsCrumb,bw6_761,groth16,3,2
api/AssertIsCrumb,bw6_633,groth16,3,2
api/AssertIsCrumb,bn254,plonk,2,1
api/AssertIsCrumb,bls12_377,plonk,2,1
api/AssertIsCrumb,bls12_381,plonk,2,1
api/AssertIsCrumb,bls24_315,plonk,2,1
api/AssertIsCrumb,bls24_317,plonk,2,1
api/AssertIsCrumb,bw6_761,plonk,2,1
api/AssertIsCrumb,bw6_633,plonk,2,1
api/AssertIsLessOrEqual,bn254,groth16,1523,1367
api/AssertIsLessOrEqual,bls12_377,groth16,1517,1349
api/AssertIsLessOrEqual,bls12_381,groth16,1529,1405
Expand Down
4 changes: 4 additions & 0 deletions internal/stats/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func initSnippets() {
api.AssertIsLessOrEqual(newVariable(), bound)
})

registerSnippet("api/AssertIsCrumb", func(api frontend.API, newVariable func() frontend.Variable) {
api.AssertIsCrumb(newVariable())
})

// add std snippets
registerSnippet("math/bits.ToBinary", func(api frontend.API, newVariable func() frontend.Variable) {
_ = bits.ToBinary(api, newVariable())
Expand Down