Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
weikengchen committed Jan 17, 2024
1 parent c42dcd9 commit cfbd4ce
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 2 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ Cargo.lock
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

.idea
.idea

.python-version
19 changes: 18 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,21 @@ name = "l2_r0prover"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.20."}
pyo3 = { version = "0.20.2", features = ["extension-module", "anyhow", "auto-initialize"] }
risc0-binfmt = { git = "https://github.com/l2iterative/risc0/", branch="no-rust-runtime-for-host" }
risc0-zkvm-platform = { git = "https://github.com/l2iterative/risc0/", branch="no-rust-runtime-for-host" }
risc0-zkvm = { git = "https://github.com/l2iterative/risc0/", branch="no-rust-runtime-for-host", features = ["prove", "metal"] }
anyhow = "1.0.79"

[profile.dev]
opt-level = 3

[profile.dev.build-override]
opt-level = 3

[profile.release]
lto = true
debug = true

[profile.release.build-override]
opt-level = 3
12 changes: 12 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"

[project]
name = "l2_r0prover"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
24 changes: 24 additions & 0 deletions src/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use anyhow::Result;
use pyo3::prelude::*;
use risc0_binfmt::{MemoryImage, Program};
use risc0_zkvm_platform::memory::GUEST_MAX_MEM;
use risc0_zkvm_platform::PAGE_SIZE;

#[pyclass]
pub struct Image {
memory_image: MemoryImage,
}

impl Image {
pub fn from_elf(elf: &[u8]) -> Result<Self> {
let program = Program::load_elf(elf, GUEST_MAX_MEM as u32)?;
let image = MemoryImage::new(&program, PAGE_SIZE as u32)?;
Ok(Self {
memory_image: image,
})
}

pub fn get_image(&self) -> MemoryImage {
self.memory_image.clone()
}
}
50 changes: 50 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
mod image;
mod segment;
mod session;

use crate::image::Image;
use crate::segment::{Segment, SegmentReceipt};
use crate::session::SessionInfo;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use risc0_zkvm::{ExecutorEnv, ExecutorImpl, FileSegmentRef, SimpleSegmentRef, VerifierContext};

#[pyfunction]
fn load_image_from_elf(elf: &PyBytes) -> PyResult<Image> {
Ok(Image::from_elf(elf.as_bytes())?)
}

#[pyfunction]
fn execute_with_input(image: &Image, input: &PyBytes) -> PyResult<(Vec<Segment>, SessionInfo)> {
let env = ExecutorEnv::builder()
.write_slice(input.as_bytes())
.build()?;

let mut exec = ExecutorImpl::new(env, image.get_image())?;

let time = std::time::Instant::now();
let session = exec.run()?;

let mut segments = vec![];
for segment_ref in session.segments.iter() {
segments.push(Segment::new(segment_ref.resolve()?));
}

let session_info = SessionInfo::new(&session)?;
Ok((segments, session_info))
}

#[pyfunction]
fn prove_segment(segment: &Segment) -> PyResult<SegmentReceipt> {
let verifier_context = VerifierContext::default();
let res = segment.prove(&verifier_context)?;
Ok(res)
}

#[pymodule]
fn l2_r0prover(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(load_image_from_elf, m)?)?;
m.add_function(wrap_pyfunction!(execute_with_input, m)?)?;
m.add_function(wrap_pyfunction!(prove_segment, m)?)?;
Ok(())
}
29 changes: 29 additions & 0 deletions src/segment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use anyhow::Result;
use pyo3::prelude::*;
use risc0_zkvm::VerifierContext;

#[pyclass]
pub struct Segment {
segment: risc0_zkvm::Segment,
}

impl Segment {
pub fn new(segment: risc0_zkvm::Segment) -> Self {
Self { segment }
}

pub fn prove(&self, verifier_context: &VerifierContext) -> Result<SegmentReceipt> {
Ok(SegmentReceipt::new(self.segment.prove(verifier_context)?))
}
}

#[pyclass]
pub struct SegmentReceipt {
segment_receipt: risc0_zkvm::SegmentReceipt,
}

impl SegmentReceipt {
pub fn new(segment_receipt: risc0_zkvm::SegmentReceipt) -> Self {
Self { segment_receipt }
}
}
82 changes: 82 additions & 0 deletions src/session.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use anyhow::Result;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;

