From 381fae0e8541a6b08d3ad1cb0c4fa236b3a456c6 Mon Sep 17 00:00:00 2001 From: sbillig Date: Fri, 26 Jun 2026 09:24:55 -0700 Subject: [PATCH 1/2] Update for fe 26.2 --- README.md | 4 +- bench/compare_fe_gas.rb | 362 +++++ bench/compare_sol_fe_gas.rb | 255 ++++ examples/amm/test/SimpleAmmBench.t.sol | 10 +- examples/ec/test/BN254Bench.t.sol | 10 +- examples/erc20/fe/src/lib.fe | 112 +- examples/erc20/test/ERC20Bench.t.sol | 6 +- examples/escrow/fe/src/lib.fe | 45 +- examples/escrow/test/EscrowBench.t.sol | 6 +- examples/governance/fe/src/lib.fe | 81 +- .../governance/test/GovernanceBench.t.sol | 10 +- examples/math/fe/src/full_math.fe | 12 +- examples/math/test/FullMathBench.t.sol | 10 +- examples/merkle/test/MerkleProofBench.t.sol | 10 +- examples/poseidon/fe/src/constants.fe | 37 +- examples/poseidon/fe/src/fp.fe | 14 +- examples/poseidon/fe/src/lib.fe | 22 +- examples/poseidon/test/PoseidonBench.t.sol | 12 +- examples/reverts/README.md | 19 + examples/reverts/fe/fe.toml | 3 + examples/reverts/fe/src/lib.fe | 29 + examples/reverts/foundry.toml | 11 + examples/reverts/test/RevertOutput.t.sol | 79 ++ examples/verifier/fe/src/circuit.fe | 9 +- examples/verifier/fe/src/field.fe | 9 +- examples/verifier/fe/src/groth16.fe | 35 +- examples/verifier/fe/src/halo2.fe | 44 +- examples/verifier/fe/src/msm.fe | 2 +- examples/verifier/fe/src/plonk.fe | 1171 ++++++++++++----- examples/verifier/fe/src/plonk_bench.fe | 165 ++- examples/verifier/fe/src/transcript.fe | 43 +- examples/verifier/test/Groth16Bench.t.sol | 10 +- examples/verifier/test/PlonkBench.t.sol | 12 +- examples/verifier/test/PlonkCompare.t.sol | 12 +- shared/ec/fe/src/bench_contract.fe | 10 +- shared/ec/fe/src/bn254.fe | 83 +- 36 files changed, 2113 insertions(+), 651 deletions(-) create mode 100755 bench/compare_fe_gas.rb create mode 100755 bench/compare_sol_fe_gas.rb create mode 100644 examples/reverts/README.md create mode 100644 examples/reverts/fe/fe.toml create mode 100644 examples/reverts/fe/src/lib.fe create mode 100644 examples/reverts/foundry.toml create mode 100644 examples/reverts/test/RevertOutput.t.sol diff --git a/README.md b/README.md index d9f1b3a..6277636 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Side-by-side Fe and Solidity implementations of Ethereum contract patterns. Each | [amm](examples/amm) | Constant-product AMM (swap, add liquidity) | yes | | [escrow](examples/escrow) | Escrow with typed state machine | yes | | [governance](examples/governance) | Token + governance (vs Solidity Diamond) | yes | +| [reverts](examples/reverts) | Solidity-compatible Fe panic and revert-string payloads | n/a | | [verifier](examples/verifier) | Plonk and Halo2 proof verification | | | [poseidon](examples/poseidon) | Poseidon hash (T=3, BN254) | excluded ([sonatina#232](https://github.com/fe-lang/sonatina/issues/232)) | @@ -25,7 +26,7 @@ Shared libraries in `shared/`: ```bash # Optional: point Foundry FFI tests at a specific Fe binary -export FE_BIN="$HOME/code/fe/quagmir/target/release/fe" +export FE_BIN="$HOME/code/fe/master/target/release/fe" # Build all Fe contracts "${FE_BIN:-fe}" build . @@ -37,6 +38,7 @@ cd examples/merkle && forge test -vv cd examples/amm && forge test -vv cd examples/escrow && forge test -vv cd examples/governance && forge test -vv +cd examples/reverts && forge test -vv ``` Requires [Fe](https://github.com/ethereum/fe) and [Foundry](https://book.getfoundry.sh/). diff --git a/bench/compare_fe_gas.rb b/bench/compare_fe_gas.rb new file mode 100755 index 0000000..de60eb9 --- /dev/null +++ b/bench/compare_fe_gas.rb @@ -0,0 +1,362 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "fileutils" +require "open3" +require "optparse" +require "time" + +GasResult = Struct.new(:example, :suite, :test, :gas, keyword_init: true) do + def key + [example, suite, test] + end +end + +RunResult = Struct.new(:label, :fe_bin, :log_path, :exit_status, :gas, :failed_examples, keyword_init: true) + +def usage + <<~TEXT + Usage: + bench/compare_fe_gas.rb [options] BASE_FE_BIN CANDIDATE_FE_BIN [-- FORGE_ARGS...] + + Examples: + bench/compare_fe_gas.rb \\ + ~/code/fe/master/target/release/fe \\ + ~/code/fe/debug/target/release/fe + + bench/compare_fe_gas.rb --label-base master --label-candidate debug \\ + ~/code/fe/master/target/release/fe \\ + ~/code/fe/debug/target/release/fe -- --match-test testGas + TEXT +end + +options = { + out_dir: File.expand_path("out/gas-compare", Dir.pwd), + label_base: "base", + label_candidate: "candidate", + keep_ansi: false, + top: 20, +} + +parser = OptionParser.new do |opts| + opts.banner = usage + + opts.on("--out-dir DIR", "Directory for captured logs (default: out/gas-compare)") do |dir| + options[:out_dir] = File.expand_path(dir) + end + + opts.on("--label-base LABEL", "Label for the first Fe binary (default: base)") do |label| + options[:label_base] = label + end + + opts.on("--label-candidate LABEL", "Label for the second Fe binary (default: candidate)") do |label| + options[:label_candidate] = label + end + + opts.on("--top N", Integer, "Number of largest improvements/regressions to print (default: 20)") do |n| + options[:top] = n + end + + opts.on("--keep-ansi", "Keep ANSI color escapes in saved logs") do + options[:keep_ansi] = true + end + + opts.on("-h", "--help", "Show this help") do + puts opts + exit 0 + end +end + +argv = ARGV.dup +separator = argv.index("--") +forge_args = separator ? argv[(separator + 1)..] : [] +argv = argv[0...separator] if separator +parser.parse!(argv) + +if argv.length != 2 + warn parser.to_s + exit 2 +end + +base_bin = File.expand_path(argv[0]) +candidate_bin = File.expand_path(argv[1]) + +[base_bin, candidate_bin].each do |path| + next if File.executable?(path) + + warn "Fe binary is not executable: #{path}" + exit 2 +end + +def strip_ansi(line) + line.gsub(/\e\[[0-9;]*[A-Za-z]/, "") +end + +def parse_log(path) + gas = {} + failed_examples = {} + current_example = nil + current_suite = nil + + File.foreach(path) do |raw_line| + line = strip_ansi(raw_line) + + if line =~ /^\[(examples\/[^\] ]+)\]$/ + current_example = Regexp.last_match(1) + current_suite = nil + next + end + + if line =~ /^Ran \d+ tests? for (.+)$/ + current_suite = Regexp.last_match(1).strip + next + end + + if line =~ /^\[(examples\/[^\]]+) failed with exit code (\d+)\]$/ + failed_examples[Regexp.last_match(1)] = Regexp.last_match(2).to_i + next + end + + next unless line =~ /^\[PASS\]\s+(.+?)\s+\(gas:\s*(\d+)\)/ + + result = GasResult.new( + example: current_example || "unknown", + suite: current_suite || "unknown", + test: Regexp.last_match(1), + gas: Regexp.last_match(2).to_i, + ) + gas[result.key] = result + end + + [gas, failed_examples] +end + +def run_rosetta(label:, fe_bin:, out_dir:, forge_args:, keep_ansi:) + FileUtils.mkdir_p(out_dir) + timestamp = Time.now.utc.strftime("%Y%m%dT%H%M%SZ") + safe_label = label.gsub(/[^A-Za-z0-9_.-]+/, "_") + log_path = File.join(out_dir, "rosetta-#{safe_label}-#{timestamp}.log") + + env = { "FE_BIN" => fe_bin } + cmd = ["./test.sh", *forge_args] + + puts "Running #{label}: FE_BIN=#{fe_bin}" + puts "Log: #{log_path}" + + status = nil + File.open(log_path, "w") do |log| + Open3.popen2e(env, *cmd) do |_stdin, output, wait_thread| + output.each_line do |line| + log.write(keep_ansi ? line : strip_ansi(line)) + print line + end + status = wait_thread.value + end + end + + gas, failed_examples = parse_log(log_path) + RunResult.new( + label: label, + fe_bin: fe_bin, + log_path: log_path, + exit_status: status.exitstatus, + gas: gas, + failed_examples: failed_examples, + ) +end + +def format_delta(delta, base) + pct = base.zero? ? 0.0 : (delta * 100.0 / base) + format("%+d (%+.2f%%)", delta, pct) +end + +def print_rows(rows, limit:) + rows.first(limit).each do |row| + key, base_gas, candidate_gas, delta = row + example, _suite, test = key + printf( + "%-12s %-55s %9d -> %9d %s\n", + example.delete_prefix("examples/"), + test, + base_gas, + candidate_gas, + format_delta(delta, base_gas), + ) + end +end + +def gas_pairs(results) + by_subject = {} + + results.each_value do |result| + next unless result.test =~ /^testGas_(sol|fe)_(.+)$/ + + kind = Regexp.last_match(1).to_sym + subject = Regexp.last_match(2) + key = [result.example, result.suite, subject] + by_subject[key] ||= {} + by_subject[key][kind] = result + end + + by_subject.select { |_key, pair| pair.key?(:sol) && pair.key?(:fe) } +end + +def sol_fe_rows(run) + gas_pairs(run.gas).map do |key, pair| + sol_gas = pair.fetch(:sol).gas + fe_gas = pair.fetch(:fe).gas + [key, sol_gas, fe_gas, fe_gas - sol_gas] + end.sort_by(&:first) +end + +def print_sol_fe_rows(rows, limit:) + rows.first(limit).each do |row| + (example, _suite, subject), sol_gas, fe_gas, delta = row + printf( + "%-12s %-40s %9d -> %9d %s\n", + example.delete_prefix("examples/"), + subject, + sol_gas, + fe_gas, + format_delta(delta, sol_gas), + ) + end +end + +def print_sol_fe_comparison(run, top:) + rows = sol_fe_rows(run) + sol_total = rows.sum { |_key, gas, _fe_gas, _delta| gas } + fe_total = rows.sum { |_key, _sol_gas, gas, _delta| gas } + total_delta = fe_total - sol_total + + puts + puts "Solidity vs #{run.label}" + puts "============#{'=' * run.label.length}" + puts "Comparable Sol/Fe gas tests: #{rows.length}" + puts "Solidity total: #{sol_total}" + puts "#{run.label} Fe total: #{fe_total}" + puts "Delta: #{format_delta(total_delta, sol_total)}" + + puts + puts "Largest #{run.label} Fe wins" + puts "========#{'=' * run.label.length}========" + wins = rows.select { |_key, _sol_gas, _fe_gas, delta| delta.negative? } + .sort_by { |_key, _sol_gas, _fe_gas, delta| delta } + if wins.empty? + puts "none" + else + print_sol_fe_rows(wins, limit: top) + end + + puts + puts "Largest #{run.label} Fe losses" + puts "========#{'=' * run.label.length}==========" + losses = rows.select { |_key, _sol_gas, _fe_gas, delta| delta.positive? } + .sort_by { |_key, _sol_gas, _fe_gas, delta| -delta } + if losses.empty? + puts "none" + else + print_sol_fe_rows(losses, limit: top) + end +end + +base = run_rosetta( + label: options[:label_base], + fe_bin: base_bin, + out_dir: options[:out_dir], + forge_args: forge_args, + keep_ansi: options[:keep_ansi], +) + +candidate = run_rosetta( + label: options[:label_candidate], + fe_bin: candidate_bin, + out_dir: options[:out_dir], + forge_args: forge_args, + keep_ansi: options[:keep_ansi], +) + +base_keys = base.gas.keys +candidate_keys = candidate.gas.keys +common_keys = (base_keys & candidate_keys).sort + +rows = common_keys.map do |key| + base_gas = base.gas.fetch(key).gas + candidate_gas = candidate.gas.fetch(key).gas + [key, base_gas, candidate_gas, candidate_gas - base_gas] +end + +base_total = rows.sum { |_key, gas, _candidate_gas, _delta| gas } +candidate_total = rows.sum { |_key, _base_gas, gas, _delta| gas } +total_delta = candidate_total - base_total + +puts +puts "Summary" +puts "=======" +puts "#{base.label} exit: #{base.exit_status}, log: #{base.log_path}" +puts "#{candidate.label} exit: #{candidate.exit_status}, log: #{candidate.log_path}" +puts "Comparable gas tests: #{rows.length}" +puts "#{base.label} total: #{base_total}" +puts "#{candidate.label} total: #{candidate_total}" +puts "Delta: #{format_delta(total_delta, base_total)}" + +puts +puts "Largest improvements" +puts "====================" +improvements = rows.select { |_key, _base_gas, _candidate_gas, delta| delta.negative? } + .sort_by { |_key, _base_gas, _candidate_gas, delta| delta } +if improvements.empty? + puts "none" +else + print_rows(improvements, limit: options[:top]) +end + +puts +puts "Largest regressions" +puts "===================" +regressions = rows.select { |_key, _base_gas, _candidate_gas, delta| delta.positive? } + .sort_by { |_key, _base_gas, _candidate_gas, delta| -delta } +if regressions.empty? + puts "none" +else + print_rows(regressions, limit: options[:top]) +end + +print_sol_fe_comparison(base, top: options[:top]) +print_sol_fe_comparison(candidate, top: options[:top]) + +base_only = base_keys - candidate_keys +candidate_only = candidate_keys - base_keys + +unless base_only.empty? + puts + puts "Only in #{base.label}" + puts "==============#{'=' * base.label.length}" + base_only.sort.each do |example, _suite, test| + puts "#{example.delete_prefix("examples/")} #{test}=#{base.gas.fetch([example, _suite, test]).gas}" + end +end + +unless candidate_only.empty? + puts + puts "Only in #{candidate.label}" + puts "==============#{'=' * candidate.label.length}" + candidate_only.sort.each do |example, _suite, test| + puts "#{example.delete_prefix("examples/")} #{test}=#{candidate.gas.fetch([example, _suite, test]).gas}" + end +end + +unless base.failed_examples.empty? && candidate.failed_examples.empty? + puts + puts "Failed examples" + puts "===============" + { base => base.failed_examples, candidate => candidate.failed_examples }.each do |run, failures| + next if failures.empty? + + failures.sort.each do |example, code| + puts "#{run.label}: #{example} failed with exit code #{code}" + end + end +end + +exit(base.exit_status.zero? && candidate.exit_status.zero? ? 0 : 1) diff --git a/bench/compare_sol_fe_gas.rb b/bench/compare_sol_fe_gas.rb new file mode 100755 index 0000000..14d8ec6 --- /dev/null +++ b/bench/compare_sol_fe_gas.rb @@ -0,0 +1,255 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "fileutils" +require "open3" +require "optparse" +require "time" + +GasResult = Struct.new(:example, :suite, :test, :gas, keyword_init: true) +RunResult = Struct.new(:label, :fe_bin, :log_path, :exit_status, :gas, :failed_examples, keyword_init: true) + +def usage + <<~TEXT + Usage: + bench/compare_sol_fe_gas.rb [options] [FE_BIN] [-- FORGE_ARGS...] + + Examples: + bench/compare_sol_fe_gas.rb ~/code/fe/sona-bump/target/release/fe + bench/compare_sol_fe_gas.rb --top 10 -- --match-test testGas + TEXT +end + +options = { + out_dir: File.expand_path("out/gas-compare", Dir.pwd), + label: "sol-vs-fe", + keep_ansi: false, + top: 20, +} + +parser = OptionParser.new do |opts| + opts.banner = usage + + opts.on("--out-dir DIR", "Directory for captured logs (default: out/gas-compare)") do |dir| + options[:out_dir] = File.expand_path(dir) + end + + opts.on("--label LABEL", "Label for the captured log (default: sol-vs-fe)") do |label| + options[:label] = label + end + + opts.on("--top N", Integer, "Number of largest Fe wins/losses to print (default: 20)") do |n| + options[:top] = n + end + + opts.on("--keep-ansi", "Keep ANSI color escapes in saved logs") do + options[:keep_ansi] = true + end + + opts.on("-h", "--help", "Show this help") do + puts opts + exit 0 + end +end + +argv = ARGV.dup +separator = argv.index("--") +forge_args = separator ? argv[(separator + 1)..] : [] +argv = argv[0...separator] if separator +parser.parse!(argv) + +if argv.length > 1 + warn parser.to_s + exit 2 +end + +fe_bin = File.expand_path(argv.first || ENV.fetch("FE_BIN", "fe")) +unless File.executable?(fe_bin) + warn "Fe binary is not executable: #{fe_bin}" + exit 2 +end + +def strip_ansi(line) + line.gsub(/\e\[[0-9;]*[A-Za-z]/, "") +end + +def parse_log(path) + gas = {} + failed_examples = {} + current_example = nil + current_suite = nil + + File.foreach(path) do |raw_line| + line = strip_ansi(raw_line) + + if line =~ /^\[(examples\/[^\] ]+)\]$/ + current_example = Regexp.last_match(1) + current_suite = nil + next + end + + if line =~ /^Ran \d+ tests? for (.+)$/ + current_suite = Regexp.last_match(1).strip + next + end + + if line =~ /^\[(examples\/[^\]]+) failed with exit code (\d+)\]$/ + failed_examples[Regexp.last_match(1)] = Regexp.last_match(2).to_i + next + end + + next unless line =~ /^\[PASS\]\s+(.+?)\s+\(gas:\s*(\d+)\)/ + + result = GasResult.new( + example: current_example || "unknown", + suite: current_suite || "unknown", + test: Regexp.last_match(1), + gas: Regexp.last_match(2).to_i, + ) + gas[[result.example, result.suite, result.test]] = result + end + + [gas, failed_examples] +end + +def run_rosetta(label:, fe_bin:, out_dir:, forge_args:, keep_ansi:) + FileUtils.mkdir_p(out_dir) + timestamp = Time.now.utc.strftime("%Y%m%dT%H%M%SZ") + safe_label = label.gsub(/[^A-Za-z0-9_.-]+/, "_") + log_path = File.join(out_dir, "rosetta-#{safe_label}-#{timestamp}.log") + + env = { "FE_BIN" => fe_bin } + cmd = ["./test.sh", *forge_args] + + puts "Running #{label}: FE_BIN=#{fe_bin}" + puts "Log: #{log_path}" + + status = nil + File.open(log_path, "w") do |log| + Open3.popen2e(env, *cmd) do |_stdin, output, wait_thread| + output.each_line do |line| + log.write(keep_ansi ? line : strip_ansi(line)) + print line + end + status = wait_thread.value + end + end + + gas, failed_examples = parse_log(log_path) + RunResult.new( + label: label, + fe_bin: fe_bin, + log_path: log_path, + exit_status: status.exitstatus, + gas: gas, + failed_examples: failed_examples, + ) +end + +def gas_pairs(results) + by_subject = {} + + results.each_value do |result| + next unless result.test =~ /^testGas_(sol|fe)_(.+)$/ + + kind = Regexp.last_match(1).to_sym + subject = Regexp.last_match(2) + key = [result.example, result.suite, subject] + by_subject[key] ||= {} + by_subject[key][kind] = result + end + + by_subject.select { |_key, pair| pair.key?(:sol) && pair.key?(:fe) } +end + +def format_delta(delta, base) + pct = base.zero? ? 0.0 : (delta * 100.0 / base) + format("%+d (%+.2f%%)", delta, pct) +end + +def print_rows(rows, limit:) + rows.first(limit).each do |row| + (example, _suite, subject), sol_gas, fe_gas, delta = row + printf( + "%-12s %-40s %9d -> %9d %s\n", + example.delete_prefix("examples/"), + subject, + sol_gas, + fe_gas, + format_delta(delta, sol_gas), + ) + end +end + +run = run_rosetta( + label: options[:label], + fe_bin: fe_bin, + out_dir: options[:out_dir], + forge_args: forge_args, + keep_ansi: options[:keep_ansi], +) + +pairs = gas_pairs(run.gas) +rows = pairs.map do |key, pair| + sol_gas = pair.fetch(:sol).gas + fe_gas = pair.fetch(:fe).gas + [key, sol_gas, fe_gas, fe_gas - sol_gas] +end.sort_by(&:first) + +sol_total = rows.sum { |_key, gas, _fe_gas, _delta| gas } +fe_total = rows.sum { |_key, _sol_gas, gas, _delta| gas } +total_delta = fe_total - sol_total + +puts +puts "Summary" +puts "=======" +puts "exit: #{run.exit_status}, log: #{run.log_path}" +puts "Comparable Sol/Fe gas tests: #{rows.length}" +puts "Solidity total: #{sol_total}" +puts "Fe total: #{fe_total}" +puts "Delta: #{format_delta(total_delta, sol_total)}" + +puts +puts "Largest Fe wins" +puts "===============" +wins = rows.select { |_key, _sol_gas, _fe_gas, delta| delta.negative? } + .sort_by { |_key, _sol_gas, _fe_gas, delta| delta } +if wins.empty? + puts "none" +else + print_rows(wins, limit: options[:top]) +end + +puts +puts "Largest Fe losses" +puts "=================" +losses = rows.select { |_key, _sol_gas, _fe_gas, delta| delta.positive? } + .sort_by { |_key, _sol_gas, _fe_gas, delta| -delta } +if losses.empty? + puts "none" +else + print_rows(losses, limit: options[:top]) +end + +unpaired_sol = run.gas.values.select { |result| result.test =~ /^testGas_sol_/ && pairs.none? { |_key, pair| pair[:sol] == result } } +unpaired_fe = run.gas.values.select { |result| result.test =~ /^testGas_fe_/ && pairs.none? { |_key, pair| pair[:fe] == result } } + +unless unpaired_sol.empty? && unpaired_fe.empty? + puts + puts "Unpaired gas tests" + puts "==================" + (unpaired_sol + unpaired_fe).sort_by { |result| [result.example, result.test] }.each do |result| + puts "#{result.example.delete_prefix("examples/")} #{result.test}=#{result.gas}" + end +end + +unless run.failed_examples.empty? + puts + puts "Failed examples" + puts "===============" + run.failed_examples.sort.each do |example, code| + puts "#{example} failed with exit code #{code}" + end +end + +exit(run.exit_status.zero? ? 0 : 1) diff --git a/examples/amm/test/SimpleAmmBench.t.sol b/examples/amm/test/SimpleAmmBench.t.sol index 1230040..ee87f99 100644 --- a/examples/amm/test/SimpleAmmBench.t.sol +++ b/examples/amm/test/SimpleAmmBench.t.sol @@ -35,14 +35,12 @@ contract SimpleAmmBenchTest { vm.pauseGasMetering(); uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); - string[] memory cmd = new string[](7); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/ec/test/BN254Bench.t.sol b/examples/ec/test/BN254Bench.t.sol index 0645df0..5971f46 100644 --- a/examples/ec/test/BN254Bench.t.sol +++ b/examples/ec/test/BN254Bench.t.sol @@ -32,14 +32,12 @@ contract BN254BenchTest { vm.pauseGasMetering(); uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); - string[] memory cmd = new string[](7); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "../../shared/ec/fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "../../shared/ec/fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/erc20/fe/src/lib.fe b/examples/erc20/fe/src/lib.fe index 54d4142..e35f103 100644 --- a/examples/erc20/fe/src/lib.fe +++ b/examples/erc20/fe/src/lib.fe @@ -2,7 +2,6 @@ /// /// One file, no inheritance, exhaustive msg dispatch. Effects declare /// exactly what each handler can access. - msg Erc20 { #[selector = sol("totalSupply()")] TotalSupply -> u256, @@ -30,16 +29,22 @@ struct TokenStore { } pub contract Token uses (ctx: Ctx) { - mut store: TokenStore + mut store: TokenStore, - init(initial_supply: u256, owner: Address) uses (mut store) { + init(initial_supply: u256, owner: Address) + uses (mut store) + { store.total_supply = initial_supply store.balances.set(key: owner, value: initial_supply) } recv Erc20 { - TotalSupply -> u256 uses (store) { store.total_supply } - BalanceOf { account } -> u256 uses (store) { store.balances.get(key: account) } + TotalSupply -> u256 uses (store) { + store.total_supply + } + BalanceOf { account } -> u256 uses (store) { + store.balances.get(key: account) + } Allowance { owner, spender } -> u256 uses (store) { store.allowances.get(key: (owner, spender)) } @@ -47,7 +52,9 @@ pub contract Token uses (ctx: Ctx) { Transfer { to, amount } -> bool uses (mut store, ctx) { let from = ctx.caller() let bal = store.balances.get(key: from) - if bal < amount { return false } + if bal < amount { + return false + } store.balances.set(key: from, value: bal - amount) store.balances.set(key: to, value: store.balances.get(key: to) + amount) true @@ -58,12 +65,18 @@ pub contract Token uses (ctx: Ctx) { true } - TransferFrom { from, to, amount } -> bool uses (mut store, ctx) { + TransferFrom { from, to, amount } -> bool + uses (mut store, ctx) + { let spender = ctx.caller() let allowed = store.allowances.get(key: (from, spender)) - if allowed < amount { return false } + if allowed < amount { + return false + } let bal = store.balances.get(key: from) - if bal < amount { return false } + if bal < amount { + return false + } store.allowances.set(key: (from, spender), value: allowed - amount) store.balances.set(key: from, value: bal - amount) store.balances.set(key: to, value: store.balances.get(key: to) + amount) @@ -93,12 +106,10 @@ msg VaultMsg { Balance { token: Address } -> u256, } -struct VaultStore { - holdings: StorageMap, -} +struct VaultStore { holdings: StorageMap } pub contract Vault uses (ctx: Ctx, call: mut Call) { - mut store: VaultStore + mut store: VaultStore, init() {} @@ -106,19 +117,15 @@ pub contract Vault uses (ctx: Ctx, call: mut Call) { // Pull tokens from caller into the vault via transferFrom. // Caller must have approved the vault first. Deposit { token, amount } uses (mut store, ctx, mut call) { - let ok: bool = call.call( - addr: token, gas: 100000, value: 0, - message: Erc20::TransferFrom { - from: ctx.caller(), - to: ctx.address(), - amount, - }, - ) - if ok { - store.holdings.set( - key: token, - value: store.holdings.get(key: token) + amount, + let ok: bool = call + .call( + addr: token, + gas: 100000, + value: 0, + message: Erc20::TransferFrom { from: ctx.caller(), to: ctx.address(), amount }, ) + if ok { + store.holdings.set(key: token, value: store.holdings.get(key: token) + amount) } } @@ -133,13 +140,24 @@ pub contract Vault uses (ctx: Ctx, call: mut Call) { fn test_deploy_and_check_supply() uses (evm: mut Evm) { let owner = Address { inner: 1 } let token = evm.create2(value: 0, args: (1000, owner), salt: 0) - assert(token.inner != 0) - - let supply: u256 = evm.call(addr: token, gas: 100000, value: 0, message: Erc20::TotalSupply {}) - assert(supply == 1000) - - let bal: u256 = evm.call(addr: token, gas: 100000, value: 0, message: Erc20::BalanceOf { account: owner }) - assert(bal == 1000) + assert!(token.inner != 0) + + let supply: u256 = evm.call( + addr: token, + gas: 100000, + value: 0, + message: Erc20::TotalSupply {}, + ) + assert!(supply == 1000) + + let bal: u256 = evm + .call( + addr: token, + gas: 100000, + value: 0, + message: Erc20::BalanceOf { account: owner }, + ) + assert!(bal == 1000) } #[test] @@ -147,10 +165,21 @@ fn test_transfer() uses (evm: mut Evm) { let alice = Address { inner: 0xA11CE } let token = evm.create2(value: 0, args: (0, alice), salt: 1) - evm.call(addr: token, gas: 100000, value: 0, message: Mintable::Mint { to: alice, amount: 500 }) - - let bal: u256 = evm.call(addr: token, gas: 100000, value: 0, message: Erc20::BalanceOf { account: alice }) - assert(bal == 500) + evm.call( + addr: token, + gas: 100000, + value: 0, + message: Mintable::Mint { to: alice, amount: 500 }, + ) + + let bal: u256 = evm + .call( + addr: token, + gas: 100000, + value: 0, + message: Erc20::BalanceOf { account: alice }, + ) + assert!(bal == 500) } #[test] @@ -160,7 +189,12 @@ fn test_allowance() uses (evm: mut Evm) { let token = evm.create2(value: 0, args: (1000, owner), salt: 2) - let a: u256 = evm.call(addr: token, gas: 100000, value: 0, - message: Erc20::Allowance { owner, spender }) - assert(a == 0) + let a: u256 = evm + .call( + addr: token, + gas: 100000, + value: 0, + message: Erc20::Allowance { owner, spender }, + ) + assert!(a == 0) } diff --git a/examples/erc20/test/ERC20Bench.t.sol b/examples/erc20/test/ERC20Bench.t.sol index a362a1e..4f57fad 100644 --- a/examples/erc20/test/ERC20Bench.t.sol +++ b/examples/erc20/test/ERC20Bench.t.sol @@ -26,12 +26,10 @@ contract ERC20BenchTest { sol = new ERC20(INITIAL, OWNER); // Deploy Fe ERC20 - string[] memory cmd = new string[](5); + string[] memory cmd = new string[](3); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "fe"; + cmd[2] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/escrow/fe/src/lib.fe b/examples/escrow/fe/src/lib.fe index 93104d6..d6d4490 100644 --- a/examples/escrow/fe/src/lib.fe +++ b/examples/escrow/fe/src/lib.fe @@ -3,7 +3,6 @@ /// Two contracts sharing a msg type: Escrow holds funds, Arbiter releases them. /// The Arbiter contract's capabilities are bounded at the type level — /// it can only make calls (uses Call), not access storage or emit events. - msg EscrowMsg { #[selector = sol("deposit()")] Deposit, @@ -27,8 +26,12 @@ const fn state_disc(s: State) -> u256 { } } -const fn is_empty(s: State) -> bool { state_disc(s) == 0 } -const fn is_funded(s: State) -> bool { state_disc(s) == 1 } +const fn is_empty(s: State) -> bool { + state_disc(s) == 0 +} +const fn is_funded(s: State) -> bool { + state_disc(s) == 1 +} struct EscrowStore { depositor: Address, @@ -39,9 +42,11 @@ struct EscrowStore { } pub contract Escrow uses (ctx: Ctx) { - mut store: EscrowStore + mut store: EscrowStore, - init(beneficiary: Address, arbiter: Address) uses (mut store, ctx) { + init(beneficiary: Address, arbiter: Address) + uses (mut store, ctx) + { store.depositor = ctx.caller() store.beneficiary = beneficiary store.arbiter = arbiter @@ -52,14 +57,14 @@ pub contract Escrow uses (ctx: Ctx) { recv EscrowMsg { #[payable] Deposit uses (mut store, ctx) { - assert_msg(is_empty(s: store.state), "escrow already funded") + assert!(is_empty(s: store.state), "escrow already funded") store.balance = ctx.value() store.state = State::Funded } Release -> u256 uses (mut store, ctx) { - assert_msg(ctx.caller() == store.arbiter, "only arbiter can release") - assert_msg(is_funded(s: store.state), "escrow not funded") + assert!(ctx.caller() == store.arbiter, "only arbiter can release") + assert!(is_funded(s: store.state), "escrow not funded") store.state = State::Released let amount = store.balance store.balance = 0 @@ -67,7 +72,9 @@ pub contract Escrow uses (ctx: Ctx) { } // Read-only: no mut, no ctx. - GetBalance -> u256 uses (store) { store.balance } + GetBalance -> u256 uses (store) { + store.balance + } } } @@ -84,7 +91,12 @@ pub contract Arbiter uses (call: mut Call) { recv ArbiterMsg { DoRelease { escrow_addr } -> u256 uses (mut call) { - call.call(addr: escrow_addr, gas: 100000, value: 0, message: EscrowMsg::Release {}) + call.call( + addr: escrow_addr, + gas: 100000, + value: 0, + message: EscrowMsg::Release {}, + ) } } } @@ -95,8 +107,13 @@ fn test_escrow_lifecycle() uses (evm: mut Evm) { let arbiter_addr = Address { inner: 0xA1B1 } let escrow = evm.create2(value: 0, args: (beneficiary, arbiter_addr), salt: 0) - assert(escrow.inner != 0) - - let bal: u256 = evm.call(addr: escrow, gas: 100000, value: 0, message: EscrowMsg::GetBalance {}) - assert(bal == 0) + assert!(escrow.inner != 0) + + let bal: u256 = evm.call( + addr: escrow, + gas: 100000, + value: 0, + message: EscrowMsg::GetBalance {}, + ) + assert!(bal == 0) } diff --git a/examples/escrow/test/EscrowBench.t.sol b/examples/escrow/test/EscrowBench.t.sol index 3e8612e..df82326 100644 --- a/examples/escrow/test/EscrowBench.t.sol +++ b/examples/escrow/test/EscrowBench.t.sol @@ -41,12 +41,10 @@ contract EscrowBenchTest { vm.pauseGasMetering(); // --- Build Fe contracts --- - string[] memory buildCmd = new string[](5); + string[] memory buildCmd = new string[](3); buildCmd[0] = vm.envOr("FE_BIN", "fe"); buildCmd[1] = "build"; - buildCmd[2] = "--backend"; - buildCmd[3] = "sonatina"; - buildCmd[4] = "fe"; + buildCmd[2] = "fe"; vm.ffi(buildCmd); // --- Read Fe bytecode (cached for reuse in fuzz tests) --- diff --git a/examples/governance/fe/src/lib.fe b/examples/governance/fe/src/lib.fe index 937e52e..9a77667 100644 --- a/examples/governance/fe/src/lib.fe +++ b/examples/governance/fe/src/lib.fe @@ -3,8 +3,7 @@ /// Equivalent to the Solidity EIP-2535 Diamond version in sol/. /// TokenContract owns the token state. GovContract stores governance /// state and reads token balances via typed cross-contract calls. - -// ---- Token ---- + // ---- Token ---- msg TokenMsg { #[selector = sol("mint(address,uint256)")] @@ -17,22 +16,16 @@ msg TokenMsg { TotalSupply -> u256, } -struct TokenStore { - total_supply: u256, - balances: StorageMap, - owner: Address, -} +struct TokenStore { total_supply: u256, balances: StorageMap, owner: Address } pub contract TokenContract uses (ctx: Ctx) { - mut store: TokenStore + mut store: TokenStore, - init() uses (mut store, ctx) { - store.owner = ctx.caller() - } + init() uses (mut store, ctx) { store.owner = ctx.caller() } recv TokenMsg { Mint { to, amount } uses (mut store, ctx) { - assert_msg(ctx.caller() == store.owner, "only owner can mint") + assert!(ctx.caller() == store.owner, "only owner can mint") store.total_supply += amount store.balances.set(key: to, value: store.balances.get(key: to) + amount) } @@ -40,7 +33,9 @@ pub contract TokenContract uses (ctx: Ctx) { Transfer { to, amount } -> bool uses (mut store, ctx) { let from = ctx.caller() let bal = store.balances.get(key: from) - if bal < amount { return false } + if bal < amount { + return false + } store.balances.set(key: from, value: bal - amount) store.balances.set(key: to, value: store.balances.get(key: to) + amount) true @@ -50,7 +45,9 @@ pub contract TokenContract uses (ctx: Ctx) { store.balances.get(key: account) } - TotalSupply -> u256 uses (store) { store.total_supply } + TotalSupply -> u256 uses (store) { + store.total_supply + } } } @@ -92,16 +89,20 @@ struct GovStore { /// Read a token balance via typed cross-contract call. fn token_balance(token: Address, account: Address) -> u256 - uses (call: mut Call) +uses (call: mut Call) { - call.static(addr: token, gas: 100000, message: TokenMsg::BalanceOf { account }) + call.static( + addr: token, + gas: 100000, + message: TokenMsg::BalanceOf { account }, + ) } pub contract GovContract uses (ctx: Ctx, call: mut Call) { - mut store: GovStore + mut store: GovStore, init(token: Address, voting_period: u256, quorum: u256) - uses (mut store) + uses (mut store) { store.token = token store.voting_period = voting_period @@ -110,7 +111,10 @@ pub contract GovContract uses (ctx: Ctx, call: mut Call) { recv GovMsg { Propose { description } -> u256 uses (mut store, ctx, mut call) { - assert_msg(token_balance(token: store.token, account: ctx.caller()) > 0, "proposer has no tokens") + assert!( + token_balance(token: store.token, account: ctx.caller()) > 0, + "proposer has no tokens", + ) let id = store.proposal_count store.proposal_count = id + 1 @@ -125,15 +129,24 @@ pub contract GovContract uses (ctx: Ctx, call: mut Call) { let caller = ctx.caller() let block = ctx.block_number() - assert_msg(block >= store.vote_starts.get(key: proposal_id), "voting not started") - assert_msg(block <= store.vote_ends.get(key: proposal_id), "voting ended") - assert_msg(!store.has_voted.get(key: (proposal_id, caller)), "already voted") + assert!( + block >= store.vote_starts.get(key: proposal_id), + "voting not started", + ) + assert!( + block <= store.vote_ends.get(key: proposal_id), + "voting ended", + ) + assert!( + !store.has_voted.get(key: (proposal_id, caller)), + "already voted", + ) store.has_voted.set(key: (proposal_id, caller), value: true) let weight = token_balance(token: store.token, account: caller) - + store.delegated_weight.get(key: caller) - assert_msg(weight > 0, "no voting power") + + store.delegated_weight.get(key: caller) + assert!(weight > 0, "no voting power") if support { let v = store.yes_votes.get(key: proposal_id) @@ -163,18 +176,23 @@ pub contract GovContract uses (ctx: Ctx, call: mut Call) { } Execute { proposal_id } -> bool uses (mut store, ctx) { - assert_msg(ctx.block_number() > store.vote_ends.get(key: proposal_id), "voting not ended") - assert_msg(!store.executed.get(key: proposal_id), "already executed") + assert!( + ctx.block_number() > store.vote_ends.get(key: proposal_id), + "voting not ended", + ) + assert!(!store.executed.get(key: proposal_id), "already executed") let yes = store.yes_votes.get(key: proposal_id) let no = store.no_votes.get(key: proposal_id) - assert_msg(yes + no >= store.quorum, "quorum not reached") + assert!(yes + no >= store.quorum, "quorum not reached") store.executed.set(key: proposal_id, value: true) yes > no } - GetProposal { proposal_id } -> (u256, u256, u256, bool) uses (store) { + GetProposal { proposal_id } -> (u256, u256, u256, bool) + uses (store) + { ( store.yes_votes.get(key: proposal_id), store.no_votes.get(key: proposal_id), @@ -183,15 +201,16 @@ pub contract GovContract uses (ctx: Ctx, call: mut Call) { ) } - ProposalCount -> u256 uses (store) { store.proposal_count } + ProposalCount -> u256 uses (store) { + store.proposal_count + } GetDelegate { account } -> Address uses (store) { store.delegates.get(key: account) } VotingPower { account } -> u256 uses (store, mut call) { - token_balance(token: store.token, account) - + store.delegated_weight.get(key: account) + token_balance(token: store.token, account) + store.delegated_weight.get(key: account) } } } diff --git a/examples/governance/test/GovernanceBench.t.sol b/examples/governance/test/GovernanceBench.t.sol index 8a6b1ac..961069c 100644 --- a/examples/governance/test/GovernanceBench.t.sol +++ b/examples/governance/test/GovernanceBench.t.sol @@ -81,14 +81,12 @@ contract GovernanceBenchTest { // --- Build Fe contracts --- uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); - string[] memory cmd = new string[](7); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); // Read TokenContract bytecode diff --git a/examples/math/fe/src/full_math.fe b/examples/math/fe/src/full_math.fe index c8e649e..ba0ecdf 100644 --- a/examples/math/fe/src/full_math.fe +++ b/examples/math/fe/src/full_math.fe @@ -15,12 +15,16 @@ pub fn mul_div(a: u256, b: u256, denominator: u256) -> u256 { // Simple case: result fits in 256 bits if prod1 == 0 { - if denominator == 0 { assert(false) } + if denominator == 0 { + assert!(false) + } return prod0 / denominator } // Overflow check - if denominator <= prod1 { assert(false) } + if denominator <= prod1 { + assert!(false) + } // Subtract remainder to make division exact let remainder = mulmod(a, b, denominator) @@ -53,7 +57,9 @@ pub fn mul_div(a: u256, b: u256, denominator: u256) -> u256 { pub fn mul_div_rounding_up(a: u256, b: u256, denominator: u256) -> u256 { let mut result = mul_div(a, b, denominator) if mulmod(a, b, denominator) > 0 { - if result == 0 - 1 { assert(false) } + if result == 0 - 1 { + assert!(false) + } result = result + 1 } result diff --git a/examples/math/test/FullMathBench.t.sol b/examples/math/test/FullMathBench.t.sol index fbb2286..e0a5518 100644 --- a/examples/math/test/FullMathBench.t.sol +++ b/examples/math/test/FullMathBench.t.sol @@ -46,14 +46,12 @@ contract FullMathBenchTest { uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); require(optLevel <= 2, "BAD_FE_SONA_OPT_LEVEL"); - string[] memory cmd = new string[](7); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; // local math ingot + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; // local math ingot vm.ffi(cmd); // Deploy Fe contract from compiled bytecode diff --git a/examples/merkle/test/MerkleProofBench.t.sol b/examples/merkle/test/MerkleProofBench.t.sol index cf9714b..64cc8a6 100644 --- a/examples/merkle/test/MerkleProofBench.t.sol +++ b/examples/merkle/test/MerkleProofBench.t.sol @@ -32,14 +32,12 @@ contract MerkleProofBenchTest { vm.pauseGasMetering(); uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); - string[] memory cmd = new string[](7); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/poseidon/fe/src/constants.fe b/examples/poseidon/fe/src/constants.fe index 76de484..c6043c0 100644 --- a/examples/poseidon/fe/src/constants.fe +++ b/examples/poseidon/fe/src/constants.fe @@ -1,7 +1,6 @@ /// Poseidon optimized constants for T=3 (2-input) over BN254. /// Source: iden3/circomlibjs poseidon_constants_opt.json /// Uses the Filecoin/Neptune sparse matrix optimization. - /// BN254 scalar field prime pub const PRIME: u256 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 @@ -388,14 +387,38 @@ pub const S: [u256; 285] = [ /// MDS matrix (full rounds) pub const M: [[u256; 3]; 3] = [ - [0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b, 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771, 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7], - [0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0, 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23, 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911], - [0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d, 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa, 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0], + [ + 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b, + 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771, + 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7, + ], + [ + 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0, + 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23, + 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911, + ], + [ + 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d, + 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa, + 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0, + ], ] /// Pre-sparse matrix (applied before partial rounds) pub const P_MAT: [[u256; 3]; 3] = [ - [0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b, 0x1e6f20a11d1e31e43f83dcedddb9a0236203f5f24ae72c925a8a79a66831f51d, 0x1bd8c528472e57bdc722a141f8785694484f426725403ae24084e3027e782467], - [0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0, 0x2d51ba82c8073c6d6bacf1ad5e56655b7143625b0a9e9c3190527a1a5f05079a, 0x1b07d6d51e6f7e97e0ab10fc2e51ea83ce0611f940ff0731b5f927fe8d6a77c9], - [0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d, 0x11e12a40d262ae88e8376f62d19edf43093cdef1ccf34d985a3e53f0bc5765a0, 0x221c170e4d02a2479c6f3e47b5ff55781574f980d89038308a3ef37cce8463bd], + [ + 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b, + 0x1e6f20a11d1e31e43f83dcedddb9a0236203f5f24ae72c925a8a79a66831f51d, + 0x1bd8c528472e57bdc722a141f8785694484f426725403ae24084e3027e782467, + ], + [ + 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0, + 0x2d51ba82c8073c6d6bacf1ad5e56655b7143625b0a9e9c3190527a1a5f05079a, + 0x1b07d6d51e6f7e97e0ab10fc2e51ea83ce0611f940ff0731b5f927fe8d6a77c9, + ], + [ + 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d, + 0x11e12a40d262ae88e8376f62d19edf43093cdef1ccf34d985a3e53f0bc5765a0, + 0x221c170e4d02a2479c6f3e47b5ff55781574f980d89038308a3ef37cce8463bd, + ], ] diff --git a/examples/poseidon/fe/src/fp.fe b/examples/poseidon/fe/src/fp.fe index 3be100e..256eae9 100644 --- a/examples/poseidon/fe/src/fp.fe +++ b/examples/poseidon/fe/src/fp.fe @@ -2,9 +2,7 @@ use std::evm::crypto::{addmod, mulmod} use core::ops::{Add, Mul} use super::constants::PRIME -pub struct Fp { - pub val: u256, -} +pub struct Fp { pub val: u256 } impl Copy for Fp {} @@ -21,10 +19,12 @@ impl Mul for Fp { } impl Fp { - pub const fn new(val: u256) -> Fp { Fp { val } } - - pub const fn zero() -> Fp { Fp { val: 0 } } - + pub const fn new(val: u256) -> Fp { + Fp { val } + } + pub const fn zero() -> Fp { + Fp { val: 0 } + } pub const fn pow5(self) -> Fp { let x2: Fp = self * self let x4: Fp = x2 * x2 diff --git a/examples/poseidon/fe/src/lib.fe b/examples/poseidon/fe/src/lib.fe index 05bbdea..561df0a 100644 --- a/examples/poseidon/fe/src/lib.fe +++ b/examples/poseidon/fe/src/lib.fe @@ -6,7 +6,6 @@ /// All pure functions are const fn, enabling compile-time evaluation /// via CTFE. The static_asserts below verify known-answer vectors at /// compile time with zero gas cost. - pub use constants::{PRIME, C, S, M, P_MAT, N_ROUNDS_F, N_ROUNDS_P} pub use fp::Fp @@ -54,7 +53,9 @@ pub const fn hash(a: u256, b: u256) -> u256 { while r < N_ROUNDS_P { s0 = s0.pow5() + Fp::new(val: C[(N_ROUNDS_F / 2 + 1) * 3 + r]) let off: usize = 5 * r - let new_s0 = Fp::new(val: S[off]) * s0 + Fp::new(val: S[off + 1]) * s1 + Fp::new(val: S[off + 2]) * s2 + let new_s0 = Fp::new(val: S[off]) * s0 + + Fp::new(val: S[off + 1]) * s1 + + Fp::new(val: S[off + 2]) * s2 s1 = s1 + s0 * Fp::new(val: S[off + 3]) s2 = s2 + s0 * Fp::new(val: S[off + 4]) s0 = new_s0 @@ -64,7 +65,13 @@ pub const fn hash(a: u256, b: u256) -> u256 { // Second half full rounds let mut r: usize = 0 while r < N_ROUNDS_F / 2 - 1 { - let (r0, r1, r2) = full_round(s0, s1, s2, rc: (N_ROUNDS_F / 2 + 1) * 3 + N_ROUNDS_P + r * 3, mat: M) + let (r0, r1, r2) = full_round( + s0, + s1, + s2, + rc: (N_ROUNDS_F / 2 + 1) * 3 + N_ROUNDS_P + r * 3, + mat: M, + ) s0 = r0 s1 = r1 s2 = r2 @@ -80,6 +87,9 @@ pub const fn hash(a: u256, b: u256) -> u256 { } // Known-answer vectors from circomlibjs buildPoseidon(), verified at compile time. -static_assert(hash(a: 0, b: 0) == 0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864) -static_assert(hash(a: 1, b: 2) == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a) -static_assert(hash(a: 42, b: 17) == 0x18ddf7be412cfc792364a46eba440316dbb2427346063b747899e6ee0c3aa214) +static_assert(hash(a: 0, b: 0) + == 0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864) +static_assert(hash(a: 1, b: 2) + == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a) +static_assert(hash(a: 42, b: 17) + == 0x18ddf7be412cfc792364a46eba440316dbb2427346063b747899e6ee0c3aa214) diff --git a/examples/poseidon/test/PoseidonBench.t.sol b/examples/poseidon/test/PoseidonBench.t.sol index 22d1f4e..bf7ed19 100644 --- a/examples/poseidon/test/PoseidonBench.t.sol +++ b/examples/poseidon/test/PoseidonBench.t.sol @@ -24,15 +24,13 @@ contract PoseidonBenchTest { vm.pauseGasMetering(); // Deploy Fe Poseidon - uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(0)); - string[] memory cmd = new string[](7); + uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/reverts/README.md b/examples/reverts/README.md new file mode 100644 index 0000000..5570e4c --- /dev/null +++ b/examples/reverts/README.md @@ -0,0 +1,19 @@ +# Revert output + +This example demonstrates Fe contract reverts that Foundry decodes like standard Solidity payloads: + +- checked arithmetic overflow emits `Panic(0x11)`, displayed as `panic: arithmetic underflow or overflow (0x11)` +- `assert!(false)` emits `Panic(0x01)`, displayed as `panic: assertion failed (0x01)` +- `assert!(false, "boom")` emits `Error(string)`, displayed as `boom` + +Run: + +```bash +FE_BIN="$HOME/code/fe/master/target/release/fe" forge test -vv +``` + +To see Foundry's decoded failure output directly, run the opt-in display tests. These intentionally fail: + +```bash +FE_BIN="$HOME/code/fe/master/target/release/fe" forge test --match-test test_display --no-match-test '^$' -vvv +``` diff --git a/examples/reverts/fe/fe.toml b/examples/reverts/fe/fe.toml new file mode 100644 index 0000000..54cb9c2 --- /dev/null +++ b/examples/reverts/fe/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "reverts" +version = "0.1.0" diff --git a/examples/reverts/fe/src/lib.fe b/examples/reverts/fe/src/lib.fe new file mode 100644 index 0000000..83941e1 --- /dev/null +++ b/examples/reverts/fe/src/lib.fe @@ -0,0 +1,29 @@ +use std::abi::sol + +msg RevertOutputMsg { + #[selector = sol("checkedOverflow()")] + CheckedOverflow, + #[selector = sol("failedAssert()")] + FailedAssert, + #[selector = sol("failedAssertMsg()")] + FailedAssertMsg, +} + +pub contract RevertOutput { + init() {} + + recv RevertOutputMsg { + CheckedOverflow {} { + let x: u8 = 255 + let y: u8 = x + 1 + } + + FailedAssert {} { + assert!(false) + } + + FailedAssertMsg {} { + assert!(false, "boom") + } + } +} diff --git a/examples/reverts/foundry.toml b/examples/reverts/foundry.toml new file mode 100644 index 0000000..03f580c --- /dev/null +++ b/examples/reverts/foundry.toml @@ -0,0 +1,11 @@ +[profile.default] +test = "test" +ffi = true +auto_detect_solc = true +optimizer = true +optimizer_runs = 200 +no_match_test = "test_display_" + +fs_permissions = [ + { access = "read", path = "/" }, +] diff --git a/examples/reverts/test/RevertOutput.t.sol b/examples/reverts/test/RevertOutput.t.sol new file mode 100644 index 0000000..6277366 --- /dev/null +++ b/examples/reverts/test/RevertOutput.t.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface Vm { + function ffi(string[] calldata) external returns (bytes memory); + function envOr(string calldata name, string calldata defaultValue) external returns (string memory); + function expectRevert(bytes calldata revertData) external; +} + +interface IRevertOutput { + function checkedOverflow() external; + function failedAssert() external; + function failedAssertMsg() external; +} + +contract RevertOutputTest { + address private constant HEVM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); + Vm private constant vm = Vm(HEVM_ADDRESS); + + bytes4 private constant PANIC_SELECTOR = 0x4e487b71; + bytes4 private constant ERROR_SELECTOR = 0x08c379a0; + + IRevertOutput private fe; + + function setUp() public { + string[] memory buildCmd = new string[](3); + buildCmd[0] = "bash"; + buildCmd[1] = "-lc"; + buildCmd[2] = string.concat( + "fe_bin=${FE_BIN:-}; ", + "if { [ -z \"$fe_bin\" ] || [ \"$fe_bin\" = fe ]; } ", + "&& [ -x \"$HOME/code/fe/master/target/release/fe\" ]; then ", + "fe_bin=\"$HOME/code/fe/master/target/release/fe\"; ", + "fi; ", + "\"${fe_bin:-fe}\" build fe" + ); + vm.ffi(buildCmd); + + string[] memory readCmd = new string[](3); + readCmd[0] = "bash"; + readCmd[1] = "-c"; + readCmd[2] = "printf '0x'; tr -d '\\n' < fe/out/RevertOutput.bin"; + bytes memory initcode = vm.ffi(readCmd); + + address deployed; + assembly { + deployed := create(0, add(initcode, 0x20), mload(initcode)) + } + require(deployed != address(0), "Fe deploy failed"); + fe = IRevertOutput(deployed); + } + + function test_fe_checked_overflow_reverts_with_solidity_panic() public { + vm.expectRevert(abi.encodeWithSelector(PANIC_SELECTOR, uint256(0x11))); + fe.checkedOverflow(); + } + + function test_fe_assert_reverts_with_solidity_panic() public { + vm.expectRevert(abi.encodeWithSelector(PANIC_SELECTOR, uint256(0x01))); + fe.failedAssert(); + } + + function test_fe_assert_msg_reverts_with_solidity_error_string() public { + vm.expectRevert(abi.encodeWithSelector(ERROR_SELECTOR, "boom")); + fe.failedAssertMsg(); + } + + function test_display_fe_checked_overflow() public { + fe.checkedOverflow(); + } + + function test_display_fe_assert() public { + fe.failedAssert(); + } + + function test_display_fe_assert_msg() public { + fe.failedAssertMsg(); + } +} diff --git a/examples/verifier/fe/src/circuit.fe b/examples/verifier/fe/src/circuit.fe index 5f055bd..533442c 100644 --- a/examples/verifier/fe/src/circuit.fe +++ b/examples/verifier/fe/src/circuit.fe @@ -6,7 +6,6 @@ /// /// A code generator targeting Fe would emit ONE of these — ~50 lines of /// const declarations — instead of a 2000-line assembly monolith. - /// Describes a Halo2-style circuit's shape for verification. pub struct CircuitShape { /// Number of advice columns (prover witness). @@ -41,10 +40,7 @@ const fn openvm_circuit() -> CircuitShape { /// Verification key as const data. /// In Solidity: 26 hex constants scattered across 2036 lines. /// In Fe: a struct literal. -pub struct VKPoint { - pub x: u256, - pub y: u256, -} +pub struct VKPoint { pub x: u256, pub y: u256 } pub struct Halo2CircuitVK { /// Transcript initialization constant. @@ -54,7 +50,8 @@ pub struct Halo2CircuitVK { /// Permutation commitments. pub permutation_comms: [VKPoint; 4], /// KZG SRS G2 point ([x]_2). - pub g2_x: [u256; 4], // x_imag, x_real, y_imag, y_real + pub g2_x: [u256; 4], + // x_imag, x_real, y_imag, y_real } /// The OpenVM verification key — the 26 unique constants from Halo2Verifier.sol, diff --git a/examples/verifier/fe/src/field.fe b/examples/verifier/fe/src/field.fe index 32a56a1..07488b3 100644 --- a/examples/verifier/fe/src/field.fe +++ b/examples/verifier/fe/src/field.fe @@ -11,7 +11,6 @@ pub const F_Q: u256 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593 /// compile-time parameter: Field

and Field are distinct types /// that the compiler monomorphizes into the same addmod/mulmod opcodes, /// but you can't accidentally cross them. - pub const fn add(_ a: u256, _ b: u256) -> u256 { addmod(a, b, MODULUS) } @@ -26,7 +25,13 @@ pub const fn sub(_ a: u256, _ b: u256) -> u256 { /// Modular exponentiation via the modexp precompile (address 0x05). pub fn exp(_ base: u256, _ exponent: u256) -> u256 { - modexp(base, exponent, MODULUS) + match modexp(base, exp: exponent, modulus: MODULUS) { + Result::Ok(value,) => value, + Result::Err(_,) => { + assert!(false) + 0 + }, + } } /// Modular inverse: a^(-1) mod MODULUS via Fermat's little theorem. diff --git a/examples/verifier/fe/src/groth16.fe b/examples/verifier/fe/src/groth16.fe index cb3b192..600faa5 100644 --- a/examples/verifier/fe/src/groth16.fe +++ b/examples/verifier/fe/src/groth16.fe @@ -12,15 +12,12 @@ use std::evm::Evm /// Fe version: the verifier is a reusable function parameterized by a /// VerifyingKey struct. The same code works for any circuit — swap the /// key, not the verifier. EC operations use the bn254 module. - /// A validated scalar field element. Guaranteed < SCALAR_FIELD on construction. -pub struct ScalarInput { - pub val: u256, -} +pub struct ScalarInput { pub val: u256 } impl ScalarInput { pub fn new(val: u256) -> ScalarInput { - assert_msg(val < F_Q, "input exceeds scalar field") + assert!(val < F_Q, "input exceeds scalar field") ScalarInput { val } } } @@ -35,18 +32,20 @@ pub struct VerifyingKey { pub ic: [G1Point; IC_LEN], } -pub struct Proof { - pub a: G1Point, - pub b: G2Point, - pub c: G1Point, -} +pub struct Proof { pub a: G1Point, pub b: G2Point, pub c: G1Point } /// Verify a Groth16 proof with N public inputs. /// Inputs are validated on construction (ScalarInput::new asserts < SCALAR_FIELD). -pub fn verify(vk: VerifyingKey, proof: Proof, inputs: [ScalarInput; N]) -> bool uses (evm: mut Evm) { +pub fn verify( + vk: VerifyingKey, + proof: Proof, + inputs: [ScalarInput; N], +) -> bool +uses (evm: mut Evm) +{ // Compute vk_x = IC[0] + sum(inputs[i] * IC[i+1]) let mut vk_x = vk.ic[0] - for i in 0..N { + for i in 0 .. N { vk_x = vk_x + vk.ic[i + 1].scale(s: inputs[i].val) } @@ -55,9 +54,13 @@ pub fn verify(vk: VerifyingKey, // Pairing check: e(-A, B) * e(alpha, beta) * e(vk_x, gamma) * e(C, delta) == 1 pairing_4( - a1: neg_a, b1: proof.b, - a2: vk.alpha1, b2: vk.beta2, - a3: vk_x, b3: vk.gamma2, - a4: proof.c, b4: vk.delta2, + a1: neg_a, + b1: proof.b, + a2: vk.alpha1, + b2: vk.beta2, + a3: vk_x, + b3: vk.gamma2, + a4: proof.c, + b4: vk.delta2, ) } diff --git a/examples/verifier/fe/src/halo2.fe b/examples/verifier/fe/src/halo2.fe index 7b61e5e..b42650c 100644 --- a/examples/verifier/fe/src/halo2.fe +++ b/examples/verifier/fe/src/halo2.fe @@ -13,7 +13,6 @@ use std::evm::crypto::{mulmod, addmod} use std::evm::{Evm, RawOps} /// BN254 scalar field — for all polynomial evaluations and challenges. - /// Halo2/KZG verification key. /// /// In Solidity: 24 hardcoded hex constants inlined at the top of a 2036-line @@ -24,10 +23,8 @@ pub struct Halo2VK { /// Fixed commitments from the circuit (preprocessed polynomials). /// The number of these varies by circuit, but the verifier logic is the same. pub fixed_comms: [G1Point; 1], - /// Permutation commitments. pub permutation_comms: [G1Point; 4], - /// The constant term embedded in the transcript initialization. pub transcript_init: u256, } @@ -42,31 +39,22 @@ pub struct Halo2VK { pub struct Halo2Proof { /// Evaluations of instance (public input) polynomials at challenge point. pub instance_evals: [u256; 1], - /// Advice column commitments (prover's witness). pub advice_comms: [G1Point; 5], - /// Evaluations of advice polynomials at challenge point. pub advice_evals: [u256; 14], - /// Evaluations of fixed polynomials at challenge point. pub fixed_evals: [u256; 2], - /// Permutation product commitments. pub permutation_z_comms: [G1Point; 4], - /// Evaluations of permutation polynomials. pub permutation_evals: [u256; 12], - /// Vanishing argument commitment. pub vanishing_random_comm: G1Point, - /// Quotient polynomial commitments. pub h_comms: [G1Point; 4], - /// Random evaluation. pub random_eval: u256, - /// KZG opening proofs. pub w_x: G1Point, pub w_g: G1Point, @@ -80,9 +68,11 @@ pub struct Halo2Proof { /// ... /// /// In Fe: a loop. -pub fn read_field_elements_from_calldata(offset: u256, count: usize) -> [u256; 32] uses (evm: mut Evm) { +pub fn read_field_elements_from_calldata(offset: u256, count: usize) -> [u256; 32] +uses (evm: mut Evm) +{ let mut elems: [u256; 32] = [0; 32] - for i in 0..count { + for i in 0 .. count { let raw = evm.calldataload(offset + (i as u256) * 32) elems[i] = raw % F_Q } @@ -120,21 +110,21 @@ pub fn validate_ec_point(p: G1Point) -> bool { /// ... /// /// In Fe: append to transcript, squeeze. -pub fn derive_challenges( - vk: Halo2VK, proof: Halo2Proof, -) -> (u256, u256, u256, u256, u256) uses (evm: mut Evm) { +pub fn derive_challenges(vk: Halo2VK, proof: Halo2Proof) -> (u256, u256, u256, u256, u256) +uses (evm: mut Evm) +{ let mut t = Transcript::new() // Initialize with VK constant t = t.write_u256(val: vk.transcript_init) // Append instance evaluations - for i in 0..1 { + for i in 0 .. 1 { t = t.write_u256(val: proof.instance_evals[i]) } // Append advice commitments - for i in 0..5 { + for i in 0 .. 5 { t = t.write_g1(p: proof.advice_comms[i]) } @@ -150,13 +140,13 @@ pub fn derive_challenges( let (gamma, _, mut t) = t.squeeze() // Append permutation commitments for alpha - for i in 0..4 { + for i in 0 .. 4 { t = t.write_g1(p: proof.permutation_z_comms[i]) } let (alpha, _, mut t) = t.squeeze() // Append quotient commitments for x (evaluation point) - for i in 0..4 { + for i in 0 .. 4 { t = t.write_g1(p: proof.h_comms[i]) } let (x, _, _) = t.squeeze() @@ -172,10 +162,7 @@ pub fn derive_challenges( /// success := and(eq(staticcall(gas(), 0x6, ...), 1), success) // ecAdd /// /// In Fe: msm::msm() — one function call. -pub fn kzg_accumulate( - commitments: [G1Point; 21], - scalars: [u256; 21], -) -> G1Point { +pub fn kzg_accumulate(commitments: [G1Point; 21], scalars: [u256; 21]) -> G1Point { msm::msm<21>(commitments, scalars) } @@ -185,10 +172,9 @@ pub fn kzg_accumulate( /// hand-laid-out memory. /// /// In Fe: use our bn254 pairing primitive. -pub fn kzg_pairing_check( - p: G1Point, q: G1Point, - g2_srs: G2Point, -) -> bool uses (evm: mut Evm) { +pub fn kzg_pairing_check(p: G1Point, q: G1Point, g2_srs: G2Point) -> bool +uses (evm: mut Evm) +{ // e(-P, [x]_2) * e(Q, G2_generator) == 1 let neg_p = negate(p: p) diff --git a/examples/verifier/fe/src/msm.fe b/examples/verifier/fe/src/msm.fe index aa9361f..ebd75c8 100644 --- a/examples/verifier/fe/src/msm.fe +++ b/examples/verifier/fe/src/msm.fe @@ -3,7 +3,7 @@ use ec::bn254::G1Point /// Multi-scalar multiplication: sum(scalars[i] * points[i]). pub fn msm(_ points: [G1Point; N], _ scalars: [u256; N]) -> G1Point { let mut acc = points[0].scale(s: scalars[0]) - for i in 1..N { + for i in 1 .. N { acc = acc + points[i].scale(s: scalars[i]) } acc diff --git a/examples/verifier/fe/src/plonk.fe b/examples/verifier/fe/src/plonk.fe index 14519d4..1ff12c3 100644 --- a/examples/verifier/fe/src/plonk.fe +++ b/examples/verifier/fe/src/plonk.fe @@ -9,13 +9,15 @@ use std::evm::{Evm, RawMem} /// Plonk proof verification on BN254. /// Ported from SP1's Rust verifier (crates/verifier/src/plonk/). /// Rust: 904 lines across 4 files. Solidity: 1341 lines of assembly. - -// Fiat-Shamir challenge labels (ASCII, left-aligned in u256) -const GAMMA_LABEL: u256 = 0x67616d6d61000000000000000000000000000000000000000000000000000000 // "gamma" -const BETA_LABEL: u256 = 0x6265746100000000000000000000000000000000000000000000000000000000 // "beta" -const ALPHA_LABEL: u256 = 0x616c706861000000000000000000000000000000000000000000000000000000 // "alpha" -const ZETA_LABEL: u256 = 0x7a65746100000000000000000000000000000000000000000000000000000000 // "zeta" - + // Fiat-Shamir challenge labels (ASCII, left-aligned in u256) +const GAMMA_LABEL: u256 = 0x67616d6d61000000000000000000000000000000000000000000000000000000 +// "gamma" +const BETA_LABEL: u256 = 0x6265746100000000000000000000000000000000000000000000000000000000 +// "beta" +const ALPHA_LABEL: u256 = 0x616c706861000000000000000000000000000000000000000000000000000000 +// "alpha" +const ZETA_LABEL: u256 = 0x7a65746100000000000000000000000000000000000000000000000000000000 +// "zeta" pub struct PlonkVK { pub n: u256, @@ -29,23 +31,31 @@ pub struct PlonkVK { pub qo: G1Point, pub qk: G1Point, pub s: [G1Point; 3], - pub qcp: [G1Point; 1], // BSB22 custom gate commitments (SP1 has 1) + pub qcp: [G1Point; 1], + // BSB22 custom gate commitments (SP1 has 1) pub commit_constraint_idx: [usize; 1], - pub g2_srs: [G2Point; 2], // [G2, [alpha]G2] - pub g1_srs: G1Point, // G1 generator from SRS + pub g2_srs: [G2Point; 2], + // [G2, [alpha]G2] + pub g1_srs: G1Point, + // G1 generator from SRS } pub struct PlonkProof { - pub lro: [G1Point; 3], // [L], [R], [O] - pub z: G1Point, // grand product - pub h: [G1Point; 3], // quotient [H0], [H1], [H2] + pub lro: [G1Point; 3], + // [L], [R], [O] + pub z: G1Point, + // grand product + pub h: [G1Point; 3], + // quotient [H0], [H1], [H2] pub bsb22_commitments: [G1Point; 1], // Batched opening proof pub batched_h: G1Point, - pub claimed_values: [u256; 6], // l, r, o, s1, s2, qcp[0] + pub claimed_values: [u256; 6], + // l, r, o, s1, s2, qcp[0] // Shifted opening proof pub z_shifted_h: G1Point, - pub z_shifted_value: u256, // z(omega*zeta) + pub z_shifted_value: u256, + // z(omega*zeta) } // Transcript with Writable trait is in transcript.fe. @@ -56,20 +66,33 @@ pub struct PlonkProof { // This is the gnark hash_fr function from the Solidity verifier. /// DST "BSB22-Plonk" as individual bytes for mstore8 -const HASH_FR_LEN_IN_BYTES: u256 = 48 // 0x30 -const HASH_FR_SIZE_DOMAIN: u256 = 11 // length of "BSB22-Plonk" -const HASH_FR_BB: u256 = 340282366920938463463374607431768211456 // 2^128 +const HASH_FR_LEN_IN_BYTES: u256 = 48 +// 0x30 +const HASH_FR_SIZE_DOMAIN: u256 = 11 +// length of "BSB22-Plonk" +const HASH_FR_BB: u256 = 340282366920938463463374607431768211456 +// 2^128 + +fn sha256_word(offset: u256, len: u256) -> u256 { + match sha256(offset, len) { + Result::Ok(value,) => value, + Result::Err(_,) => { + assert!(false) + 0 + }, + } +} fn hash_fr(_ x: u256, _ y: u256) -> u256 uses (evm: mut Evm) { let ptr = std::evm::alloc(256) // Round 0: SHA256(zeros64 || x || y || 0x00 || 0x30 || 0x00 || "BSB22-Plonk" || 0x0b) - evm.mstore(addr: ptr, value: 0) // 32 zero bytes - evm.mstore(addr: ptr + 32, value: 0) // 32 zero bytes + evm.mstore(addr: ptr, value: 0) // 32 zero bytes + evm.mstore(addr: ptr + 32, value: 0) // 32 zero bytes evm.mstore(addr: ptr + 64, value: x) evm.mstore(addr: ptr + 96, value: y) evm.mstore8(addr: ptr + 128, value: 0) - evm.mstore8(addr: ptr + 129, value: 48) // HASH_FR_LEN_IN_BYTES + evm.mstore8(addr: ptr + 129, value: 48) // HASH_FR_LEN_IN_BYTES evm.mstore8(addr: ptr + 130, value: 0) // "BSB22-Plonk" = 42 53 42 32 32 2d 50 6c 6f 6e 6b evm.mstore8(addr: ptr + 131, value: 0x42) @@ -83,13 +106,13 @@ fn hash_fr(_ x: u256, _ y: u256) -> u256 uses (evm: mut Evm) { evm.mstore8(addr: ptr + 139, value: 0x6f) evm.mstore8(addr: ptr + 140, value: 0x6e) evm.mstore8(addr: ptr + 141, value: 0x6b) - evm.mstore8(addr: ptr + 142, value: 11) // HASH_FR_SIZE_DOMAIN + evm.mstore8(addr: ptr + 142, value: 11) // HASH_FR_SIZE_DOMAIN - let b0 = sha256(ptr, 143) + let b0 = sha256_word(offset: ptr, len: 143) // Round 1: SHA256(b0 || 0x01 || "BSB22-Plonk" || 0x0b) evm.mstore(addr: ptr, value: b0) - evm.mstore8(addr: ptr + 32, value: 1) // HASH_FR_ONE + evm.mstore8(addr: ptr + 32, value: 1) // HASH_FR_ONE evm.mstore8(addr: ptr + 33, value: 0x42) evm.mstore8(addr: ptr + 34, value: 0x53) evm.mstore8(addr: ptr + 35, value: 0x42) @@ -103,11 +126,11 @@ fn hash_fr(_ x: u256, _ y: u256) -> u256 uses (evm: mut Evm) { evm.mstore8(addr: ptr + 43, value: 0x6b) evm.mstore8(addr: ptr + 44, value: 11) - let b1 = sha256(ptr, 45) + let b1 = sha256_word(offset: ptr, len: 45) // Round 2: SHA256((b0 ^ b1) || 0x02 || "BSB22-Plonk" || 0x0b) evm.mstore(addr: ptr, value: b0 ^ b1) - evm.mstore8(addr: ptr + 32, value: 2) // HASH_FR_TWO + evm.mstore8(addr: ptr + 32, value: 2) // HASH_FR_TWO evm.mstore8(addr: ptr + 33, value: 0x42) evm.mstore8(addr: ptr + 34, value: 0x53) evm.mstore8(addr: ptr + 35, value: 0x42) @@ -121,10 +144,10 @@ fn hash_fr(_ x: u256, _ y: u256) -> u256 uses (evm: mut Evm) { evm.mstore8(addr: ptr + 43, value: 0x6b) evm.mstore8(addr: ptr + 44, value: 11) - let b2 = sha256(ptr, 45) + let b2 = sha256_word(offset: ptr, len: 45) // Result: 2^128 * b1 + b2[:16] mod R - let b2_high = b2 >> 128 // top 16 bytes of b2 + let b2_high = b2 >> 128 // top 16 bytes of b2 addmod(mulmod(b1, HASH_FR_BB, F_Q), b2_high, F_Q) } @@ -147,96 +170,99 @@ fn batch_invert_2(_ a: u256, _ b: u256) -> (u256, u256) { /// Debug: compute and return challenge hashes for comparison. /// Returns (gamma_raw, beta_raw, alpha_raw, zeta_raw) -pub fn debug_challenges( - vk: own PlonkVK, proof: own PlonkProof, - pub_inputs: [u256; 5], -) -> (u256, u256, u256, u256) uses (evm: mut Evm) { +pub fn debug_challenges(vk: own PlonkVK, proof: own PlonkProof, pub_inputs: [u256; 5]) -> ( + u256, + u256, + u256, + u256, +) +uses (evm: mut Evm) +{ let mut t = Transcript::new() // gamma t = t.write_label(label: GAMMA_LABEL, label_len: 5) - t = t.write_g1(p:vk.s[0]) - t = t.write_g1(p:vk.s[1]) - t = t.write_g1(p:vk.s[2]) - t = t.write_g1(p:vk.ql) - t = t.write_g1(p:vk.qr) - t = t.write_g1(p:vk.qm) - t = t.write_g1(p:vk.qo) - t = t.write_g1(p:vk.qk) - t = t.write_g1(p:vk.qcp[0]) + t = t.write_g1(p: vk.s[0]) + t = t.write_g1(p: vk.s[1]) + t = t.write_g1(p: vk.s[2]) + t = t.write_g1(p: vk.ql) + t = t.write_g1(p: vk.qr) + t = t.write_g1(p: vk.qm) + t = t.write_g1(p: vk.qo) + t = t.write_g1(p: vk.qk) + t = t.write_g1(p: vk.qcp[0]) let mut pi_idx: usize = 0 while pi_idx < 5 { - t = t.write_u256(val:pub_inputs[pi_idx]) + t = t.write_u256(val: pub_inputs[pi_idx]) pi_idx = pi_idx + 1 } - t = t.write_g1(p:proof.lro[0]) - t = t.write_g1(p:proof.lro[1]) - t = t.write_g1(p:proof.lro[2]) + t = t.write_g1(p: proof.lro[0]) + t = t.write_g1(p: proof.lro[1]) + t = t.write_g1(p: proof.lro[2]) let (_, gamma_raw, mut t) = t.squeeze() // beta t = t.write_label(label: BETA_LABEL, label_len: 4) - t = t.write_u256(val:gamma_raw) + t = t.write_u256(val: gamma_raw) let (_, beta_raw, mut t) = t.squeeze() // alpha t = t.write_label(label: ALPHA_LABEL, label_len: 5) - t = t.write_u256(val:beta_raw) - t = t.write_g1(p:proof.bsb22_commitments[0]) - t = t.write_g1(p:proof.z) + t = t.write_u256(val: beta_raw) + t = t.write_g1(p: proof.bsb22_commitments[0]) + t = t.write_g1(p: proof.z) let (_, alpha_raw, mut t) = t.squeeze() // zeta t = t.write_label(label: ZETA_LABEL, label_len: 4) - t = t.write_u256(val:alpha_raw) - t = t.write_g1(p:proof.h[0]) - t = t.write_g1(p:proof.h[1]) - t = t.write_g1(p:proof.h[2]) + t = t.write_u256(val: alpha_raw) + t = t.write_g1(p: proof.h[0]) + t = t.write_g1(p: proof.h[1]) + t = t.write_g1(p: proof.h[2]) let (_, zeta_raw, t) = t.squeeze() (gamma_raw, beta_raw, alpha_raw, zeta_raw) } /// Debug: return (pi_total, const_lin) for verification debugging. -pub fn debug_pi( - vk: own PlonkVK, proof: own PlonkProof, - pub_inputs: [u256; 5], -) -> (u256, u256) uses (evm: mut Evm) { +pub fn debug_pi(vk: own PlonkVK, proof: own PlonkProof, pub_inputs: [u256; 5]) -> (u256, u256) +uses (evm: mut Evm) +{ let one: u256 = 1 // Reuse the challenge derivation from verify let mut t = Transcript::new() t = t.write_label(label: GAMMA_LABEL, label_len: 5) - t = t.write_g1(p:vk.s[0]) - t = t.write_g1(p:vk.s[1]) - t = t.write_g1(p:vk.s[2]) - t = t.write_g1(p:vk.ql) - t = t.write_g1(p:vk.qr) - t = t.write_g1(p:vk.qm) - t = t.write_g1(p:vk.qo) - t = t.write_g1(p:vk.qk) - t = t.write_g1(p:vk.qcp[0]) + t = t.write_g1(p: vk.s[0]) + t = t.write_g1(p: vk.s[1]) + t = t.write_g1(p: vk.s[2]) + t = t.write_g1(p: vk.ql) + t = t.write_g1(p: vk.qr) + t = t.write_g1(p: vk.qm) + t = t.write_g1(p: vk.qo) + t = t.write_g1(p: vk.qk) + t = t.write_g1(p: vk.qcp[0]) let mut pi_idx: usize = 0 while pi_idx < 5 { - t = t.write_u256(val:pub_inputs[pi_idx]) + t = t.write_u256(val: pub_inputs[pi_idx]) pi_idx = pi_idx + 1 } - t = t.write_g1(p:proof.lro[0]) - t = t.write_g1(p:proof.lro[1]) - t = t.write_g1(p:proof.lro[2]) + t = t.write_g1(p: proof.lro[0]) + t = t.write_g1(p: proof.lro[1]) + t = t.write_g1(p: proof.lro[2]) let (gamma, gamma_raw, mut t) = t.squeeze() t = t.write_label(label: BETA_LABEL, label_len: 4) - t = t.write_u256(val:gamma_raw) + t = t.write_u256(val: gamma_raw) let (beta, beta_raw, mut t) = t.squeeze() t = t.write_label(label: ALPHA_LABEL, label_len: 5) - t = t.write_u256(val:beta_raw) - t = t.write_g1(p:proof.bsb22_commitments[0]) - t = t.write_g1(p:proof.z) + t = t.write_u256(val: beta_raw) + t = t.write_g1(p: proof.bsb22_commitments[0]) + t = t.write_g1(p: proof.z) let (alpha, alpha_raw, mut t) = t.squeeze() t = t.write_label(label: ZETA_LABEL, label_len: 4) - t = t.write_u256(val:alpha_raw) - t = t.write_g1(p:proof.h[0]) - t = t.write_g1(p:proof.h[1]) - t = t.write_g1(p:proof.h[2]) + t = t.write_u256(val: alpha_raw) + t = t.write_g1(p: proof.h[0]) + t = t.write_g1(p: proof.h[1]) + t = t.write_g1(p: proof.h[2]) let (zeta, zeta_raw, _) = t.squeeze() // Compute PI @@ -248,7 +274,10 @@ pub fn debug_pi( while i < 5 { let den = field::sub(zeta, omega_i) let den_inv = field::inv(den) - let li = field::mul(field::mul(field::mul(zh_zeta, omega_i), den_inv), vk.n_inv) + let li = field::mul( + field::mul(field::mul(zh_zeta, omega_i), den_inv), + vk.n_inv, + ) pi = field::add(pi, field::mul(li, pub_inputs[i])) omega_i = field::mul(omega_i, vk.omega) i = i + 1 @@ -261,7 +290,7 @@ pub fn debug_pi( let bsb_den_inv = field::inv(bsb_den) let bsb_lagrange = field::mul( field::mul(field::mul(zh_zeta, omega_i), bsb_den_inv), - vk.n_inv + vk.n_inv, ) pi = field::add(pi, field::mul(h_fr, bsb_lagrange)) @@ -271,10 +300,9 @@ pub fn debug_pi( /// Verify a Plonk proof with 2 public inputs (SP1 standard). /// Follows SP1 verify_plonk_algebraic() from verify.rs. -pub fn verify( - vk: own PlonkVK, proof: own PlonkProof, - pub_inputs: [u256; 5], -) -> bool uses (evm: mut Evm) { +pub fn verify(vk: own PlonkVK, proof: own PlonkProof, pub_inputs: [u256; 5]) -> bool +uses (evm: mut Evm) +{ let one: u256 = 1 // --- 1. Fiat-Shamir challenge derivation --- @@ -286,44 +314,44 @@ pub fn verify( // gamma: bind VK public data + public inputs, then commit wire polys // Hash("gamma" || s1 || s2 || s3 || ql || qr || qm || qo || qk || qcp[0] || pub_inputs || [L] || [R] || [O]) t = t.write_label(label: GAMMA_LABEL, label_len: 5) // "gamma" - t = t.write_g1(p:vk.s[0]) - t = t.write_g1(p:vk.s[1]) - t = t.write_g1(p:vk.s[2]) - t = t.write_g1(p:vk.ql) - t = t.write_g1(p:vk.qr) - t = t.write_g1(p:vk.qm) - t = t.write_g1(p:vk.qo) - t = t.write_g1(p:vk.qk) - t = t.write_g1(p:vk.qcp[0]) + t = t.write_g1(p: vk.s[0]) + t = t.write_g1(p: vk.s[1]) + t = t.write_g1(p: vk.s[2]) + t = t.write_g1(p: vk.ql) + t = t.write_g1(p: vk.qr) + t = t.write_g1(p: vk.qm) + t = t.write_g1(p: vk.qo) + t = t.write_g1(p: vk.qk) + t = t.write_g1(p: vk.qcp[0]) let mut pi_idx: usize = 0 while pi_idx < 5 { - t = t.write_u256(val:pub_inputs[pi_idx]) + t = t.write_u256(val: pub_inputs[pi_idx]) pi_idx = pi_idx + 1 } - t = t.write_g1(p:proof.lro[0]) - t = t.write_g1(p:proof.lro[1]) - t = t.write_g1(p:proof.lro[2]) + t = t.write_g1(p: proof.lro[0]) + t = t.write_g1(p: proof.lro[1]) + t = t.write_g1(p: proof.lro[2]) let (gamma, gamma_raw, mut t) = t.squeeze() // beta: SHA256("beta" || gamma_raw_hash) // Note: gnark chains the RAW 32-byte hash, not the reduced field element t = t.write_label(label: BETA_LABEL, label_len: 4) // "beta" - t = t.write_u256(val:gamma_raw) + t = t.write_u256(val: gamma_raw) let (beta, beta_raw, mut t) = t.squeeze() // alpha: SHA256("alpha" || beta_raw_hash || bsb22_commitments || Z) t = t.write_label(label: ALPHA_LABEL, label_len: 5) // "alpha" - t = t.write_u256(val:beta_raw) - t = t.write_g1(p:proof.bsb22_commitments[0]) - t = t.write_g1(p:proof.z) + t = t.write_u256(val: beta_raw) + t = t.write_g1(p: proof.bsb22_commitments[0]) + t = t.write_g1(p: proof.z) let (alpha, alpha_raw, mut t) = t.squeeze() // zeta: SHA256("zeta" || alpha_raw_hash || H0 || H1 || H2) t = t.write_label(label: ZETA_LABEL, label_len: 4) // "zeta" - t = t.write_u256(val:alpha_raw) - t = t.write_g1(p:proof.h[0]) - t = t.write_g1(p:proof.h[1]) - t = t.write_g1(p:proof.h[2]) + t = t.write_u256(val: alpha_raw) + t = t.write_g1(p: proof.h[0]) + t = t.write_g1(p: proof.h[1]) + t = t.write_g1(p: proof.h[2]) let (zeta, zeta_raw, _) = t.squeeze() // --- 2. Compute zeta^n - 1 and L1(zeta) --- @@ -333,18 +361,21 @@ pub fn verify( // L1(zeta) = (zeta^n - 1) / (n * (zeta - 1)) let l1_zeta = field::mul( field::mul(zh_zeta, field::inv(field::sub(zeta, one))), - vk.n_inv + vk.n_inv, ) // --- 3. Compute PI(zeta) = sum of L_i(zeta) * pub_inputs[i] --- // L_i(zeta) = (zh * omega^i) / (n * (zeta - omega^i)) let mut pi: u256 = 0 - let mut omega_i: u256 = 1 // omega^0 = 1 + let mut omega_i: u256 = 1 // omega^0 = 1 let mut i: usize = 0 while i < 5 { let den = field::sub(zeta, omega_i) let den_inv = field::inv(den) - let li = field::mul(field::mul(field::mul(zh_zeta, omega_i), den_inv), vk.n_inv) + let li = field::mul( + field::mul(field::mul(zh_zeta, omega_i), den_inv), + vk.n_inv, + ) pi = field::add(pi, field::mul(li, pub_inputs[i])) omega_i = field::mul(omega_i, vk.omega) i = i + 1 @@ -360,7 +391,7 @@ pub fn verify( let bsb_den_inv = field::inv(bsb_den) let bsb_lagrange = field::mul( field::mul(field::mul(zh_zeta, omega_i), bsb_den_inv), - vk.n_inv + vk.n_inv, ) pi = field::add(pi, field::mul(h_fr, bsb_lagrange)) @@ -380,15 +411,24 @@ pub fn verify( let term_a = field::add(l, field::add(field::mul(beta, s1), gamma)) let term_b = field::add(r, field::add(field::mul(beta, s2), gamma)) let term_c = field::add(o, gamma) - let perm_term = field::mul(field::mul(field::mul(term_a, term_b), term_c), field::mul(alpha, zu)) + let perm_term = field::mul( + field::mul(field::mul(term_a, term_b), term_c), + field::mul(alpha, zu), + ) let const_lin = field::neg( - field::add(field::sub(pi, alpha_sq_l1), perm_term) + field::add(field::sub(pi, alpha_sq_l1), perm_term), ) // --- 6. Linearized polynomial coefficients --- // _s1 = alpha * (l + beta*s1 + gamma) * (r + beta*s2 + gamma) * beta * zu - let coeff_s1 = field::mul(field::mul(field::mul(term_a, term_b), field::mul(beta, alpha)), zu) + let coeff_s1 = field::mul( + field::mul( + field::mul(term_a, term_b), + field::mul(beta, alpha), + ), + zu, + ) // _s2 = -alpha * (l + beta*zeta + gamma) * (r + beta*u*zeta + gamma) * (o + beta*u^2*zeta + gamma) let u_zeta = field::mul(vk.coset_shift, zeta) @@ -396,7 +436,9 @@ pub fn verify( let s2_a = field::add(l, field::add(field::mul(beta, zeta), gamma)) let s2_b = field::add(r, field::add(field::mul(beta, u_zeta), gamma)) let s2_c = field::add(o, field::add(field::mul(beta, u2_zeta), gamma)) - let coeff_s2 = field::neg(field::mul(field::mul(field::mul(s2_a, s2_b), s2_c), alpha)) + let coeff_s2 = field::neg( + field::mul(field::mul(field::mul(s2_a, s2_b), s2_c), alpha), + ) // coeff_z = alpha^2*L1(zeta) + coeff_s2 let coeff_z = field::add(alpha_sq_l1, coeff_s2) @@ -419,15 +461,29 @@ pub fn verify( let points: [G1Point; 11] = [ proof.bsb22_commitments[0], - vk.ql, vk.qr, vk.qm, vk.qo, vk.qk, - vk.s[2], proof.z, - proof.h[0], proof.h[1], proof.h[2], + vk.ql, + vk.qr, + vk.qm, + vk.qo, + vk.qk, + vk.s[2], + proof.z, + proof.h[0], + proof.h[1], + proof.h[2], ] let scalars: [u256; 11] = [ qc_val, - l, r, rl, o, one, - coeff_s1, coeff_z, - coeff_h0, coeff_h1, coeff_h2, + l, + r, + rl, + o, + one, + coeff_s1, + coeff_z, + coeff_h0, + coeff_h1, + coeff_h2, ] let linearized = msm::msm<11>(points, scalars) @@ -438,26 +494,30 @@ pub fn verify( // Derive gamma_kzg for folding let mut t2 = Transcript::new() - t2 = t2.write_label(label: 0x67616d6d61000000000000000000000000000000000000000000000000000000, label_len: 5) // "gamma" - t2 = t2.write_u256(val:zeta) + t2 = t2 + .write_label( + label: 0x67616d6d61000000000000000000000000000000000000000000000000000000, + label_len: 5, + ) // "gamma" + t2 = t2.write_u256(val: zeta) // Write all 7 digests - t2 = t2.write_g1(p:linearized) - t2 = t2.write_g1(p:proof.lro[0]) - t2 = t2.write_g1(p:proof.lro[1]) - t2 = t2.write_g1(p:proof.lro[2]) - t2 = t2.write_g1(p:vk.s[0]) - t2 = t2.write_g1(p:vk.s[1]) - t2 = t2.write_g1(p:vk.qcp[0]) + t2 = t2.write_g1(p: linearized) + t2 = t2.write_g1(p: proof.lro[0]) + t2 = t2.write_g1(p: proof.lro[1]) + t2 = t2.write_g1(p: proof.lro[2]) + t2 = t2.write_g1(p: vk.s[0]) + t2 = t2.write_g1(p: vk.s[1]) + t2 = t2.write_g1(p: vk.qcp[0]) // Write all 7 claimed values - t2 = t2.write_u256(val:const_lin) - t2 = t2.write_u256(val:l) - t2 = t2.write_u256(val:r) - t2 = t2.write_u256(val:o) - t2 = t2.write_u256(val:s1) - t2 = t2.write_u256(val:s2) - t2 = t2.write_u256(val:qc_val) + t2 = t2.write_u256(val: const_lin) + t2 = t2.write_u256(val: l) + t2 = t2.write_u256(val: r) + t2 = t2.write_u256(val: o) + t2 = t2.write_u256(val: s1) + t2 = t2.write_u256(val: s2) + t2 = t2.write_u256(val: qc_val) // Write zu (data transcript) - t2 = t2.write_u256(val:zu) + t2 = t2.write_u256(val: zu) let (gamma_kzg, _, _) = t2.squeeze() // gamma_kzg raw hash not needed (separate transcript) @@ -473,8 +533,11 @@ pub fn verify( // Folded digest = MSM(digests, gamma_kzg^i) let fold_points: [G1Point; 7] = [ linearized, - proof.lro[0], proof.lro[1], proof.lro[2], - vk.s[0], vk.s[1], + proof.lro[0], + proof.lro[1], + proof.lro[2], + vk.s[0], + vk.s[1], vk.qcp[0], ] let fold_scalars: [u256; 7] = [g0, g1, g2, g3, g4, g5, g6] @@ -496,15 +559,21 @@ pub fn verify( // then derive_randomness(fs, "u", [folded_digest, Z, batched_h, z_shifted_h]) // computes SHA256("u" || zeta_raw_hash || gamma_kzg || folded_digest || Z || batched_h || z_shifted_h) let mut t3 = Transcript::new() - t3 = t3.write_label(label: 0x7500000000000000000000000000000000000000000000000000000000000000, label_len: 1) // "u" - t3 = t3.write_u256(val:zeta_raw) // chain from previous challenge (zeta's raw hash) + t3 = t3 + .write_label( + label: 0x7500000000000000000000000000000000000000000000000000000000000000, + label_len: 1, + ) // "u" + t3 = t3.write_u256( + val: zeta_raw, + ) // chain from previous challenge (zeta's raw hash) // gamma_kzg bound to "u" challenge by fold_proof - t3 = t3.write_u256(val:gamma_kzg) + t3 = t3.write_u256(val: gamma_kzg) // derive_randomness binds these points - t3 = t3.write_g1(p:folded_digest) - t3 = t3.write_g1(p:proof.z) - t3 = t3.write_g1(p:proof.batched_h) - t3 = t3.write_g1(p:proof.z_shifted_h) + t3 = t3.write_g1(p: folded_digest) + t3 = t3.write_g1(p: proof.z) + t3 = t3.write_g1(p: proof.batched_h) + t3 = t3.write_g1(p: proof.z_shifted_h) let (u_challenge, _, _) = t3.squeeze() // --- 10. KZG batch verification --- @@ -513,30 +582,44 @@ pub fn verify( let shifted_zeta = field::mul(zeta, vk.omega) // folded_quotients = batched_h + u * z_shifted_h - let folded_quotients = g1_add(a: proof.batched_h, b: g1_mul(p: proof.z_shifted_h, s: u_challenge)) + let folded_quotients = g1_add( + a: proof.batched_h, + b: g1_mul(p: proof.z_shifted_h, s: u_challenge), + ) // folded_digests = folded_digest + u * proof.z // - folded_eval_commit // + zeta * batched_h + u * shifted_zeta * z_shifted_h - let folded_evals_commit = g1_mul(p: vk.g1_srs, s: field::add(folded_eval, field::mul(u_challenge, zu))) + let folded_evals_commit = g1_mul( + p: vk.g1_srs, + s: field::add(folded_eval, field::mul(u_challenge, zu)), + ) let point_term = g1_add( a: g1_mul(p: proof.batched_h, s: zeta), - b: g1_mul(p: proof.z_shifted_h, s: field::mul(u_challenge, shifted_zeta)) + b: g1_mul( + p: proof.z_shifted_h, + s: field::mul(u_challenge, shifted_zeta), + ), ) let lhs = g1_add( a: g1_add( a: g1_add(a: folded_digest, b: g1_mul(p: proof.z, s: u_challenge)), - b: negate(p: folded_evals_commit) + b: negate(p: folded_evals_commit), ), - b: point_term + b: point_term, ) let neg_quotients = negate(p: folded_quotients) // Pairing check: e(lhs, G2) * e(-folded_quotients, [alpha]G2) == 1 - pairing_2(a1: lhs, b1: vk.g2_srs[0], a2: neg_quotients, b2: vk.g2_srs[1]) + pairing_2( + a1: lhs, + b1: vk.g2_srs[0], + a2: neg_quotients, + b2: vk.g2_srs[1], + ) } // ----------------------------------------------------------------------- @@ -551,7 +634,9 @@ fn test_hash_fr() uses (evm: mut Evm) { let x: u256 = 0x27086e886de4f31d65e997d9dd08e35dd6590101d9185db7c8c5dd7dd9348c9c let y: u256 = 0x1b711862d9757e744014177960751f5f5a84f14bbc1b019021db0666b8e55fc3 let result = hash_fr(x, y) - assert(result == 0x11fde042bd38090174940ef4ce967657b9b334a8f80c25694311c8846320a582) + assert!( + result == 0x11fde042bd38090174940ef4ce967657b9b334a8f80c25694311c8846320a582, + ) } #[test] @@ -560,7 +645,7 @@ fn test_field_inv() { let two: u256 = 2 let inv2 = field::inv(two) let product = field::mul(two, inv2) - assert(product == 1) + assert!(product == 1) } #[test] @@ -569,7 +654,7 @@ fn test_pow_mod() { let omega: u256 = 5709868443893258075976348696661355716898495876243883251619397131511003808859 let n: u256 = 16777216 let result = pow_mod(omega, n) - assert(result == 1) + assert!(result == 1) } #[test] @@ -581,7 +666,9 @@ fn test_transcript_gamma() uses (evm: mut Evm) { // Just hash "gamma" alone to verify the label encoding let (_, gamma_only_raw, t) = t.squeeze() // SHA256("gamma") = 0xbe9d587defa1f0c09ef49eb17e206983a5f8f8289e4281860bd0ee5a19592c67 - assert(gamma_only_raw == 0xbe9d587defa1f0c09ef49eb17e206983a5f8f8289e4281860bd0ee5a19592c67) + assert!( + gamma_only_raw == 0xbe9d587defa1f0c09ef49eb17e206983a5f8f8289e4281860bd0ee5a19592c67, + ) } #[test] @@ -589,8 +676,8 @@ fn test_batch_invert() { let a: u256 = 7 let b: u256 = 13 let (a_inv, b_inv) = batch_invert_2(a, b) - assert(field::mul(a, a_inv) == 1) - assert(field::mul(b, b_inv) == 1) + assert!(field::mul(a, a_inv) == 1) + assert!(field::mul(b, b_inv) == 1) } #[test] @@ -616,7 +703,7 @@ fn test_const_lin() { let zh_zeta = field::sub(zeta_power_n, one) let l1_zeta = field::mul( field::mul(zh_zeta, field::inv(field::sub(zeta, one))), - n_inv + n_inv, ) let alpha_sq_l1 = field::mul(field::mul(alpha, alpha), l1_zeta) @@ -624,12 +711,17 @@ fn test_const_lin() { let term_a = field::add(l, field::add(field::mul(beta, s1), gamma)) let term_b = field::add(r, field::add(field::mul(beta, s2), gamma)) let term_c = field::add(o, gamma) - let perm_term = field::mul(field::mul(field::mul(term_a, term_b), term_c), field::mul(alpha, zu)) + let perm_term = field::mul( + field::mul(field::mul(term_a, term_b), term_c), + field::mul(alpha, zu), + ) let inner = field::add(field::sub(pi, alpha_sq_l1), perm_term) let const_lin = field::neg(inner) - assert(const_lin == 0x22eb75080a77724bc8dc8c0548f18bef1975fa6c302909af13c960db6c585954) + assert!( + const_lin == 0x22eb75080a77724bc8dc8c0548f18bef1975fa6c302909af13c960db6c585954, + ) } #[test] @@ -651,14 +743,25 @@ fn test_linearization_coefficients() { let zeta_power_n = pow_mod(zeta, n) let zh_zeta = field::sub(zeta_power_n, one) - let l1_zeta = field::mul(field::mul(zh_zeta, field::inv(field::sub(zeta, one))), n_inv) + let l1_zeta = field::mul( + field::mul(zh_zeta, field::inv(field::sub(zeta, one))), + n_inv, + ) let alpha_sq_l1 = field::mul(field::mul(alpha, alpha), l1_zeta) // coeff_s1 let term_a = field::add(l, field::add(field::mul(beta, s1), gamma)) let term_b = field::add(r, field::add(field::mul(beta, s2), gamma)) - let coeff_s1 = field::mul(field::mul(field::mul(term_a, term_b), field::mul(beta, alpha)), zu) - assert(coeff_s1 == 0x014e31ed4d81ae6dd405e472b9107d89e37137cb2f7e0fc5d32ac04e149f3989) + let coeff_s1 = field::mul( + field::mul( + field::mul(term_a, term_b), + field::mul(beta, alpha), + ), + zu, + ) + assert!( + coeff_s1 == 0x014e31ed4d81ae6dd405e472b9107d89e37137cb2f7e0fc5d32ac04e149f3989, + ) // coeff_s2 / coeff_z let u_zeta = field::mul(coset_shift, zeta) @@ -666,9 +769,13 @@ fn test_linearization_coefficients() { let s2_a = field::add(l, field::add(field::mul(beta, zeta), gamma)) let s2_b = field::add(r, field::add(field::mul(beta, u_zeta), gamma)) let s2_c = field::add(o, field::add(field::mul(beta, u2_zeta), gamma)) - let coeff_s2 = field::neg(field::mul(field::mul(field::mul(s2_a, s2_b), s2_c), alpha)) + let coeff_s2 = field::neg( + field::mul(field::mul(field::mul(s2_a, s2_b), s2_c), alpha), + ) let coeff_z = field::add(alpha_sq_l1, coeff_s2) - assert(coeff_z == 0x0d51bf1d4a66e52d8bb72ec963f83620301a16c69392558e1953b15c3fa555c4) + assert!( + coeff_z == 0x0d51bf1d4a66e52d8bb72ec963f83620301a16c69392558e1953b15c3fa555c4, + ) // Quotient coefficients let n_plus_two = n + 2 @@ -677,9 +784,15 @@ fn test_linearization_coefficients() { let coeff_h0 = field::neg(zh_zeta) let coeff_h1 = field::neg(field::mul(zeta_np2, zh_zeta)) let coeff_h2 = field::neg(field::mul(zeta_np2_sq, zh_zeta)) - assert(coeff_h0 == 0x2708f62ad73df8922601f3c8a66ee33858d4488264f5ada9885746cd1d181879) - assert(coeff_h1 == 0x11a7fff44d705a9129a01901a0183754b4e256d6d5f790c3b12a04daad6d7260) - assert(coeff_h2 == 0x0cd41c8f7b46057ad0ca5113f129ccb033f0db5f2bdd4b4742baf44cffd6f9cd) + assert!( + coeff_h0 == 0x2708f62ad73df8922601f3c8a66ee33858d4488264f5ada9885746cd1d181879, + ) + assert!( + coeff_h1 == 0x11a7fff44d705a9129a01901a0183754b4e256d6d5f790c3b12a04daad6d7260, + ) + assert!( + coeff_h2 == 0x0cd41c8f7b46057ad0ca5113f129ccb033f0db5f2bdd4b4742baf44cffd6f9cd, + ) } #[test] @@ -691,42 +804,111 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { omega: 5709868443893258075976348696661355716898495876243883251619397131511003808859, nb_public_inputs: 2, coset_shift: 5, - ql: G1Point { x: 2714773032566361735398260413518107570706289019141573602093747023461681138141, y: 10207220609888567477852282724812707756861966294950666667119692155077205992894 }, - qr: G1Point { x: 17919274808167168584263187859012763816365260341587621260815379357637476029962, y: 14558165337321799812085033100515533981610351056305142204990949940017867076397 }, - qm: G1Point { x: 1814703450159964740292891910795980721108620081843240976053374083376051887455, y: 11252528960397523304289223453506717847025678682133692300385063157160041127070 }, - qo: G1Point { x: 20843277058771674275997213106654908867381045039357421108797602213552545033079, y: 9646775541123942436366130063934415659078920798926708026864638413383214238671 }, - qk: G1Point { x: 5484717465597821820411103650564499774744032473047103693751158150047197753654, y: 5561799343038529497262757012400750786503050088440144551259537360162821571059 }, + ql: G1Point { + x: 2714773032566361735398260413518107570706289019141573602093747023461681138141, + y: 10207220609888567477852282724812707756861966294950666667119692155077205992894, + }, + qr: G1Point { + x: 17919274808167168584263187859012763816365260341587621260815379357637476029962, + y: 14558165337321799812085033100515533981610351056305142204990949940017867076397, + }, + qm: G1Point { + x: 1814703450159964740292891910795980721108620081843240976053374083376051887455, + y: 11252528960397523304289223453506717847025678682133692300385063157160041127070, + }, + qo: G1Point { + x: 20843277058771674275997213106654908867381045039357421108797602213552545033079, + y: 9646775541123942436366130063934415659078920798926708026864638413383214238671, + }, + qk: G1Point { + x: 5484717465597821820411103650564499774744032473047103693751158150047197753654, + y: 5561799343038529497262757012400750786503050088440144551259537360162821571059, + }, s: [ - G1Point { x: 16111562061301112215931665617877464360548491176332584512747295033804502769274, y: 15035232142063390140879951391784254536324051421746307325879221184372296043705 }, - G1Point { x: 899944321381010541211546037826620464002745326050515852312919625047231523882, y: 61717668739330555376092528203839789132705738484346798874082062974863965392 }, - G1Point { x: 9316901462569250008665217603385561854185385862824092362271612343176126127375, y: 13799900238612879579721466063922041459340434537392216736920805107993374657577 }, + G1Point { + x: 16111562061301112215931665617877464360548491176332584512747295033804502769274, + y: 15035232142063390140879951391784254536324051421746307325879221184372296043705, + }, + G1Point { + x: 899944321381010541211546037826620464002745326050515852312919625047231523882, + y: 61717668739330555376092528203839789132705738484346798874082062974863965392, + }, + G1Point { + x: 9316901462569250008665217603385561854185385862824092362271612343176126127375, + y: 13799900238612879579721466063922041459340434537392216736920805107993374657577, + }, + ], + qcp: [ + G1Point { + x: 21578473557091588309361521643625606794648013014197133181947992670819103775934, + y: 18236588362476326695195531997097392315059481348147701548685746610417604595065, + }, ], - qcp: [G1Point { x: 21578473557091588309361521643625606794648013014197133181947992670819103775934, y: 18236588362476326695195531997097392315059481348147701548685746610417604595065 }], commit_constraint_idx: [10900304], g2_srs: [ - G2Point { x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930 }, - G2Point { x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979 }, + G2Point { + x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, + x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, + y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, + y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930, + }, + G2Point { + x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, + x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, + y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, + y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979, + }, ], - g1_srs: G1Point { x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, y: 11763105256161367503191792604679297387056316997144156930871823008787082098465 }, + g1_srs: G1Point { + x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, + y: 11763105256161367503191792604679297387056316997144156930871823008787082098465, + }, } // Construct the proof from known test vector bytes let proof = PlonkProof { lro: [ - G1Point { x: 0x2dac3ba24f360a6deda246f6d3e9d8080ed09f97126ef9af18c5de05ca340416, y: 0x054a4430da47cc1a780b8c91f2c4a3347b1523d220bee21f7b10016e0df7e708 }, - G1Point { x: 0x235ff58eb8e9feb8cf75355f3daec83dd4dde0ebe08ca90ea98510aba1585f4f, y: 0x0d1716e7f3a01ac0ac6f3a1f6130256444c0b25a114f9300abaeb0d0838d29b2 }, - G1Point { x: 0x2c1dbdf8e4d0f950e7d062751c52a03ad451685e0d23b45563aa87a7d74ec1cf, y: 0x2cff1161e6d5c9272c3892a76adeb9a5aada5d7065c8e41121ebf4bd9d0fb2cc }, + G1Point { + x: 0x2dac3ba24f360a6deda246f6d3e9d8080ed09f97126ef9af18c5de05ca340416, + y: 0x054a4430da47cc1a780b8c91f2c4a3347b1523d220bee21f7b10016e0df7e708, + }, + G1Point { + x: 0x235ff58eb8e9feb8cf75355f3daec83dd4dde0ebe08ca90ea98510aba1585f4f, + y: 0x0d1716e7f3a01ac0ac6f3a1f6130256444c0b25a114f9300abaeb0d0838d29b2, + }, + G1Point { + x: 0x2c1dbdf8e4d0f950e7d062751c52a03ad451685e0d23b45563aa87a7d74ec1cf, + y: 0x2cff1161e6d5c9272c3892a76adeb9a5aada5d7065c8e41121ebf4bd9d0fb2cc, + }, ], - z: G1Point { x: 0x1657a289347c1973783849c4545c7ed8f0ea65f1eb40b31678d627e70b79bfd1, y: 0x1592517f8902c7364f90126fe04c28381fe12e165adcf82217876359a544e218 }, + z: G1Point { + x: 0x1657a289347c1973783849c4545c7ed8f0ea65f1eb40b31678d627e70b79bfd1, + y: 0x1592517f8902c7364f90126fe04c28381fe12e165adcf82217876359a544e218, + }, h: [ - G1Point { x: 0x1aed4c55f27c9cb2021ddae086085388d250dc257cc61ece968e674b25ed18a3, y: 0x2e9fbdee2b76ceb2c26d73c760252070fab8d5c04dd4f5616ca352666bb18782 }, - G1Point { x: 0x0bb1920bd0959e61569ec796bd832e78f92e20320fc9cc9ae6ae8470dab64371, y: 0x09fdd853d0db78b9ce5811df7c7bf6a7c0486cf433219034e1c43206b64a404a }, - G1Point { x: 0x19280febb426e548e99e6279adceffaa4ddae622ec50624afb4ea827467adc41, y: 0x099d164e7abfdc97d9b168461c2626e88239d30529974cb1b582d7362ed6d6d5 }, + G1Point { + x: 0x1aed4c55f27c9cb2021ddae086085388d250dc257cc61ece968e674b25ed18a3, + y: 0x2e9fbdee2b76ceb2c26d73c760252070fab8d5c04dd4f5616ca352666bb18782, + }, + G1Point { + x: 0x0bb1920bd0959e61569ec796bd832e78f92e20320fc9cc9ae6ae8470dab64371, + y: 0x09fdd853d0db78b9ce5811df7c7bf6a7c0486cf433219034e1c43206b64a404a, + }, + G1Point { + x: 0x19280febb426e548e99e6279adceffaa4ddae622ec50624afb4ea827467adc41, + y: 0x099d164e7abfdc97d9b168461c2626e88239d30529974cb1b582d7362ed6d6d5, + }, ], bsb22_commitments: [ - G1Point { x: 0x27086e886de4f31d65e997d9dd08e35dd6590101d9185db7c8c5dd7dd9348c9c, y: 0x1b711862d9757e744014177960751f5f5a84f14bbc1b019021db0666b8e55fc3 }, + G1Point { + x: 0x27086e886de4f31d65e997d9dd08e35dd6590101d9185db7c8c5dd7dd9348c9c, + y: 0x1b711862d9757e744014177960751f5f5a84f14bbc1b019021db0666b8e55fc3, + }, ], - batched_h: G1Point { x: 0x24577aded4d6983f59f123e52821f39141c397fcc957b4f7a2a4ede57b55ef10, y: 0x05e37b2c4f98ccba063aef65967db4e19547a1e18bea96ab8ec861b7a3e085db }, + batched_h: G1Point { + x: 0x24577aded4d6983f59f123e52821f39141c397fcc957b4f7a2a4ede57b55ef10, + y: 0x05e37b2c4f98ccba063aef65967db4e19547a1e18bea96ab8ec861b7a3e085db, + }, claimed_values: [ 0x2a2af70b568d007ce53077a078437c2acf6cad206354b0ffa823b4de87918a05, 0x03a674ce289759e10b9da150b523d55886b63dc8526f0e36132edb5239c0c238, @@ -735,7 +917,10 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { 0x0dad3ef6870691a71276c4f6f185fb14cfe8c7a418c26b3620ce09eff0d21a42, 0x28ecd97e273c00ce59504ccc15bfe8a2d8f87d1bbd7dc63df606b3c975080a6f, ], - z_shifted_h: G1Point { x: 0x2a5ab0dadfc301d004d4863346963baebbd7ea536022a8b90cd9b52d865d5c9c, y: 0x1e6437eaa886f121c46132f1bcd890827f7860cc63e5b27d8319f9b0cab8d27e }, + z_shifted_h: G1Point { + x: 0x2a5ab0dadfc301d004d4863346963baebbd7ea536022a8b90cd9b52d865d5c9c, + y: 0x1e6437eaa886f121c46132f1bcd890827f7860cc63e5b27d8319f9b0cab8d27e, + }, z_shifted_value: 0x2c1660be901029bf87b9796eedddc65aef146a4419ae4a123ee18aacfa9c41d1, } @@ -760,13 +945,29 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { let points: [G1Point; 11] = [ proof.bsb22_commitments[0], - vk.ql, vk.qr, vk.qm, vk.qo, vk.qk, - vk.s[2], proof.z, - proof.h[0], proof.h[1], proof.h[2], + vk.ql, + vk.qr, + vk.qm, + vk.qo, + vk.qk, + vk.s[2], + proof.z, + proof.h[0], + proof.h[1], + proof.h[2], ] let scalars: [u256; 11] = [ - qc_val, l, r, rl, o, one, - coeff_s1, coeff_z, coeff_h0, coeff_h1, coeff_h2, + qc_val, + l, + r, + rl, + o, + one, + coeff_s1, + coeff_z, + coeff_h0, + coeff_h1, + coeff_h2, ] let linearized = msm::msm<11>(points, scalars) @@ -780,23 +981,27 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { // gamma_kzg transcript let mut t2 = Transcript::new() - t2 = t2.write_label(label: 0x67616d6d61000000000000000000000000000000000000000000000000000000, label_len: 5) - t2 = t2.write_u256(val:zeta) - t2 = t2.write_g1(p:linearized) - t2 = t2.write_g1(p:proof.lro[0]) - t2 = t2.write_g1(p:proof.lro[1]) - t2 = t2.write_g1(p:proof.lro[2]) - t2 = t2.write_g1(p:vk.s[0]) - t2 = t2.write_g1(p:vk.s[1]) - t2 = t2.write_g1(p:vk.qcp[0]) - t2 = t2.write_u256(val:const_lin) - t2 = t2.write_u256(val:l) - t2 = t2.write_u256(val:r) - t2 = t2.write_u256(val:o) - t2 = t2.write_u256(val:s1) - t2 = t2.write_u256(val:s2) - t2 = t2.write_u256(val:qc_val) - t2 = t2.write_u256(val:zu) + t2 = t2 + .write_label( + label: 0x67616d6d61000000000000000000000000000000000000000000000000000000, + label_len: 5, + ) + t2 = t2.write_u256(val: zeta) + t2 = t2.write_g1(p: linearized) + t2 = t2.write_g1(p: proof.lro[0]) + t2 = t2.write_g1(p: proof.lro[1]) + t2 = t2.write_g1(p: proof.lro[2]) + t2 = t2.write_g1(p: vk.s[0]) + t2 = t2.write_g1(p: vk.s[1]) + t2 = t2.write_g1(p: vk.qcp[0]) + t2 = t2.write_u256(val: const_lin) + t2 = t2.write_u256(val: l) + t2 = t2.write_u256(val: r) + t2 = t2.write_u256(val: o) + t2 = t2.write_u256(val: s1) + t2 = t2.write_u256(val: s2) + t2 = t2.write_u256(val: qc_val) + t2 = t2.write_u256(val: zu) let (gamma_kzg, _, _) = t2.squeeze() // Fold @@ -810,8 +1015,12 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { let fold_points: [G1Point; 7] = [ linearized, - proof.lro[0], proof.lro[1], proof.lro[2], - vk.s[0], vk.s[1], vk.qcp[0], + proof.lro[0], + proof.lro[1], + proof.lro[2], + vk.s[0], + vk.s[1], + vk.qcp[0], ] let fold_scalars: [u256; 7] = [g0, g1, g2, g3, g4, g5, g6] let folded_digest = msm::msm<7>(fold_points, fold_scalars) @@ -828,20 +1037,24 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { folded_eval = field::add(folded_eval, field::mul(claimed[6], g6)) // KZG points — test each individually - let _t1 = g1_mul(p: proof.batched_h, s: 1) // W_zeta - let _t2 = g1_mul(p: proof.z_shifted_h, s: 1) // W_zeta_omega - let _t3 = g1_mul(p: vk.g1_srs, s: 1) // G1 SRS + let _t1 = g1_mul(p: proof.batched_h, s: 1) // W_zeta + let _t2 = g1_mul(p: proof.z_shifted_h, s: 1) // W_zeta_omega + let _t3 = g1_mul(p: vk.g1_srs, s: 1) // G1 SRS // Derive U challenge let zeta_raw: u256 = 0xc3b84ae752216a5cae098493e1282230f2d8db540b958f2c6d0067f9ee0fed8a let mut t3 = Transcript::new() - t3 = t3.write_label(label: 0x7500000000000000000000000000000000000000000000000000000000000000, label_len: 1) - t3 = t3.write_u256(val:zeta_raw) - t3 = t3.write_u256(val:gamma_kzg) - t3 = t3.write_g1(p:folded_digest) - t3 = t3.write_g1(p:proof.z) - t3 = t3.write_g1(p:proof.batched_h) - t3 = t3.write_g1(p:proof.z_shifted_h) + t3 = t3 + .write_label( + label: 0x7500000000000000000000000000000000000000000000000000000000000000, + label_len: 1, + ) + t3 = t3.write_u256(val: zeta_raw) + t3 = t3.write_u256(val: gamma_kzg) + t3 = t3.write_g1(p: folded_digest) + t3 = t3.write_g1(p: proof.z) + t3 = t3.write_g1(p: proof.batched_h) + t3 = t3.write_g1(p: proof.z_shifted_h) let (u_challenge, _, _) = t3.squeeze() let omega: u256 = 5709868443893258075976348696661355716898495876243883251619397131511003808859 @@ -851,16 +1064,19 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { use ec::bn254::negate // Step A - let u_times_zsh = g1_mul(p: proof.z_shifted_h, s: u_challenge) // checkpoint A1 - let folded_quotients = g1_add(a: proof.batched_h, b: u_times_zsh) // checkpoint A2 + let u_times_zsh = g1_mul(p: proof.z_shifted_h, s: u_challenge) // checkpoint A1 + let folded_quotients = g1_add(a: proof.batched_h, b: u_times_zsh) // checkpoint A2 // Step B let eval_sum = field::add(folded_eval, field::mul(u_challenge, zu)) - let folded_evals_commit = g1_mul(p: vk.g1_srs, s: eval_sum) // checkpoint B + let folded_evals_commit = g1_mul(p: vk.g1_srs, s: eval_sum) // checkpoint B // Step C: point_term = zeta * batched_h + u * shifted_zeta * z_shifted_h let zeta_times_wz = g1_mul(p: proof.batched_h, s: zeta) - let us_zeta_times_wzo = g1_mul(p: proof.z_shifted_h, s: field::mul(u_challenge, shifted_zeta)) + let us_zeta_times_wzo = g1_mul( + p: proof.z_shifted_h, + s: field::mul(u_challenge, shifted_zeta), + ) let point_term = g1_add(a: zeta_times_wz, b: us_zeta_times_wzo) // Step D: lhs = folded_digest + u*Z - folded_evals_commit + point_term @@ -876,41 +1092,123 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { n: 16777216, n_inv: 21888241567198334088790460357988866238279339518792980768180410072331574733841, omega: 5709868443893258075976348696661355716898495876243883251619397131511003808859, - nb_public_inputs: 2, coset_shift: 5, - ql: G1Point { x: 2714773032566361735398260413518107570706289019141573602093747023461681138141, y: 10207220609888567477852282724812707756861966294950666667119692155077205992894 }, - qr: G1Point { x: 17919274808167168584263187859012763816365260341587621260815379357637476029962, y: 14558165337321799812085033100515533981610351056305142204990949940017867076397 }, - qm: G1Point { x: 1814703450159964740292891910795980721108620081843240976053374083376051887455, y: 11252528960397523304289223453506717847025678682133692300385063157160041127070 }, - qo: G1Point { x: 20843277058771674275997213106654908867381045039357421108797602213552545033079, y: 9646775541123942436366130063934415659078920798926708026864638413383214238671 }, - qk: G1Point { x: 5484717465597821820411103650564499774744032473047103693751158150047197753654, y: 5561799343038529497262757012400750786503050088440144551259537360162821571059 }, + nb_public_inputs: 2, + coset_shift: 5, + ql: G1Point { + x: 2714773032566361735398260413518107570706289019141573602093747023461681138141, + y: 10207220609888567477852282724812707756861966294950666667119692155077205992894, + }, + qr: G1Point { + x: 17919274808167168584263187859012763816365260341587621260815379357637476029962, + y: 14558165337321799812085033100515533981610351056305142204990949940017867076397, + }, + qm: G1Point { + x: 1814703450159964740292891910795980721108620081843240976053374083376051887455, + y: 11252528960397523304289223453506717847025678682133692300385063157160041127070, + }, + qo: G1Point { + x: 20843277058771674275997213106654908867381045039357421108797602213552545033079, + y: 9646775541123942436366130063934415659078920798926708026864638413383214238671, + }, + qk: G1Point { + x: 5484717465597821820411103650564499774744032473047103693751158150047197753654, + y: 5561799343038529497262757012400750786503050088440144551259537360162821571059, + }, s: [ - G1Point { x: 16111562061301112215931665617877464360548491176332584512747295033804502769274, y: 15035232142063390140879951391784254536324051421746307325879221184372296043705 }, - G1Point { x: 899944321381010541211546037826620464002745326050515852312919625047231523882, y: 61717668739330555376092528203839789132705738484346798874082062974863965392 }, - G1Point { x: 9316901462569250008665217603385561854185385862824092362271612343176126127375, y: 13799900238612879579721466063922041459340434537392216736920805107993374657577 }, + G1Point { + x: 16111562061301112215931665617877464360548491176332584512747295033804502769274, + y: 15035232142063390140879951391784254536324051421746307325879221184372296043705, + }, + G1Point { + x: 899944321381010541211546037826620464002745326050515852312919625047231523882, + y: 61717668739330555376092528203839789132705738484346798874082062974863965392, + }, + G1Point { + x: 9316901462569250008665217603385561854185385862824092362271612343176126127375, + y: 13799900238612879579721466063922041459340434537392216736920805107993374657577, + }, + ], + qcp: [ + G1Point { + x: 21578473557091588309361521643625606794648013014197133181947992670819103775934, + y: 18236588362476326695195531997097392315059481348147701548685746610417604595065, + }, ], - qcp: [G1Point { x: 21578473557091588309361521643625606794648013014197133181947992670819103775934, y: 18236588362476326695195531997097392315059481348147701548685746610417604595065 }], commit_constraint_idx: [10900304], g2_srs: [ - G2Point { x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930 }, - G2Point { x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979 }, + G2Point { + x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, + x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, + y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, + y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930, + }, + G2Point { + x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, + x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, + y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, + y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979, + }, ], - g1_srs: G1Point { x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, y: 11763105256161367503191792604679297387056316997144156930871823008787082098465 }, + g1_srs: G1Point { + x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, + y: 11763105256161367503191792604679297387056316997144156930871823008787082098465, + }, } let proof2 = PlonkProof { lro: [ - G1Point { x: 0x2dac3ba24f360a6deda246f6d3e9d8080ed09f97126ef9af18c5de05ca340416, y: 0x054a4430da47cc1a780b8c91f2c4a3347b1523d220bee21f7b10016e0df7e708 }, - G1Point { x: 0x235ff58eb8e9feb8cf75355f3daec83dd4dde0ebe08ca90ea98510aba1585f4f, y: 0x0d1716e7f3a01ac0ac6f3a1f6130256444c0b25a114f9300abaeb0d0838d29b2 }, - G1Point { x: 0x2c1dbdf8e4d0f950e7d062751c52a03ad451685e0d23b45563aa87a7d74ec1cf, y: 0x2cff1161e6d5c9272c3892a76adeb9a5aada5d7065c8e41121ebf4bd9d0fb2cc }, + G1Point { + x: 0x2dac3ba24f360a6deda246f6d3e9d8080ed09f97126ef9af18c5de05ca340416, + y: 0x054a4430da47cc1a780b8c91f2c4a3347b1523d220bee21f7b10016e0df7e708, + }, + G1Point { + x: 0x235ff58eb8e9feb8cf75355f3daec83dd4dde0ebe08ca90ea98510aba1585f4f, + y: 0x0d1716e7f3a01ac0ac6f3a1f6130256444c0b25a114f9300abaeb0d0838d29b2, + }, + G1Point { + x: 0x2c1dbdf8e4d0f950e7d062751c52a03ad451685e0d23b45563aa87a7d74ec1cf, + y: 0x2cff1161e6d5c9272c3892a76adeb9a5aada5d7065c8e41121ebf4bd9d0fb2cc, + }, ], - z: G1Point { x: 0x1657a289347c1973783849c4545c7ed8f0ea65f1eb40b31678d627e70b79bfd1, y: 0x1592517f8902c7364f90126fe04c28381fe12e165adcf82217876359a544e218 }, + z: G1Point { + x: 0x1657a289347c1973783849c4545c7ed8f0ea65f1eb40b31678d627e70b79bfd1, + y: 0x1592517f8902c7364f90126fe04c28381fe12e165adcf82217876359a544e218, + }, h: [ - G1Point { x: 0x1aed4c55f27c9cb2021ddae086085388d250dc257cc61ece968e674b25ed18a3, y: 0x2e9fbdee2b76ceb2c26d73c760252070fab8d5c04dd4f5616ca352666bb18782 }, - G1Point { x: 0x0bb1920bd0959e61569ec796bd832e78f92e20320fc9cc9ae6ae8470dab64371, y: 0x09fdd853d0db78b9ce5811df7c7bf6a7c0486cf433219034e1c43206b64a404a }, - G1Point { x: 0x19280febb426e548e99e6279adceffaa4ddae622ec50624afb4ea827467adc41, y: 0x099d164e7abfdc97d9b168461c2626e88239d30529974cb1b582d7362ed6d6d5 }, + G1Point { + x: 0x1aed4c55f27c9cb2021ddae086085388d250dc257cc61ece968e674b25ed18a3, + y: 0x2e9fbdee2b76ceb2c26d73c760252070fab8d5c04dd4f5616ca352666bb18782, + }, + G1Point { + x: 0x0bb1920bd0959e61569ec796bd832e78f92e20320fc9cc9ae6ae8470dab64371, + y: 0x09fdd853d0db78b9ce5811df7c7bf6a7c0486cf433219034e1c43206b64a404a, + }, + G1Point { + x: 0x19280febb426e548e99e6279adceffaa4ddae622ec50624afb4ea827467adc41, + y: 0x099d164e7abfdc97d9b168461c2626e88239d30529974cb1b582d7362ed6d6d5, + }, ], - bsb22_commitments: [G1Point { x: 0x27086e886de4f31d65e997d9dd08e35dd6590101d9185db7c8c5dd7dd9348c9c, y: 0x1b711862d9757e744014177960751f5f5a84f14bbc1b019021db0666b8e55fc3 }], - batched_h: G1Point { x: 0x24577aded4d6983f59f123e52821f39141c397fcc957b4f7a2a4ede57b55ef10, y: 0x05e37b2c4f98ccba063aef65967db4e19547a1e18bea96ab8ec861b7a3e085db }, - claimed_values: [0x2a2af70b568d007ce53077a078437c2acf6cad206354b0ffa823b4de87918a05, 0x03a674ce289759e10b9da150b523d55886b63dc8526f0e36132edb5239c0c238, 0x19d465a94658e64d3798897c8438352029a3d285a049af99ff195c36359c16d0, 0x086e2bbb1679e24af18ee4aac62fded55640735e7c6aeda82abe2c01a3f307c9, 0x0dad3ef6870691a71276c4f6f185fb14cfe8c7a418c26b3620ce09eff0d21a42, 0x28ecd97e273c00ce59504ccc15bfe8a2d8f87d1bbd7dc63df606b3c975080a6f], - z_shifted_h: G1Point { x: 0x2a5ab0dadfc301d004d4863346963baebbd7ea536022a8b90cd9b52d865d5c9c, y: 0x1e6437eaa886f121c46132f1bcd890827f7860cc63e5b27d8319f9b0cab8d27e }, + bsb22_commitments: [ + G1Point { + x: 0x27086e886de4f31d65e997d9dd08e35dd6590101d9185db7c8c5dd7dd9348c9c, + y: 0x1b711862d9757e744014177960751f5f5a84f14bbc1b019021db0666b8e55fc3, + }, + ], + batched_h: G1Point { + x: 0x24577aded4d6983f59f123e52821f39141c397fcc957b4f7a2a4ede57b55ef10, + y: 0x05e37b2c4f98ccba063aef65967db4e19547a1e18bea96ab8ec861b7a3e085db, + }, + claimed_values: [ + 0x2a2af70b568d007ce53077a078437c2acf6cad206354b0ffa823b4de87918a05, + 0x03a674ce289759e10b9da150b523d55886b63dc8526f0e36132edb5239c0c238, + 0x19d465a94658e64d3798897c8438352029a3d285a049af99ff195c36359c16d0, + 0x086e2bbb1679e24af18ee4aac62fded55640735e7c6aeda82abe2c01a3f307c9, + 0x0dad3ef6870691a71276c4f6f185fb14cfe8c7a418c26b3620ce09eff0d21a42, + 0x28ecd97e273c00ce59504ccc15bfe8a2d8f87d1bbd7dc63df606b3c975080a6f, + ], + z_shifted_h: G1Point { + x: 0x2a5ab0dadfc301d004d4863346963baebbd7ea536022a8b90cd9b52d865d5c9c, + y: 0x1e6437eaa886f121c46132f1bcd890827f7860cc63e5b27d8319f9b0cab8d27e, + }, z_shifted_value: 0x2c1660be901029bf87b9796eedddc65aef146a4419ae4a123ee18aacfa9c41d1, } // NOTE: Full verify() can't be tested via `fe test` because the large @@ -928,15 +1226,15 @@ fn test_verify_end_to_end() uses (evm: mut Evm) { fn test_kzg_fold_scalars() { // Verify that gamma_kzg powers are computed correctly // Given a known gamma_kzg, the powers should be [1, g, g^2, g^3, ...] - let g: u256 = 7 // arbitrary test value + let g: u256 = 7 // arbitrary test value let g0: u256 = 1 let g1 = g let g2 = field::mul(g1, g) let g3 = field::mul(g2, g) - assert(g0 == 1) - assert(g1 == 7) - assert(g2 == 49) - assert(g3 == 343) + assert!(g0 == 1) + assert!(g1 == 7) + assert!(g2 == 49) + assert!(g3 == 343) // Folded eval = sum(claimed[i] * g^i) let claimed: [u256; 4] = [10, 20, 30, 40] @@ -946,7 +1244,7 @@ fn test_kzg_fold_scalars() { folded = field::add(folded, field::mul(claimed[2], g2)) folded = field::add(folded, field::mul(claimed[3], g3)) // 10*1 + 20*7 + 30*49 + 40*343 = 10 + 140 + 1470 + 13720 = 15340 - assert(folded == 15340) + assert!(folded == 15340) } #[test] @@ -955,10 +1253,13 @@ fn test_v6_gamma_label_and_vk() uses (evm: mut Evm) { let mut t = Transcript::new() t = t.write_label(label: GAMMA_LABEL, label_len: 5) // S1_X from v6 VK - t = t.write_u256(val:9956956733383539527828528704346982954811130189972564286856709656590492390783) + t = t + .write_u256( + val: 9956956733383539527828528704346982954811130189972564286856709656590492390783, + ) // Just test the first 37 bytes produce the right start // If this passes, the label encoding and first VK value are correct - assert(t.len == 37) + assert!(t.len == 37) } #[test] @@ -967,50 +1268,125 @@ fn test_v6_gamma_challenge() uses (evm: mut Evm) { let mut t = Transcript::new() t = t.write_label(label: GAMMA_LABEL, label_len: 5) // S1 (x, y) - t = t.write_u256(val:9956956733383539527828528704346982954811130189972564286856709656590492390783) - t = t.write_u256(val:16561941243254960602343726886092320314575403533969071986056337989120686189882) + t = t + .write_u256( + val: 9956956733383539527828528704346982954811130189972564286856709656590492390783, + ) + t = t + .write_u256( + val: 16561941243254960602343726886092320314575403533969071986056337989120686189882, + ) // S2 (x, y) - t = t.write_u256(val:20374741449540543556499660179263983188390530543434373332462519860014413516289) - t = t.write_u256(val:8513455195626237033487485922347652175901063415320070186421575800344119284165) + t = t + .write_u256( + val: 20374741449540543556499660179263983188390530543434373332462519860014413516289, + ) + t = t + .write_u256( + val: 8513455195626237033487485922347652175901063415320070186421575800344119284165, + ) // S3 (x, y) - t = t.write_u256(val:17142227368091895742859950676182428277541451282467607716193503860042663502549) - t = t.write_u256(val:12829174888268721439052436070745892278566825005271705420525175951948128646413) + t = t + .write_u256( + val: 17142227368091895742859950676182428277541451282467607716193503860042663502549, + ) + t = t + .write_u256( + val: 12829174888268721439052436070745892278566825005271705420525175951948128646413, + ) // QL (x, y) - t = t.write_u256(val:10936403877113400171814029258586094421105837069937643866695822256928377913968) - t = t.write_u256(val:17311182666118490454430265150735546359745608671115556843788944971563039300016) + t = t + .write_u256( + val: 10936403877113400171814029258586094421105837069937643866695822256928377913968, + ) + t = t + .write_u256( + val: 17311182666118490454430265150735546359745608671115556843788944971563039300016, + ) // QR (x, y) - t = t.write_u256(val:16032802090162682725903814972017385562886704406468141346968145983310475511911) - t = t.write_u256(val:16803777488545011133374943287703653910096989118082992957503857327508542673114) + t = t + .write_u256( + val: 16032802090162682725903814972017385562886704406468141346968145983310475511911, + ) + t = t + .write_u256( + val: 16803777488545011133374943287703653910096989118082992957503857327508542673114, + ) // QM (x, y) - t = t.write_u256(val:7041740842251303753059524333065028234525995021747993588573234611770565484750) - t = t.write_u256(val:7756772573841616356086078220846315947185632323121180681135487008638788536094) + t = t + .write_u256( + val: 7041740842251303753059524333065028234525995021747993588573234611770565484750, + ) + t = t + .write_u256( + val: 7756772573841616356086078220846315947185632323121180681135487008638788536094, + ) // QO (x, y) - t = t.write_u256(val:8807113593766821411972572357967651651345992430818514862534237402324085851875) - t = t.write_u256(val:9827115302746495531711628100661677137543191000457083504235238115987266270156) + t = t + .write_u256( + val: 8807113593766821411972572357967651651345992430818514862534237402324085851875, + ) + t = t + .write_u256( + val: 9827115302746495531711628100661677137543191000457083504235238115987266270156, + ) // QK (x, y) - t = t.write_u256(val:4659055902458442531441402775737578699425178194825095727614170820825642511722) - t = t.write_u256(val:2854792741677890224744936867769988448466277171603338196705997958237726289995) + t = t + .write_u256( + val: 4659055902458442531441402775737578699425178194825095727614170820825642511722, + ) + t = t + .write_u256( + val: 2854792741677890224744936867769988448466277171603338196705997958237726289995, + ) // QCP[0] (x, y) - t = t.write_u256(val:8032592088052402119252717716570843753829708920203926363246235929861264130729) - t = t.write_u256(val:13824231045893282984607165590437904083147145250603202024200081150405773221988) + t = t + .write_u256( + val: 8032592088052402119252717716570843753829708920203926363246235929861264130729, + ) + t = t + .write_u256( + val: 13824231045893282984607165590437904083147145250603202024200081150405773221988, + ) // 5 public inputs - t = t.write_u256(val:0x004a55ed3c7a07d0233a027278a8b7ff8681ffbd5d1ec4795c18966f6e693090) // pi0 - t = t.write_u256(val:0x0f1cb7decf31e49c7934c3740bec5df3ead27bc947af739782930df6e37e9d90) // pi1 - t = t.write_u256(val:0) // pi2 exitCode - t = t.write_u256(val:0x008cd56e10c2fe24795cff1e1d1f40d3a324528d315674da45d26afb376e8670) // pi3 vkRoot - t = t.write_u256(val:0) // pi4 nonce + t = t.write_u256( + val: 0x004a55ed3c7a07d0233a027278a8b7ff8681ffbd5d1ec4795c18966f6e693090, + ) // pi0 + t = t.write_u256( + val: 0x0f1cb7decf31e49c7934c3740bec5df3ead27bc947af739782930df6e37e9d90, + ) // pi1 + t = t.write_u256(val: 0) // pi2 exitCode + t = t + .write_u256( + val: 0x008cd56e10c2fe24795cff1e1d1f40d3a324528d315674da45d26afb376e8670, + ) // pi3 vkRoot + t = t.write_u256(val: 0) // pi4 nonce // Wire commitments L, R, O from v6 proof (starting at byte 100) - t = t.write_u256(val:0x06232459bd070942b95a2fd99cf8872eeee49ba7cd7ebe07ec3ef1b4b2a74d91) // L.x - t = t.write_u256(val:0x154e14823bfcf0b1fcd5a43df161341594c070ae4d2814a818c262a435b68188) // L.y - t = t.write_u256(val:0x15404bf08191a8b3095519798cf76c536f65e264997b59e7f9f554d7b9d24b12) // R.x - t = t.write_u256(val:0x0887cd80d76b8b56c9261983cae540bfbc77f2aabb5ee16cee0362dfe079bd12) // R.y - t = t.write_u256(val:0x0940fd1df4048a50b264164a972fc28739dc1365e3dcd3f0fb39a89e5dbf48e2) // O.x - t = t.write_u256(val:0x2903f07b564d791699df704aeb7fc3a0d196e392eaba115b629a562d7b54daa9) // O.y + t = t.write_u256( + val: 0x06232459bd070942b95a2fd99cf8872eeee49ba7cd7ebe07ec3ef1b4b2a74d91, + ) // L.x + t = t.write_u256( + val: 0x154e14823bfcf0b1fcd5a43df161341594c070ae4d2814a818c262a435b68188, + ) // L.y + t = t.write_u256( + val: 0x15404bf08191a8b3095519798cf76c536f65e264997b59e7f9f554d7b9d24b12, + ) // R.x + t = t.write_u256( + val: 0x0887cd80d76b8b56c9261983cae540bfbc77f2aabb5ee16cee0362dfe079bd12, + ) // R.y + t = t.write_u256( + val: 0x0940fd1df4048a50b264164a972fc28739dc1365e3dcd3f0fb39a89e5dbf48e2, + ) // O.x + t = t.write_u256( + val: 0x2903f07b564d791699df704aeb7fc3a0d196e392eaba115b629a562d7b54daa9, + ) // O.y let (_, gamma_raw, t) = t.squeeze() // Expected v6 gamma_raw from SP1 trace - assert(gamma_raw == 0x86c4ac45a0be843cc24c65d9d559dd135a4ecad0f433372fa79f7979c9c6705c) + assert!( + gamma_raw == 0x86c4ac45a0be843cc24c65d9d559dd135a4ecad0f433372fa79f7979c9c6705c, + ) } #[test] @@ -1021,23 +1397,35 @@ fn test_v6_all_challenges() uses (evm: mut Evm) { // Beta = SHA256("beta" || gamma_raw) let mut t = Transcript::new() t = t.write_label(label: BETA_LABEL, label_len: 4) - t = t.write_u256(val:gamma_raw) + t = t.write_u256(val: gamma_raw) let (_, beta_raw, t) = t.squeeze() - assert(beta_raw == 0x4e56c438a8b25590e81c8bb610688afb1f226fba52e6edc7363b21baca7eacab) + assert!( + beta_raw == 0x4e56c438a8b25590e81c8bb610688afb1f226fba52e6edc7363b21baca7eacab, + ) // Alpha = SHA256("alpha" || beta_raw || bsb22_commit || Z) // bsb22 and Z from v6 proof at offsets 0x320 and 0x220 respectively let mut t = Transcript::new() t = t.write_label(label: ALPHA_LABEL, label_len: 5) - t = t.write_u256(val:beta_raw) + t = t.write_u256(val: beta_raw) // BSB22 commitment (v6 proof offset 0x320) - t = t.write_u256(val:0x296197d04b2d5f020a80db0ee9b7a0d62f0c0576997960bf24b718a65edd5bd2) - t = t.write_u256(val:0x089afd886e912bef248a89f57e075f5737fe71f57be70fdabb6a55f25f1c092b) + t = t.write_u256( + val: 0x296197d04b2d5f020a80db0ee9b7a0d62f0c0576997960bf24b718a65edd5bd2, + ) + t = t.write_u256( + val: 0x089afd886e912bef248a89f57e075f5737fe71f57be70fdabb6a55f25f1c092b, + ) // Z commitment (v6 proof offset 0x220) - t = t.write_u256(val:0x1b8cf196023c29725a2b372fcb7b02579480e58281ddba98dc4d054620dd1c50) - t = t.write_u256(val:0x2cb66a9d6e2f0c5dc50e32510c756b0ad319202f34171e69820014d705c3bcea) + t = t.write_u256( + val: 0x1b8cf196023c29725a2b372fcb7b02579480e58281ddba98dc4d054620dd1c50, + ) + t = t.write_u256( + val: 0x2cb66a9d6e2f0c5dc50e32510c756b0ad319202f34171e69820014d705c3bcea, + ) let (_, alpha_raw, t) = t.squeeze() - assert(alpha_raw == 0x8b4c0cc8930c7effedf6847ec5b115c7b78b3b9a74ff18c9fa4ead79e39e14dc) + assert!( + alpha_raw == 0x8b4c0cc8930c7effedf6847ec5b115c7b78b3b9a74ff18c9fa4ead79e39e14dc, + ) } #[test] @@ -1047,18 +1435,32 @@ fn test_v6_zeta_challenge() uses (evm: mut Evm) { // Zeta = SHA256("zeta" || alpha_raw || H0 || H1 || H2) let mut t = Transcript::new() t = t.write_label(label: ZETA_LABEL, label_len: 4) - t = t.write_u256(val:alpha_raw) + t = t.write_u256(val: alpha_raw) // H0 - t = t.write_u256(val:0x2f7c08eac11c4a53dff5104825a743418815599bec73b5b516c4a912343ad33a) - t = t.write_u256(val:0x1ba1c600ce5d285d97428833bd6050d5708efc922b15ce7778d675071d5e2c0f) + t = t.write_u256( + val: 0x2f7c08eac11c4a53dff5104825a743418815599bec73b5b516c4a912343ad33a, + ) + t = t.write_u256( + val: 0x1ba1c600ce5d285d97428833bd6050d5708efc922b15ce7778d675071d5e2c0f, + ) // H1 - t = t.write_u256(val:0x0aef2040bd5a16bf81e154b76387192a91362b431311434801774584c4cca5af) - t = t.write_u256(val:0x2f82a5996ddafb7b5bc009ee1c336412a1abcb9de944ec961d65de0f07c36178) + t = t.write_u256( + val: 0x0aef2040bd5a16bf81e154b76387192a91362b431311434801774584c4cca5af, + ) + t = t.write_u256( + val: 0x2f82a5996ddafb7b5bc009ee1c336412a1abcb9de944ec961d65de0f07c36178, + ) // H2 - t = t.write_u256(val:0x26dd3d2fab429286a3832f221505ac56cdbdc2dff07337a970b6d45aa542b395) - t = t.write_u256(val:0x2fd4c0d4b57b853c080939eebac4298a3d1dbd19fa28fc2026efd88935bef01f) + t = t.write_u256( + val: 0x26dd3d2fab429286a3832f221505ac56cdbdc2dff07337a970b6d45aa542b395, + ) + t = t.write_u256( + val: 0x2fd4c0d4b57b853c080939eebac4298a3d1dbd19fa28fc2026efd88935bef01f, + ) let (_, zeta_raw, t) = t.squeeze() - assert(zeta_raw == 0x9cfa5df065c8d2170b994be2f13f26328184096125b806282cc4482082fb210d) + assert!( + zeta_raw == 0x9cfa5df065c8d2170b994be2f13f26328184096125b806282cc4482082fb210d, + ) } #[test] @@ -1088,29 +1490,41 @@ fn test_v6_pi_computation() uses (evm: mut Evm) { while i < 5 { let den = field::sub(zeta, omega_i) let den_inv = field::inv(den) - let li = field::mul(field::mul(field::mul(zh_zeta, omega_i), den_inv), n_inv) + let li = field::mul( + field::mul(field::mul(zh_zeta, omega_i), den_inv), + n_inv, + ) pi = field::add(pi, field::mul(li, pis[i])) omega_i = field::mul(omega_i, omega) i = i + 1 } - assert(pi == 0x1fc53445308fcd4dec8047d362e47cc9bb24195e189111fea064c47bf560f82a) + assert!( + pi == 0x1fc53445308fcd4dec8047d362e47cc9bb24195e189111fea064c47bf560f82a, + ) // BSB22 let bsb22_x: u256 = 0x296197d04b2d5f020a80db0ee9b7a0d62f0c0576997960bf24b718a65edd5bd2 let bsb22_y: u256 = 0x089afd886e912bef248a89f57e075f5737fe71f57be70fdabb6a55f25f1c092b let h_fr = hash_fr(bsb22_x, bsb22_y) - assert(h_fr == 0x179f5050a2bd2f19ea3123f40b245b79058b7fea9ff6f31464e8e65798898c02) + assert!( + h_fr == 0x179f5050a2bd2f19ea3123f40b245b79058b7fea9ff6f31464e8e65798898c02, + ) // BSB22 Lagrange at index 5 + 25298720 let bsb_index: u256 = 25298725 let omega_bsb = pow_mod(omega, bsb_index) let bsb_den = field::sub(zeta, omega_bsb) let bsb_den_inv = field::inv(bsb_den) - let bsb_lagrange = field::mul(field::mul(field::mul(zh_zeta, omega_bsb), bsb_den_inv), n_inv) + let bsb_lagrange = field::mul( + field::mul(field::mul(zh_zeta, omega_bsb), bsb_den_inv), + n_inv, + ) pi = field::add(pi, field::mul(h_fr, bsb_lagrange)) - assert(pi == 0x27386b8ab2747c014ccbb1de5b044b4d8559193f19e454ece2d9a6bc8ed51b47) + assert!( + pi == 0x27386b8ab2747c014ccbb1de5b044b4d8559193f19e454ece2d9a6bc8ed51b47, + ) } #[test] @@ -1130,7 +1544,10 @@ fn test_v6_const_lin() { let zeta_power_n = pow_mod(zeta, n) let zh_zeta = field::sub(zeta_power_n, one) - let l1_zeta = field::mul(field::mul(zh_zeta, field::inv(field::sub(zeta, one))), n_inv) + let l1_zeta = field::mul( + field::mul(zh_zeta, field::inv(field::sub(zeta, one))), + n_inv, + ) let alpha_sq_l1 = field::mul(field::mul(alpha, alpha), l1_zeta) // v6 proof claimed values (from gnark proof offsets 0x180-0x260) @@ -1144,12 +1561,17 @@ fn test_v6_const_lin() { let term_a = field::add(l, field::add(field::mul(beta, s1_eval), gamma)) let term_b = field::add(r, field::add(field::mul(beta, s2_eval), gamma)) let term_c = field::add(o, gamma) - let perm_term = field::mul(field::mul(field::mul(term_a, term_b), term_c), field::mul(alpha, zu)) + let perm_term = field::mul( + field::mul(field::mul(term_a, term_b), term_c), + field::mul(alpha, zu), + ) let inner = field::add(field::sub(pi, alpha_sq_l1), perm_term) let const_lin = field::neg(inner) - assert(const_lin == 0x1dcbeb83df0f674b94f2dc968be367b5f26acd1be73b49fd3341a25b6161d386) + assert!( + const_lin == 0x1dcbeb83df0f674b94f2dc968be367b5f26acd1be73b49fd3341a25b6161d386, + ) } #[test] @@ -1158,42 +1580,112 @@ fn test_v6_verify_end_to_end() uses (evm: mut Evm) { n: 33554432, n_inv: 21888242219518804655518433051623070663413851959604507555939307129453691614729, omega: 19200870435978225707111062059747084165650991997241425080699860725083300967194, - nb_public_inputs: 5, coset_shift: 5, - ql: G1Point { x: 10936403877113400171814029258586094421105837069937643866695822256928377913968, y: 17311182666118490454430265150735546359745608671115556843788944971563039300016 }, - qr: G1Point { x: 16032802090162682725903814972017385562886704406468141346968145983310475511911, y: 16803777488545011133374943287703653910096989118082992957503857327508542673114 }, - qm: G1Point { x: 7041740842251303753059524333065028234525995021747993588573234611770565484750, y: 7756772573841616356086078220846315947185632323121180681135487008638788536094 }, - qo: G1Point { x: 8807113593766821411972572357967651651345992430818514862534237402324085851875, y: 9827115302746495531711628100661677137543191000457083504235238115987266270156 }, - qk: G1Point { x: 4659055902458442531441402775737578699425178194825095727614170820825642511722, y: 2854792741677890224744936867769988448466277171603338196705997958237726289995 }, + nb_public_inputs: 5, + coset_shift: 5, + ql: G1Point { + x: 10936403877113400171814029258586094421105837069937643866695822256928377913968, + y: 17311182666118490454430265150735546359745608671115556843788944971563039300016, + }, + qr: G1Point { + x: 16032802090162682725903814972017385562886704406468141346968145983310475511911, + y: 16803777488545011133374943287703653910096989118082992957503857327508542673114, + }, + qm: G1Point { + x: 7041740842251303753059524333065028234525995021747993588573234611770565484750, + y: 7756772573841616356086078220846315947185632323121180681135487008638788536094, + }, + qo: G1Point { + x: 8807113593766821411972572357967651651345992430818514862534237402324085851875, + y: 9827115302746495531711628100661677137543191000457083504235238115987266270156, + }, + qk: G1Point { + x: 4659055902458442531441402775737578699425178194825095727614170820825642511722, + y: 2854792741677890224744936867769988448466277171603338196705997958237726289995, + }, s: [ - G1Point { x: 9956956733383539527828528704346982954811130189972564286856709656590492390783, y: 16561941243254960602343726886092320314575403533969071986056337989120686189882 }, - G1Point { x: 20374741449540543556499660179263983188390530543434373332462519860014413516289, y: 8513455195626237033487485922347652175901063415320070186421575800344119284165 }, - G1Point { x: 17142227368091895742859950676182428277541451282467607716193503860042663502549, y: 12829174888268721439052436070745892278566825005271705420525175951948128646413 }, + G1Point { + x: 9956956733383539527828528704346982954811130189972564286856709656590492390783, + y: 16561941243254960602343726886092320314575403533969071986056337989120686189882, + }, + G1Point { + x: 20374741449540543556499660179263983188390530543434373332462519860014413516289, + y: 8513455195626237033487485922347652175901063415320070186421575800344119284165, + }, + G1Point { + x: 17142227368091895742859950676182428277541451282467607716193503860042663502549, + y: 12829174888268721439052436070745892278566825005271705420525175951948128646413, + }, + ], + qcp: [ + G1Point { + x: 8032592088052402119252717716570843753829708920203926363246235929861264130729, + y: 13824231045893282984607165590437904083147145250603202024200081150405773221988, + }, ], - qcp: [G1Point { x: 8032592088052402119252717716570843753829708920203926363246235929861264130729, y: 13824231045893282984607165590437904083147145250603202024200081150405773221988 }], commit_constraint_idx: [25298720], g2_srs: [ - G2Point { x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930 }, - G2Point { x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979 }, + G2Point { + x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, + x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, + y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, + y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930, + }, + G2Point { + x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, + x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, + y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, + y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979, + }, ], - g1_srs: G1Point { x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, y: 11763105256161367503191792604679297387056316997144156930871823008787082098465 }, + g1_srs: G1Point { + x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, + y: 11763105256161367503191792604679297387056316997144156930871823008787082098465, + }, } let proof = PlonkProof { lro: [ - G1Point { x: 0x06232459bd070942b95a2fd99cf8872eeee49ba7cd7ebe07ec3ef1b4b2a74d91, y: 0x154e14823bfcf0b1fcd5a43df161341594c070ae4d2814a818c262a435b68188 }, - G1Point { x: 0x15404bf08191a8b3095519798cf76c536f65e264997b59e7f9f554d7b9d24b12, y: 0x0887cd80d76b8b56c9261983cae540bfbc77f2aabb5ee16cee0362dfe079bd12 }, - G1Point { x: 0x0940fd1df4048a50b264164a972fc28739dc1365e3dcd3f0fb39a89e5dbf48e2, y: 0x2903f07b564d791699df704aeb7fc3a0d196e392eaba115b629a562d7b54daa9 }, + G1Point { + x: 0x06232459bd070942b95a2fd99cf8872eeee49ba7cd7ebe07ec3ef1b4b2a74d91, + y: 0x154e14823bfcf0b1fcd5a43df161341594c070ae4d2814a818c262a435b68188, + }, + G1Point { + x: 0x15404bf08191a8b3095519798cf76c536f65e264997b59e7f9f554d7b9d24b12, + y: 0x0887cd80d76b8b56c9261983cae540bfbc77f2aabb5ee16cee0362dfe079bd12, + }, + G1Point { + x: 0x0940fd1df4048a50b264164a972fc28739dc1365e3dcd3f0fb39a89e5dbf48e2, + y: 0x2903f07b564d791699df704aeb7fc3a0d196e392eaba115b629a562d7b54daa9, + }, ], - z: G1Point { x: 0x1b8cf196023c29725a2b372fcb7b02579480e58281ddba98dc4d054620dd1c50, y: 0x2cb66a9d6e2f0c5dc50e32510c756b0ad319202f34171e69820014d705c3bcea }, + z: G1Point { + x: 0x1b8cf196023c29725a2b372fcb7b02579480e58281ddba98dc4d054620dd1c50, + y: 0x2cb66a9d6e2f0c5dc50e32510c756b0ad319202f34171e69820014d705c3bcea, + }, h: [ - G1Point { x: 0x2f7c08eac11c4a53dff5104825a743418815599bec73b5b516c4a912343ad33a, y: 0x1ba1c600ce5d285d97428833bd6050d5708efc922b15ce7778d675071d5e2c0f }, - G1Point { x: 0x0aef2040bd5a16bf81e154b76387192a91362b431311434801774584c4cca5af, y: 0x2f82a5996ddafb7b5bc009ee1c336412a1abcb9de944ec961d65de0f07c36178 }, - G1Point { x: 0x26dd3d2fab429286a3832f221505ac56cdbdc2dff07337a970b6d45aa542b395, y: 0x2fd4c0d4b57b853c080939eebac4298a3d1dbd19fa28fc2026efd88935bef01f }, + G1Point { + x: 0x2f7c08eac11c4a53dff5104825a743418815599bec73b5b516c4a912343ad33a, + y: 0x1ba1c600ce5d285d97428833bd6050d5708efc922b15ce7778d675071d5e2c0f, + }, + G1Point { + x: 0x0aef2040bd5a16bf81e154b76387192a91362b431311434801774584c4cca5af, + y: 0x2f82a5996ddafb7b5bc009ee1c336412a1abcb9de944ec961d65de0f07c36178, + }, + G1Point { + x: 0x26dd3d2fab429286a3832f221505ac56cdbdc2dff07337a970b6d45aa542b395, + y: 0x2fd4c0d4b57b853c080939eebac4298a3d1dbd19fa28fc2026efd88935bef01f, + }, ], bsb22_commitments: [ - G1Point { x: 0x296197d04b2d5f020a80db0ee9b7a0d62f0c0576997960bf24b718a65edd5bd2, y: 0x089afd886e912bef248a89f57e075f5737fe71f57be70fdabb6a55f25f1c092b }, + G1Point { + x: 0x296197d04b2d5f020a80db0ee9b7a0d62f0c0576997960bf24b718a65edd5bd2, + y: 0x089afd886e912bef248a89f57e075f5737fe71f57be70fdabb6a55f25f1c092b, + }, ], - batched_h: G1Point { x: 0x0e547a658a424c1413881c8f2e0e29fd52bf6acf111b5074de77b5d2d784b21e, y: 0x2d52532d1394a596a5348281176f0626cb778144643fcd86064e3a2a194d1d64 }, + batched_h: G1Point { + x: 0x0e547a658a424c1413881c8f2e0e29fd52bf6acf111b5074de77b5d2d784b21e, + y: 0x2d52532d1394a596a5348281176f0626cb778144643fcd86064e3a2a194d1d64, + }, claimed_values: [ 0x0dfb52cd65b1dde09dd5e7373a63c51e05a58495258a04b6e574e340dfd536df, 0x27a4f599643d644fbe36b006881701451d989915930150bad493a796fb3e68e7, @@ -1202,7 +1694,10 @@ fn test_v6_verify_end_to_end() uses (evm: mut Evm) { 0x20154e58f8b39569abed5efd79c54b737ca3326b6275d67c7993a401cd1a1d5e, 0x193c344ac7c5c37ac8a0f811ab22817de35f53c4b798b3ee8cb45a172919428a, ], - z_shifted_h: G1Point { x: 0x24ca8db14a80f97c3303dfba7f3aabe723fe6b6916310ea271b0ee9ad8866962, y: 0x0d99ebe9951836d7a8d672312a87b0b080b9c7d6fb4c952968e3601cbd2d0d60 }, + z_shifted_h: G1Point { + x: 0x24ca8db14a80f97c3303dfba7f3aabe723fe6b6916310ea271b0ee9ad8866962, + y: 0x0d99ebe9951836d7a8d672312a87b0b080b9c7d6fb4c952968e3601cbd2d0d60, + }, z_shifted_value: 0x1c72da9b1cce1cf13f273ece3ac7a7ba065d2497132355a2611dc214f8be8417, } @@ -1215,5 +1710,5 @@ fn test_v6_verify_end_to_end() uses (evm: mut Evm) { ] let result = verify(vk, proof, pub_inputs) - assert(result) + assert!(result) } diff --git a/examples/verifier/fe/src/plonk_bench.fe b/examples/verifier/fe/src/plonk_bench.fe index cd769e0..3323bc1 100644 --- a/examples/verifier/fe/src/plonk_bench.fe +++ b/examples/verifier/fe/src/plonk_bench.fe @@ -10,25 +10,65 @@ fn example_vk() -> PlonkVK { omega: 19200870435978225707111062059747084165650991997241425080699860725083300967194, nb_public_inputs: 5, coset_shift: 5, - ql: G1Point { x: 10936403877113400171814029258586094421105837069937643866695822256928377913968, y: 17311182666118490454430265150735546359745608671115556843788944971563039300016 }, - qr: G1Point { x: 16032802090162682725903814972017385562886704406468141346968145983310475511911, y: 16803777488545011133374943287703653910096989118082992957503857327508542673114 }, - qm: G1Point { x: 7041740842251303753059524333065028234525995021747993588573234611770565484750, y: 7756772573841616356086078220846315947185632323121180681135487008638788536094 }, - qo: G1Point { x: 8807113593766821411972572357967651651345992430818514862534237402324085851875, y: 9827115302746495531711628100661677137543191000457083504235238115987266270156 }, - qk: G1Point { x: 4659055902458442531441402775737578699425178194825095727614170820825642511722, y: 2854792741677890224744936867769988448466277171603338196705997958237726289995 }, + ql: G1Point { + x: 10936403877113400171814029258586094421105837069937643866695822256928377913968, + y: 17311182666118490454430265150735546359745608671115556843788944971563039300016, + }, + qr: G1Point { + x: 16032802090162682725903814972017385562886704406468141346968145983310475511911, + y: 16803777488545011133374943287703653910096989118082992957503857327508542673114, + }, + qm: G1Point { + x: 7041740842251303753059524333065028234525995021747993588573234611770565484750, + y: 7756772573841616356086078220846315947185632323121180681135487008638788536094, + }, + qo: G1Point { + x: 8807113593766821411972572357967651651345992430818514862534237402324085851875, + y: 9827115302746495531711628100661677137543191000457083504235238115987266270156, + }, + qk: G1Point { + x: 4659055902458442531441402775737578699425178194825095727614170820825642511722, + y: 2854792741677890224744936867769988448466277171603338196705997958237726289995, + }, s: [ - G1Point { x: 9956956733383539527828528704346982954811130189972564286856709656590492390783, y: 16561941243254960602343726886092320314575403533969071986056337989120686189882 }, - G1Point { x: 20374741449540543556499660179263983188390530543434373332462519860014413516289, y: 8513455195626237033487485922347652175901063415320070186421575800344119284165 }, - G1Point { x: 17142227368091895742859950676182428277541451282467607716193503860042663502549, y: 12829174888268721439052436070745892278566825005271705420525175951948128646413 }, + G1Point { + x: 9956956733383539527828528704346982954811130189972564286856709656590492390783, + y: 16561941243254960602343726886092320314575403533969071986056337989120686189882, + }, + G1Point { + x: 20374741449540543556499660179263983188390530543434373332462519860014413516289, + y: 8513455195626237033487485922347652175901063415320070186421575800344119284165, + }, + G1Point { + x: 17142227368091895742859950676182428277541451282467607716193503860042663502549, + y: 12829174888268721439052436070745892278566825005271705420525175951948128646413, + }, ], qcp: [ - G1Point { x: 8032592088052402119252717716570843753829708920203926363246235929861264130729, y: 13824231045893282984607165590437904083147145250603202024200081150405773221988 }, + G1Point { + x: 8032592088052402119252717716570843753829708920203926363246235929861264130729, + y: 13824231045893282984607165590437904083147145250603202024200081150405773221988, + }, ], commit_constraint_idx: [25298720], g2_srs: [ - G2Point { x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930 }, - G2Point { x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979 }, + G2Point { + x_imag: 11559732032986387107991004021392285783925812861821192530917403151452391805634, + x_real: 10857046999023057135944570762232829481370756359578518086990519993285655852781, + y_imag: 4082367875863433681332203403145435568316851327593401208105741076214120093531, + y_real: 8495653923123431417604973247489272438418190587263600148770280649306958101930, + }, + G2Point { + x_imag: 15805639136721018565402881920352193254830339253282065586954346329754995870280, + x_real: 19089565590083334368588890253123139704298730990782503769911324779715431555531, + y_imag: 9779648407879205346559610309258181044130619080926897934572699915909528404984, + y_real: 6779728121489434657638426458390319301070371227460768374343986326751507916979, + }, ], - g1_srs: G1Point { x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, y: 11763105256161367503191792604679297387056316997144156930871823008787082098465 }, + g1_srs: G1Point { + x: 14312776538779914388377568895031746459131577658076416373430523308756343304251, + y: 11763105256161367503191792604679297387056316997144156930871823008787082098465, + }, } } @@ -36,19 +76,43 @@ fn parse_proof(proof_offset: u256) -> PlonkProof uses (evm: mut Evm) { PlonkProof { lro: [ G1Point { x: evm.calldataload(proof_offset), y: evm.calldataload(proof_offset + 0x20) }, - G1Point { x: evm.calldataload(proof_offset + 0x40), y: evm.calldataload(proof_offset + 0x60) }, - G1Point { x: evm.calldataload(proof_offset + 0x80), y: evm.calldataload(proof_offset + 0xa0) }, + G1Point { + x: evm.calldataload(proof_offset + 0x40), + y: evm.calldataload(proof_offset + 0x60), + }, + G1Point { + x: evm.calldataload(proof_offset + 0x80), + y: evm.calldataload(proof_offset + 0xa0), + }, ], - z: G1Point { x: evm.calldataload(proof_offset + 0x220), y: evm.calldataload(proof_offset + 0x240) }, + z: G1Point { + x: evm.calldataload(proof_offset + 0x220), + y: evm.calldataload(proof_offset + 0x240), + }, h: [ - G1Point { x: evm.calldataload(proof_offset + 0xc0), y: evm.calldataload(proof_offset + 0xe0) }, - G1Point { x: evm.calldataload(proof_offset + 0x100), y: evm.calldataload(proof_offset + 0x120) }, - G1Point { x: evm.calldataload(proof_offset + 0x140), y: evm.calldataload(proof_offset + 0x160) }, + G1Point { + x: evm.calldataload(proof_offset + 0xc0), + y: evm.calldataload(proof_offset + 0xe0), + }, + G1Point { + x: evm.calldataload(proof_offset + 0x100), + y: evm.calldataload(proof_offset + 0x120), + }, + G1Point { + x: evm.calldataload(proof_offset + 0x140), + y: evm.calldataload(proof_offset + 0x160), + }, ], bsb22_commitments: [ - G1Point { x: evm.calldataload(proof_offset + 0x320), y: evm.calldataload(proof_offset + 0x340) }, + G1Point { + x: evm.calldataload(proof_offset + 0x320), + y: evm.calldataload(proof_offset + 0x340), + }, ], - batched_h: G1Point { x: evm.calldataload(proof_offset + 0x280), y: evm.calldataload(proof_offset + 0x2a0) }, + batched_h: G1Point { + x: evm.calldataload(proof_offset + 0x280), + y: evm.calldataload(proof_offset + 0x2a0), + }, claimed_values: [ evm.calldataload(proof_offset + 0x180), evm.calldataload(proof_offset + 0x1a0), @@ -57,7 +121,10 @@ fn parse_proof(proof_offset: u256) -> PlonkProof uses (evm: mut Evm) { evm.calldataload(proof_offset + 0x200), evm.calldataload(proof_offset + 0x300), ], - z_shifted_h: G1Point { x: evm.calldataload(proof_offset + 0x2c0), y: evm.calldataload(proof_offset + 0x2e0) }, + z_shifted_h: G1Point { + x: evm.calldataload(proof_offset + 0x2c0), + y: evm.calldataload(proof_offset + 0x2e0), + }, z_shifted_value: evm.calldataload(proof_offset + 0x260), } } @@ -86,32 +153,66 @@ pub contract VKFieldTest { #[test] fn test_vk_field_via_contract() uses (evm: mut Evm) { let c = evm.create2(value: 0, args: (), salt: 0) - assert(c.inner != 0) + assert!(c.inner != 0) - let n: u256 = evm.call(addr: c, gas: 10000000, value: 0, message: VKFieldMsg::GetN {}) - assert(n == 33554432) + let n: u256 = evm.call( + addr: c, + gas: 10000000, + value: 0, + message: VKFieldMsg::GetN {}, + ) + assert!(n == 33554432) - let s0x: u256 = evm.call(addr: c, gas: 10000000, value: 0, message: VKFieldMsg::GetS0X {}) - assert(s0x == 9956956733383539527828528704346982954811130189972564286856709656590492390783) + let s0x: u256 = evm.call( + addr: c, + gas: 10000000, + value: 0, + message: VKFieldMsg::GetS0X {}, + ) + assert!( + s0x == 9956956733383539527828528704346982954811130189972564286856709656590492390783, + ) } msg PlonkMsg { - #[selector = sol("verifyPlonkProof(uint256,uint256,uint256,uint256,uint256,uint256)")] - VerifyPlonkProof { pi0: u256, pi1: u256, pi2: u256, pi3: u256, pi4: u256, proof_offset: u256 } -> bool, - #[selector = sol("debugGamma(uint256,uint256,uint256,uint256,uint256,uint256)")] - DebugGamma { pi0: u256, pi1: u256, pi2: u256, pi3: u256, pi4: u256, proof_offset: u256 } -> u256, + #[selector = sol( + "verifyPlonkProof(uint256,uint256,uint256,uint256,uint256,uint256)", + )] + VerifyPlonkProof { + pi0: u256, + pi1: u256, + pi2: u256, + pi3: u256, + pi4: u256, + proof_offset: u256, + } -> bool, + #[selector = sol( + "debugGamma(uint256,uint256,uint256,uint256,uint256,uint256)", + )] + DebugGamma { + pi0: u256, + pi1: u256, + pi2: u256, + pi3: u256, + pi4: u256, + proof_offset: u256, + } -> u256, } pub contract PlonkBench { init() {} recv PlonkMsg { - VerifyPlonkProof { pi0, pi1, pi2, pi3, pi4, proof_offset } -> bool uses (evm: mut Evm) { + VerifyPlonkProof { pi0, pi1, pi2, pi3, pi4, proof_offset } -> bool + uses (evm: mut Evm) + { let vk = example_vk() let pub_inputs: [u256; 5] = [pi0, pi1, pi2, pi3, pi4] let proof = parse_proof(proof_offset) verify(vk, proof, pub_inputs) } - DebugGamma { pi0, pi1, pi2, pi3, pi4, proof_offset } -> u256 uses (evm: mut Evm) { + DebugGamma { pi0, pi1, pi2, pi3, pi4, proof_offset } -> u256 + uses (evm: mut Evm) + { let vk = example_vk() let pub_inputs: [u256; 5] = [pi0, pi1, pi2, pi3, pi4] let proof = parse_proof(proof_offset) diff --git a/examples/verifier/fe/src/transcript.fe b/examples/verifier/fe/src/transcript.fe index d7c034b..2ea2b05 100644 --- a/examples/verifier/fe/src/transcript.fe +++ b/examples/verifier/fe/src/transcript.fe @@ -3,12 +3,10 @@ use super::field::F_Q /// /// Generic write method accepts any type that implements Writable. /// Raw memory operations use the effects system — callers provide the Evm capability. - use std::evm::crypto use std::evm::{Evm, RawMem} use ec::bn254::G1Point - /// A type that can serialize itself into a memory buffer. pub trait Writable { fn byte_size(self) -> u256 @@ -16,22 +14,25 @@ pub trait Writable { } impl Writable for u256 { - fn byte_size(self) -> u256 { 32 } - fn write_to(self, ptr: u256) uses (evm: mut Evm) { evm.mstore(addr: ptr, value: self) } + fn byte_size(self) -> u256 { + 32 + } + fn write_to(self, ptr: u256) uses (evm: mut Evm) { + evm.mstore(addr: ptr, value: self) + } } impl Writable for G1Point { - fn byte_size(self) -> u256 { 64 } + fn byte_size(self) -> u256 { + 64 + } fn write_to(self, ptr: u256) uses (evm: mut Evm) { evm.mstore(addr: ptr, value: self.x) evm.mstore(addr: ptr + 32, value: self.y) } } -pub struct Transcript { - pub ptr: u256, - pub len: u256, -} +pub struct Transcript { pub ptr: u256, pub len: u256 } impl Copy for Transcript {} @@ -39,24 +40,34 @@ impl Transcript { pub fn new() -> Transcript { Transcript { ptr: std::evm::alloc(4096), len: 0 } } - pub fn write(self, val: T) -> Transcript uses (evm: mut Evm) { val.write_to(ptr: self.ptr + self.len) Transcript { ptr: self.ptr, len: self.len + val.byte_size() } } - - pub fn write_label(self, label: u256, label_len: u256) -> Transcript uses (evm: mut Evm) { + pub fn write_label(self, label: u256, label_len: u256) -> Transcript + uses (evm: mut Evm) + { evm.mstore(addr: self.ptr + self.len, value: label) Transcript { ptr: self.ptr, len: self.len + label_len } } + // Convenience methods for common types. // These delegate to write but provide a familiar API. - pub fn write_u256(self, val: u256) -> Transcript uses (evm: mut Evm) { self.write(val: val) } - pub fn write_g1(self, p: G1Point) -> Transcript uses (evm: mut Evm) { self.write(val: p) } - + pub fn write_u256(self, val: u256) -> Transcript uses (evm: mut Evm){ + self.write(val: val) + } + pub fn write_g1(self, p: G1Point) -> Transcript uses (evm: mut Evm) { + self.write(val: p) + } pub fn squeeze(self) -> (u256, u256, Transcript) { - let hash = crypto::sha256(self.ptr, self.len) + let hash = match crypto::sha256(offset: self.ptr, len: self.len) { + Result::Ok(value,) => value, + Result::Err(_,) => { + assert!(false) + 0 + }, + } (hash % F_Q, hash, Transcript { ptr: self.ptr, len: 0 }) } } diff --git a/examples/verifier/test/Groth16Bench.t.sol b/examples/verifier/test/Groth16Bench.t.sol index 94a2299..669535c 100644 --- a/examples/verifier/test/Groth16Bench.t.sol +++ b/examples/verifier/test/Groth16Bench.t.sol @@ -23,14 +23,12 @@ contract Groth16BenchTest { vm.pauseGasMetering(); uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); - string[] memory cmd = new string[](7); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/verifier/test/PlonkBench.t.sol b/examples/verifier/test/PlonkBench.t.sol index 6d45376..de69b01 100644 --- a/examples/verifier/test/PlonkBench.t.sol +++ b/examples/verifier/test/PlonkBench.t.sol @@ -57,15 +57,13 @@ contract PlonkBenchTest { function setUp() public { vm.pauseGasMetering(); - uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(0)); - string[] memory cmd = new string[](7); + uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/examples/verifier/test/PlonkCompare.t.sol b/examples/verifier/test/PlonkCompare.t.sol index ffb16cb..0b673cc 100644 --- a/examples/verifier/test/PlonkCompare.t.sol +++ b/examples/verifier/test/PlonkCompare.t.sol @@ -62,15 +62,13 @@ contract PlonkCompareTest { solVerifier = new PlonkVerifier(); // Deploy Fe PlonkBench - uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(0)); - string[] memory cmd = new string[](7); + uint256 optLevel = vm.envOr("FE_SONA_OPT_LEVEL", uint256(2)); + string[] memory cmd = new string[](5); cmd[0] = vm.envOr("FE_BIN", "fe"); cmd[1] = "build"; - cmd[2] = "--backend"; - cmd[3] = "sonatina"; - cmd[4] = "-O"; - cmd[5] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; - cmd[6] = "fe"; + cmd[2] = "-O"; + cmd[3] = optLevel == 0 ? "0" : optLevel == 1 ? "1" : "2"; + cmd[4] = "fe"; vm.ffi(cmd); string[] memory readCmd = new string[](3); diff --git a/shared/ec/fe/src/bench_contract.fe b/shared/ec/fe/src/bench_contract.fe index 98dd733..e3d7d66 100644 --- a/shared/ec/fe/src/bench_contract.fe +++ b/shared/ec/fe/src/bench_contract.fe @@ -15,17 +15,11 @@ pub contract BN254Bench { init() {} recv BN254Msg { EcAdd { ax, ay, bx, by } -> (u256, u256) { - let result = bn254::g1_add( - a: G1Point { x: ax, y: ay }, - b: G1Point { x: bx, y: by }, - ) + let result = bn254::g1_add(a: G1Point { x: ax, y: ay }, b: G1Point { x: bx, y: by }) (result.x, result.y) } EcMul { px, py, s } -> (u256, u256) { - let result = bn254::g1_mul( - p: G1Point { x: px, y: py }, - s, - ) + let result = bn254::g1_mul(p: G1Point { x: px, y: py }, s) (result.x, result.y) } Negate { px, py } -> (u256, u256) { diff --git a/shared/ec/fe/src/bn254.fe b/shared/ec/fe/src/bn254.fe index 5f58465..6a810fc 100644 --- a/shared/ec/fe/src/bn254.fe +++ b/shared/ec/fe/src/bn254.fe @@ -3,15 +3,11 @@ /// /// Uses typed wrappers from std::evm::crypto for all precompile calls. /// The 4-pair pairing uses effects to avoid exceeding the EVM stack depth. - use core::ops::Add use std::evm::{Evm, RawMem} /// A G1 point (x, y) on the BN254 curve. -pub struct G1Point { - pub x: u256, - pub y: u256, -} +pub struct G1Point { pub x: u256, pub y: u256 } impl Copy for G1Point {} @@ -22,22 +18,22 @@ impl Add for G1Point { } impl G1Point { - pub fn origin() -> G1Point { G1Point { x: 0, y: 0 } } - - pub fn is_origin(self) -> bool { self.x == 0 && self.y == 0 } + pub fn origin() -> G1Point { + G1Point { x: 0, y: 0 } + } + pub fn is_origin(self) -> bool { + self.x == 0 && self.y == 0 + } /// Scalar multiply: self * s - pub fn scale(self, s: u256) -> G1Point { g1_mul(p: self, s) } + pub fn scale(self, s: u256) -> G1Point { + g1_mul(p: self, s) + } } /// A G2 point (x, y) where x and y are elements of F_p^2. /// Represented as (x_imag, x_real, y_imag, y_real). -pub struct G2Point { - pub x_imag: u256, - pub x_real: u256, - pub y_imag: u256, - pub y_real: u256, -} +pub struct G2Point { pub x_imag: u256, pub x_real: u256, pub y_imag: u256, pub y_real: u256 } impl Copy for G2Point {} @@ -59,26 +55,41 @@ pub fn negate(p: G1Point) -> G1Point { /// EC addition via precompile at address 0x06. pub fn g1_add(a: G1Point, b: G1Point) -> G1Point { - let (rx, ry) = std::evm::crypto::ec_add(a.x, a.y, b.x, b.y) - G1Point { x: rx, y: ry } + match std::evm::crypto::ec_add(ax: a.x, ay: a.y, bx: b.x, by: b.y) { + Result::Ok((rx, ry),) => G1Point { x: rx, y: ry }, + Result::Err(_,) => { + assert!(false) + G1Point { x: 0, y: 0 } + }, + } } /// EC scalar multiplication via precompile at address 0x07. pub fn g1_mul(p: G1Point, s: u256) -> G1Point { - let (rx, ry) = std::evm::crypto::ec_mul(p.x, p.y, s) - G1Point { x: rx, y: ry } + match std::evm::crypto::ec_mul(px: p.x, py: p.y, s) { + Result::Ok((rx, ry),) => G1Point { x: rx, y: ry }, + Result::Err(_,) => { + assert!(false) + G1Point { x: 0, y: 0 } + }, + } } /// EC pairing check via precompile at address 0x08. /// Checks: e(a1, b1) * e(a2, b2) == 1 -pub fn pairing_2( - a1: G1Point, b1: G2Point, - a2: G1Point, b2: G2Point, -) -> bool uses (evm: mut Evm) { +pub fn pairing_2(a1: G1Point, b1: G2Point, a2: G1Point, b2: G2Point) -> bool +uses (evm: mut Evm) +{ let ptr = std::evm::alloc(384) write_pair(ptr, a: a1, b: b1) write_pair(ptr: ptr + 192, a: a2, b: b2) - std::evm::crypto::ec_pairing(ptr, 384) + match std::evm::crypto::ec_pairing(offset: ptr, len: 384) { + Result::Ok(ok,) => ok, + Result::Err(_,) => { + assert!(false) + false + }, + } } /// Write a (G1, G2) pair to memory at the given offset. @@ -97,16 +108,28 @@ fn write_pair(ptr: u256, a: G1Point, b: G2Point) uses (evm: mut Evm) { /// Uses effects to build memory layout directly, avoiding EVM stack overflow /// from passing 24 scalar arguments through the call convention. pub fn pairing_4( - a1: G1Point, b1: G2Point, - a2: G1Point, b2: G2Point, - a3: G1Point, b3: G2Point, - a4: G1Point, b4: G2Point, -) -> bool uses (evm: mut Evm) { + a1: G1Point, + b1: G2Point, + a2: G1Point, + b2: G2Point, + a3: G1Point, + b3: G2Point, + a4: G1Point, + b4: G2Point, +) -> bool +uses (evm: mut Evm) +{ let ptr = std::evm::alloc(768) write_pair(ptr, a: a1, b: b1) write_pair(ptr: ptr + 192, a: a2, b: b2) write_pair(ptr: ptr + 384, a: a3, b: b3) write_pair(ptr: ptr + 576, a: a4, b: b4) - std::evm::crypto::ec_pairing(ptr, 768) + match std::evm::crypto::ec_pairing(offset: ptr, len: 768) { + Result::Ok(ok,) => ok, + Result::Err(_,) => { + assert!(false) + false + }, + } } From 74ca0a6728874d57a4e4df5979c7fc32d6359eb4 Mon Sep 17 00:00:00 2001 From: sbillig Date: Fri, 26 Jun 2026 15:11:45 -0700 Subject: [PATCH 2/2] Preserve PATH lookup for Fe gas scripts --- bench/compare_fe_gas.rb | 25 ++++++++++++++++++++----- bench/compare_sol_fe_gas.rb | 24 +++++++++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/bench/compare_fe_gas.rb b/bench/compare_fe_gas.rb index de60eb9..0f8ddc6 100755 --- a/bench/compare_fe_gas.rb +++ b/bench/compare_fe_gas.rb @@ -78,16 +78,31 @@ def usage exit 2 end -base_bin = File.expand_path(argv[0]) -candidate_bin = File.expand_path(argv[1]) +def executable_on_path?(command) + ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).any? do |dir| + path = File.join(dir, command) + File.file?(path) && File.executable?(path) + end +end + +def normalize_fe_bin(value) + if value.include?(File::SEPARATOR) + path = File.expand_path(value) + return path if File.executable?(path) -[base_bin, candidate_bin].each do |path| - next if File.executable?(path) + warn "Fe binary is not executable: #{path}" + exit 2 + end + + return value if executable_on_path?(value) - warn "Fe binary is not executable: #{path}" + warn "Fe binary was not found on PATH: #{value}" exit 2 end +base_bin = normalize_fe_bin(argv[0]) +candidate_bin = normalize_fe_bin(argv[1]) + def strip_ansi(line) line.gsub(/\e\[[0-9;]*[A-Za-z]/, "") end diff --git a/bench/compare_sol_fe_gas.rb b/bench/compare_sol_fe_gas.rb index 14d8ec6..63237b9 100755 --- a/bench/compare_sol_fe_gas.rb +++ b/bench/compare_sol_fe_gas.rb @@ -63,12 +63,30 @@ def usage exit 2 end -fe_bin = File.expand_path(argv.first || ENV.fetch("FE_BIN", "fe")) -unless File.executable?(fe_bin) - warn "Fe binary is not executable: #{fe_bin}" +def executable_on_path?(command) + ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).any? do |dir| + path = File.join(dir, command) + File.file?(path) && File.executable?(path) + end +end + +def normalize_fe_bin(value) + if value.include?(File::SEPARATOR) + path = File.expand_path(value) + return path if File.executable?(path) + + warn "Fe binary is not executable: #{path}" + exit 2 + end + + return value if executable_on_path?(value) + + warn "Fe binary was not found on PATH: #{value}" exit 2 end +fe_bin = normalize_fe_bin(argv.first || ENV.fetch("FE_BIN", "fe")) + def strip_ansi(line) line.gsub(/\e\[[0-9;]*[A-Za-z]/, "") end