From 9bdde49e60234655b6b61c1af165389f83f81975 Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Thu, 13 Aug 2026 17:14:29 +0530 Subject: [PATCH 1/7] feat: sha2 code is added Signed-off-by: arijitdutta67 --- .../src/main/lib/sha2/constants.zkc | 91 ++++++++++ arithmetization/src/main/lib/sha2/impl.zkc | 162 ++++++++++++++++++ arithmetization/src/main/lib/sha2/utils.zkc | 46 +++++ 3 files changed, 299 insertions(+) create mode 100644 arithmetization/src/main/lib/sha2/constants.zkc create mode 100644 arithmetization/src/main/lib/sha2/impl.zkc create mode 100644 arithmetization/src/main/lib/sha2/utils.zkc diff --git a/arithmetization/src/main/lib/sha2/constants.zkc b/arithmetization/src/main/lib/sha2/constants.zkc new file mode 100644 index 0000000000..6d34fcbd3f --- /dev/null +++ b/arithmetization/src/main/lib/sha2/constants.zkc @@ -0,0 +1,91 @@ +// SHA-256 round constants: the first 32 bits of the fractional parts of the +// cube roots of the first 64 prime numbers (FIPS 180-4 section 4.2.2). +static SHA2_K(address:u6) -> (constant:u32) { + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2 +} + +// SHA-256 initial hash values (FIPS 180-4 section 5.3.3). +const SHA2_INITIAL_H0:u32 = 0x6a09e667 +const SHA2_INITIAL_H1:u32 = 0xbb67ae85 +const SHA2_INITIAL_H2:u32 = 0x3c6ef372 +const SHA2_INITIAL_H3:u32 = 0xa54ff53a +const SHA2_INITIAL_H4:u32 = 0x510e527f +const SHA2_INITIAL_H5:u32 = 0x9b05688c +const SHA2_INITIAL_H6:u32 = 0x1f83d9ab +const SHA2_INITIAL_H7:u32 = 0x5be0cd19 + +const SHA2_BLOCK_BYTE_LENGTH:u32 = 64 +const SHA2_PADDING_START:u8 = 0x80 + +// sha2_state layout: W[0..64) followed by H[0..8). +const SHA2_H0:u7 = 64 +const SHA2_H1:u7 = 65 +const SHA2_H2:u7 = 66 +const SHA2_H3:u7 = 67 +const SHA2_H4:u7 = 68 +const SHA2_H5:u7 = 69 +const SHA2_H6:u7 = 70 +const SHA2_H7:u7 = 71 diff --git a/arithmetization/src/main/lib/sha2/impl.zkc b/arithmetization/src/main/lib/sha2/impl.zkc new file mode 100644 index 0000000000..28df023df8 --- /dev/null +++ b/arithmetization/src/main/lib/sha2/impl.zkc @@ -0,0 +1,162 @@ +include "constants.zkc" +include "utils.zkc" +include "../../riscv/memory.zkc" +include "../../riscv/ram/read.zkc" +include "../../riscv/ram/write.zkc" +include "../../riscv/utils/type.zkc" + +// Compute SHA-256 over msg_length bytes in guest RAM. The custom instruction +// ABI supplies the input address through rs1, the byte length through rs2, and +// the output address through the current value of rd. Like the Keccak +// accelerator, the instruction does not modify the architectural value of rd. +// +// The accelerated ABI currently accepts lengths through the interpreter's u32 +// path, matching Keccak. SHA-256's encoded bit length is nevertheless formed in +// u64 before shifting, so all supported byte lengths are encoded correctly. +fn sha2(msg_address:Address, msg_length:u32, output_address:Address) { + sha2_initialize() + + // todo(arijit): use divmod (/%) once zkc version is bumped in prover-ray + var full_blocks:u32 = msg_length / SHA2_BLOCK_BYTE_LENGTH + var remainder:u6 = (msg_length % SHA2_BLOCK_BYTE_LENGTH) as u6 + var current_address:Address = msg_address + + // Absorb every complete message block before writing any output. This is + // important when the caller's input and output ranges overlap. + for block:u32 = 0; block() { + sha2_state[SHA2_H0] = SHA2_INITIAL_H0 + sha2_state[SHA2_H1] = SHA2_INITIAL_H1 + sha2_state[SHA2_H2] = SHA2_INITIAL_H2 + sha2_state[SHA2_H3] = SHA2_INITIAL_H3 + sha2_state[SHA2_H4] = SHA2_INITIAL_H4 + sha2_state[SHA2_H5] = SHA2_INITIAL_H5 + sha2_state[SHA2_H6] = SHA2_INITIAL_H6 + sha2_state[SHA2_H7] = SHA2_INITIAL_H7 +} + +// Load one complete message block. Guest RAM stores bytes little-endian within +// each RAM word, while SHA-256 interprets every schedule word as big-endian. +fn sha2_load_message_block(address:Address) { + for i:u5 = 0; i<16; i = i + 1 { + var word_address:Address = address + (4 * (i as Address)) + var b0:u8 = read_8(word_address) + var b1:u8 = read_8(word_address + 1) + var b2:u8 = read_8(word_address + 2) + var b3:u8 = read_8(word_address + 3) + sha2_state[i as u7] = b0::b1::b2::b3 + } +} + +// Load the first padding block: remaining message bytes, one 0x80 byte, then +// zeroes. The caller replaces W[14..16] with the length when it fits. +fn sha2_load_padding_block(address:Address, remainder:u6) { + for word_index:u5 = 0; word_index<16; word_index = word_index + 1 { + var byte_index:u6 = (word_index as u6) << 2 + var b0:u8 = sha2_padding_byte(address, remainder, byte_index) + var b1:u8 = sha2_padding_byte(address, remainder, byte_index + 1) + var b2:u8 = sha2_padding_byte(address, remainder, byte_index + 2) + var b3:u8 = sha2_padding_byte(address, remainder, byte_index + 3) + sha2_state[word_index as u7] = b0::b1::b2::b3 + } +} + +fn sha2_padding_byte(address:Address, remainder:u6, index:u6) -> (value:u8) { + if index(bit_length_high:u32, bit_length_low:u32) { + for i:u5 = 0; i<14; i = i + 1 { + sha2_state[i as u7] = 0 + } + sha2_state[14] = bit_length_high + sha2_state[15] = bit_length_low +} + +fn sha2_extend_schedule() { + for i:u7 = 16; i<64; i = i + 1 { + var sigma0:u32 = sha2_small_sigma0(sha2_state[i - 15]) + var sigma1:u32 = sha2_small_sigma1(sha2_state[i - 2]) + sha2_state[i] = sha2_add4(sha2_state[i - 16], sigma0, sha2_state[i - 7], sigma1) + } +} + +fn sha2_compress() { + sha2_extend_schedule() + + var a:u32 = sha2_state[SHA2_H0] + var b:u32 = sha2_state[SHA2_H1] + var c:u32 = sha2_state[SHA2_H2] + var d:u32 = sha2_state[SHA2_H3] + var e:u32 = sha2_state[SHA2_H4] + var f:u32 = sha2_state[SHA2_H5] + var g:u32 = sha2_state[SHA2_H6] + var h:u32 = sha2_state[SHA2_H7] + + for i:u7 = 0; i<64; i = i + 1 { + var t1:u32 = sha2_add5(h, sha2_big_sigma1(e), sha2_choose(e, f, g), SHA2_K[i as u6], sha2_state[i]) + var t2:u32 = sha2_add2(sha2_big_sigma0(a), sha2_majority(a, b, c)) + + h = g + g = f + f = e + e = sha2_add2(d, t1) + d = c + c = b + b = a + a = sha2_add2(t1, t2) + } + + sha2_state[SHA2_H0] = sha2_add2(sha2_state[SHA2_H0], a) + sha2_state[SHA2_H1] = sha2_add2(sha2_state[SHA2_H1], b) + sha2_state[SHA2_H2] = sha2_add2(sha2_state[SHA2_H2], c) + sha2_state[SHA2_H3] = sha2_add2(sha2_state[SHA2_H3], d) + sha2_state[SHA2_H4] = sha2_add2(sha2_state[SHA2_H4], e) + sha2_state[SHA2_H5] = sha2_add2(sha2_state[SHA2_H5], f) + sha2_state[SHA2_H6] = sha2_add2(sha2_state[SHA2_H6], g) + sha2_state[SHA2_H7] = sha2_add2(sha2_state[SHA2_H7], h) +} + +fn sha2_write_digest(output_address:Address) { + for i:u4 = 0; i<8; i = i + 1 { + var b0:u8, b1:u8, b2:u8, b3:u8 + b0::b1::b2::b3 = sha2_state[SHA2_H0 + (i as u7)] + var word_address:Address = output_address + (4 * (i as Address)) + write_8(word_address, b0) + write_8(word_address + 1, b1) + write_8(word_address + 2, b2) + write_8(word_address + 3, b3) + } +} diff --git a/arithmetization/src/main/lib/sha2/utils.zkc b/arithmetization/src/main/lib/sha2/utils.zkc new file mode 100644 index 0000000000..154d78e542 --- /dev/null +++ b/arithmetization/src/main/lib/sha2/utils.zkc @@ -0,0 +1,46 @@ +// SHA-256 uses addition modulo 2^32. Each helper performs the addition in a +// wide unsigned type and then decomposes the result into a range-constrained +// carry and low word. The carry is intentionally discarded. +fn sha2_add2(a:u32, b:u32) -> (result:u32) { + var carry:u1 + carry::result = (a as u33) + (b as u33) +} + +fn sha2_add4(a:u32, b:u32, c:u32, d:u32) -> (result:u32) { + var carry:u2 + carry::result = (a as u34) + (b as u34) + (c as u34) + (d as u34) +} + +fn sha2_add5(a:u32, b:u32, c:u32, d:u32, e:u32) -> (result:u32) { + var carry:u3 + carry::result = (a as u35) + (b as u35) + (c as u35) + (d as u35) + (e as u35) +} + +fn sha2_rotr32(value:u32, amount:u5) -> (result:u32) { + var inverse_amount:u6 = 32 - (amount as u6) + result = (value >> amount) | (value << inverse_amount) +} + +fn sha2_small_sigma0(value:u32) -> (result:u32) { + result = sha2_rotr32(value, 7) ^ sha2_rotr32(value, 18) ^ (value >> 3) +} + +fn sha2_small_sigma1(value:u32) -> (result:u32) { + result = sha2_rotr32(value, 17) ^ sha2_rotr32(value, 19) ^ (value >> 10) +} + +fn sha2_big_sigma0(value:u32) -> (result:u32) { + result = sha2_rotr32(value, 2) ^ sha2_rotr32(value, 13) ^ sha2_rotr32(value, 22) +} + +fn sha2_big_sigma1(value:u32) -> (result:u32) { + result = sha2_rotr32(value, 6) ^ sha2_rotr32(value, 11) ^ sha2_rotr32(value, 25) +} + +fn sha2_choose(e:u32, f:u32, g:u32) -> (result:u32) { + result = (e & f) ^ ((~e) & g) +} + +fn sha2_majority(a:u32, b:u32, c:u32) -> (result:u32) { + result = (a & b) ^ (a & c) ^ (b & c) +} From 51e08d08ca9515a8cb802f1e84ef1286fd5018c1 Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Fri, 14 Aug 2026 13:04:06 +0530 Subject: [PATCH 2/7] feat: sha2 tests are added Signed-off-by: arijitdutta67 --- .../src/test/zkc/sha2/sha2_direct.json | 9 ++++ .../src/test/zkc/sha2/sha2_direct.zkc | 36 ++++++++++++++ prover-ray/zkcdriver/native_modules_test.go | 4 ++ prover-ray/zkcdriver/testdata/sha2_run.json | 9 ++++ prover-ray/zkcdriver/testdata/sha2_run.zkc | 47 +++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 arithmetization/src/test/zkc/sha2/sha2_direct.json create mode 100644 arithmetization/src/test/zkc/sha2/sha2_direct.zkc create mode 100644 prover-ray/zkcdriver/testdata/sha2_run.json create mode 100644 prover-ray/zkcdriver/testdata/sha2_run.zkc diff --git a/arithmetization/src/test/zkc/sha2/sha2_direct.json b/arithmetization/src/test/zkc/sha2/sha2_direct.json new file mode 100644 index 0000000000..62ab5be2d2 --- /dev/null +++ b/arithmetization/src/test/zkc/sha2/sha2_direct.json @@ -0,0 +1,9 @@ +{ + "entry_point_and_blobs_count": "0x00000000000000000000000000000000", + "blobs_offset_and_size": "0x", + "blobs_data": "0x", + "sha2_vector_count": "0x07", + "sha2_lengths": "0x00000000000000030000003700000038000000400000004100000081", + "sha2_messages": "0x6162630b30557a9fc4e90e33587da2c7ec11365b80a5caef14395e83a8cdf2173c6186abd0f51a3f6489aed3f81d42678cb1d6fb20456a8fb4d90b30557a9fc4e90e33587da2c7ec11365b80a5caef14395e83a8cdf2173c6186abd0f51a3f6489aed3f81d42678cb1d6fb20456a8fb4d9fe0b30557a9fc4e90e33587da2c7ec11365b80a5caef14395e83a8cdf2173c6186abd0f51a3f6489aed3f81d42678cb1d6fb20456a8fb4d9fe23486d92b7dc01260b30557a9fc4e90e33587da2c7ec11365b80a5caef14395e83a8cdf2173c6186abd0f51a3f6489aed3f81d42678cb1d6fb20456a8fb4d9fe23486d92b7dc01264b0b30557a9fc4e90e33587da2c7ec11365b80a5caef14395e83a8cdf2173c6186abd0f51a3f6489aed3f81d42678cb1d6fb20456a8fb4d9fe23486d92b7dc01264b7095badf04294e7398bde2072c51769bc0e50a2f54799ec3e80d32577ca1c6eb10355a7fa4c9ee13385d82a7ccf1163b6085aacff4193e6388add2f71c41668b", + "sha2_expected": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad2900465fcb533e05a158fd2b3be0e5e3b03740d83060aa3580e0d98a96bf238431454ff48ef36af2f08fd511bdc37d9d5855ac23e992e5ff5445cb6b7674a67494eb5de4943613fd048dc93393ab06877405faa39c11f53e9386083339833e7efc518669b6eb4b4dd91827ecacef86689c725bd5bab888fd3b26dbb196eec9544f1757ae4bffbae86d775b831765b75af154d52f7deaa46dd378051a2d3ad57f" +} diff --git a/arithmetization/src/test/zkc/sha2/sha2_direct.zkc b/arithmetization/src/test/zkc/sha2/sha2_direct.zkc new file mode 100644 index 0000000000..4613afc266 --- /dev/null +++ b/arithmetization/src/test/zkc/sha2/sha2_direct.zkc @@ -0,0 +1,36 @@ +type lane = u64 + +include "../../../main/lib/sha2/impl.zkc" + +// A focused SHA-256 harness that avoids the unrelated full RISC-V interpreter +// when checking the hash constraints. Messages are concatenated in +// sha2_messages and expected digests are concatenated in sha2_expected. +pub input sha2_vector_count(address:u1) -> (count:u8) +pub input sha2_lengths(address:u8) -> (length:u32) +pub input sha2_messages(address:u16) -> (byte:u8) +pub input sha2_expected(address:u8) -> (byte:u8) + +fn main() { + var input_address:Address = 0x100 + var output_address:Address = 0 + var message_offset:u16 = 0 + + for vector:u8 = 0; vector (count:u8) +pub input in_msg_length(address:u8) -> (length:u32) +pub input in_msg(address:u16) -> (byte:u8) +pub input in_hash(address:u16) -> (byte:u8) + +fn main() { + var input_address:Address = 0x100 + var output_address:Address = 0 + var message_offset:u16 = 0 + + var n:u8 = in_n[0] + for vector:u8 = 0; vector Date: Fri, 14 Aug 2026 16:13:27 +0530 Subject: [PATCH 3/7] feat: arithmetization side wiring of sha2 Signed-off-by: arijitdutta67 --- arithmetization/src/main/lib/README.md | 2 +- .../src/main/riscv/instruction_processing/r_type.zkc | 11 +++++++++-- arithmetization/src/main/riscv/interpreter.zkc | 2 +- arithmetization/src/main/riscv/main.zkc | 3 +-- arithmetization/src/main/riscv/memory.zkc | 5 +++++ arithmetization/src/main/riscv/utils/constants.zkc | 4 +++- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/arithmetization/src/main/lib/README.md b/arithmetization/src/main/lib/README.md index fac941c198..4a68b8c800 100644 --- a/arithmetization/src/main/lib/README.md +++ b/arithmetization/src/main/lib/README.md @@ -4,7 +4,7 @@ This folder contains the zkvm library: zkc implementations of EVM precompiles an | EVM precompiles | status | opc | funct3 | funct7 | |---------------------|:------:|:--------:|:------:|:---------:| | ECRECOVER | 🔴 | custom-0 | 0b000 | 0b0000001 | -| SHA2-256 | 🔴 | custom-0 | 0b... | 0b.....10 | +| SHA2-256 | 🟢 | custom-0 | 0b000 | 0b0000010 | | RIPEMD | 🔴 | custom-0 | 0b... | 0b.....11 | | IDENTITY | 🔴 | custom-0 | 0b... | 0b....100 | | MODEXP_small | 🔴 | custom-0 | 0b..0 | 0b....101 | diff --git a/arithmetization/src/main/riscv/instruction_processing/r_type.zkc b/arithmetization/src/main/riscv/instruction_processing/r_type.zkc index 19919dd9d7..d3776bb939 100644 --- a/arithmetization/src/main/riscv/instruction_processing/r_type.zkc +++ b/arithmetization/src/main/riscv/instruction_processing/r_type.zkc @@ -8,6 +8,7 @@ include "../utils/signed_comparisons.zkc" include "../utils/multiplication.zkc" include "../../lib/ecrecover/impl.zkc" include "../../lib/keccak/impl.zkc" +include "../../lib/sha2/impl.zkc" include "../../lib/io/write_output.zkc" include "../../lib/poseidon2/ram.zkc" @@ -41,7 +42,7 @@ include "../../lib/poseidon2/ram.zkc" // treat register values as two's-complement integers. Division and remainder // follow RISC-V semantics: division by zero returns −1 (or the dividend for REM), // and signed overflow (INT_MIN / −1) returns INT_MIN (or 0 for REM). -fn process_R_type_instruction(opcode:Opcode, instruction_parameters:u25, +fn process_R_type_instruction(opcode:Opcode, instruction_parameters:u25, output_write_address:OutputAddress) -> (new_output_write_address:OutputAddress) { @@ -315,7 +316,7 @@ output_write_address:OutputAddress) -> } // - // case CUSTOM_1 + // custom precompiles // case R_EVM_ECRECOVER: { printf "EVM ECRECOVER precompile " @@ -329,6 +330,12 @@ output_write_address:OutputAddress) -> new_output_write_address = output_write_address return } + case R_EVM_SHA2: { + printf "EVM SHA2-256 precompile " + sha2(v1 as Address, v2 as u32, registers[rd] as Address) + new_output_write_address = output_write_address + return + } case R_KECCAK: { printf "KECCAK precompile " keccak(v1 as Address, v2 as u32, registers[rd] as Address) diff --git a/arithmetization/src/main/riscv/interpreter.zkc b/arithmetization/src/main/riscv/interpreter.zkc index aae2bb6731..77cb9f9293 100644 --- a/arithmetization/src/main/riscv/interpreter.zkc +++ b/arithmetization/src/main/riscv/interpreter.zkc @@ -8,7 +8,7 @@ include "instruction_processing/s_type.zkc" include "instruction_processing/j_type.zkc" include "instruction_processing/u_type.zkc" -fn interpreter(instruction:u32, pc:Address, output_write_address:OutputAddress) -> (new_pc:Address, new_output_write_address:OutputAddress) { +fn interpreter(instruction:u32, pc:Address, output_write_address:OutputAddress) -> (new_pc:Address, new_output_write_address:OutputAddress) { var instruction_parameters:u25 var instruction_type:Type diff --git a/arithmetization/src/main/riscv/main.zkc b/arithmetization/src/main/riscv/main.zkc index e3fde6e0fb..bd139695bd 100644 --- a/arithmetization/src/main/riscv/main.zkc +++ b/arithmetization/src/main/riscv/main.zkc @@ -13,7 +13,7 @@ include "utils/register_utils.zkc" include "memory.zkc" // TODO @Ghost -fn main() { +fn main() { var instruction:Instruction var clock_cycle:u32 = 0 var entry_point:Address @@ -48,4 +48,3 @@ fn main() { pc, output_write_address = interpreter(instruction, pc, output_write_address) } } - diff --git a/arithmetization/src/main/riscv/memory.zkc b/arithmetization/src/main/riscv/memory.zkc index c32b589d33..5d3775f0ae 100644 --- a/arithmetization/src/main/riscv/memory.zkc +++ b/arithmetization/src/main/riscv/memory.zkc @@ -38,5 +38,10 @@ const C:u7 = 80 const D:u7 = 88 const PIB:u7 = 96 +// SHA-256 scratch memory: W[0..64) is the message schedule and H[64..72) +// stores the chaining state. Every schedule word is overwritten per block and +// every chaining word is reinitialized per invocation. +memory sha2_state(address:u7) -> (word:u32) + // guest program output pub output guest_output(address:OutputAddress) -> (byte:u8) diff --git a/arithmetization/src/main/riscv/utils/constants.zkc b/arithmetization/src/main/riscv/utils/constants.zkc index 52f75e7d3d..743daf5b55 100644 --- a/arithmetization/src/main/riscv/utils/constants.zkc +++ b/arithmetization/src/main/riscv/utils/constants.zkc @@ -157,6 +157,8 @@ const CUSTOM_1:Opcode = 0b0101011 // custom-1 // EVM precompiles const FUNCT3_EVM_ECRECOVER:Funct3 = 0b000 const FUNCT7_EVM_ECRECOVER:Funct7 = 0b0000001 +const FUNCT3_EVM_SHA2:Funct3 = 0b000 +const FUNCT7_EVM_SHA2:Funct7 = 0b0000010 // Extraneous precompiles const FUNCT3_KECCAK:Funct3 = 0b000 @@ -344,6 +346,7 @@ const R_KECCAK:u17 = ((CUSTOM_1 as u17) << 10) | ((FUNCT3_KECCAK as u17) << 7) | const R_POSEIDON2:u17 = ((CUSTOM_1 as u17) << 10) | ((FUNCT3_POSEIDON2 as u17) << 7) | (FUNCT7_POSEIDON2 as u17) const R_WRITE_OUTPUT:u17 = ((CUSTOM_1 as u17) << 10) | ((FUNCT3_WRITE_OUTPUT as u17) << 7) | (FUNCT7_WRITE_OUTPUT as u17) const R_EVM_ECRECOVER:u17 = ((CUSTOM_0 as u17) << 10) | ((FUNCT3_EVM_ECRECOVER as u17) << 7) | (FUNCT7_EVM_ECRECOVER as u17) +const R_EVM_SHA2:u17 = ((CUSTOM_0 as u17) << 10) | ((FUNCT3_EVM_SHA2 as u17) << 7) | (FUNCT7_EVM_SHA2 as u17) const R_ECRECOVER:u17 = ((CUSTOM_1 as u17) << 10) | ((FUNCT3_ECRECOVER as u17) << 7) | (FUNCT7_ECRECOVER as u17) // Concatenate constant to flatten the I_switch table @@ -367,4 +370,3 @@ const I_SLLIW:u10 = (((OP_IMM_32 as u10) << 3) | (FUNCT3_SLLIW as u10)) const I_SRLIW:u10 = (((OP_IMM_32 as u10) << 3) | (FUNCT3_SRLIW as u10)) const I_JALR:u10 = (((JALR as u10) << 3) | (FUNCT3_JALR as u10)) const I_ECALL:u10 = (((SYSTEM as u10) << 3) | (FUNCT3_ECALL as u10)) - From 7d1ccc081906253fdcec60d92e56e09a2dd1288c Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Mon, 17 Aug 2026 12:43:57 +0530 Subject: [PATCH 4/7] feat: guest side zig wiring is done Signed-off-by: arijitdutta67 --- .claude/settings.json | 30 +- arithmetization/src/test/Makefile | 32 +- arithmetization/src/test/README.md | 7 +- arithmetization/src/test/zig/build.zig | 14 +- .../src/test/zig/src/sha2/sha2_provider.zig | 276 ++++++++++++++++++ riscv-guests/README.md | 2 +- riscv-guests/l2-execution/Makefile | 6 +- riscv-guests/l2-execution/README.md | 21 +- riscv-guests/l2-execution/build.zig | 13 +- .../l2-execution/src/zkvm_provide.zig | 17 +- riscv-guests/lineth-accelerators/src/root.zig | 5 + riscv-guests/lineth-accelerators/src/sha2.zig | 28 ++ 12 files changed, 420 insertions(+), 31 deletions(-) create mode 100644 arithmetization/src/test/zig/src/sha2/sha2_provider.zig create mode 100644 riscv-guests/lineth-accelerators/src/sha2.zig diff --git a/.claude/settings.json b/.claude/settings.json index 56506a127c..dcc22777c9 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,19 +1,33 @@ { - "Hooks": { - "PostToolUse": [ - { - "matcher": "Edit", - "command": "./gradlew compileJava compileKotlin spotlessApply 2>&1 | tail -10" - } - ] - }, "env": { "ENABLE_LSP_TOOL": "1" }, + "permissions": { + "allow": [ + "Bash(make compile *)", + "Bash(git --no-pager diff arithmetization/src/main/riscv/instruction_processing/r_type.zkc arithmetization/src/main/riscv/utils/constants.zkc arithmetization/src/main/riscv/interpreter.zkc arithmetization/src/main/riscv/main.zkc arithmetization/src/main/riscv/memory.zkc riscv-guests/lineth-accelerators/src/root.zig riscv-guests/l2-execution/src/zkvm_provide.zig)", + "Bash(make sha2-zkc-exec *)", + "Bash(echo \"fast-exit=$?\")", + "Bash(exit 1)", + "Bash(awk '/^sha2-zkc-\\(exec|check\\):/{p=1} p{print} p&&/^$/{p=0}' Makefile)", + "Bash(awk 'NR>=344 && NR<=380' Makefile)", + "Bash(grep -nE \"sha2-zig|SHA2_ACCEL|TEST=|ZKC_EXEC_FLAGS|zig build|\\\\$\\\\\\(ZKC\\\\\\)|elf|zkc-exec\")", + "Bash(awk '/^sha2-zig-build:/{p=1} /^sha2-zig-exec:/{p=1} p{print} p&&/^$/{c++} c==2{exit}' Makefile)", + "Bash(grep -nA12 \"zkvm_status\" zkvm_types.zig)" + ] + }, "enabledPlugins": { "typescript-lsp@claude-plugins-official": true, "gopls-lsp@claude-plugins-official": true, "jdtls-lsp@claude-plugins-official": true, "kotlin-lsp@claude-plugins-official": true + }, + "Hooks": { + "PostToolUse": [ + { + "matcher": "Edit", + "command": "./gradlew compileJava compileKotlin spotlessApply 2>&1 | tail -10" + } + ] } } diff --git a/arithmetization/src/test/Makefile b/arithmetization/src/test/Makefile index d7df128209..f8a6110f37 100644 --- a/arithmetization/src/test/Makefile +++ b/arithmetization/src/test/Makefile @@ -3,7 +3,7 @@ # Moreover, ABI being LP64 (soft-float) is relevant only for float numbers, which we do not use, so it can be omitted as well. # Declare all targets as phony to prevent conflicts with files of the same name -.PHONY: exec debug compile elf-exec elf-debug install-zkc zkc-exec zkc-trace zkc-debug clean clean-all vector-build vector-json vector-exec keccak-rust-build keccak-rust-json keccak-rust-exec keccak-zig-build keccak-zig-json keccak-zig-exec blake-rust-build blake-rust-json blake-rust-exec act4-build act4-exec require-test require-src require-vector-file require-n-vectors require-vector-build-artifacts require-vector-json-mode require-json +.PHONY: exec debug compile elf-exec elf-debug install-zkc zkc-exec zkc-trace zkc-debug clean clean-all vector-build vector-json vector-exec keccak-rust-build keccak-rust-json keccak-rust-exec keccak-zig-build keccak-zig-json keccak-zig-exec sha2-zkc-exec sha2-zkc-check sha2-zig-build sha2-zig-exec blake-rust-build blake-rust-json blake-rust-exec act4-build act4-exec require-test require-src require-vector-file require-n-vectors require-vector-build-artifacts require-vector-json-mode require-json # set explicitly the default target so the include below (which has linker-script as first target) does not steal the default goal. .DEFAULT_GOAL := exec @@ -102,7 +102,7 @@ compile: require-src riscv64-unknown-elf-gcc -march=rv64im -mabi=lp64 -nostdlib -T$(LINKER_SCRIPT) -o $(BIN) $(SRC); \ elif [ "$(EXT)" = ".zig" ]; then \ echo "\nGenerating $(BIN) from $(SRC)"; \ - zig build --build-file $(MAKEFILE_DIR)zig/build.zig -Dpath=$(BASENAME) $(if $(filter true,$(KECCAK_ACCEL)),-Dkeccak-accel=true); \ + zig build --build-file $(MAKEFILE_DIR)zig/build.zig -Dpath=$(BASENAME) $(if $(filter true,$(KECCAK_ACCEL)),-Dkeccak-accel=true) $(if $(filter true,$(SHA2_ACCEL)),-Dsha2-accel=true); \ elif [ "$(EXT)" = ".rs" ]; then \ echo "\nGenerating $(BIN) from $(SRC)"; \ cargo +nightly rustc \ @@ -278,6 +278,7 @@ vector-exec: require-vector-json-mode install-zkc if [ "$$fail" -ne 0 ]; then echo "error: $$fail vector(s) failed" >&2; exit 1; fi; \ fi + ##################################################################### # Keccak wrapper targets ##################################################################### @@ -326,6 +327,33 @@ keccak-zig-json: keccak-zig-build keccak-zig-exec: keccak-zig-json $(MAKE) -f $(MAKEFILE_DIR)Makefile vector-exec VECTOR_JSON_MODE=batched VECTOR_JSON_FILE=$(KECCAK_JSON_FILE) +##################################################################### +# SHA-256 targets +##################################################################### + +# Passthrough argument to l2-execution package import `sha2-accel` to enable/disable the zkc accelerated SHA-256 precompile. +SHA2_ACCEL ?= false +SHA2_ZKC_MAIN := $(MAKEFILE_DIR)zkc/sha2/sha2_direct.zkc +SHA2_ZKC_INPUT := $(MAKEFILE_DIR)zkc/sha2/sha2_direct.json + +sha2-zkc-exec: + $(ZKC) exec $(ZKC_EXEC_FLAGS) $(SHA2_ZKC_INPUT) $(SHA2_ZKC_MAIN) + +sha2-zkc-check: + $(ZKC) exec --check $(SHA2_ZKC_INPUT) $(SHA2_ZKC_MAIN) + +sha2-zig-build: + $(MAKE) -f $(MAKEFILE_DIR)Makefile compile \ + SHA2_ACCEL=true \ + TEST=sha2/sha2_provider.zig + +# The full interpreter's fast backend currently fails while compiling an +# unchanged pre-existing instruction, so run this integration test in tracing mode. +sha2-zig-exec: sha2-zig-build + $(MAKE) -f $(MAKEFILE_DIR)Makefile zkc-exec \ + ZKC_EXEC_FLAGS= \ + TEST=sha2/sha2_provider.zig + ##################################################################### # Blake Rust wrapper targets ##################################################################### diff --git a/arithmetization/src/test/README.md b/arithmetization/src/test/README.md index 98d2ae84fe..737d5685fb 100644 --- a/arithmetization/src/test/README.md +++ b/arithmetization/src/test/README.md @@ -89,7 +89,7 @@ Useful shell function (add to `~/.zshrc` or `~/.bashrc`): riscv-test() { local makefile="path/to/lineth-monorepo/arithmetization/src/test/Makefile" case "$1" in - elf-exec|elf-debug|elf-to-json|install-zkc|clean-all|linker-script|vector-exec|zkc-exec|zkc-debug|keccak-rust-build|keccak-rust-json|keccak-rust-exec|keccak-zig-build|keccak-zig-json|keccak-zig-exec|blake-rust-build|blake-rust-json|blake-rust-exec|act4-build|act4-exec) + elf-exec|elf-debug|elf-to-json|install-zkc|clean-all|linker-script|vector-exec|zkc-exec|zkc-debug|keccak-rust-build|keccak-rust-json|keccak-rust-exec|keccak-zig-build|keccak-zig-json|keccak-zig-exec|sha2-zkc-exec|sha2-zkc-check|sha2-zig-build|sha2-zig-exec|blake-rust-build|blake-rust-json|blake-rust-exec|act4-build|act4-exec) # targets that do NOT require TEST argument make -f "$makefile" "$1" "${@:2}" ;; @@ -202,6 +202,10 @@ riscv-test compile . VERIFY_ELF=true | `make keccak-rust-build` | Build the Keccak Rust vector test and helper binaries | | `make keccak-rust-json` | Generate one batched Keccak vector JSON input | | `make keccak-rust-exec` | Run the batched Keccak vector JSON input | +| `make sha2-zkc-exec` | Run focused SHA-256 core vectors in fast mode | +| `make sha2-zkc-check` | Run the SHA-256 core vectors and verify their constraints | +| `make sha2-zig-build` | Build the self-checking Zig SHA-256 accelerator guest | +| `make sha2-zig-exec` | Run SHA-256 boundary, state-reset, unaligned, and overlap checks through the accelerator in tracing mode | | `make blake-rust-build` | Build the Blake Rust vector test and helper binaries | | `make blake-rust-json` | Generate Blake vector JSON inputs | | `make blake-rust-exec` | Run all Blake vectors from `rust/src/blake/blake10.all` | @@ -229,6 +233,7 @@ riscv-test compile . VERIFY_ELF=true | `VECTOR_SUBSET_FILE` | `$(BIN).all` | Intermediate `.all` file selected from `VECTOR_FILE`; one line per vector, or one blob including all vectors | | `IN_BYTES` | `""` | Hex big-endian input written in RAM at `IN_ORIGIN` as little-endian bytes before execution (either string or `@path/to/in_bytes`) | | `KECCAK_ACCEL` | `false` | Set to `true` for Zig tests using `keccak_provide` to call the Linea keccak wrapper instead of Zesu stdlibs_accel | +| `SHA2_ACCEL` | `false` | Set to `true` for Zig tests to call the Linea SHA-256 wrapper instead of Zesu stdlibs_accel | | `STACK_ORIGIN` | `0x00000000` | Low stack boundary; `_stack_end` is generated from this value | | `SP` | `STACK_ORIGIN + 0x00800000` | Initial stack pointer; `_stack_start` is generated from this value | | `PROGRAM_ORIGIN` | `SP` | Program start address | diff --git a/arithmetization/src/test/zig/build.zig b/arithmetization/src/test/zig/build.zig index 393b394da0..7432be01bb 100644 --- a/arithmetization/src/test/zig/build.zig +++ b/arithmetization/src/test/zig/build.zig @@ -3,6 +3,11 @@ const common = @import("build_common"); pub fn build(b: *std.Build) void { const keccak_accel = b.option(bool, "keccak-accel", "Enable zkc accelerated keccak precompile (argument passthrough to l2-execution)") orelse false; + const sha2_accel = b.option( + bool, + "sha2-accel", + "Enable zkc accelerated SHA-256 precompile (argument passthrough to l2-execution)", + ) orelse false; // The shared freestanding rv64im ZkC profile every guest builds for (build_common). const target = common.standardGuestTarget(b); @@ -29,8 +34,13 @@ pub fn build(b: *std.Build) void { const lineth_accel_mod = b.dependency("lineth_accelerators", .{ .target = target, .optimize = optimize }).module("lineth_accelerators"); root_mod.addImport("lineth_zkvm_accel", lineth_accel_mod); - // non-accelerated precompiles from l2-execution (through zesu implementation). We set keccak-accel=false to force the standard zesu keccak to native implementation of keccak - const provide_mod = b.dependency("l2_execution", .{ .target = target, .optimize = optimize, .@"keccak-accel" = keccak_accel }).module("zkvm_provide"); + // Precompile providers from l2-execution (through the Zesu implementation or an enabled wrapper). + const provide_mod = b.dependency("l2_execution", .{ + .target = target, + .optimize = optimize, + .@"keccak-accel" = keccak_accel, + .@"sha2-accel" = sha2_accel, + }).module("zkvm_provide"); root_mod.addImport("zkvm_provide", provide_mod); // Link the statically-linked rv64im ELF with the shared entry stub (start.s, which calls `main`) diff --git a/arithmetization/src/test/zig/src/sha2/sha2_provider.zig b/arithmetization/src/test/zig/src/sha2/sha2_provider.zig new file mode 100644 index 0000000000..1e6f08ed94 --- /dev/null +++ b/arithmetization/src/test/zig/src/sha2/sha2_provider.zig @@ -0,0 +1,276 @@ +//! Self-checking, freestanding SHA-256 provider test. +//! +//! The expected digests are fixed test vectors produced independently of the +//! provider. In particular, this guest never uses `zkvm_sha256` to derive an +//! expected value. + +const lineth_accel = @import("lineth_zkvm_accel"); +const provide = @import("zkvm_provide"); + +// Force zkvm_provide's comptime exports to define zkvm_sha256 in this ELF. +comptime { + _ = provide; +} + +extern fn zkvm_sha256( + data: [*c]const u8, + len: usize, + output: [*c]lineth_accel.zkvm_sha256_hash, +) lineth_accel.zkvm_status; + +const Digest = [32]u8; +const HASH_ALIGNMENT: usize = 8; +const MAX_PATTERN_LEN: usize = 129; + +const BEFORE_CANARY = [_]u8{ 0xa5, 0x3c, 0x7e, 0x91, 0x42, 0xd8, 0x16, 0xeb }; +const AFTER_CANARY = [_]u8{ 0x5a, 0xc3, 0x81, 0x6e, 0xbd, 0x27, 0xe9, 0x14 }; + +const GuardedHash = extern struct { + before: [8]u8, + hash: lineth_accel.zkvm_sha256_hash, + after: [8]u8, +}; + +// FIPS 180-4's well-known empty-string and "abc" vectors. +const SHA256_EMPTY: Digest = .{ + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, + 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, +}; +const SHA256_ABC: Digest = .{ + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad, +}; + +// SHA-256 of byte[i] = (i * 37 + 11) % 256 at the indicated lengths. +const SHA256_PATTERN_32: Digest = .{ + 0x83, 0xb7, 0xa8, 0xed, 0x85, 0x90, 0x53, 0xc8, 0x1d, 0x81, 0x88, 0x70, 0xfa, 0xb1, 0xf8, 0xb1, + 0xae, 0x44, 0xd0, 0x6a, 0x98, 0xa9, 0x66, 0x5d, 0x36, 0x9a, 0x8f, 0xd7, 0xd2, 0x83, 0x8d, 0xed, +}; +const SHA256_PATTERN_40: Digest = .{ + 0x76, 0xde, 0xf7, 0x58, 0x56, 0xe5, 0xd7, 0x3e, 0xce, 0x01, 0x1b, 0x05, 0x8b, 0x02, 0xd2, 0x05, + 0x99, 0x1a, 0x48, 0xf0, 0xfc, 0xf8, 0xb7, 0xdd, 0xcc, 0x24, 0x00, 0x5d, 0x57, 0x75, 0x9b, 0x23, +}; +const SHA256_PATTERN_55: Digest = .{ + 0x29, 0x00, 0x46, 0x5f, 0xcb, 0x53, 0x3e, 0x05, 0xa1, 0x58, 0xfd, 0x2b, 0x3b, 0xe0, 0xe5, 0xe3, + 0xb0, 0x37, 0x40, 0xd8, 0x30, 0x60, 0xaa, 0x35, 0x80, 0xe0, 0xd9, 0x8a, 0x96, 0xbf, 0x23, 0x84, +}; +const SHA256_PATTERN_56: Digest = .{ + 0x31, 0x45, 0x4f, 0xf4, 0x8e, 0xf3, 0x6a, 0xf2, 0xf0, 0x8f, 0xd5, 0x11, 0xbd, 0xc3, 0x7d, 0x9d, + 0x58, 0x55, 0xac, 0x23, 0xe9, 0x92, 0xe5, 0xff, 0x54, 0x45, 0xcb, 0x6b, 0x76, 0x74, 0xa6, 0x74, +}; +const SHA256_PATTERN_63: Digest = .{ + 0x5f, 0x64, 0x01, 0xb9, 0x65, 0x32, 0xc3, 0x6d, 0xe4, 0xe6, 0x5b, 0xee, 0xc0, 0x40, 0x9b, 0x69, + 0xb1, 0xd1, 0x81, 0x86, 0x4c, 0x80, 0x09, 0xb7, 0xa0, 0x4f, 0x43, 0xe5, 0xd5, 0x63, 0x50, 0xd1, +}; +const SHA256_PATTERN_64: Digest = .{ + 0x94, 0xeb, 0x5d, 0xe4, 0x94, 0x36, 0x13, 0xfd, 0x04, 0x8d, 0xc9, 0x33, 0x93, 0xab, 0x06, 0x87, + 0x74, 0x05, 0xfa, 0xa3, 0x9c, 0x11, 0xf5, 0x3e, 0x93, 0x86, 0x08, 0x33, 0x39, 0x83, 0x3e, 0x7e, +}; +const SHA256_PATTERN_65: Digest = .{ + 0xfc, 0x51, 0x86, 0x69, 0xb6, 0xeb, 0x4b, 0x4d, 0xd9, 0x18, 0x27, 0xec, 0xac, 0xef, 0x86, 0x68, + 0x9c, 0x72, 0x5b, 0xd5, 0xba, 0xb8, 0x88, 0xfd, 0x3b, 0x26, 0xdb, 0xb1, 0x96, 0xee, 0xc9, 0x54, +}; +const SHA256_PATTERN_119: Digest = .{ + 0xb0, 0xdc, 0x41, 0xb1, 0xa3, 0x84, 0xe2, 0xf1, 0x20, 0x3f, 0x03, 0x51, 0xb3, 0x8f, 0xbe, 0xaa, + 0xfc, 0xee, 0xf5, 0x77, 0xce, 0x11, 0x91, 0xd5, 0xbf, 0xc2, 0x5d, 0xa3, 0x9f, 0x72, 0x1e, 0xae, +}; +const SHA256_PATTERN_120: Digest = .{ + 0x5d, 0xf2, 0x4d, 0xd8, 0x02, 0xac, 0x26, 0x13, 0x2c, 0xe6, 0x08, 0xdc, 0xb5, 0xf0, 0x98, 0x41, + 0xee, 0xf0, 0x39, 0xee, 0x0f, 0x15, 0x2a, 0xcf, 0x98, 0xd2, 0x6d, 0x17, 0xfe, 0x4e, 0x88, 0xe6, +}; +const SHA256_PATTERN_127: Digest = .{ + 0x0f, 0xe7, 0x29, 0xff, 0x19, 0x25, 0x7b, 0xd6, 0xfe, 0xc8, 0x53, 0xac, 0xc2, 0xea, 0x35, 0x5f, + 0x6b, 0x34, 0xb5, 0x8e, 0x6c, 0x0f, 0x68, 0x4c, 0x3e, 0x18, 0x8f, 0xcd, 0xfc, 0xd9, 0xba, 0xae, +}; +const SHA256_PATTERN_128: Digest = .{ + 0x0a, 0xed, 0xd4, 0x85, 0x6f, 0x8e, 0xba, 0x09, 0x63, 0x62, 0x73, 0x36, 0xad, 0x51, 0x44, 0xa9, + 0xa7, 0xdb, 0xe1, 0x24, 0x98, 0xe6, 0x06, 0x6f, 0x01, 0x65, 0xfc, 0x97, 0xd8, 0xdd, 0xee, 0x4c, +}; +const SHA256_PATTERN_129: Digest = .{ + 0x4f, 0x17, 0x57, 0xae, 0x4b, 0xff, 0xba, 0xe8, 0x6d, 0x77, 0x5b, 0x83, 0x17, 0x65, 0xb7, 0x5a, + 0xf1, 0x54, 0xd5, 0x2f, 0x7d, 0xea, 0xa4, 0x6d, 0xd3, 0x78, 0x05, 0x1a, 0x2d, 0x3a, 0xd5, 0x7f, +}; + +const PatternCase = struct { + len: usize, + expected: *const Digest, +}; + +// Repeat two distinct inputs in strict A/B/A/B order, then alternate short and +// long padding-boundary cases so provider state leakage is observable. +const PATTERN_CASES = [_]PatternCase{ + .{ .len = 55, .expected = &SHA256_PATTERN_55 }, + .{ .len = 129, .expected = &SHA256_PATTERN_129 }, + .{ .len = 55, .expected = &SHA256_PATTERN_55 }, + .{ .len = 129, .expected = &SHA256_PATTERN_129 }, + .{ .len = 56, .expected = &SHA256_PATTERN_56 }, + .{ .len = 128, .expected = &SHA256_PATTERN_128 }, + .{ .len = 63, .expected = &SHA256_PATTERN_63 }, + .{ .len = 127, .expected = &SHA256_PATTERN_127 }, + .{ .len = 64, .expected = &SHA256_PATTERN_64 }, + .{ .len = 120, .expected = &SHA256_PATTERN_120 }, + .{ .len = 65, .expected = &SHA256_PATTERN_65 }, + .{ .len = 119, .expected = &SHA256_PATTERN_119 }, +}; + +export fn main() noreturn { + if (!runChecks()) { + lineth_accel.zkvm_exit(1); + } + lineth_accel.zkvm_exit(0); +} + +fn runChecks() bool { + const empty_anchor = [_]u8{0x6d}; + const abc = [_]u8{ 'a', 'b', 'c' }; + + // A/B/A/B makes accidental retained state fail even for familiar vectors. + if (!hashMatches(empty_anchor[0..0], &SHA256_EMPTY)) return false; + if (!hashMatches(abc[0..], &SHA256_ABC)) return false; + if (!hashMatches(empty_anchor[0..0], &SHA256_EMPTY)) return false; + if (!hashMatches(abc[0..], &SHA256_ABC)) return false; + + // Offset one byte from an eight-byte-aligned backing buffer on purpose. + var pattern_storage: [MAX_PATTERN_LEN + 1]u8 align(HASH_ALIGNMENT) = undefined; + fillPattern(pattern_storage[1..]); + const pattern: []const u8 = pattern_storage[1..]; + if (@intFromPtr(pattern.ptr) % HASH_ALIGNMENT == 0) return false; + + for (PATTERN_CASES) |test_case| { + if (!hashMatches(pattern[0..test_case.len], test_case.expected)) return false; + } + + if (!checkExactOverlap()) return false; + if (!checkPartialOverlap()) return false; + if (!checkMultiBlockPartialOverlap()) return false; + + return true; +} + +fn hashMatches(data: []const u8, expected: *const Digest) bool { + var guarded = GuardedHash{ + .before = BEFORE_CANARY, + .hash = .{ .data = [_]u8{0xcc} ** 32 }, + .after = AFTER_CANARY, + }; + + if (@intFromPtr(&guarded.hash) % HASH_ALIGNMENT != 0) return false; + + const status = zkvm_sha256(data.ptr, data.len, &guarded.hash); + if (status != .ZKVM_EOK) return false; + if (!bytesEqual(guarded.before[0..], BEFORE_CANARY[0..])) return false; + if (!bytesEqual(guarded.hash.data[0..], expected.*[0..])) return false; + if (!bytesEqual(guarded.after[0..], AFTER_CANARY[0..])) return false; + + return true; +} + +fn checkExactOverlap() bool { + var storage: [48]u8 align(HASH_ALIGNMENT) = [_]u8{0xcc} ** 48; + copyBytes(storage[0..8], BEFORE_CANARY[0..]); + fillPattern(storage[8..40]); + copyBytes(storage[40..48], AFTER_CANARY[0..]); + + const address = @intFromPtr(&storage[8]); + if (address % HASH_ALIGNMENT != 0) return false; + + const input: [*c]const u8 = @ptrFromInt(address); + const output: [*c]lineth_accel.zkvm_sha256_hash = @ptrFromInt(address); + const status = zkvm_sha256(input, 32, output); + + if (status != .ZKVM_EOK) return false; + if (!bytesEqual(storage[0..8], BEFORE_CANARY[0..])) return false; + if (!bytesEqual(storage[8..40], SHA256_PATTERN_32[0..])) return false; + if (!bytesEqual(storage[40..48], AFTER_CANARY[0..])) return false; + + return true; +} + +fn checkPartialOverlap() bool { + // input = [1, 41), output = [8, 40): distinct, overlapping ranges. The + // output address remains eight-byte aligned while the input is unaligned. + var storage: [48]u8 align(HASH_ALIGNMENT) = [_]u8{0x69} ** 48; + storage[0] = 0x96; + fillPattern(storage[1..41]); + + const input_address = @intFromPtr(&storage[1]); + const output_address = @intFromPtr(&storage[8]); + if (input_address % HASH_ALIGNMENT == 0) return false; + if (output_address % HASH_ALIGNMENT != 0) return false; + if (input_address == output_address) return false; + + const input: [*c]const u8 = @ptrFromInt(input_address); + const output: [*c]lineth_accel.zkvm_sha256_hash = @ptrFromInt(output_address); + const status = zkvm_sha256(input, 40, output); + + // The immediate output guards include the input's first seven bytes and + // final byte, making an output underrun/overrun visible despite overlap. + const expected_before = [_]u8{ 0x96, 0x0b, 0x30, 0x55, 0x7a, 0x9f, 0xc4, 0xe9 }; + const expected_after = [_]u8{ 0xae, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69 }; + + if (status != .ZKVM_EOK) return false; + if (!bytesEqual(storage[0..8], expected_before[0..])) return false; + if (!bytesEqual(storage[8..40], SHA256_PATTERN_40[0..])) return false; + if (!bytesEqual(storage[40..48], expected_after[0..])) return false; + + return true; +} + +fn checkMultiBlockPartialOverlap() bool { + // input = [1, 130), output = [64, 96): the digest overlaps the second + // message block. An implementation that writes output before absorbing the + // whole input corrupts bytes it still needs to read and fails this vector. + var storage: [144]u8 align(HASH_ALIGNMENT) = [_]u8{0x69} ** 144; + storage[0] = 0x96; + fillPattern(storage[1..130]); + + const input_address = @intFromPtr(&storage[1]); + const output_address = @intFromPtr(&storage[64]); + if (input_address % HASH_ALIGNMENT == 0) return false; + if (output_address % HASH_ALIGNMENT != 0) return false; + + const input: [*c]const u8 = @ptrFromInt(input_address); + const output: [*c]lineth_accel.zkvm_sha256_hash = @ptrFromInt(output_address); + const status = zkvm_sha256(input, 129, output); + + if (status != .ZKVM_EOK) return false; + if (storage[0] != 0x96) return false; + if (!patternMatches(storage[56..64], 55)) return false; + if (!bytesEqual(storage[64..96], SHA256_PATTERN_129[0..])) return false; + if (!patternMatches(storage[96..104], 95)) return false; + if (!bytesEqual(storage[130..138], ([_]u8{0x69} ** 8)[0..])) return false; + + return true; +} + +fn fillPattern(bytes: []u8) void { + var i: usize = 0; + while (i < bytes.len) : (i += 1) { + bytes[i] = @intCast((i * 37 + 11) % 256); + } +} + +fn patternMatches(bytes: []const u8, start_index: usize) bool { + var i: usize = 0; + while (i < bytes.len) : (i += 1) { + if (bytes[i] != @as(u8, @intCast(((start_index + i) * 37 + 11) % 256))) return false; + } + return true; +} + +fn copyBytes(destination: []u8, source: []const u8) void { + var i: usize = 0; + while (i < destination.len) : (i += 1) { + destination[i] = source[i]; + } +} + +fn bytesEqual(left: []const u8, right: []const u8) bool { + if (left.len != right.len) return false; + + var i: usize = 0; + while (i < left.len) : (i += 1) { + if (left[i] != right[i]) return false; + } + return true; +} diff --git a/riscv-guests/README.md b/riscv-guests/README.md index 3ae77e76f2..5505615c38 100644 --- a/riscv-guests/README.md +++ b/riscv-guests/README.md @@ -41,7 +41,7 @@ A guest's `make test` runs its logic on the **host**, where Zesu's `default.zig` | `libblst` | BLS12-381 + KZG point evaluation | | `libmcl` | BN254 | -Expected under a single prefix — `/opt/homebrew` on macOS, `/usr/local` on Linux — overridable with `-Dcrypto-prefix=`. Install them all via Zesu's helper (from a Zesu checkout): `make install-deps`. The freestanding guest ELF (`make compile`) needs **none** of these: its precompiles are either pure-Zig (zesu-zkvm's `stdlibs_accel`, compiled in) or a custom RISC-V opcode (keccak) the prover arithmetizes at execution. +Expected under a single prefix — `/opt/homebrew` on macOS, `/usr/local` on Linux — overridable with `-Dcrypto-prefix=`. Install them all via Zesu's helper (from a Zesu checkout): `make install-deps`. The freestanding guest ELF (`make compile`) needs **none** of these: its precompiles are either pure-Zig (zesu-zkvm's `stdlibs_accel`, compiled in) or custom RISC-V opcodes (Keccak and SHA-256) the prover arithmetizes at execution. ## Development diff --git a/riscv-guests/l2-execution/Makefile b/riscv-guests/l2-execution/Makefile index 60af6fefed..a6d3e091ae 100644 --- a/riscv-guests/l2-execution/Makefile +++ b/riscv-guests/l2-execution/Makefile @@ -54,6 +54,10 @@ OBJDUMP ?= false # custom op) instead of the standard zig keccak. KECCAK_ACCEL ?= false +# If true, build with the arithmetization SHA-256 wrapper (prover-accelerated +# custom op) instead of the standard zig SHA-256. +SHA2_ACCEL ?= false + # ── Proving-system-agnostic guest lifecycle ───────────────────────────────── fetch: $(ZIG) build $(ZIG_BUILD_FLAGS) --fetch @@ -81,7 +85,7 @@ clean: rm -rf $(BUILD_CACHE_DIR) compile: - $(ZIG) build $(ZIG_BUILD_FLAGS) $(ZIG_FETCH_FLAGS) $(if $(filter true,$(KECCAK_ACCEL)),-Dkeccak-accel=true) + $(ZIG) build $(ZIG_BUILD_FLAGS) $(ZIG_FETCH_FLAGS) $(if $(filter true,$(KECCAK_ACCEL)),-Dkeccak-accel=true) $(if $(filter true,$(SHA2_ACCEL)),-Dsha2-accel=true) @if command -v riscv64-unknown-elf-objdump >/dev/null 2>&1; then \ if [ "$(OBJDUMP)" = "true" ]; then \ echo "\nDisassembling $(BIN)"; \ diff --git a/riscv-guests/l2-execution/README.md b/riscv-guests/l2-execution/README.md index 19352f94d9..443dcb6134 100644 --- a/riscv-guests/l2-execution/README.md +++ b/riscv-guests/l2-execution/README.md @@ -24,23 +24,28 @@ make -C l2-execution exec ## Compilation `make -C l2-execution compile` (and `exec`/`debug`) build the guest with -the **standard** zig keccak by default. Pass `KECCAK_ACCEL=true` to build with the -arithmetization keccak wrapper (the prover-accelerated custom op) instead: +the **standard** Zig Keccak and SHA-256 implementations by default. Pass +`KECCAK_ACCEL=true` and/or `SHA2_ACCEL=true` to use the corresponding +arithmetization wrapper (a prover-accelerated custom op) instead: ```bash -make -C l2-execution compile # standard zig keccak -make -C l2-execution compile KECCAK_ACCEL=true # arithmetization keccak wrapper +make -C l2-execution compile # standard Zig Keccak and SHA-256 +make -C l2-execution compile KECCAK_ACCEL=true # accelerated Keccak +make -C l2-execution compile SHA2_ACCEL=true # accelerated SHA-256 +make -C l2-execution compile KECCAK_ACCEL=true SHA2_ACCEL=true # both wrappers ``` Equivalently, running `zig build` directly from this directory (requires the generated linker script; run `make linker-script` once after a clean checkout): make linker-script - zig build # standard zig keccak - zig build -Dkeccak-accel=true # arithmetization keccak wrapper + zig build # standard Zig Keccak and SHA-256 + zig build -Dkeccak-accel=true # accelerated Keccak + zig build -Dsha2-accel=true # accelerated SHA-256 + zig build -Dkeccak-accel=true -Dsha2-accel=true # both wrappers ## Shell alias -`agp` (accelerated guest program): build this guest with the keccak wrapper and run +`agp` (accelerated guest program): build this guest with the Keccak and SHA-256 wrappers and run it in the ZKC interpreter on an SSZ input, from anywhere. Add to `~/.zshrc`: ```bash @@ -48,7 +53,7 @@ agp() { local input input="$(realpath "$1")" || { echo "agp: no such file: $1" >&2; return 1; } /usr/bin/time -p make -C /path/to/lineth-monorepo/riscv-guests/l2-execution \ - exec KECCAK_ACCEL=true INPUT="$input" "${@:2}" + exec KECCAK_ACCEL=true SHA2_ACCEL=true INPUT="$input" "${@:2}" } ``` diff --git a/riscv-guests/l2-execution/build.zig b/riscv-guests/l2-execution/build.zig index 00a80b3b15..6c0a38659d 100644 --- a/riscv-guests/l2-execution/build.zig +++ b/riscv-guests/l2-execution/build.zig @@ -17,6 +17,14 @@ pub fn build(b: *std.Build) void { // arithmetization keccak wrapper (prover-accelerated custom op) when opted in // with -Dkeccak-accel=true. Read by zkvm_provide.zig at comptime. const keccak_accel = b.option(bool, "keccak-accel", "Use the arithmetization keccak wrapper instead of standard zig keccak (default: standard)") orelse false; + // SHA-256 provider: standard zig SHA-256 (zesu stdlibs_accel) by default; the + // arithmetization SHA-256 wrapper (prover-accelerated custom op) when opted in + // with -Dsha2-accel=true. Read by zkvm_provide.zig at comptime. + const sha2_accel = b.option( + bool, + "sha2-accel", + "Use the arithmetization SHA-256 wrapper instead of standard zig SHA-256 (default: standard)", + ) orelse false; // write_output provider: default stdout `write` ecall (zesu zkvm_io) unless the // Lineth write_output custom-op accelerator is opted in with -Dwrite-output-accel=true. // Read by zkvm_provide.zig at comptime. @@ -24,6 +32,7 @@ pub fn build(b: *std.Build) void { const execution_specs_fixtures_link = b.option([]const u8, "execution-specs-fixtures-link", "Path where execution-specs zkevm fixtures are exposed") orelse "/tmp/execution-specs-json-fixtures/fixtures"; const guest_options = b.addOptions(); guest_options.addOption(bool, "keccak_accel", keccak_accel); + guest_options.addOption(bool, "sha2_accel", sha2_accel); guest_options.addOption(bool, "write_output_accel", write_output_accel); const gp_name = "evm_execution_guest"; @@ -39,7 +48,7 @@ pub fn build(b: *std.Build) void { // • zesu executor + SSZ modules — the execution logic; // • zesu_zkvm_accel — zesu-zkvm's stdlibs_accel: in-guest software precompiles that // zkvm_provide.zig exports as the zkvm_* symbols zesu references; - // • lineth_zkvm_accel — Lineth accelerator wrappers (keccak today): zkvm_* the prover accelerates + // • lineth_zkvm_accel — Lineth accelerator wrappers: zkvm_* the prover accelerates // at execution rather than at link time, so the ELF stays fully resolved; // • linea_zkvm_io — zesu-zkvm's zkvm_io: satisfies the standards `read_input` by reading the // memory-mapped `_in_start` (the input slot is the proving system's detail, kept out of the @@ -83,7 +92,7 @@ pub fn build(b: *std.Build) void { guest_module.addImport("zesu_zkvm_accel", zesu_accel_mod); guest_module.addImport("lineth_zkvm_accel", lineth_accel_mod); guest_module.addImport("linea_zkvm_io", linea_io_mod); - guest_module.addOptions("build_options", guest_options); // keccak_accel flag, read in zkvm_provide.zig + guest_module.addOptions("build_options", guest_options); // accelerator flags, read in zkvm_provide.zig common.clearFreestandingNativeLinkage(b, guest_module); common.installGuestElf(b, guest_module, gp_name); diff --git a/riscv-guests/l2-execution/src/zkvm_provide.zig b/riscv-guests/l2-execution/src/zkvm_provide.zig index 5aea589203..9a90ed9b53 100644 --- a/riscv-guests/l2-execution/src/zkvm_provide.zig +++ b/riscv-guests/l2-execution/src/zkvm_provide.zig @@ -6,7 +6,7 @@ //! all of them, from two sources: //! //! • Lineth accelerator wrappers (`lineth_zkvm_accel`) — for the precompiles the prover accelerates -//! (keccak today). We re-export each wrapper under the C name zesu references; HOW a wrapper +//! (Keccak and SHA-256 today). We re-export each wrapper under the C name zesu references; HOW a wrapper //! accelerates is the wrapper module's own concern. The *set of wrappers that exist* is what is //! accelerated, and grows as the prover implements more. //! • zesu-zkvm `stdlibs_accel` (`zesu_zkvm_accel`) — every precompile without a wrapper yet, via a @@ -19,18 +19,22 @@ const zesu_accel = @import("zesu_zkvm_accel"); // zesu-zkvm's pure-Zig precompile backend (stdlibs_accel) const lineth_accel = @import("lineth_zkvm_accel"); // Lineth accelerator wrappers (source paths wired in build.zig) const linea_io = @import("linea_zkvm_io"); // zesu-zkvm's zkvm_io: default (stdout ecall) write_output -const build_options = @import("build_options"); // keccak_accel: standard zig keccak vs Lineth wrapper +const build_options = @import("build_options"); // Selects standard Zig providers or Lineth wrappers. -// The manifest: every `zkvm_*` symbol zesu references, and where each comes from — keccak is either -// the Lineth wrapper (prover-accelerated) or the standard stdlibs_accel shim, selected at build time -// by -Dkeccak-accel; the rest come from the stdlibs_accel shims defined below. +// The manifest: every `zkvm_*` symbol zesu references, and where each comes from — Keccak and SHA-256 +// each select either the Lineth wrapper (prover-accelerated) or the standard stdlibs_accel shim at +// build time; the rest come from the stdlibs_accel shims defined below. comptime { if (build_options.keccak_accel) { @export(&lineth_accel.zkvm_keccak256, .{ .name = "zkvm_keccak256" }); } else { @export(&keccak256, .{ .name = "zkvm_keccak256" }); } - @export(&sha256, .{ .name = "zkvm_sha256" }); + if (build_options.sha2_accel) { + @export(&lineth_accel.zkvm_sha256, .{ .name = "zkvm_sha256" }); + } else { + @export(&sha256, .{ .name = "zkvm_sha256" }); + } @export(&secp256k1_verify, .{ .name = "zkvm_secp256k1_verify" }); @export(&secp256k1_ecrecover, .{ .name = "zkvm_secp256k1_ecrecover" }); @export(&ripemd160, .{ .name = "zkvm_ripemd160" }); @@ -85,6 +89,7 @@ fn keccak256(data: [*]const u8, len: usize, output: *[32]u8) callconv(.c) i32 { zesu_accel.keccak256(data[0..len], output); return OK; } +// Standard zig SHA-256 (std.crypto via stdlibs_accel); used unless -Dsha2-accel selects the wrapper. fn sha256(data: [*]const u8, len: usize, output: *[32]u8) callconv(.c) i32 { zesu_accel.sha256(data[0..len], output); return OK; diff --git a/riscv-guests/lineth-accelerators/src/root.zig b/riscv-guests/lineth-accelerators/src/root.zig index ec10fc0c1b..b3d106eb57 100644 --- a/riscv-guests/lineth-accelerators/src/root.zig +++ b/riscv-guests/lineth-accelerators/src/root.zig @@ -9,6 +9,7 @@ const zkvm_types = @import("zkvm_types.zig"); const evm_ecrecover = @import("evm_ecrecover.zig"); const keccak = @import("keccak.zig"); const poseidon2 = @import("poseidon2.zig"); +const sha2 = @import("sha2.zig"); const io = @import("io.zig"); const secp256k1_verify = @import("secp256k1_verify.zig"); @@ -33,6 +34,10 @@ pub const zkvm_secp256k1_pubkey = zkvm_types.zkvm_bytes_64; pub const zkvm_keccak256_hash = keccak.zkvm_keccak256_hash; pub const zkvm_keccak256 = keccak.zkvm_keccak256; +// ── SHA-256 accelerator (include/zkvm_accelerators.h) ─────────────────────── +pub const zkvm_sha256_hash = sha2.zkvm_sha256_hash; +pub const zkvm_sha256 = sha2.zkvm_sha256; + // ── io accelerator (zkvm-standards io-interface, include/zkvm_io.h) ────────── pub const write_output = io.write_output; diff --git a/riscv-guests/lineth-accelerators/src/sha2.zig b/riscv-guests/lineth-accelerators/src/sha2.zig new file mode 100644 index 0000000000..8a8ab3d72d --- /dev/null +++ b/riscv-guests/lineth-accelerators/src/sha2.zig @@ -0,0 +1,28 @@ +const lineth_std = @import("std.zig"); +const types = @import("zkvm_types.zig"); + +pub const zkvm_status = types.zkvm_status; +pub const zkvm_bytes_32 = types.zkvm_bytes_32; + +pub const zkvm_sha256_hash = zkvm_bytes_32; + +// https://github.com/eth-act/zkvm-standards/blob/main/standards/c-interface-accelerators/zkvm_accelerators.h#L222 +pub fn zkvm_sha256(data: [*c]const u8, len: usize, output: [*c]zkvm_sha256_hash) callconv(.c) zkvm_status { + if (data == null or output == null) { + lineth_std.panic(); + } + + // Invoke the SHA-256 custom opcode. + // Format: opcode(0x0b = custom-0) | funct3(0b000) | funct7(0b0000010) | rd(output) | rs1(input) | rs2(size) + asm volatile ( + \\.insn r 0x0b, 0b000, 0b0000010, %[out], %[in], %[size] + : + : [out] "r" (@intFromPtr(output)), + [in] "r" (@intFromPtr(data)), + [size] "r" (len), + // The opcode writes 32 bytes to *output through rd. Treat rd as an input because it carries + // the destination address; the memory clobber prevents output-buffer accesses from being + // dropped, reordered, or satisfied with stale data around the custom instruction. + : .{ .memory = true }); + return .ZKVM_EOK; +} From a67757863b141192ddd37dd88b21cb0d89a5515a Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Mon, 17 Aug 2026 16:27:03 +0530 Subject: [PATCH 5/7] chore: drop accidental .claude/settings.json changes Signed-off-by: arijitdutta67 --- .claude/settings.json | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index dcc22777c9..56506a127c 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,33 +1,19 @@ { + "Hooks": { + "PostToolUse": [ + { + "matcher": "Edit", + "command": "./gradlew compileJava compileKotlin spotlessApply 2>&1 | tail -10" + } + ] + }, "env": { "ENABLE_LSP_TOOL": "1" }, - "permissions": { - "allow": [ - "Bash(make compile *)", - "Bash(git --no-pager diff arithmetization/src/main/riscv/instruction_processing/r_type.zkc arithmetization/src/main/riscv/utils/constants.zkc arithmetization/src/main/riscv/interpreter.zkc arithmetization/src/main/riscv/main.zkc arithmetization/src/main/riscv/memory.zkc riscv-guests/lineth-accelerators/src/root.zig riscv-guests/l2-execution/src/zkvm_provide.zig)", - "Bash(make sha2-zkc-exec *)", - "Bash(echo \"fast-exit=$?\")", - "Bash(exit 1)", - "Bash(awk '/^sha2-zkc-\\(exec|check\\):/{p=1} p{print} p&&/^$/{p=0}' Makefile)", - "Bash(awk 'NR>=344 && NR<=380' Makefile)", - "Bash(grep -nE \"sha2-zig|SHA2_ACCEL|TEST=|ZKC_EXEC_FLAGS|zig build|\\\\$\\\\\\(ZKC\\\\\\)|elf|zkc-exec\")", - "Bash(awk '/^sha2-zig-build:/{p=1} /^sha2-zig-exec:/{p=1} p{print} p&&/^$/{c++} c==2{exit}' Makefile)", - "Bash(grep -nA12 \"zkvm_status\" zkvm_types.zig)" - ] - }, "enabledPlugins": { "typescript-lsp@claude-plugins-official": true, "gopls-lsp@claude-plugins-official": true, "jdtls-lsp@claude-plugins-official": true, "kotlin-lsp@claude-plugins-official": true - }, - "Hooks": { - "PostToolUse": [ - { - "matcher": "Edit", - "command": "./gradlew compileJava compileKotlin spotlessApply 2>&1 | tail -10" - } - ] } } From 446930aef961227ee01098f16da34d597b3ee16e Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Mon, 17 Aug 2026 16:27:58 +0530 Subject: [PATCH 6/7] fix: go test fix as pointed out by copilot Signed-off-by: arijitdutta67 --- prover-ray/zkcdriver/testdata/sha2_run.zkc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prover-ray/zkcdriver/testdata/sha2_run.zkc b/prover-ray/zkcdriver/testdata/sha2_run.zkc index 31727e6509..dcab808dd7 100644 --- a/prover-ray/zkcdriver/testdata/sha2_run.zkc +++ b/prover-ray/zkcdriver/testdata/sha2_run.zkc @@ -15,6 +15,8 @@ include "../../../arithmetization/src/main/lib/sha2/impl.zkc" +type lane = u64 + pub input in_n(address:u1) -> (count:u8) pub input in_msg_length(address:u8) -> (length:u32) pub input in_msg(address:u16) -> (byte:u8) From c8b6bc15f741666f3d42361a5c9de184345a5d77 Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Mon, 17 Aug 2026 16:57:10 +0530 Subject: [PATCH 7/7] feat: zig test is added in ci workflow Signed-off-by: arijitdutta67 --- .github/workflows/arithmetization-guest-programs-run.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/arithmetization-guest-programs-run.yml b/.github/workflows/arithmetization-guest-programs-run.yml index 8c3c569b10..65e6a42a73 100644 --- a/.github/workflows/arithmetization-guest-programs-run.yml +++ b/.github/workflows/arithmetization-guest-programs-run.yml @@ -65,6 +65,15 @@ jobs: working-directory: arithmetization/src/test timeout-minutes: 30 + # Builds sha2_provider.zig with SHA2_ACCEL=true (the Zig SHA-256 wrapper) and runs it through + # the full interpreter, exercising the R_EVM_SHA2 custom-op dispatch. The target forces tracing + # mode (ZKC_EXEC_FLAGS=) because the interpreter's fast backend currently fails on an unrelated + # pre-existing instruction; the guest is self-checking and exits non-zero on any mismatch. + - name: Run SHA-256 accelerator guest with zkc interpreter + run: make sha2-zig-exec + working-directory: arithmetization/src/test + timeout-minutes: 30 + - name: Run l2-execution guest program with zkc interpreter run: make -C riscv-guests/l2-execution exec timeout-minutes: 30