#[pyclass]
#[derive(Clone)]
pub struct ExitCode {
exit_code: risc0_zkvm::ExitCode,
}

impl ExitCode {
pub fn new(exit_code: risc0_zkvm::ExitCode) -> Self {
Self { exit_code }
}
}

#[pymethods]
impl ExitCode {
pub fn is_system_split(&self) -> PyResult<bool> {
Ok(matches!(self.exit_code, risc0_zkvm::ExitCode::SystemSplit))
}

pub fn is_session_limit(&self) -> PyResult<bool> {
Ok(matches!(self.exit_code, risc0_zkvm::ExitCode::SessionLimit))
}

pub fn is_paused(&self) -> PyResult<bool> {
Ok(matches!(self.exit_code, risc0_zkvm::ExitCode::Paused(_)))
}

pub fn get_paused_code(&self) -> PyResult<u32> {
match self.exit_code {
risc0_zkvm::ExitCode::Paused(v) => Ok(v),
_ => Err(PyValueError::new_err("The exit code is not for pausing.")),
}
}

pub fn is_halted(&self) -> PyResult<bool> {
Ok(matches!(self.exit_code, risc0_zkvm::ExitCode::Halted(_)))
}

pub fn get_halted_code(&self) -> PyResult<u32> {
match self.exit_code {
risc0_zkvm::ExitCode::Halted(v) => Ok(v),
_ => Err(PyValueError::new_err("The exit code is not for halting.")),
}
}

pub fn is_fault(&self) -> PyResult<bool> {
Ok(matches!(self.exit_code, risc0_zkvm::ExitCode::Fault))
}
}

#[pyclass]
pub struct SessionInfo {
journal: Vec<u8>,
exit_code: ExitCode,
}

impl SessionInfo {
pub fn new(session: &risc0_zkvm::Session) -> Result<Self> {
let journal = match &session.journal {
Some(v) => v.bytes.clone(),
None => vec![],
};
Ok(Self {
journal,
exit_code: ExitCode::new(session.exit_code),
})
}
}

#[pymethods]
impl SessionInfo {
pub fn get_journal(&self) -> PyResult<Vec<u8>> {
Ok(self.journal.clone())
}

pub fn get_exit_code(&self) -> PyResult<ExitCode> {
Ok(self.exit_code.clone())
}
}
Binary file added test/elf
Binary file not shown.
29 changes: 29 additions & 0 deletions test/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import l2_r0prover
import time

print("loading the ELF...")
elf_handle = open("elf", mode="rb")
elf = elf_handle.read()
image = l2_r0prover.load_image_from_elf(elf)

