Skip to content

Commit

Permalink
Merge branch 'main' into book/algebra-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Golovanov399 committed Dec 16, 2024
2 parents 06f9a48 + c9f6c33 commit 96b611a
Show file tree
Hide file tree
Showing 28 changed files with 258 additions and 147 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ecc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ jobs:
- name: Build openvm-ecc-guest crate for openvm
working-directory: extensions/ecc/guest
run: |
cargo openvm build
cargo openvm build --no-transpile
3 changes: 2 additions & 1 deletion benchmarks/src/bin/base64_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ fn main() -> Result<()> {
.with_extension(Keccak256TranspilerExtension),
)?;
let app_config = AppConfig {
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup),
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup)
.into(),
app_vm_config: Keccak256Rv32Config::default(),
leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security(agg_log_blowup)
.into(),
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/src/bin/bincode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn main() -> Result<()> {
};

let app_config = AppConfig {
app_fri_params,
app_fri_params: app_fri_params.into(),
app_vm_config: Rv32ImConfig::default(),
leaf_fri_params: leaf_fri_params.into(),
compiler_options,
Expand Down
3 changes: 2 additions & 1 deletion benchmarks/src/bin/ecrecover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ fn main() -> Result<()> {
)?;
// TODO: update sw_setup macros and read it from elf.
let vm_config = AppConfig {
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup),
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup)
.into(),
app_vm_config: Rv32ImEcRecoverConfig::for_curves(vec![SECP256K1_CONFIG.clone()]),
leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security(agg_log_blowup)
.into(),
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/src/bin/fib_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async fn main() -> Result<()> {
let max_segment_length = cli_args.max_segment_length.unwrap_or(1_000_000);

let app_config = AppConfig {
app_fri_params,
app_fri_params: app_fri_params.into(),
app_vm_config: Rv32ImConfig::with_public_values_and_segment_len(
NUM_PUBLIC_VALUES,
max_segment_length,
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/src/bin/fibonacci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn main() -> Result<()> {
};

let app_config = AppConfig {
app_fri_params,
app_fri_params: app_fri_params.into(),
app_vm_config: Rv32ImConfig::default(),
leaf_fri_params: leaf_fri_params.into(),
compiler_options,
Expand Down
3 changes: 2 additions & 1 deletion benchmarks/src/bin/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ fn main() -> Result<()> {
.with_extension(Keccak256TranspilerExtension),
)?;
let app_config = AppConfig {
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup),
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup)
.into(),
app_vm_config: Keccak256Rv32Config::default(),
leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security(agg_log_blowup)
.into(),
Expand Down
3 changes: 2 additions & 1 deletion benchmarks/src/bin/revm_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ fn main() -> Result<()> {
.with_extension(Rv32IoTranspilerExtension),
)?;
let app_config = AppConfig {
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup),
app_fri_params: FriParameters::standard_with_100_bits_conjectured_security(app_log_blowup)
.into(),
app_vm_config: Keccak256Rv32Config::default(),
leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security(1).into(),
compiler_options: CompilerOptions::default().with_cycle_tracker(),
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/src/bin/rkyv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn main() -> Result<()> {
};

let app_config = AppConfig {
app_fri_params,
app_fri_params: app_fri_params.into(),
app_vm_config: Rv32ImConfig::default(),
leaf_fri_params: leaf_fri_params.into(),
compiler_options,
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/src/bin/verify_fibair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn main() -> Result<()> {
..Default::default()
};
let app_config = AppConfig {
app_fri_params: leaf_fri_params,
app_fri_params: leaf_fri_params.into(),
app_vm_config,
leaf_fri_params: leaf_fri_params.into(),
compiler_options,
Expand Down
6 changes: 3 additions & 3 deletions benchmarks/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ where
VC::Executor: Chip<SC>,
VC::Periphery: Chip<SC>,
{
counter!("fri.log_blowup").absolute(app_config.app_fri_params.log_blowup as u64);
let engine = BabyBearPoseidon2Engine::new(app_config.app_fri_params);
counter!("fri.log_blowup").absolute(app_config.app_fri_params.fri_params.log_blowup as u64);
let engine = BabyBearPoseidon2Engine::new(app_config.app_fri_params.fri_params);
let vm = VirtualMachine::new(engine, app_config.app_vm_config.clone());
// 1. Generate proving key from config.
let app_pk = time(gauge!("keygen_time_ms"), || {
AppProvingKey::keygen(app_config.clone())
});
// 2. Commit to the exe by generating cached trace for program.
let committed_exe = time(gauge!("commit_exe_time_ms"), || {
commit_app_exe(app_config.app_fri_params, exe)
commit_app_exe(app_config.app_fri_params.fri_params, exe)
});
// 3. Executes runtime once with full metric collection for flamegraphs (slow).
// 4. Executes runtime again without metric collection and generate trace.
Expand Down
7 changes: 6 additions & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@

# Using Extensions

- [Customizable Extensions](./custom-extensions/overview.md)
- [Overview](./custom-extensions/overview.md)
- [Keccak](./custom-extensions/keccak.md)
- [Big Integer](./custom-extensions/bigint.md)
- [Algebra](./custom-extensions/algebra.md)
- [Elliptic Curve Cryptography](./custom-extensions/ecc.md)
- [Elliptic Curve Pairing](./custom-extensions/pairing.md)

# Advanced Usage

Expand Down
87 changes: 86 additions & 1 deletion book/src/custom-extensions/ecc.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
# OpenVM ECC

For elliptic curve cryptography, the `openvm-ecc` crate provides macros similar to those in [`openvm-algebra`](./algebra.md):
The OpenVM Elliptic Curve Cryptography Extension provides support for elliptic curve operations through the `openvm-ecc-guest` crate.

## Available traits and methods

- `Group` trait:
This represents an element of a [group](<https://en.wikipedia.org/wiki/Group_(mathematics)>) where the operation is addition. Therefore the trait includes functions for `add`, `sub`, and `double`.

- `IDENTITY` is the identity element of the group.

- `CyclicGroup` trait:
It's a group that has a generator, so it defines `GENERATOR` and `NEG_GENERATOR`.

- `WeierstrassPoint` trait:
It represents an affine point on a Weierstrass elliptic curve and it extends `Group`.

- `Coordinate` type is the type of the coordinates of the point, and it implements `IntMod`.
- `x()`, `y()` are used to get the affine coordinates
- `from_xy` is a constructor for the point, which checks if the point is either identity or on the affine curve.
- The point supports elliptic curve operations through intrinsic functions `add_ne_nonidentity` and `double_nonidentity`.
- `decompress`: Sometimes an elliptic curve point is compressed and represented by its `x` coordinate and the odd/even parity of the `y` coordinate. `decompress` is used to decompress the point back to `(x, y)`.

- `msm`: for multi-scalar multiplication.

- `ecdsa`: for doing ECDSA signature verification and public key recovery from signature.

## Macros

For elliptic curve cryptography, the `openvm-ecc-guest` crate provides macros similar to those in [`openvm-algebra-guest`](./algebra.md):

1. **Declare**: Use `sw_declare!` to define elliptic curves over the previously declared moduli. For example:

Expand All @@ -12,6 +39,7 @@ sw_declare! {
```

Each declared curve must specify the `mod_type` (implementing `IntMod`) and a constant `b` for the Weierstrass curve equation $y^2 = x^3 + b$.
This creates `Bls12_381G1Affine` and `Bn254G1Affine` structs which implement the `Group` and `WeierstrassPoint` traits. The underlying memory layout of the structs uses the memory layout of the `Bls12_381Fp` and `Bn254Fp` structs, respectively.

2. **Init**: Called once, it enumerates these curves and allows the compiler to produce optimized instructions:

Expand All @@ -28,3 +56,60 @@ sw_init! {
- `sw_declare!`: Declares elliptic curve structures.
- `sw_init!`: Initializes them once, linking them to the underlying moduli.
- `setup_sw_<i>()`/`setup_all_curves()`: Secures runtime correctness.

To use elliptic curve operations on a struct defined with `sw_declare!`, it is expected that the struct for the curve's coordinate field was defined using `moduli_declare!`. In particular, the coordinate field needs to be initialized and set up as described in the [algebra extension](./algebra.md) chapter.

For the basic operations provided by the `WeierstrassPoint` trait, the scalar field is not needed. For the ECDSA functions in the `ecdsa` module, the scalar field must also be declared, initialized, and set up.

## Example program

See a working example [here](https://github.com/openvm-org/openvm/blob/main/crates/toolchain/tests/programs/examples/ec.rs).

To use the ECC extension, add the following dependencies to `Cargo.toml`:

```toml
openvm-algebra-guest = { git = "https://github.com/openvm-org/openvm.git" }
openvm-ecc-guest = { git = "https://github.com/openvm-org/openvm.git", features = ["k256"] }
```

One can define their own ECC structs but we will use the Secp256k1 struct from `openvm-ecc-guest` and thus the `k256` feature should be enabled.

```rust
use openvm_ecc_guest::{
k256::{Secp256k1Coord, Secp256k1Point, Secp256k1Scalar},
Group, weierstrass::WeierstrassPoint,
};

openvm_algebra_guest::moduli_setup::moduli_init! {
"0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F",
"0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141"
}

openvm_ecc_guest::sw_setup::sw_init! {
Secp256k1Coord,
}
```

We `moduli_init!` both the coordinate and scalar field because they were declared in the `k256` module, although we will not be using the scalar field below.

With the above we can start doing elliptic curve operations like adding points:

```rust
pub fn main() {
setup_all_moduli();
setup_all_curves();
let x1 = Secp256k1Coord::from_u32(1);
let y1 = Secp256k1Coord::from_le_bytes(&hex!(
"EEA7767E580D75BC6FDD7F58D2A84C2614FB22586068DB63B346C6E60AF21842"
));
let p1 = Secp256k1Point::from_xy_nonidentity(x1, y1).unwrap();

let x2 = Secp256k1Coord::from_u32(2);
let y2 = Secp256k1Coord::from_le_bytes(&hex!(
"D1A847A8F879E0AEE32544DA5BA0B3BD1703A1F52867A5601FF6454DD8180499"
));
let p2 = Secp256k1Point::from_xy_nonidentity(x2, y2).unwrap();

let p3 = &p1 + &p2;
}
```
14 changes: 13 additions & 1 deletion book/src/custom-extensions/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

You can seamlessly integrate certain performance-optimized extensions maintained by the OpenVM team to enhance your arithmetic operations and cryptographic computations.

Certain arithmetic operations, particularly modular arithmetic, can be optimized significantly when the modulus is known at compile time. This approach requires a framework to inform the compiler about all the moduli and associated arithmetic structures we intend to use. To achieve this, three steps are involved:
In this chapter, we will explain how to use the following existing extensions:

- [`openvm-keccak-guest`](./keccak.md) - Keccak256 hash function.
- [`openvm-bigint-guest`](./bigint.md) - Big integer arithmetic for 256-bit signed and unsigned integers.
- [`openvm-algebra-guest`](./algebra.md) - Modular arithmetic and complex field extensions.
- [`openvm-ecc-guest`](./ecc.md) - Elliptic curve cryptography.
- [`openvm-pairing-guest`](./pairing.md) - Elliptic curve optimal Ate pairings.

Some extensions such as `openvm-keccak-guest` and `openvm-bigint-guest` can be enabled without specifying any additional configuration.

On the other hand certain arithmetic operations, particularly modular arithmetic, can be optimized significantly when the modulus is known at compile time. This approach requires a framework to inform the compiler about all the moduli and associated arithmetic structures we intend to use. To achieve this, three steps are involved:

1. **Declare**: Introduce a modular arithmetic or related structure, along with its modulus and functionality. This can be done in any library or binary file.
2. **Init**: Performed exactly once in the final binary. It aggregates all previously declared structures, assigns them stable indices, and sets up linkage so that they can be referenced in generated code.
Expand All @@ -17,3 +27,5 @@ The list of existing extensions:
- [OpenVM Keccak](./keccak.md)
- [OpenVM Pairing](./pairing.md)
- [OpenVM ECC](./ecc.md)

Our design for the configuration procedure above was inspired by the [EVMMAX proposal](https://github.com/jwasinger/EIPs/blob/evmmax-2/EIPS/eip-6601.md).
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
[app_fri_params]
log_blowup = 2
num_queries = 42
proof_of_work_bits = 16

[app_vm_config.rv32i]
[app_vm_config.rv32m]
range_tuple_checker_sizes = [256, 2048]
6 changes: 2 additions & 4 deletions crates/cli/src/commands/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use openvm_stark_sdk::{
};

use super::build::{build, BuildArgs};
use crate::util::{write_status, Input};
use crate::util::{classical_exe_path, write_status, Input};

#[derive(Clone, Parser)]
#[command(name = "bench", about = "(default) Build and prove a program")]
Expand All @@ -43,10 +43,8 @@ impl BenchCmd {
if self.profile {
setup_tracing();
}
let mut build_args = self.build_args.clone();
build_args.transpile = true;
let elf_path = build(&self.build_args)?.unwrap();
let exe_path = build_args.exe_path(&elf_path);
let exe_path = classical_exe_path(&elf_path);
let exe = read_exe_from_file(&exe_path)?;

// TODO: read from openvm.toml
Expand Down
55 changes: 19 additions & 36 deletions crates/cli/src/commands/build.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
use std::{
fs::read,
path::{Path, PathBuf},
};
use std::{fs::read, path::PathBuf};

use clap::Parser;
use eyre::Result;
use openvm_build::{
build_guest_package, find_unique_executable, get_package, GuestOptions, TargetFilter,
};
use openvm_rv32im_transpiler::{Rv32ITranspilerExtension, Rv32MTranspilerExtension};
use openvm_sdk::{
config::{AppConfig, SdkVmConfig},
fs::write_exe_to_file,
Sdk,
};
use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE, transpiler::Transpiler};
use openvm_sdk::{fs::write_exe_to_file, Sdk};
use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE};

use crate::{default::DEFAULT_MANIFEST_DIR, util::read_to_struct_toml};
use crate::{
default::{DEFAULT_APP_CONFIG_PATH, DEFAULT_APP_EXE_PATH, DEFAULT_MANIFEST_DIR},
util::read_config_toml_or_default,
};

#[derive(Parser)]
#[command(name = "build", about = "Compile an OpenVM program")]
Expand Down Expand Up @@ -53,34 +48,28 @@ pub struct BuildArgs {
#[arg(
long,
default_value = "false",
help = "Transpiles the program after building when set"
help = "Skips transpilation into exe when set"
)]
pub transpile: bool,
pub no_transpile: bool,

#[arg(
long,
default_value = DEFAULT_APP_CONFIG_PATH,
help = "Path to the SDK config .toml file that specifies the transpiler extensions"
)]
pub transpiler_config: Option<PathBuf>,
pub config: PathBuf,

#[arg(
long,
help = "Output path for the transpiled program (default: <ELF base path>.vmexe)"
default_value = DEFAULT_APP_EXE_PATH,
help = "Output path for the transpiled program"
)]
pub transpile_to: Option<PathBuf>,
pub exe_output: PathBuf,

#[arg(long, default_value = "release", help = "Build profile")]
pub profile: String,
}

impl BuildArgs {
pub fn exe_path(&self, elf_path: &Path) -> PathBuf {
self.transpile_to
.clone()
.unwrap_or_else(|| elf_path.with_extension("vmexe"))
}
}

#[derive(Clone, clap::Args)]
#[group(required = false, multiple = false)]
pub struct BinTypeFilter {
Expand Down Expand Up @@ -129,23 +118,17 @@ pub(crate) fn build(build_args: &BuildArgs) -> Result<Option<PathBuf>> {
}
};

if build_args.transpile {
if !build_args.no_transpile {
let elf_path = elf_path?;
println!("[openvm] Transpiling the package...");
let output_path = build_args.exe_path(&elf_path);
let transpiler = if let Some(transpiler_config) = build_args.transpiler_config.clone() {
let app_config: AppConfig<SdkVmConfig> = read_to_struct_toml(&transpiler_config)?;
app_config.app_vm_config.transpiler()
} else {
Transpiler::default()
.with_extension(Rv32ITranspilerExtension)
.with_extension(Rv32MTranspilerExtension)
};
let output_path = &build_args.exe_output;
let app_config = read_config_toml_or_default(&build_args.config)?;
let transpiler = app_config.app_vm_config.transpiler();

let data = read(elf_path.clone())?;
let elf = Elf::decode(&data, MEM_SIZE as u32)?;
let exe = Sdk.transpile(elf, transpiler)?;
write_exe_to_file(exe, &output_path)?;
write_exe_to_file(exe, output_path)?;

println!(
"[openvm] Successfully transpiled to {}",
Expand Down
Loading

0 comments on commit 96b611a

Please sign in to comment.