Skip to content

Commit

Permalink
feat: add read/commit API for go runtime (#158)
Browse files Browse the repository at this point in the history
* feat: add read/commit API for go runtime

* update README

* fix clippy

* fix bugs in zkmips

* update zkmips based on comments

* update comment in add.go

* simplify zkmips commands

* fix fmt and clippy

* fix seg number

* remove blink line
  • Loading branch information
weilzkm authored Aug 17, 2024
1 parent 3eae409 commit 2faba08
Show file tree
Hide file tree
Showing 13 changed files with 532 additions and 131 deletions.
16 changes: 8 additions & 8 deletions emulator/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,14 @@ impl State {
.expect("set memory range failed");
}
_ => {
// if name.contains("sys_common") && name.contains("thread_info") {
// log::debug!("patch {}", name);
// let r: Vec<u8> = vec![0x03, 0xe0, 0x00, 0x08, 0, 0, 0, 0];
// let r = Box::new(r.as_slice());
// self.memory
// .set_memory_range(symbol.st_value as u32, r)
// .expect("set memory range failed");
//}
if name.contains("sys_common") && name.contains("thread_info") {
log::debug!("patch {}", name);
let r: Vec<u8> = vec![0x03, 0xe0, 0x00, 0x08, 0, 0, 0, 0];
let r = Box::new(r.as_slice());
self.memory
.set_memory_range(symbol.st_value as u32, r)
.expect("set memory range failed");
}
}
},
Err(e) => {
Expand Down
130 changes: 130 additions & 0 deletions go-runtime/zkm_runtime/deserialize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package zkm_runtime

import (
"encoding/binary"
"fmt"
"reflect"
)

func DeserializeData(data []byte, e any) {
if e == nil {
return
}
value := reflect.ValueOf(e)
// If e represents a value as opposed to a pointer, the answer won't
// get back to the caller. Make sure it's a pointer.
if value.Type().Kind() != reflect.Pointer {
panic("attempt to deserialize into a non-pointer")
}

if value.IsValid() {
if value.Kind() == reflect.Pointer && !value.IsNil() {
// That's okay, we'll store through the pointer.
} else if !value.CanSet() {
panic("gob: DecodeValue of unassignable value")
}
}

index, err := deserializeData(data, value.Elem(), 0)
if err != nil {
panic(err)
}
if index != len(data) {
panic("deserialize failed")
}
}

func deserializeData(data []byte, v reflect.Value, index int) (int, error) {
switch v.Kind() {
case reflect.Bool:
v.SetBool(data[index] == 1)
return index + 1, nil
case reflect.Int8:
v.SetInt(int64(int8(data[index])))
return index + 1, nil
case reflect.Uint8:
v.SetUint(uint64(data[index]))
return index + 1, nil
case reflect.Int16:
b := []byte{data[index], data[index+1]}
a := binary.LittleEndian.Uint16(b)
v.SetInt(int64(int16(a)))
return index + 2, nil
case reflect.Uint16:
b := []byte{data[index], data[index+1]}
a := binary.LittleEndian.Uint16(b)
v.SetUint(uint64(a))
return index + 2, nil
case reflect.Int32:
b := []byte{data[index], data[index+1], data[index+2], data[index+3]}
a := binary.LittleEndian.Uint32(b)
v.SetInt(int64(int32(a)))
return index + 4, nil
case reflect.Uint32:
b := []byte{data[index], data[index+1], data[index+2], data[index+3]}
a := binary.LittleEndian.Uint32(b)
v.SetUint(uint64(a))
return index + 4, nil
case reflect.Int64:
b := []byte{data[index], data[index+1], data[index+2], data[index+3],
data[index+4], data[index+5], data[index+6], data[index+7]}
a := binary.LittleEndian.Uint64(b)
v.SetInt(int64(a))
return index + 8, nil
case reflect.Uint64:
b := []byte{data[index], data[index+1], data[index+2], data[index+3],
data[index+4], data[index+5], data[index+6], data[index+7]}
a := binary.LittleEndian.Uint64(b)
v.SetUint(a)
return index + 8, nil
case reflect.Slice:
b := []byte{data[index], data[index+1], data[index+2], data[index+3],
data[index+4], data[index+5], data[index+6], data[index+7]}

length := binary.LittleEndian.Uint64(b)
index += 8
switch v.Type().Elem().Kind() {
case reflect.Uint8:
bytes := data[index : index+int(length)]
v.SetBytes(bytes)
return index + int(length), nil
}
return index, fmt.Errorf("unsupport type: %v, elem: %v", v.Kind(), v.Elem().Kind())
case reflect.Array:
for i := 0; i < v.Len(); i++ {
var err error
index, err = deserializeData(data, v.Index(i), index)
if err != nil {
return index, err
}
}
return index, nil
case reflect.String:
b := []byte{data[index], data[index+1], data[index+2], data[index+3],
data[index+4], data[index+5], data[index+6], data[index+7]}
l := binary.LittleEndian.Uint64(b)
index += 8
length := int(l)
str := make([]byte, length)
copy(str[:], data[index:index+length])
v.SetString(string(str))
return index + length, nil
case reflect.Ptr:
if data[index] == 0 {
v.SetZero()
return index + 1, nil
}
return deserializeData(data, v.Elem(), index+1)
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
var err error
index, err = deserializeData(data, field, index)
if err != nil {
return index, err
}
}
return index, nil
}
return index, fmt.Errorf("unsupport type: %v", v.Kind())
}
3 changes: 3 additions & 0 deletions go-runtime/zkm_runtime/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/zkMIPS/zkm/go-runtime/zkm_runtime

