Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
gbotrel committed Feb 15, 2022
2 parents 00a8dce + 91d463f commit a88771d
Show file tree
Hide file tree
Showing 113 changed files with 6,490 additions and 4,459 deletions.
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,49 @@
<a name="v0.6.4"></a>

## [v0.6.4] - 2022-02-15

### Build

- update to gnark-crpto v0.6.1

### Feat

- Constraint system solvers (Groth16 and PlonK) now run in parallel

### Fix

- `api.DivUnchecked` with PlonK between 2 constants was incorrect

### Perf

- **EdDSA:** `std/algebra/twistededwards` takes ~2K less constraints (Groth16). Bandersnatch benefits from same improvments.


### Pull Requests

- Merge pull request [#259](https://github.com/consensys/gnark/issues/259) from ConsenSys/perf-parallel-solver
- Merge pull request [#261](https://github.com/consensys/gnark/issues/261) from ConsenSys/feat/kzg_updated
- Merge pull request [#257](https://github.com/consensys/gnark/issues/257) from ConsenSys/perf/EdDSA
- Merge pull request [#253](https://github.com/consensys/gnark/issues/253) from ConsenSys/feat/fft_cosets

<a name="v0.6.3"></a>

## [v0.6.3] - 2022-02-13

### Feat

- MiMC changes: api doesn't take a "seed" parameter. MiMC impl matches Ethereum one.

### Fix

- fixes [#255](https://github.com/consensys/gnark/issues/255) variable visibility inheritance regression
- counter was set with PLONK backend ID in R1CS
- R1CS Solver was incorrectly calling a "MulByCoeff" instead of "DivByCoeff" (no impact, coeff was always 1 or -1)
- SparseR1CS cbor unmarshal failed [#247](https://github.com/consensys/gnark/issues/247) for compiled.Term


### Pull Requests

- Merge pull request [#256](https://github.com/consensys/gnark/issues/256) from ConsenSys/fix-bug-compile-visibility
- Merge pull request [#249](https://github.com/consensys/gnark/issues/249) from ConsenSys/perf-ccs-hint
- Merge pull request [#248](https://github.com/consensys/gnark/issues/248) from ConsenSys/perf-ccs-solver
Expand All @@ -24,9 +55,11 @@
## [v0.6.1] - 2022-01-28

### Build

- go version dependency bumped from 1.16 to 1.17

### Feat

- added witness.MarshalJSON and witness.MarshalBinary
- added `ccs.GetSchema()` - the schema of a circuit is required for witness json (de)serialization
- added `ccs.GetConstraints()` - returns a list of human-readable constraints
Expand All @@ -35,18 +68,21 @@
- addition of `Cmp` in the circuit API

### Refactor

- compiled.Visbility -> schema.Visibiility
- witness.WriteSequence -> schema.WriteSequence
- killed `ReadAndProve` and `ReadAndVerify` (plonk)
- killed `ReadAndProve` and `ReadAndVerify` (groth16)
- remove embbed struct tag for frontend.Variable fields

### Docs

- **backend:** unify documentation for options
- **frontend:** unify docs for options
- **test:** unify documentation for options

### Pull Requests

- Merge pull request [#244](https://github.com/consensys/gnark/issues/244) from ConsenSys/plonk-human-readable
- Merge pull request [#237](https://github.com/consensys/gnark/issues/237) from ConsenSys/ccs-get-constraints
- Merge pull request [#233](https://github.com/consensys/gnark/issues/233) from ConsenSys/feat/api_cmp
Expand Down
6 changes: 3 additions & 3 deletions debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ func TestPrintln(t *testing.T) {
expected.WriteString("debug_test.go:27 26 42\n")
expected.WriteString("debug_test.go:29 bits 1\n")
expected.WriteString("debug_test.go:30 circuit {A: 2, B: 11}\n")
expected.WriteString("debug_test.go:34 m <unsolved>\n")
expected.WriteString("debug_test.go:34 m .*\n")

{
trace, _ := getGroth16Trace(&circuit, &witness)
assert.Equal(expected.String(), trace)
assert.Regexp(expected.String(), trace)
}

{
trace, _ := getPlonkTrace(&circuit, &witness)
assert.Equal(expected.String(), trace)
assert.Regexp(expected.String(), trace)
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/rollup/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (

var (
// SizeAccount byte size of a serialized account (5*32bytes)
// index || nonce || balance || pubkeyX || pubkeyY, each chunk is 32 bytes
// index nonce balance pubkeyX pubkeyY, each chunk is 32 bytes
SizeAccount = 160
)

Expand All @@ -48,7 +48,7 @@ func (ac *Account) Reset() {

// Serialize serializes the account as a concatenation of 5 chunks of 256 bits
// one chunk per field (pubKey has 2 chunks), except index and nonce that are concatenated in a single 256 bits chunk
// index || nonce || balance || pubkeyX || pubkeyY, each chunk is 256 bits
// index nonce balance pubkeyX pubkeyY, each chunk is 256 bits
func (ac *Account) Serialize() []byte {

//var buffer bytes.Buffer
Expand Down
2 changes: 1 addition & 1 deletion examples/rollup/circuit.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (circuit *Circuit) Define(api frontend.API) error {
// verifySignatureTransfer ensures that the signature of the transfer is valid
func verifyTransferSignature(api frontend.API, t TransferConstraints, hFunc mimc.MiMC) error {

// the signature is on h(nonce || amount || senderpubKey (x&y) || receiverPubkey(x&y))
// the signature is on h(nonce amount senderpubKey (x&y) receiverPubkey(x&y))
hFunc.Write(t.Nonce, t.Amount, t.SenderPubKey.A.X, t.SenderPubKey.A.Y, t.ReceiverPubKey.A.X, t.ReceiverPubKey.A.Y)
htransfer := hFunc.Sum()

Expand Down
6 changes: 3 additions & 3 deletions examples/rollup/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ func NewQueue(batchSize int) Queue {

// Operator represents a rollup operator
type Operator struct {
State []byte // list of accounts: index || nonce || balance || pubkeyX || pubkeyY, each chunk is 256 bits
HashState []byte // Hashed version of the state, each chunk is 256bits: ... || H(index || nonce || balance || pubkeyX || pubkeyY)) || ...
State []byte // list of accounts: index nonce balance pubkeyX pubkeyY, each chunk is 256 bits
HashState []byte // Hashed version of the state, each chunk is 256bits: ... H(index nonce balance pubkeyX pubkeyY)) ...
AccountMap map[string]uint64 // hashmap of all available accounts (the key is the account.pubkey.X), the value is the index of the account in the state
nbAccounts int // number of accounts managed by this operator
h hash.Hash // hash function used to build the Merkle Tree
Expand Down Expand Up @@ -178,7 +178,7 @@ func (o *Operator) updateState(t Transfer, numTransfer int) error {
o.witnesses.Transfers[numTransfer].Signature.S = t.signature.S[:]

// verifying the signature. The msg is the hash (o.h) of the transfer
// nonce || amount || senderpubKey(x&y) || receiverPubkey(x&y)
// nonce amount senderpubKey(x&y) receiverPubkey(x&y)
resSig, err := t.Verify(o.h)
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions examples/rollup/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (t *Transfer) Sign(priv eddsa.PrivateKey, h hash.Hash) (eddsa.Signature, er
//var frNonce, msg fr.Element
var frNonce fr.Element

// serializing transfer. The signature is on h(nonce || amount || senderpubKey (x&y) || receiverPubkey(x&y))
// serializing transfer. The signature is on h(nonce amount senderpubKey (x&y) receiverPubkey(x&y))
// (each pubkey consist of 2 chunks of 256bits)
frNonce.SetUint64(t.nonce)
b := frNonce.Bytes()
Expand Down Expand Up @@ -91,7 +91,7 @@ func (t *Transfer) Verify(h hash.Hash) (bool, error) {
var frNonce fr.Element

// serializing transfer. The msg to sign is
// nonce || amount || senderpubKey(x&y) || receiverPubkey(x&y)
// nonce amount senderpubKey(x&y) receiverPubkey(x&y)
// (each pubkey consist of 2 chunks of 256bits)
frNonce.SetUint64(t.nonce)
b := frNonce.Bytes()
Expand Down
2 changes: 1 addition & 1 deletion frontend/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type API interface {
// AssertIsDifferent fails if i1 == i2
AssertIsDifferent(i1, i2 Variable)

// AssertIsBoolean fails if v != 0 || v != 1
// AssertIsBoolean fails if v != 0 v != 1
AssertIsBoolean(i1 Variable)

// AssertIsLessOrEqual fails if v > bound
Expand Down
4 changes: 2 additions & 2 deletions frontend/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ type NewBuilder func(ecc.ID) (Builder, error)
// from the declarative code
//
// 3. finally, it converts that to a ConstraintSystem.
// if zkpID == backend.GROTH16 --> R1CS
// if zkpID == backend.PLONK --> SparseR1CS
// if zkpID == backend.GROTH16 R1CS
// if zkpID == backend.PLONK SparseR1CS
//
// initialCapacity is an optional parameter that reserves memory in slices
// it should be set to the estimated number of constraints in the circuit, if known.
Expand Down
2 changes: 1 addition & 1 deletion frontend/cs/plonk/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (system *sparseR1CS) DivUnchecked(i1, i2 frontend.Variable) frontend.Variab
q := system.CurveID.Info().Fr.Modulus()
return r.ModInverse(&r, q).
Mul(&l, &r).
Mod(&l, q)
Mod(&r, q)
}
if system.IsConstant(i2) {
c := utils.FromInterface(i2)
Expand Down
6 changes: 3 additions & 3 deletions frontend/cs/plonk/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (system *sparseR1CS) AssertIsDifferent(i1, i2 frontend.Variable) {
system.Inverse(system.Sub(i1, i2))
}

// AssertIsBoolean fails if v != 0 || v != 1
// AssertIsBoolean fails if v != 0 v != 1
func (system *sparseR1CS) AssertIsBoolean(i1 frontend.Variable) {
if system.IsConstant(i1) {
c := utils.FromInterface(i1)
Expand Down Expand Up @@ -125,7 +125,7 @@ func (system *sparseR1CS) mustBeLessOrEqVar(a compiled.Term, bound compiled.Term
l := system.Sub(1, t, aBits[i])

// note if bound[i] == 1, this constraint is (1 - ai) * ai == 0
// --> this is a boolean constraint
// this is a boolean constraint
// if bound[i] == 0, t must be 0 or 1, thus ai must be 0 or 1 too
system.markBoolean(aBits[i].(compiled.Term)) // this does not create a constraint

Expand Down Expand Up @@ -172,7 +172,7 @@ func (system *sparseR1CS) mustBeLessOrEqCst(a compiled.Term, bound big.Int) {
}

p := make([]frontend.Variable, nbBits+1)
// p[i] == 1 --> a[j] == c[j] for all j >= i
// p[i] == 1 a[j] == c[j] for all j i
p[nbBits] = 1

for i := nbBits - 1; i >= t; i-- {
Expand Down
103 changes: 103 additions & 0 deletions frontend/cs/plonk/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ HINTLOOP:
}
res.MHints = shiftedMap

// build levels
res.Levels = buildLevels(res)

switch cs.CurveID {
case ecc.BLS12_377:
return bls12377r1cs.NewSparseR1CS(res, cs.Coeffs), nil
Expand All @@ -138,3 +141,103 @@ HINTLOOP:
func (cs *sparseR1CS) SetSchema(s *schema.Schema) {
cs.Schema = s
}

func buildLevels(ccs compiled.SparseR1CS) [][]int {

b := levelBuilder{
mWireToNode: make(map[int]int, ccs.NbInternalVariables), // at which node we resolved which wire
nodeLevels: make([]int, len(ccs.Constraints)), // level of a node
mLevels: make(map[int]int), // level counts
ccs: ccs,
nbInputs: ccs.NbPublicVariables + ccs.NbSecretVariables,
}

// for each constraint, we're going to find its direct dependencies
// that is, wires (solved by previous constraints) on which it depends
// each of these dependencies is tagged with a level
// current constraint will be tagged with max(level) + 1
for cID, c := range ccs.Constraints {

b.nodeLevel = 0

b.processTerm(c.L, cID)
b.processTerm(c.R, cID)
b.processTerm(c.O, cID)

b.nodeLevels[cID] = b.nodeLevel
b.mLevels[b.nodeLevel]++

}

levels := make([][]int, len(b.mLevels))
for i := 0; i < len(levels); i++ {
// allocate memory
levels[i] = make([]int, 0, b.mLevels[i])
}

for n, l := range b.nodeLevels {
levels[l] = append(levels[l], n)
}

return levels
}

type levelBuilder struct {
ccs compiled.SparseR1CS
nbInputs int

mWireToNode map[int]int // at which node we resolved which wire
nodeLevels []int // level per node
mLevels map[int]int // number of constraint per level

nodeLevel int // current level
}

func (b *levelBuilder) processTerm(t compiled.Term, cID int) {
wID := t.WireID()
if wID < b.nbInputs {
// it's a input, we ignore it
return
}

// if we know a which constraint solves this wire, then it's a dependency
n, ok := b.mWireToNode[wID]
if ok {
if n != cID { // can happen with hints...
// we add a dependency, check if we need to increment our current level
if b.nodeLevels[n] >= b.nodeLevel {
b.nodeLevel = b.nodeLevels[n] + 1 // we are at the next level at least since we depend on it
}
}
return
}

// check if it's a hint and mark all the output wires
if h, ok := b.ccs.MHints[wID]; ok {

for _, in := range h.Inputs {
switch t := in.(type) {
case compiled.Variable:
for _, tt := range t.LinExp {
b.processTerm(tt, cID)
}
case compiled.LinearExpression:
for _, tt := range t {
b.processTerm(tt, cID)
}
case compiled.Term:
b.processTerm(t, cID)
}
}

for _, hwid := range h.Wires {
b.mWireToNode[hwid] = cID
}

return
}

// mark this wire solved by current node
b.mWireToNode[wID] = cID

}
4 changes: 2 additions & 2 deletions frontend/cs/plonk/sparse_r1cs.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (system *sparseR1CS) NewSecretVariable(name string) frontend.Variable {

// reduces redundancy in linear expression
// It factorizes Variable that appears multiple times with != coeff Ids
// To ensure the determinism in the compile process, Variables are stored as public||secret||internal||unset
// To ensure the determinism in the compile process, Variables are stored as publicsecretinternalunset
// for each visibility, the Variables are sorted from lowest ID to highest ID
func (system *sparseR1CS) reduce(l compiled.LinearExpression) compiled.LinearExpression {

Expand Down Expand Up @@ -268,7 +268,7 @@ func (system *sparseR1CS) CheckVariables() error {
sbb.WriteString(strconv.Itoa(cptHints))
sbb.WriteString(" unconstrained hints")
sbb.WriteByte('\n')
// TODO we may add more debug info here --> idea, in NewHint, take the debug stack, and store in the hint map some
// TODO we may add more debug info here idea, in NewHint, take the debug stack, and store in the hint map some
// debugInfo to find where a hint was declared (and not constrained)
}
return errors.New(sbb.String())
Expand Down
8 changes: 4 additions & 4 deletions frontend/cs/r1cs/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (system *r1CS) AssertIsDifferent(i1, i2 frontend.Variable) {
system.Inverse(system.Sub(i1, i2))
}

// AssertIsBoolean adds an assertion in the constraint system (v == 0 || v == 1)
// AssertIsBoolean adds an assertion in the constraint system (v == 0 v == 1)
func (system *r1CS) AssertIsBoolean(i1 frontend.Variable) {

vars, _ := system.toVariables(i1)
Expand Down Expand Up @@ -69,7 +69,7 @@ func (system *r1CS) AssertIsBoolean(i1 frontend.Variable) {
system.addConstraint(newR1C(v, _v, o), debug)
}

// AssertIsLessOrEqual adds assertion in constraint system (v <= bound)
// AssertIsLessOrEqual adds assertion in constraint system (v bound)
//
// bound can be a constant or a Variable
//
Expand Down Expand Up @@ -120,7 +120,7 @@ func (system *r1CS) mustBeLessOrEqVar(a, bound compiled.Variable) {
l = system.Sub(l, t, aBits[i])

// note if bound[i] == 1, this constraint is (1 - ai) * ai == 0
// --> this is a boolean constraint
// this is a boolean constraint
// if bound[i] == 0, t must be 0 or 1, thus ai must be 0 or 1 too
system.markBoolean(aBits[i].(compiled.Variable)) // this does not create a constraint

Expand Down Expand Up @@ -158,7 +158,7 @@ func (system *r1CS) mustBeLessOrEqCst(a compiled.Variable, bound big.Int) {
}

p := make([]frontend.Variable, nbBits+1)
// p[i] == 1 --> a[j] == c[j] for all j >= i
// p[i] == 1 a[j] == c[j] for all j i
p[nbBits] = system.constant(1)

for i := nbBits - 1; i >= t; i-- {
Expand Down
Loading

0 comments on commit a88771d

Please sign in to comment.