mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 04:08:01 -05:00
Prover/full recursion (#350)
* (feat): Implements the full-recursion and test for a simple test * msg(sticker): better error in the sticker * test(full-recursion): adds a test for double full-recursion (overflowing memory) * fix: sort out the packages after rebasing * fix(pi): renaming of the public inputs * fix(hasher): adjust the code to using a [hash.StateStorer] * fix(pairing): pass the new format for fp12 elements * doc(plonk): adds more doc in plonk.alignment.go * doc(fs-hook): improves the documentation of the FiatShamirHook field. * docs(skipping): adds doc on the ByRoundRegister * feat(pubinp): move the zkevm public inputs to using the new public-input framework * doc(column-store): adds documentation for the more precise methods regarding the inclusion in the FS transcript. * clean(self-recursion): remove the self-recursion tuning file * doc(vortex): explain the separation between the verifier steps * doc(full-recursion): documents the prover and verifier actions * doc(columns): improve the documentation on the IncludeInProverFS
This commit is contained in:
@@ -28,5 +28,5 @@ type circuit struct {
|
||||
|
||||
func (c *circuit) Define(api frontend.API) error {
|
||||
hsh := gkrmimc.NewHasherFactory(api).NewHasher()
|
||||
return v1.CheckBatchesSums(api, &hsh, c.NbBatches, c.BlobPayload[:], c.BatchEnds[:], c.ExpectedSums[:])
|
||||
return v1.CheckBatchesSums(api, hsh, c.NbBatches, c.BlobPayload[:], c.BatchEnds[:], c.ExpectedSums[:])
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob/dictionary"
|
||||
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob/encode"
|
||||
"hash"
|
||||
"math/big"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob/dictionary"
|
||||
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob/encode"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc"
|
||||
fr377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr"
|
||||
fr381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
@@ -192,8 +193,7 @@ func (i *FunctionalPublicInputSnark) Sum(api frontend.API, hsh snarkHash.FieldHa
|
||||
func (c Circuit) Define(api frontend.API) error {
|
||||
var hsh snarkHash.FieldHasher
|
||||
if c.UseGkrMiMC {
|
||||
h := gkrmimc.NewHasherFactory(api).NewHasher()
|
||||
hsh = &h
|
||||
hsh = gkrmimc.NewHasherFactory(api).NewHasher()
|
||||
} else {
|
||||
if h, err := mimc.NewMiMC(api); err != nil {
|
||||
return err
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/circuits"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/wizard"
|
||||
"github.com/consensys/linea-monorepo/prover/zkevm"
|
||||
"github.com/consensys/linea-monorepo/prover/zkevm/prover/publicInput"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/consensys/gnark/std/hash/mimc"
|
||||
@@ -23,11 +22,6 @@ import (
|
||||
type CircuitExecution struct {
|
||||
// The wizard verifier circuit
|
||||
WizardVerifier wizard.WizardVerifierCircuit `gnark:",secret"`
|
||||
// The extractor is not part of the circuit per se, but hold informations
|
||||
// that is used to extract the public inputs from the the WizardVerifier.
|
||||
// The extractor only needs to be provided during the definition of the
|
||||
// circuit and is omitted during the assignment of the circuit.
|
||||
extractor publicInput.FunctionalInputExtractor `gnark:"-"`
|
||||
// The functional public inputs are the "actual" statement made by the
|
||||
// circuit. They are not part of the public input of the circuit for
|
||||
// a number of reasons involving efficiency and simplicity in the aggregation
|
||||
@@ -45,7 +39,6 @@ func Allocate(zkevm *zkevm.ZkEvm) CircuitExecution {
|
||||
}
|
||||
return CircuitExecution{
|
||||
WizardVerifier: *wverifier,
|
||||
extractor: zkevm.PublicInput.Extractor,
|
||||
FuncInputs: FunctionalPublicInputSnark{
|
||||
FunctionalPublicInputQSnark: FunctionalPublicInputQSnark{
|
||||
L2MessageHashes: L2MessageHashes{
|
||||
@@ -90,7 +83,6 @@ func (c *CircuitExecution) Define(api frontend.API) error {
|
||||
api,
|
||||
&c.WizardVerifier,
|
||||
c.FuncInputs,
|
||||
c.extractor,
|
||||
)
|
||||
|
||||
// Add missing public input check
|
||||
|
||||
@@ -16,13 +16,12 @@ func checkPublicInputs(
|
||||
api frontend.API,
|
||||
wvc *wizard.WizardVerifierCircuit,
|
||||
gnarkFuncInp FunctionalPublicInputSnark,
|
||||
wizardFuncInp publicInput.FunctionalInputExtractor,
|
||||
) {
|
||||
|
||||
var (
|
||||
lastRollingHash = internal.CombineBytesIntoElements(api, gnarkFuncInp.FinalRollingHashUpdate)
|
||||
firstRollingHash = internal.CombineBytesIntoElements(api, gnarkFuncInp.InitialRollingHashUpdate)
|
||||
execDataHash = execDataHash(api, wvc, wizardFuncInp)
|
||||
execDataHash = execDataHash(api, wvc)
|
||||
)
|
||||
|
||||
// As we have this issue, the execDataHash will not match what we have in the
|
||||
@@ -32,7 +31,7 @@ func checkPublicInputs(
|
||||
shouldBeEqual(api, execDataHash, gnarkFuncInp.DataChecksum)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.L2MessageHash.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.L2MessageHash),
|
||||
// TODO: this operation is done a second time when computing the final
|
||||
// public input which is wasteful although not dramatic (~8000 unused
|
||||
// constraints)
|
||||
@@ -40,62 +39,62 @@ func checkPublicInputs(
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.InitialStateRootHash.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.InitialStateRootHash),
|
||||
gnarkFuncInp.InitialStateRootHash,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.InitialBlockNumber.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.InitialBlockNumber),
|
||||
gnarkFuncInp.InitialBlockNumber,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.InitialBlockTimestamp.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.InitialBlockTimestamp),
|
||||
gnarkFuncInp.InitialBlockTimestamp,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FirstRollingHashUpdate[0].ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.FirstRollingHashUpdate_0),
|
||||
firstRollingHash[0],
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FirstRollingHashUpdate[1].ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.FirstRollingHashUpdate_1),
|
||||
firstRollingHash[1],
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FirstRollingHashUpdateNumber.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.FirstRollingHashUpdateNumber),
|
||||
gnarkFuncInp.FirstRollingHashUpdateNumber,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FinalStateRootHash.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.FinalStateRootHash),
|
||||
gnarkFuncInp.FinalStateRootHash,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FinalBlockNumber.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.FinalBlockNumber),
|
||||
gnarkFuncInp.FinalBlockNumber,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FinalBlockTimestamp.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.FinalBlockTimestamp),
|
||||
gnarkFuncInp.FinalBlockTimestamp,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.LastRollingHashUpdate[0].ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.LastRollingHashUpdate_0),
|
||||
lastRollingHash[0],
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.LastRollingHashUpdate[1].ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.LastRollingHashUpdate_1),
|
||||
lastRollingHash[1],
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.LastRollingHashUpdateNumber.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.LastRollingHashNumberUpdate),
|
||||
gnarkFuncInp.LastRollingHashUpdateNumber,
|
||||
)
|
||||
|
||||
@@ -107,9 +106,9 @@ func checkPublicInputs(
|
||||
bridgeAddress = api.Add(
|
||||
api.Mul(
|
||||
twoPow128,
|
||||
wizardFuncInp.L2MessageServiceAddrHi.GetFrontendVariable(api, wvc),
|
||||
wvc.GetPublicInput(api, publicInput.L2MessageServiceAddrHi),
|
||||
),
|
||||
wizardFuncInp.L2MessageServiceAddrLo.GetFrontendVariable(api, wvc),
|
||||
wvc.GetPublicInput(api, publicInput.L2MessageServiceAddrLo),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -119,10 +118,10 @@ func checkPublicInputs(
|
||||
// chainID) then the traces will return a chainID of zero.
|
||||
api.AssertIsEqual(
|
||||
api.Mul(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.ChainID.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.ChainID),
|
||||
api.Sub(
|
||||
api.Div(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.ChainID.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.ChainID),
|
||||
twoPow112,
|
||||
),
|
||||
gnarkFuncInp.ChainID,
|
||||
@@ -141,7 +140,6 @@ func checkPublicInputs(
|
||||
func execDataHash(
|
||||
api frontend.API,
|
||||
wvc *wizard.WizardVerifierCircuit,
|
||||
wFuncInp publicInput.FunctionalInputExtractor,
|
||||
) frontend.Variable {
|
||||
|
||||
hsh, err := mimc.NewMiMC(api)
|
||||
@@ -150,8 +148,8 @@ func execDataHash(
|
||||
}
|
||||
|
||||
hsh.Write(
|
||||
wvc.GetLocalPointEvalParams(wFuncInp.DataNbBytes.ID).Y,
|
||||
wvc.GetLocalPointEvalParams(wFuncInp.DataChecksum.ID).Y,
|
||||
wvc.GetPublicInput(api, publicInput.DataNbBytes),
|
||||
wvc.GetPublicInput(api, publicInput.DataChecksum),
|
||||
)
|
||||
|
||||
return hsh.Sum()
|
||||
|
||||
@@ -98,8 +98,7 @@ func (c *Circuit) Define(api frontend.API) error {
|
||||
hshM hash.FieldHasher
|
||||
)
|
||||
if c.UseGkrMimc {
|
||||
hsh := gkrmimc.NewHasherFactory(api).NewHasher()
|
||||
hshM = &hsh
|
||||
hshM = gkrmimc.NewHasherFactory(api).NewHasher()
|
||||
} else {
|
||||
if hsh, err := mimc.NewMiMC(api); err != nil {
|
||||
return err
|
||||
|
||||
@@ -112,6 +112,7 @@ func TestTinyTwoBatchBlob(t *testing.T) {
|
||||
req := pi_interconnection.Request{
|
||||
Decompressions: []blobsubmission.Response{*blobResp},
|
||||
Executions: execReq,
|
||||
DictPath: "../../lib/compressor/compressor_dict.bin",
|
||||
Aggregation: public_input.Aggregation{
|
||||
FinalShnarf: blobResp.ExpectedShnarf,
|
||||
ParentAggregationFinalShnarf: blobReq.PrevShnarf,
|
||||
@@ -208,6 +209,7 @@ func TestTwoTwoBatchBlobs(t *testing.T) {
|
||||
req := pi_interconnection.Request{
|
||||
Decompressions: []blobsubmission.Response{*blobResp0, *blobResp1},
|
||||
Executions: execReq,
|
||||
DictPath: "../../lib/compressor/compressor_dict.bin",
|
||||
Aggregation: public_input.Aggregation{
|
||||
FinalShnarf: blobResp1.ExpectedShnarf,
|
||||
ParentAggregationFinalShnarf: blobReq0.PrevShnarf,
|
||||
|
||||
@@ -48,6 +48,7 @@ func AssignSingleBlockBlob(t require.TestingT) pi_interconnection.Request {
|
||||
merkleRoots := aggregation.PackInMiniTrees(test_utils.BlocksToHex(execReq.L2MessageHashes))
|
||||
|
||||
return pi_interconnection.Request{
|
||||
DictPath: "../../lib/compressor/compressor_dict.bin",
|
||||
Decompressions: []blobsubmission.Response{*blobResp},
|
||||
Executions: []public_input.Execution{execReq},
|
||||
Aggregation: public_input.Aggregation{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package fiatshamir
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"math"
|
||||
|
||||
"github.com/consensys/gnark-crypto/hash"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/common/smartvectors"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/field"
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
//
|
||||
// https://blog.trailofbits.com/2022/04/18/the-frozen-heart-vulnerability-in-plonk/
|
||||
type State struct {
|
||||
hasher hash.Hash
|
||||
hasher hash.StateStorer
|
||||
TranscriptSize int
|
||||
NumCoinGenerated int
|
||||
}
|
||||
@@ -39,10 +39,26 @@ type State struct {
|
||||
// NewMiMCFiatShamir constructs a fresh and empty Fiat-Shamir state.
|
||||
func NewMiMCFiatShamir() *State {
|
||||
return &State{
|
||||
hasher: mimc.NewMiMC(),
|
||||
hasher: mimc.NewMiMC().(hash.StateStorer),
|
||||
}
|
||||
}
|
||||
|
||||
// State returns the internal state of the Fiat-Shamir hasher. Only works for
|
||||
// MiMC.
|
||||
func (s *State) State() []field.Element {
|
||||
_ = s.hasher.Sum(nil)
|
||||
b := s.hasher.State()
|
||||
f := new(field.Element).SetBytes(b)
|
||||
return []field.Element{*f}
|
||||
}
|
||||
|
||||
// SetState sets the fiat-shamir state to the requested value
|
||||
func (s *State) SetState(f []field.Element) {
|
||||
_ = s.hasher.Sum(nil)
|
||||
b := f[0].Bytes()
|
||||
s.hasher.SetState(b[:])
|
||||
}
|
||||
|
||||
// Update the Fiat-Shamir state with a one or more of field elements. The
|
||||
// function as no-op if the caller supplies no field elements.
|
||||
func (fs *State) Update(vec ...field.Element) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// of the verifier of a protocol calling [State] as it allows having a very
|
||||
// similar code for both tasks.
|
||||
type GnarkFiatShamir struct {
|
||||
hasher hash.FieldHasher
|
||||
hasher hash.StateStorer
|
||||
// pointer to the gnark-API (also passed to the hasher but behind an
|
||||
// interface). This is needed to perform bit-decomposition.
|
||||
api frontend.API
|
||||
@@ -30,10 +30,10 @@ type GnarkFiatShamir struct {
|
||||
// used in the scope of a [frontend.Define] function.
|
||||
func NewGnarkFiatShamir(api frontend.API, factory *gkrmimc.HasherFactory) *GnarkFiatShamir {
|
||||
|
||||
var hasher hash.FieldHasher
|
||||
var hasher hash.StateStorer
|
||||
if factory != nil {
|
||||
h := factory.NewHasher()
|
||||
hasher = &h
|
||||
hasher = h
|
||||
} else {
|
||||
h, err := mimc.NewMiMC(api)
|
||||
if err != nil {
|
||||
@@ -52,6 +52,34 @@ func NewGnarkFiatShamir(api frontend.API, factory *gkrmimc.HasherFactory) *Gnark
|
||||
}
|
||||
}
|
||||
|
||||
// SetState mutates the fiat-shamir state of
|
||||
func (fs *GnarkFiatShamir) SetState(state []frontend.Variable) {
|
||||
|
||||
switch hsh := fs.hasher.(type) {
|
||||
case interface {
|
||||
SetState([]frontend.Variable) error
|
||||
}:
|
||||
if err := hsh.SetState(state); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
default:
|
||||
panic("unexpected hasher type")
|
||||
}
|
||||
}
|
||||
|
||||
// State mutates the fiat-shamir state of
|
||||
func (fs *GnarkFiatShamir) State() []frontend.Variable {
|
||||
|
||||
switch hsh := fs.hasher.(type) {
|
||||
case interface {
|
||||
State() []frontend.Variable
|
||||
}:
|
||||
return hsh.State()
|
||||
default:
|
||||
panic("unexpected hasher type")
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates the Fiat-Shamir state with a vector of frontend.Variable
|
||||
// representing field element each.
|
||||
func (fs *GnarkFiatShamir) Update(vec ...frontend.Variable) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gkrmimc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/consensys/gnark/frontend"
|
||||
@@ -70,8 +71,8 @@ type Hasher struct {
|
||||
// and will provide the same results for the same usage.
|
||||
//
|
||||
// However, the hasher should not be used in deferred gnark circuit execution.
|
||||
func (f *HasherFactory) NewHasher() Hasher {
|
||||
return Hasher{factory: f, state: frontend.Variable(0)}
|
||||
func (f *HasherFactory) NewHasher() *Hasher {
|
||||
return &Hasher{factory: f, state: frontend.Variable(0)}
|
||||
}
|
||||
|
||||
// Writes fields elements into the hasher; implements [hash.FieldHasher]
|
||||
@@ -107,6 +108,30 @@ func (h *Hasher) Sum() frontend.Variable {
|
||||
return curr
|
||||
}
|
||||
|
||||
// SetState manually sets the state of the hasher to the provided value. In the
|
||||
// case of MiMC only a single frontend variable is expected to represent the
|
||||
// state.
|
||||
func (h *Hasher) SetState(newState []frontend.Variable) error {
|
||||
|
||||
if len(h.data) > 0 {
|
||||
return errors.New("the hasher is not in an initial state")
|
||||
}
|
||||
|
||||
if len(newState) != 1 {
|
||||
return errors.New("the MiMC hasher expects a single field element to represent the state")
|
||||
}
|
||||
|
||||
h.state = newState[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// State returns the inner-state of the hasher. In the context of MiMC only a
|
||||
// single field element is returned.
|
||||
func (h *Hasher) State() []frontend.Variable {
|
||||
_ = h.Sum() // to flush the hasher
|
||||
return []frontend.Variable{h.state}
|
||||
}
|
||||
|
||||
// compress calls returns a frontend.Variable holding the result of applying
|
||||
// the compression function of MiMC over state and block. The alleged returned
|
||||
// result is pushed on the stack of all the claims to verify.
|
||||
|
||||
@@ -5,10 +5,10 @@ go 1.22.7
|
||||
toolchain go1.23.0
|
||||
|
||||
require (
|
||||
github.com/consensys/bavard v0.1.22
|
||||
github.com/consensys/bavard v0.1.24
|
||||
github.com/consensys/compress v0.2.5
|
||||
github.com/consensys/gnark v0.11.1-0.20240910135928-e8cb61d0be1d
|
||||
github.com/consensys/gnark-crypto v0.14.1-0.20241007145620-e26bbdf97a4a
|
||||
github.com/consensys/gnark v0.11.1-0.20241217141116-f3d91999250b
|
||||
github.com/consensys/gnark-crypto v0.14.1-0.20241217134352-810063550bd4
|
||||
github.com/consensys/go-corset v0.0.0-20241125005324-5cb0c289c021
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0
|
||||
github.com/dlclark/regexp2 v1.11.2
|
||||
@@ -24,9 +24,9 @@ require (
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/net v0.27.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/time v0.5.0
|
||||
)
|
||||
|
||||
@@ -63,8 +63,7 @@ require (
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/holiman/uint256 v1.3.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/ingonyama-zk/icicle v1.1.0 // indirect
|
||||
github.com/ingonyama-zk/iciclegnark v0.1.0 // indirect
|
||||
github.com/ingonyama-zk/icicle/v3 v3.1.1-0.20241118092657-fccdb2f0921b // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
@@ -98,7 +97,7 @@ require (
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
rsc.io/tmplfunc v0.0.3 // indirect
|
||||
@@ -111,6 +110,6 @@ require (
|
||||
github.com/pkg/profile v1.7.0
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -92,14 +92,14 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP
|
||||
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||
github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=
|
||||
github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/bavard v0.1.24 h1:Lfe+bjYbpaoT7K5JTFoMi5wo9V4REGLvQQbHmatoN2I=
|
||||
github.com/consensys/bavard v0.1.24/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/compress v0.2.5 h1:gJr1hKzbOD36JFsF1AN8lfXz1yevnJi1YolffY19Ntk=
|
||||
github.com/consensys/compress v0.2.5/go.mod h1:pyM+ZXiNUh7/0+AUjUf9RKUM6vSH7T/fsn5LLS0j1Tk=
|
||||
github.com/consensys/gnark v0.11.1-0.20240910135928-e8cb61d0be1d h1:TmNupI1+K5/LOg1K0kqEhRf5sZwRtxXah5iTHQ6fJvw=
|
||||
github.com/consensys/gnark v0.11.1-0.20240910135928-e8cb61d0be1d/go.mod h1:f9CH911SPCrbSZp5z9LYzJ3rZvI7mOUzzf48lCZO/5o=
|
||||
github.com/consensys/gnark-crypto v0.14.1-0.20241007145620-e26bbdf97a4a h1:yUHuYq+v1C3maTwnntLYhTDmboq3scSo1PQIl375/sE=
|
||||
github.com/consensys/gnark-crypto v0.14.1-0.20241007145620-e26bbdf97a4a/go.mod h1:F/hJyWBcTr1sWeifAKfEN3aVb3G4U5zheEC8IbWQun4=
|
||||
github.com/consensys/gnark v0.11.1-0.20241217141116-f3d91999250b h1:isTN/YOs57bOt0JlJHJ8gF8C3CdETU2Z9ao4y8R6qms=
|
||||
github.com/consensys/gnark v0.11.1-0.20241217141116-f3d91999250b/go.mod h1:8YNyW/+XsYiLRzROLaj/PSktYO4VAdv6YW1b1P3UsZk=
|
||||
github.com/consensys/gnark-crypto v0.14.1-0.20241217134352-810063550bd4 h1:Kp6egjRqKZf4469dfAWqFe6gi3MRs4VvNHmTfEjUlS8=
|
||||
github.com/consensys/gnark-crypto v0.14.1-0.20241217134352-810063550bd4/go.mod h1:GMPeN3dUSslNBYJsK3WTjIGd3l0ccfMbcEh/d5knFrc=
|
||||
github.com/consensys/go-corset v0.0.0-20241125005324-5cb0c289c021 h1:zAPMHjY72pXmjuyb/niQ816pd+B9RAmZoL/W/f5uJSU=
|
||||
github.com/consensys/go-corset v0.0.0-20241125005324-5cb0c289c021/go.mod h1:J64guTfpmfXl4Yk2D7lsWdYg0ilP+N8JWPudP7+sZpA=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -290,10 +290,8 @@ github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBD
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/ingonyama-zk/icicle v1.1.0 h1:a2MUIaF+1i4JY2Lnb961ZMvaC8GFs9GqZgSnd9e95C8=
|
||||
github.com/ingonyama-zk/icicle v1.1.0/go.mod h1:kAK8/EoN7fUEmakzgZIYdWy1a2rBnpCaZLqSHwZWxEk=
|
||||
github.com/ingonyama-zk/iciclegnark v0.1.0 h1:88MkEghzjQBMjrYRJFxZ9oR9CTIpB8NG2zLeCJSvXKQ=
|
||||
github.com/ingonyama-zk/iciclegnark v0.1.0/go.mod h1:wz6+IpyHKs6UhMMoQpNqz1VY+ddfKqC/gRwR/64W6WU=
|
||||
github.com/ingonyama-zk/icicle/v3 v3.1.1-0.20241118092657-fccdb2f0921b h1:AvQTK7l0PTHODD06PVQX1Tn2o29sRIaKIDOvTJmKurY=
|
||||
github.com/ingonyama-zk/icicle/v3 v3.1.1-0.20241118092657-fccdb2f0921b/go.mod h1:e0JHb27/P6WorCJS3YolbY5XffS4PGBuoW38OthLkDs=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@@ -504,8 +502,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -615,8 +613,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -678,8 +676,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -694,8 +692,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -44,6 +44,11 @@ type storedColumnInfo struct {
|
||||
ID ifaces.ColID
|
||||
// Status of the commitment
|
||||
Status Status
|
||||
// IncludeInProverFS states the prover should include the column in his FS
|
||||
// transcript. This is used for columns that are recursed using
|
||||
// FullRecursion. This field is only meaningfull for [Ignored] columns as
|
||||
// they are excluded by default.
|
||||
IncludeInProverFS bool
|
||||
}
|
||||
|
||||
// AddToRound constructs a [Natural], registers it in the [Store] and returns
|
||||
@@ -444,3 +449,39 @@ func assertCorrectStatusTransition(old, new Status) {
|
||||
utils.Panic("attempted the transition %v -> %v, which is forbidden", old.String(), new.String())
|
||||
}
|
||||
}
|
||||
|
||||
// IgnoreButKeepInProverTranscript marks a column as ignored but also asks that
|
||||
// the column stays included in the FS transcript. This is used as part of
|
||||
// full-recursion where the commitments to an inner-proofs should not be sent to
|
||||
// the verifier but should still play a part in the FS transcript.
|
||||
func (s *Store) IgnoreButKeepInProverTranscript(colName ifaces.ColID) {
|
||||
in := s.info(colName)
|
||||
in.Status = Ignored
|
||||
in.IncludeInProverFS = true
|
||||
}
|
||||
|
||||
// IsIgnoredAndNotKeptInTranscript indicates whether the column can be ignored
|
||||
// from the transcript and is used during the Fiat-Shamir randomness generation.
|
||||
func (s *Store) IsIgnoredAndNotKeptInTranscript(colName ifaces.ColID) bool {
|
||||
in := s.info(colName)
|
||||
return in.Status == Ignored && !in.IncludeInProverFS
|
||||
}
|
||||
|
||||
// AllKeysProofsOrIgnoredButKeptInProverTranscript returns the list of the
|
||||
// columns to be used as part of the FS transcript.
|
||||
func (s *Store) AllKeysProofsOrIgnoredButKeptInProverTranscript(round int) []ifaces.ColID {
|
||||
res := []ifaces.ColID{}
|
||||
rnd := s.byRounds.MustGet(round) // precomputed are always at round zero
|
||||
|
||||
for i, info := range rnd {
|
||||
|
||||
ok := (info.Status == Proof) || (info.Status == Ignored && info.IncludeInProverFS)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, rnd[i].ID)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
// suitable for established unit-tests where we want to analyze the errors.
|
||||
func CompileAtProverLvl(comp *wizard.CompiledIOP) {
|
||||
|
||||
comp.DummyCompiled = true
|
||||
|
||||
/*
|
||||
Registers all declared commitments and query parameters
|
||||
as messages in the same round. This steps is only relevant
|
||||
|
||||
207
prover/protocol/compiler/fullrecursion/actions.go
Normal file
207
prover/protocol/compiler/fullrecursion/actions.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package fullrecursion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/consensys/gnark/frontend"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/field"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/wizard"
|
||||
)
|
||||
|
||||
// CircuitAssignment is an implementation of [wizard.ProverAction]. As such, it
|
||||
// embodies the action of assigning the full-recursion Plonk circuit columns.
|
||||
type CircuitAssignment fullRecursionCtx
|
||||
|
||||
// ConsistencyCheck is an implementation of [wizard.VerifierAction]. As such it
|
||||
// is responsible for checking that the public inputs of the full-recursion
|
||||
// Plonk circuit are assigned to values that are consistent with (1) the public
|
||||
// inputs of the wrapping wizard protocol and with the inputs of the
|
||||
// self-recursion wizard.
|
||||
type ConsistencyCheck struct {
|
||||
fullRecursionCtx
|
||||
isSkipped bool
|
||||
}
|
||||
|
||||
// ReplacementAssignment is a [wizard.ProverAction] implementation. It assigns
|
||||
// the queries and columns that are "replaced" in the wizard. In essence, this
|
||||
// concerns the main grail polynomial evaluation (the grail query) and the
|
||||
// Merkle roots assignment. These have to be replaced so that they can be
|
||||
// refered to by the self-recursion. Otherwise, they would be swallowed by the
|
||||
// recursion Plonk circuit.
|
||||
type ReplacementAssignment fullRecursionCtx
|
||||
|
||||
// LocalOpeningAssignment assigns the local openings made over the Plonk PI.
|
||||
// These are needed in order to (1) perform the consistency check (2) replace
|
||||
// the "old" and recursed public inputs of the original wizard by new ones.
|
||||
type LocalOpeningAssignment fullRecursionCtx
|
||||
|
||||
// ResetFsActions is a [wizard.FsHook] responsible for tweaking the FS state as
|
||||
// required by the self-recursion process.
|
||||
type ResetFsActions struct {
|
||||
fullRecursionCtx
|
||||
isSkipped bool
|
||||
}
|
||||
|
||||
func (c CircuitAssignment) Run(run *wizard.ProverRuntime) {
|
||||
c.PlonkInWizard.ProverAction.Run(run, WitnessAssigner(c))
|
||||
}
|
||||
|
||||
func (c ReplacementAssignment) Run(run *wizard.ProverRuntime) {
|
||||
params := run.GetUnivariateParams(c.PolyQuery.QueryID)
|
||||
run.AssignUnivariate(c.PolyQueryReplacement.QueryID, params.X, params.Ys...)
|
||||
|
||||
oldRoots := c.PcsCtx.Items.MerkleRoots
|
||||
for i := range c.MerkleRootsReplacement {
|
||||
|
||||
if c.PcsCtx.Items.MerkleRoots[i] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
run.AssignColumn(
|
||||
c.MerkleRootsReplacement[i].GetColID(),
|
||||
oldRoots[i].GetColAssignment(run),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (c LocalOpeningAssignment) Run(run *wizard.ProverRuntime) {
|
||||
for i := range c.LocalOpenings {
|
||||
run.AssignLocalPoint(
|
||||
c.LocalOpenings[i].ID,
|
||||
c.PlonkInWizard.PI.GetColAssignmentAt(run, i),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConsistencyCheck) Run(run *wizard.VerifierRuntime) error {
|
||||
|
||||
var (
|
||||
initialFsCirc = run.GetLocalPointEvalParams(c.LocalOpenings[0].ID).Y
|
||||
initialFsRt = run.FiatShamirHistory[c.FirstRound+1][0][0]
|
||||
piCursor = 2
|
||||
)
|
||||
|
||||
if initialFsCirc != initialFsRt {
|
||||
return fmt.Errorf("full recursion: the initial FS do not match")
|
||||
}
|
||||
|
||||
for i := range c.NonEmptyMerkleRootPositions {
|
||||
|
||||
var (
|
||||
pos = c.NonEmptyMerkleRootPositions[i]
|
||||
fromRt = c.MerkleRootsReplacement[pos].GetColAssignmentAt(run, 0)
|
||||
fromCirc = run.GetLocalPointEvalParams(c.LocalOpenings[piCursor+i].ID).Y
|
||||
)
|
||||
|
||||
if fromRt != fromCirc {
|
||||
return fmt.Errorf("full recursion: the commitment does not match (pos: %v)", i)
|
||||
}
|
||||
}
|
||||
|
||||
piCursor += len(c.NonEmptyMerkleRootPositions)
|
||||
|
||||
var (
|
||||
paramsRt = run.GetUnivariateParams(c.PolyQueryReplacement.QueryID)
|
||||
xRt = paramsRt.X
|
||||
xCirc = run.GetLocalPointEvalParams(c.LocalOpenings[piCursor].ID).Y
|
||||
)
|
||||
|
||||
if xRt != xCirc {
|
||||
return fmt.Errorf("full recursion: the Ys does not match")
|
||||
}
|
||||
|
||||
piCursor++
|
||||
|
||||
for i := range paramsRt.Ys {
|
||||
|
||||
var (
|
||||
fromRt = paramsRt.Ys[i]
|
||||
fromCirc = run.GetLocalPointEvalParams(c.LocalOpenings[piCursor+i].ID).Y
|
||||
)
|
||||
|
||||
if fromRt != fromCirc {
|
||||
return fmt.Errorf("full recursion: the Ys does not match (pos: %v)", i)
|
||||
}
|
||||
}
|
||||
|
||||
// The public inputs do not need to be checked because they are redefined in
|
||||
// term of the local openings directly. So checking it would amount to checking
|
||||
// that the local openings are equal to themselves.
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConsistencyCheck) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
|
||||
var (
|
||||
initialFsCirc = run.GetLocalPointEvalParams(c.LocalOpenings[0].ID).Y
|
||||
initialFsRt = run.FiatShamirHistory[c.FirstRound+1][0][0]
|
||||
piCursor = 2
|
||||
)
|
||||
|
||||
api.AssertIsEqual(initialFsCirc, initialFsRt)
|
||||
|
||||
for i := range c.NonEmptyMerkleRootPositions {
|
||||
|
||||
var (
|
||||
pos = c.NonEmptyMerkleRootPositions[i]
|
||||
fromRt = c.MerkleRootsReplacement[pos].GetColAssignmentGnarkAt(run, 0)
|
||||
fromCirc = run.GetLocalPointEvalParams(c.LocalOpenings[piCursor+i].ID).Y
|
||||
)
|
||||
|
||||
api.AssertIsEqual(fromRt, fromCirc)
|
||||
}
|
||||
|
||||
piCursor += len(c.NonEmptyMerkleRootPositions)
|
||||
|
||||
var (
|
||||
paramsRt = run.GetUnivariateParams(c.PolyQueryReplacement.QueryID)
|
||||
xRt = paramsRt.X
|
||||
xCirc = run.GetLocalPointEvalParams(c.LocalOpenings[piCursor].ID).Y
|
||||
)
|
||||
|
||||
api.AssertIsEqual(xRt, xCirc)
|
||||
|
||||
piCursor++
|
||||
|
||||
for i := range paramsRt.Ys {
|
||||
|
||||
var (
|
||||
fromRt = paramsRt.Ys[i]
|
||||
fromCirc = run.GetLocalPointEvalParams(c.LocalOpenings[piCursor+i].ID).Y
|
||||
)
|
||||
|
||||
api.AssertIsEqual(fromRt, fromCirc)
|
||||
}
|
||||
|
||||
// The public inputs do not need to be checked because they are redefined in
|
||||
// term of the local openings directly. So checking it would amount to checking
|
||||
// that the local openings are equal to themselves.
|
||||
}
|
||||
|
||||
func (c *ConsistencyCheck) Skip() {
|
||||
c.isSkipped = true
|
||||
}
|
||||
|
||||
func (c *ConsistencyCheck) IsSkipped() bool {
|
||||
return c.isSkipped
|
||||
}
|
||||
|
||||
func (r *ResetFsActions) Run(run *wizard.VerifierRuntime) error {
|
||||
finalFsCirc := run.GetLocalPointEvalParams(r.LocalOpenings[1].ID).Y
|
||||
run.FS.SetState([]field.Element{finalFsCirc})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ResetFsActions) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
finalFsCirc := run.GetLocalPointEvalParams(r.LocalOpenings[1].ID).Y
|
||||
run.FS.SetState([]frontend.Variable{finalFsCirc})
|
||||
}
|
||||
|
||||
func (r *ResetFsActions) Skip() {
|
||||
r.isSkipped = true
|
||||
}
|
||||
|
||||
func (r *ResetFsActions) IsSkipped() bool {
|
||||
return r.isSkipped
|
||||
}
|
||||
255
prover/protocol/compiler/fullrecursion/circuit.go
Normal file
255
prover/protocol/compiler/fullrecursion/circuit.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package fullrecursion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc"
|
||||
"github.com/consensys/gnark/backend/witness"
|
||||
"github.com/consensys/gnark/frontend"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/fiatshamir"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc/gkrmimc"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/coin"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/query"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/wizard"
|
||||
)
|
||||
|
||||
type gnarkCircuit struct {
|
||||
InitialFsState frontend.Variable `gnark:",public"`
|
||||
FinalFsState frontend.Variable `gnark:",public"`
|
||||
Commitments []frontend.Variable `gnark:",public"`
|
||||
X frontend.Variable `gnark:",public"`
|
||||
Ys []frontend.Variable `gnark:",public"`
|
||||
Pubs []frontend.Variable `gnark:",public"`
|
||||
WizardVerifier *wizard.WizardVerifierCircuit
|
||||
comp *wizard.CompiledIOP `gnark:"-"`
|
||||
ctx *fullRecursionCtx `gnark:"-"`
|
||||
withoutGkr bool `gnark:"-"`
|
||||
}
|
||||
|
||||
func allocateGnarkCircuit(comp *wizard.CompiledIOP, ctx *fullRecursionCtx) *gnarkCircuit {
|
||||
|
||||
var (
|
||||
wizardVerifier = wizard.NewWizardVerifierCircuit()
|
||||
)
|
||||
|
||||
for round := range ctx.Columns {
|
||||
for _, col := range ctx.Columns[round] {
|
||||
wizardVerifier.AllocColumn(col.GetColID(), col.Size())
|
||||
}
|
||||
}
|
||||
|
||||
for round := range ctx.QueryParams {
|
||||
for _, qInfoIface := range ctx.QueryParams[round] {
|
||||
switch qInfo := qInfoIface.(type) {
|
||||
case query.UnivariateEval:
|
||||
wizardVerifier.AllocUnivariateEval(qInfo.QueryID, qInfo)
|
||||
case query.InnerProduct:
|
||||
wizardVerifier.AllocInnerProduct(qInfo.ID, qInfo)
|
||||
case query.LocalOpening:
|
||||
wizardVerifier.AllocLocalOpening(qInfo.ID, qInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wizardVerifier.Spec = comp
|
||||
|
||||
return &gnarkCircuit{
|
||||
ctx: ctx,
|
||||
comp: comp,
|
||||
WizardVerifier: wizardVerifier,
|
||||
Commitments: make([]frontend.Variable, len(ctx.NonEmptyMerkleRootPositions)),
|
||||
Ys: make([]frontend.Variable, len(ctx.PolyQuery.Pols)),
|
||||
Pubs: make([]frontend.Variable, len(comp.PublicInputs)),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *gnarkCircuit) Define(api frontend.API) error {
|
||||
|
||||
w := c.WizardVerifier
|
||||
|
||||
if c.withoutGkr {
|
||||
w.FS = fiatshamir.NewGnarkFiatShamir(api, nil)
|
||||
} else {
|
||||
w.HasherFactory = gkrmimc.NewHasherFactory(api)
|
||||
w.FS = fiatshamir.NewGnarkFiatShamir(api, w.HasherFactory)
|
||||
}
|
||||
|
||||
w.FiatShamirHistory = make([][2][]frontend.Variable, c.comp.NumRounds())
|
||||
|
||||
c.generateAllRandomCoins(api)
|
||||
|
||||
for round := 0; round <= c.ctx.LastRound; round++ {
|
||||
roundSteps := c.ctx.VerifierActions[round]
|
||||
for _, step := range roundSteps {
|
||||
step.RunGnark(api, w)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range c.Pubs {
|
||||
api.AssertIsEqual(c.Pubs[i], c.ctx.PublicInputs[i].Acc.GetFrontendVariable(api, w))
|
||||
}
|
||||
|
||||
polyParams := w.GetUnivariateParams(c.ctx.PolyQuery.Name())
|
||||
|
||||
api.AssertIsEqual(c.X, polyParams.X)
|
||||
|
||||
for i := range polyParams.Ys {
|
||||
api.AssertIsEqual(c.Ys[i], polyParams.Ys[i])
|
||||
}
|
||||
|
||||
for i := range c.Commitments {
|
||||
pos := c.ctx.NonEmptyMerkleRootPositions[i]
|
||||
api.AssertIsEqual(
|
||||
c.Commitments[i],
|
||||
w.GetColumn(c.ctx.PcsCtx.Items.MerkleRoots[pos].GetColID())[0],
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateAllRandomCoins is as [VerifierRuntime.generateAllRandomCoins]. Note
|
||||
// that the function does create constraints via the hasher factory that is
|
||||
// inside of `c.FS`.
|
||||
func (c *gnarkCircuit) generateAllRandomCoins(api frontend.API) {
|
||||
|
||||
var (
|
||||
ctx = c.ctx
|
||||
w = c.WizardVerifier
|
||||
)
|
||||
|
||||
w.FS.SetState([]frontend.Variable{c.InitialFsState})
|
||||
|
||||
for currRound := 0; currRound <= c.ctx.LastRound; currRound++ {
|
||||
|
||||
initialState := w.FS.State()
|
||||
|
||||
if currRound > 0 {
|
||||
|
||||
toUpdateFS := ctx.Columns[currRound-1]
|
||||
for _, msg := range toUpdateFS {
|
||||
val := w.GetColumn(msg.GetColID())
|
||||
w.FS.UpdateVec(val)
|
||||
}
|
||||
|
||||
queries := ctx.QueryParams[currRound-1]
|
||||
for _, q := range queries {
|
||||
params := w.GetParams(q.Name())
|
||||
params.UpdateFS(w.FS)
|
||||
}
|
||||
}
|
||||
|
||||
for _, info := range ctx.Coins[currRound] {
|
||||
switch info.Type {
|
||||
case coin.Field:
|
||||
value := w.FS.RandomField()
|
||||
w.Coins.InsertNew(info.Name, value)
|
||||
case coin.IntegerVec:
|
||||
value := w.FS.RandomManyIntegers(info.Size, info.UpperBound)
|
||||
w.Coins.InsertNew(info.Name, value)
|
||||
}
|
||||
}
|
||||
|
||||
for _, fsHook := range ctx.FsHooks[currRound] {
|
||||
fsHook.RunGnark(api, w)
|
||||
}
|
||||
|
||||
w.FiatShamirHistory[currRound] = [2][]frontend.Variable{
|
||||
initialState,
|
||||
w.FS.State(),
|
||||
}
|
||||
}
|
||||
|
||||
api.AssertIsEqual(w.FS.State()[0], c.FinalFsState)
|
||||
}
|
||||
|
||||
// AssignGnarkCircuit returns an assignment for the gnark circuit
|
||||
func AssignGnarkCircuit(ctx *fullRecursionCtx, comp *wizard.CompiledIOP, run *wizard.ProverRuntime) *gnarkCircuit {
|
||||
|
||||
var (
|
||||
wizardVerifier = wizard.NewWizardVerifierCircuit()
|
||||
)
|
||||
|
||||
for round := range ctx.Columns {
|
||||
for _, col := range ctx.Columns[round] {
|
||||
wizardVerifier.AssignColumn(col.GetColID(), col.GetColAssignment(run))
|
||||
}
|
||||
}
|
||||
|
||||
for round := range ctx.QueryParams {
|
||||
for _, qInfoIface := range ctx.QueryParams[round] {
|
||||
switch qInfo := qInfoIface.(type) {
|
||||
case query.UnivariateEval:
|
||||
params := run.GetUnivariateParams(qInfo.QueryID)
|
||||
wizardVerifier.AssignUnivariateEval(qInfo.QueryID, params)
|
||||
case query.InnerProduct:
|
||||
params := run.GetInnerProductParams(qInfo.ID)
|
||||
wizardVerifier.AssignInnerProduct(qInfo.ID, params)
|
||||
case query.LocalOpening:
|
||||
params := run.GetLocalPointEvalParams(qInfo.ID)
|
||||
wizardVerifier.AssignLocalOpening(qInfo.ID, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c := &gnarkCircuit{
|
||||
ctx: ctx,
|
||||
comp: comp,
|
||||
WizardVerifier: wizardVerifier,
|
||||
Pubs: make([]frontend.Variable, len(comp.PublicInputs)),
|
||||
Commitments: make([]frontend.Variable, len(ctx.NonEmptyMerkleRootPositions)),
|
||||
// It is important we start from the begining because of the case where
|
||||
// we stack several FullRecursion. In that case, the FsHooks are going
|
||||
// to automatically set the FsState to the correct value at first round.
|
||||
InitialFsState: run.FiatShamirHistory[1][0][0],
|
||||
FinalFsState: run.FiatShamirHistory[ctx.LastRound][1][0],
|
||||
}
|
||||
|
||||
polyParams := run.GetUnivariateParams(ctx.PolyQuery.QueryID).GnarkAssign()
|
||||
c.X = polyParams.X
|
||||
c.Ys = polyParams.Ys
|
||||
|
||||
for i := range c.Pubs {
|
||||
c.Pubs[i] = comp.PublicInputs[i].Acc.GetVal(run)
|
||||
}
|
||||
|
||||
for i := range c.Commitments {
|
||||
pos := ctx.NonEmptyMerkleRootPositions[i]
|
||||
c.Commitments[i] = ctx.PcsCtx.Items.MerkleRoots[pos].GetColAssignmentAt(run, 0)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// WitnessAssign is an implementation of the [plonk.WitnessAssigner] and is used to
|
||||
// generate the assignment of the fullRecursion circuit.
|
||||
type WitnessAssigner fullRecursionCtx
|
||||
|
||||
func (w WitnessAssigner) NumEffWitnesses(_ *wizard.ProverRuntime) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (w WitnessAssigner) Assign(run *wizard.ProverRuntime, i int) (private, public witness.Witness, err error) {
|
||||
|
||||
if i > 0 {
|
||||
panic("only a single witness for the full-recursion")
|
||||
}
|
||||
|
||||
var (
|
||||
ctx = fullRecursionCtx(w)
|
||||
assignment = AssignGnarkCircuit(&ctx, w.Comp, run)
|
||||
)
|
||||
|
||||
witness, err := frontend.NewWitness(assignment, ecc.BLS12_377.ScalarField())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("new witness: %W", err)
|
||||
}
|
||||
|
||||
pubWitness, err := witness.Public()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("public witness: %w", err)
|
||||
}
|
||||
|
||||
return witness, pubWitness, nil
|
||||
}
|
||||
230
prover/protocol/compiler/fullrecursion/full_recursion.go
Normal file
230
prover/protocol/compiler/fullrecursion/full_recursion.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package fullrecursion
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/accessors"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/coin"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/column"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/selfrecursion"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/vortex"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/dedicated/plonk"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/ifaces"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/query"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/wizard"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
)
|
||||
|
||||
// FullRecursion "recurses" the wizard protocol by wrapping all the verifier
|
||||
// steps in a Plonk-in-Wizard context as well as all the Proof columns. The
|
||||
// Vortex PCS verification is done via self-recursion.
|
||||
func FullRecursion(withoutGkr bool) func(comp *wizard.CompiledIOP) {
|
||||
|
||||
return func(comp *wizard.CompiledIOP) {
|
||||
var (
|
||||
ctx = captureCtx(comp)
|
||||
c = allocateGnarkCircuit(comp, ctx)
|
||||
numPI = len(c.ctx.NonEmptyMerkleRootPositions) +
|
||||
len(c.Pubs) +
|
||||
len(c.Ys) +
|
||||
3 // (1.) for X (2.) for the initial FS state (3.) for the final state
|
||||
funcPiOffset = 3 + len(ctx.NonEmptyMerkleRootPositions) + len(ctx.PolyQuery.Pols)
|
||||
)
|
||||
|
||||
selfrecursion.SelfRecurse(comp)
|
||||
|
||||
piw := plonk.PlonkCheck(comp, "full-recursion-"+strconv.Itoa(comp.SelfRecursionCount), ctx.LastRound, c, 1)
|
||||
|
||||
ctx.PlonkInWizard.PI = piw.ConcatenatedTinyPIs(utils.NextPowerOfTwo(numPI))
|
||||
ctx.PlonkInWizard.ProverAction = piw.GetPlonkProverAction()
|
||||
|
||||
for i := 0; i < numPI; i++ {
|
||||
|
||||
var (
|
||||
pi = ctx.PlonkInWizard.PI
|
||||
lo = comp.InsertLocalOpening(
|
||||
ctx.PlonkInWizard.PI.Round(),
|
||||
ifaces.QueryIDf("%v_LO_%v", pi.String(), i),
|
||||
column.Shift(pi, i),
|
||||
)
|
||||
)
|
||||
|
||||
ctx.LocalOpenings = append(ctx.LocalOpenings, lo)
|
||||
}
|
||||
|
||||
for i := range comp.PublicInputs {
|
||||
comp.PublicInputs[i].Acc = accessors.NewLocalOpeningAccessor(
|
||||
ctx.LocalOpenings[funcPiOffset+i],
|
||||
ctx.PlonkInWizard.PI.Round(),
|
||||
)
|
||||
}
|
||||
|
||||
comp.FiatShamirHooks.AppendToInner(ctx.LastRound, &ResetFsActions{fullRecursionCtx: *ctx})
|
||||
comp.RegisterProverAction(ctx.LastRound, CircuitAssignment(*ctx))
|
||||
comp.RegisterProverAction(ctx.LastRound, ReplacementAssignment(*ctx))
|
||||
comp.RegisterProverAction(ctx.PlonkInWizard.PI.Round(), LocalOpeningAssignment(*ctx))
|
||||
comp.RegisterVerifierAction(ctx.PlonkInWizard.PI.Round(), &ConsistencyCheck{fullRecursionCtx: *ctx})
|
||||
}
|
||||
}
|
||||
|
||||
// fullRecursionCtx holds compilation context informations about the wizard
|
||||
// protocol being compiled by a FullRecursion routine.
|
||||
type fullRecursionCtx struct {
|
||||
// A pointer to the compiled-IOP over which the compilation step has run
|
||||
Comp *wizard.CompiledIOP
|
||||
// The Vortex compilation context
|
||||
PcsCtx *vortex.Ctx
|
||||
PublicInputs []wizard.PublicInput
|
||||
PolyQuery query.UnivariateEval
|
||||
PolyQueryReplacement query.UnivariateEval
|
||||
MerkleRootsReplacement []ifaces.Column
|
||||
NonEmptyMerkleRootPositions []int
|
||||
FirstRound, LastRound int
|
||||
QueryParams [][]ifaces.Query
|
||||
Columns [][]ifaces.Column
|
||||
VerifierActions [][]wizard.VerifierAction
|
||||
Coins [][]coin.Info
|
||||
FsHooks [][]wizard.VerifierAction
|
||||
PlonkInWizard struct {
|
||||
ProverAction plonk.PlonkInWizardProverAction
|
||||
PI ifaces.Column
|
||||
}
|
||||
LocalOpenings []query.LocalOpening
|
||||
}
|
||||
|
||||
// captureCtx scans the content of comp to store the compilation infos of the
|
||||
// CompiledIOP at the beginning of the compilation.
|
||||
func captureCtx(comp *wizard.CompiledIOP) *fullRecursionCtx {
|
||||
|
||||
var (
|
||||
polyQuery = comp.PcsCtxs.(*vortex.Ctx).Query
|
||||
lastRound = comp.QueriesParams.Round(polyQuery.QueryID)
|
||||
ctx = &fullRecursionCtx{
|
||||
Comp: comp,
|
||||
PcsCtx: comp.PcsCtxs.(*vortex.Ctx),
|
||||
PolyQuery: polyQuery,
|
||||
LastRound: lastRound,
|
||||
FirstRound: lastRound,
|
||||
PublicInputs: append([]wizard.PublicInput{}, comp.PublicInputs...),
|
||||
}
|
||||
)
|
||||
|
||||
for round := 0; round <= lastRound; round++ {
|
||||
|
||||
ctx.QueryParams = append(ctx.QueryParams, []ifaces.Query{})
|
||||
ctx.Columns = append(ctx.Columns, []ifaces.Column{})
|
||||
ctx.VerifierActions = append(ctx.VerifierActions, []wizard.VerifierAction{})
|
||||
ctx.Coins = append(ctx.Coins, []coin.Info{})
|
||||
ctx.FsHooks = append(ctx.FsHooks, []wizard.VerifierAction{})
|
||||
|
||||
for _, colName := range comp.Columns.AllKeysAt(round) {
|
||||
|
||||
// filter the columns by status
|
||||
var (
|
||||
status = comp.Columns.Status(colName)
|
||||
col = comp.Columns.GetHandle(colName)
|
||||
)
|
||||
|
||||
if !status.IsPublic() {
|
||||
// the column is not public so it is not part of the proof
|
||||
continue
|
||||
}
|
||||
|
||||
if status == column.VerifyingKey {
|
||||
// these are constant columns
|
||||
continue
|
||||
}
|
||||
|
||||
ctx.FirstRound = min(ctx.FirstRound, round)
|
||||
ctx.Columns[round] = append(ctx.Columns[round], col)
|
||||
comp.Columns.IgnoreButKeepInProverTranscript(colName)
|
||||
}
|
||||
|
||||
for _, qName := range comp.QueriesParams.AllKeysAt(round) {
|
||||
|
||||
if comp.QueriesParams.IsSkippedFromVerifierTranscript(qName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Not that we do not filter the already compiled queries
|
||||
qInfo := comp.QueriesParams.Data(qName)
|
||||
ctx.QueryParams[round] = append(ctx.QueryParams[round], qInfo)
|
||||
comp.QueriesParams.MarkAsSkippedFromVerifierTranscript(qName)
|
||||
}
|
||||
|
||||
for _, cname := range comp.Coins.AllKeysAt(round) {
|
||||
|
||||
if comp.Coins.IsSkippedFromVerifierTranscript(cname) {
|
||||
continue
|
||||
}
|
||||
|
||||
coin := comp.Coins.Data(cname)
|
||||
ctx.Coins[round] = append(ctx.Coins[round], coin)
|
||||
comp.Coins.MarkAsSkippedFromVerifierTranscript(cname)
|
||||
}
|
||||
|
||||
verifierActions := comp.SubVerifiers.Inner()
|
||||
|
||||
for i := range verifierActions[round] {
|
||||
|
||||
va := verifierActions[round][i]
|
||||
if va.IsSkipped() {
|
||||
continue
|
||||
}
|
||||
|
||||
ctx.VerifierActions[round] = append(ctx.VerifierActions[round], va)
|
||||
va.Skip()
|
||||
}
|
||||
|
||||
if comp.FiatShamirHooks.Len() > round {
|
||||
resetFs := comp.FiatShamirHooks.Inner()[round]
|
||||
for i := range resetFs {
|
||||
|
||||
fsHook := resetFs[i]
|
||||
if fsHook.IsSkipped() {
|
||||
continue
|
||||
}
|
||||
|
||||
ctx.FsHooks[round] = append(ctx.VerifierActions[round], fsHook)
|
||||
fsHook.Skip()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comp.QueriesParams.MarkAsSkippedFromProverTranscript(polyQuery.QueryID)
|
||||
|
||||
ctx.PcsCtx.IsSelfrecursed = true
|
||||
|
||||
pcsCtxReplacement := *ctx.PcsCtx
|
||||
pcsCtxReplacement.Items.MerkleRoots = make([]ifaces.Column, len(pcsCtxReplacement.Items.MerkleRoots))
|
||||
|
||||
for i := range pcsCtxReplacement.Items.MerkleRoots {
|
||||
|
||||
if ctx.PcsCtx.Items.MerkleRoots[i] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ctx.NonEmptyMerkleRootPositions = append(ctx.NonEmptyMerkleRootPositions, i)
|
||||
pcsCtxReplacement.Items.MerkleRoots[i] = comp.InsertProof(
|
||||
ctx.LastRound,
|
||||
ctx.PcsCtx.Items.MerkleRoots[i].GetColID()+"_REPLACEMENT",
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
ctx.MerkleRootsReplacement = pcsCtxReplacement.Items.MerkleRoots
|
||||
|
||||
comp.PcsCtxs = &pcsCtxReplacement
|
||||
newPolyQuery := comp.InsertUnivariate(
|
||||
lastRound,
|
||||
polyQuery.QueryID+"_REPLACEMENT",
|
||||
polyQuery.Pols,
|
||||
)
|
||||
|
||||
comp.QueriesParams.MarkAsIgnored(newPolyQuery.QueryID)
|
||||
|
||||
ctx.PolyQueryReplacement = newPolyQuery
|
||||
pcsCtxReplacement.Query = ctx.PolyQueryReplacement
|
||||
|
||||
return ctx
|
||||
}
|
||||
105
prover/protocol/compiler/fullrecursion/full_recursion_test.go
Normal file
105
prover/protocol/compiler/fullrecursion/full_recursion_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
//go:build !fuzzlight
|
||||
|
||||
package fullrecursion_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/ringsis"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/common/smartvectors"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/dummy"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/fullrecursion"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/globalcs"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/innerproduct"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/localcs"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/lookup"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/mimc"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/permutation"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/specialqueries"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/splitter"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/splitter/sticker"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/univariates"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/compiler/vortex"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/ifaces"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/wizard"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
|
||||
logrus.SetLevel(logrus.FatalLevel)
|
||||
|
||||
define := func(bui *wizard.Builder) {
|
||||
|
||||
var (
|
||||
a = bui.RegisterCommit("A", 8)
|
||||
b = bui.RegisterCommit("B", 8)
|
||||
)
|
||||
|
||||
bui.Inclusion("Q", []ifaces.Column{a}, []ifaces.Column{b})
|
||||
}
|
||||
|
||||
prove := func(run *wizard.ProverRuntime) {
|
||||
run.AssignColumn("A", smartvectors.ForTest(1, 2, 3, 4, 5, 6, 7, 8))
|
||||
run.AssignColumn("B", smartvectors.ForTest(1, 2, 3, 4, 5, 6, 7, 8))
|
||||
}
|
||||
|
||||
suites := [][]func(*wizard.CompiledIOP){
|
||||
{
|
||||
lookup.CompileLogDerivative,
|
||||
localcs.Compile,
|
||||
globalcs.Compile,
|
||||
univariates.CompileLocalOpening,
|
||||
univariates.Naturalize,
|
||||
univariates.MultiPointToSinglePoint(8),
|
||||
vortex.Compile(2, vortex.ForceNumOpenedColumns(4), vortex.WithSISParams(&ringsis.StdParams)),
|
||||
fullrecursion.FullRecursion(true),
|
||||
dummy.CompileAtProverLvl,
|
||||
},
|
||||
{
|
||||
lookup.CompileLogDerivative,
|
||||
localcs.Compile,
|
||||
globalcs.Compile,
|
||||
univariates.CompileLocalOpening,
|
||||
univariates.Naturalize,
|
||||
univariates.MultiPointToSinglePoint(8),
|
||||
vortex.Compile(2, vortex.ForceNumOpenedColumns(4), vortex.WithSISParams(&ringsis.StdParams)),
|
||||
fullrecursion.FullRecursion(true),
|
||||
mimc.CompileMiMC,
|
||||
specialqueries.RangeProof,
|
||||
lookup.CompileLogDerivative,
|
||||
specialqueries.CompileFixedPermutations,
|
||||
permutation.CompileGrandProduct,
|
||||
innerproduct.Compile,
|
||||
sticker.Sticker(1<<8, 1<<16),
|
||||
splitter.SplitColumns(1 << 16),
|
||||
localcs.Compile,
|
||||
globalcs.Compile,
|
||||
univariates.CompileLocalOpening,
|
||||
univariates.Naturalize,
|
||||
univariates.MultiPointToSinglePoint(1 << 16),
|
||||
vortex.Compile(2, vortex.ForceNumOpenedColumns(4), vortex.WithSISParams(&ringsis.StdParams)),
|
||||
fullrecursion.FullRecursion(true),
|
||||
dummy.CompileAtProverLvl,
|
||||
},
|
||||
}
|
||||
|
||||
for i, s := range suites {
|
||||
|
||||
t.Run(fmt.Sprintf("case-%v", i), func(t *testing.T) {
|
||||
|
||||
comp := wizard.Compile(
|
||||
define,
|
||||
s...,
|
||||
)
|
||||
|
||||
proof := wizard.Prove(comp, prove)
|
||||
|
||||
if err := wizard.Verify(comp, proof); err != nil {
|
||||
t.Fatalf("verifier failed: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func Compile(comp *wizard.CompiledIOP) {
|
||||
|
||||
comp.RegisterProverAction(quotientRound, "ientCtx)
|
||||
comp.RegisterProverAction(evaluationRound, evaluationProver(evaluationCtx))
|
||||
comp.RegisterVerifierAction(evaluationRound, evaluationVerifier(evaluationCtx))
|
||||
comp.RegisterVerifierAction(evaluationRound, &evaluationVerifier{evaluationCtx: evaluationCtx})
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,10 @@ type evaluationProver evaluationCtx
|
||||
|
||||
// evaluationVerifier wraps [evaluationCtx] to implement the [wizard.VerifierAction]
|
||||
// interface.
|
||||
type evaluationVerifier evaluationCtx
|
||||
type evaluationVerifier struct {
|
||||
evaluationCtx
|
||||
skipped bool
|
||||
}
|
||||
|
||||
// declareUnivariateQueries declares the univariate queries over all the quotient
|
||||
// shares, making sure that the shares needing to be evaluated over the same
|
||||
@@ -162,7 +165,7 @@ func (pa evaluationProver) Run(run *wizard.ProverRuntime) {
|
||||
}
|
||||
|
||||
// Run evaluate the constraint and checks that
|
||||
func (ctx evaluationVerifier) Run(run *wizard.VerifierRuntime) error {
|
||||
func (ctx *evaluationVerifier) Run(run *wizard.VerifierRuntime) error {
|
||||
|
||||
var (
|
||||
// Will be assigned to "X", the random point at which we check the constraint.
|
||||
@@ -236,7 +239,7 @@ func (ctx evaluationVerifier) Run(run *wizard.VerifierRuntime) error {
|
||||
}
|
||||
|
||||
// Verifier step, evaluate the constraint and checks that
|
||||
func (ctx evaluationVerifier) RunGnark(api frontend.API, c *wizard.WizardVerifierCircuit) {
|
||||
func (ctx *evaluationVerifier) RunGnark(api frontend.API, c *wizard.WizardVerifierCircuit) {
|
||||
|
||||
// Will be assigned to "X", the random point at which we check the constraint.
|
||||
r := c.GetRandomCoinField(ctx.EvalCoin.Name)
|
||||
@@ -463,3 +466,11 @@ func (ctx evaluationVerifier) recombineQuotientSharesEvaluationGnark(api fronten
|
||||
|
||||
return recombinedYs
|
||||
}
|
||||
|
||||
func (ctx *evaluationVerifier) Skip() {
|
||||
ctx.skipped = true
|
||||
}
|
||||
|
||||
func (ctx *evaluationVerifier) IsSkipped() bool {
|
||||
return ctx.skipped
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type verifierForSize struct {
|
||||
SummationOpening query.LocalOpening
|
||||
// BatchOpening is the challenge used for the linear combination
|
||||
BatchOpening coin.Info
|
||||
skipped bool
|
||||
}
|
||||
|
||||
// Run implements [wizard.VerifierAction]
|
||||
@@ -87,3 +88,11 @@ func (v *verifierForSize) RunGnark(api frontend.API, run *wizard.WizardVerifierC
|
||||
|
||||
api.AssertIsEqual(expected, actual)
|
||||
}
|
||||
|
||||
func (v *verifierForSize) Skip() {
|
||||
v.skipped = true
|
||||
}
|
||||
|
||||
func (v *verifierForSize) IsSkipped() bool {
|
||||
return v.skipped
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func CompileLogDerivative(comp *wizard.CompiledIOP) {
|
||||
zCatalog = map[[2]int]*zCtx{}
|
||||
zEntries = [][2]int{}
|
||||
// verifier actions
|
||||
va = finalEvaluationCheck{}
|
||||
va = &finalEvaluationCheck{}
|
||||
)
|
||||
|
||||
// Skip the compilation phase if no lookup constraint is being used. Otherwise
|
||||
@@ -116,7 +116,7 @@ func CompileLogDerivative(comp *wizard.CompiledIOP) {
|
||||
}
|
||||
}
|
||||
|
||||
comp.RegisterVerifierAction(lastRound, &va)
|
||||
comp.RegisterVerifierAction(lastRound, va)
|
||||
}
|
||||
|
||||
// captureLookupTables inspects comp and look for Inclusion queries that are not
|
||||
|
||||
@@ -21,6 +21,7 @@ type finalEvaluationCheck struct {
|
||||
Name string
|
||||
// ZOpenings lists all the openings of all the zCtx
|
||||
ZOpenings []query.LocalOpening
|
||||
skipped bool
|
||||
}
|
||||
|
||||
// Run implements the [wizard.VerifierAction]
|
||||
@@ -54,3 +55,11 @@ func (f *finalEvaluationCheck) RunGnark(api frontend.API, run *wizard.WizardVeri
|
||||
|
||||
api.AssertIsEqual(zSum, 0)
|
||||
}
|
||||
|
||||
func (f *finalEvaluationCheck) Skip() {
|
||||
f.skipped = true
|
||||
}
|
||||
|
||||
func (f *finalEvaluationCheck) IsSkipped() bool {
|
||||
return f.skipped
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func CompileGrandProduct(comp *wizard.CompiledIOP) {
|
||||
for round := range allProverActions {
|
||||
if len(allProverActions[round]) > 0 {
|
||||
comp.RegisterProverAction(round, allProverActions[round])
|
||||
comp.RegisterVerifierAction(round, VerifierCtx(allProverActions[round]))
|
||||
comp.RegisterVerifierAction(round, &VerifierCtx{Ctxs: allProverActions[round]})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,15 +11,18 @@ import (
|
||||
// The verifier gets all the query openings and multiple them together and
|
||||
// expect them to be one. It is represented by an array of ZCtx holding for
|
||||
// the same round. (we have the guarantee that they come from the same query).
|
||||
type VerifierCtx []*ZCtx
|
||||
type VerifierCtx struct {
|
||||
Ctxs []*ZCtx
|
||||
skipped bool
|
||||
}
|
||||
|
||||
// Run implements the [wizard.VerifierAction] interface and checks that the
|
||||
// product of the products given by the ZCtx is equal to one.
|
||||
func (v VerifierCtx) Run(run *wizard.VerifierRuntime) error {
|
||||
func (v *VerifierCtx) Run(run *wizard.VerifierRuntime) error {
|
||||
|
||||
mustBeOne := field.One()
|
||||
|
||||
for _, zCtx := range v {
|
||||
for _, zCtx := range v.Ctxs {
|
||||
for _, opening := range zCtx.ZOpenings {
|
||||
y := run.GetLocalPointEvalParams(opening.ID).Y
|
||||
mustBeOne.Mul(&mustBeOne, &y)
|
||||
@@ -35,11 +38,11 @@ func (v VerifierCtx) Run(run *wizard.VerifierRuntime) error {
|
||||
|
||||
// Run implements the [wizard.VerifierAction] interface and is as
|
||||
// [VerifierCtx.Run] but in the context of a gnark circuit.
|
||||
func (v VerifierCtx) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
func (v *VerifierCtx) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
|
||||
mustBeOne := frontend.Variable(1)
|
||||
|
||||
for _, zCtx := range v {
|
||||
for _, zCtx := range v.Ctxs {
|
||||
for _, opening := range zCtx.ZOpenings {
|
||||
y := run.GetLocalPointEvalParams(opening.ID).Y
|
||||
mustBeOne = api.Mul(mustBeOne, y)
|
||||
@@ -48,3 +51,11 @@ func (v VerifierCtx) RunGnark(api frontend.API, run *wizard.WizardVerifierCircui
|
||||
|
||||
api.AssertIsEqual(mustBeOne, frontend.Variable(1))
|
||||
}
|
||||
|
||||
func (v *VerifierCtx) Skip() {
|
||||
v.skipped = true
|
||||
}
|
||||
|
||||
func (v *VerifierCtx) IsSkipped() bool {
|
||||
return v.skipped
|
||||
}
|
||||
|
||||
@@ -284,10 +284,10 @@ func NewSelfRecursionCxt(comp *wizard.CompiledIOP) SelfRecursionCtx {
|
||||
func assertVortexCompiled(comp *wizard.CompiledIOP) *vortex.Ctx {
|
||||
// When we compiled using Vortex, we annotated the compiledIOP
|
||||
// that the current protocol was a result of the
|
||||
ctx := comp.CryptographicCompilerCtx
|
||||
ctx := comp.PcsCtxs
|
||||
|
||||
// Take ownership of the vortex context
|
||||
comp.CryptographicCompilerCtx = nil
|
||||
comp.PcsCtxs = nil
|
||||
|
||||
// Check for non-nilness
|
||||
if ctx == nil {
|
||||
|
||||
@@ -405,7 +405,7 @@ func (ctx *stickContext) compileFixedEvaluation() {
|
||||
// Filters out only the
|
||||
q, ok := ctx.comp.QueriesParams.Data(qName).(query.LocalOpening)
|
||||
if !ok {
|
||||
utils.Panic("got an uncompilable query %v", qName)
|
||||
utils.Panic("got an uncompilable query name=%v type=%T", qName, q)
|
||||
}
|
||||
|
||||
// Assumption, the query is not over an interleaved column
|
||||
|
||||
@@ -61,7 +61,7 @@ func Compile(blowUpFactor int, options ...VortexOp) func(*wizard.CompiledIOP) {
|
||||
lastRound := comp.NumRounds() - 1
|
||||
|
||||
// Stores a pointer to the cryptographic compiler of Vortex
|
||||
comp.CryptographicCompilerCtx = &ctx
|
||||
comp.PcsCtxs = &ctx
|
||||
|
||||
// Converts the precomputed as verifying key (e.g. send
|
||||
// them to the verifier) in the offline phase if the
|
||||
@@ -86,6 +86,10 @@ func Compile(blowUpFactor int, options ...VortexOp) func(*wizard.CompiledIOP) {
|
||||
// Registers the prover and verifier steps
|
||||
comp.SubProvers.AppendToInner(lastRound+1, ctx.ComputeLinearComb)
|
||||
comp.SubProvers.AppendToInner(lastRound+2, ctx.OpenSelectedColumns)
|
||||
// This is separated from GnarkVerify because, when doing full-recursion
|
||||
// , we want to recurse this verifier step but not [ctx.Verify] which is
|
||||
// already handled by the self-recursion mechanism.
|
||||
comp.InsertVerifier(lastRound, ctx.explicitPublicEvaluation, ctx.gnarkExplicitPublicEvaluation)
|
||||
comp.InsertVerifier(lastRound+2, ctx.Verify, ctx.GnarkVerify)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
)
|
||||
|
||||
func (ctx *Ctx) GnarkVerify(api frontend.API, vr *wizard.WizardVerifierCircuit) {
|
||||
// Evaluate explicitly the public columns
|
||||
ctx.gnarkExplicitPublicEvaluation(api, vr)
|
||||
|
||||
// The skip verification flag may be on, if the current vortex
|
||||
// context get self-recursed. In this case, the verifier does
|
||||
@@ -64,7 +62,7 @@ func (ctx *Ctx) GnarkVerify(api frontend.API, vr *wizard.WizardVerifierCircuit)
|
||||
// function that will defer the hashing to gkr
|
||||
factoryHasherFunc := func(_ frontend.API) (hash.FieldHasher, error) {
|
||||
h := vr.HasherFactory.NewHasher()
|
||||
return &h, nil
|
||||
return h, nil
|
||||
}
|
||||
|
||||
packedMProofs := vr.GetColumn(ctx.MerkleProofName())
|
||||
@@ -93,7 +91,7 @@ func (ctx *Ctx) GnarkVerify(api frontend.API, vr *wizard.WizardVerifierCircuit)
|
||||
}
|
||||
|
||||
// returns the Ys as a vector
|
||||
func (ctx *Ctx) gnarkGetYs(api frontend.API, vr *wizard.WizardVerifierCircuit) (ys [][]frontend.Variable) {
|
||||
func (ctx *Ctx) gnarkGetYs(_ frontend.API, vr *wizard.WizardVerifierCircuit) (ys [][]frontend.Variable) {
|
||||
|
||||
query := ctx.Query
|
||||
params := vr.GetUnivariateParams(ctx.Query.QueryID)
|
||||
|
||||
@@ -15,11 +15,6 @@ import (
|
||||
|
||||
func (ctx *Ctx) Verify(vr *wizard.VerifierRuntime) error {
|
||||
|
||||
// Evaluate explicitly the public columns
|
||||
if err := ctx.explicitPublicEvaluation(vr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The skip verification flag may be on, if the current vortex
|
||||
// context get self-recursed. In this case, the verifier does
|
||||
// not need to do anything
|
||||
|
||||
@@ -171,6 +171,8 @@ func (ci *CircuitAlignmentInput) Assign(run *wizard.ProverRuntime, i int) (priva
|
||||
return ci.witnesses[i], ci.witnesses[i], nil
|
||||
}
|
||||
|
||||
// NumEffWitnesses returns the effective number of Plonk witnesses that are
|
||||
// collected from the assignment of the AlignmentModule.
|
||||
func (ci *CircuitAlignmentInput) NumEffWitnesses(run *wizard.ProverRuntime) int {
|
||||
ci.prepareWitnesses(run)
|
||||
return ci.numEffWitnesses
|
||||
@@ -258,6 +260,9 @@ func DefineAlignment(comp *wizard.CompiledIOP, toAlign *CircuitAlignmentInput) *
|
||||
return res
|
||||
}
|
||||
|
||||
// csIsActive adds the cosntraints ensuring that the [Alignment.IsActive] column
|
||||
// is well-formed. Namely, that this is a sequence of 1s followed by a sequence
|
||||
// of 0s.
|
||||
func (a *Alignment) csIsActive(comp *wizard.CompiledIOP) {
|
||||
// IsActive is binary column
|
||||
comp.InsertGlobal(a.Round, ifaces.QueryIDf("%v_IS_ACTIVE_BINARY", a.Name), symbolic.Mul(a.IsActive, symbolic.Sub(a.IsActive, 1)))
|
||||
@@ -265,10 +270,16 @@ func (a *Alignment) csIsActive(comp *wizard.CompiledIOP) {
|
||||
comp.InsertGlobal(a.Round, ifaces.QueryIDf("%v_IS_ACTIVE_SWITCH", a.Name), symbolic.Sub(a.IsActive, symbolic.Mul(a.IsActive, column.Shift(a.IsActive, -1))))
|
||||
}
|
||||
|
||||
// csProjection ensures the data in the [Alignment.Data] column is the same as
|
||||
// the data provided by the [Alignment.CircuitInput].
|
||||
func (a *Alignment) csProjection(comp *wizard.CompiledIOP) {
|
||||
projection.InsertProjection(comp, ifaces.QueryIDf("%v_PROJECTION", a.Name), []ifaces.Column{a.DataToCircuit}, []ifaces.Column{a.CircuitInput}, a.DataToCircuitMask, a.ActualCircuitInputMask)
|
||||
}
|
||||
|
||||
// csProjectionSelector constraints that the projection selection
|
||||
// [Alignment.ActualCircuitInputMask] is well-formed. This ensures that the
|
||||
// imported data are correctly imported "in-front" of the public inputs of the
|
||||
// Plonk.
|
||||
func (a *Alignment) csProjectionSelector(comp *wizard.CompiledIOP) {
|
||||
// ACTUAL_PI_MASK = IS_ACTIVE * STATIC_PI_MASK
|
||||
comp.InsertGlobal(a.Round, ifaces.QueryIDf("%v_ACTUAL_SUBSET", a.Name), symbolic.Sub(a.ActualCircuitInputMask, symbolic.Mul(a.IsActive, a.FullCircuitInputMask)))
|
||||
@@ -281,6 +292,8 @@ func (a *Alignment) Assign(run *wizard.ProverRuntime) {
|
||||
a.assignCircMaskOpenings(run)
|
||||
}
|
||||
|
||||
// assignMasks assigns the [Alignment.IsActive] and the [Alignment.ActualCircuitInputMask]
|
||||
// into `run`.
|
||||
func (a *Alignment) assignMasks(run *wizard.ProverRuntime) {
|
||||
// we want to assign IS_ACTIVE and ACTUAL_MASK columns. We can construct
|
||||
// them at the same time from the precomputed mask and selector.
|
||||
@@ -320,7 +333,7 @@ func (a *Alignment) assignMasks(run *wizard.ProverRuntime) {
|
||||
run.AssignColumn(a.ActualCircuitInputMask.GetColID(), smartvectors.NewRegular(actualCircMaskAssignment))
|
||||
}
|
||||
|
||||
// assignCircMaskOpenings assigns the openings queries over [actualCircMaskAssignment]
|
||||
// assignCircMaskOpenings assigns the openings queries over the actualCircMaskAssignment
|
||||
func (a *Alignment) assignCircMaskOpenings(run *wizard.ProverRuntime) {
|
||||
for i := range a.circMaskOpenings {
|
||||
v := a.circMaskOpenings[i].Pol.GetColAssignmentAt(run, 0)
|
||||
@@ -328,7 +341,8 @@ func (a *Alignment) assignCircMaskOpenings(run *wizard.ProverRuntime) {
|
||||
}
|
||||
}
|
||||
|
||||
// getCircuitMaskValue returns the
|
||||
// getCircuitMaskValue returns the static assignment of the precomputed columns
|
||||
// to be assigned to [Alignment.FullCircuitInputMask].
|
||||
func getCircuitMaskValue(nbPublicInputPerCircuit, nbCircuitInstance int) smartvectors.SmartVector {
|
||||
|
||||
var (
|
||||
@@ -345,7 +359,8 @@ func getCircuitMaskValue(nbPublicInputPerCircuit, nbCircuitInstance int) smartve
|
||||
return smartvectors.NewRegular(maskValue)
|
||||
}
|
||||
|
||||
// check the activators are well-set w.r.t to the circuit mask column
|
||||
// checkActivators adds the constraints checking the activators are well-set w.r.t
|
||||
// to the circuit mask column. See [compilationCtx.Columns.Activators].
|
||||
func (ci *Alignment) checkActivators(comp *wizard.CompiledIOP) {
|
||||
|
||||
var (
|
||||
@@ -366,12 +381,17 @@ func (ci *Alignment) checkActivators(comp *wizard.CompiledIOP) {
|
||||
|
||||
ci.circMaskOpenings = openings
|
||||
|
||||
comp.RegisterVerifierAction(ci.Round, checkActivatorAndMask(*ci))
|
||||
comp.RegisterVerifierAction(ci.Round, &checkActivatorAndMask{Alignment: *ci})
|
||||
}
|
||||
|
||||
type checkActivatorAndMask Alignment
|
||||
// checkActivatorAndMask is an implementation of [wizard.VerifierAction] and is
|
||||
// used to embody the verifier checks added by [checkActivators].
|
||||
type checkActivatorAndMask struct {
|
||||
Alignment
|
||||
skipped bool
|
||||
}
|
||||
|
||||
func (c checkActivatorAndMask) Run(run *wizard.VerifierRuntime) error {
|
||||
func (c *checkActivatorAndMask) Run(run *wizard.VerifierRuntime) error {
|
||||
for i := range c.circMaskOpenings {
|
||||
var (
|
||||
localOpening = run.GetLocalPointEvalParams(c.circMaskOpenings[i].ID)
|
||||
@@ -390,7 +410,7 @@ func (c checkActivatorAndMask) Run(run *wizard.VerifierRuntime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c checkActivatorAndMask) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
func (c *checkActivatorAndMask) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
for i := range c.circMaskOpenings {
|
||||
var (
|
||||
valOpened = run.GetLocalPointEvalParams(c.circMaskOpenings[i].ID).Y
|
||||
@@ -400,3 +420,11 @@ func (c checkActivatorAndMask) RunGnark(api frontend.API, run *wizard.WizardVeri
|
||||
api.AssertIsEqual(valOpened, valActiv)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *checkActivatorAndMask) Skip() {
|
||||
c.skipped = true
|
||||
}
|
||||
|
||||
func (c *checkActivatorAndMask) IsSkipped() bool {
|
||||
return c.skipped
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func PlonkCheck(
|
||||
comp.RegisterProverAction(round+1, lroCommitProverAction{compilationCtx: ctx, proverStateLock: &sync.Mutex{}})
|
||||
}
|
||||
|
||||
comp.RegisterVerifierAction(round, checkingActivators(ctx.Columns.Activators))
|
||||
comp.RegisterVerifierAction(round, &checkingActivators{Cols: ctx.Columns.Activators})
|
||||
|
||||
return ctx
|
||||
}
|
||||
@@ -299,20 +299,23 @@ func (ctx *compilationCtx) addCopyConstraint() {
|
||||
|
||||
// checkingActivators implements the [wizard.VerifierAction] interface and
|
||||
// checks that the [Activators] columns are correctly assigned
|
||||
type checkingActivators []ifaces.Column
|
||||
type checkingActivators struct {
|
||||
Cols []ifaces.Column
|
||||
skipped bool
|
||||
}
|
||||
|
||||
var _ wizard.VerifierAction = checkingActivators{}
|
||||
var _ wizard.VerifierAction = &checkingActivators{}
|
||||
|
||||
func (ca checkingActivators) Run(run *wizard.VerifierRuntime) error {
|
||||
for i := range ca {
|
||||
func (ca *checkingActivators) Run(run *wizard.VerifierRuntime) error {
|
||||
for i := range ca.Cols {
|
||||
|
||||
curr := ca[i].GetColAssignmentAt(run, 0)
|
||||
curr := ca.Cols[i].GetColAssignmentAt(run, 0)
|
||||
if !curr.IsOne() && !curr.IsZero() {
|
||||
return fmt.Errorf("error the activators must be 0 or 1")
|
||||
}
|
||||
|
||||
if i+1 < len(ca) {
|
||||
next := ca[i+1].GetColAssignmentAt(run, 0)
|
||||
if i+1 < len(ca.Cols) {
|
||||
next := ca.Cols[i+1].GetColAssignmentAt(run, 0)
|
||||
if curr.IsZero() && !next.IsZero() {
|
||||
return fmt.Errorf("the activators must never go from 0 to 1")
|
||||
}
|
||||
@@ -322,15 +325,23 @@ func (ca checkingActivators) Run(run *wizard.VerifierRuntime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ca checkingActivators) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
for i := range ca {
|
||||
func (ca *checkingActivators) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
for i := range ca.Cols {
|
||||
|
||||
curr := ca[i].GetColAssignmentGnarkAt(run, 0)
|
||||
curr := ca.Cols[i].GetColAssignmentGnarkAt(run, 0)
|
||||
api.AssertIsBoolean(curr)
|
||||
|
||||
if i+1 < len(ca) {
|
||||
next := ca[i+1].GetColAssignmentGnarkAt(run, 0)
|
||||
if i+1 < len(ca.Cols) {
|
||||
next := ca.Cols[i+1].GetColAssignmentGnarkAt(run, 0)
|
||||
api.AssertIsEqual(next, api.Mul(curr, next))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ca *checkingActivators) Skip() {
|
||||
ca.skipped = true
|
||||
}
|
||||
|
||||
func (ca *checkingActivators) IsSkipped() bool {
|
||||
return ca.skipped
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ type projectionProverAction struct {
|
||||
type projectionVerifierAction struct {
|
||||
Name ifaces.QueryID
|
||||
HornerA0, HornerB0 query.LocalOpening
|
||||
skipped bool
|
||||
}
|
||||
|
||||
// InsertProjection applies a projection query between sets (columnsA, filterA)
|
||||
@@ -212,7 +213,7 @@ func InsertProjection(
|
||||
pa.HornerB0 = comp.InsertLocalOpening(round, ifaces.QueryIDf("%v_HORNER_B0", queryName), pa.HornerB)
|
||||
|
||||
comp.RegisterProverAction(round, pa)
|
||||
comp.RegisterVerifierAction(round, projectionVerifierAction{HornerA0: pa.HornerA0, HornerB0: pa.HornerB0, Name: queryName})
|
||||
comp.RegisterVerifierAction(round, &projectionVerifierAction{HornerA0: pa.HornerA0, HornerB0: pa.HornerB0, Name: queryName})
|
||||
}
|
||||
|
||||
// Run implements the [wizard.ProverAction] interface.
|
||||
@@ -314,7 +315,7 @@ func (pa projectionProverAction) Run(run *wizard.ProverRuntime) {
|
||||
}
|
||||
|
||||
// Run implements the [wizard.VerifierAction] interface.
|
||||
func (va projectionVerifierAction) Run(run *wizard.VerifierRuntime) error {
|
||||
func (va *projectionVerifierAction) Run(run *wizard.VerifierRuntime) error {
|
||||
|
||||
var (
|
||||
a = run.GetLocalPointEvalParams(va.HornerA0.ID).Y
|
||||
@@ -329,7 +330,7 @@ func (va projectionVerifierAction) Run(run *wizard.VerifierRuntime) error {
|
||||
}
|
||||
|
||||
// RunGnark implements the [wizard.VerifierAction] interface.
|
||||
func (va projectionVerifierAction) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
func (va *projectionVerifierAction) RunGnark(api frontend.API, run *wizard.WizardVerifierCircuit) {
|
||||
|
||||
var (
|
||||
a = run.GetLocalPointEvalParams(va.HornerA0.ID).Y
|
||||
@@ -339,6 +340,14 @@ func (va projectionVerifierAction) RunGnark(api frontend.API, run *wizard.Wizard
|
||||
api.AssertIsEqual(a, b)
|
||||
}
|
||||
|
||||
func (va *projectionVerifierAction) Skip() {
|
||||
va.skipped = true
|
||||
}
|
||||
|
||||
func (va *projectionVerifierAction) IsSkipped() bool {
|
||||
return va.skipped
|
||||
}
|
||||
|
||||
// cmptHorner computes a random Horner accumulation of the filtered elements
|
||||
// starting from the last entry down to the first entry. The final value is
|
||||
// stored in the last entry of the returned slice.
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/consensys/gnark/frontend"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/fiatshamir"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/field"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/column"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/ifaces"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
)
|
||||
@@ -30,20 +29,6 @@ func (lop LocalOpeningParams) UpdateFS(fs *fiatshamir.State) {
|
||||
// Constructs a new local opening query
|
||||
func NewLocalOpening(id ifaces.QueryID, pol ifaces.Column) LocalOpening {
|
||||
|
||||
// For simplicity, we enforce the `pol` to be either Natural or Shifted(Natural)
|
||||
// Allegedly, this does not block any-case
|
||||
switch h := pol.(type) {
|
||||
case column.Natural:
|
||||
// allowed
|
||||
case column.Shifted:
|
||||
if _, ok := h.Parent.(column.Natural); !ok {
|
||||
utils.Panic("Unsupported handle should only be a shifted or a natural %v", pol)
|
||||
}
|
||||
// allowed
|
||||
default:
|
||||
utils.Panic("Unsupported handle should only be a shifted %v", pol)
|
||||
}
|
||||
|
||||
if len(pol.GetColID()) == 0 {
|
||||
utils.Panic("Assigned a polynomial name with an empty length")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ type ProverAction interface {
|
||||
// protocol. Usually, this is used to represent verifier checks. They can be
|
||||
// registered via [CompiledIOP.RegisterVerifierAction].
|
||||
type VerifierAction interface {
|
||||
// Skip indicates that the verifier action can be skipped
|
||||
Skip()
|
||||
// IsSkipped returns whether the current VerifierAction is skipped
|
||||
IsSkipped() bool
|
||||
// Run executes the VerifierAction over a [VerifierRuntime] it returns an
|
||||
// error.
|
||||
Run(*VerifierRuntime) error
|
||||
@@ -21,3 +25,26 @@ type VerifierAction interface {
|
||||
// error the function enforces the passing of the verifier's checks.
|
||||
RunGnark(frontend.API, *WizardVerifierCircuit)
|
||||
}
|
||||
|
||||
// genVerifierAction represents a verifier action represented by closures
|
||||
type genVerifierAction struct {
|
||||
skipped bool
|
||||
run func(*VerifierRuntime) error
|
||||
runGnark func(frontend.API, *WizardVerifierCircuit)
|
||||
}
|
||||
|
||||
func (gva *genVerifierAction) Run(run *VerifierRuntime) error {
|
||||
return gva.run(run)
|
||||
}
|
||||
|
||||
func (gva *genVerifierAction) RunGnark(api frontend.API, run *WizardVerifierCircuit) {
|
||||
gva.runGnark(api, run)
|
||||
}
|
||||
|
||||
func (gva *genVerifierAction) Skip() {
|
||||
gva.skipped = true
|
||||
}
|
||||
|
||||
func (gva *genVerifierAction) IsSkipped() bool {
|
||||
return gva.skipped
|
||||
}
|
||||
|
||||
@@ -294,16 +294,8 @@ func (b *Builder) equalizeRounds(numRounds int) {
|
||||
/*
|
||||
Check and reserve for the verifiers
|
||||
*/
|
||||
if comp.subVerifiers.Len() > numRounds {
|
||||
utils.Panic("Bug : numRounds is %v but %v rounds are registered for the verifier. %v", numRounds, comp.subVerifiers.Len(), helpMsg)
|
||||
if comp.SubVerifiers.Len() > numRounds {
|
||||
utils.Panic("Bug : numRounds is %v but %v rounds are registered for the verifier. %v", numRounds, comp.SubVerifiers.Len(), helpMsg)
|
||||
}
|
||||
comp.subVerifiers.Reserve(numRounds)
|
||||
|
||||
/*
|
||||
Check and reserve for the gnark verifiers
|
||||
*/
|
||||
if comp.gnarkSubVerifiers.Len() > numRounds {
|
||||
utils.Panic("Bug : numRounds is %v but %v rounds are registered for the gnark verifier. %v", numRounds, comp.gnarkSubVerifiers.Len(), helpMsg)
|
||||
}
|
||||
comp.gnarkSubVerifiers.Reserve(numRounds)
|
||||
comp.SubVerifiers.Reserve(numRounds)
|
||||
}
|
||||
|
||||
@@ -71,28 +71,26 @@ type CompiledIOP struct {
|
||||
// manual checks that the verifier has to perform. This is useful when a check
|
||||
// cannot be represented in term of query but, when possible, queries should
|
||||
// always be preferred to express a relation that the witness must satisfy.
|
||||
subVerifiers collection.VecVec[VerifierStep]
|
||||
SubVerifiers collection.VecVec[VerifierAction]
|
||||
|
||||
// gnarkSubVerifiers does the same as [gnarkSubVerifiers] but in a gnark
|
||||
// circuit. Whenever, the user add a subVerifier function into the compiled
|
||||
// IOP, he should also provide an equivalent gnark function that does
|
||||
// exactly the same thing, but in a gnark circuit. This used when
|
||||
// instantiating a gnark verifier for the sub-protocol.
|
||||
gnarkSubVerifiers collection.VecVec[GnarkVerifierStep]
|
||||
// FiatShamirHooks is an action that is run during the FS sampling. Compared
|
||||
// to a normal verifier action it has the possibility to interact with the
|
||||
// Fiat-Shamir state.
|
||||
FiatShamirHooks collection.VecVec[VerifierAction]
|
||||
|
||||
// Precomputed stores the assignments of all the Precomputed and VerifierKey
|
||||
// polynomials. It is assigned directly when registering a precomputed
|
||||
// column.
|
||||
Precomputed collection.Mapping[ifaces.ColID, ifaces.ColAssignment]
|
||||
|
||||
// CryptographicCompilerCtx stores the compilation context of the last used
|
||||
// PcsCtxs stores the compilation context of the last used
|
||||
// cryptographic compiler. Specifically, it is aimed to store the last
|
||||
// Vortex compilation context (see [github.com/consensys/linea-monorepo/prover/protocol/compiler]) that was used. And
|
||||
// its purpose is to provide the Vortex context to the self-recursion
|
||||
// compilation context; see [github.com/consensys/linea-monorepo/prover/protocol/compiler/selfrecursion]. This allows
|
||||
// the self-recursion context to learn about the columns to use and the
|
||||
// Vortex parameters.
|
||||
CryptographicCompilerCtx any
|
||||
PcsCtxs any
|
||||
|
||||
// DummyCompiled that can be set internally by the compilation, when we are
|
||||
// using the [github.com/consensys/linea-monorepo/prover/protocol/compiler/dummy.Compile] compilation step. This steps
|
||||
@@ -120,6 +118,10 @@ type CompiledIOP struct {
|
||||
//
|
||||
// For efficiency reasons, the fiatShamirSetup is derived using SHA2.
|
||||
fiatShamirSetup field.Element
|
||||
|
||||
// FunctionalPublic inputs lists the queries representing a public inputs
|
||||
// and their identifiers
|
||||
PublicInputs []PublicInput
|
||||
}
|
||||
|
||||
// NumRounds returns the total number of prover interactions with the verifier
|
||||
@@ -478,8 +480,10 @@ func (c *CompiledIOP) InsertPublicInput(round int, name ifaces.ColID, size int)
|
||||
// passing `nil` is fine.
|
||||
func (c *CompiledIOP) InsertVerifier(round int, ver VerifierStep, gnarkVer GnarkVerifierStep) {
|
||||
c.assertConsistentRound(round)
|
||||
c.gnarkSubVerifiers.AppendToInner(round, gnarkVer)
|
||||
c.subVerifiers.AppendToInner(round, ver)
|
||||
c.SubVerifiers.AppendToInner(round, &genVerifierAction{
|
||||
run: ver,
|
||||
runGnark: gnarkVer,
|
||||
})
|
||||
}
|
||||
|
||||
// InsertRange registers [query.Range] in the CompiledIOP. Namely, it ensures
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/fiatshamir"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc/gkrmimc"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/common/smartvectors"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/field"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/coin"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/column"
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/ifaces"
|
||||
@@ -80,6 +81,11 @@ type WizardVerifierCircuit struct {
|
||||
// hashes but also the MiMC Vortex column hashes that we use for the
|
||||
// last round of the self-recursion.
|
||||
HasherFactory *gkrmimc.HasherFactory `gnark:"-"`
|
||||
|
||||
// FiatShamirHistory tracks the fiat-shamir state at the beginning of every
|
||||
// round. The first entry is the initial state, the final entry is the final
|
||||
// state.
|
||||
FiatShamirHistory [][2][]frontend.Variable `gnark:"-"`
|
||||
}
|
||||
|
||||
// AllocateWizardCircuit allocates the inner-slices of the verifier struct from a precompiled IOP. It
|
||||
@@ -89,7 +95,7 @@ type WizardVerifierCircuit struct {
|
||||
// the circuit.
|
||||
func AllocateWizardCircuit(comp *CompiledIOP) (*WizardVerifierCircuit, error) {
|
||||
|
||||
res := newWizardVerifierCircuit()
|
||||
res := NewWizardVerifierCircuit()
|
||||
|
||||
for i, colName := range comp.Columns.AllKeys() {
|
||||
// filter the columns by status
|
||||
@@ -109,9 +115,7 @@ func AllocateWizardCircuit(comp *CompiledIOP) (*WizardVerifierCircuit, error) {
|
||||
size := comp.Columns.GetSize(colName)
|
||||
|
||||
// Allocates the column in the circuit and indexes it
|
||||
colID := len(res.Columns)
|
||||
res.Columns = append(res.Columns, gnarkutil.AllocateSlice(size))
|
||||
res.columnsIDs.InsertNew(colName, colID)
|
||||
res.AllocColumn(colName, size)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -129,17 +133,11 @@ func AllocateWizardCircuit(comp *CompiledIOP) (*WizardVerifierCircuit, error) {
|
||||
|
||||
switch qInfo := qInfoIface.(type) {
|
||||
case query.UnivariateEval:
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
res.univariateParamsIDs.InsertNew(qName, len(res.UnivariateParams))
|
||||
res.UnivariateParams = append(res.UnivariateParams, qInfo.GnarkAllocate())
|
||||
res.AllocUnivariateEval(qName, qInfo)
|
||||
case query.InnerProduct:
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
res.innerProductIDs.InsertNew(qName, len(res.InnerProductParams))
|
||||
res.InnerProductParams = append(res.InnerProductParams, qInfo.GnarkAllocate())
|
||||
res.AllocInnerProduct(qName, qInfo)
|
||||
case query.LocalOpening:
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
res.localOpeningIDs.InsertNew(qName, len(res.LocalOpeningParams))
|
||||
res.LocalOpeningParams = append(res.LocalOpeningParams, query.GnarkLocalOpeningParams{})
|
||||
res.AllocLocalOpening(qName, qInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,67 +152,55 @@ func (c *WizardVerifierCircuit) Verify(api frontend.API) {
|
||||
c.HasherFactory = gkrmimc.NewHasherFactory(api)
|
||||
c.FS = fiatshamir.NewGnarkFiatShamir(api, c.HasherFactory)
|
||||
c.FS.Update(c.Spec.fiatShamirSetup)
|
||||
c.FiatShamirHistory = make([][2][]frontend.Variable, c.Spec.NumRounds())
|
||||
c.generateAllRandomCoins(api)
|
||||
|
||||
logrus.Tracef("Generated the coins")
|
||||
|
||||
for _, roundSteps := range c.Spec.gnarkSubVerifiers.Inner() {
|
||||
for _, roundSteps := range c.Spec.SubVerifiers.Inner() {
|
||||
for _, step := range roundSteps {
|
||||
step(api, c)
|
||||
if !step.IsSkipped() {
|
||||
step.RunGnark(api, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generateAllRandomCoins is as [VerifierRuntime.generateAllRandomCoins]. Note that the function
|
||||
// does create constraints via the hasher factory that is inside of `c.FS`.
|
||||
func (c *WizardVerifierCircuit) generateAllRandomCoins(_ frontend.API) {
|
||||
func (c *WizardVerifierCircuit) generateAllRandomCoins(api frontend.API) {
|
||||
|
||||
for currRound := 0; currRound < c.Spec.NumRounds(); currRound++ {
|
||||
|
||||
initialState := c.FS.State()
|
||||
|
||||
if currRound > 0 {
|
||||
|
||||
// Make sure that all messages have been written and use them
|
||||
// to update the FS state. Note that we do not need to update
|
||||
// FS using the last round of the prover because he is always
|
||||
// the last one to "talk" in the protocol.
|
||||
toUpdateFS := c.Spec.Columns.AllKeysProofAt(currRound - 1)
|
||||
for _, msg := range toUpdateFS {
|
||||
msgContent := c.GetColumn(msg)
|
||||
c.FS.UpdateVec(msgContent)
|
||||
}
|
||||
|
||||
toUpdateFS = c.Spec.Columns.AllKeysPublicInputAt(currRound - 1)
|
||||
for _, msg := range toUpdateFS {
|
||||
msgContent := c.GetColumn(msg)
|
||||
c.FS.UpdateVec(msgContent)
|
||||
}
|
||||
|
||||
/*
|
||||
Sanity-check : Make sure all issued random coin have been
|
||||
"consumed" by all the verifiers steps, in the round we are
|
||||
"closing"
|
||||
Also include the prover's allegations for all evaluations
|
||||
*/
|
||||
toBeConsumed := c.Spec.Coins.AllKeysAt(currRound - 1)
|
||||
c.Coins.Exists(toBeConsumed...)
|
||||
|
||||
if !c.Spec.DummyCompiled {
|
||||
|
||||
// Make sure that all messages have been written and use them
|
||||
// to update the FS state. Note that we do not need to update
|
||||
// FS using the last round of the prover because he is always
|
||||
// the last one to "talk" in the protocol.
|
||||
toUpdateFS := c.Spec.Columns.AllKeysProofAt(currRound - 1)
|
||||
for _, msg := range toUpdateFS {
|
||||
|
||||
msgID := c.columnsIDs.MustGet(msg)
|
||||
msgContent := c.Columns[msgID]
|
||||
|
||||
logrus.Tracef("VERIFIER CIRCUIT : Updating the FS oracle with a message - %v", msg)
|
||||
c.FS.UpdateVec(msgContent)
|
||||
queries := c.Spec.QueriesParams.AllKeysAt(currRound - 1)
|
||||
for _, qName := range queries {
|
||||
if c.Spec.QueriesParams.IsSkippedFromVerifierTranscript(qName) {
|
||||
continue
|
||||
}
|
||||
|
||||
toUpdateFS = c.Spec.Columns.AllKeysPublicInputAt(currRound - 1)
|
||||
for _, msg := range toUpdateFS {
|
||||
|
||||
msgID := c.columnsIDs.MustGet(msg)
|
||||
msgContent := c.Columns[msgID]
|
||||
|
||||
logrus.Tracef("VERIFIER CIRCUIT : Updating the FS oracle with public input - %v", msg)
|
||||
c.FS.UpdateVec(msgContent)
|
||||
}
|
||||
|
||||
/*
|
||||
Also include the prover's allegations for all evaluations
|
||||
*/
|
||||
queries := c.Spec.QueriesParams.AllKeysAt(currRound - 1)
|
||||
for _, qName := range queries {
|
||||
// Implicitly, this will panic whenever we start supporting
|
||||
// a new type of query params
|
||||
params := c.GetParams(qName)
|
||||
params.UpdateFS(c.FS)
|
||||
}
|
||||
params := c.GetParams(qName)
|
||||
params.UpdateFS(c.FS)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,8 +209,11 @@ func (c *WizardVerifierCircuit) generateAllRandomCoins(_ frontend.API) {
|
||||
*/
|
||||
toCompute := c.Spec.Coins.AllKeysAt(currRound)
|
||||
for _, coinName := range toCompute {
|
||||
if c.Spec.Coins.IsSkippedFromVerifierTranscript(coinName) {
|
||||
continue
|
||||
}
|
||||
|
||||
info := c.Spec.Coins.Data(coinName)
|
||||
logrus.Tracef("VERIFIER CIRCUIT : Generate a random coin - %v", coinName)
|
||||
switch info.Type {
|
||||
case coin.Field:
|
||||
value := c.FS.RandomField()
|
||||
@@ -234,6 +223,22 @@ func (c *WizardVerifierCircuit) generateAllRandomCoins(_ frontend.API) {
|
||||
c.Coins.InsertNew(coinName, value)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Spec.FiatShamirHooks.Len() > currRound {
|
||||
fsHooks := c.Spec.FiatShamirHooks.MustGet(currRound)
|
||||
for i := range fsHooks {
|
||||
if fsHooks[i].IsSkipped() {
|
||||
continue
|
||||
}
|
||||
|
||||
fsHooks[i].RunGnark(api, c)
|
||||
}
|
||||
}
|
||||
|
||||
c.FiatShamirHistory[currRound] = [2][]frontend.Variable{
|
||||
initialState,
|
||||
c.FS.State(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,9 +351,9 @@ func (c *WizardVerifierCircuit) GetColumnAt(name ifaces.ColID, pos int) frontend
|
||||
return c.GetColumn(name)[pos]
|
||||
}
|
||||
|
||||
// newWizardVerifierCircuit creates an empty wizard verifier circuit.
|
||||
// NewWizardVerifierCircuit creates an empty wizard verifier circuit.
|
||||
// Initializes the underlying structs and collections.
|
||||
func newWizardVerifierCircuit() *WizardVerifierCircuit {
|
||||
func NewWizardVerifierCircuit() *WizardVerifierCircuit {
|
||||
res := &WizardVerifierCircuit{}
|
||||
res.columnsIDs = collection.NewMapping[ifaces.ColID, int]()
|
||||
res.univariateParamsIDs = collection.NewMapping[ifaces.QueryID, int]()
|
||||
@@ -367,7 +372,7 @@ func newWizardVerifierCircuit() *WizardVerifierCircuit {
|
||||
// gnark assignment circuit involving the verification of Wizard proof.
|
||||
func GetWizardVerifierCircuitAssignment(comp *CompiledIOP, proof Proof) *WizardVerifierCircuit {
|
||||
|
||||
res := newWizardVerifierCircuit()
|
||||
res := NewWizardVerifierCircuit()
|
||||
|
||||
/*
|
||||
Assigns the messages. Note that the iteration order is made
|
||||
@@ -411,17 +416,11 @@ func GetWizardVerifierCircuitAssignment(comp *CompiledIOP, proof Proof) *WizardV
|
||||
switch params := paramsIface.(type) {
|
||||
|
||||
case query.UnivariateEvalParams:
|
||||
res.univariateParamsIDs.InsertNew(qName, len(res.UnivariateParams))
|
||||
res.UnivariateParams = append(res.UnivariateParams, params.GnarkAssign())
|
||||
|
||||
res.AssignUnivariateEval(qName, params)
|
||||
case query.InnerProductParams:
|
||||
res.innerProductIDs.InsertNew(qName, len(res.InnerProductParams))
|
||||
res.InnerProductParams = append(res.InnerProductParams, params.GnarkAssign())
|
||||
|
||||
res.AssignInnerProduct(qName, params)
|
||||
case query.LocalOpeningParams:
|
||||
res.localOpeningIDs.InsertNew(qName, len(res.LocalOpeningParams))
|
||||
res.LocalOpeningParams = append(res.LocalOpeningParams, params.GnarkAssign())
|
||||
|
||||
res.AssignLocalOpening(qName, params)
|
||||
default:
|
||||
utils.Panic("unknow type %T", params)
|
||||
}
|
||||
@@ -445,3 +444,79 @@ func (c *WizardVerifierCircuit) GetParams(id ifaces.QueryID) ifaces.GnarkQueryPa
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// AllocColumn inserts a column in the Wizard verifier circuit and is meant
|
||||
// to be called at allocation time.
|
||||
func (c *WizardVerifierCircuit) AllocColumn(id ifaces.ColID, size int) []frontend.Variable {
|
||||
column := make([]frontend.Variable, size)
|
||||
c.columnsIDs.InsertNew(id, len(c.Columns))
|
||||
c.Columns = append(c.Columns, column)
|
||||
return column
|
||||
}
|
||||
|
||||
// AssignColumn assigns a column in the Wizard verifier circuit
|
||||
func (c *WizardVerifierCircuit) AssignColumn(id ifaces.ColID, sv smartvectors.SmartVector) {
|
||||
column := smartvectors.IntoGnarkAssignment(sv)
|
||||
c.columnsIDs.InsertNew(id, len(c.Columns))
|
||||
c.Columns = append(c.Columns, column)
|
||||
}
|
||||
|
||||
// AllocUnivariableEval inserts a slot for a univariate query opening in the
|
||||
// witness of the verifier circuit.
|
||||
func (c *WizardVerifierCircuit) AllocUnivariateEval(qName ifaces.QueryID, qInfo query.UnivariateEval) {
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
c.univariateParamsIDs.InsertNew(qName, len(c.UnivariateParams))
|
||||
c.UnivariateParams = append(c.UnivariateParams, qInfo.GnarkAllocate())
|
||||
}
|
||||
|
||||
// AllocInnerProduct inserts a slot for an inner-product query opening in the
|
||||
// witness of the verifier circuit.
|
||||
func (c *WizardVerifierCircuit) AllocInnerProduct(qName ifaces.QueryID, qInfo query.InnerProduct) {
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
c.innerProductIDs.InsertNew(qName, len(c.InnerProductParams))
|
||||
c.InnerProductParams = append(c.InnerProductParams, qInfo.GnarkAllocate())
|
||||
}
|
||||
|
||||
// AllocLocalOpening inserts a slot for a local position opening in the witness
|
||||
// of the verifier circuit.
|
||||
func (c *WizardVerifierCircuit) AllocLocalOpening(qName ifaces.QueryID, qInfo query.LocalOpening) {
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
c.localOpeningIDs.InsertNew(qName, len(c.LocalOpeningParams))
|
||||
c.LocalOpeningParams = append(c.LocalOpeningParams, query.GnarkLocalOpeningParams{})
|
||||
}
|
||||
|
||||
// AssignUnivariableEval inserts a slot for a univariate query opening in the
|
||||
// witness of the verifier circuit.
|
||||
func (c *WizardVerifierCircuit) AssignUnivariateEval(qName ifaces.QueryID, params query.UnivariateEvalParams) {
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
c.univariateParamsIDs.InsertNew(qName, len(c.UnivariateParams))
|
||||
c.UnivariateParams = append(c.UnivariateParams, params.GnarkAssign())
|
||||
}
|
||||
|
||||
// AssignInnerProduct inserts a slot for an inner-product query opening in the
|
||||
// witness of the verifier circuit.
|
||||
func (c *WizardVerifierCircuit) AssignInnerProduct(qName ifaces.QueryID, params query.InnerProductParams) {
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
c.innerProductIDs.InsertNew(qName, len(c.InnerProductParams))
|
||||
c.InnerProductParams = append(c.InnerProductParams, params.GnarkAssign())
|
||||
}
|
||||
|
||||
// AssignLocalOpening inserts a slot for a local position opening in the witness
|
||||
// of the verifier circuit.
|
||||
func (c *WizardVerifierCircuit) AssignLocalOpening(qName ifaces.QueryID, params query.LocalOpeningParams) {
|
||||
// Note that nil is the default value for frontend.Variable
|
||||
c.localOpeningIDs.InsertNew(qName, len(c.LocalOpeningParams))
|
||||
c.LocalOpeningParams = append(c.LocalOpeningParams, params.GnarkAssign())
|
||||
}
|
||||
|
||||
// GetPublicInput returns a public input value from its name
|
||||
func (c *WizardVerifierCircuit) GetPublicInput(api frontend.API, name string) frontend.Variable {
|
||||
allPubs := c.Spec.PublicInputs
|
||||
for i := range allPubs {
|
||||
if allPubs[i].Name == name {
|
||||
return allPubs[i].Acc.GetFrontendVariable(api, c)
|
||||
}
|
||||
}
|
||||
utils.Panic("could not find public input nb %v", name)
|
||||
return field.Element{}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/query"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
"github.com/consensys/linea-monorepo/prover/utils/collection"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ProverStep represents an operation to be performed by the prover of a
|
||||
@@ -120,6 +119,11 @@ type ProverRuntime struct {
|
||||
|
||||
// lock is global lock so that the assignment maps are thread safes
|
||||
lock *sync.Mutex
|
||||
|
||||
// FiatShamirHistory tracks the fiat-shamir state at the beginning of every
|
||||
// round. The first entry is the initial state, the final entry is the final
|
||||
// state.
|
||||
FiatShamirHistory [][2][]field.Element
|
||||
}
|
||||
|
||||
// Prove is the top-level function that runs the Prover on the user's side. It
|
||||
@@ -204,14 +208,20 @@ func (c *CompiledIOP) createProver() ProverRuntime {
|
||||
|
||||
// Instantiates an empty Assignment (but link it to the CompiledIOP)
|
||||
runtime := ProverRuntime{
|
||||
Spec: c,
|
||||
Columns: collection.NewMapping[ifaces.ColID, ifaces.ColAssignment](),
|
||||
QueriesParams: collection.NewMapping[ifaces.QueryID, ifaces.QueryParams](),
|
||||
Coins: collection.NewMapping[coin.Name, interface{}](),
|
||||
State: collection.NewMapping[string, interface{}](),
|
||||
FS: fs,
|
||||
currRound: 0,
|
||||
lock: &sync.Mutex{},
|
||||
Spec: c,
|
||||
Columns: collection.NewMapping[ifaces.ColID, ifaces.ColAssignment](),
|
||||
QueriesParams: collection.NewMapping[ifaces.QueryID, ifaces.QueryParams](),
|
||||
Coins: collection.NewMapping[coin.Name, interface{}](),
|
||||
State: collection.NewMapping[string, interface{}](),
|
||||
FS: fs,
|
||||
currRound: 0,
|
||||
lock: &sync.Mutex{},
|
||||
FiatShamirHistory: make([][2][]field.Element, c.NumRounds()),
|
||||
}
|
||||
|
||||
runtime.FiatShamirHistory[0] = [2][]field.Element{
|
||||
fs.State(),
|
||||
fs.State(),
|
||||
}
|
||||
|
||||
// Pass the precomputed polynomials
|
||||
@@ -452,6 +462,8 @@ func (run *ProverRuntime) getRandomCoinGeneric(name coin.Name, requestedType coi
|
||||
// parameters. This makes all the new coins available in the prover runtime.
|
||||
func (run *ProverRuntime) goNextRound() {
|
||||
|
||||
initialState := run.FS.State()
|
||||
|
||||
/*
|
||||
Make sure all issued random coin have been "consumed" by all the prover
|
||||
steps, in the round we are closing. An error occuring here is more likely
|
||||
@@ -473,11 +485,6 @@ func (run *ProverRuntime) goNextRound() {
|
||||
toBeParametrized := run.Spec.QueriesParams.AllKeysAt(run.currRound)
|
||||
run.QueriesParams.MustExists(toBeParametrized...)
|
||||
|
||||
// Counts the transcript size of the round and the number of field
|
||||
// element generated.
|
||||
initialTranscriptSize := run.FS.TranscriptSize
|
||||
initialNumCoinsGenerated := run.FS.NumCoinGenerated
|
||||
|
||||
if !run.Spec.DummyCompiled {
|
||||
|
||||
/*
|
||||
@@ -486,13 +493,11 @@ func (run *ProverRuntime) goNextRound() {
|
||||
FS using the last round of the prover because he is always
|
||||
the last one to "talk" in the protocol.
|
||||
*/
|
||||
start := run.FS.TranscriptSize
|
||||
msgsToFS := run.Spec.Columns.AllKeysProofAt(run.currRound)
|
||||
msgsToFS := run.Spec.Columns.AllKeysProofsOrIgnoredButKeptInProverTranscript(run.currRound)
|
||||
for _, msgName := range msgsToFS {
|
||||
instance := run.GetMessage(msgName)
|
||||
run.FS.UpdateSV(instance)
|
||||
}
|
||||
logrus.Debugf("Fiat-shamir round %v - %v proof elements in the transcript", run.currRound, run.FS.TranscriptSize-start)
|
||||
|
||||
/*
|
||||
Make sure that all messages have been written and use them
|
||||
@@ -500,26 +505,26 @@ func (run *ProverRuntime) goNextRound() {
|
||||
FS using the last round of the prover because he is always
|
||||
the last one to "talk" in the protocol.
|
||||
*/
|
||||
start = run.FS.TranscriptSize
|
||||
msgsToFS = run.Spec.Columns.AllKeysPublicInputAt(run.currRound)
|
||||
for _, msgName := range msgsToFS {
|
||||
instance := run.GetMessage(msgName)
|
||||
run.FS.UpdateSV(instance)
|
||||
}
|
||||
logrus.Debugf("Fiat-shamir round %v - %v public inputs in the transcript", run.currRound, run.FS.TranscriptSize-start)
|
||||
|
||||
/*
|
||||
Also include the prover's allegations for all evaluations
|
||||
*/
|
||||
start = run.FS.TranscriptSize
|
||||
paramsToFS := run.Spec.QueriesParams.AllKeysAt(run.currRound)
|
||||
for _, qName := range paramsToFS {
|
||||
if run.Spec.QueriesParams.IsSkippedFromProverTranscript(qName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Implicitly, this will panic whenever we start supporting
|
||||
// a new type of query params
|
||||
params := run.QueriesParams.MustGet(qName)
|
||||
params.UpdateFS(run.FS)
|
||||
}
|
||||
logrus.Debugf("Fiat-shamir round %v - %v query params in the transcript", run.currRound, run.FS.TranscriptSize-start)
|
||||
}
|
||||
|
||||
// Increment the number of rounds
|
||||
@@ -537,9 +542,12 @@ func (run *ProverRuntime) goNextRound() {
|
||||
run.Coins.InsertNew(coin, value)
|
||||
}
|
||||
|
||||
logrus.Debugf("Ran Fiat-Shamir for round %v, transcript size %v (field element), generated %v field elements, total-transcript %v, total-generated %v",
|
||||
run.currRound, run.FS.TranscriptSize-initialTranscriptSize, run.FS.NumCoinGenerated-initialNumCoinsGenerated, run.FS.TranscriptSize, run.FS.NumCoinGenerated,
|
||||
)
|
||||
finalState := run.FS.State()
|
||||
|
||||
run.FiatShamirHistory[run.currRound] = [2][]field.Element{
|
||||
initialState,
|
||||
finalState,
|
||||
}
|
||||
}
|
||||
|
||||
// runProverSteps runs all the [ProverStep] specified in the underlying
|
||||
|
||||
14
prover/protocol/wizard/public_input.go
Normal file
14
prover/protocol/wizard/public_input.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package wizard
|
||||
|
||||
import "github.com/consensys/linea-monorepo/prover/protocol/ifaces"
|
||||
|
||||
// PublicInput represents a public input in a wizard protocol. Public inputs
|
||||
// are materialized with a functional identifier and a local opening query.
|
||||
// The identifier is what ultimately identifies the public input as the query
|
||||
// may be mutated by compilation (if we use the FullRecursion compiler), therefore
|
||||
// it would unsafe to use the ID of the query to identify the public input in
|
||||
// the circuit.
|
||||
type PublicInput struct {
|
||||
Name string
|
||||
Acc ifaces.Accessor
|
||||
}
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/utils/collection"
|
||||
)
|
||||
|
||||
/*
|
||||
In a nutshell, an item is an abstract type that
|
||||
accounts for the fact that CompiledProtocol
|
||||
registers various things for different rounds
|
||||
*/
|
||||
// ByRoundRegister is a an abstract data-structure used to register the
|
||||
// [column.Natural], [coin.Info] and [ifaces.Query] etc... Each item is added
|
||||
// at a particular round. The structure additionally records compilation
|
||||
// informations about the objects stored in the register.
|
||||
type ByRoundRegister[ID comparable, DATA any] struct {
|
||||
// All the data for each key
|
||||
mapping collection.Mapping[ID, DATA]
|
||||
@@ -19,6 +18,17 @@ type ByRoundRegister[ID comparable, DATA any] struct {
|
||||
byRoundsIndex collection.Mapping[ID, int]
|
||||
// Marks an entry as ignorable (but does not delete it)
|
||||
ignored collection.Set[ID]
|
||||
// skippedFromVerifierTranscript marks an entry as "skipped from verifier
|
||||
// transcript from the FS transcript for the verifier. This means that the
|
||||
// verifier will not use this value. However, the value can still be used
|
||||
// by the prover. The reason for this field is to work around subtle issues
|
||||
// while dealing with recursion.
|
||||
skippedFromVerifierTranscript collection.Set[ID]
|
||||
// skippedFromProverTranscript marks an entry as "skipped from prover
|
||||
// transcript" this means that neither the prover nor the verifier will use
|
||||
// this value to update the transcript. The reason for this field is to work
|
||||
// around subtle issues while dealing with recursion.
|
||||
skippedFromProverTranscript collection.Set[ID]
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -26,10 +36,12 @@ Construct a new round register
|
||||
*/
|
||||
func NewRegister[ID comparable, DATA any]() ByRoundRegister[ID, DATA] {
|
||||
return ByRoundRegister[ID, DATA]{
|
||||
mapping: collection.NewMapping[ID, DATA](),
|
||||
byRounds: collection.NewVecVec[ID](),
|
||||
byRoundsIndex: collection.NewMapping[ID, int](),
|
||||
ignored: collection.NewSet[ID](),
|
||||
mapping: collection.NewMapping[ID, DATA](),
|
||||
byRounds: collection.NewVecVec[ID](),
|
||||
byRoundsIndex: collection.NewMapping[ID, int](),
|
||||
ignored: collection.NewSet[ID](),
|
||||
skippedFromVerifierTranscript: collection.NewSet[ID](),
|
||||
skippedFromProverTranscript: collection.NewSet[ID](),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,3 +178,34 @@ func (r *ByRoundRegister[ID, DATA]) IsIgnored(id ID) bool {
|
||||
r.mapping.MustExists(id)
|
||||
return r.ignored.Exists(id)
|
||||
}
|
||||
|
||||
// MarkAsSkippedFromVerifierTranscript marks an entry as skipped from the transcript
|
||||
// of the verifier. Panic if the key is missing from the register. Returns true if
|
||||
// the item was already ignored.
|
||||
func (r *ByRoundRegister[ID, DATA]) MarkAsSkippedFromVerifierTranscript(id ID) bool {
|
||||
r.mapping.MustExists(id)
|
||||
return r.skippedFromVerifierTranscript.Insert(id)
|
||||
}
|
||||
|
||||
// IsSkippedFromVerifierTranscript returns if the entry is skipped from the
|
||||
// transcript. Panics if the entry is missing from the map.
|
||||
func (r *ByRoundRegister[ID, DATA]) IsSkippedFromVerifierTranscript(id ID) bool {
|
||||
r.mapping.MustExists(id)
|
||||
return r.skippedFromVerifierTranscript.Exists(id)
|
||||
}
|
||||
|
||||
// MarkAsSkippedFromProverTranscript marks an entry as skipped from the transcript
|
||||
// of the verifier. Panic if the key is missing from the register. Returns true
|
||||
// if the item was already ignored.
|
||||
func (r *ByRoundRegister[ID, DATA]) MarkAsSkippedFromProverTranscript(id ID) bool {
|
||||
r.mapping.MustExists(id)
|
||||
r.skippedFromVerifierTranscript.Insert(id)
|
||||
return r.skippedFromProverTranscript.Insert(id)
|
||||
}
|
||||
|
||||
// IsSkippedFromProverTranscript returns if the entry is skipped from the
|
||||
// transcript. Panics if the entry is missing from the map.
|
||||
func (r *ByRoundRegister[ID, DATA]) IsSkippedFromProverTranscript(id ID) bool {
|
||||
r.mapping.MustExists(id)
|
||||
return r.skippedFromProverTranscript.Exists(id)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/protocol/query"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
"github.com/consensys/linea-monorepo/prover/utils/collection"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Proof generically represents a proof obtained from the wizard. This object does not
|
||||
@@ -69,6 +68,11 @@ type VerifierRuntime struct {
|
||||
// the verifer end up having different state or the same message being
|
||||
// included a second time. Use it externally at your own risks.
|
||||
FS *fiatshamir.State
|
||||
|
||||
// FiatShamirHistory tracks the fiat-shamir state at the beginning of every
|
||||
// round. The first entry is the initial state, the final entry is the final
|
||||
// state.
|
||||
FiatShamirHistory [][2][]field.Element
|
||||
}
|
||||
|
||||
// Verify verifies a wizard proof. The caller specifies a [CompiledIOP] that
|
||||
@@ -91,10 +95,12 @@ func Verify(c *CompiledIOP, proof Proof) error {
|
||||
any
|
||||
*/
|
||||
errs := []error{}
|
||||
for _, roundSteps := range runtime.Spec.subVerifiers.Inner() {
|
||||
for _, roundSteps := range runtime.Spec.SubVerifiers.Inner() {
|
||||
for _, step := range roundSteps {
|
||||
if err := step(&runtime); err != nil {
|
||||
errs = append(errs, err)
|
||||
if !step.IsSkipped() {
|
||||
if err := step.Run(&runtime); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,11 +123,12 @@ func (c *CompiledIOP) createVerifier(proof Proof) VerifierRuntime {
|
||||
Instantiate an empty assigment for the verifier
|
||||
*/
|
||||
runtime := VerifierRuntime{
|
||||
Spec: c,
|
||||
Coins: collection.NewMapping[coin.Name, interface{}](),
|
||||
Columns: proof.Messages,
|
||||
QueriesParams: proof.QueriesParams,
|
||||
FS: fiatshamir.NewMiMCFiatShamir(),
|
||||
Spec: c,
|
||||
Coins: collection.NewMapping[coin.Name, interface{}](),
|
||||
Columns: proof.Messages,
|
||||
QueriesParams: proof.QueriesParams,
|
||||
FS: fiatshamir.NewMiMCFiatShamir(),
|
||||
FiatShamirHistory: make([][2][]field.Element, c.NumRounds()),
|
||||
}
|
||||
|
||||
runtime.FS.Update(c.fiatShamirSetup)
|
||||
@@ -146,14 +153,10 @@ func (c *CompiledIOP) createVerifier(proof Proof) VerifierRuntime {
|
||||
func (run *VerifierRuntime) generateAllRandomCoins() {
|
||||
|
||||
for currRound := 0; currRound < run.Spec.NumRounds(); currRound++ {
|
||||
|
||||
initialState := run.FS.State()
|
||||
|
||||
if currRound > 0 {
|
||||
/*
|
||||
Sanity-check : Make sure all issued random coin have been
|
||||
"consumed" by all the verifiers steps, in the round we are
|
||||
"closing"
|
||||
*/
|
||||
toBeConsumed := run.Spec.Coins.AllKeysAt(currRound - 1)
|
||||
run.Coins.MustExists(toBeConsumed...)
|
||||
|
||||
if !run.Spec.DummyCompiled {
|
||||
|
||||
@@ -166,14 +169,12 @@ func (run *VerifierRuntime) generateAllRandomCoins() {
|
||||
msgsToFS := run.Spec.Columns.AllKeysProofAt(currRound - 1)
|
||||
for _, msgName := range msgsToFS {
|
||||
instance := run.GetColumn(msgName)
|
||||
logrus.Tracef("VERIFIER : Update fiat-shamir with proof message %v", msgName)
|
||||
run.FS.UpdateSV(instance)
|
||||
}
|
||||
|
||||
msgsToFS = run.Spec.Columns.AllKeysPublicInputAt(currRound - 1)
|
||||
for _, msgName := range msgsToFS {
|
||||
instance := run.GetColumn(msgName)
|
||||
logrus.Tracef("VERIFIER : Update fiat-shamir with public input %v", msgName)
|
||||
run.FS.UpdateSV(instance)
|
||||
}
|
||||
|
||||
@@ -182,9 +183,10 @@ func (run *VerifierRuntime) generateAllRandomCoins() {
|
||||
*/
|
||||
queries := run.Spec.QueriesParams.AllKeysAt(currRound - 1)
|
||||
for _, qName := range queries {
|
||||
// Implicitly, this will panic whenever we start supporting
|
||||
// a new type of query params
|
||||
logrus.Tracef("VERIFIER : Update fiat-shamir with query parameters %v", qName)
|
||||
if run.Spec.QueriesParams.IsSkippedFromVerifierTranscript(qName) {
|
||||
continue
|
||||
}
|
||||
|
||||
params := run.QueriesParams.MustGet(qName)
|
||||
params.UpdateFS(run.FS)
|
||||
}
|
||||
@@ -197,11 +199,30 @@ func (run *VerifierRuntime) generateAllRandomCoins() {
|
||||
*/
|
||||
toCompute := run.Spec.Coins.AllKeysAt(currRound)
|
||||
for _, coin := range toCompute {
|
||||
logrus.Tracef("VERIFIER : Generate coin %v", coin)
|
||||
if run.Spec.Coins.IsSkippedFromVerifierTranscript(coin) {
|
||||
continue
|
||||
}
|
||||
|
||||
info := run.Spec.Coins.Data(coin)
|
||||
value := info.Sample(run.FS)
|
||||
run.Coins.InsertNew(coin, value)
|
||||
}
|
||||
|
||||
if run.Spec.FiatShamirHooks.Len() > currRound {
|
||||
fsHooks := run.Spec.FiatShamirHooks.MustGet(currRound)
|
||||
for i := range fsHooks {
|
||||
if fsHooks[i].IsSkipped() {
|
||||
continue
|
||||
}
|
||||
|
||||
fsHooks[i].Run(run)
|
||||
}
|
||||
}
|
||||
|
||||
run.FiatShamirHistory[currRound] = [2][]field.Element{
|
||||
initialState,
|
||||
run.FS.State(),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -358,3 +379,15 @@ func (run VerifierRuntime) GetColumnAt(name ifaces.ColID, pos int) field.Element
|
||||
func (run *VerifierRuntime) GetParams(name ifaces.QueryID) ifaces.QueryParams {
|
||||
return run.QueriesParams.MustGet(name)
|
||||
}
|
||||
|
||||
// GetPublicInput returns a public input from its name
|
||||
func (run *VerifierRuntime) GetPublicInput(name string) field.Element {
|
||||
allPubs := run.Spec.PublicInputs
|
||||
for i := range allPubs {
|
||||
if allPubs[i].Name == name {
|
||||
return allPubs[i].Acc.GetVal(run)
|
||||
}
|
||||
}
|
||||
utils.Panic("could not find public input nb %v", name)
|
||||
return field.Element{}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ecpair
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/consensys/gnark/frontend"
|
||||
"github.com/consensys/gnark/std/algebra/emulated/fields_bn254"
|
||||
@@ -9,10 +10,16 @@ import (
|
||||
"github.com/consensys/gnark/std/evmprecompiles"
|
||||
"github.com/consensys/gnark/std/math/bitslice"
|
||||
"github.com/consensys/gnark/std/math/emulated"
|
||||
"github.com/consensys/gnark/std/math/emulated/emparams"
|
||||
)
|
||||
|
||||
var fpParams sw_bn254.BaseField
|
||||
|
||||
type (
|
||||
fpField = emulated.Field[emparams.BN254Fp]
|
||||
fpElement = emulated.Element[emparams.BN254Fp]
|
||||
)
|
||||
|
||||
// G1ElementWizard represents G1 element as Wizard limbs (2 limbs of 128 bits)
|
||||
type G1ElementWizard struct {
|
||||
P [nbG1Limbs]frontend.Variable
|
||||
@@ -126,50 +133,22 @@ func (c *GtElementWizard) ToGtElement(api frontend.API, fp *emulated.Field[sw_bn
|
||||
C1B2YLimbs[2], C1B2YLimbs[3] = bitslice.Partition(api, c.T[22], 64, bitslice.WithNbDigits(128))
|
||||
C1B2YLimbs[0], C1B2YLimbs[1] = bitslice.Partition(api, c.T[23], 64, bitslice.WithNbDigits(128))
|
||||
|
||||
C0B0X := fp.NewElement(C0B0XLimbs)
|
||||
C0B0Y := fp.NewElement(C0B0YLimbs)
|
||||
C0B1X := fp.NewElement(C0B1XLimbs)
|
||||
C0B1Y := fp.NewElement(C0B1YLimbs)
|
||||
C0B2X := fp.NewElement(C0B2XLimbs)
|
||||
C0B2Y := fp.NewElement(C0B2YLimbs)
|
||||
C1B0X := fp.NewElement(C1B0XLimbs)
|
||||
C1B0Y := fp.NewElement(C1B0YLimbs)
|
||||
C1B1X := fp.NewElement(C1B1XLimbs)
|
||||
C1B1Y := fp.NewElement(C1B1YLimbs)
|
||||
C1B2X := fp.NewElement(C1B2XLimbs)
|
||||
C1B2Y := fp.NewElement(C1B2YLimbs)
|
||||
|
||||
T := sw_bn254.GTEl{
|
||||
C0: fields_bn254.E6{
|
||||
B0: fields_bn254.E2{
|
||||
A0: *C0B0X,
|
||||
A1: *C0B0Y,
|
||||
},
|
||||
B1: fields_bn254.E2{
|
||||
A0: *C0B1X,
|
||||
A1: *C0B1Y,
|
||||
},
|
||||
B2: fields_bn254.E2{
|
||||
A0: *C0B2X,
|
||||
A1: *C0B2Y,
|
||||
},
|
||||
},
|
||||
C1: fields_bn254.E6{
|
||||
B0: fields_bn254.E2{
|
||||
A0: *C1B0X,
|
||||
A1: *C1B0Y,
|
||||
},
|
||||
B1: fields_bn254.E2{
|
||||
A0: *C1B1X,
|
||||
A1: *C1B1Y,
|
||||
},
|
||||
B2: fields_bn254.E2{
|
||||
A0: *C1B2X,
|
||||
A1: *C1B2Y,
|
||||
},
|
||||
},
|
||||
e12Tower := [12]*fpElement{
|
||||
fp.NewElement(C0B0XLimbs),
|
||||
fp.NewElement(C0B0YLimbs),
|
||||
fp.NewElement(C0B1XLimbs),
|
||||
fp.NewElement(C0B1YLimbs),
|
||||
fp.NewElement(C0B2XLimbs),
|
||||
fp.NewElement(C0B2YLimbs),
|
||||
fp.NewElement(C1B0XLimbs),
|
||||
fp.NewElement(C1B0YLimbs),
|
||||
fp.NewElement(C1B1XLimbs),
|
||||
fp.NewElement(C1B1YLimbs),
|
||||
fp.NewElement(C1B2XLimbs),
|
||||
fp.NewElement(C1B2YLimbs),
|
||||
}
|
||||
return T
|
||||
|
||||
return intoGtNoTower(fp, e12Tower)
|
||||
}
|
||||
|
||||
// MultiG2GroupcheckCircuit is a circuit that checks multiple G2 group
|
||||
@@ -309,3 +288,53 @@ func (c *MillerLoopFinalExpInstance) Check(api frontend.API, fp *emulated.Field[
|
||||
|
||||
return evmprecompiles.ECPairMillerLoopAndFinalExpCheck(api, &prev, &P, &Q, c.Expected[1])
|
||||
}
|
||||
|
||||
// intoGtNoTower converts an E12 element as in the outputs of the pairing
|
||||
// precompile on Ethereum into a non-tower representation of the same E12
|
||||
// element.
|
||||
func intoGtNoTower(api *fpField, coordinates [12]*fpElement) sw_bn254.GTEl {
|
||||
|
||||
var (
|
||||
C0B0X = coordinates[0]
|
||||
C0B0Y = coordinates[1]
|
||||
C0B1X = coordinates[2]
|
||||
C0B1Y = coordinates[3]
|
||||
C0B2X = coordinates[4]
|
||||
C0B2Y = coordinates[5]
|
||||
C1B0X = coordinates[6]
|
||||
C1B0Y = coordinates[7]
|
||||
C1B1X = coordinates[8]
|
||||
C1B1Y = coordinates[9]
|
||||
C1B2X = coordinates[10]
|
||||
C1B2Y = coordinates[11]
|
||||
)
|
||||
|
||||
var t *fpElement
|
||||
t = api.MulConst(C0B0Y, big.NewInt(9))
|
||||
c0 := api.Sub(C0B0X, t)
|
||||
t = api.MulConst(C1B0Y, big.NewInt(9))
|
||||
c1 := api.Sub(C1B0X, t)
|
||||
t = api.MulConst(C0B1Y, big.NewInt(9))
|
||||
c2 := api.Sub(C0B1X, t)
|
||||
t = api.MulConst(C1B1Y, big.NewInt(9))
|
||||
c3 := api.Sub(C1B1X, t)
|
||||
t = api.MulConst(C0B2Y, big.NewInt(9))
|
||||
c4 := api.Sub(C0B2X, t)
|
||||
t = api.MulConst(C1B2Y, big.NewInt(9))
|
||||
c5 := api.Sub(C1B2X, t)
|
||||
|
||||
return sw_bn254.GTEl{
|
||||
A0: *c0,
|
||||
A1: *c1,
|
||||
A2: *c2,
|
||||
A3: *c3,
|
||||
A4: *c4,
|
||||
A5: *c5,
|
||||
A6: *C0B0Y,
|
||||
A7: *C1B0Y,
|
||||
A8: *C0B1Y,
|
||||
A9: *C1B1Y,
|
||||
A10: *C0B2Y,
|
||||
A11: *C1B2Y,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,28 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
DataNbBytes = "DataNbBytes"
|
||||
DataChecksum = "DataChecksum"
|
||||
L2MessageHash = "L2MessageHash"
|
||||
InitialStateRootHash = "InitialStateRootHash"
|
||||
FinalStateRootHash = "FinalStateRootHash"
|
||||
InitialBlockNumber = "InitialBlockNumber"
|
||||
FinalBlockNumber = "FinalBlockNumber"
|
||||
InitialBlockTimestamp = "InitialBlockTimestamp"
|
||||
FinalBlockTimestamp = "FinalBlockTimestamp"
|
||||
FirstRollingHashUpdate_0 = "FirstRollingHashUpdate_0"
|
||||
FirstRollingHashUpdate_1 = "FirstRollingHashUpdate_1"
|
||||
LastRollingHashUpdate_0 = "LastRollingHashUpdate_0"
|
||||
LastRollingHashUpdate_1 = "LastRollingHashUpdate_1"
|
||||
FirstRollingHashUpdateNumber = "FirstRollingHashUpdateNumber"
|
||||
LastRollingHashNumberUpdate = "LastRollingHashNumberUpdate"
|
||||
ChainID = "ChainID"
|
||||
NBytesChainID = "NBytesChainID"
|
||||
L2MessageServiceAddrHi = "L2MessageServiceAddrHi"
|
||||
L2MessageServiceAddrLo = "L2MessageServiceAddrLo"
|
||||
)
|
||||
|
||||
// PublicInput collects a number of submodules responsible for collecting the
|
||||
// wizard witness data holding the public inputs of the execution circuit.
|
||||
type PublicInput struct {
|
||||
@@ -302,4 +324,26 @@ func (pi *PublicInput) generateExtractor(comp *wizard.CompiledIOP) {
|
||||
L2MessageServiceAddrHi: accessors.NewFromPublicColumn(pi.Aux.logSelectors.L2BridgeAddressColHI, 0),
|
||||
L2MessageServiceAddrLo: accessors.NewFromPublicColumn(pi.Aux.logSelectors.L2BridgeAddressColLo, 0),
|
||||
}
|
||||
|
||||
comp.PublicInputs = append(comp.PublicInputs,
|
||||
wizard.PublicInput{Name: "DataNbBytes", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.DataNbBytes, 0)},
|
||||
wizard.PublicInput{Name: "DataChecksum", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.DataChecksum, 0)},
|
||||
wizard.PublicInput{Name: "L2MessageHash", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.L2MessageHash, 0)},
|
||||
wizard.PublicInput{Name: "InitialStateRootHash", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.InitialStateRootHash, 0)},
|
||||
wizard.PublicInput{Name: "FinalStateRootHash", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.FinalStateRootHash, 0)},
|
||||
wizard.PublicInput{Name: "InitialBlockNumber", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.InitialBlockNumber, 0)},
|
||||
wizard.PublicInput{Name: "FinalBlockNumber", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.FinalBlockNumber, 0)},
|
||||
wizard.PublicInput{Name: "InitialBlockTimestamp", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.InitialBlockTimestamp, 0)},
|
||||
wizard.PublicInput{Name: "FinalBlockTimestamp", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.FinalBlockTimestamp, 0)},
|
||||
wizard.PublicInput{Name: "FirstRollingHashUpdate[0]", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.FirstRollingHashUpdate[0], 0)},
|
||||
wizard.PublicInput{Name: "FirstRollingHashUpdate[1]", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.FirstRollingHashUpdate[1], 0)},
|
||||
wizard.PublicInput{Name: "LastRollingHashUpdate[0]", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.LastRollingHashUpdate[0], 0)},
|
||||
wizard.PublicInput{Name: "LastRollingHashUpdate[1]", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.LastRollingHashUpdate[1], 0)},
|
||||
wizard.PublicInput{Name: "FirstRollingHashUpdateNumber", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.FirstRollingHashUpdateNumber, 0)},
|
||||
wizard.PublicInput{Name: "LastRollingHashNumberUpdate", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.LastRollingHashUpdateNumber, 0)},
|
||||
wizard.PublicInput{Name: "ChainID", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.ChainID, 0)},
|
||||
wizard.PublicInput{Name: "NBytesChainID", Acc: accessors.NewLocalOpeningAccessor(pi.Extractor.NBytesChainID, 0)},
|
||||
wizard.PublicInput{Name: "L2MessageServiceAddrHi", Acc: pi.Extractor.L2MessageServiceAddrHi},
|
||||
wizard.PublicInput{Name: "L2MessageServiceAddrLo", Acc: pi.Extractor.L2MessageServiceAddrLo},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user