go 1.22.5
30 changes: 30 additions & 0 deletions go-runtime/zkm_runtime/runtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//go:build mips
// +build mips

package zkm_runtime

func SyscallWrite(fd int, write_buf []byte, nbytes int) int
func SyscallHintLen() int
func SyscallHintRead(ptr []byte, len int)

func Read[T any]() T {
len := SyscallHintLen()
var value []byte
capacity := (len + 3) / 4 * 4
value = make([]byte, capacity)
var result T
SyscallHintRead(value, len)
DeserializeData(value[0:len], &result)
return result
}

func Commit[T any](value T) {
bytes := MustSerializeData(value)
length := len(bytes)
if (length & 3) != 0 {
d := make([]byte, 4-(length&3))
bytes = append(bytes, d...)
}

SyscallWrite(3, bytes, length)
}
114 changes: 114 additions & 0 deletions go-runtime/zkm_runtime/serialize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// ported from https://github.com/blocto/solana-go-sdk/blob/v1.30.0/pkg/bincode/serialize.go

package zkm_runtime

import (
"encoding/binary"
"fmt"
"reflect"
)

func SerializeData(data any) ([]byte, error) {
return serializeData(reflect.ValueOf(data))
}

func MustSerializeData(data interface{}) []byte {
serializedData, err := serializeData(reflect.ValueOf(data))
if err != nil {
panic(err)
}
return serializedData
}

func serializeData(v reflect.Value) ([]byte, error) {
switch v.Kind() {
case reflect.Bool:
if v.Bool() {
return []byte{1}, nil
}
return []byte{0}, nil
case reflect.Int8:
return []byte{uint8(v.Int())}, nil
case reflect.Uint8:
return []byte{uint8(v.Uint())}, nil
case reflect.Int16:
b := make([]byte, 2)
binary.LittleEndian.PutUint16(b, uint16(v.Int()))
return b, nil
case reflect.Uint16:
b := make([]byte, 2)
binary.LittleEndian.PutUint16(b, uint16(v.Uint()))
return b, nil
case reflect.Int32:
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(v.Int()))
return b, nil
case reflect.Uint32:
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(v.Uint()))
return b, nil
case reflect.Int64:
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(v.Int()))
return b, nil
case reflect.Uint64:
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(v.Uint()))
return b, nil
case reflect.Slice:
switch v.Type().Elem().Kind() {
case reflect.Uint8:
output := make([]byte, 8)
binary.LittleEndian.PutUint64(output, uint64(v.Len()))

for i := 0; i < v.Len(); i++ {
d, err := serializeData(v.Index(i))
if err != nil {
return nil, err
}
output = append(output, d...)
}
return output, nil
}
return nil, fmt.Errorf("unsupport type: %v, elem: %v", v.Kind(), v.Elem().Kind())
case reflect.Array:
switch v.Type().Elem().Kind() {
case reflect.Uint8:
b := make([]byte, 0, v.Len())
for i := 0; i < v.Len(); i++ {
b = append(b, byte(v.Index(i).Uint()))
}
return b, nil
}
return nil, fmt.Errorf("unsupport type: %v, elem: %v", v.Kind(), v.Elem().Kind())
case reflect.String:
b := make([]byte, 8+len(v.String()))
binary.LittleEndian.PutUint64(b, uint64(len(v.String())))
copy(b[8:], []byte(v.String()))
return b, nil
case reflect.Ptr:
if v.IsNil() {
return []byte{0}, nil
}
d, err := serializeData(v.Elem())
if err != nil {
return nil, err
}
b := make([]byte, 1+len(d))
b[0] = 1
copy(b[1:], d[:])
return b, nil
case reflect.Struct:
data := make([]byte, 0, 1024)
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
d, err := serializeData(field)
if err != nil {
return nil, err
}
data = append(data, d...)
}
return data, nil
}
return nil, fmt.Errorf("unsupport type: %v", v.Kind())
}
24 changes: 24 additions & 0 deletions go-runtime/zkm_runtime/syscall_mips.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build mips
// +build mips

