Skip to content

Commit

Permalink
initial merkle tree implementation for VM memory
Browse files Browse the repository at this point in the history
  • Loading branch information
govereau committed Jan 9, 2024
1 parent 49d7068 commit 3a71910
Show file tree
Hide file tree
Showing 20 changed files with 1,154 additions and 228 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ serde_json = "1.0"
supernova = { git = "ssh://git@github.com:22/nexus-xyz/supernova.git", rev = "92e884c" }
#supernova = { path = "../supernova" }

ark-crypto-primitives = { version = "0.4.0", features = ["r1cs", "sponge"] }
ark-crypto-primitives = { version = "0.4.0", features = ["r1cs", "sponge", "crh", "merkle_tree"] }
ark-std = "0.4.0"

ark-relations = { version = "0.4.0" }
Expand Down
2 changes: 1 addition & 1 deletion network/src/bin/pcdnode/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn api(mut state: WorkerState, msg: NexusAPI) -> Result<NexusAPI> {
match msg {
Program { elf, .. } => {
println!("GOT a program");
let vm = parse_elf(&elf)?;
let vm = parse_elf(&elf, true)?;
let hash = hex::encode(Sha256::digest(&elf));
manage_proof(state, hash.clone(), vm)?;
Ok(Proof(Proof { hash, ..Proof::default() }))
Expand Down
8 changes: 8 additions & 0 deletions riscv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ clap.workspace = true
serde.workspace = true
elf = { version = "0.7", default-features = false, features = ["std"] }

ark-ff.workspace = true
ark-relations.workspace = true
ark-r1cs-std.workspace = true
ark-serialize.workspace = true
ark-bn254.workspace = true
ark-crypto-primitives.workspace = true

[dev-dependencies]
ark-std.workspace = true
57 changes: 57 additions & 0 deletions riscv/src/ark_serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::fmt;
use serde::{Serializer, Deserializer, de::Visitor};
use ark_serialize::{CanonicalSerialize, CanonicalDeserialize};

pub fn serialize<T, S>(t: &T, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: CanonicalSerialize,
{
let mut v = Vec::new();
t.serialize_uncompressed(&mut v)
.map_err(|_| serde::ser::Error::custom("ark error"))?;
s.serialize_bytes(&v)
}

pub fn deserialize<'a, D, T>(d: D) -> Result<T, D::Error>
where
D: Deserializer<'a>,
T: CanonicalDeserialize,
{
let v = d.deserialize_bytes(BV)?;
let t = T::deserialize_uncompressed(v.as_slice())
.map_err(|_| serde::de::Error::custom("ark Error"))?;
Ok(t)
}

struct BV;

impl<'a> Visitor<'a> for BV {
type Value = Vec<u8>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a byte sequence")
}

fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
Ok(v.to_vec())
}

fn visit_byte_buf<E: serde::de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
Ok(v)
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'a>,
{
let mut v = Vec::new();
loop {
match seq.next_element() {
Ok(Some(x)) => v.push(x),
Ok(None) => return Ok(v),
Err(e) => return Err(e),
}
}
}
}
20 changes: 16 additions & 4 deletions riscv/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ pub enum VMError {
/// Unknown ECALL number
UnknownECall(u32, u32),

/// Invalid memory address
SegFault(u32),

/// Invalid memory alignment
Misaligned(u32),

/// An error occured while hashing
HashError(String),

/// An error occurred reading file system
IOError(std::io::Error),

Expand Down Expand Up @@ -52,10 +61,13 @@ impl Error for VMError {
impl Display for VMError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
PartialInstruction(pc) => write!(f, "partial instruction at pc:{pc}"),
InvalidSize(pc, sz) => write!(f, "invalid instruction size, {sz}, at pc:{pc}"),
InvalidInstruction(pc, i) => write!(f, "invalid instruction {i:x} at pc:{pc}"),
UnknownECall(pc, n) => write!(f, "unknown ecall {n} at pc:{pc}"),
PartialInstruction(pc) => write!(f, "partial instruction at pc:{pc:x}"),
InvalidSize(pc, sz) => write!(f, "invalid instruction size, {sz}, at pc:{pc:x}"),
InvalidInstruction(pc, i) => write!(f, "invalid instruction {i:x} at pc:{pc:x}"),
UnknownECall(pc, n) => write!(f, "unknown ecall {n} at pc:{pc:x}"),
SegFault(addr) => write!(f, "invalid memory address {addr:x}"),
Misaligned(addr) => write!(f, "mis-alligned memory address {addr:x}"),
HashError(s) => write!(f, "hash error {s}"),
IOError(e) => write!(f, "{e}"),
ELFError(e) => write!(f, "{e}"),
}
Expand Down
43 changes: 25 additions & 18 deletions riscv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod error;
pub mod rv32;
pub mod vm;
mod ark_serde;

#[cfg(test)]
mod tests;
Expand All @@ -24,42 +25,42 @@ use std::path::PathBuf;
/// Create a VM with k no-op instructions
pub fn nop_vm(k: usize) -> VM {
let mut pc = 0x1000;
let mut vm = VM::new(pc);
let mut vm = VM::new(pc, true);

// TODO: we can do better for large k
for _ in 0..k {
vm.mem.sw(pc, 0x00000013); // nop
vm.mem.store(SW, pc, 0x00000013).unwrap(); // nop
pc += 4;
}
vm.mem.sw(pc, 0xc0001073); // unimp
vm.mem.store(SW, pc, 0xc0001073).unwrap(); // unimp
vm
}

/// Create a VM which loops k times
pub fn loop_vm(k: usize) -> VM {
assert!(k < (1 << 31));

let mut vm = VM::new(0x1000);
let mut vm = VM::new(0x1000, true);

let hi = (k as u32) & 0xfffff000;
let lo = ((k & 0xfff) << 20) as u32;
vm.mem.sw(0x1000, hi | 0x137); // lui x2, hi
vm.mem.sw(0x1004, lo | 0x10113); // addi x2, x2, lo
vm.mem.sw(0x1008, 0x00000093); // li x1, 0
vm.mem.sw(0x100c, 0x00108093); // addi x1, x1, 1
vm.mem.sw(0x1010, 0xfe209ee3); // bne x1, x2, 0x100c
vm.mem.sw(0x1014, 0xc0001073); // unimp
vm.mem.store(SW, 0x1000, hi | 0x137).unwrap(); // lui x2, hi
vm.mem.store(SW, 0x1004, lo | 0x10113).unwrap(); // addi x2, x2, lo
vm.mem.store(SW, 0x1008, 0x00000093).unwrap(); // li x1, 0
vm.mem.store(SW, 0x100c, 0x00108093).unwrap(); // addi x1, x1, 1
vm.mem.store(SW, 0x1010, 0xfe209ee3).unwrap(); // bne x1, x2, 0x100c
vm.mem.store(SW, 0x1014, 0xc0001073).unwrap(); // unimp
vm
}

/// Load a VM state from an ELF file
pub fn load_elf(path: &PathBuf) -> Result<VM> {
pub fn load_elf(path: &PathBuf, merkle: bool) -> Result<VM> {
let file_data = read(path)?;
let slice = file_data.as_slice();
parse_elf(slice)
parse_elf(slice, merkle)
}

pub fn parse_elf(bytes: &[u8]) -> Result<VM> {
pub fn parse_elf(bytes: &[u8], merkle: bool) -> Result<VM> {
let file = ElfBytes::<LittleEndian>::minimal_parse(bytes)?;

let load_phdrs: Vec<ProgramHeader> = file
Expand All @@ -70,13 +71,13 @@ pub fn parse_elf(bytes: &[u8]) -> Result<VM> {
.collect();

// TODO: read PC from elf file (and related changes)
let mut vm = VM::new(0x1000);
let mut vm = VM::new(0x1000, merkle);

for p in &load_phdrs {
let s = p.p_offset as usize;
let e = (p.p_offset + p.p_filesz) as usize;
let bytes = &bytes[s..e];
vm.init_memory(p.p_vaddr as u32, bytes);
vm.init_memory(p.p_vaddr as u32, bytes)?;
}

Ok(vm)
Expand All @@ -90,6 +91,10 @@ pub struct VMOpts {
#[arg(short, name = "k", default_value = "1")]
pub k: usize,

/// Use merkle-tree memory
#[arg(short, default_value = "false")]
pub merkle: bool,

/// Use a no-op machine of size n
#[arg(group = "vm", short, name = "n")]
pub nop: Option<usize>,
Expand All @@ -110,7 +115,7 @@ pub fn load_vm(opts: &VMOpts) -> Result<VM> {
} else if let Some(k) = opts.loopk {
Ok(loop_vm(k))
} else {
load_elf(opts.file.as_ref().unwrap())
load_elf(opts.file.as_ref().unwrap(), opts.merkle)
}
}

Expand All @@ -125,14 +130,16 @@ pub fn eval(vm: &mut VM, show: bool) -> Result<()> {
}

loop {
if show {
print!("{:50} ", vm.inst);
}
eval_inst(vm)?;
if show {
println!("{:50} {:8x} {:8x}", vm.inst, vm.Z, vm.PC);
println!("{:8x} {:8x}", vm.Z, vm.regs.pc);
}
if vm.inst.inst == RV32::UNIMP {
break;
}
eval_writeback(vm);
}

fn table(name: &str, mem: &[u32]) {
Expand Down
2 changes: 1 addition & 1 deletion riscv/src/vm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! A Virtual Machine for RISC-V
pub mod mem;
pub mod memory;
pub mod eval;
pub mod trace;
Loading

0 comments on commit 3a71910

Please sign in to comment.