mirror of
https://github.com/eth-act/ere.git
synced 2026-04-03 03:00:17 -04:00
nexus: implement execute and io serialization (#171)
Co-authored-by: han0110 <tinghan0110@gmail.com>
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -3720,7 +3720,10 @@ dependencies = [
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"nexus-core",
|
||||
"nexus-sdk",
|
||||
"nexus-vm",
|
||||
"postcard",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
|
||||
@@ -43,6 +43,7 @@ clap = "4.5.42"
|
||||
dashmap = "6.1.0"
|
||||
erased-serde = "0.4.6"
|
||||
indexmap = "2.10.0"
|
||||
postcard = "1.0.8"
|
||||
prost = "0.13"
|
||||
prost-build = "0.13"
|
||||
rand = "0.9.2"
|
||||
@@ -76,6 +77,8 @@ miden-verifier = { git = "https://github.com/0xPolygonMiden/miden-vm.git", tag =
|
||||
|
||||
# Nexus dependencies
|
||||
nexus-sdk = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
nexus-core = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
nexus-vm = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
|
||||
# OpenVM dependencies
|
||||
openvm-build = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.4.0" }
|
||||
|
||||
@@ -7,12 +7,15 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
postcard.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
# Nexus dependencies
|
||||
nexus-sdk.workspace = true
|
||||
nexus-core.workspace = true
|
||||
nexus-vm.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
ere-compile-utils.workspace = true
|
||||
|
||||
@@ -2,10 +2,19 @@ use crate::{
|
||||
compiler::NexusProgram,
|
||||
error::{CompileError, NexusError},
|
||||
};
|
||||
use ere_compile_utils::cargo_metadata;
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use nexus_sdk::compile::{Compile, Compiler as NexusCompiler, cargo::CargoPackager};
|
||||
use std::{fs, path::Path};
|
||||
use std::path::Path;
|
||||
|
||||
const TARGET_TRIPLE: &str = "riscv32i-unknown-none-elf";
|
||||
// Linker script from nexus-sdk
|
||||
// https://github.com/nexus-xyz/nexus-zkvm/blob/v0.3.4/sdk/src/compile/linker-scripts/default.x
|
||||
const LINKER_SCRIPT: &str = include_str!("rust_rv32i/linker.x");
|
||||
const RUSTFLAGS: &[&str] = &["-C", "relocation-model=pic", "-C", "panic=abort"];
|
||||
const CARGO_BUILD_OPTIONS: &[&str] = &[
|
||||
// For bare metal we have to build core and alloc
|
||||
"-Zbuild-std=core,alloc",
|
||||
];
|
||||
|
||||
/// Compiler for Rust guest program to RV32I architecture.
|
||||
pub struct RustRv32i;
|
||||
@@ -15,23 +24,16 @@ impl Compiler for RustRv32i {
|
||||
|
||||
type Program = NexusProgram;
|
||||
|
||||
fn compile(&self, guest_path: &Path) -> Result<Self::Program, Self::Error> {
|
||||
// 1. Check guest path
|
||||
if !guest_path.exists() {
|
||||
return Err(CompileError::PathNotFound(guest_path.to_path_buf()))?;
|
||||
}
|
||||
std::env::set_current_dir(guest_path).map_err(|e| CompileError::Client(e.into()))?;
|
||||
|
||||
let metadata = cargo_metadata(guest_path).map_err(CompileError::CompileUtilError)?;
|
||||
let package_name = &metadata.root_package().unwrap().name;
|
||||
|
||||
let mut prover_compiler = NexusCompiler::<CargoPackager>::new(package_name);
|
||||
let elf_path = prover_compiler
|
||||
.build()
|
||||
.map_err(|e| CompileError::Client(e.into()))?;
|
||||
|
||||
let elf = fs::read(&elf_path).map_err(|_| CompileError::ElfNotFound(elf_path))?;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
let elf = CargoBuildCmd::new()
|
||||
.linker_script(Some(LINKER_SCRIPT))
|
||||
// The compiled ELF will be incompatible with Nexus VM if we don't pin this version
|
||||
// https://github.com/nexus-xyz/nexus-zkvm/blob/main/rust-toolchain.toml
|
||||
.toolchain("nightly-2025-04-06")
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)
|
||||
.map_err(CompileError::CompileUtilError)?;
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
|
||||
55
crates/zkvm/nexus/src/compiler/rust_rv32i/linker.x
Normal file
55
crates/zkvm/nexus/src/compiler/rust_rv32i/linker.x
Normal file
@@ -0,0 +1,55 @@
|
||||
ENTRY(_start);
|
||||
|
||||
/* nb: when proving we will rebuild the memory model based on the first
|
||||
pass' usages, so there is no cost for a "suboptimal" layout here */
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
__memory_top = 0x80400000;
|
||||
. = 0x88;
|
||||
|
||||
.text : ALIGN(4)
|
||||
{
|
||||
KEEP(*(.init));
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.init.rust));
|
||||
*(.text .text.*);
|
||||
}
|
||||
|
||||
. = ALIGN(8);
|
||||
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
/* Must be called __global_pointer$ for linker relaxations to work. */
|
||||
__global_pointer$ = . + 0x800;
|
||||
*(.srodata .srodata.*);
|
||||
*(.rodata .rodata.*);
|
||||
*(.sdata .sdata.* .sdata2 .sdata2.*);
|
||||
*(.data .data.*);
|
||||
|
||||
/* this is used by the global allocator (see:src/lib.rs) */
|
||||
. = ALIGN(4);
|
||||
_heap = .;
|
||||
LONG(_ebss);
|
||||
}
|
||||
|
||||
.bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
*(.sbss .sbss.* .bss .bss.*);
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
_end = .;
|
||||
}
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.comment*)
|
||||
*(.debug*)
|
||||
}
|
||||
|
||||
/* Stack unwinding is not supported, but we will keep these for now */
|
||||
.eh_frame (INFO) : { KEEP(*(.eh_frame)) }
|
||||
.eh_frame_hdr (INFO) : { *(.eh_frame_hdr) }
|
||||
}
|
||||
|
||||
ASSERT(. < __memory_top, "Program is too large for the VM memory.");
|
||||
@@ -40,6 +40,8 @@ pub enum ProveError {
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
#[error("Serialising input with `postcard` failed: {0}")]
|
||||
Postcard(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
||||
@@ -5,11 +5,16 @@ use crate::{
|
||||
error::{NexusError, ProveError, VerifyError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use nexus_sdk::{Local, Prover, Verifiable, stwo::seq::Stwo};
|
||||
use serde::de::DeserializeOwned;
|
||||
use nexus_core::nvm::{self, ElfFile};
|
||||
use nexus_sdk::{
|
||||
KnownExitCodes, Prover, Verifiable, Viewable,
|
||||
stwo::seq::{Proof as NexusProof, Stwo},
|
||||
};
|
||||
use nexus_vm::trace::Trace;
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use std::{io::Read, time::Instant};
|
||||
use tracing::info;
|
||||
|
||||
@@ -18,65 +23,100 @@ include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
pub mod compiler;
|
||||
pub mod error;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NexusProofBundle {
|
||||
proof: NexusProof,
|
||||
public_values: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct EreNexus {
|
||||
program: NexusProgram,
|
||||
elf: NexusProgram,
|
||||
}
|
||||
|
||||
impl EreNexus {
|
||||
pub fn new(program: NexusProgram, _resource_type: ProverResourceType) -> Self {
|
||||
Self { program }
|
||||
pub fn new(elf: NexusProgram, _resource_type: ProverResourceType) -> Self {
|
||||
Self { elf }
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreNexus {
|
||||
fn execute(
|
||||
&self,
|
||||
_inputs: &Input,
|
||||
) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
// TODO: Serialize inputs by `postcard` and make sure there is no double serailization.
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/63.
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let elf = ElfFile::from_bytes(&self.elf)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
// TODO: Execute and get cycle count
|
||||
let input_bytes = serialize_inputs(inputs)?;
|
||||
|
||||
// TODO: Public values
|
||||
let public_values = Vec::new();
|
||||
// Nexus sdk does not provide a trace, so we need to use core `nvm`
|
||||
// Encoding is copied directly from `prove_with_input`
|
||||
let mut private_encoded = if input_bytes.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
postcard::to_stdvec_cobs(&input_bytes)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?
|
||||
};
|
||||
|
||||
Ok((public_values, ProgramExecutionReport::default()))
|
||||
if !private_encoded.is_empty() {
|
||||
let private_padded_len = (private_encoded.len() + 3) & !3;
|
||||
assert!(private_padded_len >= private_encoded.len());
|
||||
private_encoded.resize(private_padded_len, 0x00);
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let (view, trace) = nvm::k_trace(elf, &[], &[], private_encoded.as_slice(), 1)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let public_values = view
|
||||
.public_output::<Vec<u8>>()
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
ProgramExecutionReport {
|
||||
total_num_cycles: trace.get_num_steps() as u64,
|
||||
region_cycles: Default::default(), // not available
|
||||
execution_duration: start.elapsed(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
_inputs: &Input,
|
||||
inputs: &Input,
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
}
|
||||
|
||||
let prover: Stwo<Local> = Stwo::new_from_bytes(&self.program)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))
|
||||
.map_err(zkVMError::from)?;
|
||||
let elf = ElfFile::from_bytes(&self.elf)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
// TODO: Serialize inputs by `postcard` and make sure there is no double serailization.
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/63.
|
||||
let prover =
|
||||
Stwo::new(&elf).map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let now = Instant::now();
|
||||
let (_view, proof) = prover
|
||||
.prove_with_input(&(), &())
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))
|
||||
.map_err(zkVMError::from)?;
|
||||
let elapsed = now.elapsed();
|
||||
let input_bytes = serialize_inputs(inputs)?;
|
||||
|
||||
let bytes = bincode::serialize(&proof)
|
||||
let start = Instant::now();
|
||||
let (view, proof) = prover
|
||||
.prove_with_input::<Vec<u8>, ()>(&input_bytes, &())
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let public_values = view
|
||||
.public_output::<Vec<u8>>()
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let proof_bundle = NexusProofBundle {
|
||||
proof,
|
||||
public_values: public_values.clone(),
|
||||
};
|
||||
|
||||
let proof_bytes = bincode::serialize(&proof_bundle)
|
||||
.map_err(|err| NexusError::Prove(ProveError::Bincode(err)))?;
|
||||
|
||||
// TODO: Public values
|
||||
let public_values = Vec::new();
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
Proof::Compressed(bytes),
|
||||
ProgramProvingReport::new(elapsed),
|
||||
Proof::Compressed(proof_bytes),
|
||||
ProgramProvingReport::new(start.elapsed()),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -87,30 +127,23 @@ impl zkVM for EreNexus {
|
||||
|
||||
info!("Verifying proof...");
|
||||
|
||||
let proof: nexus_sdk::stwo::seq::Proof = bincode::deserialize(proof)
|
||||
let proof_bundle = bincode::deserialize::<NexusProofBundle>(proof)
|
||||
.map_err(|err| NexusError::Verify(VerifyError::Bincode(err)))?;
|
||||
|
||||
let prover: Stwo<Local> = Stwo::new_from_bytes(&self.program)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))
|
||||
.map_err(zkVMError::from)?;
|
||||
let elf = prover.elf.clone(); // save elf for use with verification
|
||||
proof
|
||||
.verify_expected::<(), ()>(
|
||||
&(), // no public input
|
||||
nexus_sdk::KnownExitCodes::ExitSuccess as u32,
|
||||
&(), // no public output
|
||||
&elf, // expected elf (program binary)
|
||||
&[], // no associated data,
|
||||
proof_bundle
|
||||
.proof
|
||||
.verify_expected_from_program_bytes::<(), Vec<u8>>(
|
||||
&(),
|
||||
KnownExitCodes::ExitSuccess as u32,
|
||||
&proof_bundle.public_values,
|
||||
&self.elf,
|
||||
&[],
|
||||
)
|
||||
.map_err(|e| NexusError::Verify(VerifyError::Client(e.into())))
|
||||
.map_err(zkVMError::from)?;
|
||||
.map_err(|e| NexusError::Verify(VerifyError::Client(e.into())))?;
|
||||
|
||||
info!("Verify Succeeded!");
|
||||
|
||||
// TODO: Public values
|
||||
let public_values = Vec::new();
|
||||
|
||||
Ok(public_values)
|
||||
Ok(proof_bundle.public_values)
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
@@ -121,8 +154,154 @@ impl zkVM for EreNexus {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, _reader: R) -> Result<T, zkVMError> {
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/63.
|
||||
todo!()
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
let mut buf = vec![0; 1 << 20]; // allocate 1MiB as buffer.
|
||||
let (value, _) = postcard::from_io((reader, &mut buf)).map_err(zkVMError::other)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes nexus program inputs
|
||||
pub fn serialize_inputs(inputs: &Input) -> Result<Vec<u8>, NexusError> {
|
||||
inputs
|
||||
.iter()
|
||||
.try_fold(Vec::new(), |mut acc, item| -> Result<Vec<u8>, NexusError> {
|
||||
match item {
|
||||
InputItem::Object(obj) => {
|
||||
let buffer = postcard::to_allocvec(obj.as_ref())
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?;
|
||||
acc.extend_from_slice(&buffer);
|
||||
Ok(acc)
|
||||
}
|
||||
InputItem::SerializedObject(bytes) => {
|
||||
acc.extend_from_slice(bytes);
|
||||
Ok(acc)
|
||||
}
|
||||
InputItem::Bytes(bytes) => {
|
||||
let buffer = postcard::to_allocvec(bytes)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?;
|
||||
acc.extend_from_slice(&buffer);
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EreNexus, compiler::RustRv32i};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
|
||||
static FIB_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
|
||||
|
||||
fn basic_program() -> Vec<u8> {
|
||||
BASIC_PROGRAM
|
||||
.get_or_init(|| {
|
||||
RustRv32i
|
||||
.compile(&testing_guest_directory("nexus", "basic"))
|
||||
.unwrap()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn fib_program() -> Vec<u8> {
|
||||
FIB_PROGRAM
|
||||
.get_or_init(|| {
|
||||
RustRv32i
|
||||
.compile(&testing_guest_directory("nexus", "fib"))
|
||||
.unwrap()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.execute(&inputs).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fibonacci() {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct FibInput {
|
||||
n: u32,
|
||||
}
|
||||
|
||||
let program = fib_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(FibInput { n: 10 });
|
||||
|
||||
let (public_values, _report) = zkvm.execute(&input).expect("Execution failed");
|
||||
|
||||
let result: u32 = zkvm
|
||||
.deserialize_from(&public_values[..])
|
||||
.expect("Failed to deserialize output");
|
||||
assert_eq!(result, 55, "fib(10) should be 55");
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(FibInput { n: 0 });
|
||||
|
||||
let (public_values, _report) = zkvm.execute(&input).expect("Execution failed");
|
||||
let result: u32 = zkvm
|
||||
.deserialize_from(&public_values[..])
|
||||
.expect("Failed to deserialize output");
|
||||
assert_eq!(result, 0, "fib(0) should be 0");
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(FibInput { n: 1 });
|
||||
|
||||
let (public_values, _report) = zkvm.execute(&input).expect("Execution failed");
|
||||
let result: u32 = zkvm
|
||||
.deserialize_from(&public_values[..])
|
||||
.expect("Failed to deserialize output");
|
||||
assert_eq!(result, 1, "fib(1) should be 1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,7 @@ RUN /tmp/install_nexus_sdk.sh && rm /tmp/install_nexus_sdk.sh # Clean up the scr
|
||||
# Verify Nexus installation
|
||||
RUN echo "Verifying Nexus installation in Dockerfile (post-script)..." && cargo-nexus --version
|
||||
|
||||
# Add `rust-src` component to enable std build for nightly rust.
|
||||
RUN rustup component add rust-src --toolchain "$NEXUS_TOOLCHAIN_VERSION"
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
[package]
|
||||
name = "ere-nexus-guest"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nexus-rt = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
postcard = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
|
||||
|
||||
# Generated by cargo-nexus, do not remove!
|
||||
#
|
||||
|
||||
@@ -1,18 +1,60 @@
|
||||
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]
|
||||
|
||||
use nexus_rt::println;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use nexus_rt::{read_private_input, write_public_output};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[nexus_rt::main]
|
||||
#[nexus_rt::private_input(x)]
|
||||
fn main(x: u32) {
|
||||
println!("Read public input: {}", x);
|
||||
let res = fibonacci(x);
|
||||
println!("fib result: {}", res);
|
||||
fn main() {
|
||||
let input_bytes: Vec<u8> = read_private_input().expect("failed to read input");
|
||||
|
||||
// Deserialize the first input (Vec<u8>)
|
||||
let (bytes, remaining): (Vec<u8>, &[u8]) =
|
||||
postcard::take_from_bytes(&input_bytes).expect("failed to deserialize bytes");
|
||||
|
||||
// Deserialize the second input (BasicStruct)
|
||||
let basic_struct: BasicStruct =
|
||||
postcard::from_bytes(remaining).expect("failed to deserialize struct");
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let rev_bytes: Vec<u8> = bytes.iter().rev().copied().collect();
|
||||
let basic_struct_output = basic_struct.output();
|
||||
|
||||
// Write `rev_bytes` and `basic_struct_output`
|
||||
let mut output_bytes = Vec::new();
|
||||
output_bytes.extend_from_slice(&rev_bytes);
|
||||
output_bytes.extend_from_slice(&postcard::to_allocvec(&basic_struct_output).unwrap());
|
||||
|
||||
write_public_output(&output_bytes).expect("failed to write output");
|
||||
}
|
||||
pub fn fibonacci(n: u32) -> u32 {
|
||||
match n {
|
||||
0 => 1,
|
||||
1 => 1,
|
||||
_ => fibonacci(n - 1) + fibonacci(n - 2),
|
||||
|
||||
// Copied from test_utils
|
||||
// test_utils is not used due to no_std conflicts with sha2 dependency.
|
||||
const BYTES_LENGTH: usize = 32;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BasicStruct {
|
||||
pub a: u8,
|
||||
pub b: u16,
|
||||
pub c: u32,
|
||||
pub d: u64,
|
||||
pub e: Vec<u8>,
|
||||
}
|
||||
|
||||
impl BasicStruct {
|
||||
/// Performs some computation (Wrapping add all fields by 1).
|
||||
pub fn output(&self) -> Self {
|
||||
Self {
|
||||
a: self.a.wrapping_add(1),
|
||||
b: self.b.wrapping_add(1),
|
||||
c: self.c.wrapping_add(1),
|
||||
d: self.d.wrapping_add(1),
|
||||
e: self.e.iter().map(|byte| byte.wrapping_add(1)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
tests/nexus/fib/Cargo.toml
Normal file
15
tests/nexus/fib/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "ere-nexus-guest-fib"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nexus-rt = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
|
||||
postcard = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
|
||||
# Generated by cargo-nexus, do not remove!
|
||||
#
|
||||
[features]
|
||||
cycles = [] # Enable cycle counting for run command
|
||||
[workspace]
|
||||
50
tests/nexus/fib/src/main.rs
Normal file
50
tests/nexus/fib/src/main.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use nexus_rt::{read_private_input, write_public_output};
|
||||
use postcard;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct FibInput {
|
||||
n: u32,
|
||||
}
|
||||
|
||||
#[nexus_rt::main]
|
||||
fn main() {
|
||||
let input_bytes: Vec<u8> = read_private_input().expect("failed to read input");
|
||||
|
||||
// Deserialize FibInput from the postcard bytes
|
||||
let fib_input: FibInput =
|
||||
postcard::from_bytes(&input_bytes).expect("failed to deserialize input");
|
||||
|
||||
let n = fib_input.n;
|
||||
let result = fibonacci(n);
|
||||
|
||||
// Serialize result to bytes before writing
|
||||
let output_bytes = postcard::to_allocvec(&result).expect("failed to serialize output");
|
||||
|
||||
write_public_output(&output_bytes).expect("failed to write output");
|
||||
}
|
||||
|
||||
fn fibonacci(n: u32) -> u32 {
|
||||
if n == 0 {
|
||||
return 0;
|
||||
}
|
||||
if n == 1 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut a = 0u32;
|
||||
let mut b = 1u32;
|
||||
|
||||
for _ in 2..=n {
|
||||
let temp = a.wrapping_add(b);
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
b
|
||||
}
|
||||
Reference in New Issue
Block a user