├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── TODO.md ├── asctest └── arith.ts ├── ctest ├── Makefile ├── arith.c └── batchinput.c ├── rust-toolchain ├── rustfmt.toml ├── smt ├── README.md ├── addi64.py ├── subi64.py └── utils.py ├── specs ├── Cargo.toml ├── rustfmt.toml └── src │ ├── brtable.rs │ ├── configure_table.rs │ ├── encode │ ├── br_table.rs │ ├── frame_table.rs │ ├── image_table.rs │ ├── init_memory_table.rs │ ├── instruction_table.rs │ ├── memory_table.rs │ ├── mod.rs │ └── opcode.rs │ ├── etable.rs │ ├── external_host_call_table │ ├── encode.rs │ ├── mod.rs │ └── table.rs │ ├── host_function.rs │ ├── imtable.rs │ ├── itable.rs │ ├── jtable.rs │ ├── lib.rs │ ├── mtable.rs │ ├── step.rs │ └── types.rs ├── src ├── circuits │ ├── bit_table │ │ ├── assign.rs │ │ ├── configure.rs │ │ └── mod.rs │ ├── cell.rs │ ├── checksum │ │ ├── mod.rs │ │ ├── poseidon.rs │ │ └── poseidon │ │ │ ├── pow5.rs │ │ │ ├── primitives.rs │ │ │ └── primitives │ │ │ ├── grain.rs │ │ │ ├── mds.rs │ │ │ └── p128pow5t9.rs │ ├── config.rs │ ├── etable │ │ ├── allocator.rs │ │ ├── assign.rs │ │ ├── constraint_builder.rs │ │ ├── mod.rs │ │ └── op_configure │ │ │ ├── mod.rs │ │ │ ├── op_bin.rs │ │ │ ├── op_bin_bit.rs │ │ │ ├── op_bin_shift.rs │ │ │ ├── op_br.rs │ │ │ ├── op_br_if.rs │ │ │ ├── op_br_if_eqz.rs │ │ │ ├── op_br_table.rs │ │ │ ├── op_call.rs │ │ │ ├── op_call_host_foreign_circuit.rs │ │ │ ├── op_call_indirect.rs │ │ │ ├── op_const.rs │ │ │ ├── op_conversion.rs │ │ │ ├── op_drop.rs │ │ │ ├── op_global_get.rs │ │ │ ├── op_global_set.rs │ │ │ ├── op_load.rs │ │ │ ├── op_local_get.rs │ │ │ ├── op_local_set.rs │ │ │ ├── op_local_tee.rs │ │ │ ├── op_memory_grow.rs │ │ │ ├── op_memory_size.rs │ │ │ ├── op_rel.rs │ │ │ ├── op_return.rs │ │ │ ├── op_select.rs │ │ │ ├── op_store.rs │ │ │ ├── op_test.rs │ │ │ └── op_unary.rs │ ├── external_host_call_table │ │ ├── assign.rs │ │ ├── configure.rs │ │ └── mod.rs │ ├── image_table_checksum │ │ ├── assign.rs │ │ ├── configure.rs │ │ └── mod.rs │ ├── image_table_fixed │ │ ├── assign.rs │ │ ├── configure.rs │ │ └── mod.rs │ ├── jtable │ │ ├── assign.rs │ │ ├── configure.rs │ │ ├── expression.rs │ │ └── mod.rs │ ├── mod.rs │ ├── mtable │ │ ├── allocator.rs │ │ ├── assign.rs │ │ └── mod.rs │ ├── rtable.rs │ ├── test_circuit │ │ └── mod.rs │ ├── traits.rs │ └── utils │ │ ├── bit.rs │ │ ├── common_range.rs │ │ ├── mod.rs │ │ ├── row_diff.rs │ │ ├── step_status.rs │ │ ├── table_entry.rs │ │ ├── u16.rs │ │ └── u8.rs ├── cli │ ├── app_builder.rs │ ├── args.rs │ ├── command.rs │ ├── exec.rs │ ├── main.rs │ └── mod.rs ├── foreign │ ├── keccak_helper │ │ ├── mod.rs │ │ └── test.rs │ ├── log_helper │ │ └── mod.rs │ ├── mod.rs │ ├── require_helper │ │ ├── etable_op_configure.rs │ │ └── mod.rs │ └── wasm_input_helper │ │ ├── circuits │ │ ├── assign.rs │ │ ├── config.rs │ │ └── mod.rs │ │ ├── etable_op_configure.rs │ │ ├── mod.rs │ │ ├── runtime.rs │ │ └── test.rs ├── image_hasher │ └── mod.rs ├── lib.rs ├── profile │ ├── helper.rs │ ├── instruction_merge.rs │ ├── instruction_statistic.rs │ └── mod.rs ├── runtime │ ├── host │ │ ├── external_circuit_plugin.rs │ │ ├── host_env.rs │ │ ├── internal_circuit_plugin.rs │ │ └── mod.rs │ ├── mod.rs │ └── wasmi_interpreter.rs ├── test │ ├── mod.rs │ ├── spec │ │ ├── i32.wast │ │ └── mod.rs │ ├── test_binary_search.rs │ ├── test_fibonacci.rs │ ├── test_rlp.rs │ ├── test_rlp_simple.rs │ ├── test_start.rs │ ├── test_uniform_verifier.rs │ └── test_wasm_instructions │ │ ├── mod.rs │ │ ├── op_bin.rs │ │ ├── op_bin_bit.rs │ │ ├── op_bin_shift.rs │ │ ├── op_br.rs │ │ ├── op_br_if.rs │ │ ├── op_br_if_eqz.rs │ │ ├── op_br_table.rs │ │ ├── op_call.rs │ │ ├── op_call_host.rs │ │ ├── op_call_indirect.rs │ │ ├── op_const.rs │ │ ├── op_conversion.rs │ │ ├── op_global_get.rs │ │ ├── op_global_set.rs │ │ ├── op_load.rs │ │ ├── op_local_get.rs │ │ ├── op_local_set.rs │ │ ├── op_local_tee.rs │ │ ├── op_memory_grow.rs │ │ ├── op_memory_size.rs │ │ ├── op_rel.rs │ │ ├── op_return.rs │ │ ├── op_select.rs │ │ ├── op_store.rs │ │ ├── op_test.rs │ │ └── op_unary.rs └── traits │ ├── circuits │ ├── bit_range_table.rs │ └── mod.rs │ └── mod.rs ├── test_cli.sh ├── test_cli_checksum.sh ├── wasm ├── bsearch_64.c ├── bsearch_64.wasm ├── keccak.wasm ├── rlp.wasm ├── rlp_simple.wasm ├── sha256.wasm ├── sha256_v2.wasm └── wasm_output.wasm ├── zkwasm-bk.png └── zkwasm-wh.png /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: rust-build 2 | 3 | on: 4 | pull_request: 5 | branches: [ "**" ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | components: rustfmt, clippy 20 | - name: Code Style Check 21 | run: cargo fmt --check 22 | - name: Build 23 | run: cargo build 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | # Allows you to run this workflow manually from the Actions tab 4 | workflow_dispatch: 5 | 6 | env: 7 | CARGO_TERM_COLOR: always 8 | 9 | jobs: 10 | build: 11 | name: release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Get current date 15 | id: date 16 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: recursive 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: nightly 23 | components: clippy 24 | override: true 25 | - name: Build 26 | run: cargo +nightly build --release 27 | - name: Package 28 | run: tar cf zkwasm-cli-nightly-build-Linux_x86_64.tar -C target/release cli 29 | - name: Release 30 | uses: "marvinpinto/action-automatic-releases@latest" 31 | with: 32 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 33 | automatic_release_tag: "nightly-build-${{ steps.date.outputs.date }}" 34 | prerelease: true 35 | title: "Development Build" 36 | files: | 37 | zkwasm-cli-nightly-build-Linux_x86_64.tar 38 | 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.json 3 | /output 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wasmi"] 2 | path = wasmi 3 | url = git@github.com:DelphinusLab/wasmi.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "delphinus-zkwasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "cli" 8 | path = "src/cli/main.rs" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | anyhow = "1.0.68" 14 | ark-std = { version = "0.3.0", features = ["print-trace"] } 15 | downcast-rs = "1.2.0" 16 | env_logger = "0.9.3" 17 | halo2aggregator-s = { git = "https://github.com/DelphinusLab/halo2aggregator-s.git", tag = "bisect-lookup-0.2.1" } 18 | halo2_proofs = { git = "https://github.com/junyu0312/halo2", branch = "gpu", default-features = true } 19 | log = "0.4.17" 20 | md5 = "0.7.0" 21 | num-integer = "0.1" 22 | num-bigint = { version = "0.4", features = ["rand"] } 23 | num-traits = "0.2.15" 24 | wabt = "0.10.0" 25 | wasmi = { path = "./wasmi" } 26 | lazy_static = "1.4.0" 27 | rand = "0.8.4" 28 | sha2 = "0.10.6" 29 | specs = { path = "./specs" } 30 | strum = "0.24.1" 31 | strum_macros = "0.24.1" 32 | wast = "47.0.0" 33 | clap = { version = "3.2.22", features = ["derive","cargo"] } 34 | hex = "0.4.3" 35 | uuid = { version = "1.2.2", features = ["serde", "v4"] } 36 | serde = { version = "1.0", features = ["derive"] } 37 | serde_json = "1.0" 38 | bitvec = "1.0.1" 39 | 40 | [dev-dependencies] 41 | rusty-fork = "0.3.0" 42 | 43 | [features] 44 | default = [] 45 | checksum = [] 46 | cuda = ["halo2_proofs/cuda", "specs/cuda"] 47 | 48 | [profile.dev] 49 | opt-level = 3 50 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 1. Use column allocator to avoid prepare column at start 2 | 2. Classify the columns to unify some configure, especially for range check 3 | 3. Add API to fill data into tables 4 | -------------------------------------------------------------------------------- /asctest/arith.ts: -------------------------------------------------------------------------------- 1 | @external("env", "wasm_input") 2 | declare function wasm_input(x: T): T 3 | export function mix(): i32 { 4 | var n = wasm_input(0); 5 | var m = wasm_input(0); 6 | return n+m; 7 | } 8 | -------------------------------------------------------------------------------- /ctest/Makefile: -------------------------------------------------------------------------------- 1 | LIBS = -lkernel32 -luser32 -lgdi32 -lopengl32 2 | CFLAGS = -Wall 3 | 4 | # Should be equivalent to your list of C files, if you don't build selectively 5 | CFILES = $(wildcard *.c) 6 | CLANG = clang-15 7 | FLAGS = -flto -O3 -nostdlib -fno-builtin -ffreestanding -mexec-model=reactor --target=wasm32 -Wl,--strip-all -Wl,--initial-memory=131072 -Wl,--max-memory=131072 -Wl,--no-entry -Wl,--allow-undefined -Wl,--compress-relocations -Wl,--export-dynamic 8 | 9 | WASMS = $(patsubst %.c, %.wasm, $(CFILES)) 10 | 11 | %.wasm: %.c 12 | $(CLANG) -o $@ $^ $(FLAGS) 13 | 14 | all: $(WASMS) 15 | -------------------------------------------------------------------------------- /ctest/arith.c: -------------------------------------------------------------------------------- 1 | #include 2 | void require(int cond); 3 | void log(uint64_t value); 4 | 5 | unsigned long long wasm_input(int); 6 | __attribute__((visibility("default"))) int zkmain() 7 | { 8 | uint32_t a = (uint32_t)wasm_input(1); 9 | uint32_t b = (uint32_t)wasm_input(1); 10 | require(a > b); 11 | log(a > b); 12 | return a + b; 13 | } 14 | -------------------------------------------------------------------------------- /ctest/batchinput.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #if defined(__wasm__) 4 | void require(int cond); 5 | #endif 6 | 7 | unsigned long long wasm_input(int); 8 | 9 | /* Convert list of u64 into bytes */ 10 | static __inline__ void read_bytes_from_u64(void *dst, int byte_length, bool is_public) { 11 | uint64_t *dst64 = (uint64_t*) dst; 12 | #pragma clang loop unroll(full) 13 | for (int i = 0; i * 8 < byte_length; i++) { 14 | if (i*8 + 8 < byte_length) { 15 | dst64[i] = wasm_input(is_public); 16 | } else { 17 | //less then 16 bytes on demand 18 | uint64_t uint64_cache = wasm_input(is_public); 19 | uint8_t *u8_p = (uint8_t *)(&uint64_cache); 20 | #pragma clang loop unroll(full) 21 | for (int j = i*8; j= 0, lhs <= 1))) 26 | s.add(ForAll([lhs], is_i32(lhs) == And(lhs >= 0, lhs < I32_MODULUS))) 27 | s.add(ForAll([lhs], is_i64(lhs) == And(lhs >= 0, lhs < I64_MODULUS))) 28 | s.add(ForAll([lhs], is_field(lhs) == And(lhs >= 0, lhs < Fr))) 29 | s.add(ForAll([lhs], is_common_range(lhs) == And(lhs >= 0, lhs <= MAX_COMMON_RANGE))) 30 | 31 | return s -------------------------------------------------------------------------------- /specs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "specs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | halo2_proofs = { git = "https://github.com/junyu0312/halo2", branch = "gpu", default-features = true } 10 | lazy_static = "1.4.0" 11 | num-bigint = { version = "0.4", features = ["rand"] } 12 | parity-wasm = { version = "0.42.0", default-features = false } 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | strum = "0.24.1" 16 | strum_macros = "0.24.1" 17 | 18 | [features] 19 | default = [] 20 | cuda = ["halo2_proofs/cuda"] 21 | -------------------------------------------------------------------------------- /specs/rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_granularity = "Item" -------------------------------------------------------------------------------- /specs/src/brtable.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use serde::Serialize; 4 | 5 | #[derive(Serialize, Debug, Clone)] 6 | pub struct BrTableEntry { 7 | pub fid: u32, 8 | pub iid: u32, 9 | pub index: u32, 10 | pub drop: u32, 11 | pub keep: u32, 12 | pub dst_pc: u32, 13 | } 14 | 15 | #[derive(Debug)] 16 | pub struct BrTable(Vec); 17 | 18 | impl BrTable { 19 | pub fn new(entries: Vec) -> Self { 20 | BrTable(entries) 21 | } 22 | 23 | pub fn entries(&self) -> &Vec { 24 | &self.0 25 | } 26 | } 27 | 28 | #[derive(Serialize, Debug, Clone)] 29 | pub struct ElemEntry { 30 | pub table_idx: u32, 31 | pub type_idx: u32, 32 | pub offset: u32, 33 | pub func_idx: u32, 34 | } 35 | 36 | #[derive(Debug, Default, Serialize, Clone)] 37 | pub struct ElemTable(BTreeMap<(u32, u32), ElemEntry>); 38 | 39 | impl ElemTable { 40 | pub fn insert(&mut self, entry: ElemEntry) { 41 | self.0.insert((entry.table_idx, entry.offset), entry); 42 | } 43 | 44 | pub fn entries(&self) -> Vec { 45 | self.0.values().cloned().collect() 46 | } 47 | } 48 | 49 | pub enum IndirectClass { 50 | BrTable, 51 | CallIndirect, 52 | } 53 | -------------------------------------------------------------------------------- /specs/src/configure_table.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | pub const WASM_PAGE_SIZE: u64 = 65536; 4 | 5 | const WASM_32_MAXIMAL_PAGES_DEFAULT: u32 = 65536; 6 | 7 | #[derive(Serialize, Debug, Clone, Copy)] 8 | pub struct ConfigureTable { 9 | pub init_memory_pages: u32, 10 | pub maximal_memory_pages: u32, 11 | } 12 | 13 | impl Default for ConfigureTable { 14 | fn default() -> Self { 15 | Self { 16 | init_memory_pages: 0, 17 | maximal_memory_pages: WASM_32_MAXIMAL_PAGES_DEFAULT, 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /specs/src/encode/br_table.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigUint; 2 | 3 | use crate::brtable::BrTableEntry; 4 | use crate::brtable::ElemEntry; 5 | use crate::brtable::IndirectClass; 6 | use crate::encode::COMMON_RANGE_OFFSET; 7 | 8 | use super::FromBn; 9 | 10 | lazy_static! { 11 | static ref INDIRECT_CLASS_SHIFT: BigUint = BigUint::from(1u64) << 192; 12 | } 13 | 14 | pub(crate) const BR_TABLE_ENCODE_BOUNDARY: u32 = 224; 15 | 16 | pub fn encode_br_table_entry( 17 | fid: T, 18 | iid: T, 19 | index: T, 20 | drop: T, 21 | keep: T, 22 | dst_pc: T, 23 | ) -> T { 24 | const FID_SHIFT: u32 = IID_SHIFT + COMMON_RANGE_OFFSET; 25 | const IID_SHIFT: u32 = INDEX_SHIFT + COMMON_RANGE_OFFSET; 26 | const INDEX_SHIFT: u32 = DROP_SHIFT + COMMON_RANGE_OFFSET; 27 | const DROP_SHIFT: u32 = KEEP_SHIFT + COMMON_RANGE_OFFSET; 28 | const KEEP_SHIFT: u32 = DST_PC_SHIFT + COMMON_RANGE_OFFSET; 29 | const DST_PC_SHIFT: u32 = 0; 30 | 31 | assert!(FID_SHIFT + COMMON_RANGE_OFFSET <= BR_TABLE_ENCODE_BOUNDARY); 32 | 33 | T::from_bn(&(BigUint::from(IndirectClass::BrTable as u64))) * T::from_bn(&INDIRECT_CLASS_SHIFT) 34 | + fid * T::from_bn(&(BigUint::from(1u64) << FID_SHIFT)) 35 | + iid * T::from_bn(&(BigUint::from(1u64) << IID_SHIFT)) 36 | + index * T::from_bn(&(BigUint::from(1u64) << INDEX_SHIFT)) 37 | + drop * T::from_bn(&(BigUint::from(1u64) << DROP_SHIFT)) 38 | + keep * T::from_bn(&(BigUint::from(1u64) << KEEP_SHIFT)) 39 | + dst_pc 40 | } 41 | 42 | pub fn encode_elem_entry(table_idx: T, type_idx: T, offset: T, func_idx: T) -> T { 43 | const TABLE_INDEX_SHIFT: u32 = TYPE_INDEX_SHIFT + COMMON_RANGE_OFFSET; 44 | const TYPE_INDEX_SHIFT: u32 = OFFSET_SHIFT + COMMON_RANGE_OFFSET; 45 | const OFFSET_SHIFT: u32 = FUNC_INDEX + COMMON_RANGE_OFFSET; 46 | const FUNC_INDEX: u32 = 0; 47 | 48 | assert!(TABLE_INDEX_SHIFT + COMMON_RANGE_OFFSET <= BR_TABLE_ENCODE_BOUNDARY); 49 | 50 | T::from_bn(&(BigUint::from(IndirectClass::CallIndirect as u64))) 51 | * T::from_bn(&INDIRECT_CLASS_SHIFT) 52 | + table_idx * T::from_bn(&(BigUint::from(1u64) << TABLE_INDEX_SHIFT)) 53 | + type_idx * T::from_bn(&(BigUint::from(1u64) << TYPE_INDEX_SHIFT)) 54 | + offset * T::from_bn(&(BigUint::from(1u64) << OFFSET_SHIFT)) 55 | + func_idx 56 | } 57 | 58 | impl BrTableEntry { 59 | pub fn encode(&self) -> BigUint { 60 | encode_br_table_entry( 61 | BigUint::from(self.fid), 62 | BigUint::from(self.iid), 63 | BigUint::from(self.index), 64 | BigUint::from(self.drop), 65 | BigUint::from(self.keep), 66 | BigUint::from(self.dst_pc), 67 | ) 68 | } 69 | } 70 | 71 | impl ElemEntry { 72 | pub fn encode(&self) -> BigUint { 73 | encode_elem_entry( 74 | BigUint::from(self.table_idx), 75 | BigUint::from(self.type_idx), 76 | BigUint::from(self.offset), 77 | BigUint::from(self.func_idx), 78 | ) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /specs/src/encode/frame_table.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigUint; 2 | use num_bigint::ToBigUint; 3 | 4 | use crate::encode::COMMON_RANGE_OFFSET; 5 | use crate::jtable::JumpTableEntry; 6 | use crate::jtable::StaticFrameEntry; 7 | 8 | use super::FromBn; 9 | 10 | pub fn encode_frame_table_entry( 11 | frame_id: T, 12 | last_frame_id: T, 13 | callee_fid: T, 14 | fid: T, 15 | iid: T, 16 | ) -> T { 17 | const FRAME_ID_SHIFT: u32 = LAST_JUMP_FRAME_ID_SHIFT + COMMON_RANGE_OFFSET; 18 | const LAST_JUMP_FRAME_ID_SHIFT: u32 = CALLEE_FID + COMMON_RANGE_OFFSET; 19 | const CALLEE_FID: u32 = FID_SHIFT + COMMON_RANGE_OFFSET; 20 | const FID_SHIFT: u32 = IID_SHIFT + COMMON_RANGE_OFFSET; 21 | const IID_SHIFT: u32 = 0; 22 | 23 | frame_id * T::from_bn(&(1u64.to_biguint().unwrap() << FRAME_ID_SHIFT)) 24 | + last_frame_id * T::from_bn(&(1u64.to_biguint().unwrap() << LAST_JUMP_FRAME_ID_SHIFT)) 25 | + callee_fid * T::from_bn(&(1u64.to_biguint().unwrap() << CALLEE_FID)) 26 | + fid * T::from_bn(&(1u64.to_biguint().unwrap() << FID_SHIFT)) 27 | + iid 28 | } 29 | 30 | impl StaticFrameEntry { 31 | pub fn encode(&self) -> BigUint { 32 | encode_frame_table_entry( 33 | self.frame_id.to_biguint().unwrap(), 34 | self.next_frame_id.to_biguint().unwrap(), 35 | self.callee_fid.to_biguint().unwrap(), 36 | self.fid.to_biguint().unwrap(), 37 | self.iid.to_biguint().unwrap(), 38 | ) 39 | } 40 | } 41 | 42 | impl JumpTableEntry { 43 | pub fn encode(&self) -> BigUint { 44 | encode_frame_table_entry( 45 | self.eid.to_biguint().unwrap(), 46 | self.last_jump_eid.to_biguint().unwrap(), 47 | self.callee_fid.to_biguint().unwrap(), 48 | self.inst.fid.to_biguint().unwrap(), 49 | self.inst.iid.to_biguint().unwrap(), 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /specs/src/encode/image_table.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::ToBigUint; 2 | 3 | use crate::encode::br_table::BR_TABLE_ENCODE_BOUNDARY; 4 | use crate::encode::init_memory_table::INIT_MEMORY_ENCODE_BOUNDARY; 5 | use crate::encode::instruction_table::INSTRUCTION_ENCODE_BOUNDARY; 6 | 7 | use super::FromBn; 8 | 9 | #[derive(Clone, Copy)] 10 | pub enum ImageTableEncoder { 11 | Instruction = 1, 12 | BrTable = 2, 13 | InitMemory = 3, 14 | } 15 | 16 | impl ImageTableEncoder { 17 | pub fn encode(&self, data: T) -> T { 18 | const CLASS_SHIFT: u32 = 224; 19 | 20 | assert!(INSTRUCTION_ENCODE_BOUNDARY <= CLASS_SHIFT); 21 | assert!(BR_TABLE_ENCODE_BOUNDARY <= CLASS_SHIFT); 22 | assert!(INIT_MEMORY_ENCODE_BOUNDARY <= CLASS_SHIFT); 23 | 24 | T::from_bn(&(*self as u64).to_biguint().unwrap()) 25 | * T::from_bn(&(1u64.to_biguint().unwrap() << CLASS_SHIFT)) 26 | + data 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /specs/src/encode/init_memory_table.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigUint; 2 | use num_bigint::ToBigUint; 3 | 4 | use super::FromBn; 5 | use crate::encode::COMMON_RANGE_OFFSET; 6 | use crate::imtable::InitMemoryTableEntry; 7 | 8 | pub(crate) const INIT_MEMORY_ENCODE_BOUNDARY: u32 = 224; 9 | 10 | pub fn encode_init_memory_table_entry( 11 | ltype: T, 12 | is_mutable: T, 13 | start_offset: T, 14 | end_offset: T, 15 | value: T, 16 | ) -> T { 17 | const LTYPE_SHIFT: u32 = IS_MUTABLE_SHIFT + COMMON_RANGE_OFFSET; 18 | const IS_MUTABLE_SHIFT: u32 = START_OFFSET_SHIFT + COMMON_RANGE_OFFSET; 19 | const START_OFFSET_SHIFT: u32 = END_OFFSET_SHIFT + 64; 20 | const END_OFFSET_SHIFT: u32 = VALUE_SHIFT + 64; 21 | const VALUE_SHIFT: u32 = 0; 22 | 23 | assert!(LTYPE_SHIFT + COMMON_RANGE_OFFSET <= INIT_MEMORY_ENCODE_BOUNDARY); 24 | 25 | ltype * T::from_bn(&(1u64.to_biguint().unwrap() << LTYPE_SHIFT)) 26 | + is_mutable * T::from_bn(&(1u64.to_biguint().unwrap() << IS_MUTABLE_SHIFT)) 27 | + start_offset * T::from_bn(&(1u64.to_biguint().unwrap() << START_OFFSET_SHIFT)) 28 | + end_offset * T::from_bn(&(1u64.to_biguint().unwrap() << END_OFFSET_SHIFT)) 29 | + value 30 | } 31 | 32 | impl InitMemoryTableEntry { 33 | pub fn encode(&self) -> BigUint { 34 | encode_init_memory_table_entry( 35 | BigUint::from(self.ltype as u32), 36 | BigUint::from(self.is_mutable as u32), 37 | BigUint::from(self.start_offset), 38 | BigUint::from(self.end_offset), 39 | self.value.to_biguint().unwrap(), 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /specs/src/encode/instruction_table.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigUint; 2 | use num_bigint::ToBigUint; 3 | 4 | use crate::encode::COMMON_RANGE_OFFSET; 5 | use crate::itable::InstructionTableEntry; 6 | use crate::itable::OPCODE_SHIFT; 7 | 8 | use super::FromBn; 9 | 10 | pub(crate) const INSTRUCTION_ENCODE_BOUNDARY: u32 = 224; 11 | 12 | pub fn encode_instruction_table_entry(fid: T, iid: T, opcode: T) -> T { 13 | const FID_SHIFT: u32 = IID_SHIFT + COMMON_RANGE_OFFSET; 14 | const IID_SHIFT: u32 = OPCODE_SHIFT; 15 | 16 | assert!(FID_SHIFT + COMMON_RANGE_OFFSET <= INSTRUCTION_ENCODE_BOUNDARY); 17 | 18 | fid * T::from_bn(&(1u64.to_biguint().unwrap() << FID_SHIFT)) 19 | + iid * T::from_bn(&(1u64.to_biguint().unwrap() << IID_SHIFT)) 20 | + opcode 21 | } 22 | 23 | impl InstructionTableEntry { 24 | pub fn encode(&self) -> BigUint { 25 | encode_instruction_table_entry( 26 | BigUint::from(self.fid), 27 | BigUint::from(self.iid), 28 | self.opcode.clone().into(), 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /specs/src/encode/memory_table.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::ToBigUint; 2 | 3 | use super::FromBn; 4 | use crate::encode::COMMON_RANGE_OFFSET; 5 | 6 | pub fn encode_memory_table_entry( 7 | start_eid: T, 8 | end_eid: T, 9 | offset: T, 10 | location_type: T, 11 | is_i32: T, 12 | value: T, 13 | ) -> T { 14 | const END_SHIFT: u32 = START_EID_SHIFT + COMMON_RANGE_OFFSET; 15 | const START_EID_SHIFT: u32 = END_EID_SHIFT + COMMON_RANGE_OFFSET; 16 | const END_EID_SHIFT: u32 = OFFSET_SHIFT + COMMON_RANGE_OFFSET; 17 | const OFFSET_SHIFT: u32 = LOCATION_TYPE_SHIFT + COMMON_RANGE_OFFSET; 18 | const LOCATION_TYPE_SHIFT: u32 = IS_I32_SHIFT + 1; 19 | const IS_I32_SHIFT: u32 = VALUE_SHIFT + 64; 20 | const VALUE_SHIFT: u32 = 0; 21 | 22 | assert!(END_SHIFT < 240); 23 | 24 | start_eid * T::from_bn(&(1u64.to_biguint().unwrap() << START_EID_SHIFT)) 25 | + end_eid * T::from_bn(&(1u64.to_biguint().unwrap() << END_EID_SHIFT)) 26 | + offset * T::from_bn(&(1u64.to_biguint().unwrap() << OFFSET_SHIFT)) 27 | + location_type * T::from_bn(&(1u64.to_biguint().unwrap() << LOCATION_TYPE_SHIFT)) 28 | + is_i32 * T::from_bn(&(1u64.to_biguint().unwrap() << IS_I32_SHIFT)) 29 | + value 30 | } 31 | -------------------------------------------------------------------------------- /specs/src/encode/mod.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Add; 2 | use std::ops::Mul; 3 | 4 | use halo2_proofs::arithmetic::FieldExt; 5 | use halo2_proofs::plonk::Expression; 6 | use num_bigint::BigUint; 7 | 8 | pub mod br_table; 9 | pub mod frame_table; 10 | pub mod image_table; 11 | pub mod init_memory_table; 12 | pub mod instruction_table; 13 | pub mod memory_table; 14 | pub mod opcode; 15 | 16 | pub(crate) const COMMON_RANGE_OFFSET: u32 = 32; 17 | 18 | pub trait FromBn: Sized + Add + Mul { 19 | fn zero() -> Self; 20 | fn from_bn(bn: &BigUint) -> Self; 21 | } 22 | 23 | impl FromBn for BigUint { 24 | fn zero() -> Self { 25 | BigUint::from(0u64) 26 | } 27 | 28 | fn from_bn(bn: &BigUint) -> Self { 29 | bn.clone() 30 | } 31 | } 32 | 33 | fn bn_to_field(bn: &BigUint) -> F { 34 | let mut bytes = bn.to_bytes_le(); 35 | bytes.resize(32, 0); 36 | let mut bytes = &bytes[..]; 37 | F::read(&mut bytes).unwrap() 38 | } 39 | 40 | impl FromBn for Expression { 41 | fn from_bn(bn: &BigUint) -> Self { 42 | halo2_proofs::plonk::Expression::Constant(bn_to_field(bn)) 43 | } 44 | 45 | fn zero() -> Self { 46 | halo2_proofs::plonk::Expression::Constant(F::zero()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /specs/src/encode/opcode.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigUint; 2 | 3 | use crate::itable::ConversionOp; 4 | use crate::itable::OpcodeClass; 5 | use crate::itable::OPCODE_ARG0_SHIFT as OPCODE_ARG0; 6 | use crate::itable::OPCODE_ARG1_SHIFT as OPCODE_ARG1; 7 | use crate::itable::OPCODE_CLASS_SHIFT as OPCODE_CLASS; 8 | 9 | use super::FromBn; 10 | 11 | lazy_static! { 12 | static ref OPCODE_CLASS_SHIFT: BigUint = BigUint::from(1u64) << OPCODE_CLASS; 13 | static ref OPCODE_ARG0_SHIFT: BigUint = BigUint::from(1u64) << OPCODE_ARG0; 14 | static ref OPCODE_ARG1_SHIFT: BigUint = BigUint::from(1u64) << OPCODE_ARG1; 15 | } 16 | 17 | pub fn encode_global_get(globalidx: T) -> T { 18 | T::from_bn(&(BigUint::from(OpcodeClass::GlobalGet as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 19 | + globalidx 20 | } 21 | 22 | pub fn encode_global_set(globalidx: T) -> T { 23 | T::from_bn(&(BigUint::from(OpcodeClass::GlobalSet as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 24 | + globalidx 25 | } 26 | 27 | pub fn encode_call(function_index: T) -> T { 28 | T::from_bn(&(BigUint::from(OpcodeClass::Call as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 29 | + function_index * T::from_bn(&OPCODE_ARG0_SHIFT) 30 | } 31 | 32 | pub fn encode_call_indirect(type_index: T) -> T { 33 | T::from_bn(&(BigUint::from(OpcodeClass::CallIndirect as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 34 | + type_index * T::from_bn(&OPCODE_ARG0_SHIFT) 35 | } 36 | 37 | pub fn encode_call_host(op: T, is_ret: T) -> T { 38 | T::from_bn(&(BigUint::from(OpcodeClass::CallHost as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 39 | + op * T::from_bn(&OPCODE_ARG0_SHIFT) 40 | + is_ret * T::from_bn(&OPCODE_ARG1_SHIFT) 41 | } 42 | 43 | pub fn encode_br(drop: T, keep: T, dst_pc: T) -> T { 44 | T::from_bn(&(BigUint::from(OpcodeClass::Br as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 45 | + drop * T::from_bn(&OPCODE_ARG0_SHIFT) 46 | + keep * T::from_bn(&OPCODE_ARG1_SHIFT) 47 | + dst_pc 48 | } 49 | 50 | pub fn encode_br_if_eqz(drop: T, keep: T, dst_pc: T) -> T { 51 | T::from_bn(&(BigUint::from(OpcodeClass::BrIfEqz as u64))) * T::from_bn(&OPCODE_CLASS_SHIFT) 52 | + drop * T::from_bn(&OPCODE_ARG0_SHIFT) 53 | + keep * T::from_bn(&OPCODE_ARG1_SHIFT) 54 | + dst_pc 55 | } 56 | 57 | pub fn encode_br_table(len: T) -> T { 58 | T::from_bn(&BigUint::from(OpcodeClass::BrTable as u64)) + len 59 | } 60 | 61 | pub fn encode_conversion(op: ConversionOp) -> T { 62 | match op { 63 | ConversionOp::I32WrapI64 => { 64 | T::from_bn(&(BigUint::from(OpcodeClass::Conversion as u64))) 65 | * T::from_bn(&OPCODE_CLASS_SHIFT) 66 | + T::from_bn(&(BigUint::from(ConversionOp::I32WrapI64 as u64))) 67 | * T::from_bn(&OPCODE_ARG0_SHIFT) 68 | } 69 | ConversionOp::I64ExtendI32s => { 70 | T::from_bn(&(BigUint::from(OpcodeClass::Conversion as u64))) 71 | * T::from_bn(&OPCODE_CLASS_SHIFT) 72 | + T::from_bn(&(BigUint::from(ConversionOp::I64ExtendI32s as u64))) 73 | * T::from_bn(&OPCODE_ARG0_SHIFT) 74 | } 75 | ConversionOp::I64ExtendI32u => { 76 | T::from_bn(&(BigUint::from(OpcodeClass::Conversion as u64))) 77 | * T::from_bn(&OPCODE_CLASS_SHIFT) 78 | + T::from_bn(&(BigUint::from(ConversionOp::I64ExtendI32u as u64))) 79 | * T::from_bn(&OPCODE_ARG0_SHIFT) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /specs/src/etable.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use super::itable::InstructionTableEntry; 4 | use crate::host_function::HostPlugin; 5 | use crate::step::StepInfo; 6 | 7 | #[derive(Clone, Debug, Serialize)] 8 | pub struct EventTableEntry { 9 | pub eid: u32, 10 | pub sp: u32, 11 | pub allocated_memory_pages: u32, 12 | pub last_jump_eid: u32, 13 | pub inst: InstructionTableEntry, 14 | pub step_info: StepInfo, 15 | } 16 | 17 | pub struct RestMops { 18 | rest_mops: Vec, 19 | } 20 | 21 | impl Iterator for RestMops { 22 | type Item = u64; 23 | 24 | fn next(&mut self) -> Option { 25 | self.rest_mops.pop() 26 | } 27 | } 28 | 29 | pub struct RestJops { 30 | rest_jops: Vec, 31 | } 32 | 33 | impl Iterator for RestJops { 34 | type Item = u64; 35 | 36 | fn next(&mut self) -> Option { 37 | self.rest_jops.pop() 38 | } 39 | } 40 | 41 | #[derive(Debug, Default, Clone, Serialize)] 42 | pub struct EventTable(Vec); 43 | 44 | impl EventTable { 45 | pub fn new(entries: Vec) -> Self { 46 | Self(entries) 47 | } 48 | 49 | pub fn entries(&self) -> &Vec { 50 | &self.0 51 | } 52 | 53 | pub fn entries_mut(&mut self) -> &mut Vec { 54 | &mut self.0 55 | } 56 | 57 | pub fn filter_foreign_entries(&self, foreign: HostPlugin) -> Vec { 58 | self.0 59 | .clone() 60 | .into_iter() 61 | .filter(|entry| match entry.step_info { 62 | StepInfo::CallHost { plugin, .. } => plugin == foreign, 63 | _ => false, 64 | }) 65 | .collect::>() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /specs/src/external_host_call_table/encode.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigUint; 2 | 3 | use crate::encode::FromBn; 4 | 5 | pub fn encode_host_call_entry(host_call_idx: T, op: T, is_ret: T, arg: T) -> T { 6 | host_call_idx * T::from_bn(&(BigUint::from(1u64) << 96)) 7 | + op * T::from_bn(&(BigUint::from(1u64) << 80)) 8 | + is_ret * T::from_bn(&(BigUint::from(1u64) << 64)) 9 | + arg 10 | } 11 | -------------------------------------------------------------------------------- /specs/src/external_host_call_table/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::ser::SerializeStruct; 2 | use serde::Serialize; 3 | 4 | use crate::host_function::Signature; 5 | use crate::types::ValueType; 6 | 7 | pub mod encode; 8 | mod table; 9 | 10 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, PartialOrd, Ord)] 11 | pub enum ExternalHostCallSignature { 12 | Argument, 13 | Return, 14 | } 15 | 16 | impl ExternalHostCallSignature { 17 | pub fn is_ret(&self) -> bool { 18 | *self == ExternalHostCallSignature::Return 19 | } 20 | } 21 | 22 | impl Into for ExternalHostCallSignature { 23 | fn into(self) -> Signature { 24 | match self { 25 | ExternalHostCallSignature::Argument => Signature { 26 | params: vec![ValueType::I64], 27 | return_type: None, 28 | }, 29 | ExternalHostCallSignature::Return => Signature { 30 | params: vec![], 31 | return_type: Some(ValueType::I64), 32 | }, 33 | } 34 | } 35 | } 36 | 37 | pub struct ExternalHostCallEntry { 38 | pub op: usize, 39 | pub value: u64, 40 | pub sig: ExternalHostCallSignature, 41 | } 42 | 43 | impl Serialize for ExternalHostCallEntry { 44 | fn serialize(&self, serializer: S) -> Result 45 | where 46 | S: serde::Serializer, 47 | { 48 | let mut s = serializer.serialize_struct("ExternalHostCallEntry", 3)?; 49 | s.serialize_field("op", &self.op)?; 50 | s.serialize_field("value", &self.value)?; 51 | s.serialize_field("is_ret", &self.sig.is_ret())?; 52 | s.end() 53 | } 54 | } 55 | 56 | #[derive(Serialize)] 57 | pub struct ExternalHostCallTable(Vec); 58 | 59 | impl ExternalHostCallTable { 60 | pub fn entries(&self) -> &Vec { 61 | &self.0 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /specs/src/external_host_call_table/table.rs: -------------------------------------------------------------------------------- 1 | use super::ExternalHostCallEntry; 2 | use super::ExternalHostCallTable; 3 | use crate::etable::EventTable; 4 | use crate::step::StepInfo; 5 | 6 | impl EventTable { 7 | pub fn filter_external_host_call_table(&self) -> ExternalHostCallTable { 8 | let entries = self 9 | .entries() 10 | .iter() 11 | .filter_map(|entry| { 12 | if let StepInfo::ExternalHostCall { op, value, sig, .. } = &entry.step_info { 13 | Some(ExternalHostCallEntry { 14 | op: *op, 15 | value: value.unwrap(), 16 | sig: *sig, 17 | }) 18 | } else { 19 | None 20 | } 21 | }) 22 | .collect(); 23 | 24 | ExternalHostCallTable(entries) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /specs/src/host_function.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::external_host_call_table::ExternalHostCallSignature; 4 | use crate::types::ValueType; 5 | 6 | #[derive(Debug, Clone, PartialEq, Eq, Serialize)] 7 | pub struct Signature { 8 | pub params: Vec, 9 | pub return_type: Option, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub enum Error { 14 | DuplicateRegister, 15 | } 16 | 17 | #[derive(Debug, Clone)] 18 | pub enum HostFunctionDesc { 19 | Internal { 20 | name: String, 21 | op_index_in_plugin: usize, 22 | plugin: HostPlugin, 23 | }, 24 | External { 25 | name: String, 26 | op: usize, 27 | sig: ExternalHostCallSignature, 28 | }, 29 | } 30 | 31 | #[derive(Clone, Debug, Serialize, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 32 | pub enum HostPlugin { 33 | HostInput = 0, 34 | Sha256, 35 | Require, 36 | } 37 | -------------------------------------------------------------------------------- /specs/src/jtable.rs: -------------------------------------------------------------------------------- 1 | use super::itable::InstructionTableEntry; 2 | use serde::Serialize; 3 | 4 | #[derive(Default, Serialize, Debug, Clone)] 5 | pub struct StaticFrameEntry { 6 | pub frame_id: u32, 7 | pub next_frame_id: u32, 8 | pub callee_fid: u32, 9 | pub fid: u32, 10 | pub iid: u32, 11 | } 12 | 13 | #[derive(Debug, Serialize, Clone)] 14 | pub struct JumpTableEntry { 15 | // caller eid (unique) 16 | pub eid: u32, 17 | pub last_jump_eid: u32, 18 | pub callee_fid: u32, 19 | pub inst: Box, 20 | } 21 | 22 | impl JumpTableEntry { 23 | pub fn to_string(&self) -> String { 24 | serde_json::to_string(self).unwrap() 25 | } 26 | } 27 | 28 | #[derive(Clone, Debug, Default, Serialize)] 29 | pub struct JumpTable(Vec); 30 | 31 | impl JumpTable { 32 | pub fn entries(&self) -> &Vec { 33 | &self.0 34 | } 35 | 36 | pub fn push(&mut self, entry: JumpTableEntry) { 37 | self.0.push(entry) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /specs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(trait_alias)] 2 | #![deny(unused_imports)] 3 | #![deny(dead_code)] 4 | 5 | use std::env; 6 | use std::io::Write; 7 | use std::path::PathBuf; 8 | 9 | use brtable::ElemTable; 10 | use configure_table::ConfigureTable; 11 | use etable::EventTable; 12 | use imtable::InitMemoryTable; 13 | use itable::InstructionTable; 14 | use jtable::JumpTable; 15 | use jtable::StaticFrameEntry; 16 | use mtable::MTable; 17 | use serde::Serialize; 18 | 19 | #[macro_use] 20 | extern crate lazy_static; 21 | 22 | pub mod brtable; 23 | pub mod configure_table; 24 | pub mod encode; 25 | pub mod etable; 26 | pub mod external_host_call_table; 27 | pub mod host_function; 28 | pub mod imtable; 29 | pub mod itable; 30 | pub mod jtable; 31 | pub mod mtable; 32 | pub mod step; 33 | pub mod types; 34 | 35 | #[derive(Default, Serialize, Debug, Clone)] 36 | pub struct CompilationTable { 37 | pub itable: InstructionTable, 38 | pub imtable: InitMemoryTable, 39 | pub elem_table: ElemTable, 40 | pub configure_table: ConfigureTable, 41 | pub static_jtable: Vec, 42 | pub fid_of_entry: u32, 43 | } 44 | 45 | #[derive(Default, Serialize, Clone)] 46 | pub struct ExecutionTable { 47 | pub etable: EventTable, 48 | pub mtable: MTable, 49 | pub jtable: JumpTable, 50 | } 51 | 52 | #[derive(Default, Clone)] 53 | pub struct Tables { 54 | pub compilation_tables: CompilationTable, 55 | pub execution_tables: ExecutionTable, 56 | } 57 | 58 | impl Tables { 59 | pub fn write_json(&self, dir: Option) { 60 | fn write_file(folder: &PathBuf, filename: &str, buf: &String) { 61 | let mut folder = folder.clone(); 62 | folder.push(filename); 63 | let mut fd = std::fs::File::create(folder.as_path()).unwrap(); 64 | folder.pop(); 65 | 66 | fd.write(buf.as_bytes()).unwrap(); 67 | } 68 | 69 | let itable = serde_json::to_string(&self.compilation_tables.itable).unwrap(); 70 | let imtable = serde_json::to_string(&self.compilation_tables.imtable).unwrap(); 71 | let etable = serde_json::to_string(&self.execution_tables.etable).unwrap(); 72 | let external_host_call_table = serde_json::to_string( 73 | &self 74 | .execution_tables 75 | .etable 76 | .filter_external_host_call_table(), 77 | ) 78 | .unwrap(); 79 | let mtable = serde_json::to_string(&self.execution_tables.mtable).unwrap(); 80 | let jtable = serde_json::to_string(&self.execution_tables.jtable).unwrap(); 81 | 82 | let dir = dir.unwrap_or(env::current_dir().unwrap()); 83 | write_file(&dir, "itable.json", &itable); 84 | write_file(&dir, "imtable.json", &imtable); 85 | write_file(&dir, "etable.json", &etable); 86 | write_file(&dir, "mtable.json", &mtable); 87 | write_file(&dir, "jtable.json", &jtable); 88 | write_file(&dir, "external_host_table.json", &external_host_call_table); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /specs/src/types.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::external_host_call_table::ExternalHostCallSignature; 4 | use crate::host_function::HostPlugin; 5 | use crate::mtable::VarType; 6 | 7 | #[derive(Clone, Copy, Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)] 8 | pub enum ValueType { 9 | I32, 10 | I64, 11 | } 12 | 13 | impl From for ValueType { 14 | fn from(v: parity_wasm::elements::ValueType) -> Self { 15 | match v { 16 | parity_wasm::elements::ValueType::I32 => ValueType::I32, 17 | parity_wasm::elements::ValueType::I64 => ValueType::I64, 18 | parity_wasm::elements::ValueType::F32 => todo!(), 19 | parity_wasm::elements::ValueType::F64 => todo!(), 20 | } 21 | } 22 | } 23 | 24 | #[derive(Debug, Clone)] 25 | pub enum Value { 26 | I32(i32), 27 | I64(i64), 28 | } 29 | 30 | impl Into for Value { 31 | fn into(self) -> VarType { 32 | match self { 33 | Value::I32(_) => VarType::I32, 34 | Value::I64(_) => VarType::I64, 35 | } 36 | } 37 | } 38 | 39 | impl Value { 40 | pub fn internal(&self) -> u64 { 41 | match self { 42 | Value::I32(v) => (*v) as u64, 43 | Value::I64(v) => (*v) as u64, 44 | } 45 | } 46 | } 47 | 48 | #[derive(Debug, Clone, Serialize)] 49 | pub enum FunctionType { 50 | WasmFunction, 51 | HostFunction { 52 | plugin: HostPlugin, 53 | function_index: usize, 54 | function_name: String, 55 | op_index_in_plugin: usize, 56 | }, 57 | HostFunctionExternal { 58 | function_name: String, 59 | op: usize, 60 | sig: ExternalHostCallSignature, 61 | }, 62 | } 63 | -------------------------------------------------------------------------------- /src/circuits/bit_table/configure.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::ConstraintSystem; 3 | use halo2_proofs::plonk::Expression; 4 | use halo2_proofs::plonk::VirtualCells; 5 | 6 | use crate::circuits::Lookup; 7 | use crate::curr; 8 | use crate::fixed_curr; 9 | 10 | use super::BitTableConfig; 11 | 12 | impl Lookup for BitTableConfig { 13 | fn configure_in_table( 14 | &self, 15 | meta: &mut ConstraintSystem, 16 | key: &'static str, 17 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 18 | ) { 19 | meta.lookup_any(key, |meta| { 20 | vec![( 21 | expr(meta), 22 | curr!(meta, self.values[0]) * fixed_curr!(meta, self.step_sel), 23 | )] 24 | }); 25 | } 26 | 27 | fn encode(&self, meta: &mut VirtualCells<'_, F>) -> Expression { 28 | curr!(meta, self.values[0]) * fixed_curr!(meta, self.step_sel) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/circuits/checksum/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::circuit::AssignedCell; 3 | use halo2_proofs::circuit::Layouter; 4 | use halo2_proofs::plonk::ConstraintSystem; 5 | use halo2_proofs::plonk::Error; 6 | 7 | use self::poseidon::primitives::p128pow5t9::RATE; 8 | use self::poseidon::primitives::p128pow5t9::WIDTH; 9 | use self::poseidon::primitives::ConstantLength; 10 | use self::poseidon::primitives::P128Pow5T9; 11 | use self::poseidon::Hash; 12 | use self::poseidon::Pow5Chip; 13 | use self::poseidon::Pow5Config; 14 | 15 | pub mod poseidon; 16 | 17 | // image data: 8192 18 | // frame table: 4 19 | // event table: 1 20 | 21 | pub const L: usize = 8192 + 4 + 1; 22 | 23 | #[derive(Clone)] 24 | pub(crate) struct CheckSumConfig { 25 | pow5_config: Pow5Config, 26 | } 27 | 28 | pub(crate) struct CheckSumChip { 29 | config: CheckSumConfig, 30 | } 31 | 32 | impl CheckSumConfig { 33 | pub(crate) fn configure(meta: &mut ConstraintSystem) -> Self { 34 | let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); 35 | let partial_sbox = meta.advice_column(); 36 | let mid_0_helper = meta.advice_column(); 37 | let mid_0_helper_sqr = meta.advice_column(); 38 | let cur_0_rc_a0 = meta.advice_column(); 39 | let cur_0_rc_a0_sqr = meta.advice_column(); 40 | 41 | let state_rc_a = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); 42 | let state_rc_a_sqr = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); 43 | 44 | let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); 45 | let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); 46 | 47 | Self { 48 | pow5_config: Pow5Chip::configure::>( 49 | meta, 50 | state.try_into().unwrap(), 51 | state_rc_a.try_into().unwrap(), 52 | state_rc_a_sqr.try_into().unwrap(), 53 | partial_sbox, 54 | mid_0_helper, 55 | mid_0_helper_sqr, 56 | cur_0_rc_a0, 57 | cur_0_rc_a0_sqr, 58 | rc_a.try_into().unwrap(), 59 | rc_b.try_into().unwrap(), 60 | ), 61 | } 62 | } 63 | } 64 | 65 | impl CheckSumChip { 66 | pub(crate) fn new(config: CheckSumConfig) -> Self { 67 | Self { config } 68 | } 69 | 70 | pub(crate) fn assign( 71 | &self, 72 | layouter: &mut impl Layouter, 73 | message: Vec>, 74 | ) -> Result, Error> { 75 | let config = self.config.pow5_config.clone(); 76 | let chip = Pow5Chip::construct(config.clone()); 77 | 78 | assert_eq!(message.len(), L); 79 | 80 | let hasher = Hash::<_, _, P128Pow5T9, ConstantLength, WIDTH, RATE>::init( 81 | chip, 82 | layouter.namespace(|| "init"), 83 | )?; 84 | let output = hasher.hash(layouter.namespace(|| "hash"), message.try_into().unwrap())?; 85 | 86 | Ok(output) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/circuits/checksum/poseidon/primitives/mds.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | 3 | use super::grain::Grain; 4 | use super::Mds; 5 | 6 | pub(super) fn generate_mds( 7 | grain: &mut Grain, 8 | mut select: usize, 9 | ) -> (Mds, Mds) { 10 | let (xs, ys, mds) = loop { 11 | // Generate two [F; T] arrays of unique field elements. 12 | let (xs, ys) = loop { 13 | let mut vals: Vec<_> = (0..2 * T) 14 | .map(|_| grain.next_field_element_without_rejection()) 15 | .collect(); 16 | 17 | // Check that we have unique field elements. 18 | let mut unique = vals.clone(); 19 | unique.sort_unstable(); 20 | unique.dedup(); 21 | if vals.len() == unique.len() { 22 | let rhs = vals.split_off(T); 23 | break (vals, rhs); 24 | } 25 | }; 26 | 27 | // We need to ensure that the MDS is secure. Instead of checking the MDS against 28 | // the relevant algorithms directly, we witness a fixed number of MDS matrices 29 | // that we need to sample from the given Grain state before obtaining a secure 30 | // matrix. This can be determined out-of-band via the reference implementation in 31 | // Sage. 32 | if select != 0 { 33 | select -= 1; 34 | continue; 35 | } 36 | 37 | // Generate a Cauchy matrix, with elements a_ij in the form: 38 | // a_ij = 1/(x_i + y_j); x_i + y_j != 0 39 | // 40 | // It would be much easier to use the alternate definition: 41 | // a_ij = 1/(x_i - y_j); x_i - y_j != 0 42 | // 43 | // These are clearly equivalent on `y <- -y`, but it is easier to work with the 44 | // negative formulation, because ensuring that xs ∪ ys is unique implies that 45 | // x_i - y_j != 0 by construction (whereas the positive case does not hold). It 46 | // also makes computation of the matrix inverse simpler below (the theorem used 47 | // was formulated for the negative definition). 48 | // 49 | // However, the Poseidon paper and reference impl use the positive formulation, 50 | // and we want to rely on the reference impl for MDS security, so we use the same 51 | // formulation. 52 | let mut mds = [[F::zero(); T]; T]; 53 | #[allow(clippy::needless_range_loop)] 54 | for i in 0..T { 55 | for j in 0..T { 56 | let sum = xs[i] + ys[j]; 57 | // We leverage the secure MDS selection counter to also check this. 58 | assert!(!sum.is_zero_vartime()); 59 | mds[i][j] = sum.invert().unwrap(); 60 | } 61 | } 62 | 63 | break (xs, ys, mds); 64 | }; 65 | 66 | // Compute the inverse. All square Cauchy matrices have a non-zero determinant and 67 | // thus are invertible. The inverse for a Cauchy matrix of the form: 68 | // 69 | // a_ij = 1/(x_i - y_j); x_i - y_j != 0 70 | // 71 | // has elements b_ij given by: 72 | // 73 | // b_ij = (x_j - y_i) A_j(y_i) B_i(x_j) (Schechter 1959, Theorem 1) 74 | // 75 | // where A_i(x) and B_i(x) are the Lagrange polynomials for xs and ys respectively. 76 | // 77 | // We adapt this to the positive Cauchy formulation by negating ys. 78 | let mut mds_inv = [[F::zero(); T]; T]; 79 | let l = |xs: &[F], j, x: F| { 80 | let x_j = xs[j]; 81 | xs.iter().enumerate().fold(F::one(), |acc, (m, x_m)| { 82 | if m == j { 83 | acc 84 | } else { 85 | // We hard-code the type, to avoid spurious "cannot infer type" rustc errors. 86 | let denominator: F = x_j - x_m; 87 | 88 | // We can invert freely; by construction, the elements of xs are distinct. 89 | let denominator_inverted: F = denominator.invert().unwrap(); 90 | 91 | acc * (x - x_m) * denominator_inverted 92 | } 93 | }) 94 | }; 95 | let neg_ys: Vec<_> = ys.iter().map(|y| -*y).collect(); 96 | for i in 0..T { 97 | for j in 0..T { 98 | mds_inv[i][j] = (xs[j] - neg_ys[i]) * l(&xs, j, neg_ys[i]) * l(&neg_ys, i, xs[j]); 99 | } 100 | } 101 | 102 | (mds, mds_inv) 103 | } 104 | -------------------------------------------------------------------------------- /src/circuits/checksum/poseidon/primitives/p128pow5t9.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use halo2_proofs::arithmetic::FieldExt; 4 | 5 | use super::generate_constants; 6 | use super::Mds; 7 | use super::Spec; 8 | 9 | pub(crate) const WIDTH: usize = 9; 10 | pub(crate) const RATE: usize = 8; 11 | 12 | /// Poseidon-128 using the $x^5$ S-box, with a width of 3 field elements, and the 13 | /// standard number of rounds for 128-bit security "with margin". 14 | /// 15 | /// The standard specification for this set of parameters (on either of the Pasta 16 | /// fields) uses $R_F = 8, R_P = 56$. This is conveniently an even number of 17 | /// partial rounds, making it easier to construct a Halo 2 circuit. 18 | #[derive(Debug)] 19 | pub struct P128Pow5T9 { 20 | _mark: PhantomData, 21 | } 22 | 23 | impl Spec for P128Pow5T9 { 24 | fn full_rounds() -> usize { 25 | 8 26 | } 27 | 28 | fn partial_rounds() -> usize { 29 | 64 30 | } 31 | 32 | fn sbox(val: F) -> F { 33 | val.pow_vartime([5]) 34 | } 35 | 36 | fn secure_mds() -> usize { 37 | 0 38 | } 39 | 40 | fn constants() -> (Vec<[F; 9]>, Mds, Mds) { 41 | generate_constants::() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/circuits/config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::env; 3 | use std::sync::Mutex; 4 | 5 | use specs::itable::OpcodeClassPlain; 6 | use specs::CompilationTable; 7 | 8 | pub const POW_TABLE_LIMIT: u64 = 128; 9 | 10 | pub const MIN_K: u32 = 18; 11 | 12 | lazy_static! { 13 | static ref ZKWASM_K: Mutex = 14 | Mutex::new(env::var("ZKWASM_K").map_or(MIN_K, |k| k.parse().unwrap())); 15 | } 16 | 17 | #[derive(Clone)] 18 | pub struct CircuitConfigure { 19 | pub initial_memory_pages: u32, 20 | pub maximal_memory_pages: u32, 21 | pub opcode_selector: HashSet, 22 | } 23 | 24 | #[thread_local] 25 | static mut CIRCUIT_CONFIGURE: Option = None; 26 | 27 | impl CircuitConfigure { 28 | #[allow(non_snake_case)] 29 | pub(crate) fn set_global_CIRCUIT_CONFIGURE(self) { 30 | unsafe { 31 | CIRCUIT_CONFIGURE = Some(self); 32 | } 33 | } 34 | 35 | pub(crate) fn get() -> CircuitConfigure { 36 | unsafe { 37 | if CIRCUIT_CONFIGURE.is_none() { 38 | panic!("CIRCUIT_CONFIGURE is not set, call init_zkwasm_runtime before configuring circuit."); 39 | } else { 40 | return CIRCUIT_CONFIGURE.clone().unwrap(); 41 | } 42 | } 43 | } 44 | } 45 | 46 | impl From<&CompilationTable> for CircuitConfigure { 47 | fn from(table: &CompilationTable) -> Self { 48 | CircuitConfigure { 49 | initial_memory_pages: table.configure_table.init_memory_pages, 50 | maximal_memory_pages: table.configure_table.maximal_memory_pages, 51 | opcode_selector: table.itable.opcode_class(), 52 | } 53 | } 54 | } 55 | 56 | pub fn set_zkwasm_k(k: u32) { 57 | assert!(k >= MIN_K); 58 | 59 | let mut zkwasm_k = (*ZKWASM_K).lock().unwrap(); 60 | *zkwasm_k = k; 61 | } 62 | 63 | pub fn zkwasm_k() -> u32 { 64 | *ZKWASM_K.lock().unwrap() 65 | } 66 | 67 | pub fn init_zkwasm_runtime(k: u32, table: &CompilationTable) { 68 | set_zkwasm_k(k); 69 | 70 | CircuitConfigure::from(table).set_global_CIRCUIT_CONFIGURE(); 71 | } 72 | 73 | #[cfg(feature = "checksum")] 74 | pub(crate) fn max_image_table_rows() -> u32 { 75 | 8192 76 | } 77 | -------------------------------------------------------------------------------- /src/circuits/etable/constraint_builder.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use halo2_proofs::arithmetic::FieldExt; 4 | use halo2_proofs::plonk::ConstraintSystem; 5 | use halo2_proofs::plonk::Expression; 6 | use halo2_proofs::plonk::VirtualCells; 7 | 8 | use crate::foreign::ForeignTableConfig; 9 | 10 | pub(crate) struct ConstraintBuilder<'a, 'b, F: FieldExt> { 11 | meta: &'a mut ConstraintSystem, 12 | foreign_table_configs: &'b BTreeMap<&'static str, Box>>, 13 | pub(crate) constraints: Vec<( 14 | &'static str, 15 | Box) -> Vec>>, 16 | )>, 17 | pub(crate) lookups: BTreeMap< 18 | &'static str, 19 | Vec<( 20 | &'static str, 21 | Box) -> Vec>>, 22 | )>, 23 | >, 24 | } 25 | 26 | impl<'a, 'b, F: FieldExt> ConstraintBuilder<'a, 'b, F> { 27 | pub(super) fn new( 28 | meta: &'a mut ConstraintSystem, 29 | foreign_table_configs: &'b BTreeMap<&'static str, Box>>, 30 | ) -> Self { 31 | Self { 32 | meta, 33 | foreign_table_configs, 34 | constraints: vec![], 35 | lookups: BTreeMap::new(), 36 | } 37 | } 38 | 39 | pub(crate) fn push( 40 | &mut self, 41 | name: &'static str, 42 | constraint: Box) -> Vec>>, 43 | ) { 44 | self.constraints.push((name, constraint)) 45 | } 46 | 47 | pub(crate) fn lookup( 48 | &mut self, 49 | foreign_table_id: &'static str, 50 | name: &'static str, 51 | builder: Box) -> Vec>>, 52 | ) { 53 | match self.lookups.get_mut(&foreign_table_id) { 54 | Some(lookups) => lookups.push((name, builder)), 55 | None => { 56 | self.lookups.insert(foreign_table_id, vec![(name, builder)]); 57 | } 58 | } 59 | } 60 | 61 | pub(super) fn finalize( 62 | self, 63 | selector: impl Fn(&mut VirtualCells) -> (Expression, Expression), 64 | ) { 65 | for (name, builder) in self.constraints { 66 | self.meta.create_gate(&name, |meta| { 67 | builder(meta) 68 | .into_iter() 69 | .map(|constraint| { 70 | let (step_sel, op_sel) = selector(meta); 71 | 72 | constraint * step_sel * op_sel 73 | }) 74 | .collect::>() 75 | }); 76 | } 77 | 78 | for (id, lookups) in self.lookups { 79 | let config = self.foreign_table_configs.get(&id).unwrap(); 80 | 81 | for (key, expr) in lookups { 82 | config.configure_in_table(self.meta, key, &|meta| { 83 | expr(meta) 84 | .into_iter() 85 | .map(|expr| { 86 | let (step_sel, _op_sel) = selector(meta); 87 | expr * step_sel 88 | }) 89 | .collect() 90 | }); 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/circuits/etable/op_configure/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod op_bin; 2 | pub mod op_bin_bit; 3 | pub mod op_bin_shift; 4 | pub mod op_br; 5 | pub mod op_br_if; 6 | pub mod op_br_if_eqz; 7 | pub mod op_br_table; 8 | pub mod op_call; 9 | pub mod op_call_host_foreign_circuit; 10 | pub mod op_call_indirect; 11 | pub mod op_const; 12 | pub mod op_conversion; 13 | pub mod op_drop; 14 | pub mod op_global_get; 15 | pub mod op_global_set; 16 | pub mod op_load; 17 | pub mod op_local_get; 18 | pub mod op_local_set; 19 | pub mod op_local_tee; 20 | pub mod op_memory_grow; 21 | pub mod op_memory_size; 22 | pub mod op_rel; 23 | pub mod op_return; 24 | pub mod op_select; 25 | pub mod op_store; 26 | pub mod op_test; 27 | pub mod op_unary; 28 | -------------------------------------------------------------------------------- /src/circuits/etable/op_configure/op_call.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::cell::*; 2 | use crate::circuits::etable::allocator::*; 3 | use crate::circuits::etable::ConstraintBuilder; 4 | use crate::circuits::etable::EventTableCommonConfig; 5 | use crate::circuits::etable::EventTableOpcodeConfig; 6 | use crate::circuits::etable::EventTableOpcodeConfigBuilder; 7 | use crate::circuits::jtable::expression::JtableLookupEntryEncode; 8 | use crate::circuits::jtable::JumpTableConfig; 9 | use crate::circuits::utils::bn_to_field; 10 | use crate::circuits::utils::step_status::StepStatus; 11 | use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; 12 | use crate::circuits::utils::Context; 13 | use crate::constant_from; 14 | use halo2_proofs::arithmetic::FieldExt; 15 | use halo2_proofs::plonk::Error; 16 | use halo2_proofs::plonk::Expression; 17 | use halo2_proofs::plonk::VirtualCells; 18 | use specs::encode::frame_table::encode_frame_table_entry; 19 | use specs::encode::opcode::encode_call; 20 | use specs::step::StepInfo; 21 | 22 | pub struct CallConfig { 23 | index_cell: AllocatedCommonRangeCell, 24 | frame_table_lookup: AllocatedJumpTableLookupCell, 25 | } 26 | 27 | pub struct CallConfigBuilder {} 28 | 29 | impl EventTableOpcodeConfigBuilder for CallConfigBuilder { 30 | fn configure( 31 | common_config: &EventTableCommonConfig, 32 | allocator: &mut EventTableCellAllocator, 33 | constraint_builder: &mut ConstraintBuilder, 34 | ) -> Box> { 35 | let index_cell = allocator.alloc_common_range_cell(); 36 | let frame_table_lookup = common_config.jtable_lookup_cell; 37 | 38 | let fid_cell = common_config.fid_cell; 39 | let iid_cell = common_config.iid_cell; 40 | let frame_id_cell = common_config.frame_id_cell; 41 | let eid = common_config.eid_cell; 42 | 43 | constraint_builder.constraints.push(( 44 | "return frame table lookups", 45 | Box::new(move |meta| { 46 | vec![ 47 | frame_table_lookup.0.expr(meta) 48 | - JumpTableConfig::encode_lookup( 49 | eid.expr(meta), 50 | frame_id_cell.expr(meta), 51 | index_cell.expr(meta), 52 | fid_cell.expr(meta), 53 | iid_cell.expr(meta) + constant_from!(1), 54 | ), 55 | ] 56 | }), 57 | )); 58 | 59 | Box::new(CallConfig { 60 | index_cell, 61 | frame_table_lookup, 62 | }) 63 | } 64 | } 65 | 66 | impl EventTableOpcodeConfig for CallConfig { 67 | fn opcode(&self, meta: &mut VirtualCells<'_, F>) -> Expression { 68 | encode_call(self.index_cell.expr(meta)) 69 | } 70 | 71 | fn assign( 72 | &self, 73 | ctx: &mut Context<'_, F>, 74 | step: &StepStatus, 75 | entry: &EventTableEntryWithMemoryInfo, 76 | ) -> Result<(), Error> { 77 | match &entry.eentry.step_info { 78 | StepInfo::Call { index } => { 79 | self.index_cell.assign(ctx, F::from(*index as u64))?; 80 | self.frame_table_lookup.0.assign( 81 | ctx, 82 | bn_to_field(&encode_frame_table_entry( 83 | step.current.eid.into(), 84 | step.current.last_jump_eid.into(), 85 | (*index).into(), 86 | step.current.fid.into(), 87 | (step.current.iid + 1).into(), 88 | )), 89 | )?; 90 | Ok(()) 91 | } 92 | 93 | _ => unreachable!(), 94 | } 95 | } 96 | 97 | fn jops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { 98 | Some(constant_from!(1)) 99 | } 100 | 101 | fn jops(&self) -> u32 { 102 | 1 103 | } 104 | 105 | fn next_frame_id( 106 | &self, 107 | meta: &mut VirtualCells<'_, F>, 108 | common_config: &EventTableCommonConfig, 109 | ) -> Option> { 110 | Some(common_config.eid_cell.curr_expr(meta)) 111 | } 112 | 113 | fn next_fid( 114 | &self, 115 | meta: &mut VirtualCells<'_, F>, 116 | _common_config: &EventTableCommonConfig, 117 | ) -> Option> { 118 | Some(self.index_cell.expr(meta)) 119 | } 120 | 121 | fn next_iid( 122 | &self, 123 | _meta: &mut VirtualCells<'_, F>, 124 | _common_config: &EventTableCommonConfig, 125 | ) -> Option> { 126 | Some(constant_from!(0)) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/circuits/etable/op_configure/op_const.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::cell::*; 2 | use crate::circuits::etable::allocator::*; 3 | use crate::circuits::etable::ConstraintBuilder; 4 | use crate::circuits::etable::EventTableCommonConfig; 5 | use crate::circuits::etable::EventTableOpcodeConfig; 6 | use crate::circuits::etable::EventTableOpcodeConfigBuilder; 7 | use crate::circuits::utils::bn_to_field; 8 | use crate::circuits::utils::step_status::StepStatus; 9 | use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; 10 | use crate::circuits::utils::Context; 11 | use crate::constant; 12 | use crate::constant_from; 13 | use halo2_proofs::arithmetic::FieldExt; 14 | use halo2_proofs::plonk::Error; 15 | use halo2_proofs::plonk::Expression; 16 | use halo2_proofs::plonk::VirtualCells; 17 | use num_bigint::BigUint; 18 | use specs::etable::EventTableEntry; 19 | use specs::itable::OpcodeClass; 20 | use specs::itable::OPCODE_ARG0_SHIFT; 21 | use specs::itable::OPCODE_CLASS_SHIFT; 22 | use specs::mtable::LocationType; 23 | use specs::step::StepInfo; 24 | 25 | pub struct ConstConfig { 26 | is_i32: AllocatedBitCell, 27 | value: AllocatedU64Cell, 28 | memory_table_lookup_stack_write: AllocatedMemoryTableLookupWriteCell, 29 | } 30 | 31 | pub struct ConstConfigBuilder {} 32 | 33 | impl EventTableOpcodeConfigBuilder for ConstConfigBuilder { 34 | fn configure( 35 | common_config: &EventTableCommonConfig, 36 | allocator: &mut EventTableCellAllocator, 37 | constraint_builder: &mut ConstraintBuilder, 38 | ) -> Box> { 39 | let is_i32 = allocator.alloc_bit_cell(); 40 | let value = allocator.alloc_u64_cell(); 41 | 42 | let sp_cell = common_config.sp_cell; 43 | let eid_cell = common_config.eid_cell; 44 | 45 | let memory_table_lookup_stack_write = allocator.alloc_memory_table_lookup_write_cell( 46 | "op_const stack write", 47 | constraint_builder, 48 | eid_cell, 49 | move |____| constant_from!(LocationType::Stack as u64), 50 | move |meta| sp_cell.expr(meta), 51 | move |meta| is_i32.expr(meta), 52 | move |meta| value.u64_cell.expr(meta), 53 | move |____| constant_from!(1), 54 | ); 55 | 56 | Box::new(ConstConfig { 57 | is_i32, 58 | value, 59 | memory_table_lookup_stack_write, 60 | }) 61 | } 62 | } 63 | 64 | impl EventTableOpcodeConfig for ConstConfig { 65 | fn opcode(&self, meta: &mut VirtualCells<'_, F>) -> Expression { 66 | constant!(bn_to_field( 67 | &(BigUint::from(OpcodeClass::Const as u64) << OPCODE_CLASS_SHIFT) 68 | )) + self.is_i32.expr(meta) 69 | * constant!(bn_to_field(&(BigUint::from(1u64) << OPCODE_ARG0_SHIFT))) 70 | + self.value.u64_cell.expr(meta) 71 | } 72 | 73 | fn assign( 74 | &self, 75 | ctx: &mut Context<'_, F>, 76 | step: &StepStatus, 77 | entry: &EventTableEntryWithMemoryInfo, 78 | ) -> Result<(), Error> { 79 | match &entry.eentry.step_info { 80 | StepInfo::I32Const { value } => { 81 | self.value.assign(ctx, *value as u32 as u64)?; 82 | self.is_i32.assign(ctx, F::one())?; 83 | self.memory_table_lookup_stack_write.assign( 84 | ctx, 85 | step.current.eid, 86 | entry.memory_rw_entires[0].end_eid, 87 | step.current.sp, 88 | LocationType::Stack, 89 | true, 90 | *value as u32 as u64, 91 | )?; 92 | 93 | Ok(()) 94 | } 95 | StepInfo::I64Const { value } => { 96 | self.value.assign(ctx, *value as u64)?; 97 | self.is_i32.assign(ctx, F::zero())?; 98 | self.memory_table_lookup_stack_write.assign( 99 | ctx, 100 | step.current.eid, 101 | entry.memory_rw_entires[0].end_eid, 102 | step.current.sp, 103 | LocationType::Stack, 104 | false, 105 | *value as u64, 106 | )?; 107 | 108 | Ok(()) 109 | } 110 | _ => unreachable!(), 111 | } 112 | } 113 | 114 | fn sp_diff(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { 115 | Some(constant!(-F::one())) 116 | } 117 | 118 | fn mops(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { 119 | Some(constant_from!(1)) 120 | } 121 | 122 | fn memory_writing_ops(&self, _: &EventTableEntry) -> u32 { 123 | 1 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/circuits/etable/op_configure/op_drop.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::etable::allocator::*; 2 | use crate::circuits::etable::ConstraintBuilder; 3 | use crate::circuits::etable::EventTableCommonConfig; 4 | use crate::circuits::etable::EventTableOpcodeConfig; 5 | use crate::circuits::etable::EventTableOpcodeConfigBuilder; 6 | use crate::circuits::utils::bn_to_field; 7 | use crate::circuits::utils::step_status::StepStatus; 8 | use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; 9 | use crate::circuits::utils::Context; 10 | use crate::constant_from; 11 | use crate::constant_from_bn; 12 | use halo2_proofs::arithmetic::FieldExt; 13 | use halo2_proofs::plonk::Error; 14 | use halo2_proofs::plonk::Expression; 15 | use halo2_proofs::plonk::VirtualCells; 16 | use num_bigint::BigUint; 17 | use specs::itable::OpcodeClass; 18 | use specs::itable::OPCODE_CLASS_SHIFT; 19 | use specs::step::StepInfo; 20 | 21 | pub struct DropConfig; 22 | 23 | pub struct DropConfigBuilder; 24 | 25 | impl EventTableOpcodeConfigBuilder for DropConfigBuilder { 26 | fn configure( 27 | _: &EventTableCommonConfig, 28 | _: &mut EventTableCellAllocator, 29 | _: &mut ConstraintBuilder, 30 | ) -> Box> { 31 | Box::new(DropConfig) 32 | } 33 | } 34 | 35 | impl EventTableOpcodeConfig for DropConfig { 36 | fn opcode(&self, _: &mut VirtualCells<'_, F>) -> Expression { 37 | constant_from_bn!(&(BigUint::from(OpcodeClass::Drop as u64) << OPCODE_CLASS_SHIFT)) 38 | } 39 | 40 | fn assign( 41 | &self, 42 | _: &mut Context<'_, F>, 43 | _: &StepStatus, 44 | entry: &EventTableEntryWithMemoryInfo, 45 | ) -> Result<(), Error> { 46 | match &entry.eentry.step_info { 47 | StepInfo::Drop => Ok(()), 48 | _ => unreachable!(), 49 | } 50 | } 51 | 52 | fn sp_diff(&self, _: &mut VirtualCells<'_, F>) -> Option> { 53 | Some(constant_from!(1)) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/circuits/etable/op_configure/op_memory_size.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::cell::*; 2 | use crate::circuits::etable::allocator::*; 3 | use crate::circuits::etable::ConstraintBuilder; 4 | use crate::circuits::etable::EventTableCommonConfig; 5 | use crate::circuits::etable::EventTableOpcodeConfig; 6 | use crate::circuits::etable::EventTableOpcodeConfigBuilder; 7 | use crate::circuits::utils::bn_to_field; 8 | use crate::circuits::utils::step_status::StepStatus; 9 | use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; 10 | use crate::circuits::utils::Context; 11 | use crate::constant; 12 | use crate::constant_from; 13 | use halo2_proofs::arithmetic::FieldExt; 14 | use halo2_proofs::plonk::Error; 15 | use halo2_proofs::plonk::Expression; 16 | use halo2_proofs::plonk::VirtualCells; 17 | use num_bigint::BigUint; 18 | use specs::etable::EventTableEntry; 19 | use specs::itable::OpcodeClass; 20 | use specs::itable::OPCODE_CLASS_SHIFT; 21 | use specs::mtable::LocationType; 22 | use specs::step::StepInfo; 23 | 24 | pub struct MemorySizeConfig { 25 | memory_table_lookup_stack_write: AllocatedMemoryTableLookupWriteCell, 26 | } 27 | 28 | pub struct MemorySizeConfigBuilder {} 29 | 30 | impl EventTableOpcodeConfigBuilder for MemorySizeConfigBuilder { 31 | fn configure( 32 | common_config: &EventTableCommonConfig, 33 | allocator: &mut EventTableCellAllocator, 34 | constraint_builder: &mut ConstraintBuilder, 35 | ) -> Box> { 36 | let allocated_memory_pages = common_config.mpages_cell; 37 | let sp = common_config.sp_cell; 38 | let eid = common_config.eid_cell; 39 | 40 | let memory_table_lookup_stack_write = allocator.alloc_memory_table_lookup_write_cell( 41 | "op_test stack write", 42 | constraint_builder, 43 | eid, 44 | move |____| constant_from!(LocationType::Stack as u64), 45 | move |meta| sp.expr(meta), 46 | move |____| constant_from!(1), 47 | move |meta| allocated_memory_pages.expr(meta), 48 | move |____| constant_from!(1), 49 | ); 50 | 51 | Box::new(MemorySizeConfig { 52 | memory_table_lookup_stack_write, 53 | }) 54 | } 55 | } 56 | 57 | impl EventTableOpcodeConfig for MemorySizeConfig { 58 | fn opcode(&self, _meta: &mut VirtualCells<'_, F>) -> Expression { 59 | constant!(bn_to_field( 60 | &(BigUint::from(OpcodeClass::MemorySize as u64) << OPCODE_CLASS_SHIFT) 61 | )) 62 | } 63 | 64 | fn assign( 65 | &self, 66 | ctx: &mut Context<'_, F>, 67 | step: &StepStatus, 68 | entry: &EventTableEntryWithMemoryInfo, 69 | ) -> Result<(), Error> { 70 | match &entry.eentry.step_info { 71 | StepInfo::MemorySize => { 72 | self.memory_table_lookup_stack_write.assign( 73 | ctx, 74 | step.current.eid, 75 | entry.memory_rw_entires[0].end_eid, 76 | step.current.sp, 77 | LocationType::Stack, 78 | true, 79 | step.current.allocated_memory_pages as u32 as u64, 80 | )?; 81 | 82 | Ok(()) 83 | } 84 | 85 | _ => unreachable!(), 86 | } 87 | } 88 | 89 | fn sp_diff(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { 90 | Some(constant!(-F::one())) 91 | } 92 | 93 | fn mops(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { 94 | Some(constant_from!(1)) 95 | } 96 | 97 | fn memory_writing_ops(&self, _: &EventTableEntry) -> u32 { 98 | 1 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/circuits/external_host_call_table/assign.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::circuit::Layouter; 3 | use halo2_proofs::plonk::Error; 4 | use specs::external_host_call_table::ExternalHostCallTable; 5 | 6 | use super::ExternalHostCallChip; 7 | 8 | impl ExternalHostCallChip { 9 | pub(in crate::circuits) fn assign( 10 | self, 11 | layouter: &mut impl Layouter, 12 | table: &ExternalHostCallTable, 13 | ) -> Result<(), Error> { 14 | layouter.assign_region( 15 | || "foreign table", 16 | |mut region| { 17 | // Assign Fixed Column 18 | { 19 | for offset in 0..self.maximal_available_rows { 20 | region.assign_fixed( 21 | || "external host call idx", 22 | self.config.idx, 23 | offset, 24 | || Ok(F::from(offset as u64)), 25 | )?; 26 | } 27 | } 28 | 29 | // Assign Advice Columns 30 | { 31 | let mut offset = 0; 32 | 33 | { 34 | region.assign_advice( 35 | || "external host call op", 36 | self.config.op, 37 | offset, 38 | || Ok(F::zero()), 39 | )?; 40 | 41 | region.assign_advice( 42 | || "external host call value", 43 | self.config.arg, 44 | offset, 45 | || Ok(F::zero()), 46 | )?; 47 | 48 | region.assign_advice( 49 | || "external host call is ret", 50 | self.config.is_ret, 51 | offset, 52 | || Ok(F::zero()), 53 | )?; 54 | } 55 | 56 | offset += 1; 57 | 58 | for entry in table.entries() { 59 | region.assign_advice( 60 | || "external host call op", 61 | self.config.op, 62 | offset, 63 | || Ok(F::from(entry.op as u64)), 64 | )?; 65 | 66 | region.assign_advice( 67 | || "external host call is ret", 68 | self.config.is_ret, 69 | offset, 70 | || Ok(F::from(entry.sig.is_ret() as u64)), 71 | )?; 72 | 73 | region.assign_advice( 74 | || "external host call value", 75 | self.config.arg, 76 | offset, 77 | || Ok(F::from(entry.value)), 78 | )?; 79 | 80 | offset += 1; 81 | } 82 | } 83 | 84 | Ok(()) 85 | }, 86 | )?; 87 | 88 | Ok(()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/circuits/external_host_call_table/configure.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::ConstraintSystem; 3 | use halo2_proofs::plonk::Expression; 4 | use halo2_proofs::plonk::VirtualCells; 5 | use specs::external_host_call_table::encode::encode_host_call_entry; 6 | use std::marker::PhantomData; 7 | 8 | use crate::circuits::traits::ConfigureLookupTable; 9 | use crate::curr; 10 | use crate::fixed_curr; 11 | 12 | use super::ExternalHostCallTableConfig; 13 | 14 | impl ExternalHostCallTableConfig { 15 | pub(in crate::circuits) fn configure(meta: &mut ConstraintSystem) -> Self { 16 | Self { 17 | idx: meta.fixed_column(), 18 | op: meta.advice_column(), 19 | is_ret: meta.advice_column(), 20 | arg: meta.advice_column(), 21 | _phantom: PhantomData, 22 | } 23 | } 24 | } 25 | 26 | impl ConfigureLookupTable for ExternalHostCallTableConfig { 27 | fn configure_in_table( 28 | &self, 29 | meta: &mut ConstraintSystem, 30 | key: &'static str, 31 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 32 | ) { 33 | meta.lookup_any(key, |meta| { 34 | vec![( 35 | expr(meta), 36 | encode_host_call_entry( 37 | fixed_curr!(meta, self.idx), 38 | curr!(meta, self.op), 39 | curr!(meta, self.is_ret), 40 | curr!(meta, self.arg), 41 | ), 42 | )] 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/circuits/external_host_call_table/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::Advice; 3 | use halo2_proofs::plonk::Column; 4 | use halo2_proofs::plonk::Fixed; 5 | use std::marker::PhantomData; 6 | 7 | mod assign; 8 | mod configure; 9 | 10 | #[derive(Clone)] 11 | pub struct ExternalHostCallTableConfig { 12 | idx: Column, 13 | op: Column, 14 | arg: Column, 15 | is_ret: Column, 16 | _phantom: PhantomData, 17 | } 18 | 19 | pub struct ExternalHostCallChip { 20 | config: ExternalHostCallTableConfig, 21 | maximal_available_rows: usize, 22 | } 23 | 24 | impl ExternalHostCallChip { 25 | pub fn new(config: ExternalHostCallTableConfig, maximal_available_rows: usize) -> Self { 26 | Self { 27 | config, 28 | maximal_available_rows, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/circuits/image_table_checksum/configure.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use halo2_proofs::arithmetic::FieldExt; 4 | use halo2_proofs::plonk::ConstraintSystem; 5 | use halo2_proofs::plonk::Expression; 6 | use halo2_proofs::plonk::VirtualCells; 7 | use specs::encode::image_table::ImageTableEncoder; 8 | 9 | use crate::curr; 10 | 11 | use super::ImageTableConfig; 12 | 13 | impl ImageTableConfig { 14 | pub(in crate::circuits) fn configure(meta: &mut ConstraintSystem) -> Self { 15 | let col = meta.advice_column(); 16 | meta.enable_equality(col); 17 | Self { 18 | col, 19 | _mark: PhantomData, 20 | } 21 | } 22 | 23 | pub fn instruction_lookup( 24 | &self, 25 | meta: &mut ConstraintSystem, 26 | key: &'static str, 27 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 28 | ) { 29 | meta.lookup_any(key, |meta| { 30 | vec![( 31 | ImageTableEncoder::Instruction.encode(expr(meta)), 32 | curr!(meta, self.col), 33 | )] 34 | }); 35 | } 36 | 37 | pub fn init_memory_lookup( 38 | &self, 39 | meta: &mut ConstraintSystem, 40 | key: &'static str, 41 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 42 | ) { 43 | meta.lookup_any(key, |meta| { 44 | vec![( 45 | ImageTableEncoder::InitMemory.encode(expr(meta)), 46 | curr!(meta, self.col), 47 | )] 48 | }); 49 | } 50 | 51 | pub fn br_table_lookup( 52 | &self, 53 | meta: &mut ConstraintSystem, 54 | key: &'static str, 55 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 56 | ) { 57 | meta.lookup_any(key, |meta| { 58 | vec![( 59 | ImageTableEncoder::BrTable.encode(expr(meta)), 60 | curr!(meta, self.col), 61 | )] 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/circuits/image_table_checksum/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::Advice; 3 | use halo2_proofs::plonk::Column; 4 | use std::marker::PhantomData; 5 | 6 | mod assign; 7 | mod configure; 8 | 9 | #[derive(Clone)] 10 | pub struct ImageTableConfig { 11 | col: Column, 12 | _mark: PhantomData, 13 | } 14 | 15 | #[derive(Clone)] 16 | pub struct ImageTableChip { 17 | config: ImageTableConfig, 18 | } 19 | 20 | impl ImageTableChip { 21 | pub fn new(config: ImageTableConfig) -> Self { 22 | ImageTableChip { config } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/circuits/image_table_fixed/configure.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use halo2_proofs::arithmetic::FieldExt; 4 | use halo2_proofs::plonk::ConstraintSystem; 5 | use halo2_proofs::plonk::Expression; 6 | use halo2_proofs::plonk::VirtualCells; 7 | use specs::encode::image_table::ImageTableEncoder; 8 | 9 | use super::ImageTableConfig; 10 | 11 | impl ImageTableConfig { 12 | pub(in crate::circuits) fn configure(meta: &mut ConstraintSystem) -> Self { 13 | Self { 14 | col: meta.lookup_table_column(), 15 | _mark: PhantomData, 16 | } 17 | } 18 | 19 | pub fn instruction_lookup( 20 | &self, 21 | meta: &mut ConstraintSystem, 22 | key: &'static str, 23 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 24 | ) { 25 | meta.lookup(key, |meta| { 26 | vec![(ImageTableEncoder::Instruction.encode(expr(meta)), self.col)] 27 | }); 28 | } 29 | 30 | pub fn init_memory_lookup( 31 | &self, 32 | meta: &mut ConstraintSystem, 33 | key: &'static str, 34 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 35 | ) { 36 | meta.lookup(key, |meta| { 37 | vec![(ImageTableEncoder::InitMemory.encode(expr(meta)), self.col)] 38 | }); 39 | } 40 | 41 | pub fn br_table_lookup( 42 | &self, 43 | meta: &mut ConstraintSystem, 44 | key: &'static str, 45 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 46 | ) { 47 | meta.lookup(key, |meta| { 48 | vec![(ImageTableEncoder::BrTable.encode(expr(meta)), self.col)] 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/circuits/image_table_fixed/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::TableColumn; 3 | use std::marker::PhantomData; 4 | 5 | mod assign; 6 | mod configure; 7 | 8 | #[derive(Clone)] 9 | pub struct ImageTableConfig { 10 | col: TableColumn, 11 | _mark: PhantomData, 12 | } 13 | 14 | pub struct ImageTableChip { 15 | config: ImageTableConfig, 16 | } 17 | 18 | impl ImageTableChip { 19 | pub fn new(config: ImageTableConfig) -> Self { 20 | ImageTableChip { config } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/circuits/jtable/configure.rs: -------------------------------------------------------------------------------- 1 | use super::JumpTableConfig; 2 | use crate::circuits::Lookup; 3 | use crate::constant_from; 4 | use crate::fixed_curr; 5 | use halo2_proofs::arithmetic::FieldExt; 6 | use halo2_proofs::plonk::Advice; 7 | use halo2_proofs::plonk::Column; 8 | use halo2_proofs::plonk::ConstraintSystem; 9 | use halo2_proofs::plonk::Expression; 10 | use halo2_proofs::plonk::VirtualCells; 11 | 12 | pub trait JTableConstraint { 13 | fn configure(&self, meta: &mut ConstraintSystem) { 14 | self.enable_is_bit(meta); 15 | self.enable_rest_jops_permutation(meta); 16 | self.configure_rest_jops_decrease(meta); 17 | self.disabled_block_should_be_end(meta); 18 | self.disabled_block_has_no_entry_value(meta); 19 | } 20 | 21 | fn enable_rest_jops_permutation(&self, meta: &mut ConstraintSystem); 22 | fn enable_is_bit(&self, meta: &mut ConstraintSystem); 23 | fn configure_rest_jops_decrease(&self, meta: &mut ConstraintSystem); 24 | fn disabled_block_should_be_end(&self, meta: &mut ConstraintSystem); 25 | fn disabled_block_has_no_entry_value(&self, meta: &mut ConstraintSystem); 26 | } 27 | 28 | impl JTableConstraint for JumpTableConfig { 29 | fn enable_rest_jops_permutation(&self, meta: &mut ConstraintSystem) { 30 | meta.enable_equality(self.data); 31 | } 32 | 33 | fn enable_is_bit(&self, meta: &mut ConstraintSystem) { 34 | meta.create_gate("enable is bit", |meta| { 35 | vec![ 36 | self.enable(meta) 37 | * (self.enable(meta) - constant_from!(1)) 38 | * fixed_curr!(meta, self.sel), 39 | ] 40 | }); 41 | } 42 | 43 | fn configure_rest_jops_decrease(&self, meta: &mut ConstraintSystem) { 44 | meta.create_gate("c3. jtable rest decrease", |meta| { 45 | vec![ 46 | (self.rest(meta) - self.next_rest(meta) - constant_from!(2) 47 | + self.static_bit(meta)) 48 | * self.enable(meta) 49 | * fixed_curr!(meta, self.sel), 50 | (self.rest(meta) - self.next_rest(meta)) 51 | * (self.enable(meta) - constant_from!(1)) 52 | * fixed_curr!(meta, self.sel), 53 | ] 54 | }); 55 | } 56 | 57 | fn disabled_block_should_be_end(&self, meta: &mut ConstraintSystem) { 58 | meta.create_gate("c5. jtable ends up", |meta| { 59 | vec![ 60 | (constant_from!(1) - self.enable(meta)) 61 | * (constant_from!(1) - self.static_bit(meta)) 62 | * self.rest(meta) 63 | * fixed_curr!(meta, self.sel), 64 | ] 65 | }); 66 | } 67 | 68 | fn disabled_block_has_no_entry_value(&self, meta: &mut ConstraintSystem) { 69 | meta.create_gate("c6. jtable entry is zero on disabled", |meta| { 70 | vec![ 71 | (constant_from!(1) - self.enable(meta)) 72 | * self.entry(meta) 73 | * fixed_curr!(meta, self.sel), 74 | ] 75 | }); 76 | } 77 | } 78 | 79 | impl Lookup for JumpTableConfig { 80 | /// Frame Table Constraint 4. Etable step's call/return record can be found on jtable_entry 81 | fn configure_in_table( 82 | &self, 83 | meta: &mut ConstraintSystem, 84 | key: &'static str, 85 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 86 | ) { 87 | meta.lookup_any(key, |meta| { 88 | vec![(expr(meta), self.entry(meta) * fixed_curr!(meta, self.sel))] 89 | }); 90 | } 91 | 92 | fn encode(&self, _meta: &mut VirtualCells<'_, F>) -> Expression { 93 | unimplemented!() 94 | } 95 | } 96 | 97 | impl JumpTableConfig { 98 | pub(super) fn new( 99 | meta: &mut ConstraintSystem, 100 | cols: &mut impl Iterator>, 101 | ) -> Self { 102 | let sel = meta.fixed_column(); 103 | let static_bit = meta.fixed_column(); 104 | let data = cols.next().unwrap(); 105 | 106 | JumpTableConfig { 107 | sel, 108 | static_bit, 109 | data, 110 | _m: std::marker::PhantomData, 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/circuits/jtable/expression.rs: -------------------------------------------------------------------------------- 1 | use super::JtableOffset; 2 | use super::JumpTableConfig; 3 | use crate::fixed_curr; 4 | use crate::nextn; 5 | use halo2_proofs::arithmetic::FieldExt; 6 | use halo2_proofs::plonk::Expression; 7 | use halo2_proofs::plonk::VirtualCells; 8 | use specs::encode::frame_table::encode_frame_table_entry; 9 | 10 | impl JumpTableConfig { 11 | pub(super) fn enable(&self, meta: &mut VirtualCells) -> Expression { 12 | nextn!(meta, self.data, JtableOffset::JtableOffsetEnable as i32) 13 | } 14 | 15 | pub(super) fn rest(&self, meta: &mut VirtualCells) -> Expression { 16 | nextn!(meta, self.data, JtableOffset::JtableOffsetRest as i32) 17 | } 18 | 19 | pub(super) fn next_rest(&self, meta: &mut VirtualCells) -> Expression { 20 | nextn!( 21 | meta, 22 | self.data, 23 | JtableOffset::JtableOffsetRest as i32 + JtableOffset::JtableOffsetMax as i32 24 | ) 25 | } 26 | 27 | pub(super) fn entry(&self, meta: &mut VirtualCells) -> Expression { 28 | nextn!(meta, self.data, JtableOffset::JtableOffsetEntry as i32) 29 | } 30 | 31 | pub(super) fn static_bit(&self, meta: &mut VirtualCells) -> Expression { 32 | fixed_curr!(meta, self.static_bit) 33 | } 34 | } 35 | 36 | pub(crate) trait JtableLookupEntryEncode { 37 | fn encode_lookup( 38 | current_last_jump_eid: Expression, 39 | next_last_jump_eid: Expression, 40 | callee_fid: Expression, 41 | next_fid: Expression, 42 | next_iid: Expression, 43 | ) -> Expression; 44 | } 45 | 46 | impl JtableLookupEntryEncode for JumpTableConfig { 47 | fn encode_lookup( 48 | current_last_jump_eid: Expression, 49 | next_last_jump_eid: Expression, 50 | callee_fid: Expression, 51 | next_fid: Expression, 52 | next_iid: Expression, 53 | ) -> Expression { 54 | encode_frame_table_entry( 55 | current_last_jump_eid, 56 | next_last_jump_eid, 57 | callee_fid, 58 | next_fid, 59 | next_iid, 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/circuits/jtable/mod.rs: -------------------------------------------------------------------------------- 1 | use self::configure::JTableConstraint; 2 | use halo2_proofs::arithmetic::FieldExt; 3 | use halo2_proofs::plonk::Advice; 4 | use halo2_proofs::plonk::Column; 5 | use halo2_proofs::plonk::ConstraintSystem; 6 | use halo2_proofs::plonk::Fixed; 7 | use std::marker::PhantomData; 8 | 9 | mod assign; 10 | mod configure; 11 | pub(crate) mod expression; 12 | 13 | pub enum JtableOffset { 14 | JtableOffsetEnable = 0, 15 | JtableOffsetRest = 1, 16 | JtableOffsetEntry = 2, 17 | JtableOffsetMax = 3, 18 | } 19 | 20 | #[derive(Clone)] 21 | pub struct JumpTableConfig { 22 | sel: Column, 23 | static_bit: Column, 24 | data: Column, 25 | _m: PhantomData, 26 | } 27 | 28 | impl JumpTableConfig { 29 | pub fn configure( 30 | meta: &mut ConstraintSystem, 31 | cols: &mut impl Iterator>, 32 | ) -> Self { 33 | let jtable = Self::new(meta, cols); 34 | jtable.configure(meta); 35 | jtable 36 | } 37 | } 38 | 39 | pub struct JumpTableChip { 40 | config: JumpTableConfig, 41 | max_available_rows: usize, 42 | } 43 | 44 | impl JumpTableChip { 45 | pub fn new(config: JumpTableConfig, max_available_rows: usize) -> Self { 46 | JumpTableChip { 47 | config, 48 | max_available_rows, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/circuits/traits.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::ConstraintSystem; 3 | use halo2_proofs::plonk::Expression; 4 | use halo2_proofs::plonk::VirtualCells; 5 | 6 | pub(super) trait ConfigureLookupTable { 7 | fn configure_in_table( 8 | &self, 9 | meta: &mut ConstraintSystem, 10 | key: &'static str, 11 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/circuits/utils/bit.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::constant_from; 3 | use crate::curr; 4 | use halo2_proofs::arithmetic::FieldExt; 5 | use halo2_proofs::plonk::Advice; 6 | use halo2_proofs::plonk::Column; 7 | use halo2_proofs::plonk::ConstraintSystem; 8 | use halo2_proofs::plonk::Error; 9 | use halo2_proofs::plonk::Expression; 10 | use halo2_proofs::plonk::VirtualCells; 11 | use std::marker::PhantomData; 12 | 13 | #[derive(Clone)] 14 | pub struct BitColumn { 15 | pub col: Column, 16 | _mark: PhantomData, 17 | } 18 | 19 | impl BitColumn { 20 | pub fn configure( 21 | meta: &mut ConstraintSystem, 22 | cols: &mut impl Iterator>, 23 | enable: impl Fn(&mut VirtualCells<'_, F>) -> Expression, 24 | ) -> Self { 25 | let col = cols.next().unwrap(); 26 | 27 | meta.create_gate("bit column", |meta| { 28 | vec![ 29 | curr!(meta, col.clone()) 30 | * (constant_from!(1) - curr!(meta, col.clone())) 31 | * enable(meta), 32 | ] 33 | }); 34 | 35 | Self { 36 | col, 37 | _mark: PhantomData, 38 | } 39 | } 40 | 41 | pub fn assign(&self, ctx: &mut Context, value: bool) -> Result<(), Error> { 42 | ctx.region.assign_advice( 43 | || "bit value", 44 | self.col, 45 | ctx.offset, 46 | || Ok(if value { F::one() } else { F::zero() }), 47 | )?; 48 | 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/circuits/utils/common_range.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::circuits::rtable::RangeTableConfig; 3 | use crate::curr; 4 | use halo2_proofs::arithmetic::FieldExt; 5 | use halo2_proofs::plonk::Advice; 6 | use halo2_proofs::plonk::Column; 7 | use halo2_proofs::plonk::ConstraintSystem; 8 | use halo2_proofs::plonk::Error; 9 | use halo2_proofs::plonk::Expression; 10 | use halo2_proofs::plonk::VirtualCells; 11 | use std::marker::PhantomData; 12 | 13 | #[derive(Clone)] 14 | pub struct CommonRangeColumn { 15 | pub col: Column, 16 | _mark: PhantomData, 17 | } 18 | 19 | impl CommonRangeColumn { 20 | pub fn configure( 21 | meta: &mut ConstraintSystem, 22 | cols: &mut impl Iterator>, 23 | rtable: &RangeTableConfig, 24 | enable: impl Fn(&mut VirtualCells<'_, F>) -> Expression, 25 | ) -> Self { 26 | let col = cols.next().unwrap(); 27 | 28 | rtable.configure_in_common_range(meta, "common range", |meta| { 29 | curr!(meta, col) * enable(meta) 30 | }); 31 | 32 | Self { 33 | col, 34 | _mark: PhantomData, 35 | } 36 | } 37 | 38 | pub fn assign(&self, ctx: &mut Context, value: u64) -> Result<(), Error> { 39 | ctx.region.assign_advice( 40 | || "common range value", 41 | self.col, 42 | ctx.offset, 43 | || Ok(value.into()), 44 | )?; 45 | 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/circuits/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::BaseExt; 2 | use halo2_proofs::arithmetic::FieldExt; 3 | use halo2_proofs::circuit::Region; 4 | use num_bigint::BigUint; 5 | 6 | pub mod bit; 7 | pub mod common_range; 8 | pub mod row_diff; 9 | pub mod step_status; 10 | pub mod u16; 11 | pub mod u8; 12 | 13 | pub mod table_entry; 14 | 15 | pub struct Context<'a, F: FieldExt> { 16 | pub region: Box>, 17 | pub offset: usize, 18 | records: Vec, 19 | } 20 | 21 | impl<'a, F: FieldExt> Context<'a, F> { 22 | pub fn new(region: Region<'a, F>) -> Self { 23 | Self { 24 | region: Box::new(region), 25 | offset: 0usize, 26 | records: vec![], 27 | } 28 | } 29 | 30 | pub fn next(&mut self) { 31 | self.offset += 1; 32 | } 33 | 34 | pub fn step(&mut self, step: usize) { 35 | self.offset += step; 36 | } 37 | 38 | pub fn reset(&mut self) { 39 | self.offset = 0; 40 | self.records.clear(); 41 | } 42 | 43 | pub fn push(&mut self) { 44 | self.records.push(self.offset) 45 | } 46 | 47 | pub fn pop(&mut self) { 48 | self.offset = self.records.pop().unwrap(); 49 | } 50 | } 51 | 52 | pub fn field_to_bn(f: &F) -> BigUint { 53 | let mut bytes: Vec = Vec::new(); 54 | f.write(&mut bytes).unwrap(); 55 | BigUint::from_bytes_le(&bytes[..]) 56 | } 57 | 58 | pub fn bn_to_field(bn: &BigUint) -> F { 59 | let mut bytes = bn.to_bytes_le(); 60 | bytes.resize(32, 0); 61 | let mut bytes = &bytes[..]; 62 | F::read(&mut bytes).unwrap() 63 | } 64 | 65 | #[macro_export] 66 | macro_rules! curr { 67 | ($meta: expr, $x: expr) => { 68 | $meta.query_advice($x, halo2_proofs::poly::Rotation::cur()) 69 | }; 70 | } 71 | 72 | #[macro_export] 73 | macro_rules! prev { 74 | ($meta: expr, $x: expr) => { 75 | $meta.query_advice($x, halo2_proofs::poly::Rotation::prev()) 76 | }; 77 | } 78 | 79 | #[macro_export] 80 | macro_rules! next { 81 | ($meta: expr, $x: expr) => { 82 | $meta.query_advice($x, halo2_proofs::poly::Rotation::next()) 83 | }; 84 | } 85 | 86 | #[macro_export] 87 | macro_rules! nextn { 88 | ($meta: expr, $x: expr, $n:expr) => { 89 | $meta.query_advice($x, halo2_proofs::poly::Rotation($n)) 90 | }; 91 | } 92 | 93 | #[macro_export] 94 | macro_rules! fixed_curr { 95 | ($meta: expr, $x: expr) => { 96 | $meta.query_fixed($x, halo2_proofs::poly::Rotation::cur()) 97 | }; 98 | } 99 | 100 | #[macro_export] 101 | macro_rules! instance_curr { 102 | ($meta: expr, $x: expr) => { 103 | $meta.query_instance($x, halo2_proofs::poly::Rotation::cur()) 104 | }; 105 | } 106 | 107 | #[macro_export] 108 | macro_rules! fixed_prev { 109 | ($meta: expr, $x: expr) => { 110 | $meta.query_fixed($x, halo2_proofs::poly::Rotation::prev()) 111 | }; 112 | } 113 | 114 | #[macro_export] 115 | macro_rules! fixed_next { 116 | ($meta: expr, $x: expr) => { 117 | $meta.query_fixed($x, halo2_proofs::poly::Rotation::next()) 118 | }; 119 | } 120 | 121 | #[macro_export] 122 | macro_rules! fixed_nextn { 123 | ($meta: expr, $x: expr, $n: expr) => { 124 | $meta.query_fixed($x, halo2_proofs::poly::Rotation($n)) 125 | }; 126 | } 127 | 128 | #[macro_export] 129 | macro_rules! constant_from { 130 | ($x: expr) => { 131 | halo2_proofs::plonk::Expression::Constant(F::from($x as u64)) 132 | }; 133 | } 134 | 135 | #[macro_export] 136 | macro_rules! constant_from_bn { 137 | ($x: expr) => { 138 | halo2_proofs::plonk::Expression::Constant(bn_to_field($x)) 139 | }; 140 | } 141 | 142 | #[macro_export] 143 | macro_rules! constant { 144 | ($x: expr) => { 145 | halo2_proofs::plonk::Expression::Constant($x) 146 | }; 147 | } 148 | -------------------------------------------------------------------------------- /src/circuits/utils/row_diff.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::constant_from; 3 | use crate::curr; 4 | use crate::nextn; 5 | use halo2_proofs::arithmetic::FieldExt; 6 | use halo2_proofs::plonk::Advice; 7 | use halo2_proofs::plonk::Column; 8 | use halo2_proofs::plonk::ConstraintSystem; 9 | use halo2_proofs::plonk::Error; 10 | use halo2_proofs::plonk::Expression; 11 | use halo2_proofs::plonk::VirtualCells; 12 | use std::marker::PhantomData; 13 | 14 | #[derive(Clone)] 15 | pub struct RowDiffConfig { 16 | pub data: Column, 17 | pub same: Column, 18 | pub inv: Column, 19 | pub distance: i32, 20 | _mark: PhantomData, 21 | } 22 | 23 | impl RowDiffConfig { 24 | pub fn configure( 25 | key: &'static str, 26 | meta: &mut ConstraintSystem, 27 | cols: &mut impl Iterator>, 28 | distance: i32, 29 | enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 30 | ) -> Self { 31 | let data = cols.next().unwrap(); 32 | let same = cols.next().unwrap(); 33 | let inv = cols.next().unwrap(); 34 | 35 | meta.enable_equality(same); 36 | 37 | meta.create_gate(key, |meta| { 38 | let enable = enable(meta); 39 | let diff = curr!(meta, data) - nextn!(meta, data, -distance); 40 | let inv = curr!(meta, inv); 41 | let same = curr!(meta, same); 42 | vec![ 43 | diff.clone() * inv.clone() + same.clone() - constant_from!(1), 44 | diff * same, 45 | ] 46 | .into_iter() 47 | .map(|x| x * enable.clone()) 48 | .collect::>>() 49 | }); 50 | 51 | RowDiffConfig { 52 | data, 53 | same, 54 | inv, 55 | distance, 56 | _mark: PhantomData, 57 | } 58 | } 59 | 60 | pub fn assign( 61 | &self, 62 | ctx: &mut Context, 63 | offset_force: Option, 64 | data: F, 65 | diff: F, 66 | ) -> Result<(), Error> { 67 | let offset = if offset_force.is_some() { 68 | offset_force.unwrap() 69 | } else { 70 | ctx.offset 71 | }; 72 | 73 | ctx.region 74 | .assign_advice(|| "row diff data", self.data, offset, || Ok(data))?; 75 | 76 | ctx.region.assign_advice( 77 | || "row diff inv", 78 | self.inv, 79 | offset, 80 | || Ok(diff.invert().unwrap_or(F::zero())), 81 | )?; 82 | 83 | if offset < self.distance as usize { 84 | ctx.region.assign_advice_from_constant( 85 | || "row diff same", 86 | self.same, 87 | offset, 88 | F::zero(), 89 | )?; 90 | } else { 91 | ctx.region.assign_advice( 92 | || "row diff same", 93 | self.same, 94 | offset, 95 | || { 96 | Ok(if diff.is_zero().into() { 97 | F::one() 98 | } else { 99 | F::zero() 100 | }) 101 | }, 102 | )?; 103 | } 104 | 105 | Ok(()) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/circuits/utils/step_status.rs: -------------------------------------------------------------------------------- 1 | use specs::configure_table::ConfigureTable; 2 | 3 | #[derive(Clone)] 4 | pub struct Status { 5 | pub eid: u32, 6 | pub fid: u32, 7 | pub iid: u32, 8 | pub sp: u32, 9 | pub last_jump_eid: u32, 10 | pub allocated_memory_pages: u32, 11 | } 12 | 13 | pub struct StepStatus<'a> { 14 | pub current: &'a Status, 15 | pub next: &'a Status, 16 | pub current_external_host_call_index: u32, 17 | pub host_public_inputs: u32, 18 | pub configure_table: ConfigureTable, 19 | } 20 | -------------------------------------------------------------------------------- /src/circuits/utils/u16.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::circuits::rtable::RangeTableConfig; 3 | use crate::curr; 4 | use halo2_proofs::arithmetic::FieldExt; 5 | use halo2_proofs::plonk::Advice; 6 | use halo2_proofs::plonk::Column; 7 | use halo2_proofs::plonk::ConstraintSystem; 8 | use halo2_proofs::plonk::Error; 9 | use halo2_proofs::plonk::Expression; 10 | use halo2_proofs::plonk::VirtualCells; 11 | use std::marker::PhantomData; 12 | 13 | #[derive(Clone)] 14 | pub struct U16Column { 15 | pub col: Column, 16 | _mark: PhantomData, 17 | } 18 | 19 | impl U16Column { 20 | pub fn configure( 21 | meta: &mut ConstraintSystem, 22 | cols: &mut impl Iterator>, 23 | rtable: &RangeTableConfig, 24 | enable: impl Fn(&mut VirtualCells<'_, F>) -> Expression, 25 | ) -> Self { 26 | let col = cols.next().unwrap(); 27 | 28 | rtable.configure_in_u16_range(meta, "u16", |meta| curr!(meta, col) * enable(meta)); 29 | 30 | Self { 31 | col, 32 | _mark: PhantomData, 33 | } 34 | } 35 | 36 | pub fn assign(&self, ctx: &mut Context, value: u64) -> Result<(), Error> { 37 | ctx.region 38 | .assign_advice(|| "u16 value", self.col, ctx.offset, || Ok(value.into()))?; 39 | 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/circuits/utils/u8.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::circuits::rtable::RangeTableConfig; 3 | use crate::curr; 4 | use halo2_proofs::arithmetic::FieldExt; 5 | use halo2_proofs::plonk::Advice; 6 | use halo2_proofs::plonk::Column; 7 | use halo2_proofs::plonk::ConstraintSystem; 8 | use halo2_proofs::plonk::Error; 9 | use halo2_proofs::plonk::Expression; 10 | use halo2_proofs::plonk::VirtualCells; 11 | use std::marker::PhantomData; 12 | 13 | #[derive(Clone)] 14 | pub struct U8Column { 15 | pub col: Column, 16 | _mark: PhantomData, 17 | } 18 | 19 | impl U8Column { 20 | pub fn configure( 21 | meta: &mut ConstraintSystem, 22 | cols: &mut impl Iterator>, 23 | rtable: &RangeTableConfig, 24 | enable: impl Fn(&mut VirtualCells<'_, F>) -> Expression, 25 | ) -> Self { 26 | let col = cols.next().unwrap(); 27 | 28 | rtable.configure_in_u8_range(meta, "u8", |meta| curr!(meta, col) * enable(meta)); 29 | 30 | Self { 31 | col, 32 | _mark: PhantomData, 33 | } 34 | } 35 | 36 | pub fn assign(&self, ctx: &mut Context, value: u64) -> Result<(), Error> { 37 | ctx.region 38 | .assign_advice(|| "u8 value", self.col, ctx.offset, || Ok(value.into()))?; 39 | 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/cli/args.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::arg; 4 | use clap::value_parser; 5 | use clap::Arg; 6 | use clap::ArgMatches; 7 | 8 | pub trait ArgBuilder { 9 | fn zkwasm_k_arg<'a>() -> Arg<'a> { 10 | arg!( 11 | -k [K] "Circuit Size K" 12 | ) 13 | .value_parser(value_parser!(u32)) 14 | } 15 | fn parse_zkwasm_k_arg(matches: &ArgMatches) -> Option { 16 | matches.get_one("K").clone().map(|v| *v) 17 | } 18 | 19 | fn zkwasm_file_arg<'a>() -> Arg<'a> { 20 | arg!( 21 | -w --wasm "Path of the Wasm binary file" 22 | ) 23 | .value_parser(value_parser!(PathBuf)) 24 | } 25 | fn parse_zkwasm_file_arg(matches: &ArgMatches) -> PathBuf { 26 | matches 27 | .get_one::("wasm") 28 | .expect("wasm is required.") 29 | .clone() 30 | } 31 | 32 | fn function_name_arg<'a>() -> Arg<'a> { 33 | arg!( 34 | -f --function "Function you would like to run." 35 | ) 36 | } 37 | fn parse_function_name(matches: &ArgMatches) -> String { 38 | matches 39 | .get_one::("function") 40 | .expect("function is required") 41 | .to_string() 42 | } 43 | 44 | fn output_path_arg<'a>() -> Arg<'a> { 45 | arg!( 46 | -o --output [OUTPUT_PATH] "Path of the output files.\nThe md5 of the wasm binary file is the default path if not supplied." 47 | ).value_parser(value_parser!(PathBuf)) 48 | } 49 | 50 | fn proof_path_arg<'a>() -> Arg<'a> { 51 | arg!( 52 | -p --proof "Path of proof." 53 | ) 54 | .value_parser(value_parser!(PathBuf)) 55 | } 56 | 57 | fn parse_proof_path_arg(matches: &ArgMatches) -> PathBuf { 58 | matches 59 | .get_one::("proof") 60 | .expect("proof is required.") 61 | .clone() 62 | } 63 | 64 | fn sol_dir_arg<'a>() -> Arg<'a> { 65 | arg!( 66 | -s --sol_dir [SOL_DIRECTORY] "Path of solidity directory." 67 | ) 68 | .value_parser(value_parser!(PathBuf)) 69 | } 70 | 71 | fn parse_sol_dir_arg(matches: &ArgMatches) -> PathBuf { 72 | matches 73 | .get_one::("sol_dir") 74 | .map_or(PathBuf::from("sol"), |x| x.clone()) 75 | } 76 | 77 | fn auxonly_arg<'a>() -> Arg<'a> { 78 | arg!( 79 | -a --auxonly "Generate aux file only." 80 | ) 81 | .takes_value(false) 82 | } 83 | 84 | fn single_public_arg<'a>() -> Arg<'a>; 85 | fn parse_single_public_arg(matches: &ArgMatches) -> Vec; 86 | 87 | fn aggregate_public_args<'a>() -> Arg<'a>; 88 | fn parse_aggregate_public_args(matches: &ArgMatches) -> Vec>; 89 | 90 | fn single_private_arg<'a>() -> Arg<'a>; 91 | fn parse_single_private_arg(matches: &ArgMatches) -> Vec; 92 | 93 | fn aggregate_private_args<'a>() -> Arg<'a>; 94 | fn parse_aggregate_private_args(matches: &ArgMatches) -> Vec>; 95 | 96 | fn single_instance_path_arg<'a>() -> Arg<'a> { 97 | arg!( 98 | -i --instance "Path of circuit instance." 99 | ) 100 | .value_parser(value_parser!(PathBuf)) 101 | } 102 | fn parse_single_instance_arg(matches: &ArgMatches) -> PathBuf { 103 | matches 104 | .get_one::("instance") 105 | .expect("instance is required.") 106 | .clone() 107 | } 108 | 109 | fn instances_path_arg<'a>() -> Arg<'a> { 110 | arg!( 111 | -i --instances "Path of aggregate instances." 112 | ) 113 | .value_parser(value_parser!(PathBuf)) 114 | } 115 | fn parse_aggregate_instance(matches: &ArgMatches) -> PathBuf { 116 | matches 117 | .get_one::("instances") 118 | .expect("instances is required.") 119 | .clone() 120 | } 121 | fn parse_auxonly(matches: &ArgMatches) -> bool { 122 | matches 123 | .get_many::("auxonly") 124 | .map_or(false, |_| true) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/cli/command.rs: -------------------------------------------------------------------------------- 1 | use clap::App; 2 | use clap::Command; 3 | 4 | use super::args::ArgBuilder; 5 | 6 | pub trait CommandBuilder: ArgBuilder { 7 | fn append_setup_subcommand(app: App) -> App { 8 | let command = Command::new("setup"); 9 | 10 | app.subcommand(command) 11 | } 12 | 13 | #[cfg(feature = "checksum")] 14 | fn append_image_checksum_subcommand(app: App) -> App { 15 | let command = Command::new("checksum"); 16 | 17 | app.subcommand(command) 18 | } 19 | 20 | fn append_dry_run_subcommand(app: App) -> App { 21 | let command = Command::new("dry-run") 22 | .arg(Self::single_public_arg()) 23 | .arg(Self::single_private_arg()); 24 | 25 | app.subcommand(command) 26 | } 27 | 28 | fn append_create_single_proof_subcommand(app: App) -> App { 29 | let command = Command::new("single-prove") 30 | .arg(Self::single_public_arg()) 31 | .arg(Self::single_private_arg()); 32 | app.subcommand(command) 33 | } 34 | 35 | fn append_verify_single_proof_subcommand(app: App) -> App { 36 | let command = Command::new("single-verify") 37 | .arg(Self::proof_path_arg()) 38 | .arg(Self::single_instance_path_arg()); 39 | 40 | app.subcommand(command) 41 | } 42 | 43 | fn append_create_aggregate_proof_subcommand(app: App) -> App { 44 | let command = Command::new("aggregate-prove") 45 | .arg(Self::aggregate_public_args()) 46 | .arg(Self::aggregate_private_args()); 47 | 48 | app.subcommand(command) 49 | } 50 | 51 | fn append_verify_aggregate_verify_subcommand(app: App) -> App { 52 | let command = Command::new("aggregate-verify") 53 | .arg(Self::proof_path_arg()) 54 | .arg(Self::instances_path_arg()); 55 | 56 | app.subcommand(command) 57 | } 58 | 59 | fn append_generate_solidity_verifier(app: App) -> App { 60 | let command = Command::new("solidity-aggregate-verifier") 61 | .arg(Self::sol_dir_arg()) 62 | .arg(Self::proof_path_arg()) 63 | .arg(Self::auxonly_arg()) 64 | .arg(Self::instances_path_arg()); 65 | 66 | app.subcommand(command) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/cli/main.rs: -------------------------------------------------------------------------------- 1 | use clap::value_parser; 2 | use clap::Arg; 3 | use clap::ArgAction; 4 | use clap::ArgMatches; 5 | use delphinus_zkwasm::cli::app_builder::AppBuilder; 6 | use delphinus_zkwasm::cli::args::ArgBuilder; 7 | use delphinus_zkwasm::cli::command::CommandBuilder; 8 | 9 | fn parse_args(values: Vec<&str>) -> Vec { 10 | values 11 | .into_iter() 12 | .map(|v| { 13 | let [v, t] = v.split(":").collect::>()[..] else { todo!() }; 14 | match t { 15 | "i64" => { 16 | if v.starts_with("0x") { 17 | vec![ 18 | u64::from_str_radix(String::from(v).trim_start_matches("0x"), 16) 19 | .unwrap(), 20 | ] 21 | } else { 22 | vec![v.parse::().unwrap()] 23 | } 24 | } 25 | "bytes" => { 26 | if !v.starts_with("0x") { 27 | panic!("bytes input need start with 0x"); 28 | } 29 | let bytes = hex::decode(String::from(v).trim_start_matches("0x")).unwrap(); 30 | bytes 31 | .into_iter() 32 | .map(|x| u64::from(x)) 33 | .collect::>() 34 | } 35 | "bytes-packed" => { 36 | if !v.starts_with("0x") { 37 | panic!("bytes input need start with 0x"); 38 | } 39 | let bytes = hex::decode(String::from(v).trim_start_matches("0x")).unwrap(); 40 | let bytes = bytes.chunks(8); 41 | bytes 42 | .into_iter() 43 | .map(|x| { 44 | let mut data = [0u8; 8]; 45 | data[..x.len()].copy_from_slice(x); 46 | 47 | u64::from_le_bytes(data) 48 | }) 49 | .collect::>() 50 | } 51 | 52 | _ => { 53 | panic!("Unsupported input data type: {}", t) 54 | } 55 | } 56 | }) 57 | .flatten() 58 | .collect() 59 | } 60 | 61 | struct SampleApp; 62 | 63 | impl ArgBuilder for SampleApp { 64 | fn single_public_arg<'a>() -> Arg<'a> { 65 | Arg::new("public") 66 | .long("public") 67 | .value_parser(value_parser!(String)) 68 | .action(ArgAction::Append) 69 | .help("Public arguments of your wasm program arguments of format value:type where type=i64|bytes|bytes-packed") 70 | .min_values(0) 71 | } 72 | fn parse_single_public_arg(matches: &ArgMatches) -> Vec { 73 | let inputs: Vec<&str> = matches 74 | .get_many("public") 75 | .unwrap_or_default() 76 | .map(|v: &String| v.as_str()) 77 | .collect(); 78 | 79 | parse_args(inputs.into()) 80 | } 81 | 82 | fn aggregate_public_args<'a>() -> Arg<'a> { 83 | // We only aggregate one proof in the sample program. 84 | Self::single_public_arg() 85 | } 86 | fn parse_aggregate_public_args(matches: &ArgMatches) -> Vec> { 87 | let inputs = Self::parse_single_public_arg(matches); 88 | 89 | vec![inputs] 90 | } 91 | 92 | fn single_private_arg<'a>() -> Arg<'a> { 93 | Arg::new("private") 94 | .long("private") 95 | .value_parser(value_parser!(String)) 96 | .action(ArgAction::Append) 97 | .help("Private arguments of your wasm program arguments of format value:type where type=i64|bytes|bytes-packed") 98 | .min_values(0) 99 | } 100 | fn parse_single_private_arg(matches: &ArgMatches) -> Vec { 101 | let inputs: Vec<&str> = matches 102 | .get_many("private") 103 | .unwrap_or_default() 104 | .map(|v: &String| v.as_str()) 105 | .collect(); 106 | 107 | parse_args(inputs.into()) 108 | } 109 | 110 | fn aggregate_private_args<'a>() -> Arg<'a> { 111 | // We only aggregate one proof in the sample program. 112 | Self::single_private_arg() 113 | } 114 | fn parse_aggregate_private_args(matches: &ArgMatches) -> Vec> { 115 | let inputs = Self::parse_single_private_arg(matches); 116 | 117 | vec![inputs] 118 | } 119 | } 120 | impl CommandBuilder for SampleApp {} 121 | impl AppBuilder for SampleApp { 122 | const NAME: &'static str = "zkwasm"; 123 | const VERSION: &'static str = "v1.0-beta"; 124 | 125 | const AGGREGATE_K: u32 = 22; 126 | const MAX_PUBLIC_INPUT_SIZE: usize = 64; 127 | 128 | const N_PROOFS: usize = 1; 129 | } 130 | 131 | /// Simple program to greet a person 132 | fn main() { 133 | let app = SampleApp::app_builder(); 134 | 135 | SampleApp::exec(app) 136 | } 137 | -------------------------------------------------------------------------------- /src/cli/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod app_builder; 2 | pub mod args; 3 | pub mod command; 4 | pub mod exec; 5 | -------------------------------------------------------------------------------- /src/foreign/keccak_helper/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod test; 2 | -------------------------------------------------------------------------------- /src/foreign/keccak_helper/test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | pub(crate) mod tests { 3 | use crate::foreign::wasm_input_helper::runtime::register_wasm_input_foreign; 4 | use crate::runtime::host::host_env::HostEnv; 5 | use crate::test::test_circuit_with_env; 6 | 7 | use crate::circuits::config::set_zkwasm_k; 8 | use rusty_fork::rusty_fork_test; 9 | use std::fs; 10 | 11 | pub(crate) fn prepare_inputs() -> (Vec, Vec) { 12 | let msg = "abcdef"; 13 | let mut msg: Vec = hex::decode(msg) 14 | .unwrap() 15 | .into_iter() 16 | .map(|v| v as u64) 17 | .collect(); 18 | let mut private_inputs = vec![msg.len() as u64]; 19 | private_inputs.append(&mut msg); 20 | 21 | let expected = "acd0c377fe36d5b209125185bc3ac41155ed1bf7103ef9f0c2aff4320460b6df"; 22 | let public_inputs = hex::decode(expected) 23 | .unwrap() 24 | .into_iter() 25 | .map(|v| v as u64) 26 | .collect(); 27 | 28 | (public_inputs, private_inputs) 29 | } 30 | 31 | /* 32 | * FORK the test since it modifies global variable zkwasm_k. 33 | */ 34 | rusty_fork_test! { 35 | #[test] 36 | fn test_keccak() { 37 | set_zkwasm_k(19); 38 | 39 | let (public_inputs, private_inputs) = prepare_inputs(); 40 | 41 | let wasm = fs::read("wasm/keccak.wasm").unwrap(); 42 | 43 | let mut env = HostEnv::new(); 44 | let wasm_runtime_io = register_wasm_input_foreign(&mut env, public_inputs.clone(), private_inputs.clone()); 45 | env.finalize(); 46 | 47 | test_circuit_with_env(env, wasm_runtime_io, wasm, "keccak_digest").unwrap(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/foreign/log_helper/mod.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use specs::external_host_call_table::ExternalHostCallSignature; 4 | 5 | use crate::runtime::host::host_env::HostEnv; 6 | use crate::runtime::host::ForeignContext; 7 | 8 | struct Context; 9 | impl ForeignContext for Context {} 10 | 11 | pub fn register_log_foreign(env: &mut HostEnv) { 12 | let foreign_log_plugin = env 13 | .external_env 14 | .register_plugin("foreign_print", Box::new(Context)); 15 | 16 | let print = Rc::new( 17 | |_context: &mut dyn ForeignContext, args: wasmi::RuntimeArgs| { 18 | let value: u64 = args.nth(0); 19 | 20 | println!("{}", value); 21 | 22 | None 23 | }, 24 | ); 25 | 26 | env.external_env.register_function( 27 | "log", 28 | 0, 29 | ExternalHostCallSignature::Argument, 30 | foreign_log_plugin, 31 | print, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/foreign/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::cell::AllocatedUnlimitedCell; 2 | use crate::circuits::etable::allocator::EventTableCellAllocator; 3 | use crate::circuits::etable::constraint_builder::ConstraintBuilder; 4 | use crate::circuits::etable::EventTableCommonConfig; 5 | use crate::circuits::etable::EventTableOpcodeConfig; 6 | use halo2_proofs::arithmetic::FieldExt; 7 | use halo2_proofs::plonk::ConstraintSystem; 8 | use halo2_proofs::plonk::Expression; 9 | use halo2_proofs::plonk::VirtualCells; 10 | 11 | pub mod keccak_helper; 12 | pub mod log_helper; 13 | pub mod require_helper; 14 | pub mod wasm_input_helper; 15 | 16 | pub trait ForeignTableConfig { 17 | fn configure_in_table( 18 | &self, 19 | meta: &mut ConstraintSystem, 20 | key: &'static str, 21 | expr: &dyn Fn(&mut VirtualCells<'_, F>) -> Vec>, 22 | ); 23 | } 24 | 25 | pub(crate) trait EventTableForeignCallConfigBuilder { 26 | fn configure( 27 | self, 28 | common_config: &EventTableCommonConfig, 29 | allocator: &mut EventTableCellAllocator, 30 | constraint_builder: &mut ConstraintBuilder, 31 | lookup_cells: &mut (impl Iterator> + Clone), 32 | ) -> Box>; 33 | } 34 | 35 | pub(crate) trait InternalHostPluginBuilder { 36 | fn new(index: usize) -> Self; 37 | } 38 | -------------------------------------------------------------------------------- /src/foreign/require_helper/etable_op_configure.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::Error; 3 | use halo2_proofs::plonk::Expression; 4 | use halo2_proofs::plonk::VirtualCells; 5 | use num_bigint::BigUint; 6 | use specs::itable::OpcodeClass; 7 | use specs::itable::OPCODE_CLASS_SHIFT; 8 | use specs::mtable::LocationType; 9 | use specs::step::StepInfo; 10 | 11 | use crate::circuits::cell::AllocatedU64Cell; 12 | use crate::circuits::cell::AllocatedUnlimitedCell; 13 | use crate::circuits::cell::CellExpression; 14 | use crate::circuits::etable::allocator::AllocatedMemoryTableLookupReadCell; 15 | use crate::circuits::etable::allocator::EventTableCellAllocator; 16 | use crate::circuits::etable::constraint_builder::ConstraintBuilder; 17 | use crate::circuits::etable::EventTableCommonConfig; 18 | use crate::circuits::etable::EventTableOpcodeConfig; 19 | use crate::circuits::utils::bn_to_field; 20 | use crate::circuits::utils::step_status::StepStatus; 21 | use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; 22 | use crate::circuits::utils::Context; 23 | use crate::constant_from; 24 | use crate::constant_from_bn; 25 | use crate::foreign::EventTableForeignCallConfigBuilder; 26 | use crate::foreign::InternalHostPluginBuilder; 27 | 28 | pub struct ETableRequireHelperTableConfig { 29 | plugin_index: usize, 30 | 31 | cond: AllocatedU64Cell, 32 | cond_inv: AllocatedUnlimitedCell, 33 | 34 | memory_table_lookup_read_stack: AllocatedMemoryTableLookupReadCell, 35 | } 36 | 37 | pub struct ETableRequireHelperTableConfigBuilder { 38 | index: usize, 39 | } 40 | 41 | impl InternalHostPluginBuilder for ETableRequireHelperTableConfigBuilder { 42 | fn new(index: usize) -> Self { 43 | Self { index } 44 | } 45 | } 46 | 47 | impl EventTableForeignCallConfigBuilder for ETableRequireHelperTableConfigBuilder { 48 | fn configure( 49 | self, 50 | common_config: &EventTableCommonConfig, 51 | allocator: &mut EventTableCellAllocator, 52 | constraint_builder: &mut ConstraintBuilder, 53 | _lookup_cells: &mut (impl Iterator> + Clone), 54 | ) -> Box> { 55 | let cond = allocator.alloc_u64_cell(); 56 | let cond_inv = allocator.alloc_unlimited_cell(); 57 | 58 | constraint_builder.push( 59 | "require: cond is not zero", 60 | Box::new(move |meta| vec![(cond.expr(meta) * cond_inv.expr(meta) - constant_from!(1))]), 61 | ); 62 | 63 | let eid = common_config.eid_cell; 64 | let sp = common_config.sp_cell; 65 | 66 | let memory_table_lookup_read_stack = allocator.alloc_memory_table_lookup_read_cell( 67 | "require stack read", 68 | constraint_builder, 69 | eid, 70 | move |_| constant_from!(LocationType::Stack as u64), 71 | move |meta| sp.expr(meta) + constant_from!(1), 72 | move |____| constant_from!(1), 73 | move |meta| cond.expr(meta), 74 | move |_| constant_from!(1), 75 | ); 76 | 77 | Box::new(ETableRequireHelperTableConfig { 78 | plugin_index: self.index, 79 | cond, 80 | cond_inv, 81 | memory_table_lookup_read_stack, 82 | }) 83 | } 84 | } 85 | 86 | impl EventTableOpcodeConfig for ETableRequireHelperTableConfig { 87 | fn opcode(&self, _meta: &mut VirtualCells<'_, F>) -> Expression { 88 | constant_from_bn!( 89 | &(BigUint::from(OpcodeClass::ForeignPluginStart as u64 + self.plugin_index as u64) 90 | << OPCODE_CLASS_SHIFT) 91 | ) 92 | } 93 | 94 | fn assign( 95 | &self, 96 | ctx: &mut Context<'_, F>, 97 | step: &StepStatus, 98 | entry: &EventTableEntryWithMemoryInfo, 99 | ) -> Result<(), Error> { 100 | match &entry.eentry.step_info { 101 | StepInfo::CallHost { args, .. } => { 102 | let cond = args[0]; 103 | 104 | self.cond.assign(ctx, cond)?; 105 | self.cond_inv 106 | .assign(ctx, F::from(cond).invert().unwrap_or(F::zero()))?; 107 | self.memory_table_lookup_read_stack.assign( 108 | ctx, 109 | entry.memory_rw_entires[0].start_eid, 110 | step.current.eid, 111 | entry.memory_rw_entires[0].end_eid, 112 | step.current.sp + 1, 113 | LocationType::Stack, 114 | true, 115 | cond, 116 | )?; 117 | 118 | Ok(()) 119 | } 120 | 121 | _ => unreachable!(), 122 | } 123 | } 124 | 125 | fn sp_diff(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { 126 | Some(constant_from!(1)) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/foreign/require_helper/mod.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use specs::host_function::HostPlugin; 4 | use specs::types::ValueType; 5 | 6 | use crate::runtime::host::host_env::HostEnv; 7 | use crate::runtime::host::ForeignContext; 8 | 9 | pub mod etable_op_configure; 10 | 11 | struct Context; 12 | impl ForeignContext for Context {} 13 | 14 | pub fn register_require_foreign(env: &mut HostEnv) { 15 | let require = Rc::new( 16 | |_context: &mut dyn ForeignContext, args: wasmi::RuntimeArgs| { 17 | let cond: u32 = args.nth(0); 18 | 19 | if cond == 0 { 20 | panic!( 21 | "require is not satisfied, which is a \ 22 | false assertion in the wasm code. Please check \ 23 | the logic of your image or input." 24 | ) 25 | } 26 | 27 | None 28 | }, 29 | ); 30 | 31 | env.internal_env 32 | .register_plugin(HostPlugin::Require, Box::new(Context)); 33 | 34 | env.internal_env.register_function( 35 | "require", 36 | specs::host_function::Signature { 37 | params: vec![ValueType::I32], 38 | return_type: None, 39 | }, 40 | HostPlugin::Require, 41 | 0, 42 | require, 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/foreign/wasm_input_helper/circuits/assign.rs: -------------------------------------------------------------------------------- 1 | use super::WasmInputHelperTableConfig; 2 | use crate::foreign::wasm_input_helper::circuits::ENABLE_LINES; 3 | use halo2_proofs::arithmetic::FieldExt; 4 | use halo2_proofs::circuit::AssignedCell; 5 | use halo2_proofs::circuit::Layouter; 6 | use halo2_proofs::plonk::Error; 7 | 8 | pub struct WasmInputHelperTableChip { 9 | pub(crate) config: WasmInputHelperTableConfig, 10 | } 11 | 12 | impl WasmInputHelperTableChip { 13 | pub fn new(config: WasmInputHelperTableConfig) -> Self { 14 | Self { config } 15 | } 16 | 17 | pub fn assign( 18 | &self, 19 | layouter: &mut impl Layouter, 20 | instances: Vec>, 21 | ) -> Result<(), Error> { 22 | for (i, instance) in instances.iter().enumerate() { 23 | layouter.constrain_instance(instance.cell(), self.config.input, i)?; 24 | } 25 | 26 | layouter.assign_region( 27 | || "wasm input helper assign", 28 | |mut region| { 29 | let offset = instances.len(); 30 | for i in 0..ENABLE_LINES { 31 | region.assign_fixed( 32 | || "wasm input helper enable", 33 | self.config.enable, 34 | i as usize + offset, 35 | || Ok(F::one()), 36 | )?; 37 | 38 | region.assign_fixed( 39 | || "wasm input index", 40 | self.config.index, 41 | i + offset, 42 | || Ok(F::from(i as u64)), 43 | )?; 44 | } 45 | 46 | Ok(()) 47 | }, 48 | )?; 49 | Ok(()) 50 | } 51 | 52 | pub fn init(&self, _layouter: &mut impl Layouter) -> Result<(), Error> { 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/foreign/wasm_input_helper/circuits/config.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::ConstraintSystem; 3 | use halo2_proofs::plonk::Expression; 4 | use halo2_proofs::plonk::VirtualCells; 5 | 6 | use crate::fixed_curr; 7 | use crate::foreign::ForeignTableConfig; 8 | use crate::instance_curr; 9 | 10 | use super::WasmInputHelperTableConfig; 11 | 12 | impl WasmInputHelperTableConfig { 13 | pub fn configure(meta: &mut ConstraintSystem) -> Self { 14 | let enable = meta.fixed_column(); 15 | let index = meta.fixed_column(); 16 | let input = meta.instance_column(); 17 | meta.enable_equality(input); 18 | 19 | WasmInputHelperTableConfig { 20 | enable, 21 | index, 22 | input, 23 | _mark: std::marker::PhantomData, 24 | } 25 | } 26 | } 27 | 28 | impl ForeignTableConfig for WasmInputHelperTableConfig { 29 | fn configure_in_table( 30 | &self, 31 | meta: &mut ConstraintSystem, 32 | key: &'static str, 33 | expr: &dyn Fn(&mut VirtualCells<'_, F>) -> Vec>, 34 | ) { 35 | meta.lookup_any(key, |meta| { 36 | let mut exprs = expr(meta); 37 | 38 | vec![ 39 | (exprs.remove(0), fixed_curr!(meta, self.index)), 40 | (exprs.remove(0), instance_curr!(meta, self.input)), 41 | ] 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/foreign/wasm_input_helper/circuits/mod.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use halo2_proofs::arithmetic::FieldExt; 4 | use halo2_proofs::plonk::Column; 5 | use halo2_proofs::plonk::Fixed; 6 | use halo2_proofs::plonk::Instance; 7 | 8 | pub mod assign; 9 | pub mod config; 10 | 11 | pub const WASM_INPUT_FOREIGN_TABLE_KEY: &'static str = "wasm-input-helper-table"; 12 | const K: usize = 15; 13 | const ENABLE_LINES: usize = 1 << (K - 1); 14 | 15 | #[derive(Clone)] 16 | pub struct WasmInputHelperTableConfig { 17 | enable: Column, 18 | index: Column, 19 | input: Column, 20 | _mark: PhantomData, 21 | } 22 | -------------------------------------------------------------------------------- /src/foreign/wasm_input_helper/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod circuits; 2 | pub mod etable_op_configure; 3 | pub mod runtime; 4 | pub mod test; 5 | 6 | enum Op { 7 | WasmInput = 0, 8 | WasmOutput = 1, 9 | } 10 | -------------------------------------------------------------------------------- /src/foreign/wasm_input_helper/runtime.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use specs::host_function::HostPlugin; 5 | use specs::types::ValueType; 6 | 7 | use crate::runtime::host::host_env::HostEnv; 8 | use crate::runtime::host::ForeignContext; 9 | use crate::runtime::wasmi_interpreter::WasmRuntimeIO; 10 | 11 | use super::Op; 12 | 13 | struct Context { 14 | public_inputs: Vec, 15 | private_inputs: Vec, 16 | instance: Rc>>, 17 | output: Rc>>, 18 | } 19 | 20 | impl Context { 21 | pub fn new( 22 | public_inputs: Vec, 23 | private_inputs: Vec, 24 | instance: Rc>>, 25 | output: Rc>>, 26 | ) -> Self { 27 | Context { 28 | public_inputs, 29 | private_inputs, 30 | instance, 31 | output, 32 | } 33 | } 34 | 35 | pub fn pop_public(&mut self) -> u64 { 36 | self.public_inputs.remove(0) 37 | } 38 | 39 | pub fn pop_private(&mut self) -> u64 { 40 | self.private_inputs.remove(0) 41 | } 42 | 43 | pub fn push_public(&mut self, value: u64) { 44 | let mut instance = self.instance.borrow_mut(); 45 | instance.push(value) 46 | } 47 | 48 | pub fn push_output(&mut self, value: u64) { 49 | let mut instance = self.instance.borrow_mut(); 50 | instance.push(value); 51 | 52 | let mut output = self.output.borrow_mut(); 53 | output.push(value); 54 | } 55 | } 56 | 57 | impl ForeignContext for Context {} 58 | 59 | // TODO: invoke this in WasmRuntime 60 | pub fn register_wasm_input_foreign( 61 | env: &mut HostEnv, 62 | public_inputs: Vec, 63 | private_inputs: Vec, 64 | ) -> WasmRuntimeIO { 65 | let public_inputs_and_outputs = Rc::new(RefCell::new(vec![])); 66 | let outputs = Rc::new(RefCell::new(vec![])); 67 | 68 | let wasm_input = Rc::new( 69 | |context: &mut dyn ForeignContext, args: wasmi::RuntimeArgs| { 70 | let context = context.downcast_mut::().unwrap(); 71 | 72 | let arg: i32 = args.nth(0); 73 | assert!(arg == 0 || arg == 1); 74 | 75 | let input = if arg == 1 { 76 | let value = context.pop_public(); 77 | context.push_public(value); 78 | value 79 | } else { 80 | context.pop_private() 81 | }; 82 | 83 | Some(wasmi::RuntimeValue::I64(input as i64)) 84 | }, 85 | ); 86 | 87 | let wasm_output = Rc::new( 88 | |context: &mut dyn ForeignContext, args: wasmi::RuntimeArgs| { 89 | let context = context.downcast_mut::().unwrap(); 90 | 91 | let value: i64 = args.nth(0); 92 | context.push_output(value as u64); 93 | 94 | None 95 | }, 96 | ); 97 | 98 | env.internal_env.register_plugin( 99 | HostPlugin::HostInput, 100 | Box::new(Context::new( 101 | public_inputs, 102 | private_inputs, 103 | public_inputs_and_outputs.clone(), 104 | outputs.clone(), 105 | )), 106 | ); 107 | 108 | env.internal_env.register_function( 109 | "wasm_input", 110 | specs::host_function::Signature { 111 | params: vec![ValueType::I32], 112 | return_type: Some(ValueType::I64), 113 | }, 114 | HostPlugin::HostInput, 115 | Op::WasmInput as usize, 116 | wasm_input, 117 | ); 118 | 119 | env.internal_env.register_function( 120 | "wasm_output", 121 | specs::host_function::Signature { 122 | params: vec![ValueType::I64], 123 | return_type: None, 124 | }, 125 | HostPlugin::HostInput, 126 | Op::WasmOutput as usize, 127 | wasm_output, 128 | ); 129 | 130 | WasmRuntimeIO { 131 | public_inputs_and_outputs, 132 | outputs, 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/foreign/wasm_input_helper/test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::foreign::wasm_input_helper::runtime::register_wasm_input_foreign; 4 | use crate::runtime::host::host_env::HostEnv; 5 | use crate::test::test_circuit_with_env; 6 | 7 | #[test] 8 | fn test_foreign_wasm_input() { 9 | let textual_repr = r#" 10 | (module 11 | (import "env" "wasm_input" (func $wasm_input (param i32) (result i64))) 12 | (export "main" (func $main)) 13 | (func $main (; 1 ;) 14 | (call $wasm_input (i32.const 1)) 15 | (drop) 16 | ) 17 | ) 18 | "#; 19 | 20 | let public_inputs = vec![9]; 21 | let wasm = wabt::wat2wasm(&textual_repr).expect("failed to parse wat"); 22 | 23 | let mut env = HostEnv::new(); 24 | let wasm_runtime_io = register_wasm_input_foreign(&mut env, public_inputs, vec![]); 25 | env.finalize(); 26 | 27 | test_circuit_with_env(env, wasm_runtime_io, wasm, "main").unwrap(); 28 | } 29 | 30 | #[test] 31 | fn test_foreign_wasm_input_multi_public() { 32 | let textual_repr = r#" 33 | (module 34 | (type (;0;) (func (param i32) (result i64))) 35 | (type (;1;) (func (result i32))) 36 | (import "env" "wasm_input" (func (;0;) (type 0))) 37 | (func (;1;) (type 1) (result i32) 38 | (local i64) 39 | i32.const 1 40 | call 0 41 | local.set 0 42 | i32.const 1 43 | call 0 44 | i32.wrap_i64 45 | local.get 0 46 | i32.wrap_i64 47 | i32.add) 48 | (memory (;0;) 2 2) 49 | (export "memory" (memory 0)) 50 | (export "main" (func 1))) 51 | "#; 52 | 53 | let wasm = wabt::wat2wasm(&textual_repr).expect("failed to parse wat"); 54 | 55 | let private_inputs = vec![]; 56 | let public_inputs = vec![1, 2]; 57 | 58 | let mut env = HostEnv::new(); 59 | let wasm_runtime_io = register_wasm_input_foreign(&mut env, public_inputs, private_inputs); 60 | env.finalize(); 61 | 62 | test_circuit_with_env(env, wasm_runtime_io, wasm, "main").unwrap(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(dead_code)] 2 | #![deny(unused_variables)] 3 | #![deny(unused_imports)] 4 | #![feature(thread_local)] 5 | 6 | pub mod circuits; 7 | pub mod cli; 8 | pub mod foreign; 9 | pub mod runtime; 10 | pub mod traits; 11 | 12 | #[cfg(feature = "checksum")] 13 | pub mod image_hasher; 14 | 15 | mod profile; 16 | 17 | #[cfg(test)] 18 | pub mod test; 19 | 20 | #[macro_use] 21 | extern crate lazy_static; 22 | extern crate downcast_rs; 23 | -------------------------------------------------------------------------------- /src/profile/helper.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! exec_with_profile { 3 | ($desc:expr, $statement: expr) => {{ 4 | let timer = start_timer!($desc); 5 | let r = $statement; 6 | end_timer!(timer); 7 | r 8 | }}; 9 | } 10 | -------------------------------------------------------------------------------- /src/profile/instruction_merge.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use log::debug; 4 | use specs::etable::EventTable; 5 | use specs::itable::Opcode; 6 | use specs::itable::OpcodeClass; 7 | 8 | pub trait InstructionMergingProfile { 9 | fn estimate_mergeable_instruction(&self); 10 | } 11 | 12 | impl InstructionMergingProfile for EventTable { 13 | fn estimate_mergeable_instruction(&self) { 14 | let mut const_count = 0; 15 | let mut local_get = 0; 16 | let mut load_count = 0; 17 | let mut global_get_count = 0; 18 | 19 | let mut const_opt: BTreeMap = BTreeMap::new(); 20 | let mut local_get_opt: BTreeMap = BTreeMap::new(); 21 | 22 | self.entries() 23 | .iter() 24 | .zip(self.entries().iter().skip(1)) 25 | .for_each(|(entry, next_entry)| { 26 | match (&entry.inst.opcode, &next_entry.inst.opcode) { 27 | (Opcode::Const { .. }, Opcode::Bin { .. }) => { 28 | const_count = const_count + 1; 29 | } 30 | (Opcode::LocalGet { .. }, Opcode::Bin { .. }) => { 31 | local_get = local_get + 1; 32 | } 33 | (Opcode::Load { .. }, Opcode::Bin { .. }) => { 34 | load_count = load_count + 1; 35 | } 36 | (Opcode::GlobalGet { .. }, Opcode::Bin { .. }) => { 37 | global_get_count = global_get_count + 1; 38 | } 39 | _ => (), 40 | } 41 | 42 | if let Opcode::Const { .. } = &entry.inst.opcode { 43 | match const_opt.get_mut(&(next_entry.inst.opcode.clone().into())) { 44 | Some(counter) => *counter = *counter + 1, 45 | None => { 46 | const_opt.insert(next_entry.inst.opcode.clone().into(), 1); 47 | } 48 | } 49 | } 50 | 51 | if let Opcode::LocalGet { .. } = &entry.inst.opcode { 52 | match local_get_opt.get_mut(&(next_entry.inst.opcode.clone().into())) { 53 | Some(counter) => *counter = *counter + 1, 54 | None => { 55 | local_get_opt.insert(next_entry.inst.opcode.clone().into(), 1); 56 | } 57 | } 58 | } 59 | }); 60 | 61 | debug!("mergeable instruction"); 62 | debug!("local get: {}", local_get); 63 | debug!("const: {}", const_count); 64 | debug!("load: {}", load_count); 65 | debug!("global get: {}", global_get_count); 66 | 67 | debug!("const follow: {:?}", const_opt); 68 | debug!("local get follow: {:?}", local_get_opt); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/profile/instruction_statistic.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use specs::etable::EventTable; 3 | use specs::itable::OpcodeClass; 4 | use specs::mtable::AccessType; 5 | use specs::mtable::MemoryTableEntry; 6 | use std::collections::BTreeMap; 7 | use std::fmt::Debug; 8 | 9 | use crate::runtime::memory_event_of_step; 10 | 11 | pub trait InstructionStatistic { 12 | fn profile_instruction(&self); 13 | } 14 | 15 | impl InstructionStatistic for EventTable { 16 | fn profile_instruction(&self) { 17 | struct Counter { 18 | counter: usize, 19 | mentries: Vec, 20 | } 21 | 22 | let mut map = BTreeMap::::new(); 23 | for entry in self.entries() { 24 | let mut mentries = memory_event_of_step(entry, &mut 1); 25 | 26 | if let Some(counter) = map.get_mut(&entry.inst.opcode.clone().into()) { 27 | counter.counter += 1; 28 | counter.mentries.append(&mut mentries); 29 | } else { 30 | map.insert( 31 | entry.inst.opcode.clone().into(), 32 | Counter { 33 | counter: 1, 34 | mentries, 35 | }, 36 | ); 37 | } 38 | } 39 | 40 | let total_mentries: usize = { 41 | let a = map 42 | .values() 43 | .map(|counter| counter.mentries.len()) 44 | .collect::>(); 45 | a.iter().sum() 46 | }; 47 | 48 | debug!("etable entries: {}", self.entries().len()); 49 | debug!("mtable entries: {}", total_mentries); 50 | 51 | struct Summary { 52 | counter: usize, 53 | mentries: usize, 54 | total_mentries: usize, 55 | write_counter: usize, 56 | read_counter: usize, 57 | } 58 | 59 | impl Debug for Summary { 60 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 61 | write!( 62 | f, 63 | "{{ counter: {}, mentries: {}({:.2}%), write: {}({:.2}%), read: {}({:.2}%) }}", 64 | self.counter, 65 | self.mentries, 66 | self.mentries as f64 / self.total_mentries as f64 * 100f64, 67 | self.write_counter, 68 | self.write_counter as f64 / self.total_mentries as f64 * 100f64, 69 | self.read_counter, 70 | self.read_counter as f64 / self.total_mentries as f64 * 100f64, 71 | ) 72 | } 73 | } 74 | 75 | let summary = map 76 | .into_iter() 77 | .map(|(inst, counter)| { 78 | ( 79 | inst, 80 | Summary { 81 | counter: counter.counter, 82 | mentries: counter.mentries.len(), 83 | total_mentries, 84 | write_counter: counter 85 | .mentries 86 | .iter() 87 | .filter(|entry| entry.atype == AccessType::Write) 88 | .collect::>() 89 | .len(), 90 | read_counter: counter 91 | .mentries 92 | .iter() 93 | .filter(|entry| entry.atype == AccessType::Read) 94 | .collect::>() 95 | .len(), 96 | }, 97 | ) 98 | }) 99 | .collect::>(); 100 | 101 | println!("{:?}", summary); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/profile/mod.rs: -------------------------------------------------------------------------------- 1 | use instruction_merge::InstructionMergingProfile; 2 | use instruction_statistic::InstructionStatistic; 3 | use specs::Tables; 4 | 5 | mod helper; 6 | mod instruction_merge; 7 | mod instruction_statistic; 8 | 9 | pub trait Profiler { 10 | fn profile_tables(&self); 11 | } 12 | 13 | impl Profiler for Tables { 14 | fn profile_tables(&self) { 15 | self.execution_tables.etable.profile_instruction(); 16 | 17 | self.execution_tables 18 | .etable 19 | .estimate_mergeable_instruction(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/runtime/host/external_circuit_plugin.rs: -------------------------------------------------------------------------------- 1 | use specs::external_host_call_table::ExternalHostCallSignature; 2 | use std::cell::RefCell; 3 | use std::collections::HashMap; 4 | use std::rc::Rc; 5 | use wasmi::FuncInstance; 6 | use wasmi::ModuleImportResolver; 7 | use wasmi::RuntimeArgs; 8 | use wasmi::RuntimeValue; 9 | 10 | use super::ForeignContext; 11 | use super::ForeignPlugin; 12 | use super::MatchForeignOpSignature; 13 | 14 | pub(super) struct ForeignOp { 15 | pub op_index: usize, 16 | pub sig: ExternalHostCallSignature, 17 | pub plugin: Rc, 18 | pub cb: Rc Option>, 19 | } 20 | 21 | pub struct ExternalCircuitEnv { 22 | pub(super) functions: HashMap, 23 | finalized: Rc>, 24 | } 25 | 26 | impl ExternalCircuitEnv { 27 | pub(super) fn new(finalized: Rc>) -> Self { 28 | Self { 29 | functions: HashMap::new(), 30 | finalized, 31 | } 32 | } 33 | 34 | /// Register a plugin without circuit 35 | pub fn register_plugin( 36 | &mut self, 37 | _name: &str, 38 | ctx: Box, 39 | ) -> Rc { 40 | Rc::new(ForeignPlugin { 41 | ctx: Rc::new(RefCell::new(ctx)), 42 | }) 43 | } 44 | 45 | /// Register a foreign function to a registed plugin 46 | pub fn register_function( 47 | &mut self, 48 | name: &str, 49 | op_index: usize, 50 | sig: ExternalHostCallSignature, 51 | plugin: Rc, 52 | cb: Rc Option>, 53 | ) { 54 | assert!(!*self.finalized.borrow()); 55 | 56 | self.functions.insert( 57 | name.to_owned(), 58 | ForeignOp { 59 | op_index, 60 | sig, 61 | plugin, 62 | cb, 63 | }, 64 | ); 65 | } 66 | } 67 | 68 | impl ModuleImportResolver for ExternalCircuitEnv { 69 | fn resolve_func( 70 | &self, 71 | function_name: &str, 72 | signature: &wasmi::Signature, 73 | ) -> Result { 74 | for (name, function) in &self.functions { 75 | if name == function_name { 76 | if function.sig.match_wasmi_signature(signature) { 77 | return Ok(FuncInstance::alloc_host( 78 | signature.clone(), 79 | function.op_index, 80 | )); 81 | } else { 82 | return Err(wasmi::Error::Instantiation(format!( 83 | "Export `{}` doesnt match expected type {:?}", 84 | function_name, signature 85 | ))); 86 | } 87 | } 88 | } 89 | 90 | return Err(wasmi::Error::Instantiation(format!( 91 | "Export {} not found", 92 | function_name 93 | ))); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/runtime/host/internal_circuit_plugin.rs: -------------------------------------------------------------------------------- 1 | use specs::host_function::HostPlugin; 2 | use specs::host_function::Signature; 3 | use std::cell::RefCell; 4 | use std::collections::HashMap; 5 | use std::rc::Rc; 6 | use wasmi::FuncInstance; 7 | use wasmi::ModuleImportResolver; 8 | use wasmi::RuntimeArgs; 9 | use wasmi::RuntimeValue; 10 | 11 | use super::ForeignContext; 12 | use super::ForeignPlugin; 13 | 14 | pub(super) struct ForeignOp { 15 | pub index: Option, 16 | pub index_within_plugin: usize, 17 | pub sig: Signature, 18 | pub plugin: HostPlugin, 19 | pub cb: Rc Option>, 20 | } 21 | 22 | pub struct InternalCircuitEnv { 23 | pub(super) plugins: HashMap, 24 | pub(super) functions: HashMap, 25 | finalized: Rc>, 26 | } 27 | 28 | impl InternalCircuitEnv { 29 | pub(super) fn new(finalized: Rc>) -> Self { 30 | Self { 31 | plugins: HashMap::new(), 32 | functions: HashMap::new(), 33 | finalized, 34 | } 35 | } 36 | 37 | pub fn register_plugin(&mut self, plugin: HostPlugin, context: Box) { 38 | let _ = self.plugins.insert( 39 | plugin, 40 | ForeignPlugin { 41 | ctx: Rc::new(RefCell::new(context)), 42 | }, 43 | ); 44 | } 45 | 46 | pub fn register_function( 47 | &mut self, 48 | function_name: &str, 49 | sig: Signature, 50 | plugin: HostPlugin, 51 | index_within_plugin: usize, 52 | cb: Rc Option>, 53 | ) { 54 | assert!(!*self.finalized.borrow()); 55 | 56 | self.functions.insert( 57 | function_name.to_owned(), 58 | ForeignOp { 59 | index: None, 60 | index_within_plugin, 61 | sig, 62 | plugin, 63 | cb, 64 | }, 65 | ); 66 | } 67 | } 68 | 69 | impl ModuleImportResolver for InternalCircuitEnv { 70 | fn resolve_func( 71 | &self, 72 | function_name: &str, 73 | signature: &wasmi::Signature, 74 | ) -> Result { 75 | if let Some(ForeignOp { index, sig, .. }) = self.functions.get(function_name) { 76 | if *sig == signature.clone().into() { 77 | Ok(FuncInstance::alloc_host( 78 | signature.clone(), 79 | index.expect("Unsolved host function index."), 80 | )) 81 | } else { 82 | Err(wasmi::Error::Instantiation(format!("Signature not match",))) 83 | } 84 | } else { 85 | Err(wasmi::Error::Instantiation(format!( 86 | "Export {} not found", 87 | function_name 88 | ))) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/runtime/host/mod.rs: -------------------------------------------------------------------------------- 1 | use downcast_rs::impl_downcast; 2 | use downcast_rs::Downcast; 3 | use specs::external_host_call_table::ExternalHostCallSignature; 4 | use specs::host_function::HostFunctionDesc; 5 | use std::cell::RefCell; 6 | use std::rc::Rc; 7 | use wasmi::RuntimeArgs; 8 | use wasmi::RuntimeValue; 9 | use wasmi::Signature; 10 | 11 | pub mod host_env; 12 | 13 | mod external_circuit_plugin; 14 | mod internal_circuit_plugin; 15 | 16 | trait MatchForeignOpSignature { 17 | fn match_wasmi_signature(&self, signature: &Signature) -> bool; 18 | } 19 | 20 | impl MatchForeignOpSignature for ExternalHostCallSignature { 21 | /// Currently we only support 22 | /// * function with one argument and without return value 23 | /// * function with return value and without any arguments 24 | fn match_wasmi_signature(&self, signature: &Signature) -> bool { 25 | match self { 26 | ExternalHostCallSignature::Argument => { 27 | signature.params().len() == 1 28 | && signature.params()[0] == wasmi::ValueType::I64 29 | && signature.return_type() == None 30 | } 31 | ExternalHostCallSignature::Return => { 32 | signature.params().len() == 0 33 | && signature.return_type() == Some(wasmi::ValueType::I64) 34 | } 35 | } 36 | } 37 | } 38 | 39 | /// Context of the plugin. 40 | /// 41 | /// # Examples 42 | /// 43 | /// ``` 44 | /// use delphinus_zkwasm::runtime::host::ForeignContext; 45 | /// 46 | /// struct Context { 47 | /// acc: u64, 48 | /// } 49 | /// impl ForeignContext for Context {} 50 | /// ``` 51 | pub trait ForeignContext: Downcast {} 52 | impl_downcast!(ForeignContext); 53 | 54 | pub struct ForeignPlugin { 55 | ctx: Rc>>, 56 | } 57 | 58 | #[derive(Clone)] 59 | struct HostFunctionExecutionEnv { 60 | ctx: Rc>>, 61 | cb: Rc Option>, 62 | } 63 | 64 | #[derive(Clone)] 65 | struct HostFunction { 66 | desc: HostFunctionDesc, 67 | execution_env: HostFunctionExecutionEnv, 68 | } 69 | -------------------------------------------------------------------------------- /src/test/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::config::zkwasm_k; 2 | use crate::circuits::utils::table_entry::MemoryWritingTable; 3 | use crate::circuits::TestCircuit; 4 | use crate::profile::Profiler; 5 | use crate::runtime::host::host_env::HostEnv; 6 | use crate::runtime::wasmi_interpreter::Execution; 7 | use crate::runtime::ExecutionResult; 8 | use crate::runtime::WasmInterpreter; 9 | 10 | #[cfg(feature = "checksum")] 11 | use crate::image_hasher::ImageHasher; 12 | use crate::runtime::wasmi_interpreter::WasmRuntimeIO; 13 | 14 | use anyhow::Result; 15 | use halo2_proofs::arithmetic::FieldExt; 16 | use halo2_proofs::dev::MockProver; 17 | use halo2_proofs::pairing::bn256::Fr; 18 | use wasmi::ImportsBuilder; 19 | use wasmi::RuntimeValue; 20 | 21 | #[cfg(test)] 22 | mod test_wasm_instructions; 23 | 24 | mod spec; 25 | mod test_binary_search; 26 | mod test_fibonacci; 27 | mod test_rlp; 28 | mod test_rlp_simple; 29 | mod test_start; 30 | 31 | #[cfg(feature = "checksum")] 32 | mod test_uniform_verifier; 33 | 34 | /// Create circuit with trace and run mock test. 35 | fn test_circuit_mock( 36 | execution_result: ExecutionResult, 37 | ) -> Result<()> { 38 | let instance = { 39 | let mut v: Vec = vec![]; 40 | 41 | #[cfg(feature = "checksum")] 42 | v.push(execution_result.tables.compilation_tables.hash()); 43 | 44 | v.append( 45 | &mut execution_result 46 | .public_inputs_and_outputs 47 | .iter() 48 | .map(|v| (*v).into()) 49 | .collect(), 50 | ); 51 | 52 | v 53 | }; 54 | 55 | execution_result.tables.write_json(None); 56 | let memory_writing_table: MemoryWritingTable = execution_result 57 | .tables 58 | .execution_tables 59 | .mtable 60 | .clone() 61 | .into(); 62 | memory_writing_table.write_json(None); 63 | 64 | execution_result.tables.profile_tables(); 65 | 66 | let circuit = TestCircuit::new(execution_result.tables); 67 | let prover = MockProver::run(zkwasm_k(), &circuit, vec![instance])?; 68 | assert_eq!(prover.verify(), Ok(())); 69 | 70 | Ok(()) 71 | } 72 | 73 | /// Run function and generate trace. 74 | fn compile_then_execute_wasm( 75 | mut env: HostEnv, 76 | wasm_runtime_io: WasmRuntimeIO, 77 | wasm: Vec, 78 | function_name: &str, 79 | ) -> Result> { 80 | let module = wasmi::Module::from_buffer(&wasm).expect("failed to load wasm"); 81 | 82 | let imports = ImportsBuilder::new().with_resolver("env", &env); 83 | 84 | let compiler = WasmInterpreter::new(); 85 | let compiled_module = compiler 86 | .compile( 87 | &module, 88 | &imports, 89 | &env.function_description_table(), 90 | function_name, 91 | ) 92 | .unwrap(); 93 | 94 | let execution_result = compiled_module.run(&mut env, wasm_runtime_io)?; 95 | 96 | Ok(execution_result) 97 | } 98 | 99 | /// Run the function and generate trace, then test circuit with mock prover. 100 | pub fn test_circuit_with_env( 101 | env: HostEnv, 102 | wasm_runtime_io: WasmRuntimeIO, 103 | wasm: Vec, 104 | function_name: &str, 105 | ) -> Result> { 106 | let trace = compile_then_execute_wasm(env, wasm_runtime_io, wasm, function_name)?; 107 | test_circuit_mock::(trace.clone())?; 108 | 109 | Ok(trace) 110 | } 111 | 112 | /// Run test function and generate trace, then test circuit with mock prover. Only tests should 113 | /// use this function. 114 | fn test_circuit_noexternal(textual_repr: &str) -> Result<()> { 115 | let wasm = wabt::wat2wasm(&textual_repr).expect("failed to parse wat"); 116 | 117 | let mut env = HostEnv::new(); 118 | env.finalize(); 119 | 120 | test_circuit_with_env(env, WasmRuntimeIO::empty(), wasm, "test")?; 121 | 122 | Ok(()) 123 | } 124 | -------------------------------------------------------------------------------- /src/test/spec/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | use wast::Error; 3 | 4 | #[cfg(test)] 5 | fn run_spec_test(_file_name: &str) -> Result<(), Error> { 6 | todo!(); 7 | 8 | /* 9 | let path = format!("src/test/spec/{}.wast", file_name); 10 | let file = fs::read_to_string(&path).unwrap(); 11 | 12 | let mut lexer = Lexer::new(&file); 13 | lexer.allow_confusing_unicode(true); 14 | let parse_buffer = ParseBuffer::new_with_lexer(lexer)?; 15 | 16 | let wast: Wast = wast::parser::parse(&parse_buffer)?; 17 | let imports = ImportsBuilder::default(); 18 | let mut externals = NopExternals; 19 | 20 | let compiler = WasmInterpreter::new(); 21 | let mut compile_outcome = None; 22 | 23 | for directive in wast.directives { 24 | match directive { 25 | wast::WastDirective::Wat(wat) => match wat { 26 | wast::QuoteWat::Wat(wat) => match wat { 27 | wast::Wat::Module(module) => { 28 | let compiled = compiler 29 | .compile_from_wast(module, &imports, &HashMap::default()) 30 | .unwrap(); 31 | compile_outcome = Some(compiled); 32 | } 33 | wast::Wat::Component(_) => todo!(), 34 | }, 35 | wast::QuoteWat::QuoteModule(_, _) => todo!(), 36 | wast::QuoteWat::QuoteComponent(_, _) => todo!(), 37 | }, 38 | wast::WastDirective::AssertMalformed { .. } => todo!(), 39 | wast::WastDirective::AssertInvalid { .. } => todo!(), 40 | wast::WastDirective::Register { .. } => todo!(), 41 | wast::WastDirective::Invoke(..) => todo!(), 42 | wast::WastDirective::AssertTrap { .. } => todo!(), 43 | wast::WastDirective::AssertReturn { 44 | span: _span, 45 | exec, 46 | results: _results, 47 | } => { 48 | let from_wastarg = |arg: &WastArg| match arg { 49 | WastArg::Core(core) => match core { 50 | wast::core::WastArgCore::I32(v) => Value::I32(*v), 51 | wast::core::WastArgCore::I64(v) => Value::I64(*v), 52 | wast::core::WastArgCore::F32(_) => todo!(), 53 | wast::core::WastArgCore::F64(_) => todo!(), 54 | wast::core::WastArgCore::V128(_) => todo!(), 55 | wast::core::WastArgCore::RefNull(_) => todo!(), 56 | wast::core::WastArgCore::RefExtern(_) => todo!(), 57 | }, 58 | WastArg::Component(_) => todo!(), 59 | }; 60 | 61 | let _actual_results = match exec { 62 | wast::WastExecute::Invoke(invoke) => compiler.run( 63 | &mut externals, 64 | &compile_outcome.unwrap(), 65 | invoke.name, 66 | invoke.args.iter().map(|arg| from_wastarg(arg)).collect(), 67 | ), 68 | wast::WastExecute::Wat(_) => todo!(), 69 | wast::WastExecute::Get { .. } => todo!(), 70 | } 71 | .unwrap(); 72 | 73 | todo!() 74 | } 75 | wast::WastDirective::AssertExhaustion { .. } => todo!(), 76 | wast::WastDirective::AssertUnlinkable { .. } => todo!(), 77 | wast::WastDirective::AssertException { .. } => todo!(), 78 | } 79 | } 80 | Ok(()) 81 | */ 82 | } 83 | 84 | /* 85 | mod tests { 86 | use super::run_spec_test; 87 | 88 | #[test] 89 | fn test_spec_i32() { 90 | run_spec_test("i32").unwrap() 91 | } 92 | } 93 | */ 94 | */ 95 | -------------------------------------------------------------------------------- /src/test/test_binary_search.rs: -------------------------------------------------------------------------------- 1 | use crate::foreign::wasm_input_helper::runtime::register_wasm_input_foreign; 2 | use crate::runtime::host::host_env::HostEnv; 3 | use crate::runtime::ExecutionResult; 4 | 5 | use anyhow::Result; 6 | use std::fs::{self}; 7 | 8 | use super::compile_then_execute_wasm; 9 | 10 | fn build_test() -> Result> { 11 | let public_inputs = vec![3]; 12 | 13 | let wasm = fs::read("wasm/bsearch_64.wasm").unwrap(); 14 | 15 | let mut env = HostEnv::new(); 16 | let wasm_runtime_io = register_wasm_input_foreign(&mut env, public_inputs, vec![]); 17 | env.finalize(); 18 | 19 | compile_then_execute_wasm(env, wasm_runtime_io, wasm, "bsearch") 20 | } 21 | 22 | mod tests { 23 | use super::*; 24 | use crate::circuits::ZkWasmCircuitBuilder; 25 | use crate::test::test_circuit_mock; 26 | use halo2_proofs::pairing::bn256::Fr as Fp; 27 | 28 | #[test] 29 | fn test_binary_search_mock() { 30 | let trace = build_test().unwrap(); 31 | 32 | test_circuit_mock::(trace).unwrap(); 33 | } 34 | 35 | #[test] 36 | fn test_binary_search_full() { 37 | let execution_result = build_test().unwrap(); 38 | 39 | let builder = ZkWasmCircuitBuilder { 40 | tables: execution_result.tables, 41 | public_inputs_and_outputs: execution_result.public_inputs_and_outputs, 42 | }; 43 | 44 | builder.bench() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/test_fibonacci.rs: -------------------------------------------------------------------------------- 1 | use crate::runtime::host::host_env::HostEnv; 2 | use crate::runtime::wasmi_interpreter::WasmRuntimeIO; 3 | use crate::runtime::ExecutionResult; 4 | use anyhow::Result; 5 | use wasmi::RuntimeValue; 6 | 7 | use super::compile_then_execute_wasm; 8 | 9 | /* 10 | unsigned long long wasm_input(int); 11 | 12 | unsigned long long fib(unsigned long long n) 13 | { 14 | if (n <= 1) 15 | return n; 16 | return fib(n - 1) + fib(n - 2); 17 | } 18 | 19 | unsigned long long test() { 20 | unsigned long long input = wasm_input(1); 21 | return fib(input); 22 | } 23 | */ 24 | fn build_test() -> Result<(ExecutionResult, i32)> { 25 | let textual_repr = r#" 26 | (module 27 | (type (;0;) (func (param i32) (result i32))) 28 | (type (;1;) (func (result i32))) 29 | (func (;0;) (type 0) (param i32) (result i32) 30 | (local i32) 31 | local.get 0 32 | i32.const 2 33 | i32.ge_u 34 | if ;; label = @1 35 | loop ;; label = @2 36 | local.get 0 37 | i32.const 1 38 | i32.sub 39 | call 0 40 | local.get 1 41 | i32.add 42 | local.set 1 43 | local.get 0 44 | i32.const 2 45 | i32.sub 46 | local.tee 0 47 | i32.const 1 48 | i32.gt_u 49 | br_if 0 (;@2;) 50 | end 51 | end 52 | local.get 0 53 | local.get 1 54 | i32.add) 55 | (func (;1;) (type 1) (result i32) 56 | i32.const 10 57 | call 0) 58 | (memory (;0;) 2 2) 59 | (export "memory" (memory 0)) 60 | (export "zkmain" (func 1))) 61 | "#; 62 | 63 | let wasm = wabt::wat2wasm(&textual_repr).expect("failed to parse wat"); 64 | 65 | let mut env = HostEnv::new(); 66 | env.finalize(); 67 | 68 | let trace = compile_then_execute_wasm(env, WasmRuntimeIO::empty(), wasm, "zkmain")?; 69 | 70 | Ok((trace, 55)) 71 | } 72 | 73 | mod tests { 74 | use super::*; 75 | use crate::circuits::ZkWasmCircuitBuilder; 76 | use crate::test::test_circuit_mock; 77 | use halo2_proofs::pairing::bn256::Fr as Fp; 78 | 79 | #[test] 80 | fn test_fibonacci_mock() { 81 | let (trace, expected_value) = build_test().unwrap(); 82 | 83 | assert_eq!(trace.result.unwrap(), RuntimeValue::I32(expected_value)); 84 | 85 | test_circuit_mock::(trace).unwrap(); 86 | } 87 | 88 | #[test] 89 | fn test_fibonacci_full() { 90 | let (execution_result, expected_value) = build_test().unwrap(); 91 | 92 | assert_eq!( 93 | execution_result.result.unwrap(), 94 | RuntimeValue::I32(expected_value) 95 | ); 96 | 97 | let builder = ZkWasmCircuitBuilder { 98 | tables: execution_result.tables, 99 | public_inputs_and_outputs: execution_result.public_inputs_and_outputs, 100 | }; 101 | 102 | builder.bench() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/test_rlp.rs: -------------------------------------------------------------------------------- 1 | use crate::foreign::wasm_input_helper::runtime::register_wasm_input_foreign; 2 | use crate::runtime::host::host_env::HostEnv; 3 | use crate::runtime::ExecutionResult; 4 | 5 | use anyhow::Result; 6 | 7 | use super::compile_then_execute_wasm; 8 | 9 | fn build_test() -> Result> { 10 | let public_inputs = vec![133]; 11 | let private_inputs: Vec = vec![ 12 | 14625441452057167097, 13 | 441, 14 | 0, 15 | 0, 16 | 144115188084244480, 17 | 17592186044416, 18 | 0, 19 | 0, 20 | 2, 21 | 0, 22 | 281474976710656, 23 | 72057594037928224, 24 | 0, 25 | 144115188075855872, 26 | 4398046511104, 27 | 2048, 28 | 0, 29 | 288230376151711744, 30 | 562949953421312, 31 | 36033195065475072, 32 | 0, 33 | 1152921504606846992, 34 | 0, 35 | 72057594037927936, 36 | 0, 37 | 0, 38 | 72057594037927936, 39 | 274877906944, 40 | 0, 41 | 8192, 42 | 0, 43 | 0, 44 | 0, 45 | 142172368092004352, 46 | 10663670667014018268, 47 | 15598333267600830878, 48 | 4825637194728734969, 49 | 11537926770794296992, 50 | 8941585237026987872, 51 | 1060144843738714138, 52 | 15286290987074524363, 53 | 41041, 54 | 0, 55 | 0, 56 | 0, 57 | 549784760702, 58 | 0, 59 | 0, 60 | 13839280179932823552, 61 | 9466528, 62 | 0, 63 | 1245741926200423424, 64 | 9993052845762533317, 65 | 643603743268, 66 | 0, 67 | 0, 68 | 0, 69 | 687194767360, 70 | 0, 71 | 0, 72 | 0, 73 | 274894684160, 74 | 0, 75 | 17752714368831347629, 76 | 14734568103978781184, 77 | 16340025600, 78 | 0, 79 | 0, 80 | 0, 81 | 17179869184, 82 | 0, 83 | 0, 84 | 13839280179932823552, 85 | 9466528, 86 | 0, 87 | 0, 88 | 13839280179932823552, 89 | 9466528, 90 | 0, 91 | 0, 92 | 13839280179932823552, 93 | 9466528, 94 | 0, 95 | 0, 96 | 13983395368008679424, 97 | 180934170288, 98 | 0, 99 | 0, 100 | 0, 101 | 216736848758702080, 102 | 0, 103 | 0, 104 | 0, 105 | 10708425217887174656, 106 | 8187143887307480351, 107 | 70325280878010241, 108 | 117203507575396024, 109 | 11486502108844260361, 110 | 13539931197926996738, 111 | 18161434576524511916, 112 | 11561024771253616253, 113 | 0, 114 | 0, 115 | 0, 116 | 12789659991778787328, 117 | 160, 118 | 0, 119 | 0, 120 | 0, 121 | 40960, 122 | 0, 123 | 0, 124 | 15880255236061790208, 125 | 17950538412901046486, 126 | 8547692942764276983, 127 | 8509190860294355049, 128 | 5730928406529570843, 129 | 18210150271972058323, 130 | 3994395479395232905, 131 | 6563862530498629762, 132 | 688805136118, 133 | 0, 134 | 0, 135 | 13839280179932823552, 136 | 175921869910688, 137 | 0, 138 | 0, 139 | 0, 140 | 45231150997700608, 141 | 0, 142 | 0, 143 | 0, 144 | 43020438485336064, 145 | ]; 146 | 147 | let wasm = std::fs::read("wasm/rlp.wasm").unwrap(); 148 | 149 | let mut env = HostEnv::new(); 150 | let wasm_runtime_io = 151 | register_wasm_input_foreign(&mut env, public_inputs.clone(), private_inputs); 152 | env.finalize(); 153 | 154 | compile_then_execute_wasm(env, wasm_runtime_io, wasm, "zkmain") 155 | } 156 | 157 | mod tests { 158 | use super::*; 159 | use crate::circuits::config::set_zkwasm_k; 160 | use crate::circuits::ZkWasmCircuitBuilder; 161 | use crate::test::test_circuit_mock; 162 | use halo2_proofs::pairing::bn256::Fr as Fp; 163 | use rusty_fork::rusty_fork_test; 164 | 165 | rusty_fork_test! { 166 | #[test] 167 | fn test_rlp_mock() { 168 | set_zkwasm_k(20); 169 | 170 | let trace = build_test().unwrap(); 171 | 172 | test_circuit_mock::(trace).unwrap(); 173 | } 174 | } 175 | 176 | rusty_fork_test! { 177 | #[test] 178 | fn test_rlp_bench() { 179 | set_zkwasm_k(20); 180 | 181 | let execution_result = build_test().unwrap(); 182 | 183 | let builder = ZkWasmCircuitBuilder { 184 | tables: execution_result.tables, 185 | public_inputs_and_outputs: execution_result.public_inputs_and_outputs, 186 | }; 187 | 188 | builder.bench() 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/test/test_rlp_simple.rs: -------------------------------------------------------------------------------- 1 | use crate::foreign::wasm_input_helper::runtime::register_wasm_input_foreign; 2 | use crate::runtime::host::host_env::HostEnv; 3 | use crate::runtime::ExecutionResult; 4 | 5 | use super::compile_then_execute_wasm; 6 | use anyhow::Result; 7 | 8 | fn build_test() -> Result> { 9 | let public_inputs = vec![2, 2]; 10 | let private_inputs = vec![]; 11 | 12 | let wasm = std::fs::read("wasm/rlp_simple.wasm").unwrap(); 13 | 14 | let mut env = HostEnv::new(); 15 | let wasm_runtime_io = 16 | register_wasm_input_foreign(&mut env, public_inputs.clone(), private_inputs); 17 | env.finalize(); 18 | 19 | let trace = compile_then_execute_wasm(env, wasm_runtime_io, wasm, "zkmain")?; 20 | 21 | Ok(trace) 22 | } 23 | 24 | mod tests { 25 | use super::*; 26 | use crate::circuits::config::set_zkwasm_k; 27 | use crate::circuits::ZkWasmCircuitBuilder; 28 | use crate::test::test_circuit_mock; 29 | use halo2_proofs::pairing::bn256::Fr as Fp; 30 | use rusty_fork::rusty_fork_test; 31 | 32 | rusty_fork_test! { 33 | #[test] 34 | fn test_rlp_simple_mock() { 35 | set_zkwasm_k(20); 36 | 37 | let trace = build_test().unwrap(); 38 | 39 | test_circuit_mock::(trace).unwrap(); 40 | } 41 | } 42 | 43 | rusty_fork_test! { 44 | #[test] 45 | fn test_rlp_bench() { 46 | set_zkwasm_k(20); 47 | 48 | let execution_result = build_test().unwrap(); 49 | 50 | let builder = ZkWasmCircuitBuilder { 51 | tables: execution_result.tables, 52 | public_inputs_and_outputs:execution_result.public_inputs_and_outputs, 53 | }; 54 | 55 | builder.bench() 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/test_start.rs: -------------------------------------------------------------------------------- 1 | mod tests { 2 | use crate::test::test_circuit_noexternal; 3 | 4 | #[test] 5 | fn test_start_mock() { 6 | let textual_repr = r#" 7 | (module 8 | (func (;0;) 9 | i32.const 0 10 | drop 11 | ) 12 | 13 | (func (;1;) 14 | i32.const 1 15 | drop 16 | ) 17 | 18 | (start 0) 19 | (export "test" (func 1)) 20 | ) 21 | "#; 22 | 23 | test_circuit_noexternal(textual_repr).unwrap(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/mod.rs: -------------------------------------------------------------------------------- 1 | mod op_bin; 2 | mod op_bin_bit; 3 | mod op_bin_shift; 4 | mod op_br; 5 | mod op_br_if; 6 | mod op_br_if_eqz; 7 | mod op_br_table; 8 | mod op_call; 9 | mod op_call_host; 10 | mod op_call_indirect; 11 | mod op_const; 12 | mod op_conversion; 13 | mod op_global_get; 14 | mod op_global_set; 15 | mod op_load; 16 | mod op_local_get; 17 | mod op_local_set; 18 | mod op_local_tee; 19 | mod op_memory_grow; 20 | mod op_memory_size; 21 | mod op_rel; 22 | mod op_return; 23 | mod op_select; 24 | mod op_store; 25 | mod op_test; 26 | mod op_unary; 27 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_bin_bit.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_bin_bit() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (i32.const 1) 9 | (i32.const 1) 10 | (i32.xor) 11 | (drop) 12 | (i32.const 21) 13 | (i32.const 31) 14 | (i32.xor) 15 | (drop) 16 | (i64.const 1) 17 | (i64.const 1) 18 | (i64.xor) 19 | (drop) 20 | (i64.const 21) 21 | (i64.const 31) 22 | (i64.xor) 23 | (drop) 24 | 25 | (i32.const 21) 26 | (i32.const 31) 27 | (i32.and) 28 | (drop) 29 | (i64.const 21) 30 | (i64.const 31) 31 | (i64.and) 32 | (drop) 33 | 34 | (i32.const 21) 35 | (i32.const 31) 36 | (i32.or) 37 | (drop) 38 | (i64.const 21) 39 | (i64.const 31) 40 | (i64.or) 41 | (drop) 42 | ) 43 | ) 44 | "#; 45 | 46 | test_circuit_noexternal(textual_repr).unwrap() 47 | } 48 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_br.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_br_ok() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (block 9 | br 0 10 | ) 11 | ) 12 | ) 13 | "#; 14 | 15 | test_circuit_noexternal(textual_repr).unwrap(); 16 | } 17 | 18 | #[test] 19 | fn test_br_drop_ok() { 20 | let textual_repr = r#" 21 | (module 22 | (func (export "test") 23 | (block 24 | (i32.const 0) 25 | br 0 26 | ) 27 | ) 28 | ) 29 | "#; 30 | 31 | test_circuit_noexternal(textual_repr).unwrap(); 32 | } 33 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_br_if.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_br_if_trivial_nojump_ok() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (block 9 | (i32.const 0) 10 | br_if 0 11 | ) 12 | ) 13 | ) 14 | "#; 15 | 16 | test_circuit_noexternal(textual_repr).unwrap(); 17 | } 18 | 19 | #[test] 20 | fn test_br_if_trivial_jump_ok() { 21 | let textual_repr = r#" 22 | (module 23 | (func (export "test") 24 | (block 25 | (i32.const 1) 26 | br_if 0 27 | (i32.const 0) 28 | drop 29 | ) 30 | ) 31 | ) 32 | "#; 33 | 34 | test_circuit_noexternal(textual_repr).unwrap(); 35 | } 36 | 37 | #[test] 38 | fn test_br_if_block_with_arg_do_not_jump_ok() { 39 | let textual_repr = r#" 40 | (module 41 | (func (export "test") 42 | (block (result i32) 43 | (i32.const 0) 44 | (i32.const 0) 45 | br_if 0 46 | ) 47 | drop 48 | ) 49 | ) 50 | "#; 51 | 52 | test_circuit_noexternal(textual_repr).unwrap(); 53 | } 54 | 55 | #[test] 56 | fn test_br_if_block_with_arg_do_jump_ok() { 57 | let textual_repr = r#" 58 | (module 59 | (func (export "test") 60 | (block (result i32) 61 | (i32.const 0) 62 | (i32.const 1) 63 | br_if 0 64 | ) 65 | drop 66 | ) 67 | ) 68 | "#; 69 | 70 | test_circuit_noexternal(textual_repr).unwrap(); 71 | } 72 | 73 | #[test] 74 | fn test_br_if_block_with_drop_do_not_jump_ok() { 75 | let textual_repr = r#" 76 | (module 77 | (func (export "test") 78 | (block 79 | (block 80 | (i32.const 0) 81 | (i32.const 0) 82 | (i32.const 0) 83 | br_if 1 84 | drop 85 | drop 86 | ) 87 | ) 88 | ) 89 | ) 90 | "#; 91 | 92 | test_circuit_noexternal(textual_repr).unwrap(); 93 | } 94 | 95 | #[test] 96 | fn test_br_if_block_with_drop_do_jump_ok() { 97 | let textual_repr = r#" 98 | (module 99 | (func (export "test") 100 | (block 101 | (block 102 | (i32.const 0) 103 | (i32.const 0) 104 | (i32.const 1) 105 | br_if 1 106 | drop 107 | drop 108 | ) 109 | ) 110 | ) 111 | ) 112 | "#; 113 | 114 | test_circuit_noexternal(textual_repr).unwrap(); 115 | } 116 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_br_if_eqz.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_br_if_eqz_ok() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (block 9 | (if (i32.const 0) 10 | (then (br 0)) 11 | (else (br 0)) 12 | ) 13 | ) 14 | ) 15 | ) 16 | "#; 17 | 18 | test_circuit_noexternal(textual_repr).unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_br_table.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_br_table_1() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") (result i32) 8 | (block 9 | (block 10 | (block 11 | (block 12 | (block 13 | (br_table 3 2 1 0 4 (i32.const 0)) 14 | (return (i32.const 99)) 15 | ) 16 | (return (i32.const 100)) 17 | ) 18 | (return (i32.const 101)) 19 | ) 20 | (return (i32.const 102)) 21 | ) 22 | (return (i32.const 103)) 23 | ) 24 | (i32.const 104) 25 | ) 26 | ) 27 | "#; 28 | 29 | test_circuit_noexternal(textual_repr).unwrap(); 30 | } 31 | 32 | #[test] 33 | fn test_br_table_2() { 34 | let textual_repr = r#" 35 | (module 36 | (func (export "test") (result i32) 37 | (block 38 | (block 39 | (block 40 | (block 41 | (block 42 | (br_table 3 2 1 0 4 (i32.const 4)) 43 | (return (i32.const 99)) 44 | ) 45 | (return (i32.const 100)) 46 | ) 47 | (return (i32.const 101)) 48 | ) 49 | (return (i32.const 102)) 50 | ) 51 | (return (i32.const 103)) 52 | ) 53 | (i32.const 104) 54 | ) 55 | ) 56 | "#; 57 | 58 | test_circuit_noexternal(textual_repr).unwrap(); 59 | } 60 | 61 | #[test] 62 | fn test_br_table_oob_1() { 63 | let textual_repr = r#" 64 | (module 65 | (func (export "test") (result i32) 66 | (block 67 | (block 68 | (block 69 | (block 70 | (block 71 | (br_table 3 2 1 0 4 (i32.const 5)) 72 | (return (i32.const 99)) 73 | ) 74 | (return (i32.const 100)) 75 | ) 76 | (return (i32.const 101)) 77 | ) 78 | (return (i32.const 102)) 79 | ) 80 | (return (i32.const 103)) 81 | ) 82 | (i32.const 104) 83 | ) 84 | ) 85 | "#; 86 | 87 | test_circuit_noexternal(textual_repr).unwrap(); 88 | } 89 | 90 | #[test] 91 | fn test_br_table_oob_2() { 92 | let textual_repr = r#" 93 | (module 94 | (func (export "test") (result i32) 95 | (block 96 | (block 97 | (block 98 | (block 99 | (block 100 | (br_table 3 2 1 0 4 (i32.const 99)) 101 | (return (i32.const 99)) 102 | ) 103 | (return (i32.const 100)) 104 | ) 105 | (return (i32.const 101)) 106 | ) 107 | (return (i32.const 102)) 108 | ) 109 | (return (i32.const 103)) 110 | ) 111 | (i32.const 104) 112 | ) 113 | ) 114 | "#; 115 | 116 | test_circuit_noexternal(textual_repr).unwrap(); 117 | } 118 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_call.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_call() { 5 | let textual_repr = r#" 6 | (module 7 | (func $foo (param i32) (result i32) 8 | (local i64 i32) 9 | i32.const 0 10 | ) 11 | (func (export "test") 12 | (i32.const 0) 13 | call $foo 14 | drop 15 | ) 16 | ) 17 | "#; 18 | 19 | test_circuit_noexternal(textual_repr).unwrap() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_call_host.rs: -------------------------------------------------------------------------------- 1 | use specs::external_host_call_table::ExternalHostCallSignature; 2 | use std::rc::Rc; 3 | 4 | use crate::runtime::host::host_env::HostEnv; 5 | use crate::runtime::host::ForeignContext; 6 | use crate::runtime::wasmi_interpreter::WasmRuntimeIO; 7 | use crate::test::test_circuit_with_env; 8 | 9 | #[derive(Default)] 10 | struct Context { 11 | acc: u64, 12 | } 13 | impl ForeignContext for Context {} 14 | 15 | #[test] 16 | fn test_call_host_external() { 17 | let textual_repr = r#" 18 | (module 19 | (type (;0;) (func (result i64))) 20 | (type (;1;) (func (param i64))) 21 | (import "env" "foreign_push" (func (;0;) (type 1))) 22 | (import "env" "foreign_pop" (func (;1;) (type 0))) 23 | (func (;2;) (type 0) (result i64) 24 | i64.const 5 25 | call 0 26 | i64.const 10 27 | call 0 28 | i64.const 3 29 | call 0 30 | call 1) 31 | (memory (;0;) 1) 32 | (export "memory" (memory 0)) 33 | (export "test" (func 2))) 34 | "#; 35 | 36 | let env = { 37 | let mut env = HostEnv::new(); 38 | 39 | let foreign_playground_plugin = env 40 | .external_env 41 | .register_plugin("foreign_playground", Box::new(Context::default())); 42 | env.external_env.register_function( 43 | "foreign_push", 44 | 0, 45 | ExternalHostCallSignature::Argument, 46 | foreign_playground_plugin.clone(), 47 | Rc::new( 48 | |context: &mut dyn ForeignContext, args: wasmi::RuntimeArgs| { 49 | let context = context.downcast_mut::().unwrap(); 50 | 51 | let value: u64 = args.nth(0); 52 | context.acc += value; 53 | 54 | None 55 | }, 56 | ), 57 | ); 58 | env.external_env.register_function( 59 | "foreign_pop", 60 | 1, 61 | ExternalHostCallSignature::Return, 62 | foreign_playground_plugin, 63 | Rc::new( 64 | |context: &mut dyn ForeignContext, _args: wasmi::RuntimeArgs| { 65 | let context = context.downcast_mut::().unwrap(); 66 | 67 | Some(wasmi::RuntimeValue::I64(context.acc as i64)) 68 | }, 69 | ), 70 | ); 71 | 72 | env.finalize(); 73 | 74 | env 75 | }; 76 | 77 | let wasm = wabt::wat2wasm(&textual_repr).expect("failed to parse wat"); 78 | test_circuit_with_env(env, WasmRuntimeIO::empty(), wasm, "test").unwrap(); 79 | } 80 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_call_indirect.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_call_indirect() { 5 | let textual_repr = r#" 6 | (module 7 | (type (;0;) (func (param i32 i32) (result i32))) 8 | (func (;0;) (type 0) (param i32 i32) (result i32) 9 | local.get 0 10 | local.get 1 11 | i32.add) 12 | (func (;1;) (type 0) (param i32 i32) (result i32) 13 | local.get 0 14 | local.get 1 15 | i32.sub) 16 | (func (;2;) (result i32) 17 | i32.const 1 18 | i32.const 2 19 | i32.const 1 20 | call_indirect (type 0)) 21 | (table (;0;) 2 2 funcref) 22 | (export "test" (func 2)) 23 | (elem (;0;) (i32.const 0) func 0 1) 24 | ) 25 | "#; 26 | 27 | test_circuit_noexternal(textual_repr).unwrap() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_const.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_const() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (i32.const 0) 9 | (drop) 10 | (i64.const 0) 11 | (drop) 12 | ) 13 | ) 14 | "#; 15 | 16 | test_circuit_noexternal(textual_repr).unwrap(); 17 | } 18 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_conversion.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_i32_wrap_i64_ok() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (i64.const 0) 9 | (i32.wrap_i64) 10 | (drop) 11 | (i64.const 0xffffffff00000000) 12 | (i32.wrap_i64) 13 | (drop) 14 | (i64.const 0xfffffffff0f0f0f0) 15 | (i32.wrap_i64) 16 | (drop) 17 | ) 18 | ) 19 | "#; 20 | 21 | test_circuit_noexternal(textual_repr).unwrap() 22 | } 23 | 24 | #[test] 25 | fn test_i64_extend_i32_u_ok() { 26 | let textual_repr = r#" 27 | (module 28 | (func (export "test") 29 | (i32.const 0) 30 | (i64.extend_i32_u) 31 | (drop) 32 | (i32.const -1) 33 | (i64.extend_i32_u) 34 | (drop) 35 | ) 36 | ) 37 | "#; 38 | 39 | test_circuit_noexternal(textual_repr).unwrap() 40 | } 41 | 42 | #[test] 43 | fn test_i64_extend_i32_s_ok() { 44 | let textual_repr = r#" 45 | (module 46 | (func (export "test") 47 | (i32.const 0) 48 | (i64.extend_i32_s) 49 | (drop) 50 | 51 | (i32.const 0x7fffffff) 52 | (i64.extend_i32_s) 53 | (drop) 54 | 55 | (i32.const -1) 56 | (i64.extend_i32_s) 57 | (drop) 58 | 59 | (i32.const 0xffffffff) 60 | (i64.extend_i32_s) 61 | (drop) 62 | ) 63 | ) 64 | "#; 65 | 66 | test_circuit_noexternal(textual_repr).unwrap() 67 | } 68 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_global_get.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_global_get() { 5 | let textual_repr = r#" 6 | (module 7 | (global $global_i32 i32 (i32.const 0)) 8 | (global $global_i64 i64 (i64.const 0)) 9 | 10 | (func (export "test") 11 | (global.get $global_i32) 12 | (drop) 13 | (global.get $global_i64) 14 | (drop) 15 | ) 16 | ) 17 | "#; 18 | 19 | test_circuit_noexternal(textual_repr).unwrap() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_global_set.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_global_set() { 5 | let textual_repr = r#" 6 | (module 7 | (global $global_i32 (mut i32) (i32.const 10)) 8 | (global $global_i64 (mut i64) (i64.const 10)) 9 | 10 | (func (export "test") 11 | (i32.const 0) 12 | (global.set $global_i32) 13 | (i64.const 0) 14 | (global.set $global_i64) 15 | ) 16 | ) 17 | "#; 18 | 19 | test_circuit_noexternal(textual_repr).unwrap() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_load.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_load_normal() { 5 | let textual_repr = r#" 6 | (module 7 | (memory $0 1) 8 | (data (i32.const 0) "\ff\00\00\00\fe\00\00\00") 9 | (func (export "test") 10 | (i32.const 0) 11 | (i64.load offset=0) 12 | (drop) 13 | (i32.const 4) 14 | (i64.load offset=4) 15 | (drop) 16 | (i32.const 0) 17 | (i64.load32_u offset=0) 18 | (drop) 19 | (i32.const 0) 20 | (i64.load32_s offset=0) 21 | (drop) 22 | (i32.const 0) 23 | (i64.load16_u offset=0) 24 | (drop) 25 | (i32.const 0) 26 | (i64.load16_s offset=0) 27 | (drop) 28 | (i32.const 0) 29 | (i64.load8_u offset=0) 30 | (drop) 31 | (i32.const 0) 32 | (i64.load8_s offset=0) 33 | (drop) 34 | 35 | (i32.const 0) 36 | (i32.load offset=0) 37 | (drop) 38 | (i32.const 4) 39 | (i32.load offset=0) 40 | (drop) 41 | (i32.const 0) 42 | (i32.load16_u offset=0) 43 | (drop) 44 | (i32.const 0) 45 | (i32.load16_s offset=0) 46 | (drop) 47 | (i32.const 0) 48 | (i32.load8_u offset=0) 49 | (drop) 50 | (i32.const 0) 51 | (i32.load8_s offset=0) 52 | (drop) 53 | ) 54 | ) 55 | "#; 56 | 57 | test_circuit_noexternal(textual_repr).unwrap(); 58 | } 59 | 60 | #[test] 61 | fn test_load_cross() { 62 | let textual_repr = r#" 63 | (module 64 | (memory $0 1) 65 | (data (i32.const 0) "\ff\00\00\00\fe\00\00\00\fd\00\00\00\fc\00\00\00") 66 | (func (export "test") 67 | (i32.const 4) 68 | (i64.load offset=0) 69 | (drop) 70 | (i32.const 6) 71 | (i64.load32_u offset=0) 72 | (drop) 73 | (i32.const 7) 74 | (i64.load16_u offset=0) 75 | (drop) 76 | 77 | (i32.const 6) 78 | (i32.load offset=0) 79 | (drop) 80 | (i32.const 7) 81 | (i32.load16_u offset=0) 82 | (drop) 83 | ) 84 | ) 85 | "#; 86 | 87 | test_circuit_noexternal(textual_repr).unwrap(); 88 | } 89 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_local_get.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_local_get() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (local i32 i64) 9 | (local.get 0) 10 | (drop) 11 | (local.get 1) 12 | (drop) 13 | 14 | ) 15 | ) 16 | "#; 17 | 18 | test_circuit_noexternal(textual_repr).unwrap() 19 | } 20 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_local_set.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::test::test_circuit_noexternal; 4 | 5 | #[test] 6 | fn test_local_set() { 7 | let textual_repr = r#" 8 | (module 9 | (func (export "test") 10 | (local i32 i64) 11 | (i32.const 0) 12 | (local.set 0) 13 | (i64.const 0) 14 | (local.set 1) 15 | ) 16 | ) 17 | "#; 18 | 19 | test_circuit_noexternal(textual_repr).unwrap() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_local_tee.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_local_tee() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (local i32 i64) 9 | (i32.const 0) 10 | (local.tee 0) 11 | (drop) 12 | (i64.const 0) 13 | (local.tee 1) 14 | (drop) 15 | ) 16 | ) 17 | "#; 18 | 19 | test_circuit_noexternal(textual_repr).unwrap() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_memory_grow.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_memory_grow() { 5 | let textual_repr = r#" 6 | (module 7 | (memory 1 2) 8 | 9 | (func (export "test") 10 | (memory.grow (i32.const 1)) 11 | (drop) 12 | ) 13 | ) 14 | "#; 15 | 16 | test_circuit_noexternal(textual_repr).unwrap() 17 | } 18 | 19 | #[test] 20 | fn test_memory_grow_fail() { 21 | let textual_repr = r#" 22 | (module 23 | (memory 1 2) 24 | 25 | (func (export "test") 26 | (memory.grow (i32.const 2)) 27 | (drop) 28 | ) 29 | ) 30 | "#; 31 | 32 | test_circuit_noexternal(textual_repr).unwrap() 33 | } 34 | 35 | #[test] 36 | fn test_memory_grow_lazy_init() { 37 | let textual_repr = r#" 38 | (module 39 | (memory 0 1) 40 | 41 | (func (export "test") 42 | (memory.grow (i32.const 1)) 43 | (drop) 44 | (i32.const 0) 45 | (i32.load offset=0) 46 | (drop) 47 | ) 48 | ) 49 | "#; 50 | 51 | test_circuit_noexternal(textual_repr).unwrap() 52 | } 53 | 54 | #[test] 55 | fn test_memory_grow_lazy_init_2() { 56 | let textual_repr = r#" 57 | (module 58 | (memory 1 2) 59 | 60 | (func (export "test") 61 | (memory.grow (i32.const 1)) 62 | (drop) 63 | (i32.const 65536) 64 | (i32.load offset=0) 65 | (drop) 66 | ) 67 | ) 68 | "#; 69 | 70 | test_circuit_noexternal(textual_repr).unwrap() 71 | } 72 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_memory_size.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_memory_size() { 5 | let textual_repr = r#" 6 | (module 7 | (memory 2) 8 | 9 | (func (export "test") 10 | (memory.size) 11 | (drop) 12 | ) 13 | ) 14 | "#; 15 | 16 | test_circuit_noexternal(textual_repr).unwrap() 17 | } 18 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_rel.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | use crate::test::test_circuit_noexternal; 4 | 5 | #[test] 6 | fn test_op_rel() { 7 | let tys = vec![ 8 | ("i32", vec!["0", "1", "2", "-1", "-2", "0x80000000"]), 9 | ( 10 | "i64", 11 | vec![ 12 | "0", 13 | "1", 14 | "2", 15 | "-1", 16 | "-2", 17 | "-0x100000001", 18 | "-0x100000002", 19 | "0x100000001", 20 | "0x100000002", 21 | "0x8000000000000000", 22 | ], 23 | ), 24 | ]; 25 | let ops = vec![ 26 | "gt_u", "ge_u", "lt_u", "le_u", "eq", "ne", "gt_s", "ge_s", "lt_s", "le_s", 27 | ]; 28 | 29 | let mut textual_repr = r#" 30 | (module 31 | (func (export "test")"# 32 | .to_owned(); 33 | 34 | for (t, values) in tys { 35 | for op in ops.iter() { 36 | for l in values.iter() { 37 | for r in values.iter() { 38 | textual_repr = format!( 39 | r#"{} 40 | ({}.const {}) 41 | ({}.const {}) 42 | ({}.{}) 43 | (drop) 44 | "#, 45 | textual_repr, t, l, t, r, t, op 46 | ); 47 | } 48 | } 49 | } 50 | } 51 | 52 | textual_repr = format!("{}))", textual_repr); 53 | test_circuit_noexternal(&textual_repr).unwrap() 54 | } 55 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_return.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_trivial_return() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | return 9 | ) 10 | ) 11 | "#; 12 | 13 | test_circuit_noexternal(textual_repr).unwrap(); 14 | } 15 | 16 | #[test] 17 | fn test_return_with_drop_ok() { 18 | let textual_repr = r#" 19 | (module 20 | (func (export "test") 21 | (block 22 | (i32.const 0) 23 | (i32.const 0) 24 | return 25 | ) 26 | ) 27 | ) 28 | "#; 29 | 30 | test_circuit_noexternal(textual_repr).unwrap(); 31 | } 32 | 33 | #[test] 34 | fn test_return_with_keep_ok() { 35 | let textual_repr = r#" 36 | (module 37 | (func (export "test") (result i32) 38 | (i32.const 0) 39 | ) 40 | ) 41 | "#; 42 | 43 | test_circuit_noexternal(textual_repr).unwrap(); 44 | } 45 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_select.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_select() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (i32.const 1) 9 | (i32.const 2) 10 | (i32.const 0) 11 | select 12 | drop 13 | (i64.const 1) 14 | (i64.const 2) 15 | (i32.const 0) 16 | select 17 | drop 18 | ) 19 | ) 20 | "#; 21 | 22 | test_circuit_noexternal(textual_repr).unwrap(); 23 | } 24 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_store.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_store_normal() { 5 | let textual_repr = r#" 6 | (module 7 | (memory $0 1) 8 | (data (i32.const 0) "\ff\00\00\00\fe\00\00\00") 9 | (func (export "test") 10 | (i32.const 0) 11 | (i64.const 0) 12 | (i64.store offset=0) 13 | (i32.const 0) 14 | (i32.const 0) 15 | (i32.store offset=4) 16 | (i32.const 0) 17 | (i64.const 0x432134214) 18 | (i64.store offset=0) 19 | (i32.const 0) 20 | (i64.const 0) 21 | (i64.store32 offset=0) 22 | (i32.const 0) 23 | (i64.const 0) 24 | (i64.store16 offset=0) 25 | (i32.const 0) 26 | (i64.const 0) 27 | (i64.store8 offset=0) 28 | 29 | (i32.const 0) 30 | (i32.const 0) 31 | (i32.store offset=0) 32 | (i32.const 4) 33 | (i32.const 0) 34 | (i32.store offset=0) 35 | (i32.const 0) 36 | (i32.const 0) 37 | (i32.store16 offset=0) 38 | (i32.const 0) 39 | (i32.const 0) 40 | (i32.store8 offset=0) 41 | (i32.const 0) 42 | (i32.const 256) 43 | (i32.store8 offset=0) 44 | ) 45 | ) 46 | "#; 47 | 48 | test_circuit_noexternal(textual_repr).unwrap(); 49 | } 50 | 51 | #[test] 52 | fn test_store_cross() { 53 | let textual_repr = r#" 54 | (module 55 | (memory $0 1) 56 | (data (i32.const 0) "\ff\00\00\00\fe\00\00\00") 57 | (func (export "test") 58 | (i32.const 6) 59 | (i64.const 32) 60 | (i64.store32 offset=0) 61 | 62 | (i32.const 4) 63 | (i64.const 64) 64 | (i64.store offset=0) 65 | 66 | (i32.const 7) 67 | (i64.const 16) 68 | (i64.store16 offset=0) 69 | 70 | (i32.const 6) 71 | (i32.const 32) 72 | (i32.store offset=0) 73 | 74 | (i32.const 7) 75 | (i32.const 16) 76 | (i32.store16 offset=0) 77 | ) 78 | ) 79 | "#; 80 | 81 | test_circuit_noexternal(textual_repr).unwrap(); 82 | } 83 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_test.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_eqz() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (i32.const 0) 9 | (i32.eqz) 10 | (drop) 11 | 12 | (i32.const 1) 13 | (i32.eqz) 14 | (drop) 15 | 16 | (i64.const 0) 17 | (i64.eqz) 18 | (drop) 19 | 20 | (i64.const 1) 21 | (i64.eqz) 22 | (drop) 23 | ) 24 | ) 25 | "#; 26 | 27 | test_circuit_noexternal(textual_repr).unwrap() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/test_wasm_instructions/op_unary.rs: -------------------------------------------------------------------------------- 1 | use crate::test::test_circuit_noexternal; 2 | 3 | #[test] 4 | fn test_ctz() { 5 | let textual_repr = r#" 6 | (module 7 | (func (export "test") 8 | (i32.const 0x00100000) 9 | (i32.ctz) 10 | (drop) 11 | 12 | (i32.const 0x00000001) 13 | (i32.ctz) 14 | (drop) 15 | 16 | (i32.const 0x80000000) 17 | (i32.ctz) 18 | (drop) 19 | 20 | (i32.const 0x00000000) 21 | (i32.ctz) 22 | (drop) 23 | 24 | (i64.const 0x0010000000000000) 25 | (i64.ctz) 26 | (drop) 27 | 28 | (i64.const 0x0000000000000001) 29 | (i64.ctz) 30 | (drop) 31 | 32 | (i64.const 0x8000000000000000) 33 | (i64.ctz) 34 | (drop) 35 | 36 | (i64.const 0x0000000000000000) 37 | (i64.ctz) 38 | (drop) 39 | ) 40 | ) 41 | "#; 42 | 43 | test_circuit_noexternal(textual_repr).unwrap() 44 | } 45 | 46 | #[test] 47 | fn test_clz() { 48 | let textual_repr = r#" 49 | (module 50 | (func (export "test") 51 | (i32.const 0x00000001) 52 | (i32.clz) 53 | (drop) 54 | 55 | (i32.const 0x80000000) 56 | (i32.clz) 57 | (drop) 58 | 59 | (i32.const 0x00000000) 60 | (i32.clz) 61 | (drop) 62 | 63 | (i32.const 0xffffffff) 64 | (i32.clz) 65 | (drop) 66 | 67 | (i64.const 0x0000000000000001) 68 | (i64.clz) 69 | (drop) 70 | 71 | (i64.const 0x8000000000000000) 72 | (i64.clz) 73 | (drop) 74 | 75 | (i64.const 0x0000000000000000) 76 | (i64.clz) 77 | (drop) 78 | 79 | (i64.const 0xffffffffffffffff) 80 | (i64.clz) 81 | (drop) 82 | ) 83 | ) 84 | "#; 85 | 86 | test_circuit_noexternal(textual_repr).unwrap() 87 | } 88 | 89 | #[test] 90 | fn test_popcnt() { 91 | let textual_repr = r#" 92 | (module 93 | (func (export "test") 94 | (i32.const 0x00000000) 95 | (i32.popcnt) 96 | (drop) 97 | 98 | (i32.const 0xffffffff) 99 | (i32.popcnt) 100 | (drop) 101 | 102 | (i64.const 0x0000000000000000) 103 | (i64.popcnt) 104 | (drop) 105 | 106 | (i64.const 0xffffffffffffffff) 107 | (i64.popcnt) 108 | (drop) 109 | ) 110 | ) 111 | "#; 112 | 113 | test_circuit_noexternal(textual_repr).unwrap() 114 | } 115 | -------------------------------------------------------------------------------- /src/traits/circuits/bit_range_table.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::arithmetic::FieldExt; 2 | use halo2_proofs::plonk::Advice; 3 | use halo2_proofs::plonk::Column; 4 | use halo2_proofs::plonk::ConstraintSystem; 5 | use halo2_proofs::plonk::Expression; 6 | use halo2_proofs::plonk::VirtualCells; 7 | 8 | use crate::constant_from; 9 | use crate::curr; 10 | 11 | #[derive(Clone)] 12 | pub struct U4Column(pub Column); 13 | 14 | #[derive(Clone)] 15 | pub struct U8Column(pub Column); 16 | 17 | #[derive(Clone)] 18 | pub struct BitColumn(pub Column); 19 | 20 | #[derive(Clone)] 21 | pub struct U4PartialColumn(pub Column); 22 | 23 | #[derive(Clone)] 24 | pub struct U8PartialColumn(pub Column); 25 | 26 | #[derive(Clone)] 27 | pub struct BitPartialColumn(pub Column); 28 | 29 | pub trait BitRangeTable { 30 | fn configure_in_u4_range( 31 | &self, 32 | meta: &mut ConstraintSystem, 33 | key: &'static str, 34 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 35 | ); 36 | fn configure_in_u8_range( 37 | &self, 38 | meta: &mut ConstraintSystem, 39 | key: &'static str, 40 | expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 41 | ); 42 | fn u4_partial_column( 43 | &self, 44 | meta: &mut ConstraintSystem, 45 | key: &'static str, 46 | filter: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 47 | ) -> U4PartialColumn { 48 | let col = meta.advice_column(); 49 | self.configure_in_u4_range(meta, key, |meta| curr!(meta, col) * filter(meta)); 50 | U4PartialColumn(col) 51 | } 52 | fn u8_partial_column( 53 | &self, 54 | meta: &mut ConstraintSystem, 55 | key: &'static str, 56 | filter: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 57 | ) -> U8PartialColumn { 58 | let col = meta.advice_column(); 59 | self.configure_in_u8_range(meta, key, |meta| curr!(meta, col) * filter(meta)); 60 | U8PartialColumn(col) 61 | } 62 | fn bit_partial_column( 63 | &self, 64 | meta: &mut ConstraintSystem, 65 | key: &'static str, 66 | filter: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 67 | ) -> BitPartialColumn { 68 | let col = meta.advice_column(); 69 | meta.create_gate(key, |meta| { 70 | vec![curr!(meta, col) * (constant_from!(1) - curr!(meta, col)) * filter(meta)] 71 | }); 72 | BitPartialColumn(col) 73 | } 74 | fn u4_column( 75 | &self, 76 | meta: &mut ConstraintSystem, 77 | key: &'static str, 78 | sel: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 79 | ) -> U4Column { 80 | let col = meta.advice_column(); 81 | self.configure_in_u4_range(meta, key, |meta| sel(meta) * curr!(meta, col)); 82 | U4Column(col) 83 | } 84 | fn u8_column( 85 | &self, 86 | meta: &mut ConstraintSystem, 87 | key: &'static str, 88 | sel: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 89 | ) -> U8Column { 90 | let col = meta.advice_column(); 91 | self.configure_in_u8_range(meta, key, |meta| sel(meta) * curr!(meta, col)); 92 | U8Column(col) 93 | } 94 | fn bit_column( 95 | &self, 96 | meta: &mut ConstraintSystem, 97 | key: &'static str, 98 | sel: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, 99 | ) -> BitColumn { 100 | let col = meta.advice_column(); 101 | meta.create_gate(key, |meta| { 102 | vec![sel(meta) * curr!(meta, col) * (constant_from!(1) - curr!(meta, col))] 103 | }); 104 | BitColumn(col) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/traits/circuits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bit_range_table; 2 | -------------------------------------------------------------------------------- /src/traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod circuits; 2 | -------------------------------------------------------------------------------- /test_cli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | rm -rf output/*.data 7 | 8 | # Single test 9 | RUST_LOG=info cargo run --release --features cuda -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm setup 10 | 11 | RUST_LOG=info cargo run --release --features cuda -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm single-prove --public 133:i64 --public 2:i64 12 | RUST_LOG=info cargo run --release --features cuda -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm single-verify --proof output/zkwasm.0.transcript.data --instance output/zkwasm.0.instance.data 13 | RUST_LOG=info cargo run --release --features cuda -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm aggregate-prove --public 133:i64 --public 2:i64 14 | RUST_LOG=info cargo run --release --features cuda -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm aggregate-verify --proof output/aggregate-circuit.0.transcript.data --instances output/aggregate-circuit.0.instance.data 15 | if [ -d "sol" ]; then 16 | RUST_LOG=info cargo run --release --features cuda -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm solidity-aggregate-verifier --proof output/aggregate-circuit.0.transcript.data --instances output/aggregate-circuit.0.instance.data 17 | fi -------------------------------------------------------------------------------- /test_cli_checksum.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | rm -rf output/*.data 7 | 8 | # Single test 9 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm setup 10 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm checksum 11 | 12 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm dry-run --public 133:i64 --public 2:i64 13 | 14 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm single-prove --public 133:i64 --public 2:i64 15 | 16 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm single-verify --instance output/zkwasm.0.instance.data --proof output/zkwasm.0.transcript.data 17 | 18 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm aggregate-prove --public 133:i64 --public 2:i64 19 | 20 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm aggregate-verify --proof output/aggregate-circuit.0.transcript.data --instances output/aggregate-circuit.0.instance.data 21 | if [ -d "sol" ]; then 22 | RUST_LOG=info cargo run --release --features cuda --features checksum -- -k 18 --function zkmain --output ./output --wasm wasm/wasm_output.wasm solidity-aggregate-verifier --proof output/aggregate-circuit.0.transcript.data --instances output/aggregate-circuit.0.instance.data 23 | fi -------------------------------------------------------------------------------- /wasm/bsearch_64.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/bsearch_64.wasm -------------------------------------------------------------------------------- /wasm/keccak.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/keccak.wasm -------------------------------------------------------------------------------- /wasm/rlp.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/rlp.wasm -------------------------------------------------------------------------------- /wasm/rlp_simple.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/rlp_simple.wasm -------------------------------------------------------------------------------- /wasm/sha256.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/sha256.wasm -------------------------------------------------------------------------------- /wasm/sha256_v2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/sha256_v2.wasm -------------------------------------------------------------------------------- /wasm/wasm_output.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/wasm/wasm_output.wasm -------------------------------------------------------------------------------- /zkwasm-bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/zkwasm-bk.png -------------------------------------------------------------------------------- /zkwasm-wh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zikc2023/zkWasm/b0313d7641fe93e0912e05e0220021c1fd825614/zkwasm-wh.png --------------------------------------------------------------------------------