diff --git a/Cargo.lock b/Cargo.lock index 5112eda0f..9999521c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,9 +148,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -168,30 +168,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -629,9 +629,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.10" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -778,9 +778,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" @@ -883,7 +883,7 @@ dependencies = [ [[package]] name = "curta" version = "0.1.0" -source = "git+https://github.com/succinctlabs/curta.git#bd14c11a17827042109dd9ba1771365070ebc98f" +source = "git+https://github.com/succinctlabs/curta.git#c4dd8dd43709134051303f1353dd4131078faca6" dependencies = [ "anyhow", "bincode", @@ -981,9 +981,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", "serde", @@ -1556,9 +1556,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd" +checksum = "8bbb8258be8305fb0237d7b295f47bb24ff1b136a535f473baf40e70468515aa" dependencies = [ "indenter", "once_cell", @@ -1988,9 +1988,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -2354,9 +2354,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -2416,9 +2416,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -2564,7 +2564,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 2.0.39", @@ -2581,9 +2581,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open-fastrlp" @@ -2612,9 +2612,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -2644,9 +2644,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ "cc", "libc", @@ -2662,9 +2662,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", "bitvec", @@ -2676,11 +2676,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 1.0.109", @@ -2908,7 +2908,7 @@ checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" [[package]] name = "plonky2" version = "0.1.4" -source = "git+https://github.com/mir-protocol/plonky2.git#2d0df393a23c4c464d9796b8018170a47c6d61d9" +source = "git+https://github.com/mir-protocol/plonky2.git#56e8395694bd53afa06df40e8d607277e75f446a" dependencies = [ "ahash", "anyhow", @@ -2932,7 +2932,7 @@ dependencies = [ [[package]] name = "plonky2_field" version = "0.1.1" -source = "git+https://github.com/mir-protocol/plonky2.git#2d0df393a23c4c464d9796b8018170a47c6d61d9" +source = "git+https://github.com/mir-protocol/plonky2.git#56e8395694bd53afa06df40e8d607277e75f446a" dependencies = [ "anyhow", "itertools 0.11.0", @@ -2956,7 +2956,7 @@ dependencies = [ [[package]] name = "plonky2_maybe_rayon" version = "0.1.1" -source = "git+https://github.com/mir-protocol/plonky2.git#2d0df393a23c4c464d9796b8018170a47c6d61d9" +source = "git+https://github.com/mir-protocol/plonky2.git#56e8395694bd53afa06df40e8d607277e75f446a" dependencies = [ "rayon", ] @@ -2964,7 +2964,7 @@ dependencies = [ [[package]] name = "plonky2_util" version = "0.1.1" -source = "git+https://github.com/mir-protocol/plonky2.git#2d0df393a23c4c464d9796b8018170a47c6d61d9" +source = "git+https://github.com/mir-protocol/plonky2.git#56e8395694bd53afa06df40e8d607277e75f446a" [[package]] name = "plonky2x" @@ -3080,11 +3080,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" dependencies = [ - "toml_edit 0.20.7", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -3345,9 +3346,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", @@ -3450,25 +3451,25 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.6", + "ring 0.17.7", "rustls-webpki", "sct", ] @@ -3488,7 +3489,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.6", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -3610,7 +3611,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.6", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -4284,21 +4285,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.20.2", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] @@ -4316,20 +4317,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.1.0", "serde", @@ -4387,9 +4377,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" @@ -4443,9 +4433,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -4840,9 +4830,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" dependencies = [ "memchr", ] @@ -4893,18 +4883,18 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", diff --git a/plonky2x/core/src/backend/circuit/serialization/hints.rs b/plonky2x/core/src/backend/circuit/serialization/hints.rs index c44745b9b..f998f621f 100644 --- a/plonky2x/core/src/backend/circuit/serialization/hints.rs +++ b/plonky2x/core/src/backend/circuit/serialization/hints.rs @@ -1,9 +1,6 @@ use core::fmt::Debug; use core::marker::PhantomData; -use curta::chip::hash::blake::blake2b::generator::{ - BLAKE2BAirParameters, BLAKE2BGenerator, BLAKE2BHintGenerator, -}; use curta::machine::hash::sha::sha256::SHA256; use curta::machine::hash::sha::sha512::SHA512; use curta::plonky2::cubic::arithmetic_gate::ArithmeticCubicGenerator; @@ -58,7 +55,8 @@ use crate::frontend::eth::mpt::generators::LteGenerator; use crate::frontend::eth::storage::generators::{ EthBlockGenerator, EthLogGenerator, EthStorageKeyGenerator, EthStorageProofHint, }; -use crate::frontend::hash::blake2::curta::MAX_NUM_CURTA_CHUNKS; +use crate::frontend::hash::blake2::digest_hint::BLAKE2BDigestHint; +use crate::frontend::hash::blake2::proof_hint::BLAKE2BProofHint; use crate::frontend::hash::keccak::keccak256::Keccak256Generator; use crate::frontend::hash::sha::curta::digest_hint::SHADigestHint; use crate::frontend::hash::sha::curta::proof_hint::SHAProofHint; @@ -369,26 +367,6 @@ where let id = MulCubicGenerator::::id(); r.register_simple::>(id); - let blake2b_hint_generator_id = BLAKE2BHintGenerator::id(); - r.register_simple::(blake2b_hint_generator_id); - - let blake2b_generator = BLAKE2BGenerator::< - L::Field, - L::CubicParams, - L::CurtaConfig, - D, - BLAKE2BAirParameters, - MAX_NUM_CURTA_CHUNKS, - >::id(); - r.register_simple::, - MAX_NUM_CURTA_CHUNKS, - >>(blake2b_generator); - r.register_hint::(); r.register_hint::(); @@ -413,6 +391,12 @@ where r.register_hint::(); r.register_async_hint::>(); + r.register_hint::(); + r.register_async_hint::>(); + + r.register_hint::(); + r.register_async_hint::>(); + let dummy_proof_generator_id = DummyProofGenerator::::default().id(); r.register_simple::>(dummy_proof_generator_id); diff --git a/plonky2x/core/src/frontend/builder/mod.rs b/plonky2x/core/src/frontend/builder/mod.rs index e9377db72..93b4e578f 100644 --- a/plonky2x/core/src/frontend/builder/mod.rs +++ b/plonky2x/core/src/frontend/builder/mod.rs @@ -22,7 +22,7 @@ use tokio::runtime::Runtime; pub use self::io::CircuitIO; use super::ecc::curve25519::curta::accelerator::EcOpAccelerator; -use super::hash::blake2::curta::Blake2bAccelerator; +use super::hash::blake2::accelerator::BLAKE2BAccelerator; use super::hash::sha::sha256::curta::SHA256Accelerator; use super::hash::sha::sha512::curta::SHA512Accelerator; use super::hint::HintGenerator; @@ -46,7 +46,7 @@ pub struct CircuitBuilder, const D: usize> { pub(crate) async_hints: Vec>, pub(crate) async_hints_indices: Vec, - pub blake2b_accelerator: Option>, + pub blake2b_accelerator: Option, pub sha256_accelerator: Option, pub sha512_accelerator: Option, pub ec_25519_ops_accelerator: Option, @@ -134,7 +134,7 @@ impl, const D: usize> CircuitBuilder { fn pre_build(&mut self) { let blake2b_accelerator = self.blake2b_accelerator.clone(); if let Some(accelerator) = blake2b_accelerator { - accelerator.build(self); + self.curta_constrain_blake2b(accelerator); } let sha256_accelerator = self.sha256_accelerator.clone(); diff --git a/plonky2x/core/src/frontend/curta/builder.rs b/plonky2x/core/src/frontend/curta/builder.rs index 458f34340..00a1df9f7 100644 --- a/plonky2x/core/src/frontend/curta/builder.rs +++ b/plonky2x/core/src/frontend/curta/builder.rs @@ -152,6 +152,8 @@ mod tests { a_init.get(1), &Time::zero(), Some(num_rows_reg), + None, + None, ); let clk = Time::from_element(air_builder.clk); @@ -161,10 +163,10 @@ mod tests { let zero_trace = air_builder.alloc::(); air_builder.set_to_expression(&zero_trace, GoldilocksField::ZERO.into()); let a_0_trace = a_ptr.get_at(zero_trace); - let a = air_builder.load(&a_0_trace, &clk); - let b = air_builder.load(&a_ptr.get(1), &Time::zero()); + let a = air_builder.load(&a_0_trace, &clk, None, None); + let b = air_builder.load(&a_ptr.get(1), &Time::zero(), None, None); let c = air_builder.and(&a, &b); - air_builder.store(&a_0_trace, c, &clk.advance(), None); + air_builder.store(&a_0_trace, c, &clk.advance(), None, None, None); let a_final = air_builder.api.alloc_public::(); diff --git a/plonky2x/core/src/frontend/ecc/curve25519/ed25519/eddsa.rs b/plonky2x/core/src/frontend/ecc/curve25519/ed25519/eddsa.rs index 5b7ee6995..92410b58b 100644 --- a/plonky2x/core/src/frontend/ecc/curve25519/ed25519/eddsa.rs +++ b/plonky2x/core/src/frontend/ecc/curve25519/ed25519/eddsa.rs @@ -18,8 +18,6 @@ use crate::prelude::{ U256Variable, U32Variable, Variable, }; -const MAX_NUM_SIGS: usize = 256; - #[derive(Clone, Debug, CircuitVariable)] pub struct EDDSASignatureVariable { pub r: CompressedEdwardsYVariable, @@ -82,7 +80,7 @@ impl, const D: usize> CircuitBuilder { signatures: ArrayVariable, pubkeys: ArrayVariable, ) { - assert!(NUM_SIGS > 0 && NUM_SIGS <= MAX_NUM_SIGS); + assert!(NUM_SIGS > 0); assert!(is_active.len() == NUM_SIGS); assert!(messages.len() == NUM_SIGS); if let Some(ref msg_lens) = message_byte_lengths { @@ -141,7 +139,7 @@ impl, const D: usize> CircuitBuilder { signatures: ArrayVariable, pubkeys: ArrayVariable, ) { - assert!(NUM_SIGS > 0 && NUM_SIGS <= MAX_NUM_SIGS); + assert!(NUM_SIGS > 0); assert!(messages.len() == NUM_SIGS); if let Some(ref msg_lens) = message_byte_lengths { assert!(msg_lens.len() == NUM_SIGS); diff --git a/plonky2x/core/src/frontend/hash/blake2/accelerator.rs b/plonky2x/core/src/frontend/hash/blake2/accelerator.rs new file mode 100644 index 000000000..6f5a51782 --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/accelerator.rs @@ -0,0 +1,8 @@ +use super::request::BLAKE2BRequest; +use crate::prelude::U64Variable; + +#[derive(Debug, Clone)] +pub struct BLAKE2BAccelerator { + pub blake2b_requests: Vec, + pub blake2b_responses: Vec<[U64Variable; 4]>, +} diff --git a/plonky2x/core/src/frontend/hash/blake2/builder.rs b/plonky2x/core/src/frontend/hash/blake2/builder.rs new file mode 100644 index 000000000..99f692ea5 --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/builder.rs @@ -0,0 +1,57 @@ +use super::accelerator::BLAKE2BAccelerator; +use super::digest_hint::BLAKE2BDigestHint; +use super::proof_hint::BLAKE2BProofHint; +use super::request::BLAKE2BRequest; +use super::stark::{get_blake2b_data, stark}; +use crate::frontend::hint::synchronous::Async; +use crate::prelude::*; + +impl, const D: usize> CircuitBuilder { + /// The constraints for an accelerated BLAKE2B computation using Curta. + pub(crate) fn curta_constrain_blake2b(&mut self, accelerator: BLAKE2BAccelerator) { + // Get all the digest values using the digest hint. + for (request, response) in accelerator + .blake2b_requests + .iter() + .zip(accelerator.blake2b_responses.iter()) + { + let digest_hint = BLAKE2BDigestHint::new(); + let mut input_stream = VariableStream::new(); + + match &request { + BLAKE2BRequest::Fixed(msg) => { + let len = self.constant::(L::Field::from_canonical_usize(msg.len())); + input_stream.write(&len); + input_stream.write_slice(msg); + } + BLAKE2BRequest::Variable(msg, len, _) => { + input_stream.write(len); + input_stream.write_slice(msg); + } + } + + let output_stream = self.hint(input_stream, digest_hint); + let digest = output_stream.read::<[U64Variable; 4]>(self); + self.assert_is_equal(digest, *response); + } + + // Prove correctness of the digest using the proof hint. + + // Initialize the corresponding stark and hint. + let blake2b_data = get_blake2b_data(self, accelerator); + let parameters = blake2b_data.parameters(); + let blake2b_stark = stark(parameters); + let proof_hint = BLAKE2BProofHint::new(parameters); + let mut input_stream = VariableStream::new(); + input_stream.write_blake2b_input(&blake2b_data); + + // Read the stark proof and public inputs from the hint's output stream. + let output_stream = self.async_hint(input_stream, Async(proof_hint)); + let proof = output_stream.read_byte_stark_proof(self, &blake2b_stark.stark); + let num_public_inputs = blake2b_stark.stark.air_data.num_public_inputs; + let public_inputs = output_stream.read_vec(self, num_public_inputs); + + // Verify the proof. + blake2b_stark.verify_proof(self, proof, &public_inputs, blake2b_data) + } +} diff --git a/plonky2x/core/src/frontend/hash/blake2/curta.rs b/plonky2x/core/src/frontend/hash/blake2/curta.rs index 3df60c823..f3acb1719 100644 --- a/plonky2x/core/src/frontend/hash/blake2/curta.rs +++ b/plonky2x/core/src/frontend/hash/blake2/curta.rs @@ -1,145 +1,90 @@ use core::marker::PhantomData; -use curta::chip::hash::blake::blake2b::builder_gadget::{BLAKE2BBuilder, BLAKE2BBuilderGadget}; -use curta::chip::hash::blake::blake2b::generator::BLAKE2BAirParameters; -use plonky2::iop::target::Target; +use curta::chip::uint::operations::instruction::UintInstruction; +use curta::chip::AirParameters; +use serde::{Deserialize, Serialize}; +use super::accelerator::BLAKE2BAccelerator; +use super::request::BLAKE2BRequest; +use super::stark::{compute_blake2b_last_chunk_index, digest_to_array}; use crate::backend::circuit::PlonkParameters; use crate::frontend::vars::Bytes32Variable; -use crate::prelude::{ByteVariable, CircuitBuilder, CircuitVariable, Variable}; +use crate::prelude::*; -pub const MAX_NUM_CURTA_CHUNKS: usize = 1600; +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct BLAKE2BAirParameters(PhantomData); -#[derive(Debug, Clone)] -pub struct CurtaBlake2BRequest { - message: Vec, - message_len: Target, - digest: [Target; 32], - chunk_size: usize, -} +impl, const D: usize> AirParameters for BLAKE2BAirParameters { + type Field = L::Field; + type CubicParams = L::CubicParams; -#[derive(Debug, Clone)] -pub struct Blake2bAccelerator, const D: usize> { - pub requests: Vec, - _marker: PhantomData, -} + type Instruction = UintInstruction; -impl, const D: usize> Blake2bAccelerator { - pub fn build(&self, builder: &mut CircuitBuilder) { - builder.curta_constrain_blake2b(self); - } + const NUM_FREE_COLUMNS: usize = 1527; + const EXTENDED_COLUMNS: usize = 708; } impl, const D: usize> CircuitBuilder { - /// Pads a BLAKE2B input - fn pad_message_blake2b( - &mut self, - message: &[ByteVariable], - ) -> Vec { - assert!(message.len() <= MAX_NUM_CHUNKS * 128, "message too long"); - - let padlen = MAX_NUM_CHUNKS * 128 - message.len(); - if padlen > 0 { - let mut padded_message = Vec::new(); - padded_message.extend(message); - - for _i in 0..padlen { - padded_message.push(self.constant::(0u8)); - } - - padded_message - } else { - message.to_vec() - } - } - - /// Executes a BLAKE2B hash on the given message. - pub fn curta_blake2b_variable( - &mut self, - message: &[ByteVariable], - message_len: Variable, - ) -> Bytes32Variable { - let padded_message = self.pad_message_blake2b::(message); - - let message_target_bytes = padded_message - .iter() - .map(|x| x.to_variable(self).0) - .collect::>(); - let message_len_target = message_len.targets()[0]; - let digest = self.api.add_virtual_target_arr::<32>(); - + pub fn curta_blake2b(&mut self, input: &[ByteVariable]) -> Bytes32Variable { if self.blake2b_accelerator.is_none() { - self.blake2b_accelerator = Some(Blake2bAccelerator:: { - requests: Vec::new(), - _marker: PhantomData, + self.blake2b_accelerator = Some(BLAKE2BAccelerator { + blake2b_requests: Vec::new(), + blake2b_responses: Vec::new(), }); } + let digest = self.init_unsafe::(); + let digest_array = digest_to_array(self, digest); let accelerator = self .blake2b_accelerator .as_mut() .expect("blake2b accelerator should exist"); + accelerator + .blake2b_requests + .push(BLAKE2BRequest::Fixed(input.to_vec())); + accelerator.blake2b_responses.push(digest_array); - let curta_blake2b_request = CurtaBlake2BRequest { - message: message_target_bytes, - message_len: message_len_target, - digest, - chunk_size: MAX_NUM_CHUNKS, - }; - - accelerator.requests.push(curta_blake2b_request); - - let bytes: [ByteVariable; 32] = digest.map(|x| ByteVariable::from_target(self, x)); - - bytes.into() + digest } - /// Verifies a blake2b curta instance - fn curta_constrain_blake2b(&mut self, accelerator: &Blake2bAccelerator) { - let mut padded_messages = Vec::new(); - let mut msg_lengths = Vec::new(); - let mut digests = Vec::new(); - let mut chunk_sizes = Vec::new(); - - for curta_req in accelerator.requests.iter() { - padded_messages.extend(curta_req.message.clone()); - msg_lengths.push(curta_req.message_len); - digests.extend(curta_req.digest); - chunk_sizes.push(curta_req.chunk_size as u64); + pub fn curta_blake2b_variable( + &mut self, + input: &[ByteVariable], + length: U32Variable, + ) -> Bytes32Variable { + let last_chunk = compute_blake2b_last_chunk_index(self, length); + if self.blake2b_accelerator.is_none() { + self.blake2b_accelerator = Some(BLAKE2BAccelerator { + blake2b_requests: Vec::new(), + blake2b_responses: Vec::new(), + }); } - let mut blake2b_builder_gadget: BLAKE2BBuilderGadget< - BLAKE2BAirParameters, - MAX_NUM_CURTA_CHUNKS, - > = self.api.init_blake2b(); - blake2b_builder_gadget - .padded_messages - .extend(padded_messages.clone()); - blake2b_builder_gadget.msg_lengths.extend(msg_lengths); - blake2b_builder_gadget.digests.extend(digests); - blake2b_builder_gadget.chunk_sizes.extend(chunk_sizes); - - // For now, only allow 1 blake2b curta proof per circuit - assert!( - padded_messages.len() <= MAX_NUM_CURTA_CHUNKS * 128, - "Too many chunks for Curta BLAKE2B" - ); - - self.api - .constrain_blake2b_gadget::(blake2b_builder_gadget); + let digest = self.init_unsafe::(); + let digest_array = digest_to_array(self, digest); + + let accelerator = self + .blake2b_accelerator + .as_mut() + .expect("blake2b accelerator should exist"); + accelerator.blake2b_requests.push(BLAKE2BRequest::Variable( + input.to_vec(), + length, + last_chunk, + )); + accelerator.blake2b_responses.push(digest_array); + + digest } } #[cfg(test)] mod tests { - use std::env; - - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::Field; + use itertools::Itertools; use crate::backend::circuit::DefaultParameters; use crate::frontend::vars::Bytes32Variable; - use crate::prelude::{BytesVariable, CircuitBuilder, Variable}; + use crate::prelude::{BytesVariable, CircuitBuilder, U32Variable}; use crate::utils::bytes32; type L = DefaultParameters; @@ -148,15 +93,11 @@ mod tests { #[test] #[cfg_attr(feature = "ci", ignore)] fn test_blake2b_curta_empty_string() { - env::set_var("RUST_LOG", "debug"); - env_logger::try_init().unwrap_or_default(); - dotenv::dotenv().ok(); - - const MAX_NUM_CHUNKS: usize = 4; + let _ = env_logger::builder().is_test(true).try_init(); let mut builder = CircuitBuilder::::new(); - let zero = builder.zero(); - let result = builder.curta_blake2b_variable::(&[], zero); + let zero = builder.zero::(); + let result = builder.curta_blake2b_variable(&[], zero); let expected_digest = bytes32!("0x0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"); @@ -174,24 +115,18 @@ mod tests { #[test] #[cfg_attr(feature = "ci", ignore)] fn test_blake2b_curta_long_string() { - env::set_var("RUST_LOG", "debug"); - env_logger::try_init().unwrap_or_default(); - dotenv::dotenv().ok(); - - type F = GoldilocksField; + let _ = env_logger::builder().is_test(true).try_init(); let msg_hex = "00f43f3ef4c05d1aca645d7b2b59af99d65661810b8a724818052db75e04afb60ea210002f9cac87493604cb5fff6644ea17c3b1817d243bc5a0aa6f0d11ab3df46f37b9adbf1ff3a446807e7a9ebc77647776b8bbda37dcf2f4f34ca7ba7bf4c7babfbe080642414245b501032c000000b7870a0500000000360b79058f3b331fbbb10d38a2e309517e24cc12094d0a5a7c9faa592884e9621aecff0224bc1a857a0bacadf4455e2c5b39684d2d5879b108c98315f6a14504348846c6deed3addcba24fc3af531d59f31c87bc454bf6f1d73eadaf2d22d60c05424142450101eead41c1266af7bc7becf961dcb93f3691642c9b6d50aeb65b92528b99c675608f2095a296ed52aa433c1bfed56e8546dae03b61cb59643a9cb39f82618f958b00041000000000000000000000000000000000000000000000000000000000000000008101a26cc6796f1025d51bd927351af541d3ab01d7a1b978a65e19c16ae2799b3286ca2401211009421c4e6bd80ef9e07918a26cc6796f1025d51bd927351af541d3ab01d7a1b978a65e19c16ae2799b3286ca2401211009421c4e6bd80ef9e079180400"; let msg_bytes = hex::decode(msg_hex).unwrap(); const MSG_LEN: usize = 423; assert!(msg_bytes.len() == MSG_LEN); - const MAX_NUM_CHUNKS: usize = 5; - let mut builder = CircuitBuilder::::new(); let msg = builder.constant::>(msg_bytes.clone().try_into().unwrap()); - let bytes_length = builder.constant::(F::from_canonical_usize(msg_bytes.len())); - let result = builder.curta_blake2b_variable::(&msg.0, bytes_length); + let bytes_length = builder.constant::(msg_bytes.len() as u32); + let result = builder.curta_blake2b_variable(&msg.0, bytes_length); let expected_digest = bytes32!("7c38fc8356aa20394c7f538e3cee3f924e6d9252494c8138d1a6aabfc253118f"); @@ -205,4 +140,86 @@ mod tests { circuit.verify(&proof, &input, &output); circuit.test_default_serializers(); } + + #[test] + #[cfg_attr(feature = "ci", ignore)] + fn test_blake2b_curta_multiple_hashes_variable() { + let _ = env_logger::builder().is_test(true).try_init(); + const MAX_MSG_SIZE: usize = 960; + + let mut builder = CircuitBuilder::::new(); + + let msgs = [ + "00f43f3ef4c05d1aca645d7b2b59af99d65661810b8a724818052db75e04afb60ea210002f9cac87493604cb5fff6644ea17c3b1817d243bc5a0aa6f0d11ab3df46f37b9adbf1ff3a446807e7a9ebc77647776b8bbda37dcf2f4f34ca7ba7bf4c7babfbe080642414245b501032c000000b7870a0500000000360b79058f3b331fbbb10d38a2e309517e24cc12094d0a5a7c9faa592884e9621aecff0224bc1a857a0bacadf4455e2c5b39684d2d5879b108c98315f6a14504348846c6deed3addcba24fc3af531d59f31c87bc454bf6f1d73eadaf2d22d60c05424142450101eead41c1266af7bc7becf961dcb93f3691642c9b6d50aeb65b92528b99c675608f2095a296ed52aa433c1bfed56e8546dae03b61cb59643a9cb39f82618f958b00041000000000000000000000000000000000000000000000000000000000000000008101a26cc6796f1025d51bd927351af541d3ab01d7a1b978a65e19c16ae2799b3286ca2401211009421c4e6bd80ef9e07918a26cc6796f1025d51bd927351af541d3ab01d7a1b978a65e19c16ae2799b3286ca2401211009421c4e6bd80ef9e079180400", + "39285734897537894674835698460198237adce984eda487459893754091", + ]; + let mut msg_bytes = msgs.map(|x| hex::decode(x).unwrap()); + + let mut results = Vec::new(); + for msg in msg_bytes.iter_mut() { + let msg_len = builder.constant::(msg.len().try_into().unwrap()); + msg.resize(MAX_MSG_SIZE, 0); + let msg_var = + builder.constant::>(msg.clone().try_into().unwrap()); + results.push(builder.curta_blake2b_variable(&msg_var.0, msg_len)); + } + + let expected_digests = [ + bytes32!("7c38fc8356aa20394c7f538e3cee3f924e6d9252494c8138d1a6aabfc253118f"), + bytes32!("7cd6b73d53b4bd7fef48f0d45782caac149615387c13891b0f3665dcfa50a4c0"), + ]; + + for (expected_digest, result) in expected_digests.iter().zip_eq(results.iter()) { + let expected_digest_var = builder.constant::(*expected_digest); + builder.assert_is_equal(*result, expected_digest_var); + } + + let circuit = builder.build(); + let input = circuit.input(); + let (proof, output) = circuit.prove(&input); + circuit.verify(&proof, &input, &output); + circuit.test_default_serializers(); + } + + #[test] + #[cfg_attr(feature = "ci", ignore)] + fn test_blake2b_curta_multiple_hashes_fixed() { + let _ = env_logger::builder().is_test(true).try_init(); + + let mut builder = CircuitBuilder::::new(); + + let msgs = [ + "00f43f3ef4c05d1aca645d7b2b59af99d65661810b8a724818052db75e04afb60ea210002f9cac87493604cb5fff6644ea17c3b1817d243bc5a0aa6f0d11ab3df46f37b9adbf1ff3a446807e7a9ebc77647776b8bbda37dcf2f4f34ca7ba7bf4c7babfbe080642414245b501032c000000b7870a0500000000360b79058f3b331fbbb10d38a2e309517e24cc12094d0a5a7c9faa592884e9621aecff0224bc1a857a0bacadf4455e2c5b39684d2d5879b108c98315f6a14504348846c6deed3addcba24fc3af531d59f31c87bc454bf6f1d73eadaf2d22d60c05424142450101eead41c1266af7bc7becf961dcb93f3691642c9b6d50aeb65b92528b99c675608f2095a296ed52aa433c1bfed56e8546dae03b61cb59643a9cb39f82618f958b00041000000000000000000000000000000000000000000000000000000000000000008101a26cc6796f1025d51bd927351af541d3ab01d7a1b978a65e19c16ae2799b3286ca2401211009421c4e6bd80ef9e07918a26cc6796f1025d51bd927351af541d3ab01d7a1b978a65e19c16ae2799b3286ca2401211009421c4e6bd80ef9e079180400", + "39285734897537894674835698460198237adce984eda487459893754091", + ]; + + let msg_bytes = msgs.map(|x| hex::decode(x).unwrap()); + let mut results = Vec::new(); + + const MSG_LEN_1: usize = 423; + let msg_var = + builder.constant::>(msg_bytes[0].clone().try_into().unwrap()); + results.push(builder.curta_blake2b(&msg_var.0)); + + const MSG_LEN_2: usize = 30; + let msg_var = + builder.constant::>(msg_bytes[1].clone().try_into().unwrap()); + results.push(builder.curta_blake2b(&msg_var.0)); + + let expected_digests = [ + bytes32!("7c38fc8356aa20394c7f538e3cee3f924e6d9252494c8138d1a6aabfc253118f"), + bytes32!("7cd6b73d53b4bd7fef48f0d45782caac149615387c13891b0f3665dcfa50a4c0"), + ]; + + for (expected_digest, result) in expected_digests.iter().zip_eq(results.iter()) { + let expected_digest_var = builder.constant::(*expected_digest); + builder.assert_is_equal(*result, expected_digest_var); + } + + let circuit = builder.build(); + let input = circuit.input(); + let (proof, output) = circuit.prove(&input); + circuit.verify(&proof, &input, &output); + circuit.test_default_serializers(); + } } diff --git a/plonky2x/core/src/frontend/hash/blake2/data.rs b/plonky2x/core/src/frontend/hash/blake2/data.rs new file mode 100644 index 000000000..755dd20b5 --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/data.rs @@ -0,0 +1,94 @@ +use serde::{Deserialize, Serialize}; + +use crate::prelude::{ + BoolVariable, PlonkParameters, U32Variable, U64Variable, ValueStream, Variable, VariableStream, +}; + +/// Circuit variables for the input data of a SHA computation. +pub struct BLAKE2BInputData { + /// The padded chunks of the input message. + pub padded_chunks: Vec, + /// The t values for each chunk. + pub t_values: Vec, + // A flag for each chunk indicating whether the hash state needs to be restarted after + // processing the chunk. + pub end_bits: Vec, + /// A flag for each chunk indicating whether the digest should be read after processing the + /// chunk. + pub digest_bits: Vec, + /// The index of the digests to be read, corresponding to their location in `padded chunks`. + pub digest_indices: Vec, + /// The message digests. + pub digests: Vec<[U64Variable; 4]>, +} + +/// The values of the input data of a BLAKE2B computation. +/// +/// This struct represents the values of the variables of `BLAKE2BInputData`. +#[derive(Debug, Clone)] +pub struct BLAKE2BInputDataValues, const D: usize> { + pub padded_chunks: Vec, + pub t_values: Vec, + pub end_bits: Vec, + pub digest_bits: Vec, + pub digest_indices: Vec, + pub digests: Vec<[u64; 4]>, +} + +/// The parameters required for reading the input data of a BLAKE2B computation from a stream. +#[derive(Clone, Debug, Copy, Serialize, Deserialize)] +pub struct BLAKE2BInputParameters { + pub num_chunks: usize, + pub num_digests: usize, +} + +impl BLAKE2BInputData { + /// Get parameters from the input data. + pub fn parameters(&self) -> BLAKE2BInputParameters { + BLAKE2BInputParameters { + num_chunks: self.end_bits.len(), + num_digests: self.digests.len(), + } + } +} + +impl VariableStream { + /// Read blake2b input data from the stream. + pub fn write_blake2b_input(&mut self, input: &BLAKE2BInputData) { + self.write_slice(&input.padded_chunks); + self.write_slice(&input.t_values); + self.write_slice(&input.end_bits); + self.write_slice(&input.digest_bits); + self.write_slice(&input.digest_indices); + self.write_slice(&input.digests); + } +} + +impl, const D: usize> ValueStream { + /// Read blake2b input data from the stream. + pub fn read_blake2b_input_values( + &mut self, + parameters: BLAKE2BInputParameters, + ) -> BLAKE2BInputDataValues { + let BLAKE2BInputParameters { + num_chunks, + num_digests, + } = parameters; + + let padded_chunks = self.read_vec::(num_chunks * 16); + let t_values = self.read_vec::(num_chunks); + let end_bits = self.read_vec::(num_chunks); + let digest_bits = self.read_vec::(num_chunks); + let digest_indices = self.read_vec::(num_digests); + let digests = self.read_vec::<[U64Variable; 4]>(num_digests); + + BLAKE2BInputDataValues { + padded_chunks, + end_bits, + digest_bits, + digest_indices, + digests, + t_values, + } + } +} diff --git a/plonky2x/core/src/frontend/hash/blake2/digest_hint.rs b/plonky2x/core/src/frontend/hash/blake2/digest_hint.rs new file mode 100644 index 000000000..4c49de629 --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/digest_hint.rs @@ -0,0 +1,56 @@ +use curta::machine::hash::blake::blake2b; +use curta::machine::hash::blake::blake2b::pure::BLAKE2BPure; +use curta::machine::hash::blake::blake2b::utils::BLAKE2BUtil; +use curta::math::prelude::PrimeField64; +use serde::{Deserialize, Serialize}; + +use crate::frontend::hint::simple::hint::Hint; +use crate::prelude::*; + +/// Provides the BLAKE2B of a message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BLAKE2BDigestHint; + +impl, const D: usize> Hint for BLAKE2BDigestHint { + fn hint(&self, input_stream: &mut ValueStream, output_stream: &mut ValueStream) { + let length = input_stream.read_value::().as_canonical_u64() as usize; + // Read the padded chunks from the input stream. + let message = input_stream.read_vec::(length); + let mut num_message_chunks = (message.len() as u64 / 128) + 1; + if num_message_chunks % 128 == 0 { + num_message_chunks -= 1; + } + + let padded_chunks = BLAKE2BUtil::pad(&message, num_message_chunks); + + // Initialize the hash state. + let mut state = blake2b::IV; + let mut t = 0; + for (i, chunk) in padded_chunks.chunks_exact(128).enumerate() { + let at_last_chunk = i as u64 == num_message_chunks - 1; + if at_last_chunk { + t = length as u64; + } else { + t += 128; + } + BLAKE2BPure::compress(chunk, &mut state, t, at_last_chunk); + } + + // Write the digest to the output stream. + let mut digest: [u64; 4] = Default::default(); + digest.copy_from_slice(&state[0..4]); + output_stream.write_value::<[U64Variable; 4]>(digest) + } +} + +impl BLAKE2BDigestHint { + pub fn new() -> Self { + Self {} + } +} + +impl Default for BLAKE2BDigestHint { + fn default() -> Self { + Self::new() + } +} diff --git a/plonky2x/core/src/frontend/hash/blake2/mod.rs b/plonky2x/core/src/frontend/hash/blake2/mod.rs index 6a9174f8c..d74b73aed 100644 --- a/plonky2x/core/src/frontend/hash/blake2/mod.rs +++ b/plonky2x/core/src/frontend/hash/blake2/mod.rs @@ -1,3 +1,8 @@ -//! An implementation of the Blake2 hash functions in a plonky2 circuit - +pub mod accelerator; +pub mod builder; pub mod curta; +pub mod data; +pub mod digest_hint; +pub mod proof_hint; +pub mod request; +pub mod stark; diff --git a/plonky2x/core/src/frontend/hash/blake2/proof_hint.rs b/plonky2x/core/src/frontend/hash/blake2/proof_hint.rs new file mode 100644 index 000000000..4b8fe03af --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/proof_hint.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +use super::data::BLAKE2BInputParameters; +use super::stark::stark; +use crate::frontend::hint::simple::hint::Hint; +use crate::prelude::*; + +/// A hint for Curta proof of a BLAKE2B stark. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BLAKE2BProofHint { + parameters: BLAKE2BInputParameters, +} + +impl BLAKE2BProofHint { + pub fn new(parameters: BLAKE2BInputParameters) -> Self { + Self { parameters } + } +} + +impl, const D: usize> Hint for BLAKE2BProofHint { + fn hint(&self, input_stream: &mut ValueStream, output_stream: &mut ValueStream) { + let inputs = input_stream.read_blake2b_input_values(self.parameters); + let stark = stark(self.parameters); + + // Generate the proof with public inputs and write them to the output stream. + let (proof, public_inputs) = stark.prove(inputs); + + output_stream.write_byte_stark_proof(proof); + output_stream.write_slice(&public_inputs); + } +} diff --git a/plonky2x/core/src/frontend/hash/blake2/request.rs b/plonky2x/core/src/frontend/hash/blake2/request.rs new file mode 100644 index 000000000..937b4d18e --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/request.rs @@ -0,0 +1,28 @@ +use serde::{Deserialize, Serialize}; + +use crate::prelude::*; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum BLAKE2BRequestType { + Fixed, + Variable, +} + +/// A request for a BLAKE2B computation. +#[derive(Debug, Clone)] +pub enum BLAKE2BRequest { + /// A message of fixed length. + Fixed(Vec), + /// A message of variable length, represented by a tuple `(total_message, lengh, last_chunk)`. + Variable(Vec, U32Variable, U32Variable), +} + +impl BLAKE2BRequest { + /// Returns the type of the request. + pub const fn req_type(&self) -> BLAKE2BRequestType { + match self { + BLAKE2BRequest::Fixed(_) => BLAKE2BRequestType::Fixed, + BLAKE2BRequest::Variable(_, _, _) => BLAKE2BRequestType::Variable, + } + } +} diff --git a/plonky2x/core/src/frontend/hash/blake2/stark.rs b/plonky2x/core/src/frontend/hash/blake2/stark.rs new file mode 100644 index 000000000..794db676a --- /dev/null +++ b/plonky2x/core/src/frontend/hash/blake2/stark.rs @@ -0,0 +1,396 @@ +use curta::chip::register::array::ArrayRegister; +use curta::chip::register::bit::BitRegister; +use curta::chip::register::element::ElementRegister; +use curta::chip::register::Register; +use curta::chip::trace::writer::{InnerWriterData, TraceWriter}; +use curta::chip::uint::register::U64Register; +use curta::chip::uint::util::u64_to_le_field_bytes; +use curta::machine::builder::Builder; +use curta::machine::bytes::builder::BytesBuilder; +use curta::machine::bytes::proof::ByteStarkProof; +use curta::machine::bytes::stark::ByteStark; +use curta::math::prelude::*; +use itertools::Itertools; +use log::debug; +use plonky2::util::log2_ceil; +use plonky2::util::timing::TimingTree; + +use super::accelerator::BLAKE2BAccelerator; +use super::curta::BLAKE2BAirParameters; +use super::data::{BLAKE2BInputData, BLAKE2BInputDataValues, BLAKE2BInputParameters}; +use super::request::BLAKE2BRequest; +use crate::frontend::curta::proof::ByteStarkProofVariable; +use crate::frontend::vars::EvmVariable; +use crate::prelude::{ + ByteVariable, Bytes32Variable, CircuitBuilder, CircuitVariable, PlonkParameters, U32Variable, + U64Variable, Variable, +}; + +#[derive(Debug, Clone)] +pub struct BLAKE2BStark, const D: usize> { + pub stark: ByteStark, L::CurtaConfig, D>, + pub padded_chunks: Vec>, + pub t_values: ArrayRegister, + pub end_bits: ArrayRegister, + pub digest_bits: ArrayRegister, + pub digest_indices: ArrayRegister, + pub digests: Vec>, + pub num_messages: ElementRegister, + pub num_rows: usize, +} + +impl, const D: usize> BLAKE2BStark { + fn write_input(&self, writer: &TraceWriter, input: BLAKE2BInputDataValues) { + for (digest_index_reg, digest_index) in self + .digest_indices + .iter() + .zip_eq(input.digest_indices.iter()) + { + writer.write(&digest_index_reg, digest_index, 0); + } + + for (digest_reg, digest_value) in self.digests.iter().zip_eq(input.digests.iter()) { + let array: ArrayRegister<_> = *digest_reg; + writer.write_array(&array, digest_value.map(u64_to_le_field_bytes), 0); + } + + for (chunk, chunk_value) in self + .padded_chunks + .iter() + .zip_eq(input.padded_chunks.chunks_exact(16)) + { + writer.write_array( + chunk, + chunk_value.iter().map(|x| u64_to_le_field_bytes(*x)), + 0, + ); + } + + for (t, t_value) in self.t_values.iter().zip_eq(input.t_values.iter()) { + writer.write(&t, &u64_to_le_field_bytes(*t_value as u64), 0); + } + + for (end_bit, end_bit_value) in self.end_bits.iter().zip_eq(input.end_bits) { + writer.write( + &end_bit, + &L::Field::from_canonical_u8(end_bit_value as u8), + 0, + ); + } + + for (digest_bit, digest_bit_value) in self.digest_bits.iter().zip_eq(input.digest_bits) { + writer.write( + &digest_bit, + &L::Field::from_canonical_u8(digest_bit_value as u8), + 0, + ); + } + + writer.write( + &self.num_messages, + &L::Field::from_canonical_u32(input.digests.len() as u32), + 0, + ); + } + + /// Generate a proof for the BLAKE2B stark given the input data. + #[allow(clippy::type_complexity)] + pub fn prove( + &self, + input: BLAKE2BInputDataValues, + ) -> (ByteStarkProof, Vec) { + // Initialize a writer for the trace. + let writer = TraceWriter::new(&self.stark.air_data, self.num_rows); + // Write the public inputs to the trace. + self.write_input(&writer, input); + // Execute the air instructions. + writer.write_global_instructions(&self.stark.air_data); + for i in 0..self.num_rows { + writer.write_row_instructions(&self.stark.air_data, i); + } + + // Extract the trace and public input slice from the writer. + let InnerWriterData { trace, public, .. } = writer.into_inner().unwrap(); + let proof = self + .stark + .prove(&trace, &public, &mut TimingTree::default()) + .unwrap(); + + // Verify proof to make sure it's valid. + self.stark.verify(proof.clone(), &public).unwrap(); + debug!("stark proof verified"); + + (proof, public) + } + + pub fn verify_proof( + &self, + builder: &mut CircuitBuilder, + proof: ByteStarkProofVariable, + public_inputs: &[Variable], + blake2b_input: BLAKE2BInputData, + ) { + // Verify the stark proof. + builder.verify_byte_stark_proof(&self.stark, proof, public_inputs); + + // Connect the public inputs of the stark proof to it's values. + + // Connect the padded chunks. + for (int, register) in blake2b_input + .padded_chunks + .iter() + .zip_eq(self.padded_chunks.iter().flat_map(|x| x.iter())) + { + let value = register.read_from_slice(public_inputs); + let var = Self::value_to_variable(builder, value); + builder.assert_is_equal(*int, var); + } + + // Connect end_bits. + for (end_bit, register) in blake2b_input.end_bits.iter().zip_eq(self.end_bits.iter()) { + let value = register.read_from_slice(public_inputs); + builder.assert_is_equal(end_bit.variable, value); + } + + // Connect digest_bits. + for (digest_bit, register) in blake2b_input + .digest_bits + .iter() + .zip_eq(self.digest_bits.iter()) + { + let value = register.read_from_slice(public_inputs); + builder.assert_is_equal(digest_bit.variable, value); + } + + // Connect digest_indices. + for (digest_index, register) in blake2b_input + .digest_indices + .iter() + .zip_eq(self.digest_indices.iter()) + { + let value = register.read_from_slice(public_inputs); + builder.assert_is_equal(*digest_index, value); + } + + // Connect digests. + for (digest, ®ister) in blake2b_input.digests.iter().zip_eq(self.digests.iter()) { + let array: ArrayRegister = register; + for (int, int_reg) in digest.iter().zip_eq(array.iter()) { + let value = int_reg.read_from_slice(public_inputs); + let var = Self::value_to_variable(builder, value); + builder.assert_is_equal(*int, var); + } + } + } + + fn value_to_variable( + builder: &mut CircuitBuilder, + value: ::Value, + ) -> U64Variable { + let low_limbs = &value[0..4]; + let high_limbs = &value[4..8]; + let mut acc_low = builder.zero::(); + let mut acc_high = builder.zero::(); + for (i, (low_byte, high_byte)) in low_limbs.iter().zip(high_limbs).enumerate() { + let two_i = builder.constant::(L::Field::from_canonical_u32(1 << (8 * i))); + let two_i_low_byte = builder.mul(two_i, *low_byte); + let two_i_high_byte = builder.mul(two_i, *high_byte); + acc_low = builder.add(acc_low, two_i_low_byte); + acc_high = builder.add(acc_high, two_i_high_byte); + } + let low_limb = U32Variable::from_variables_unsafe(&[acc_low]); + let high_limb = U32Variable::from_variables_unsafe(&[acc_high]); + U64Variable { + limbs: [low_limb, high_limb], + } + } +} + +/// The Curta Stark corresponding to the input data. +pub fn stark, const D: usize>( + parameters: BLAKE2BInputParameters, +) -> BLAKE2BStark { + let mut builder = BytesBuilder::>::new(); + + let num_chunks = parameters.num_chunks; + let padded_chunks = (0..num_chunks) + .map(|_| builder.alloc_array_public::(16)) + .collect::>(); + + // Allocate registers for the public inputs to the Stark. + let t_values = builder.alloc_array_public::(num_chunks); + let end_bits = builder.alloc_array_public::(num_chunks); + let digest_bits = builder.alloc_array_public::(num_chunks); + let digest_indices = builder.alloc_array_public::(parameters.num_digests); + let num_messages = builder.alloc_public::(); + + // Hash the padded chunks. + let digests = builder.blake2b( + &padded_chunks, + &t_values, + &end_bits, + &digest_bits, + &digest_indices, + &num_messages, + ); + + // Build the stark. + let num_rows_degree = log2_ceil(96 * num_chunks); + let num_rows = 1 << num_rows_degree; + let stark = builder.build::(num_rows); + + BLAKE2BStark { + stark, + padded_chunks, + t_values, + end_bits, + digest_bits, + digest_indices, + digests, + num_messages, + num_rows, + } +} + +/// Get the input data for the stark from a `BLAKE2BAccelerator`. +pub(crate) fn get_blake2b_data, const D: usize>( + builder: &mut CircuitBuilder, + accelerator: BLAKE2BAccelerator, +) -> BLAKE2BInputData { + // Initialze the data struictures of `BLAKE2BInputData`. + let mut t_values = Vec::new(); + let mut end_bit_values = Vec::new(); + let mut digest_bits = Vec::new(); + let mut current_chunk_index = 0; + let mut digest_indices = Vec::::new(); + + // Get the padded chunks from input messages, and assign the correct values of t_values, end_bit, + // digest_bits, and digest_indices. + // + // `t_values` - the t values for each chunk. + // `end_bit` - a bit that is true for the last chunk of a message (or the total_message + // passed in the case of a message of variable length). + // `digest_bit` - a bit that indicates the hash state is read into the digest list after + // processing the chunk. For a message of fioxed length, this is the same as `end_bit`. + // `digest_index` - the index of the digest to be read, corresponding to the location of the + // chunk in the padded chunks. + let padded_chunks = accelerator + .blake2b_requests + .iter() + .flat_map(|req| { + // For every request, we read the corresponding messagem, pad it, and compute the + // corresponding chunk index. + + // Get the padded chunks and the number of chunks in the message, depending on the + // type of the request. + let (padded_chunks, length, last_chunk_index) = match req { + BLAKE2BRequest::Fixed(input) => { + // If the length is fixed, the chunk_index is just `number of chunks - 1``. + let padded_chunks = pad_blake2b_circuit(builder, input); + let num_chunks = + builder.constant((padded_chunks.len() / 16 - 1).try_into().unwrap()); + let length = builder.constant::(input.len() as u32); + (padded_chunks, length, num_chunks) + } + // If the length of the message is a variable, we read the chunk index from the + // request. + BLAKE2BRequest::Variable(input, length, last_chunk) => { + (pad_blake2b_circuit(builder, input), *length, *last_chunk) + } + }; + + // Get the total number of chunks processed. + let total_number_of_chunks = padded_chunks.len() / 16; + // Store the end_bit values. The end bit indicates the end of message chunks. + end_bit_values.extend_from_slice(&vec![false; total_number_of_chunks - 1]); + end_bit_values.push(true); + // The chunk index is given by the currenty index plus the chunk index we got from + // the request. + let current_chunk_index_variable = + builder.constant::(L::Field::from_canonical_usize(current_chunk_index)); + let digest_index = builder.add(current_chunk_index_variable, last_chunk_index.variable); + digest_indices.push(digest_index); + // The digest bit is equal to zero for all chunks except the one that corresponds to + // the `chunk_index`. We find the bits by comparing each value between 0 and the + // total number of chunks to the `chunk_index`. + let mut t_var = builder.constant::(0); + let chunk_size = builder.constant::(128); + for j in 0..total_number_of_chunks { + t_var = builder.add(t_var, chunk_size); + let j_var = builder.constant::(j as u32); + let at_digest_chunk = builder.is_equal(j_var, last_chunk_index); + digest_bits.push(at_digest_chunk); + + t_var = builder.select(at_digest_chunk, length, t_var); + t_values.push(t_var); + } + // Increment the current chunk index by the total number of chunks. + current_chunk_index += total_number_of_chunks; + + padded_chunks + }) + .collect::>(); + + // Convert end_bits to variables. + let end_bits = builder.constant_vec(&end_bit_values); + + BLAKE2BInputData { + padded_chunks, + t_values, + digest_bits, + end_bits, + digest_indices, + digests: accelerator.blake2b_responses, + } +} + +// Need to make the message 128 byte aligned +fn pad_blake2b_circuit, const D: usize>( + builder: &mut CircuitBuilder, + input: &[ByteVariable], +) -> Vec { + let num_pad_bytes = 128 - (input.len() % 128); + + let mut padded_message = Vec::new(); + padded_message.extend_from_slice(input); + + for _ in 0..num_pad_bytes { + padded_message.push(builder.zero()); + } + + padded_message + .chunks_exact(8) + .map(|bytes| { + let mut bytes_copy = Vec::new(); + bytes_copy.extend_from_slice(bytes); + bytes_copy.reverse(); + U64Variable::decode(builder, &bytes_copy) + }) + .collect() +} + +pub(crate) fn digest_to_array, const D: usize>( + builder: &mut CircuitBuilder, + digest: Bytes32Variable, +) -> [U64Variable; 4] { + digest + .as_bytes() + .chunks_exact(8) + .map(|x| { + let mut x_copy = Vec::new(); + x_copy.extend_from_slice(x); + x_copy.reverse(); + U64Variable::decode(builder, &x_copy) + }) + .collect::>() + .try_into() + .unwrap() +} + +pub(crate) fn compute_blake2b_last_chunk_index, const D: usize>( + builder: &mut CircuitBuilder, + input_byte_length: U32Variable, +) -> U32Variable { + let chunk_size = builder.constant::(128); + builder.div(input_byte_length, chunk_size) +} diff --git a/plonky2x/core/src/frontend/hash/sha/curta/mod.rs b/plonky2x/core/src/frontend/hash/sha/curta/mod.rs index 61138ecbc..b0f744fc5 100644 --- a/plonky2x/core/src/frontend/hash/sha/curta/mod.rs +++ b/plonky2x/core/src/frontend/hash/sha/curta/mod.rs @@ -96,7 +96,7 @@ pub trait SHA, const D: usize, const CYCLE_LEN: usize>: .sha_requests .iter() .flat_map(|req| { - // For every reuqest, we read the corresponding messagem, pad it, and compute the + // For every request, we read the corresponding messagem, pad it, and compute the // corresponding chunk index. // Get the padded chunks and the number of chunks in the message, depending on the @@ -109,7 +109,7 @@ pub trait SHA, const D: usize, const CYCLE_LEN: usize>: builder.constant((padded_chunks.len() / 16 - 1).try_into().unwrap()); (padded_chunks, num_chunks) } - // If the length of the massage is a variable, we read the chunk index form the + // If the length of the message is a variable, we read the chunk index from the // request. SHARequest::Variable(input, length, last_chunk) => ( Self::pad_circuit_variable_length(builder, input, *length),