diff --git a/prover/cmd/prover/cmd/prove.go b/prover/cmd/prover/cmd/prove.go index c0a64d96339..edfff6c97fe 100644 --- a/prover/cmd/prover/cmd/prove.go +++ b/prover/cmd/prover/cmd/prove.go @@ -12,68 +12,53 @@ import ( "github.com/consensys/linea-monorepo/prover/backend/execution" "github.com/consensys/linea-monorepo/prover/backend/files" "github.com/consensys/linea-monorepo/prover/config" - "github.com/spf13/cobra" ) -var ( - fInput string - fOutput string - fLarge bool -) - -// proveCmd represents the prove command -var proveCmd = &cobra.Command{ - Use: "prove", - Short: "prove process a request, creates a proof with the adequate circuit and writes the proof to a file", - RunE: cmdProve, -} - -func init() { - rootCmd.AddCommand(proveCmd) - - proveCmd.Flags().StringVar(&fInput, "in", "", "input file") - proveCmd.Flags().StringVar(&fOutput, "out", "", "output file") - proveCmd.Flags().BoolVar(&fLarge, "large", false, "run the large execution circuit") - +type ProverArgs struct { + Input string + Output string + Large bool + ConfigFile string } -func cmdProve(cmd *cobra.Command, args []string) error { +func Prove(args ProverArgs) error { + const cmdName = "prove" // TODO @gbotrel with a specific flag, we could compile the circuit and compare with the checksum of the // asset we deserialize, to make sure we are using the circuit associated with the compiled binary and the setup. // read config - cfg, err := config.NewConfigFromFile(fConfigFile) + cfg, err := config.NewConfigFromFile(args.ConfigFile) if err != nil { - return fmt.Errorf("%s failed to read config file: %w", cmd.Name(), err) + return fmt.Errorf("%s failed to read config file: %w", cmdName, err) } // discover the type of the job from the input file name - jobExecution := strings.Contains(fInput, "getZkProof") - jobBlobDecompression := strings.Contains(fInput, "getZkBlobCompressionProof") - jobAggregation := strings.Contains(fInput, "getZkAggregatedProof") + jobExecution := strings.Contains(args.Input, "getZkProof") + jobBlobDecompression := strings.Contains(args.Input, "getZkBlobCompressionProof") + jobAggregation := strings.Contains(args.Input, "getZkAggregatedProof") if jobExecution { req := &execution.Request{} - if err := readRequest(fInput, req); err != nil { - return fmt.Errorf("could not read the input file (%v): %w", fInput, err) + if err := readRequest(args.Input, req); err != nil { + return fmt.Errorf("could not read the input file (%v): %w", args.Input, err) } // we use the large traces in 2 cases; - // 1. the user explicitly asked for it (fLarge) + // 1. the user explicitly asked for it (args.Large) // 2. the job contains the large suffix and we are a large machine (cfg.Execution.CanRunLarge) - large := fLarge || (strings.Contains(fInput, "large") && cfg.Execution.CanRunFullLarge) + large := args.Large || (strings.Contains(args.Input, "large") && cfg.Execution.CanRunFullLarge) resp, err := execution.Prove(cfg, req, large) if err != nil { return fmt.Errorf("could not prove the execution: %w", err) } - return writeResponse(fOutput, resp) + return writeResponse(args.Output, resp) } if jobBlobDecompression { req := &blobdecompression.Request{} - if err := readRequest(fInput, req); err != nil { - return fmt.Errorf("could not read the input file (%v): %w", fInput, err) + if err := readRequest(args.Input, req); err != nil { + return fmt.Errorf("could not read the input file (%v): %w", args.Input, err) } resp, err := blobdecompression.Prove(cfg, req) @@ -81,13 +66,13 @@ func cmdProve(cmd *cobra.Command, args []string) error { return fmt.Errorf("could not prove the blob decompression: %w", err) } - return writeResponse(fOutput, resp) + return writeResponse(args.Output, resp) } if jobAggregation { req := &aggregation.Request{} - if err := readRequest(fInput, req); err != nil { - return fmt.Errorf("could not read the input file (%v): %w", fInput, err) + if err := readRequest(args.Input, req); err != nil { + return fmt.Errorf("could not read the input file (%v): %w", args.Input, err) } resp, err := aggregation.Prove(cfg, req) @@ -95,7 +80,7 @@ func cmdProve(cmd *cobra.Command, args []string) error { return fmt.Errorf("could not prove the aggregation: %w", err) } - return writeResponse(fOutput, resp) + return writeResponse(args.Output, resp) } return errors.New("unknown job type") diff --git a/prover/cmd/prover/cmd/root.go b/prover/cmd/prover/cmd/root.go deleted file mode 100644 index fcb4c2c9b94..00000000000 --- a/prover/cmd/prover/cmd/root.go +++ /dev/null @@ -1,36 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/consensys/gnark/logger" - "github.com/rs/zerolog" - "github.com/spf13/cobra" -) - -var fConfigFile string - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "prover", - Short: "run pre-compute or compute proofs for Linea circuits", -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - err := rootCmd.Execute() - if err != nil { - os.Exit(1) - } -} - -func init() { - rootCmd.PersistentFlags().StringVar(&fConfigFile, "config", "", "config file") - - output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "15:04:05", NoColor: true} - l := zerolog.New(output).With().Timestamp().Logger() - - // Set global log level for gnark - logger.Set(l) -} diff --git a/prover/cmd/prover/cmd/setup.go b/prover/cmd/prover/cmd/setup.go index 99ddaaa4fc7..3ad86b16f0f 100644 --- a/prover/cmd/prover/cmd/setup.go +++ b/prover/cmd/prover/cmd/setup.go @@ -16,6 +16,7 @@ import ( "github.com/sirupsen/logrus" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/plonk" "github.com/consensys/linea-monorepo/prover/circuits" "github.com/consensys/linea-monorepo/prover/circuits/aggregation" v0 "github.com/consensys/linea-monorepo/prover/circuits/blobdecompression/v0" @@ -26,27 +27,17 @@ import ( "github.com/consensys/linea-monorepo/prover/config" "github.com/consensys/linea-monorepo/prover/utils" "github.com/consensys/linea-monorepo/prover/zkevm" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/consensys/gnark/backend/plonk" -) - -var ( - fForce bool - fCircuits string - fDictPath string - fAssetsDir string ) -// setupCmd represents the setup command -var setupCmd = &cobra.Command{ - Use: "setup", - Short: "pre compute assets for Linea circuits", - RunE: cmdSetup, +type SetupArgs struct { + Force bool + Circuits string + DictPath string + AssetsDir string + ConfigFile string } -var allCircuits = []string{ +var AllCircuits = []string{ string(circuits.ExecutionCircuitID), string(circuits.ExecutionLargeCircuitID), string(circuits.BlobDecompressionV0CircuitID), @@ -57,39 +48,30 @@ var allCircuits = []string{ string(circuits.EmulationDummyCircuitID), // we want to generate Verifier.sol for this one } -func init() { - rootCmd.AddCommand(setupCmd) - setupCmd.Flags().BoolVar(&fForce, "force", false, "overwrites existing files") - setupCmd.Flags().StringVar(&fCircuits, "circuits", strings.Join(allCircuits, ","), "comma separated list of circuits to setup") - setupCmd.Flags().StringVar(&fDictPath, "dict", "", "path to the dictionary file used in blob (de)compression") - setupCmd.Flags().StringVar(&fAssetsDir, "assets-dir", "", "path to the directory where the assets are stored (override conf)") - - viper.BindPFlag("assets_dir", setupCmd.Flags().Lookup("assets-dir")) -} - -func cmdSetup(cmd *cobra.Command, args []string) error { +func Setup(context context.Context, args SetupArgs) error { + const cmdName = "setup" // read config - cfg, err := config.NewConfigFromFile(fConfigFile) + cfg, err := config.NewConfigFromFile(args.ConfigFile) if err != nil { - return fmt.Errorf("%s failed to read config file: %w", cmd.Name(), err) + return fmt.Errorf("%s failed to read config file: %w", cmdName, err) } - if fDictPath != "" { + if args.DictPath != "" { // fail early if the dictionary file is not found but was specified. - if _, err := os.Stat(fDictPath); err != nil { - return fmt.Errorf("%s dictionary file not found: %w", cmd.Name(), err) + if _, err := os.Stat(args.DictPath); err != nil { + return fmt.Errorf("%s dictionary file not found: %w", cmdName, err) } } // parse inCircuits inCircuits := make(map[circuits.CircuitID]bool) - for _, c := range allCircuits { + for _, c := range AllCircuits { inCircuits[circuits.CircuitID(c)] = false } - _inCircuits := strings.Split(fCircuits, ",") + _inCircuits := strings.Split(args.Circuits, ",") for _, c := range _inCircuits { if _, ok := inCircuits[circuits.CircuitID(c)]; !ok { - return fmt.Errorf("%s unknown circuit: %s", cmd.Name(), c) + return fmt.Errorf("%s unknown circuit: %s", cmdName, c) } inCircuits[circuits.CircuitID(c)] = true } @@ -101,7 +83,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { var srsProvider circuits.SRSProvider srsProvider, err = circuits.NewSRSStore(cfg.PathForSRS()) if err != nil { - return fmt.Errorf("%s failed to create SRS provider: %w", cmd.Name(), err) + return fmt.Errorf("%s failed to create SRS provider: %w", cmdName, err) } // for each circuit, we start by compiling the circuit @@ -128,9 +110,9 @@ func cmdSetup(cmd *cobra.Command, args []string) error { zkEvm := zkevm.FullZkEvm(&limits) builder = execution.NewBuilder(zkEvm) case circuits.BlobDecompressionV0CircuitID, circuits.BlobDecompressionV1CircuitID: - dict, err = os.ReadFile(fDictPath) + dict, err = os.ReadFile(args.DictPath) if err != nil { - return fmt.Errorf("%s failed to read dictionary file: %w", cmd.Name(), err) + return fmt.Errorf("%s failed to read dictionary file: %w", cmdName, err) } if c == circuits.BlobDecompressionV0CircuitID { @@ -151,14 +133,14 @@ func cmdSetup(cmd *cobra.Command, args []string) error { continue // dummy, aggregation, emulation or public input circuits are handled later } - if err := updateSetup(cmd.Context(), cfg, srsProvider, c, builder, extraFlags); err != nil { + if err := updateSetup(context, cfg, args.Force, srsProvider, c, builder, extraFlags); err != nil { return err } if dict != nil { // we save the dictionary to disk dictPath := filepath.Join(cfg.PathForSetup(string(c)), config.DictionaryFileName) if err := os.WriteFile(dictPath, dict, 0600); err != nil { - return fmt.Errorf("%s failed to write dictionary file: %w", cmd.Name(), err) + return fmt.Errorf("%s failed to write dictionary file: %w", cmdName, err) } } @@ -172,7 +154,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { // get verifying key for public-input circuit piSetup, err := circuits.LoadSetup(cfg, circuits.PublicInputInterconnectionCircuitID) if err != nil { - return fmt.Errorf("%s failed to load public input interconnection setup: %w", cmd.Name(), err) + return fmt.Errorf("%s failed to load public input interconnection setup: %w", cmdName, err) } // first, we need to collect the verifying keys @@ -196,7 +178,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { return fmt.Errorf("unknown dummy circuit: %s", allowedInput) } - vk, err := getDummyCircuitVK(cmd.Context(), cfg, srsProvider, circuits.CircuitID(allowedInput), dummy.NewBuilder(mockID, curveID.ScalarField())) + vk, err := getDummyCircuitVK(context, cfg, srsProvider, circuits.CircuitID(allowedInput), dummy.NewBuilder(mockID, curveID.ScalarField())) if err != nil { return err } @@ -209,7 +191,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { vkPath := filepath.Join(setupPath, config.VerifyingKeyFileName) vk := plonk.NewVerifyingKey(ecc.BLS12_377) if err := circuits.ReadVerifyingKey(vkPath, vk); err != nil { - return fmt.Errorf("%s failed to read verifying key for circuit %s: %w", cmd.Name(), allowedInput, err) + return fmt.Errorf("%s failed to read verifying key for circuit %s: %w", cmdName, allowedInput, err) } allowedVkForAggregation = append(allowedVkForAggregation, vk) @@ -217,7 +199,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { // we need to compute the digest of the verifying keys & store them in the manifest // for the aggregation circuits to be able to check compatibility at run time with the proofs - allowedVkForAggregationDigests := listOfCheckum(allowedVkForAggregation) + allowedVkForAggregationDigests := listOfChecksums(allowedVkForAggregation) extraFlagsForAggregationCircuit := map[string]any{ "allowedVkForAggregationDigests": allowedVkForAggregationDigests, } @@ -229,7 +211,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { logrus.Infof("setting up %s (numProofs=%d)", c, numProofs) builder := aggregation.NewBuilder(numProofs, cfg.Aggregation.AllowedInputs, piSetup, allowedVkForAggregation) - if err := updateSetup(cmd.Context(), cfg, srsProvider, c, builder, extraFlagsForAggregationCircuit); err != nil { + if err := updateSetup(context, cfg, args.Force, srsProvider, c, builder, extraFlagsForAggregationCircuit); err != nil { return err } @@ -238,7 +220,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { vkPath := filepath.Join(setupPath, config.VerifyingKeyFileName) vk := plonk.NewVerifyingKey(ecc.BW6_761) if err := circuits.ReadVerifyingKey(vkPath, vk); err != nil { - return fmt.Errorf("%s failed to read verifying key for circuit %s: %w", cmd.Name(), c, err) + return fmt.Errorf("%s failed to read verifying key for circuit %s: %w", cmdName, c, err) } allowedVkForEmulation = append(allowedVkForEmulation, vk) @@ -248,7 +230,7 @@ func cmdSetup(cmd *cobra.Command, args []string) error { c := circuits.EmulationCircuitID logrus.Infof("setting up %s", c) builder := emulation.NewBuilder(allowedVkForEmulation) - return updateSetup(cmd.Context(), cfg, srsProvider, c, builder, nil) + return updateSetup(context, cfg, args.Force, srsProvider, c, builder, nil) } @@ -281,7 +263,7 @@ func getDummyCircuitVK(ctx context.Context, cfg *config.Config, srsProvider circ // and if so, if the checksums match. // if the files already exist and the checksums match, it skips the setup. // else it does the setup and writes the assets to disk. -func updateSetup(ctx context.Context, cfg *config.Config, srsProvider circuits.SRSProvider, circuit circuits.CircuitID, builder circuits.Builder, extraFlags map[string]any) error { +func updateSetup(ctx context.Context, cfg *config.Config, force bool, srsProvider circuits.SRSProvider, circuit circuits.CircuitID, builder circuits.Builder, extraFlags map[string]any) error { if extraFlags == nil { extraFlags = make(map[string]any) } @@ -297,7 +279,7 @@ func updateSetup(ctx context.Context, cfg *config.Config, srsProvider circuits.S setupPath := cfg.PathForSetup(string(circuit)) manifestPath := filepath.Join(setupPath, config.ManifestFileName) - if !fForce { + if !force { // we may want to skip setup if the files already exist // and the checksums match // read manifest if already exists @@ -325,12 +307,13 @@ func updateSetup(ctx context.Context, cfg *config.Config, srsProvider circuits.S return setup.WriteTo(setupPath) } -// listOfCheckum Computes a list of SHA256 checksums for a list of assets, the result is given +// listOfChecksums Computes a list of SHA256 checksums for a list of assets, the result is given // in hexstring. -func listOfCheckum[T io.WriterTo](assets []T) []string { +func listOfChecksums[T io.WriterTo](assets []T) []string { res := make([]string, len(assets)) + h := sha256.New() for i := range assets { - h := sha256.New() + h.Reset() _, err := assets[i].WriteTo(h) if err != nil { // It is unexpected that writing in a hasher could possibly fail. diff --git a/prover/cmd/prover/main.go b/prover/cmd/prover/main.go index f221c083ea2..99e067ef4ad 100644 --- a/prover/cmd/prover/main.go +++ b/prover/cmd/prover/main.go @@ -1,7 +1,77 @@ package main -import "github.com/consensys/linea-monorepo/prover/cmd/prover/cmd" +import ( + "github.com/consensys/gnark/logger" + "github.com/consensys/linea-monorepo/prover/cmd/prover/cmd" + "github.com/rs/zerolog" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "os" + "strings" +) + +var ( + // rootCmd represents the base command when called without any subcommands + rootCmd = &cobra.Command{ + Use: "prover", + Short: "run pre-compute or compute proofs for Linea circuits", + } + fConfigFile string + + // setupCmd represents the setup command + setupCmd = &cobra.Command{ + Use: "setup", + Short: "pre compute assets for Linea circuits", + RunE: cmdSetup, + } + setupArgs cmd.SetupArgs + + // proveCmd represents the prove command + proveCmd = &cobra.Command{ + Use: "prove", + Short: "prove process a request, creates a proof with the adequate circuit and writes the proof to a file", + RunE: cmdProve, + } + proverArgs cmd.ProverArgs +) func main() { - cmd.Execute() + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + rootCmd.PersistentFlags().StringVar(&fConfigFile, "config", "", "config file") + + output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "15:04:05", NoColor: true} + l := zerolog.New(output).With().Timestamp().Logger() + + // Set global log level for gnark + logger.Set(l) + + rootCmd.AddCommand(setupCmd) + setupCmd.Flags().BoolVar(&setupArgs.Force, "force", false, "overwrites existing files") + setupCmd.Flags().StringVar(&setupArgs.Circuits, "circuits", strings.Join(cmd.AllCircuits, ","), "comma separated list of circuits to setup") + setupCmd.Flags().StringVar(&setupArgs.DictPath, "dict", "", "path to the dictionary file used in blob (de)compression") + setupCmd.Flags().StringVar(&setupArgs.AssetsDir, "assets-dir", "", "path to the directory where the assets are stored (override conf)") + + viper.BindPFlag("assets_dir", setupCmd.Flags().Lookup("assets-dir")) + + rootCmd.AddCommand(proveCmd) + + proveCmd.Flags().StringVar(&proverArgs.Input, "in", "", "input file") + proveCmd.Flags().StringVar(&proverArgs.Output, "out", "", "output file") + proveCmd.Flags().BoolVar(&proverArgs.Large, "large", false, "run the large execution circuit") +} + +func cmdSetup(_cmd *cobra.Command, _ []string) error { + setupArgs.ConfigFile = fConfigFile + return cmd.Setup(_cmd.Context(), setupArgs) +} + +func cmdProve(*cobra.Command, []string) error { + proverArgs.ConfigFile = fConfigFile + return cmd.Prove(proverArgs) }