From bffbf54f7278434b0e4a6b6097a696d0e006e0e2 Mon Sep 17 00:00:00 2001 From: wwared <541936+wwared@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:17:27 -0300 Subject: [PATCH] feat: Working SHA512 compress single syscall --- core/src/operations/and.rs | 63 ++ core/src/operations/not.rs | 57 ++ core/src/runtime/record.rs | 38 +- core/src/runtime/syscall.rs | 15 +- core/src/stark/air.rs | 10 +- .../precompiles/sha512/compress/air.rs | 658 +++++++----------- .../precompiles/sha512/compress/columns.rs | 102 +-- .../precompiles/sha512/compress/execute.rs | 108 +-- .../precompiles/sha512/compress/mod.rs | 64 +- .../precompiles/sha512/compress/trace.rs | 350 ++++------ .../syscall/precompiles/sha512/extend/air.rs | 6 +- .../precompiles/sha512/extend/trace.rs | 11 +- core/src/syscall/precompiles/sha512/mod.rs | 4 +- core/src/utils/programs.rs | 4 +- tests/sha512-compress/Cargo.lock | 509 ++++++++++++++ tests/sha512-compress/Cargo.toml | 8 + .../elf/riscv32im-succinct-zkvm-elf | Bin 0 -> 32412 bytes tests/sha512-compress/src/main.rs | 103 +++ zkvm/entrypoint/src/syscalls/mod.rs | 6 +- .../src/syscalls/sha512_compress.rs | 19 + zkvm/precompiles/src/lib.rs | 2 +- 21 files changed, 1328 insertions(+), 809 deletions(-) create mode 100644 tests/sha512-compress/Cargo.lock create mode 100644 tests/sha512-compress/Cargo.toml create mode 100755 tests/sha512-compress/elf/riscv32im-succinct-zkvm-elf create mode 100644 tests/sha512-compress/src/main.rs create mode 100644 zkvm/entrypoint/src/syscalls/sha512_compress.rs diff --git a/core/src/operations/and.rs b/core/src/operations/and.rs index a803dfdaa..375221c59 100644 --- a/core/src/operations/and.rs +++ b/core/src/operations/and.rs @@ -3,6 +3,8 @@ use sphinx_derive::AlignedBorrow; use crate::air::ByteAirBuilder; use crate::air::Word; +use crate::air::Word64; +use crate::air::WORD64_SIZE; use crate::bytes::event::ByteRecord; use crate::bytes::ByteLookupEvent; use crate::bytes::ByteOpcode; @@ -69,3 +71,64 @@ impl AndOperation { } } } + +/// A set of columns needed to compute the and of two word64s. +#[derive(AlignedBorrow, Default, Debug, Clone, Copy)] +#[repr(C)] +pub struct And64Operation { + /// The result of `x & y`. + pub value: Word64, +} + +impl And64Operation { + pub fn populate( + &mut self, + record: &mut ExecutionRecord, + shard: u32, + channel: u32, + x: u64, + y: u64, + ) -> u64 { + let expected = x & y; + let x_bytes = x.to_le_bytes(); + let y_bytes = y.to_le_bytes(); + for i in 0..WORD64_SIZE { + let and = x_bytes[i] & y_bytes[i]; + self.value[i] = F::from_canonical_u8(and); + + let byte_event = ByteLookupEvent { + shard, + channel, + opcode: ByteOpcode::AND, + a1: u32::from(and), + a2: 0, + b: u32::from(x_bytes[i]), + c: u32::from(y_bytes[i]), + }; + record.add_byte_lookup_event(byte_event); + } + expected + } + + pub fn eval>( + builder: &mut AB, + a: Word64, + b: Word64, + cols: And64Operation, + shard: AB::Var, + channel: impl Into + Copy, + is_real: AB::Var, + ) { + for i in 0..WORD64_SIZE { + builder.send_byte( + AB::F::from_canonical_u32(ByteOpcode::AND as u32), + cols.value[i], + a[i], + b[i], + shard, + channel, + is_real, + ); + } + } +} diff --git a/core/src/operations/not.rs b/core/src/operations/not.rs index 9703e38c6..d72359ae4 100644 --- a/core/src/operations/not.rs +++ b/core/src/operations/not.rs @@ -4,6 +4,8 @@ use sphinx_derive::AlignedBorrow; use crate::air::ByteAirBuilder; use crate::air::Word; +use crate::air::Word64; +use crate::air::WORD64_SIZE; use crate::bytes::event::ByteRecord; use crate::bytes::ByteOpcode; use crate::disassembler::WORD_SIZE; @@ -62,3 +64,58 @@ impl NotOperation { } } } + +/// A set of columns needed to compute the not of a word64. +#[derive(AlignedBorrow, Default, Debug, Clone, Copy)] +#[repr(C)] +pub struct Not64Operation { + /// The result of `!x`. + pub value: Word64, +} + +impl Not64Operation { + pub fn populate( + &mut self, + record: &mut impl ByteRecord, + shard: u32, + channel: u32, + x: u64, + ) -> u64 { + let expected = !x; + let x_bytes = x.to_le_bytes(); + for i in 0..WORD64_SIZE { + self.value[i] = F::from_canonical_u8(!x_bytes[i]); + } + record.add_u8_range_checks(shard, channel, &x_bytes); + expected + } + + pub fn eval>( + builder: &mut AB, + a: Word64, + cols: Not64Operation, + shard: impl Into + Copy, + channel: impl Into + Copy, + is_real: impl Into + Copy, + ) { + for i in (0..WORD64_SIZE).step_by(2) { + builder.send_byte_pair( + AB::F::from_canonical_u32(ByteOpcode::U8Range as u32), + AB::F::zero(), + AB::F::zero(), + a[i], + a[i + 1], + shard, + channel, + is_real, + ); + } + + // For any byte b, b + !b = 0xFF. + for i in 0..WORD64_SIZE { + builder + .when(is_real) + .assert_eq(cols.value[i] + a[i], AB::F::from_canonical_u8(u8::MAX)); + } + } +} diff --git a/core/src/runtime/record.rs b/core/src/runtime/record.rs index f5ff06b90..336a39bc9 100644 --- a/core/src/runtime/record.rs +++ b/core/src/runtime/record.rs @@ -13,8 +13,8 @@ use crate::stark::MachineRecord; use crate::syscall::precompiles::edwards::EdDecompressEvent; use crate::syscall::precompiles::keccak256::KeccakPermuteEvent; use crate::syscall::precompiles::sha256::{ShaCompressEvent, ShaExtendEvent}; -// use crate::syscall::precompiles::sha512::Sha512CompressChip; 512FIXME -// use crate::syscall::precompiles::sha512::Sha512CompressEvent; 512FIXME +use crate::syscall::precompiles::sha512::Sha512CompressChip; +use crate::syscall::precompiles::sha512::Sha512CompressEvent; use crate::syscall::precompiles::sha512::Sha512ExtendChip; use crate::syscall::precompiles::sha512::Sha512ExtendEvent; use crate::syscall::precompiles::{ECAddEvent, ECDoubleEvent}; @@ -104,7 +104,8 @@ pub struct ExecutionRecord { pub sha512_extend_events: Vec, - // pub sha512_compress_events: Vec, 512FIXME + pub sha512_compress_events: Vec, + pub keccak_permute_events: Vec, pub ed_add_events: Vec, @@ -233,12 +234,11 @@ impl EventLens for ExecutionRecord { } } -// 512FIXME -// impl EventLens for ExecutionRecord { -// fn events(&self) -> >::Events { -// &self.sha512_compress_events -// } -// } +impl EventLens for ExecutionRecord { + fn events(&self) -> >::Events { + &self.sha512_compress_events + } +} impl EventLens for ExecutionRecord { fn events(&self) -> >::Events { @@ -435,10 +435,10 @@ impl MachineRecord for ExecutionRecord { "sha512_extend_events".to_string(), self.sha512_extend_events.len(), ); - // stats.insert( - // "sha512_compress_events".to_string(), - // self.sha512_compress_events.len(), - // ); 512FIXME + stats.insert( + "sha512_compress_events".to_string(), + self.sha512_compress_events.len(), + ); stats.insert( "keccak_permute_events".to_string(), self.keccak_permute_events.len(), @@ -512,8 +512,8 @@ impl MachineRecord for ExecutionRecord { .append(&mut other.sha_compress_events); self.sha512_extend_events .append(&mut other.sha512_extend_events); - // self.sha512_compress_events - // .append(&mut other.sha512_compress_events); 512FIXME + self.sha512_compress_events + .append(&mut other.sha512_compress_events); self.keccak_permute_events .append(&mut other.keccak_permute_events); self.ed_add_events.append(&mut other.ed_add_events); @@ -840,10 +840,10 @@ impl MachineRecord for ExecutionRecord { } // SHA-512 compress events. - // first.sha512_compress_events = take(&mut self.sha512_compress_events); - // for (i, event) in first.sha512_compress_events.iter().enumerate() { - // self.nonce_lookup.insert(event.lookup_id, (i * 80) as u32); - // } 512FIXME + first.sha512_compress_events = take(&mut self.sha512_compress_events); + for (i, event) in first.sha512_compress_events.iter().enumerate() { + self.nonce_lookup.insert(event.lookup_id, i as u32); + } // Edwards curve add events. first.ed_add_events = take(&mut self.ed_add_events); diff --git a/core/src/runtime/syscall.rs b/core/src/runtime/syscall.rs index a36c9fd2f..1c881c18b 100644 --- a/core/src/runtime/syscall.rs +++ b/core/src/runtime/syscall.rs @@ -18,8 +18,7 @@ use crate::syscall::precompiles::quad_field::{ }; use crate::syscall::precompiles::secp256k1::decompress::Secp256k1DecompressChip; use crate::syscall::precompiles::sha256::{ShaCompressChip, ShaExtendChip}; -// use crate::syscall::precompiles::sha512::{Sha512CompressChip, Sha512ExtendChip}; 512FIXME -use crate::syscall::precompiles::sha512::Sha512ExtendChip; +use crate::syscall::precompiles::sha512::{Sha512CompressChip, Sha512ExtendChip}; use crate::syscall::precompiles::weierstrass::{ WeierstrassAddAssignChip, WeierstrassDoubleAssignChip, }; @@ -108,7 +107,7 @@ pub enum SyscallCode { SHA512_EXTEND = 0x00_00_01_C1, /// Executes the `SHA512_COMPRESS` precompile. - SHA512_COMPRESS = 0x00_01_01_C2, + SHA512_COMPRESS = 0x00_00_01_C2, /// Executes the `COMMIT` precompile. COMMIT = 0x00_00_00_10, @@ -161,7 +160,7 @@ impl SyscallCode { 0x00_01_01_80 => SyscallCode::BLS12381_G2_ADD, 0x00_00_01_81 => SyscallCode::BLS12381_G2_DOUBLE, 0x00_00_01_C1 => SyscallCode::SHA512_EXTEND, - 0x00_01_01_C2 => SyscallCode::SHA512_COMPRESS, + 0x00_00_01_C2 => SyscallCode::SHA512_COMPRESS, _ => panic!("invalid syscall number: {}", value), } } @@ -403,10 +402,10 @@ pub fn default_syscall_map() -> HashMap> { SyscallCode::SHA512_EXTEND, Arc::new(Sha512ExtendChip::new()), ); - // syscall_map.insert( - // SyscallCode::SHA512_COMPRESS, - // Arc::new(Sha512CompressChip::new()), - // ); + syscall_map.insert( + SyscallCode::SHA512_COMPRESS, + Arc::new(Sha512CompressChip::new()), + ); syscall_map } diff --git a/core/src/stark/air.rs b/core/src/stark/air.rs index 32a0b4055..ef5d1008e 100644 --- a/core/src/stark/air.rs +++ b/core/src/stark/air.rs @@ -32,7 +32,7 @@ pub(crate) mod riscv_chips { pub use crate::syscall::precompiles::keccak256::KeccakPermuteChip; pub use crate::syscall::precompiles::sha256::ShaCompressChip; pub use crate::syscall::precompiles::sha256::ShaExtendChip; - // pub use crate::syscall::precompiles::sha512::Sha512CompressChip; 512FIXME + pub use crate::syscall::precompiles::sha512::Sha512CompressChip; pub use crate::syscall::precompiles::sha512::Sha512ExtendChip; pub use crate::syscall::precompiles::weierstrass::WeierstrassAddAssignChip; pub use crate::syscall::precompiles::weierstrass::WeierstrassDoubleAssignChip; @@ -110,8 +110,8 @@ pub enum RiscvAir { Bls12381G1Decompress(Bls12381G1DecompressChip), /// A precompile for sha512 extend. Sha512Extend(Sha512ExtendChip), - // /// A precompile for sha256 compress. - // Sha512Compress(Sha512CompressChip), 512FIXME + /// A precompile for sha256 compress. + Sha512Compress(Sha512CompressChip), } impl RiscvAir { @@ -166,8 +166,8 @@ impl RiscvAir { chips.push(RiscvAir::Bls12381G1Decompress(bls12381_g1_decompress)); let sha512_extend = Sha512ExtendChip; chips.push(RiscvAir::Sha512Extend(sha512_extend)); - // let sha512_compress = Sha512CompressChip; - // chips.push(RiscvAir::Sha512Compress(sha512_compress)); 512FIXME + let sha512_compress = Sha512CompressChip; + chips.push(RiscvAir::Sha512Compress(sha512_compress)); let div_rem = DivRemChip; chips.push(RiscvAir::DivRem(div_rem)); diff --git a/core/src/syscall/precompiles/sha512/compress/air.rs b/core/src/syscall/precompiles/sha512/compress/air.rs index 53cb84fe5..1cb523804 100644 --- a/core/src/syscall/precompiles/sha512/compress/air.rs +++ b/core/src/syscall/precompiles/sha512/compress/air.rs @@ -6,14 +6,16 @@ use p3_matrix::Matrix; use super::{ columns::{Sha512CompressCols, NUM_SHA512_COMPRESS_COLS}, - Sha512CompressChip, SHA512_COMPRESS_K, + Sha512CompressChip, }; use crate::{ - air::{AluAirBuilder, BaseAirBuilder, MemoryAirBuilder, Word, WordAirBuilder}, + air::{ + AluAirBuilder, BaseAirBuilder, ByteAirBuilder, MemoryAirBuilder, Word64, WordAirBuilder, + }, + bytes::ByteOpcode, memory::MemoryCols, operations::{ - Add5Operation, AddOperation, AndOperation, FixedRotateRightOperation, NotOperation, - XorOperation, + Add64Operation, And64Operation, FixedRotateRight64Operation, Not64Operation, Xor64Operation, }, runtime::SyscallCode, }; @@ -40,18 +42,13 @@ where .when_transition() .assert_eq(local.nonce + AB::Expr::one(), next.nonce); - self.eval_control_flow_flags(builder, local, next); + // Assert that is_real is a bool. + builder.assert_bool(local.is_real); self.eval_memory(builder, local); - self.eval_compression_ops(builder, local, next); - - self.eval_finalize_ops(builder, local); + self.eval_compression_ops(builder, local); - builder.assert_eq( - local.start, - local.is_real * local.octet[0] * local.octet_num[0], - ); builder.receive_syscall( local.shard, local.channel, @@ -60,502 +57,383 @@ where AB::F::from_canonical_u32(SyscallCode::SHA512_COMPRESS.syscall_id()), local.w_ptr, local.h_ptr, - local.start, + local.is_real, ); } } impl Sha512CompressChip { - fn eval_control_flow_flags( + /// Constrains that memory accesses are correct. + fn eval_memory( &self, builder: &mut AB, local: &Sha512CompressCols, - next: &Sha512CompressCols, ) { - // Verify that all of the octet columns are bool. - for i in 0..8 { - builder.assert_bool(local.octet[i]); - } - - // Verify that exactly one of the octet columns is true. - let mut octet_sum = AB::Expr::zero(); - for i in 0..8 { - octet_sum += local.octet[i].into(); - } - builder.assert_one(octet_sum); - - // Verify that the first row's octet value is correct. - builder.when_first_row().assert_one(local.octet[0]); - - // Verify correct transition for octet column. - for i in 0..8 { - builder - .when_transition() - .when(local.octet[i]) - .assert_one(next.octet[(i + 1) % 8]) - } - - // Verify that all of the octet_num columns are bool. - for i in 0..10 { - builder.assert_bool(local.octet_num[i]); - } - - // Verify that exactly one of the octet_num columns is true. - let mut octet_num_sum = AB::Expr::zero(); - for i in 0..10 { - octet_num_sum += local.octet_num[i].into(); - } - builder.assert_one(octet_num_sum); - - // The first row should have octet_num[0] = 1 if it's real. - builder.when_first_row().assert_one(local.octet_num[0]); - - // If current row is not last of an octet and next row is real, octet_num should be the same. - for i in 0..10 { - builder - .when_transition() - .when_not(local.octet[7]) - .assert_eq(local.octet_num[i], next.octet_num[i]); - } - - // If current row is last of an octet and next row is real, octet_num should rotate by 1. - for i in 0..10 { - builder - .when_transition() - .when(local.octet[7]) - .assert_eq(local.octet_num[i], next.octet_num[(i + 1) % 10]); - } - - // Constrain A-H columns - let vars = [ - local.a, local.b, local.c, local.d, local.e, local.f, local.g, local.h, - ]; - let next_vars = [ - next.a, next.b, next.c, next.d, next.e, next.f, next.g, next.h, - ]; - for (i, var) in vars.iter().enumerate() { - // For all initialize and finalize cycles, A-H should be the same in the next row. The - // last cycle is an exception since the next row must be a new 80-cycle loop or nonreal. - builder - .when_transition() - .when(local.octet_num[0] + local.octet_num[9] * (AB::Expr::one() - local.octet[7])) - .assert_word_eq(*var, next_vars[i]); - - // When column is read from memory during init, is should be equal to the memory value. - builder - .when_transition() - .when(local.octet_num[0] * local.octet[i]) - .assert_word_eq(*var, *local.mem.value()); - } - - // Assert that the is_initialize flag is correct. - builder.assert_eq(local.is_initialize, local.octet_num[0] * local.is_real); - - // Assert that the is_compression flag is correct. - builder.assert_eq( - local.is_compression, - (local.octet_num[1] - + local.octet_num[2] - + local.octet_num[3] - + local.octet_num[4] - + local.octet_num[5] - + local.octet_num[6] - + local.octet_num[7] - + local.octet_num[8]) - * local.is_real, - ); - - // Assert that the is_finalize flag is correct. - builder.assert_eq(local.is_finalize, local.octet_num[9] * local.is_real); - - builder.assert_eq( - local.is_last_row.into(), - local.octet[7] * local.octet_num[9], + // Assert `i` was read and written to correctly. + builder.eval_memory_access( + local.shard, + local.channel, + local.clk, + local.h_ptr + AB::F::from_canonical_u32(8 * 8), + &local.i_mem, + local.is_real, ); - - // If this row is real and not the last cycle, then next row should have same inputs - builder - .when_transition() - .when(local.is_real) - .when_not(local.is_last_row) - .assert_eq(local.shard, next.shard); - builder - .when_transition() - .when(local.is_real) - .when_not(local.is_last_row) - .assert_eq(local.clk, next.clk); - builder - .when_transition() - .when_not(local.is_last_row) - .assert_eq(local.channel, next.channel); + let reduced_prev_i = local.i_mem.prev_value().reduce::(); builder - .when_transition() .when(local.is_real) - .when_not(local.is_last_row) - .assert_eq(local.w_ptr, next.w_ptr); + .assert_eq(reduced_prev_i, local.i); + let reduced_next_i = local.i_mem.value().reduce::(); builder - .when_transition() .when(local.is_real) - .when_not(local.is_last_row) - .assert_eq(local.h_ptr, next.h_ptr); - - // Assert that is_real is a bool. - builder.assert_bool(local.is_real); - - // If this row is real and not the last cycle, then next row should also be real. - builder - .when_transition() - .when(local.is_real) - .when_not(local.is_last_row) - .assert_one(next.is_real); - - // Once the is_real flag is changed to false, it should not be changed back. - builder - .when_transition() - .when_not(local.is_real) - .assert_zero(next.is_real); - - // Assert that the table ends in nonreal columns. Since each compress ecall is 80 cycles and - // the table is padded to a power of 2, the last row of the table should always be padding. - builder.when_last_row().assert_zero(local.is_real); - } - - /// Constrains that memory address is correct and that memory is correctly written/read. - fn eval_memory( - &self, - builder: &mut AB, - local: &Sha512CompressCols, - ) { - builder.eval_memory_access( + .assert_eq(reduced_next_i, local.i + AB::Expr::one()); + builder.send_byte( + ByteOpcode::LTU.as_field::(), + AB::F::one(), + local.i, + AB::F::from_canonical_usize(80), local.shard, local.channel, - local.clk + local.is_finalize, - local.mem_addr, - &local.mem, - local.is_initialize + local.is_compression + local.is_finalize, - ); - - // Calculate the current cycle_num. - let mut cycle_num = AB::Expr::zero(); - for i in 0..10 { - cycle_num += local.octet_num[i] * AB::Expr::from_canonical_usize(i); - } - - // Calculate the current step of the cycle 8. - let mut cycle_step = AB::Expr::zero(); - for i in 0..8 { - cycle_step += local.octet[i] * AB::Expr::from_canonical_usize(i); - } - - // Verify correct mem address for initialize phase - builder.when(local.is_initialize).assert_eq( - local.mem_addr, - local.h_ptr + cycle_step.clone() * AB::Expr::from_canonical_u32(4), - ); - - // Verify correct mem address for compression phase - builder.when(local.is_compression).assert_eq( - local.mem_addr, - local.w_ptr - + (((cycle_num - AB::Expr::one()) * AB::Expr::from_canonical_u32(8)) - + cycle_step.clone()) - * AB::Expr::from_canonical_u32(4), - ); - - // Verify correct mem address for finalize phase - builder.when(local.is_finalize).assert_eq( - local.mem_addr, - local.h_ptr + cycle_step.clone() * AB::Expr::from_canonical_u32(4), - ); - - // In the initialize phase, verify that local.a, local.b, ... is correctly read from memory - // and does not change - let vars = [ - local.a, local.b, local.c, local.d, local.e, local.f, local.g, local.h, - ]; - for (i, var) in vars.iter().enumerate() { - builder - .when(local.is_initialize) - .when(local.octet[i]) - .assert_word_eq(*var, *local.mem.prev_value()); - builder - .when(local.is_initialize) - .when(local.octet[i]) - .assert_word_eq(*var, *local.mem.value()); + local.is_real, + ); + + for j in 0..2 { + // Assert `w_i` was read correctly. + builder.eval_memory_access( + local.shard, + local.channel, + local.clk, + local.w_ptr + + local.i * AB::F::from_canonical_u32(8) + + AB::F::from_canonical_u32(j as u32 * 4), + &local.w_i[j], + local.is_real, + ); + // Assert `k_i` was read correctly. + builder.eval_memory_access( + local.shard, + local.channel, + local.clk, + local.h_ptr + + AB::F::from_canonical_u32(9 * 8) + + local.i * AB::F::from_canonical_u32(8) + + AB::F::from_canonical_u32(j as u32 * 4), + &local.k_i[j], + local.is_real, + ); + // Assert `h` was read correctly - the result is checked at the end. + for m in 0..8 { + builder.eval_memory_access( + local.shard, + local.channel, + local.clk, + local.h_ptr + AB::F::from_canonical_u32(m as u32 * 8 + j as u32 * 4), + &local.h[m * 2 + j], + local.is_real, + ); + } } - - // During compression, verify that memory is read only and does not change. - builder - .when(local.is_compression) - .assert_word_eq(*local.mem.prev_value(), *local.mem.value()); - - // In the finalize phase, verify that the correct value is written to memory. - builder - .when(local.is_finalize) - .assert_word_eq(*local.mem.value(), local.finalize_add.value); } fn eval_compression_ops( &self, builder: &mut AB, local: &Sha512CompressCols, - next: &Sha512CompressCols, ) { - // Constrain k column which loops over 64 constant values. - for i in 0..64 { - let octet_num = i / 8; - let inner_index = i % 8; - builder - .when(local.octet_num[octet_num + 1] * local.octet[inner_index]) - .assert_all_eq(local.k, Word::::from(SHA512_COMPRESS_K[i])); + let k_i_lo = local.k_i[0].value(); + let k_i_hi = local.k_i[1].value(); + let k_i: Word64 = k_i_lo.into_iter().chain(*k_i_hi).collect(); + + let w_i_lo = local.w_i[0].value(); + let w_i_hi = local.w_i[1].value(); + let w_i: Word64 = w_i_lo.into_iter().chain(*w_i_hi).collect(); + + // Assemble the loaded state into `Word64`s. + fn helper(local: &Sha512CompressCols, i: usize) -> Word64 { + local.h[i * 2] + .prev_value() + .clone() + .into_iter() + .chain(local.h[i * 2 + 1].prev_value().clone()) + .collect() } - - // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25). - // Calculate e rightrotate 6. - FixedRotateRightOperation::::eval( + let a = helper(local, 0); + let b = helper(local, 1); + let c = helper(local, 2); + let d = helper(local, 3); + let e = helper(local, 4); + let f = helper(local, 5); + let g = helper(local, 6); + let h = helper(local, 7); + + // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41) + // Calculate e rightrotate 14. + FixedRotateRight64Operation::::eval( builder, - local.e, - 6, - local.e_rr_6, + e, + 14, + local.e_rr_14, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate e rightrotate 11. - FixedRotateRightOperation::::eval( + // Calculate e rightrotate 18. + FixedRotateRight64Operation::::eval( builder, - local.e, - 11, - local.e_rr_11, + e, + 18, + local.e_rr_18, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate e rightrotate 25. - FixedRotateRightOperation::::eval( + // Calculate e rightrotate 41. + FixedRotateRight64Operation::::eval( builder, - local.e, - 25, - local.e_rr_25, + e, + 41, + local.e_rr_41, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate (e rightrotate 6) xor (e rightrotate 11). - XorOperation::::eval( + // Calculate (e rightrotate 14) xor (e rightrotate 18). + Xor64Operation::::eval( builder, - local.e_rr_6.value, - local.e_rr_11.value, + local.e_rr_14.value, + local.e_rr_18.value, local.s1_intermediate, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate S1 := ((e rightrotate 6) xor (e rightrotate 11)) xor (e rightrotate 25). - XorOperation::::eval( + // Calculate S1 := ((e rightrotate 14) xor (e rightrotate 18)) xor (e rightrotate 41). + Xor64Operation::::eval( builder, local.s1_intermediate.value, - local.e_rr_25.value, + local.e_rr_41.value, local.s1, local.shard, &local.channel, - local.is_compression, + local.is_real, ); // Calculate ch := (e and f) xor ((not e) and g). // Calculate e and f. - AndOperation::::eval( + And64Operation::::eval( builder, - local.e, - local.f, + e, + f, local.e_and_f, local.shard, local.channel, - local.is_compression, + local.is_real, ); // Calculate not e. - NotOperation::::eval( + Not64Operation::::eval( builder, - local.e, + e, local.e_not, local.shard, local.channel, - local.is_compression, + local.is_real, ); // Calculate (not e) and g. - AndOperation::::eval( + And64Operation::::eval( builder, local.e_not.value, - local.g, + g, local.e_not_and_g, local.shard, local.channel, - local.is_compression, + local.is_real, ); // Calculate ch := (e and f) xor ((not e) and g). - XorOperation::::eval( + Xor64Operation::::eval( builder, local.e_and_f.value, local.e_not_and_g.value, local.ch, local.shard, &local.channel, - local.is_compression, + local.is_real, ); // Calculate temp1 := h + S1 + ch + k[i] + w[i]. - Add5Operation::::eval( + Add64Operation::::eval( + builder, + h, + local.s1.value, + local.temp1[0], + local.shard, + local.channel, + local.is_real.into(), + ); + Add64Operation::::eval( + builder, + local.temp1[0].value, + local.ch.value, + local.temp1[1], + local.shard, + local.channel, + local.is_real.into(), + ); + Add64Operation::::eval( + builder, + local.temp1[1].value, + k_i, + local.temp1[2], + local.shard, + local.channel, + local.is_real.into(), + ); + Add64Operation::::eval( builder, - &[ - local.h, - local.s1.value, - local.ch.value, - local.k, - local.mem.access.value, - ], + local.temp1[2].value, + w_i, + local.temp1[3], local.shard, local.channel, - local.is_compression, - local.temp1, + local.is_real.into(), ); - // Calculate S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22). - // Calculate a rightrotate 2. - FixedRotateRightOperation::::eval( + // Calculate S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39). + // Calculate a rightrotate 28. + FixedRotateRight64Operation::::eval( builder, - local.a, - 2, - local.a_rr_2, + a, + 28, + local.a_rr_28, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate a rightrotate 13. - FixedRotateRightOperation::::eval( + // Calculate a rightrotate 34. + FixedRotateRight64Operation::::eval( builder, - local.a, - 13, - local.a_rr_13, + a, + 34, + local.a_rr_34, local.shard, &local.channel, - local.is_compression, + local.is_real, ); // Calculate a rightrotate 22. - FixedRotateRightOperation::::eval( + FixedRotateRight64Operation::::eval( builder, - local.a, - 22, - local.a_rr_22, + a, + 39, + local.a_rr_39, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate (a rightrotate 2) xor (a rightrotate 13). - XorOperation::::eval( + // Calculate (a rightrotate 28) xor (a rightrotate 34). + Xor64Operation::::eval( builder, - local.a_rr_2.value, - local.a_rr_13.value, + local.a_rr_28.value, + local.a_rr_34.value, local.s0_intermediate, local.shard, &local.channel, - local.is_compression, + local.is_real, ); - // Calculate S0 := ((a rightrotate 2) xor (a rightrotate 13)) xor (a rightrotate 22). - XorOperation::::eval( + // Calculate S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39). + Xor64Operation::::eval( builder, local.s0_intermediate.value, - local.a_rr_22.value, + local.a_rr_39.value, local.s0, local.shard, &local.channel, - local.is_compression, + local.is_real, ); // Calculate maj := (a and b) xor (a and c) xor (b and c). // Calculate a and b. - AndOperation::::eval( + And64Operation::::eval( builder, - local.a, - local.b, + a, + b, local.a_and_b, local.shard, local.channel, - local.is_compression, + local.is_real, ); // Calculate a and c. - AndOperation::::eval( + And64Operation::::eval( builder, - local.a, - local.c, + a, + c, local.a_and_c, local.shard, local.channel, - local.is_compression, + local.is_real, ); // Calculate b and c. - AndOperation::::eval( + And64Operation::::eval( builder, - local.b, - local.c, + b, + c, local.b_and_c, local.shard, local.channel, - local.is_compression, + local.is_real, ); // Calculate (a and b) xor (a and c). - XorOperation::::eval( + Xor64Operation::::eval( builder, local.a_and_b.value, local.a_and_c.value, local.maj_intermediate, local.shard, &local.channel, - local.is_compression, + local.is_real, ); // Calculate maj := ((a and b) xor (a and c)) xor (b and c). - XorOperation::::eval( + Xor64Operation::::eval( builder, local.maj_intermediate.value, local.b_and_c.value, local.maj, local.shard, &local.channel, - local.is_compression, + local.is_real, ); // Calculate temp2 := s0 + maj. - AddOperation::::eval( + Add64Operation::::eval( builder, local.s0.value, local.maj.value, local.temp2, local.shard, local.channel, - local.is_compression.into(), + local.is_real.into(), ); // Calculate d + temp1 for the new value of e. - AddOperation::::eval( + Add64Operation::::eval( builder, - local.d, - local.temp1.value, + d, + local.temp1[3].value, local.d_add_temp1, local.shard, local.channel, - local.is_compression.into(), + local.is_real.into(), ); // Calculate temp1 + temp2 for the new value of a. - AddOperation::::eval( + Add64Operation::::eval( builder, - local.temp1.value, + local.temp1[3].value, local.temp2.value, local.temp1_add_temp2, local.shard, local.channel, - local.is_compression.into(), + local.is_real.into(), ); + // Assert the values were correctly updated according to: // h := g // g := f // f := e @@ -564,75 +442,43 @@ impl Sha512CompressChip { // c := b // b := a // a := temp1 + temp2 - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.h, local.g); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.g, local.f); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.f, local.e); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.e, local.d_add_temp1.value); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.d, local.c); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.c, local.b); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.b, local.a); - builder - .when_transition() - .when(local.is_compression) - .assert_word_eq(next.a, local.temp1_add_temp2.value); - } - fn eval_finalize_ops( - &self, - builder: &mut AB, - local: &Sha512CompressCols, - ) { - // In the finalize phase, need to execute h[0] + a, h[1] + b, ..., h[7] + h, for each of the - // phase's 8 rows. - // We can get the needed operand (a,b,c,...,h) by doing an inner product between octet and - // [a,b,c,...,h] which will act as a selector. - let add_operands = [ - local.a, local.b, local.c, local.d, local.e, local.f, local.g, local.h, - ]; - let zero = AB::Expr::zero(); - let mut filtered_operand = Word([zero.clone(), zero.clone(), zero.clone(), zero]); - for (i, operand) in local.octet.iter().zip(add_operands.iter()) { - for j in 0..4 { - filtered_operand.0[j] += *i * operand.0[j]; - } + fn helper2(local: &Sha512CompressCols, i: usize) -> Word64 { + local.h[i * 2] + .value() + .clone() + .into_iter() + .chain(local.h[i * 2 + 1].value().clone()) + .collect() + } + let next_a = helper2(local, 0); + let next_b = helper2(local, 1); + let next_c = helper2(local, 2); + let next_d = helper2(local, 3); + let next_e = helper2(local, 4); + let next_f = helper2(local, 5); + let next_g = helper2(local, 6); + let next_h = helper2(local, 7); + + fn assert_word64_eq( + builder: &mut AB, + is_real: AB::Var, + left: Word64, + right: Word64, + ) { + let l = left.to_le_words(); + let r = right.to_le_words(); + builder.when(is_real).assert_word_eq(l[0], r[0]); + builder.when(is_real).assert_word_eq(l[1], r[1]); } - builder - .when(local.is_finalize) - .assert_word_eq(filtered_operand, local.finalized_operand.map(|x| x.into())); - - // finalize_add.result = h[i] + finalized_operand - AddOperation::::eval( - builder, - local.mem.prev_value, - local.finalized_operand, - local.finalize_add, - local.shard, - local.channel, - local.is_finalize.into(), - ); - - // Memory write is constrained in constrain_memory. + assert_word64_eq(builder, local.is_real, next_h, g); + assert_word64_eq(builder, local.is_real, next_g, f); + assert_word64_eq(builder, local.is_real, next_f, e); + assert_word64_eq(builder, local.is_real, next_e, local.d_add_temp1.value); + assert_word64_eq(builder, local.is_real, next_d, c); + assert_word64_eq(builder, local.is_real, next_c, b); + assert_word64_eq(builder, local.is_real, next_b, a); + assert_word64_eq(builder, local.is_real, next_a, local.temp1_add_temp2.value); } } diff --git a/core/src/syscall/precompiles/sha512/compress/columns.rs b/core/src/syscall/precompiles/sha512/compress/columns.rs index 5af27822f..ae562b23e 100644 --- a/core/src/syscall/precompiles/sha512/compress/columns.rs +++ b/core/src/syscall/precompiles/sha512/compress/columns.rs @@ -3,11 +3,9 @@ use std::mem::size_of; use sphinx_derive::AlignedBorrow; use crate::{ - air::Word, - memory::MemoryReadWriteCols, + memory::{MemoryReadCols, MemoryWriteCols}, operations::{ - Add5Operation, AddOperation, AndOperation, FixedRotateRightOperation, NotOperation, - XorOperation, + Add64Operation, And64Operation, FixedRotateRight64Operation, Not64Operation, Xor64Operation, }, }; @@ -32,82 +30,50 @@ pub struct Sha512CompressCols { pub w_ptr: T, pub h_ptr: T, - pub start: T, + pub i_mem: MemoryWriteCols, + pub i: T, + pub w_i: [MemoryReadCols; 2], + pub k_i: [MemoryReadCols; 2], - /// Which cycle within the octet we are currently processing. - pub octet: [T; 8], + pub h: [MemoryWriteCols; 16], - /// This will specify which octet we are currently processing. - /// - The first octet is for initialize. - /// - The next 8 octets are for compress. - /// - The last octet is for finalize. - pub octet_num: [T; 10], + pub e_rr_14: FixedRotateRight64Operation, + pub e_rr_18: FixedRotateRight64Operation, + pub e_rr_41: FixedRotateRight64Operation, + pub s1_intermediate: Xor64Operation, + /// `S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)`. + pub s1: Xor64Operation, - /// Memory access. During init and compression, this is read only. During finalize, this is - /// used to write the result into memory. - pub mem: MemoryReadWriteCols, - /// Current memory address being written/read. During init and finalize, this is A-H. During - /// compression, this is w[i] being read only. - pub mem_addr: T, - - pub a: Word, - pub b: Word, - pub c: Word, - pub d: Word, - pub e: Word, - pub f: Word, - pub g: Word, - pub h: Word, - - /// Current value of K[i]. This is a constant array that loops around every 64 iterations. - pub k: Word, - - pub e_rr_6: FixedRotateRightOperation, - pub e_rr_11: FixedRotateRightOperation, - pub e_rr_25: FixedRotateRightOperation, - pub s1_intermediate: XorOperation, - /// `S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)`. - pub s1: XorOperation, - - pub e_and_f: AndOperation, - pub e_not: NotOperation, - pub e_not_and_g: AndOperation, + pub e_and_f: And64Operation, + pub e_not: Not64Operation, + pub e_not_and_g: And64Operation, /// `ch := (e and f) xor ((not e) and g)`. - pub ch: XorOperation, + pub ch: Xor64Operation, /// `temp1 := h + S1 + ch + k[i] + w[i]`. - pub temp1: Add5Operation, - - pub a_rr_2: FixedRotateRightOperation, - pub a_rr_13: FixedRotateRightOperation, - pub a_rr_22: FixedRotateRightOperation, - pub s0_intermediate: XorOperation, - /// `S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)`. - pub s0: XorOperation, - - pub a_and_b: AndOperation, - pub a_and_c: AndOperation, - pub b_and_c: AndOperation, - pub maj_intermediate: XorOperation, + pub temp1: [Add64Operation; 4], + + pub a_rr_28: FixedRotateRight64Operation, + pub a_rr_34: FixedRotateRight64Operation, + pub a_rr_39: FixedRotateRight64Operation, + pub s0_intermediate: Xor64Operation, + /// `S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)`. + pub s0: Xor64Operation, + + pub a_and_b: And64Operation, + pub a_and_c: And64Operation, + pub b_and_c: And64Operation, + pub maj_intermediate: Xor64Operation, /// `maj := (a and b) xor (a and c) xor (b and c)`. - pub maj: XorOperation, + pub maj: Xor64Operation, /// `temp2 := S0 + maj`. - pub temp2: AddOperation, + pub temp2: Add64Operation, /// The next value of `e` is `d + temp1`. - pub d_add_temp1: AddOperation, + pub d_add_temp1: Add64Operation, /// The next value of `a` is `temp1 + temp2`. - pub temp1_add_temp2: AddOperation, - - /// During finalize, this is one of a-h and is being written into `mem`. - pub finalized_operand: Word, - pub finalize_add: AddOperation, - - pub is_initialize: T, - pub is_compression: T, - pub is_finalize: T, - pub is_last_row: T, + pub temp1_add_temp2: Add64Operation, pub is_real: T, } diff --git a/core/src/syscall/precompiles/sha512/compress/execute.rs b/core/src/syscall/precompiles/sha512/compress/execute.rs index 93d6b2dc6..4fdb95c22 100644 --- a/core/src/syscall/precompiles/sha512/compress/execute.rs +++ b/core/src/syscall/precompiles/sha512/compress/execute.rs @@ -9,7 +9,7 @@ use crate::{ impl Syscall for Sha512CompressChip { fn num_extra_cycles(&self) -> u32 { - 1 + 0 } fn execute(&self, rt: &mut SyscallContext<'_, '_>, arg1: u32, arg2: u32) -> Option { @@ -18,20 +18,39 @@ impl Syscall for Sha512CompressChip { assert_ne!(w_ptr, h_ptr); let start_clk = rt.clk; - let mut h_read_records = Vec::new(); - let mut w_i_read_records = Vec::new(); let mut h_write_records = Vec::new(); + // FIXME + fn u32_vec_to_u64(val: Vec) -> u64 { + u64::from_le_bytes( + val.into_iter() + .flat_map(|x| x.to_le_bytes()) + .collect::>() + .try_into() + .unwrap(), + ) + } + // Execute the "initialize" phase where we read in the h values. - let mut hx = [0u32; 8]; - for i in 0..8 { - let (record, value) = rt.mr(h_ptr + i as u32 * 4); - h_read_records.push(record); - hx[i] = value; + let mut hx = [0u64; 8]; + for j in 0..8 { + let values = rt.slice_unsafe(h_ptr + j * 8, 2); + hx[j as usize] = u32_vec_to_u64(values); } - let mut original_w = Vec::new(); - // Execute the "compress" phase. + // The `i` index is at the end of the `h_ptr` state + let i = rt.word_unsafe(h_ptr + 8 * 8); + assert!(i < 80); + + // The constants `k` are copied by the guest to the end of the state pointer + let (k_i_read_records, k_i) = rt.mr_slice(h_ptr + (9 * 8) + i * 8, 2); + let k_i = u32_vec_to_u64(k_i); + assert_eq!(k_i, SHA512_COMPRESS_K[i as usize]); + + let (w_i_read_records, w_i) = rt.mr_slice(w_ptr + i * 8, 2); + let w_i = u32_vec_to_u64(w_i); + + // Execute the "compress" iteration. let mut a = hx[0]; let mut b = hx[1]; let mut c = hx[2]; @@ -40,40 +59,44 @@ impl Syscall for Sha512CompressChip { let mut f = hx[5]; let mut g = hx[6]; let mut h = hx[7]; - for i in 0..64 { - let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25); - let ch = (e & f) ^ (!e & g); - let (record, w_i) = rt.mr(w_ptr + i * 4); - original_w.push(w_i); - w_i_read_records.push(record); - let temp1 = h - .wrapping_add(s1) - .wrapping_add(ch) - .wrapping_add(SHA512_COMPRESS_K[i as usize]) - .wrapping_add(w_i); - let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22); - let maj = (a & b) ^ (a & c) ^ (b & c); - let temp2 = s0.wrapping_add(maj); - h = g; - g = f; - f = e; - e = d.wrapping_add(temp1); - d = c; - c = b; - b = a; - a = temp1.wrapping_add(temp2); + let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41); + let ch = (e & f) ^ (!e & g); + let temp1 = h + .wrapping_add(s1) + .wrapping_add(ch) + .wrapping_add(SHA512_COMPRESS_K[i as usize]) + .wrapping_add(w_i); + let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39); + let maj = (a & b) ^ (a & c) ^ (b & c); + let temp2 = s0.wrapping_add(maj); + + h = g; + g = f; + f = e; + e = d.wrapping_add(temp1); + d = c; + c = b; + b = a; + a = temp1.wrapping_add(temp2); + + // FIXME + fn u64_to_u32x2(n: u64) -> [u32; 2] { + let n = n.to_le_bytes(); + [ + u32::from_le_bytes(n[..4].try_into().unwrap()), + u32::from_le_bytes(n[4..].try_into().unwrap()), + ] } - // Increment the clk by 1 before writing to h, since we've already read h at the start_clk - // during the initialization phase. - rt.clk += 1; - // Execute the "finalize" phase. + // Execute the "finalize" phase of updating the memory. let v = [a, b, c, d, e, f, g, h]; - for i in 0..8 { - let record = rt.mw(h_ptr + i as u32 * 4, hx[i].wrapping_add(v[i])); + let v: Vec = v.into_iter().flat_map(u64_to_u32x2).collect(); + for i in 0..16 { + let record = rt.mw(h_ptr + i as u32 * 4, v[i]); h_write_records.push(record); } + let i_write_record = rt.mw(h_ptr + 8 * 8, i.wrapping_add(1)); // Push the SHA512 extend event. let lookup_id = rt.syscall_lookup_id; @@ -88,11 +111,14 @@ impl Syscall for Sha512CompressChip { clk: start_clk, w_ptr, h_ptr, - w: original_w, + w_i, + i, + k_i, h: hx, - h_read_records: h_read_records.try_into().unwrap(), - w_i_read_records, + w_i_read_records: w_i_read_records.try_into().unwrap(), h_write_records: h_write_records.try_into().unwrap(), + k_i_read_records: k_i_read_records.try_into().unwrap(), + i_write_record, }); None diff --git a/core/src/syscall/precompiles/sha512/compress/mod.rs b/core/src/syscall/precompiles/sha512/compress/mod.rs index 7eb7013aa..e9c903e84 100644 --- a/core/src/syscall/precompiles/sha512/compress/mod.rs +++ b/core/src/syscall/precompiles/sha512/compress/mod.rs @@ -98,11 +98,14 @@ pub struct Sha512CompressEvent { pub clk: u32, pub w_ptr: u32, pub h_ptr: u32, - pub w: Vec, - pub h: [u32; 8], - pub h_read_records: [MemoryReadRecord; 8], - pub w_i_read_records: Vec, - pub h_write_records: [MemoryWriteRecord; 8], + pub i: u32, + pub w_i: u64, + pub k_i: u64, + pub h: [u64; 8], + pub w_i_read_records: [MemoryReadRecord; 2], + pub k_i_read_records: [MemoryReadRecord; 2], + pub h_write_records: [MemoryWriteRecord; 16], + pub i_write_record: MemoryWriteRecord, } /// Implements the SHA compress operation which loops over 0 = [0, 63] and modifies A-H in each @@ -112,6 +115,7 @@ pub struct Sha512CompressEvent { /// In the AIR, each SHA compress syscall takes up 80 rows. The first and last 8 rows are for /// initialization and finalize respectively. The middle 64 rows are for compression. Each row /// operates over a single memory word. +/// FIXME #[derive(Default)] pub struct Sha512CompressChip; @@ -129,20 +133,58 @@ pub mod compress_tests { utils::{run_test, setup_logger, tests::SHA512_COMPRESS_ELF}, }; + use super::SHA512_COMPRESS_K; + + // FIXME + fn u64_to_u32x2(n: u64) -> [u32; 2] { + let n = n.to_le_bytes(); + [ + u32::from_le_bytes(n[..4].try_into().unwrap()), + u32::from_le_bytes(n[4..].try_into().unwrap()), + ] + } + pub fn sha512_compress_program() -> Program { let w_ptr = 100; - let h_ptr = 1000; - let mut instructions = vec![Instruction::new(Opcode::ADD, 29, 0, 5, false, true)]; - for i in 0..64 { + let h_ptr = 100000; + let mut instructions = vec![ + Instruction::new(Opcode::ADD, 29, 0, 5, false, true), + Instruction::new(Opcode::ADD, 28, 0, 0, false, true), + ]; + for i in 0..80 { + instructions.extend(vec![ + Instruction::new(Opcode::ADD, 30, 0, w_ptr + i * 8, false, true), + Instruction::new(Opcode::SW, 29, 30, 0, false, true), + Instruction::new(Opcode::ADD, 30, 0, w_ptr + i * 8 + 4, false, true), + Instruction::new(Opcode::SW, 28, 30, 0, false, true), + ]); + } + // Fill out state and the `i` value after it + for i in 0..9 { instructions.extend(vec![ - Instruction::new(Opcode::ADD, 30, 0, w_ptr + i * 4, false, true), + Instruction::new(Opcode::ADD, 30, 0, h_ptr + i * 8, false, true), Instruction::new(Opcode::SW, 29, 30, 0, false, true), + Instruction::new(Opcode::ADD, 30, 0, h_ptr + i * 8 + 4, false, true), + Instruction::new(Opcode::SW, 28, 30, 0, false, true), ]); } - for i in 0..8 { + // Fill out the constants `k` + for i in 0..80 { + let k_i = u64_to_u32x2(SHA512_COMPRESS_K[i]); instructions.extend(vec![ - Instruction::new(Opcode::ADD, 30, 0, h_ptr + i * 4, false, true), + Instruction::new(Opcode::ADD, 29, 0, k_i[0], false, true), + Instruction::new(Opcode::ADD, 28, 0, k_i[1], false, true), + Instruction::new(Opcode::ADD, 30, 0, h_ptr + 72 + i as u32 * 8, false, true), Instruction::new(Opcode::SW, 29, 30, 0, false, true), + Instruction::new( + Opcode::ADD, + 30, + 0, + h_ptr + 72 + i as u32 * 8 + 4, + false, + true, + ), + Instruction::new(Opcode::SW, 28, 30, 0, false, true), ]); } instructions.extend(vec![ diff --git a/core/src/syscall/precompiles/sha512/compress/trace.rs b/core/src/syscall/precompiles/sha512/compress/trace.rs index 3ec539e1e..220764f85 100644 --- a/core/src/syscall/precompiles/sha512/compress/trace.rs +++ b/core/src/syscall/precompiles/sha512/compress/trace.rs @@ -6,11 +6,11 @@ use p3_matrix::Matrix; use super::{ columns::{Sha512CompressCols, NUM_SHA512_COMPRESS_COLS}, - Sha512CompressChip, Sha512CompressEvent, SHA512_COMPRESS_K, + Sha512CompressChip, Sha512CompressEvent, }; use crate::{ - air::{EventLens, MachineAir, WithEvents, Word}, - bytes::event::ByteRecord, + air::{EventLens, MachineAir, WithEvents}, + bytes::{event::ByteRecord, ByteLookupEvent, ByteOpcode}, runtime::{ExecutionRecord, Program}, utils::pad_rows, }; @@ -36,249 +36,137 @@ impl MachineAir for Sha512CompressChip { let mut rows = Vec::new(); let mut new_byte_lookup_events = Vec::new(); - for i in 0..input.events().len() { - let mut event = input.events()[i].clone(); + for evt_idx in 0..input.events().len() { + let event = input.events()[evt_idx].clone(); let shard = event.shard; let channel = event.channel; - let og_h = event.h; - - let mut octet_num_idx = 0; - - // Load a, b, c, d, e, f, g, h. - for j in 0..8usize { - let mut row = [F::zero(); NUM_SHA512_COMPRESS_COLS]; - let cols: &mut Sha512CompressCols = row.as_mut_slice().borrow_mut(); - - cols.shard = F::from_canonical_u32(event.shard); - cols.channel = F::from_canonical_u32(event.channel); - cols.clk = F::from_canonical_u32(event.clk); - cols.w_ptr = F::from_canonical_u32(event.w_ptr); - cols.h_ptr = F::from_canonical_u32(event.h_ptr); - - cols.octet[j] = F::one(); - cols.octet_num[octet_num_idx] = F::one(); - cols.is_initialize = F::one(); + let mut row = [F::zero(); NUM_SHA512_COMPRESS_COLS]; + let cols: &mut Sha512CompressCols = row.as_mut_slice().borrow_mut(); - cols.mem.populate_read( - channel, - event.h_read_records[j], + cols.shard = F::from_canonical_u32(event.shard); + cols.channel = F::from_canonical_u32(event.channel); + cols.clk = F::from_canonical_u32(event.clk); + cols.w_ptr = F::from_canonical_u32(event.w_ptr); + cols.h_ptr = F::from_canonical_u32(event.h_ptr); + cols.i = F::from_canonical_u32(event.i); + cols.is_real = F::one(); + + // i < 80 + new_byte_lookup_events.push(ByteLookupEvent { + opcode: ByteOpcode::LTU, + shard, + channel: event.channel, + a1: 1, + a2: 0, + b: event.i, + c: 80, + }); + + cols.k_i[0].populate( + event.channel, + event.k_i_read_records[0], + &mut new_byte_lookup_events, + ); + cols.k_i[1].populate( + event.channel, + event.k_i_read_records[1], + &mut new_byte_lookup_events, + ); + + cols.w_i[0].populate( + event.channel, + event.w_i_read_records[0], + &mut new_byte_lookup_events, + ); + cols.w_i[1].populate( + event.channel, + event.w_i_read_records[1], + &mut new_byte_lookup_events, + ); + + cols.i_mem.populate( + event.channel, + event.i_write_record, + &mut new_byte_lookup_events, + ); + + for j in 0..16 { + cols.h[j].populate( + event.channel, + event.h_write_records[j], &mut new_byte_lookup_events, ); - cols.mem_addr = F::from_canonical_u32(event.h_ptr + (j * 4) as u32); - - cols.a = Word::from(event.h_read_records[0].value); - cols.b = Word::from(event.h_read_records[1].value); - cols.c = Word::from(event.h_read_records[2].value); - cols.d = Word::from(event.h_read_records[3].value); - cols.e = Word::from(event.h_read_records[4].value); - cols.f = Word::from(event.h_read_records[5].value); - cols.g = Word::from(event.h_read_records[6].value); - cols.h = Word::from(event.h_read_records[7].value); - - cols.is_real = F::one(); - cols.start = cols.is_real * cols.octet_num[0] * cols.octet[0]; - rows.push(row); } // Performs the compress operation. - for j in 0..64 { - if j % 8 == 0 { - octet_num_idx += 1; - } - let mut row = [F::zero(); NUM_SHA512_COMPRESS_COLS]; - let cols: &mut Sha512CompressCols = row.as_mut_slice().borrow_mut(); - - cols.k = Word::from(SHA512_COMPRESS_K[j]); - cols.is_compression = F::one(); - cols.octet[j % 8] = F::one(); - cols.octet_num[octet_num_idx] = F::one(); - - cols.shard = F::from_canonical_u32(event.shard); - cols.channel = F::from_canonical_u32(event.channel); - cols.clk = F::from_canonical_u32(event.clk); - cols.w_ptr = F::from_canonical_u32(event.w_ptr); - cols.h_ptr = F::from_canonical_u32(event.h_ptr); - cols.mem.populate_read( - channel, - event.w_i_read_records[j], - &mut new_byte_lookup_events, - ); - cols.mem_addr = F::from_canonical_u32(event.w_ptr + (j * 4) as u32); - - let a = event.h[0]; - let b = event.h[1]; - let c = event.h[2]; - let d = event.h[3]; - let e = event.h[4]; - let f = event.h[5]; - let g = event.h[6]; - let h = event.h[7]; - cols.a = Word::from(a); - cols.b = Word::from(b); - cols.c = Word::from(c); - cols.d = Word::from(d); - cols.e = Word::from(e); - cols.f = Word::from(f); - cols.g = Word::from(g); - cols.h = Word::from(h); - - let e_rr_6 = cols.e_rr_6.populate(output, shard, channel, e, 6); - let e_rr_11 = cols.e_rr_11.populate(output, shard, channel, e, 11); - let e_rr_25 = cols.e_rr_25.populate(output, shard, channel, e, 25); - let s1_intermediate = cols - .s1_intermediate - .populate(output, shard, channel, e_rr_6, e_rr_11); - let s1 = cols - .s1 - .populate(output, shard, channel, s1_intermediate, e_rr_25); - - let e_and_f = cols.e_and_f.populate(output, shard, channel, e, f); - let e_not = cols.e_not.populate(output, shard, channel, e); - let e_not_and_g = cols.e_not_and_g.populate(output, shard, channel, e_not, g); - let ch = cols - .ch - .populate(output, shard, channel, e_and_f, e_not_and_g); - - let temp1 = cols.temp1.populate( - output, - shard, - channel, - h, - s1, - ch, - event.w[j], - SHA512_COMPRESS_K[j], - ); - - let a_rr_2 = cols.a_rr_2.populate(output, shard, channel, a, 2); - let a_rr_13 = cols.a_rr_13.populate(output, shard, channel, a, 13); - let a_rr_22 = cols.a_rr_22.populate(output, shard, channel, a, 22); - let s0_intermediate = cols - .s0_intermediate - .populate(output, shard, channel, a_rr_2, a_rr_13); - let s0 = cols - .s0 - .populate(output, shard, channel, s0_intermediate, a_rr_22); - - let a_and_b = cols.a_and_b.populate(output, shard, channel, a, b); - let a_and_c = cols.a_and_c.populate(output, shard, channel, a, c); - let b_and_c = cols.b_and_c.populate(output, shard, channel, b, c); - let maj_intermediate = cols - .maj_intermediate - .populate(output, shard, channel, a_and_b, a_and_c); - let maj = cols - .maj - .populate(output, shard, channel, maj_intermediate, b_and_c); - - let temp2 = cols.temp2.populate(output, shard, channel, s0, maj); - - let d_add_temp1 = cols.d_add_temp1.populate(output, shard, channel, d, temp1); - let temp1_add_temp2 = cols - .temp1_add_temp2 - .populate(output, shard, channel, temp1, temp2); - - event.h[7] = g; - event.h[6] = f; - event.h[5] = e; - event.h[4] = d_add_temp1; - event.h[3] = c; - event.h[2] = b; - event.h[1] = a; - event.h[0] = temp1_add_temp2; - - cols.is_real = F::one(); - cols.start = cols.is_real * cols.octet_num[0] * cols.octet[0]; - - rows.push(row); - } - - let mut v: [u32; 8] = [0, 1, 2, 3, 4, 5, 6, 7].map(|i| event.h[i]); - - octet_num_idx += 1; - // Store a, b, c, d, e, f, g, h. - for j in 0..8usize { - let mut row = [F::zero(); NUM_SHA512_COMPRESS_COLS]; - let cols: &mut Sha512CompressCols = row.as_mut_slice().borrow_mut(); - - cols.shard = F::from_canonical_u32(event.shard); - cols.channel = F::from_canonical_u32(event.channel); - cols.clk = F::from_canonical_u32(event.clk); - cols.w_ptr = F::from_canonical_u32(event.w_ptr); - cols.h_ptr = F::from_canonical_u32(event.h_ptr); - - cols.octet[j] = F::one(); - cols.octet_num[octet_num_idx] = F::one(); - cols.is_finalize = F::one(); - - cols.finalize_add - .populate(output, shard, channel, og_h[j], event.h[j]); - cols.mem.populate_write( - channel, - event.h_write_records[j], - &mut new_byte_lookup_events, - ); - cols.mem_addr = F::from_canonical_u32(event.h_ptr + (j * 4) as u32); - - v[j] = event.h[j]; - cols.a = Word::from(v[0]); - cols.b = Word::from(v[1]); - cols.c = Word::from(v[2]); - cols.d = Word::from(v[3]); - cols.e = Word::from(v[4]); - cols.f = Word::from(v[5]); - cols.g = Word::from(v[6]); - cols.h = Word::from(v[7]); - - match j { - 0 => cols.finalized_operand = cols.a, - 1 => cols.finalized_operand = cols.b, - 2 => cols.finalized_operand = cols.c, - 3 => cols.finalized_operand = cols.d, - 4 => cols.finalized_operand = cols.e, - 5 => cols.finalized_operand = cols.f, - 6 => cols.finalized_operand = cols.g, - 7 => cols.finalized_operand = cols.h, - _ => panic!("unsupported j"), - }; - - cols.is_real = F::one(); - cols.is_last_row = cols.octet[7] * cols.octet_num[9]; - cols.start = cols.is_real * cols.octet_num[0] * cols.octet[0]; - - rows.push(row); - } + let a = event.h[0]; + let b = event.h[1]; + let c = event.h[2]; + let d = event.h[3]; + let e = event.h[4]; + let f = event.h[5]; + let g = event.h[6]; + let h = event.h[7]; + + let e_rr_14 = cols.e_rr_14.populate(output, shard, channel, e, 14); + let e_rr_18 = cols.e_rr_18.populate(output, shard, channel, e, 18); + let e_rr_41 = cols.e_rr_41.populate(output, shard, channel, e, 41); + let s1_intermediate = cols + .s1_intermediate + .populate(output, shard, channel, e_rr_14, e_rr_18); + let s1 = cols + .s1 + .populate(output, shard, channel, s1_intermediate, e_rr_41); + + let e_and_f = cols.e_and_f.populate(output, shard, channel, e, f); + let e_not = cols.e_not.populate(output, shard, channel, e); + let e_not_and_g = cols.e_not_and_g.populate(output, shard, channel, e_not, g); + let ch = cols + .ch + .populate(output, shard, channel, e_and_f, e_not_and_g); + + let temp1_0 = cols.temp1[0].populate(output, shard, channel, h, s1); + let temp1_1 = cols.temp1[1].populate(output, shard, channel, temp1_0, ch); + let temp1_2 = cols.temp1[2].populate(output, shard, channel, temp1_1, event.k_i); + let temp1 = cols.temp1[3].populate(output, shard, channel, temp1_2, event.w_i); + + let a_rr_28 = cols.a_rr_28.populate(output, shard, channel, a, 28); + let a_rr_34 = cols.a_rr_34.populate(output, shard, channel, a, 34); + let a_rr_39 = cols.a_rr_39.populate(output, shard, channel, a, 39); + let s0_intermediate = cols + .s0_intermediate + .populate(output, shard, channel, a_rr_28, a_rr_34); + let s0 = cols + .s0 + .populate(output, shard, channel, s0_intermediate, a_rr_39); + + let a_and_b = cols.a_and_b.populate(output, shard, channel, a, b); + let a_and_c = cols.a_and_c.populate(output, shard, channel, a, c); + let b_and_c = cols.b_and_c.populate(output, shard, channel, b, c); + let maj_intermediate = cols + .maj_intermediate + .populate(output, shard, channel, a_and_b, a_and_c); + let maj = cols + .maj + .populate(output, shard, channel, maj_intermediate, b_and_c); + + let temp2 = cols.temp2.populate(output, shard, channel, s0, maj); + + let d_add_temp1 = cols.d_add_temp1.populate(output, shard, channel, d, temp1); + let temp1_add_temp2 = cols + .temp1_add_temp2 + .populate(output, shard, channel, temp1, temp2); + + let out_h = [temp1_add_temp2, a, b, c, d_add_temp1, e, f, g]; + + rows.push(row); } output.add_byte_lookup_events(new_byte_lookup_events); - let num_real_rows = rows.len(); - pad_rows(&mut rows, || [F::zero(); NUM_SHA512_COMPRESS_COLS]); - // Set the octet_num and octect columns for the padded rows. - let mut octet_num = 0; - let mut octet = 0; - for row in rows[num_real_rows..].iter_mut() { - let cols: &mut Sha512CompressCols = row.as_mut_slice().borrow_mut(); - cols.octet_num[octet_num] = F::one(); - cols.octet[octet] = F::one(); - - // If in the compression phase, set the k value. - if octet_num != 0 && octet_num != 9 { - let compression_idx = octet_num - 1; - let k_idx = compression_idx * 8 + octet; - cols.k = Word::from(SHA512_COMPRESS_K[k_idx]); - } - - octet = (octet + 1) % 8; - if octet == 0 { - octet_num = (octet_num + 1) % 10; - } - - cols.is_last_row = cols.octet[7] * cols.octet_num[9]; - } - // Convert the trace to a row major matrix. let mut trace = RowMajorMatrix::new( rows.into_iter().flatten().collect::>(), diff --git a/core/src/syscall/precompiles/sha512/extend/air.rs b/core/src/syscall/precompiles/sha512/extend/air.rs index 545486077..660758f24 100644 --- a/core/src/syscall/precompiles/sha512/extend/air.rs +++ b/core/src/syscall/precompiles/sha512/extend/air.rs @@ -38,6 +38,9 @@ where .when_transition() .assert_eq(local.nonce + AB::Expr::one(), next.nonce); + // Assert that is_real is a bool. + builder.assert_bool(local.is_real); + let nb_bytes_in_word64 = AB::F::from_canonical_u32(8); // Check that `15 < i < 80` @@ -321,8 +324,5 @@ where local.i, local.is_real, ); - - // Assert that is_real is a bool. - builder.assert_bool(local.is_real); } } diff --git a/core/src/syscall/precompiles/sha512/extend/trace.rs b/core/src/syscall/precompiles/sha512/extend/trace.rs index da9439e8d..b64d29d61 100644 --- a/core/src/syscall/precompiles/sha512/extend/trace.rs +++ b/core/src/syscall/precompiles/sha512/extend/trace.rs @@ -8,6 +8,7 @@ use crate::{ air::{EventLens, MachineAir, WithEvents}, bytes::{event::ByteRecord, ByteLookupEvent, ByteOpcode}, runtime::{ExecutionRecord, Program}, + utils::pad_rows, }; impl<'a> WithEvents<'a> for Sha512ExtendChip { @@ -206,15 +207,7 @@ impl MachineAir for Sha512ExtendChip { output.add_byte_lookup_events(new_byte_lookup_events); - let nb_rows = rows.len(); - let mut padded_nb_rows = nb_rows.next_power_of_two(); - if padded_nb_rows == 2 || padded_nb_rows == 1 { - padded_nb_rows = 4; - } - for _ in nb_rows..padded_nb_rows { - let row = [F::zero(); NUM_SHA512_EXTEND_COLS]; - rows.push(row); - } + pad_rows(&mut rows, || [F::zero(); NUM_SHA512_EXTEND_COLS]); // Convert the trace to a row major matrix. let mut trace = RowMajorMatrix::new( diff --git a/core/src/syscall/precompiles/sha512/mod.rs b/core/src/syscall/precompiles/sha512/mod.rs index 06f94fe88..29d7bd1a5 100644 --- a/core/src/syscall/precompiles/sha512/mod.rs +++ b/core/src/syscall/precompiles/sha512/mod.rs @@ -1,5 +1,5 @@ -// mod compress; +mod compress; mod extend; -// pub use compress::*; +pub use compress::*; pub use extend::*; diff --git a/core/src/utils/programs.rs b/core/src/utils/programs.rs index 56ee50437..14cd8adbb 100644 --- a/core/src/utils/programs.rs +++ b/core/src/utils/programs.rs @@ -70,8 +70,8 @@ pub mod tests { pub const SHA2_ELF: &[u8] = include_bytes!("../../../tests/sha2/elf/riscv32im-succinct-zkvm-elf"); - // pub const SHA512_COMPRESS_ELF: &[u8] = 512FIXME - // include_bytes!("../../../tests/sha512-compress/elf/riscv32im-succinct-zkvm-elf"); + pub const SHA512_COMPRESS_ELF: &[u8] = + include_bytes!("../../../tests/sha512-compress/elf/riscv32im-succinct-zkvm-elf"); pub const SHA512_EXTEND_ELF: &[u8] = include_bytes!("../../../tests/sha512-extend/elf/riscv32im-succinct-zkvm-elf"); diff --git a/tests/sha512-compress/Cargo.lock b/tests/sha512-compress/Cargo.lock new file mode 100644 index 000000000..b10e41bb8 --- /dev/null +++ b/tests/sha512-compress/Cargo.lock @@ -0,0 +1,509 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "tap", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hybrid-array" +version = "0.2.0-rc.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53668f5da5a41d9eaf4bf7064be46d1ebe6a4e1ceed817f387587b18f2b51047" +dependencies = [ + "typenum", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "signature", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "git+https://github.com/sp1-patches/RustCrypto-hashes?branch=patch-v0.10.8#1f224388fdede7cef649bce0d63876d1a9e3f515" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha512-compress-test" +version = "0.1.0" +dependencies = [ + "sphinx-zkvm", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "sphinx-precompiles" +version = "1.0.0" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "getrandom", + "hybrid-array", + "k256", + "serde", +] + +[[package]] +name = "sphinx-zkvm" +version = "1.0.0" +dependencies = [ + "bincode", + "cfg-if", + "getrandom", + "k256", + "lazy_static", + "libm", + "once_cell", + "rand", + "sha2 0.10.8 (git+https://github.com/sp1-patches/RustCrypto-hashes?branch=patch-v0.10.8)", + "sphinx-precompiles", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/tests/sha512-compress/Cargo.toml b/tests/sha512-compress/Cargo.toml new file mode 100644 index 000000000..2ebdd8216 --- /dev/null +++ b/tests/sha512-compress/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +[package] +version = "0.1.0" +name = "sha512-compress-test" +edition = "2021" + +[dependencies] +sphinx-zkvm = { path = "../../zkvm/entrypoint" } diff --git a/tests/sha512-compress/elf/riscv32im-succinct-zkvm-elf b/tests/sha512-compress/elf/riscv32im-succinct-zkvm-elf new file mode 100755 index 0000000000000000000000000000000000000000..3851372725f598020cc3795a8535af5184d58396 GIT binary patch literal 32412 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