diff --git a/results.md b/results.md index e79b8a5..e811968 100644 --- a/results.md +++ b/results.md @@ -22,18 +22,18 @@ How to read this report: - Grinding (bits): 0 - Field: Goldilocks³ - Rate (ρ): 0.5 -- Trace length (H): $2^{22}$ +- Trace length (H): $2^{16}$ - FRI folding factor: 16 - FRI early stop degree: 32 - Batching: Powers -**Proof Size Estimate:** 992 KiB, where 1 KiB = 1024 bytes +**Proof Size Estimate:** 24678 KiB, where 1 KiB = 1024 bytes -| regime | total | ALI | DEEP | FRI batching round | FRI commit rounds (×5) | FRI query phase | -| --- | --- | --- | --- | --- | --- | --- | -| UDR | 53 | 185 | 167 | 162 | 165 | 53 | -| JBR | 58 | 181 | 163 | 142 | 159 | 58 | -| best attack | 128 | — | — | — | — | — | +| regime | total | ALI | DEEP | FRI batching round | FRI commit round 1 | FRI commit round 2 | FRI commit round 3 | FRI query phase | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| UDR | 53 | 180 | 173 | 163 | 175 | 179 | 183 | 53 | +| JBR | 58 | 175 | 169 | 141 | 153 | 157 | 161 | 58 | +| best attack | 128 | — | — | — | — | — | — | — | ## Miden @@ -51,11 +51,11 @@ How to read this report: **Proof Size Estimate:** 175 KiB, where 1 KiB = 1024 bytes -| regime | total | ALI | DEEP | FRI batching round | FRI commit rounds (×7) | FRI query phase | -| --- | --- | --- | --- | --- | --- | --- | -| UDR | 38 | 121 | 106 | 100 | 105 | 38 | -| JBR | 55 | 115 | 101 | 77 | 98 | 55 | -| best attack | 96 | — | — | — | — | — | +| regime | total | ALI | DEEP | FRI batching round | FRI commit round 1 | FRI commit round 2 | FRI commit round 3 | FRI commit round 4 | FRI commit round 5 | FRI commit round 6 | FRI commit round 7 | FRI query phase | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| UDR | 38 | 121 | 106 | 100 | 107 | 109 | 111 | 113 | 115 | 117 | 119 | 38 | +| JBR | 55 | 115 | 101 | 76 | 83 | 85 | 87 | 89 | 91 | 93 | 95 | 55 | +| best attack | 96 | — | — | — | — | — | — | — | — | — | — | — | ## RISC0 @@ -73,11 +73,11 @@ How to read this report: **Proof Size Estimate:** 576 KiB, where 1 KiB = 1024 bytes -| regime | total | ALI | DEEP | FRI batching round | FRI commit rounds (×4) | FRI query phase | -| --- | --- | --- | --- | --- | --- | --- | -| UDR | 33 | 115 | 100 | 92 | 96 | 33 | -| JBR | 47 | 110 | 95 | 70 | 90 | 47 | -| best attack | 99 | — | — | — | — | — | +| regime | total | ALI | DEEP | FRI batching round | FRI commit round 1 | FRI commit round 2 | FRI commit round 3 | FRI commit round 4 | FRI query phase | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| UDR | 33 | 115 | 100 | 92 | 100 | 104 | 108 | 112 | 33 | +| JBR | 47 | 110 | 95 | 69 | 78 | 82 | 86 | 90 | 47 | +| best attack | 99 | — | — | — | — | — | — | — | — | ## DummyWHIR diff --git a/soundcalc/__main__.py b/soundcalc/__main__.py index 361fb66..70cda2d 100644 --- a/soundcalc/__main__.py +++ b/soundcalc/__main__.py @@ -1,10 +1,21 @@ from __future__ import annotations +import argparse from .main import main - if __name__ == "__main__": - main() + parser = argparse.ArgumentParser( + description="soundcalc - Analyze zkVM security levels" + ) + parser.add_argument( + "--print-only", + nargs="+", + help="Only print specified zkVMs to console (e.g., --print-only ZisK Miden)", + default=None, + ) + + args = parser.parse_args() + main(print_only=args.print_only) diff --git a/soundcalc/main.py b/soundcalc/main.py index 06e19b8..7102118 100644 --- a/soundcalc/main.py +++ b/soundcalc/main.py @@ -46,7 +46,7 @@ def print_summary_for_zkvm(zkvm: zkVM, security_levels: dict | None = None) -> N print("") print("") -def main() -> None: +def main(print_only: list[str] | None = None) -> None: """ Main entry point for soundcalc @@ -67,7 +67,10 @@ def main() -> None: # Analyze each zkVM for zkvm in zkvms: security_levels = zkvm.get_security_levels() - print_summary_for_zkvm(zkvm, security_levels) + + if print_only is None or zkvm.get_name() in print_only: + print_summary_for_zkvm(zkvm, security_levels) + sections[zkvm.get_name()] = (zkvm, security_levels) # Generate and save markdown report diff --git a/soundcalc/regimes/capacity_bound.py b/soundcalc/regimes/capacity_bound.py index 75f64cc..4d70aee 100644 --- a/soundcalc/regimes/capacity_bound.py +++ b/soundcalc/regimes/capacity_bound.py @@ -81,7 +81,7 @@ def get_batching_error(self, params: FRIParameters) -> float: error *= params.num_functions ** C2 return error - def get_commit_phase_error(self, params: FRIParameters) -> float: + def get_commit_phase_error(self, params: FRIParameters, round_idx: int) -> float: """ Returns the error for the FRI commit phase for this regime. """ diff --git a/soundcalc/regimes/fri_regime.py b/soundcalc/regimes/fri_regime.py index 62ab212..9996f11 100644 --- a/soundcalc/regimes/fri_regime.py +++ b/soundcalc/regimes/fri_regime.py @@ -64,7 +64,7 @@ def get_batching_error(self, params: FRIParameters) -> float: """ raise NotImplementedError - def get_commit_phase_error(self, params: FRIParameters) -> float: + def get_commit_phase_error(self, params: FRIParameters, round_idx: int) -> float: """ Returns the error for the FRI commit phase for this regime. """ @@ -85,7 +85,7 @@ def get_rbr_levels(self, params: FRIParameters) -> dict[str, int]: # Compute FRI error for folding / commit phase FRI_rounds = params.FRI_rounds_n for i in range(FRI_rounds): - bits[f"FRI commit round {i+1}"] = get_bits_of_security_from_error(self.get_commit_phase_error(params)) + bits[f"FRI commit round {i+1}"] = get_bits_of_security_from_error(self.get_commit_phase_error(params, i)) # Compute FRI error for query phase theta = self.get_theta(params) diff --git a/soundcalc/regimes/johnson_bound.py b/soundcalc/regimes/johnson_bound.py index e755be9..8157648 100644 --- a/soundcalc/regimes/johnson_bound.py +++ b/soundcalc/regimes/johnson_bound.py @@ -49,6 +49,9 @@ def get_theta(self, params: FRIParameters) -> float: """ Returns the theta for the query phase error. """ + + # This is a number in the range [(1 - ρ)/2, 1 - √ρ) + m = self._get_m() alpha, theta = self._get_alpha_and_theta(params.rho, m) return theta @@ -74,27 +77,39 @@ def get_batching_error(self, params: FRIParameters) -> float: # # Then easiest way to see the difference is to compare Theorems 1.5 and 1.6. + # See Theorem 4.2 of BCHKS25. + m = self._get_m() rho = params.rho - error = ((m + 0.5) ** 5) / (3 * (rho ** 1.5)) * (params.D) / params.F + error = ((2*(m + 0.5) ** 5 + 3*(m + 0.5)*rho)) / (3 * rho * math.sqrt(rho)) * (params.D) / params.F if params.power_batching: error *= params.num_functions return error - def get_commit_phase_error(self, params: FRIParameters) -> float: + def get_commit_phase_error(self, params: FRIParameters, round_idx: int) -> float: """ Returns the error for the FRI commit phase for this regime. """ - # See Theorem 8.3 of BCIKS20. - # Also, seen in Theorem 2 of Ha22, and Theorem 1 of eSTARK paper. + # The same as the batching error, but with the domain adjusted to each folding round. + # In particular, if we let 𝜀 be the batching error we can obtain the commit error as: + # 𝜀 * (folding_factorᵢ - 1) / Π folding_factors # Note: This function is also used by CBR, but there is no good foundation for it yet. # TODO Find a better formula for CBR. - # TODO: check this formula carefully m = self._get_m() - error = (2 * m + 1) * (params.D + 1) * params.folding_factor / (math.sqrt(params.rho) * params.F) + rho = params.rho + error = ((2*(m + 0.5) ** 5 + 3*(m + 0.5)*rho)) / (3 * rho * math.sqrt(rho)) * (params.D) / params.F + + # Compute accumulated folding factor up to round_idx + # TODO: This assumes all folding factors are the same. Generalize!! + folding_factor = params.folding_factor + acc_folding_factor = 1 + for i in range(round_idx + 1): + acc_folding_factor *= folding_factor + + error *= (folding_factor - 1) / acc_folding_factor return error @@ -123,6 +138,6 @@ def _get_minimal_m_plus(self, r_plus: float, alpha: float) -> int: # let m_plus = 1.0 / (params.biggest_combo * (alpha / rho_plus.sqrt() - 1.0)); return math.ceil(1 / (2 * (alpha / math.sqrt(r_plus) - 1))) - def _get_m(self): + def _get_m(self) -> int: m = get_johnson_parameter_m() # TODO DK: it is not clear if this is the right m to use. To investigate. return m diff --git a/soundcalc/regimes/unique_decoding.py b/soundcalc/regimes/unique_decoding.py index a72e45c..6d02b73 100644 --- a/soundcalc/regimes/unique_decoding.py +++ b/soundcalc/regimes/unique_decoding.py @@ -53,13 +53,19 @@ def get_batching_error(self, params: FRIParameters) -> float: error *= params.num_functions return error - def get_commit_phase_error(self, params: FRIParameters) -> float: + def get_commit_phase_error(self, params: FRIParameters, round_idx: int) -> float: """ Returns the error for the FRI commit phase for this regime. """ - D = params.D - FRI_folding_factor = params.folding_factor - F = params.F - fri_folding_error = (D * (FRI_folding_factor - 1)) / F - return fri_folding_error + error = params.D / params.F + + # Compute accumulated folding factor up to round_idx + # TODO: This assumes all folding factors are the same. Generalize!! + folding_factor = params.folding_factor + acc_folding_factor = 1 + for i in range(round_idx + 1): + acc_folding_factor *= folding_factor + + error *= (folding_factor - 1) / acc_folding_factor + return error diff --git a/soundcalc/zkvms/zisk.py b/soundcalc/zkvms/zisk.py index a073ff8..8522878 100644 --- a/soundcalc/zkvms/zisk.py +++ b/soundcalc/zkvms/zisk.py @@ -13,15 +13,11 @@ def default() -> "ZiskPreset": For ZisK, we populate the trace parameters from its constraint description: https://github.com/0xPolygonHermez/zisk/blob/main/pil/zisk.pil - - The rest of the parameters are adapted from the "eSTARK: Extending STARKs with Arguments" paper: - https://eprint.iacr.org/2023/474 """ - field = GOLDILOCKS_3 - # We generate a STARK proof of different traces with different parameters. - # Therefore, in what follows, I put the parameters for our worst-case trace in terms of area. + # Therefore, in what follows, we put the parameters for our worst-case trace + # in terms of columns and batch size. # The blowup factor is dinamically chosen by this tool: # https://github.com/0xPolygonHermez/pil2-proofman-js/blob/main/src/pil2-stark/pil_info/imPolsCalculation/imPolynomials.js#L96 @@ -30,27 +26,25 @@ def default() -> "ZiskPreset": blowup_factor = 2 rho = 1 / blowup_factor - trace_length = 1 << 22 - num_columns = 66 - batch_size = num_columns + 2 # +2 for the composition polynomials + trace_length = 1 << 16 + num_columns = 3024 + batch_size = 4065 - num_queries = 128 // int(math.log2(blowup_factor)) + num_queries = 128 - AIR_max_degree = blowup_factor + 1 + AIR_max_degree = 3 FRI_folding_factor = 2**4 FRI_early_stop_degree = 2**5 - max_combo = 3 - - hash_size_bits = 256 # TODO: check if that is actually true + max_combo = 2 cfg = FRIBasedVMConfig( name="ZisK", - hash_size_bits=hash_size_bits, + hash_size_bits=256, rho=rho, trace_length=trace_length, - field=field, + field=GOLDILOCKS_3, num_columns=num_columns, batch_size=batch_size, power_batching=True,