TEXT ·SyscallWrite(SB), $0-24
MOVW $4004, R2 // #define SYS_write 4004
MOVW fd+0(FP), R4
MOVW write_buf+4(FP), R5
MOVW nbytes+16(FP), R6
SYSCALL
MOVW R2, ret+0(FP)
RET

TEXT ·SyscallHintLen(SB), $0-4
MOVW $0xF0, R2 // #define SYS_hint_len 0xF0
SYSCALL
MOVW R2, ret+0(FP)
RET

TEXT ·SyscallHintRead(SB), $0-16
MOVW $0xF1, R2 // #define SYS_hint_read 0xF1
MOVW ptr+0(FP), R4
MOVW len+12(FP), R5
SYSCALL
RET
35 changes: 20 additions & 15 deletions prover/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,37 @@ GOOS=linux GOARCH=mips GOMIPS=softfloat go build hello.go
```
BASEDIR=./emulator/test-vectors RUST_LOG=info ELF_PATH=./emulator/test-vectors/minigeth BLOCK_NO=13284491 SEG_OUTPUT=/tmp/output SEG_SIZE=1024 ARGS="" \
cargo run --release --example zkmips split
```

OR
* Generate proof for specific segment (Set SEG_START_ID to specific segment id and set SEG_NUM to 1)

RUST_LOG=info ELF_PATH=./emulator/test-vectors/hello SEG_OUTPUT=/tmp/output SEG_SIZE=1024 ARGS="" \
cargo run --release --example zkmips split_without_preimage
```
BASEDIR=./emulator/test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE_DIR="/tmp/output" SEG_START_ID=0 SEG_NUM=1 SEG_SIZE=1024 \
cargo run --release --example zkmips prove_segments
```

* Generate proof for each segment
* Aggregate proof all segments (Set SEG_START_ID to 0, and set SEG_NUM to the total segments number)

```
BASEDIR=./emulator/test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE="/tmp/output/0" SEG_SIZE=1024 \
cargo run --release --example zkmips prove
BASEDIR=./emulator/test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE_DIR="/tmp/output" SEG_START_ID=0 SEG_NUM=299 SEG_SIZE=1024 \
cargo run --release --example zkmips prove_segments
```

* Aggregate proof
### Prove Go sdk code
The SDK provide Read and Commit interface to read input and commit output.
Take add-go for example:

* Build the add-go

```
BASEDIR=./emulator/test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE="/tmp/output/0" SEG_FILE2="/tmp/output/1" SEG_SIZE=1024 \
cargo run --release --example zkmips aggregate_proof
cd prover/examples/add-go
GOOS=linux GOARCH=mips GOMIPS=softfloat go build .
cd ../../
```

* Aggregate proof all
* Run the host program

```
BASEDIR=./emulator/test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE_DIR="/tmp/output" SEG_FILE_NUM=299 SEG_SIZE=1024 \
cargo run --release --example zkmips aggregate_proof_all
RUST_LOG=info ELF_PATH=examples/add-go/go-add HOST_PROGRAM=add_example SEG_OUTPUT=/tmp/output SEG_SIZE=262144 cargo run --release --example zkmips prove_host_program
```

## Prove the Rust code
Expand Down Expand Up @@ -75,9 +80,9 @@ cargo build --target=mips-unknown-linux-musl
cd ../..
ARGS="711e9609339e92b03ddc0a211827dba421f38f9ed8b9d806e1ffdd8c15ffa03d world!" RUST_LOG=info ELF_PATH=examples/sha2/target/mips-unknown-linux-musl/debug/sha2-bench SEG_OUTPUT=/tmp/output cargo run --release --example zkmips bench
ARGS="711e9609339e92b03ddc0a211827dba421f38f9ed8b9d806e1ffdd8c15ffa03d world!" RUST_LOG=info ELF_PATH=examples/sha2/target/mips-unknown-linux-musl/debug/sha2-bench HOST_PROGRAM=sha2_bench SEG_OUTPUT=/tmp/output cargo run --release --example zkmips prove_host_program
Or
RUST_LOG=info ELF_PATH=examples/revme/target/mips-unknown-linux-musl/debug/evm JSON_PATH=../emulator/test-vectors/test.json SEG_OUTPUT=/tmp/output SEG_SIZE=262144 cargo run --release --example zkmips revm
RUST_LOG=info ELF_PATH=examples/revme/target/mips-unknown-linux-musl/debug/evm HOST_PROGRAM=revm JSON_PATH=../emulator/test-vectors/test.json SEG_OUTPUT=/tmp/output SEG_SIZE=262144 cargo run --release --example zkmips prove_host_program
```
Loading

0 comments on commit 2faba08

Please sign in to comment.