From 3bf08c9f185b16e8a709dfb156ab22c23d9fd8d7 Mon Sep 17 00:00:00 2001 From: AlexandreBelling Date: Tue, 13 May 2025 22:46:05 +0200 Subject: [PATCH] Prover: richer exit code to better detect completeness issues (#978) * feat(exit): make sure that keccak uses 77 when overflowing and not a panic message * feat(exit): makes PadAssign return a 77 when vector builder overflows * feat(exit): adds a toggle mechanism for the exiting strategy * feat(unsatisfied): adds exiting when encountering an unsatisfied constraint * adds the toggle for the exit when finding unsatisfied constraints * feat(77): uses the 77 exit code to tell when we have too many merkle proofs * fix log for modexp --- prover/backend/execution/prove.go | 8 +++- prover/protocol/compiler/lookup/prover.go | 12 ++++- .../univariates/local_opening_point.go | 2 +- .../univariates/multi_to_single_point.go | 7 ++- prover/protocol/dedicated/plonk/alignment.go | 4 +- prover/utils/exit/exit.go | 46 +++++++++++++++++++ .../zkevm/arithmetization/arithmetization.go | 2 +- prover/zkevm/arithmetization/assignment.go | 4 +- prover/zkevm/arithmetization/files.go | 2 - prover/zkevm/prover/common/vector_builder.go | 9 ++++ .../prover/hash/keccak/keccakf/keccakf.go | 5 +- .../zkevm/prover/modexp/module_assignement.go | 9 ++-- .../prover/statemanager/accumulator/assign.go | 5 +- 13 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 prover/utils/exit/exit.go diff --git a/prover/backend/execution/prove.go b/prover/backend/execution/prove.go index 2f30c403..ed59395b 100644 --- a/prover/backend/execution/prove.go +++ b/prover/backend/execution/prove.go @@ -11,6 +11,7 @@ import ( "github.com/consensys/linea-monorepo/prover/config" public_input "github.com/consensys/linea-monorepo/prover/public-input" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/consensys/linea-monorepo/prover/utils/profiling" "github.com/consensys/linea-monorepo/prover/zkevm" "github.com/sirupsen/logrus" @@ -26,6 +27,10 @@ func Prove(cfg *config.Config, req *Request, large bool) (*Response, error) { // Set MonitorParams before any proving happens profiling.SetMonitorParams(cfg) + // This instructs the [exit] package to actually exit when [OnLimitOverflow] + // or [OnSatisfiedConstraints] are called. + exit.ActivateExitOnIssue() + var resp Response // TODO @gbotrel wrap profiling in the caller; so that we can properly return errors @@ -146,7 +151,8 @@ func mustProveAndPass( logrus.Info("Sanity-checking the inner-proof") if err := fullZkEvm.VerifyInner(proof); err != nil { - utils.Panic("The prover did not pass: %v", err) + logrus.Errorf("The sanity-check of the inner-proof did not pass: %v", err) + exit.OnUnsatisfiedConstraints() } // wait for setup to be loaded diff --git a/prover/protocol/compiler/lookup/prover.go b/prover/protocol/compiler/lookup/prover.go index 063fa5a6..badd785a 100644 --- a/prover/protocol/compiler/lookup/prover.go +++ b/prover/protocol/compiler/lookup/prover.go @@ -11,7 +11,9 @@ import ( "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/protocol/wizardutils" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/consensys/linea-monorepo/prover/utils/parallel" + "github.com/sirupsen/logrus" ) // proverTaskAtRound implements the [wizard.ProverAction] interface. It gathers @@ -257,12 +259,16 @@ func (a mAssignmentTask) run(run *wizard.ProverRuntime) { } if hasFilter && !filter[k].IsOne() { - utils.Panic( + logrus.Errorf( "the filter column `%v` has a non-binary value at position `%v`: (%v)", a.SFilter[i].GetColID(), k, filter[k].String(), ) + + // Even if this is unconstrained, this is still worth interrupting the + // prover because it "should" be a binary column. + exit.OnUnsatisfiedConstraints() } var ( @@ -279,10 +285,12 @@ func (a mAssignmentTask) run(run *wizard.ProverRuntime) { for j := range tableRow { tableRow[j] = a.S[i][j].GetColAssignmentAt(run, k) } - utils.Panic( + logrus.Errorf( "entry %v of the table %v is not included in the table. tableRow=%v", k, nameTable([][]ifaces.Column{a.S[i]}), vector.Prettify(tableRow), ) + + exit.OnUnsatisfiedConstraints() } mFrag, posInFragM := posInM[0], posInM[1] diff --git a/prover/protocol/compiler/univariates/local_opening_point.go b/prover/protocol/compiler/univariates/local_opening_point.go index e3896cdd..171fe8a3 100644 --- a/prover/protocol/compiler/univariates/local_opening_point.go +++ b/prover/protocol/compiler/univariates/local_opening_point.go @@ -112,7 +112,7 @@ func (ctx localOpeningCtx) verifier(assi *wizard.VerifierRuntime) error { } if len(ys) != len(newParams.Ys) { - utils.Panic("the ys do not have the same length") + return fmt.Errorf("the ys do not have the same length") } errMsg := "fixed point compiler verifier failed\n" diff --git a/prover/protocol/compiler/univariates/multi_to_single_point.go b/prover/protocol/compiler/univariates/multi_to_single_point.go index 27020fbe..de33e04d 100644 --- a/prover/protocol/compiler/univariates/multi_to_single_point.go +++ b/prover/protocol/compiler/univariates/multi_to_single_point.go @@ -2,12 +2,14 @@ package univariates import ( "fmt" - ppool "github.com/consensys/linea-monorepo/prover/utils/parallel/pool" "math/big" "reflect" "runtime" "sync" + "github.com/consensys/linea-monorepo/prover/utils/exit" + ppool "github.com/consensys/linea-monorepo/prover/utils/parallel/pool" + "github.com/consensys/gnark/frontend" "github.com/sirupsen/logrus" @@ -322,7 +324,8 @@ func (ctx mptsCtx) accumulateQuotients(run *wizard.ProverRuntime) { panicMsg += fmt.Sprintf("\t\tfor %v, P(x) = %v\n", q.Pols[i].GetColID(), params.Ys[i].String()) } - utils.Panic("%vremainder was %v (while reducing %v from query %v) \n", panicMsg, rem.String(), polHandle.GetColID(), ctx.hs[hpos]) + logrus.Errorf("%vremainder was %v (while reducing %v from query %v) \n", panicMsg, rem.String(), polHandle.GetColID(), ctx.hs[hpos]) + exit.OnUnsatisfiedConstraints() } } diff --git a/prover/protocol/dedicated/plonk/alignment.go b/prover/protocol/dedicated/plonk/alignment.go index 76d9df1f..391475ca 100644 --- a/prover/protocol/dedicated/plonk/alignment.go +++ b/prover/protocol/dedicated/plonk/alignment.go @@ -2,7 +2,6 @@ package plonk import ( "fmt" - "os" "sync" "github.com/consensys/gnark-crypto/ecc" @@ -17,6 +16,7 @@ import ( "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/symbolic" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/sirupsen/logrus" "golang.org/x/net/context" "golang.org/x/sync/errgroup" @@ -96,7 +96,7 @@ func (ci *CircuitAlignmentInput) prepareWitnesses(run *wizard.ProverRuntime) { // Don't use the fatal level here because we want to control the exit code // to be 77. logrus.Errorf("fatal=%v", err) - os.Exit(77) + exit.OnLimitOverflow() } if ci.InputFiller == nil { diff --git a/prover/utils/exit/exit.go b/prover/utils/exit/exit.go new file mode 100644 index 00000000..8fcdf66e --- /dev/null +++ b/prover/utils/exit/exit.go @@ -0,0 +1,46 @@ +package exit + +import ( + "os" + "runtime/debug" +) + +var ( + activateExitOnIssue bool +) + +// ActivateExitOnIssue tells the program to actually exit when [OnLimitOverflow] +// or [OnSatisfiedConstraints] are called. This has to be manually called at the +// beginning of the program if we want the behavior to take place. This is +// to avoid having it running in the tests. +func ActivateExitOnIssue() { + activateExitOnIssue = true +} + +const ( + limitOverflowExitCode = 77 + unsatisfiedConstraintsExitCode = 78 +) + +// This function will exit the program with the exit code [limitOverflowExitCode] +// but only if the activateExitOnIssue flag is set to true. Otherwise, it will +// just panic. +func OnLimitOverflow() { + + debug.PrintStack() + + if !activateExitOnIssue { + panic("limit overflow") + } + os.Exit(limitOverflowExitCode) +} + +func OnUnsatisfiedConstraints() { + + debug.PrintStack() + + if !activateExitOnIssue { + panic("unsatisfied constraints") + } + os.Exit(unsatisfiedConstraintsExitCode) +} diff --git a/prover/zkevm/arithmetization/arithmetization.go b/prover/zkevm/arithmetization/arithmetization.go index 030dfe8a..4b3572ea 100644 --- a/prover/zkevm/arithmetization/arithmetization.go +++ b/prover/zkevm/arithmetization/arithmetization.go @@ -74,7 +74,7 @@ func (a *Arithmetization) Assign(run *wizard.ProverRuntime, traceFile string) { // Performs a compatibility check by comparing the constraints // commit of zkevm.bin with the constraints commit of the trace file. // Panics if an incompatibility is detected. - if *a.Settings.IgnoreCompatibilityCheck == false { + if !*a.Settings.IgnoreCompatibilityCheck { var errors []string zkevmBinCommit, ok := a.Metadata.String("commit") diff --git a/prover/zkevm/arithmetization/assignment.go b/prover/zkevm/arithmetization/assignment.go index ab5f9fe9..f33969f1 100644 --- a/prover/zkevm/arithmetization/assignment.go +++ b/prover/zkevm/arithmetization/assignment.go @@ -3,7 +3,6 @@ package arithmetization import ( "errors" "fmt" - "os" "github.com/consensys/go-corset/pkg/air" "github.com/consensys/go-corset/pkg/trace" @@ -12,6 +11,7 @@ import ( "github.com/consensys/linea-monorepo/prover/maths/field" "github.com/consensys/linea-monorepo/prover/protocol/ifaces" "github.com/consensys/linea-monorepo/prover/protocol/wizard" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/sirupsen/logrus" ) @@ -48,7 +48,7 @@ func AssignFromLtTraces(run *wizard.ProverRuntime, schema *air.Schema, expTraces if err77 != nil { logrus.Errorf("Error code 77 %v", err77) - os.Exit(TraceOverflowExitCode) + exit.OnLimitOverflow() } for id := uint(0); id < numCols; id++ { diff --git a/prover/zkevm/arithmetization/files.go b/prover/zkevm/arithmetization/files.go index f3696edf..b40f1d1f 100644 --- a/prover/zkevm/arithmetization/files.go +++ b/prover/zkevm/arithmetization/files.go @@ -16,8 +16,6 @@ import ( "github.com/consensys/go-corset/pkg/util/collection/typed" ) -const TraceOverflowExitCode = 77 - // Embed the whole constraint system at compile time, so no // more need to keep it in sync // diff --git a/prover/zkevm/prover/common/vector_builder.go b/prover/zkevm/prover/common/vector_builder.go index 35808ebb..7f2b2324 100644 --- a/prover/zkevm/prover/common/vector_builder.go +++ b/prover/zkevm/prover/common/vector_builder.go @@ -6,7 +6,9 @@ import ( "github.com/consensys/linea-monorepo/prover/protocol/ifaces" "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/consensys/linea-monorepo/prover/utils/types" + "github.com/sirupsen/logrus" ) // VectorBuilder is a convenience structure to assign columns by appending @@ -153,6 +155,13 @@ func (vb *VectorBuilder) PushAddr(addr types.EthAddress) { // PadAndAssign pads and assign the column built by `vb` using `v` as padding // value and assigning into `run`. func (vb *VectorBuilder) PadAndAssign(run *wizard.ProverRuntime, v ...field.Element) { + + if len(vb.slice) > vb.column.Size() { + logrus.Errorf("the slice size %v is larger than the column size %v", len(vb.slice), vb.column.Size()) + // We print the stack to help debugging + exit.OnLimitOverflow() + } + paddingValue := field.Zero() if len(v) > 0 { paddingValue = v[0] diff --git a/prover/zkevm/prover/hash/keccak/keccakf/keccakf.go b/prover/zkevm/prover/hash/keccak/keccakf/keccakf.go index ea3c77e3..54579a46 100644 --- a/prover/zkevm/prover/hash/keccak/keccakf/keccakf.go +++ b/prover/zkevm/prover/hash/keccak/keccakf/keccakf.go @@ -11,7 +11,9 @@ import ( "github.com/consensys/linea-monorepo/prover/protocol/ifaces" "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/consensys/linea-monorepo/prover/utils/parallel" + "github.com/sirupsen/logrus" ) const ( @@ -127,7 +129,8 @@ func (mod *Module) Assign( // If the number of keccakf constraints is larger than what the module // is sized for, then, we cannot prove everything. if numKeccakf > mod.MaxNumKeccakf { - utils.Panic("Too many keccakf %v > %v", numKeccakf, mod.MaxNumKeccakf) + logrus.Errorf("Too many keccakf %v > %v", numKeccakf, mod.MaxNumKeccakf) + exit.OnLimitOverflow() } lu := mod.lookups diff --git a/prover/zkevm/prover/modexp/module_assignement.go b/prover/zkevm/prover/modexp/module_assignement.go index 7c3f1fbb..f1693580 100644 --- a/prover/zkevm/prover/modexp/module_assignement.go +++ b/prover/zkevm/prover/modexp/module_assignement.go @@ -1,11 +1,10 @@ package modexp import ( - "os" - "github.com/consensys/linea-monorepo/prover/maths/field" "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/consensys/linea-monorepo/prover/zkevm/prover/common" "github.com/sirupsen/logrus" ) @@ -88,12 +87,12 @@ func (mod *Module) Assign(run *wizard.ProverRuntime) { if modexpCountSmall > mod.MaxNb256BitsInstances { logrus.Errorf("limit overflow: the modexp (256 bits) count is %v and the limit is %v\n", modexpCountSmall, mod.MaxNb256BitsInstances) - os.Exit(77) + exit.OnLimitOverflow() } if modexpCountLarge > mod.MaxNb4096BitsInstances { - logrus.Errorf("limit overflow: the modexp (4096 bits) count is %v and the limit is %v\n", modexpCountSmall, mod.MaxNb4096BitsInstances) - os.Exit(77) + logrus.Errorf("limit overflow: the modexp (4096 bits) count is %v and the limit is %v\n", modexpCountLarge, mod.MaxNb4096BitsInstances) + exit.OnLimitOverflow() } builder.isActive.PadAndAssign(run, field.Zero()) diff --git a/prover/zkevm/prover/statemanager/accumulator/assign.go b/prover/zkevm/prover/statemanager/accumulator/assign.go index bc8b8a4a..e89a61bb 100644 --- a/prover/zkevm/prover/statemanager/accumulator/assign.go +++ b/prover/zkevm/prover/statemanager/accumulator/assign.go @@ -12,7 +12,9 @@ import ( "github.com/consensys/linea-monorepo/prover/protocol/dedicated/merkle" "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/exit" "github.com/consensys/linea-monorepo/prover/utils/types" + "github.com/sirupsen/logrus" ) // leafOpenings represents the structure for leaf openings @@ -207,7 +209,8 @@ func (am *Module) Assign( // Sanity check on the size if len(builder.leaves) > am.MaxNumProofs { - utils.Panic("We have registered %v proofs which is more than the maximum number of proofs %v", len(builder.leaves), am.MaxNumProofs) + logrus.Errorf("We have registered %v proofs which is more than the maximum number of proofs %v", len(builder.leaves), am.MaxNumProofs) + exit.OnLimitOverflow() } // Assignments of columns