print("assembling the input...")
input = bytes([33, 0, 0, 0, 2, 0, 0, 0, 193, 0, 0, 0, 8, 0, 0, 0, 182, 0, 0, 0, 138, 0, 0, 0, 205, 0, 0, 0, 80, 0, 0, 0, 133, 0, 0, 0, 90, 0, 0, 0, 55, 0, 0, 0, 33, 0, 0, 0, 217, 0, 0, 0, 22, 0, 0, 0, 160, 0, 0, 0, 77, 0, 0, 0, 95, 0, 0, 0, 229, 0, 0, 0, 121, 0, 0, 0, 46, 0, 0, 0, 59, 0, 0, 0, 43, 0, 0, 0, 194, 0, 0, 0, 32, 0, 0, 0, 157, 0, 0, 0, 140, 0, 0, 0, 30, 0, 0, 0, 142, 0, 0, 0, 163, 0, 0, 0, 157, 0, 0, 0, 167, 0, 0, 0, 109, 0, 0, 0, 174, 0, 0, 0, 87, 0, 0, 0, 67, 0, 0, 0, 84, 0, 0, 0, 104, 0, 0, 0, 105, 0, 0, 0, 115, 0, 0, 0, 32, 0, 0, 0, 105, 0, 0, 0, 115, 0, 0, 0, 32, 0, 0, 0, 97, 0, 0, 0, 32, 0, 0, 0, 109, 0, 0, 0, 101, 0, 0, 0, 115, 0, 0, 0, 115, 0, 0, 0, 97, 0, 0, 0, 103, 0, 0, 0, 101, 0, 0, 0, 32, 0, 0, 0, 116, 0, 0, 0, 104, 0, 0, 0, 97, 0, 0, 0, 116, 0, 0, 0, 32, 0, 0, 0, 119, 0, 0, 0, 105, 0, 0, 0, 108, 0, 0, 0, 108, 0, 0, 0, 32, 0, 0, 0, 98, 0, 0, 0, 101, 0, 0, 0, 32, 0, 0, 0, 115, 0, 0, 0, 105, 0, 0, 0, 103, 0, 0, 0, 110, 0, 0, 0, 101, 0, 0, 0, 100, 0, 0, 0, 44, 0, 0, 0, 32, 0, 0, 0, 97, 0, 0, 0, 110, 0, 0, 0, 100, 0, 0, 0, 32, 0, 0, 0, 118, 0, 0, 0, 101, 0, 0, 0, 114, 0, 0, 0, 105, 0, 0, 0, 102, 0, 0, 0, 105, 0, 0, 0, 101, 0, 0, 0, 100, 0, 0, 0, 32, 0, 0, 0, 119, 0, 0, 0, 105, 0, 0, 0, 116, 0, 0, 0, 104, 0, 0, 0, 105, 0, 0, 0, 110, 0, 0, 0, 32, 0, 0, 0, 116, 0, 0, 0, 104, 0, 0, 0, 101, 0, 0, 0, 32, 0, 0, 0, 122, 0, 0, 0, 107, 0, 0, 0, 86, 0, 0, 0, 77, 0, 0, 0, 90, 0, 0, 0, 82, 0, 0, 0, 115, 0, 0, 0, 129, 0, 0, 0, 167, 0, 0, 0, 101, 0, 0, 0, 18, 0, 0, 0, 87, 0, 0, 0, 91, 0, 0, 0, 83, 0, 0, 0, 98, 0, 0, 0, 111, 0, 0, 0, 74, 0, 0, 0, 65, 0, 0, 0, 151, 0, 0, 0, 141, 0, 0, 0, 101, 0, 0, 0, 20, 0, 0, 0, 220, 0, 0, 0, 16, 0, 0, 0, 184, 0, 0, 0, 172, 0, 0, 0, 230, 0, 0, 0, 167, 0, 0, 0, 248, 0, 0, 0, 219, 0, 0, 0, 253, 0, 0, 0, 19, 0, 0, 0, 48, 0, 0, 0, 121, 0, 0, 0, 128, 0, 0, 0, 78, 0, 0, 0, 36, 0, 0, 0, 110, 0, 0, 0, 166, 0, 0, 0, 254, 0, 0, 0, 143, 0, 0, 0, 239, 0, 0, 0, 29, 0, 0, 0, 183, 0, 0, 0, 17, 0, 0, 0, 31, 0, 0, 0, 243, 0, 0, 0, 193, 0, 0, 0, 183, 0, 0, 0, 235, 0, 0, 0, 139, 0, 0, 0, 85, 0, 0, 0, 203, 0, 0, 0, 182, 0, 0, 0, 252, 0, 0, 0, 248, 0, 0, 0, 239, 0, 0, 0, 9, 0, 0, 0, 175, 0, 0, 0, 243, 0, 0, 0, 126, 0, 0, 0, 65, 0, 0, 0, 102, 0, 0, 0, 9, 0, 0, 0, 209, 0, 0, 0, 162, 0, 0, 0, 86, 0, 0, 0, 52, 0, 0, 0])

print("running the VM...")
tic = time.perf_counter()
segments, info = l2_r0prover.execute_with_input(image, input)
toc = time.perf_counter()
print(f"VM execution takes {toc - tic: 0.4f} seconds")
print(info.get_journal())

print("generate the receipt for the 1st segment...")
tic = time.perf_counter()
receipt_1 = l2_r0prover.prove_segment(segments[0])
toc = time.perf_counter()
print(f"Receipt for the 1st segment takes {toc - tic: 0.4f} seconds")

print("generate the receipt for the 2nd segment...")
tic = time.perf_counter()
receipt_2 = l2_r0prover.prove_segment(segments[1])
toc = time.perf_counter()
print(f"Receipt for the 2nd segment takes {toc - tic: 0.4f} seconds")

0 comments on commit cfbd4ce

Please sign in to comment.