Skip to content

Commit

Permalink
runtime: import code from zeam-runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
gballet committed Feb 27, 2025
1 parent fa09558 commit 556504e
Show file tree
Hide file tree
Showing 17 changed files with 566 additions and 7 deletions.
38 changes: 36 additions & 2 deletions pkgs/state-transition-runtime/build.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
const Builder = @import("std").Build;
const std = @import("std");

const zkvm_types = enum {
ceno,
powdr,
sp1,
};

pub fn build(b: *Builder) void {
const target_query = .{ .cpu_arch = .riscv32, .os_tag = .freestanding, .abi = .none, .cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 } };

const target = b.resolveTargetQuery(target_query);
const optimize = b.standardOptimizeOption(.{});

// Declare the -Dzkvm option, which is a choice between all supported zkvms
const zkvm = b.option(zkvm_types, "zkvm", "zkvm target") orelse .powdr;
const zkvm_module = b.addModule("zkvm", .{
.optimize = optimize,
.target = target,
.root_source_file = b.path(switch (zkvm) {
.ceno => "src/ceno/lib.zig",
.powdr => "src/powdr/lib.zig",
.sp1 => "src/sp1/lib.zig",
}),
});

// add ssz
const ssz = b.dependency("ssz.zig", .{
.target = target,
Expand Down Expand Up @@ -39,18 +57,34 @@ pub fn build(b: *Builder) void {
// target has to be riscv5 runtime provable/verifiable on zkVMs
const exe = b.addExecutable(.{
.name = "zeam-state-transition-runtime",
.root_source_file = .{ .cwd_relative = "src/main.zig" },
.root_source_file = b.path("src/main.zig"),
.optimize = optimize,
.target = target,
});
// addimport to root module is even required afer declaring it in mod
exe.root_module.addImport("ssz", ssz);
exe.root_module.addImport("zeam-types", zeam_types);
exe.root_module.addImport("zeam-state-transition", zeam_state_transition);
exe.root_module.addImport("zkvm", zkvm_module);
switch (zkvm) {
.ceno => {
exe.addAssemblyFile(b.path("src/ceno/start.s"));
exe.setLinkerScript(b.path("src/ceno/ceno.ld"));
},
.powdr => {
exe.addAssemblyFile(b.path("src/powdr/start.s"));
exe.setLinkerScript(b.path("src/powdr/powdr.x"));
exe.pie = true;
},
.sp1 => {
exe.addAssemblyFile(b.path("src/sp1/start.s"));
exe.setLinkerScript(b.path("src/sp1/sp1.ld"));
},
}
b.installArtifact(exe);

const tests = b.addTest(.{
.root_source_file = .{ .cwd_relative = "src/main.zig" },
.root_source_file = .{ .cwd_relative = "src/transition.zig" },
.optimize = optimize,
.target = target,
});
Expand Down
61 changes: 61 additions & 0 deletions pkgs/state-transition-runtime/src/ceno/ceno.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
MEMORY
{
RAM : ORIGIN = 0x80000000, LENGTH = 1024M
ROM : ORIGIN = 0x20000000, LENGTH = 16M
HINTS: ORIGIN = 0x40000000, LENGTH = 1024M
}

REGION_ALIAS("REGION_TEXT", ROM);
REGION_ALIAS("REGION_RODATA", ROM);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);
REGION_ALIAS("REGION_STACK", RAM);

REGION_ALIAS("REGION_HINTS", HINTS);

_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK);
_hints_start = ORIGIN(REGION_HINTS);
_hints_length = LENGTH(REGION_HINTS);
_lengths_of_hints_start = ORIGIN(REGION_HINTS);

SECTIONS
{
.text :
{
KEEP(*(.init));
. = ALIGN(4);
*(.text .text.*);
} > ROM

.rodata : ALIGN(4)
{
*(.srodata .srodata.*);
*(.rodata .rodata.*);
} > ROM

.data : ALIGN(4)
{
/* Must be called __global_pointer$ for linker relaxations to work. */
PROVIDE(__global_pointer$ = . + 0x800);

*(.sdata .sdata.*);
*(.sdata2 .sdata2.*);
*(.data .data.*);
} > RAM

.bss (NOLOAD) : ALIGN(4)
{
*(.sbss .sbss.*);
*(.bss .bss.*);

. = ALIGN(4);
_sheap = .;
} > RAM

/* Define a section for runtime-populated EEPROM-like HINTS data */
.hints (NOLOAD) : ALIGN(4)
{
*(.hints .hints.*);
} > HINTS
}
29 changes: 29 additions & 0 deletions pkgs/state-transition-runtime/src/ceno/io.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const std = @import("std");

var info_out: [*]volatile u32 = @ptrFromInt(0x8010_0000);

var cursor: usize = 0;

fn alloc(msg_len: usize) []volatile u32 {
// This isn't thread-safe, but it doesn't matter right now
// as there are no threads in this environment.
const old_cursor = cursor;
cursor += (msg_len + 3) & 0xFFFFFFFC; // word-align
return info_out[old_cursor..cursor];
}

pub fn print_str(str: []const u8) void {
var buf = alloc(str.len);
// @memcpy seems to greatly increase the heap size, which takes
// the prover down. Do it manyally for now.
// @memcpy(buf[0..], std.mem.bytesAsSlice(u32, buf));
const as_u32 = std.mem.bytesAsSlice(u32, buf);
for (as_u32, 0..) |word, i| {
if (i < str.len / 4) {
buf[i] = word;
} else {
const mask: u32 = @as(u32, 0xFFFFFFFF) >> @truncate(8 * (4 * i - str.len));
buf[i] = word & mask;
}
}
}
25 changes: 25 additions & 0 deletions pkgs/state-transition-runtime/src/ceno/lib.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const std = @import("std");
const syscalls = @import("./syscalls.zig");
pub const io = @import("./io.zig");

pub fn halt(exit_code: u32) noreturn {
asm volatile ("ecall"
:
: [exit_code] "{a0}" (exit_code),
[arg] "{t0}" (syscalls.halt_call),
);
unreachable;
}

pub fn keccack_permute(state: []u64) !void {
if (state.length != 25) {
return error.InvalidKeccakInputSize;
}

asm volatile ("ecall"
:
: [scallnum] "{t0}" (syscalls.keccack_permute),
[buf] "{a0}" (&state),
[arg1] "{a1}" (0),
);
}
12 changes: 12 additions & 0 deletions pkgs/state-transition-runtime/src/ceno/start.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.section .init
.global _start
_start:
.option push
.option norelax
la gp, __global_pointer$
.option pop

la sp, _stack_start
mv fp, sp

call main
2 changes: 2 additions & 0 deletions pkgs/state-transition-runtime/src/ceno/syscalls.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const halt_call = 0;
pub const keccack_permute = 0x10109;
26 changes: 21 additions & 5 deletions pkgs/state-transition-runtime/src/main.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const std = @import("std");
const Allocator = std.mem.Allocator;

const zkvm = @import("zkvm");
const ssz = @import("ssz");
const types = @import("zeam-types");
const state_transition = @import("zeam-state-transition");

var fixed_mem = [_]u8{0} ** (256 * 1024 * 1024);

const Inputs = struct {
state_root: []const u8,
block_root: []const u8,
Expand All @@ -17,7 +19,8 @@ const Witnesses = struct {

// implements risv5 runtime that runs in zkvm on provided inputs and witnesses to execute
// and prove the state transition as imported from `pkgs/state-transition`
pub fn main() noreturn {
export fn main() noreturn {
zkvm.io.print_str("running block transition function\n");
// access inputs and witnesses from zkvm
const inputs = Inputs{
.state_root = &[_]u8{},
Expand All @@ -30,18 +33,31 @@ pub fn main() noreturn {
};
_ = witnesses;

var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_mem[0..]);
const allocator = fixed_allocator.allocator();

// TODO: construct state and block from witnesses and validate stateroot and block root
// use the ssz deserialized state and block to apply state transition

const state = types.BeamState{};
const block = types.SignedBeamBlock{};
var state: types.BeamState = undefined;
const block: types.SignedBeamBlock = undefined;

// get some allocator
// apply the state transition to modify the state
state_transition.apply_transition(allocator, &state, block);
state_transition.apply_transition(allocator, &state, block) catch @panic("error running transition function");

// verify the block.state_root is ssz hash tree root of state
// this completes our zkvm proving

zkvm.halt(0);
}

pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
zkvm.io.print_str("PANIC: ");
zkvm.io.print_str(msg);
zkvm.io.print_str("\n");
zkvm.halt(1);
while (true) {}
}

test "ssz import" {
Expand Down
63 changes: 63 additions & 0 deletions pkgs/state-transition-runtime/src/powdr/io.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const syscalls = @import("./syscalls.zig").syscalls;

pub fn print_str(str: []const u8) void {
for (str) |c| {
asm volatile ("ecall"
:
: [scallnum] "{t0}" (@intFromEnum(syscalls.output)),
[subcommand] "{a0}" (0),
[arg1] "{a1}" (c),
: "memory"
);
}
}

pub fn read_u32(idx: u32) u32 {
return asm volatile ("ecall"
: [ret] "={a0}" (-> u32),
: [scallnum] "{t0}" (@intFromEnum(syscalls.input)),
[fd] "{a0}" (0), // fd = 0 == stdin
[idx] "{a1}" (idx + 1),
: "memory"
);
}

pub fn read_data_len(fd: u32) usize {
return asm volatile ("ecall"
: [ret] "={a0}" (-> usize),
: [scallnum] "{t0}" (@intFromEnum(syscalls.input)),
[subcommand] "{a0}" (fd),
[idx] "{a1}" (0),
: "memory"
);
}

pub fn read_slice(fd: u32, data: []u32) void {
for (data, 0..) |*d, idx| {
var item: u32 = undefined;
asm volatile ("ecall"
: [ret] "={a0}" (item),
: [scallnum] "{t0}" (@intFromEnum(syscalls.input)),
[subcommand] "{a0}" (fd),
[idx] "{a1}" (idx + 1),
: "memory"
);
d.* = item;
}
}

pub fn write_u8(fd: u32, byte: u8) void {
asm volatile ("ecall"
:
: [scallnum] "{t0}" (@intFromEnum(syscalls.output)),
[fd] "{a0}" (fd),
[arg1] "{a1}" (byte),
: "memory"
);
}

pub fn write_slice(fd: u32, data: []const u8) void {
for (data) |c| {
write_u8(fd, c);
}
}
Loading

0 comments on commit 556504e

Please sign in to comment.