diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5ea263b55..14b71bde7 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -76,6 +76,10 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Clippy Check run: cargo clippy --workspace --all-targets -- -Dwarnings + # The workspace build always enables xmss's 'test-utils' feature (rec_aggregation turns + # it on); this checks the default-features configuration downstream users get. + - name: Clippy Check (xmss standalone, default features) + run: cargo clippy -p xmss --all-targets -- -Dwarnings cargo-fmt: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 15cb51f3a..9e3931304 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,54 @@ dependencies = [ "poly", ] +[[package]] +name = "alloy-primitives" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f007e257069855bdf21d27762fd3f3705a613f805c9a08309bf353503f081d71" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "fixed-cache", + "foldhash 0.2.0", + "hashbrown 0.17.1", + "indexmap 2.14.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.5", + "rapidhash", + "ruint", + "rustc-hash", + "secp256k1", + "serde", + "sha3", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24671b1f62edcf0f9b62994c7bf72cd621a04a4b99f5020ece1a647b40e2f103" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -85,331 +133,1314 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] -name = "atomic-polyfill" -version = "1.0.3" +name = "ark-ff" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" dependencies = [ - "critical-section", + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", ] [[package]] -name = "backend" -version = "0.1.0" -dependencies = [ - "air", - "fiat-shamir", - "field", - "koala-bear", - "parallel", - "poly", - "sumcheck", - "symetric", - "utils", - "whir", - "zk-alloc", +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", ] [[package]] -name = "bitflags" -version = "2.12.1" +name = "ark-ff" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "ark-ff" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "f7a806ac6c8307b929df4645776290a50ee2aac754ad09d8bdf73391309e43af" dependencies = [ - "generic-array", + "ark-ff-asm 0.6.0", + "ark-ff-macros 0.6.0", + "ark-serialize 0.6.0", + "ark-std 0.6.0", + "digest 0.10.7", + "educe", + "num-bigint", + "num-traits", + "zeroize", ] [[package]] -name = "byteorder" -version = "1.5.0" +name = "ark-ff-asm" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] [[package]] -name = "cfg-if" -version = "1.0.4" +name = "ark-ff-asm" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] [[package]] -name = "chacha20" -version = "0.10.0" +name = "ark-ff-asm" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core", + "quote", + "syn 2.0.117", ] [[package]] -name = "clap" -version = "4.6.1" +name = "ark-ff-asm" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "1479009684adc073dff49a1025d3a7065b317a9ead25aaaca38cdc70058ba8a2" dependencies = [ - "clap_builder", - "clap_derive", + "quote", + "syn 2.0.117", ] [[package]] -name = "clap_builder" -version = "4.6.0" +name = "ark-ff-macros" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", ] [[package]] -name = "clap_derive" -version = "4.6.1" +name = "ark-ff-macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "heck", + "num-bigint", + "num-traits", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] -name = "clap_lex" -version = "1.1.0" +name = "ark-ff-macros" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] -name = "cobs" +name = "ark-ff-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0691ed21ef00ef89c1e9bda832eba493dda3ec2f8d892fb25b705f73f06bb8" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-serialize" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" dependencies = [ - "thiserror", + "ark-std 0.3.0", + "digest 0.9.0", ] [[package]] -name = "colorchoice" -version = "1.0.5" +name = "ark-serialize" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "ark-serialize" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "libc", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint", ] [[package]] -name = "cpufeatures" +name = "ark-serialize" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74dd304fd536fb95d0a328e72be759209cc496a9da094c5bc56e5fea4f9e86b" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.6.0", + "digest 0.10.7", + "num-bigint", + "serde_with", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f153690697a2b91e5e1251ff98411ee5371500a111a0fd317a70e588eb300f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-std" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ - "libc", + "num-traits", + "rand 0.8.7", ] [[package]] -name = "critical-section" -version = "1.2.0" +name = "ark-std" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.7", +] [[package]] -name = "crypto-common" -version = "0.1.7" +name = "ark-std" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ - "generic-array", - "typenum", + "num-traits", + "rand 0.8.7", ] [[package]] -name = "digest" -version = "0.10.7" +name = "ark-std" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "367c9c827ed431bff6868b7aa926e05b16eb46603cc8b6e768e4a5553fa1d155" dependencies = [ - "block-buffer", - "crypto-common", + "num-traits", + "rand 0.8.7", ] [[package]] -name = "embedded-io" -version = "0.4.0" +name = "arrayvec" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] -name = "embedded-io" -version = "0.6.1" +name = "atomic-polyfill" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] [[package]] -name = "equivalent" -version = "1.0.2" +name = "auto_impl" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] -name = "fiat-shamir" +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "backend" version = "0.1.0" dependencies = [ + "air", + "fiat-shamir", "field", "koala-bear", "parallel", - "serde", + "poly", + "sumcheck", "symetric", "utils", + "whir", + "zk-alloc", ] [[package]] -name = "field" -version = "0.1.0" +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" dependencies = [ - "parallel", - "paste", - "rand", + "bitcoin-internals", + "hex-conservative 1.2.0", "serde", - "utils", ] [[package]] -name = "foldhash" -version = "0.1.5" +name = "bitcoin-internals" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" [[package]] -name = "generic-array" -version = "0.14.7" +name = "bitcoin-io" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" dependencies = [ - "typenum", - "version_check", + "bitcoin-consensus-encoding", ] [[package]] -name = "getrandom" -version = "0.4.2" +name = "bitcoin_hashes" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core", - "wasip2", - "wasip3", + "bitcoin-io", + "hex-conservative 0.2.2", ] [[package]] -name = "hash32" -version = "0.2.1" +name = "bitflags" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ - "byteorder", + "funty", + "radium", + "tap", + "wyz", ] [[package]] -name = "hashbrown" -version = "0.15.5" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "foldhash", + "generic-array", ] [[package]] -name = "hashbrown" -version = "0.17.1" +name = "block-buffer" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] [[package]] -name = "heapless" -version = "0.7.17" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin", - "stable_deref_trait", + "tinyvec", ] [[package]] -name = "heck" -version = "0.5.0" +name = "bumpalo" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] -name = "id-arena" -version = "2.3.0" +name = "byte-slice-cast" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-hex" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[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 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.117", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[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 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "enum-ordinalize" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07f808d588c10e464ea6f7d3eaed500049eff30aaac103460f61828c2d65b3eb" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e528e2d34ba8a67a1a650b86beae8ef69fc5fdb638016f386b973226590432" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ethereum_serde_utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38df44a7a271ab43835678f9215b53cc2523e4714a215da6643d83dc110245da" +dependencies = [ + "alloy-primitives", + "hex", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ethereum_ssz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e462875ad8693755ea8913d6e905715c76ea4836e2254e18c9cf0f7a8f8c2a13" +dependencies = [ + "alloy-primitives", + "ethereum_serde_utils", + "itertools 0.14.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-shamir" +version = "0.1.0" +dependencies = [ + "field", + "koala-bear", + "parallel", + "serde", + "symetric", + "utils", +] + +[[package]] +name = "field" +version = "0.1.0" +dependencies = [ + "parallel", + "paste", + "rand 0.10.1", + "serde", + "utils", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixed-cache" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe63500644ef0269fe6b744e7e5dc5c20b5eebf3d881bc2be53f194636f6583" +dependencies = [ + "equivalent", + "rapidhash", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.7", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", + "serde", + "serde_core", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] name = "include_dir" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ - "include_dir_macros", + "either", ] [[package]] -name = "include_dir_macros" -version = "0.7.4" +name = "itoa" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ - "proc-macro2", - "quote", + "cfg-if", + "futures-util", + "wasm-bindgen", ] [[package]] -name = "indexmap" -version = "2.14.0" +name = "k256" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.2" +name = "keccak" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] [[package]] -name = "itoa" -version = "1.0.18" +name = "keccak-asm" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "dd5dc2c0d691cbf7595cde551ced329cca99c2387c2cbc97754c5d0cd045d3ee" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] [[package]] name = "koala-bear" @@ -417,11 +1448,26 @@ version = "0.1.0" dependencies = [ "field", "paste", - "rand", + "rand 0.10.1", "serde", "utils", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy_static" version = "1.5.0" @@ -435,7 +1481,7 @@ dependencies = [ "backend", "clap", "lean_vm", - "rand", + "rand 0.10.1", "rec_aggregation", "serde_json", "sub_protocols", @@ -453,7 +1499,7 @@ dependencies = [ "lean_vm", "pest", "pest_derive", - "rand", + "rand 0.10.1", "sub_protocols", "xmss", ] @@ -465,7 +1511,7 @@ dependencies = [ "backend", "lean_compiler", "lean_vm", - "rand", + "rand 0.10.1", "rec_aggregation", "serde", "serde_json", @@ -495,6 +1541,12 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "lock_api" version = "0.4.14" @@ -534,6 +1586,41 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "objc2" version = "0.6.4" @@ -578,6 +1665,34 @@ dependencies = [ "system-info", ] +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "paste" version = "1.0.15" @@ -614,7 +1729,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -633,6 +1748,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "poly" version = "0.1.0" @@ -640,7 +1765,7 @@ dependencies = [ "field", "koala-bear", "parallel", - "rand", + "rand 0.10.1", "serde", "system-info", "utils", @@ -660,6 +1785,21 @@ dependencies = [ "serde", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -667,7 +1807,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", ] [[package]] @@ -679,6 +1839,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bitflags", + "num-traits", + "rand 0.9.5", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "quote" version = "1.0.45" @@ -688,12 +1863,46 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", + "serde", +] + [[package]] name = "rand" version = "0.10.1" @@ -701,8 +1910,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom", - "rand_core", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", + "serde", ] [[package]] @@ -711,6 +1959,24 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rapidhash" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e" +dependencies = [ + "rustversion", +] + [[package]] name = "rec_aggregation" version = "0.1.0" @@ -724,42 +1990,211 @@ dependencies = [ "objc2-foundation", "postcard", "serde", - "sub_protocols", - "tracing", - "xmss", + "sub_protocols", + "tracing", + "xmss", +] + +[[package]] +name = "ref-cast" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45caf26f647c19115bf9c453c70ffe4a4a3a6390dceebd942610584f99b8ddce" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "ark-ff 0.6.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.7", + "rand 0.9.5", + "rlp", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.28", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", ] [[package]] -name = "regex-automata" -version = "0.4.14" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", ] [[package]] -name = "regex-syntax" -version = "0.8.10" +name = "secp256k1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.5", + "secp256k1-sys", +] [[package]] -name = "rustc_version" -version = "0.4.1" +name = "secp256k1-sys" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" dependencies = [ - "semver", + "cc", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "semver" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] [[package]] name = "semver" @@ -767,6 +2202,15 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -794,7 +2238,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -810,6 +2254,25 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "time", +] + [[package]] name = "sha2" version = "0.10.9" @@ -818,7 +2281,27 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6287fd675f713484342a89cbf0a386abef5f15919cfad607e5e1f19e1e15331" +dependencies = [ + "cc", + "cfg-if", ] [[package]] @@ -830,6 +2313,28 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" version = "1.15.1" @@ -845,12 +2350,28 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" @@ -864,10 +2385,16 @@ dependencies = [ "backend", "lean_prover", "lean_vm", - "rand", + "rand 0.10.1", "tracing", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "sumcheck" version = "0.1.0" @@ -892,6 +2419,17 @@ dependencies = [ "zk-alloc", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.117" @@ -903,6 +2441,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "system-info" version = "0.1.0" @@ -910,6 +2459,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "thiserror" version = "2.0.18" @@ -927,7 +2482,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -939,6 +2494,81 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + [[package]] name = "tracing" version = "0.1.44" @@ -958,7 +2588,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1025,12 +2655,36 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1065,6 +2719,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" @@ -1083,6 +2743,51 @@ dependencies = [ "wit-bindgen 0.51.0", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + [[package]] name = "wasm-encoder" version = "0.244.0" @@ -1100,7 +2805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -1113,8 +2818,8 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap", - "semver", + "indexmap 2.14.0", + "semver 1.0.28", ] [[package]] @@ -1126,7 +2831,7 @@ dependencies = [ "koala-bear", "parallel", "poly", - "rand", + "rand 0.10.1", "sumcheck", "symetric", "system-info", @@ -1159,12 +2864,65 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1174,6 +2932,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -1208,9 +2975,9 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.14.0", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -1226,7 +2993,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -1239,7 +3006,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -1258,9 +3025,9 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.14.0", "log", - "semver", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -1268,16 +3035,66 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xmss" version = "0.1.0" dependencies = [ "backend", + "ethereum_ssz", "postcard", - "rand", + "rand 0.10.1", "serde", ] +[[package]] +name = "zerocopy" +version = "0.8.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zk-alloc" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index ab6682711..feab85584 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ serde = { version = "1.0.228", features = ["derive"] } tracing-subscriber = { version = "0.3.23", features = ["std", "env-filter"] } tracing-forest = { version = "0.3.0", features = ["ansi", "smallvec"] } postcard = { version = "1.1.3", features = ["alloc"] } +ssz = { package = "ethereum_ssz", version = "0.10" } include_dir = "0.7" libc = "0.2" paste = "1" @@ -91,7 +92,7 @@ rec_aggregation.workspace = true zk-alloc.workspace = true sub_protocols.workspace = true lean_vm.workspace = true -xmss.workspace = true +xmss = { workspace = true, features = ["test-utils"] } backend.workspace = true serde_json.workspace = true system-info.workspace = true diff --git a/crates/backend/parallel/src/lib.rs b/crates/backend/parallel/src/lib.rs index 631dc8d0e..079f8799f 100644 --- a/crates/backend/parallel/src/lib.rs +++ b/crates/backend/parallel/src/lib.rs @@ -58,6 +58,13 @@ pub(crate) fn current_worker_id() -> usize { WORKER_ID.with(Cell::get) } +/// True while the calling thread is running a pool task, where a nested dispatch would panic. +/// Lets code that runs both from the dispatcher and from within tasks pick a sequential path. +#[must_use] +pub fn is_in_pool_task() -> bool { + IN_TASK.get() +} + /// Type-erased work unit. The `&dyn Fn` lifetime is erased to `'static`; it is dereferenced /// only inside a dispatch window during which the dispatcher blocks, so the borrow outlives /// every call. Range-based (`f(start, end)`) so a reduction looks up its per-worker diff --git a/crates/rec_aggregation/Cargo.toml b/crates/rec_aggregation/Cargo.toml index 3e0dda154..e8d42db12 100644 --- a/crates/rec_aggregation/Cargo.toml +++ b/crates/rec_aggregation/Cargo.toml @@ -10,7 +10,7 @@ workspace = true prox-gaps-conjecture = ["lean_prover/prox-gaps-conjecture"] [dependencies] -xmss.workspace = true +xmss = { workspace = true, features = ["test-utils"] } tracing.workspace = true include_dir.workspace = true sub_protocols.workspace = true diff --git a/crates/rec_aggregation/src/compilation.rs b/crates/rec_aggregation/src/compilation.rs index 7f9bf76ea..08e7b34c5 100644 --- a/crates/rec_aggregation/src/compilation.rs +++ b/crates/rec_aggregation/src/compilation.rs @@ -9,7 +9,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::OnceLock; use sub_protocols::{N_VARS_TO_SEND_GKR_COEFFS, min_stacked_n_vars, total_whir_statements}; use tracing::instrument; -use xmss::{LOG_LIFETIME, MESSAGE_LEN_FE, PUBLIC_PARAM_LEN_FE, RANDOMNESS_LEN_FE, TARGET_SUM, V, W, XMSS_DIGEST_LEN}; +use xmss::{LOG_LIFETIME, PUBLIC_PARAM_LEN_FE, RANDOMNESS_LEN_FE, TARGET_SUM, V, W, XMSS_DIGEST_LEN}; use crate::bytecode_claims::bytecode_reduction_sumcheck_proof_size; use crate::single_message_aggregation::TWEAK_TABLE_SIZE_FE_PADDED; @@ -51,7 +51,7 @@ pub(crate) const MULTI_MESSAGE_FLAG: usize = 0; pub(crate) const BYTECODE_CLAIM_OFFSET: usize = DIGEST_LEN; /// Single-message component data: pubkeys_hash | message | merkle_chunks | tweaks_hash. -pub(crate) const COMPONENT_DATA_SIZE: usize = DIGEST_LEN + MESSAGE_LEN_FE + N_MERKLE_CHUNKS_FOR_SLOT + DIGEST_LEN; +pub(crate) const COMPONENT_DATA_SIZE: usize = DIGEST_LEN + DIGEST_LEN + N_MERKLE_CHUNKS_FOR_SLOT + DIGEST_LEN; // pubkeys_hash + hashed message + merkle chunks + tweaks_hash pub(crate) fn bytecode_claim_size_padded(program_log_size: usize) -> usize { let bytecode_point_n_vars = program_log_size + log2_ceil_usize(N_INSTRUCTION_COLUMNS); @@ -417,7 +417,7 @@ fn build_replacements(log_inner_bytecode: usize, bytecode_zero_eval: F) -> BTree replacements.insert("W_PLACEHOLDER".to_string(), W.to_string()); replacements.insert("TARGET_SUM_PLACEHOLDER".to_string(), TARGET_SUM.to_string()); replacements.insert("LOG_LIFETIME_PLACEHOLDER".to_string(), LOG_LIFETIME.to_string()); - replacements.insert("MESSAGE_LEN_PLACEHOLDER".to_string(), MESSAGE_LEN_FE.to_string()); + replacements.insert("MESSAGE_LEN_PLACEHOLDER".to_string(), DIGEST_LEN.to_string()); // the hashed message is one digest replacements.insert("RANDOMNESS_LEN_PLACEHOLDER".to_string(), RANDOMNESS_LEN_FE.to_string()); replacements.insert( "PUBLIC_PARAM_LEN_FE_PLACEHOLDER".to_string(), diff --git a/crates/rec_aggregation/src/multi_message_aggregation.rs b/crates/rec_aggregation/src/multi_message_aggregation.rs index f5bdce577..556c5da1b 100644 --- a/crates/rec_aggregation/src/multi_message_aggregation.rs +++ b/crates/rec_aggregation/src/multi_message_aggregation.rs @@ -91,6 +91,8 @@ fn build_multi_message_input_data(digests: &[[F; DIGEST_LEN]], bytecode_claim_fl data } +/// Merge single-message aggregates (each with its own (message, slot)) into one +/// multi-message aggregate attested by a single proof. pub fn merge_single_message_aggregates( single_messages: Vec, log_inv_rate: usize, @@ -187,6 +189,7 @@ pub fn merge_single_message_aggregates( }) } +/// Verify a multi-message aggregate against its per-component `info` (message, slot, pubkeys). pub fn verify_multi_message_aggregate(sig: &MultiMessageAggregateSignature) -> Result { if sig.info.is_empty() || sig.info.len() > MAX_RECURSIONS { return Err(ProofError::InvalidProof); @@ -203,9 +206,11 @@ pub fn verify_multi_message_aggregate(sig: &MultiMessageAggregateSignature) -> R verify_inner(input_data, sig.proof.proof.clone()) } +/// [`split_multi_message_aggregate`] for the unique component with the given message; +/// errors if no component (or more than one) carries it. pub fn split_multi_message_aggregate_by_message( multi_message: MultiMessageAggregateSignature, - message: [F; DIGEST_LEN], + message: [u8; xmss::MESSAGE_LEN_BYTES], log_inv_rate: usize, ) -> Result { let Some(index) = multi_message.info.iter().position(|info| info.message == message) else { diff --git a/crates/rec_aggregation/src/single_message_aggregation.rs b/crates/rec_aggregation/src/single_message_aggregation.rs index 4a6f75694..1b2f7c2e3 100644 --- a/crates/rec_aggregation/src/single_message_aggregation.rs +++ b/crates/rec_aggregation/src/single_message_aggregation.rs @@ -7,8 +7,8 @@ use tracing::instrument; use xmss::CHAIN_LENGTH; use xmss::make_tweak; use xmss::{ - LOG_LIFETIME, MESSAGE_LEN_FE, PUB_KEY_FLAT_SIZE, TWEAK_TYPE_CHAIN, TWEAK_TYPE_ENCODING, TWEAK_TYPE_MERKLE, - TWEAK_TYPE_WOTS_PK, V, WOTS_SIG_SIZE_FE, XmssPublicKey, XmssSignature, + LOG_LIFETIME, MESSAGE_LEN_BYTES, PUB_KEY_FLAT_SIZE, TWEAK_TYPE_CHAIN, TWEAK_TYPE_ENCODING, TWEAK_TYPE_MERKLE, + TWEAK_TYPE_WOTS_PK, V, WOTS_SIG_SIZE_FE, XmssPublicKey, XmssSignature, hash_message, }; use serde::{Deserialize, Serialize}; @@ -33,7 +33,7 @@ pub(crate) const TWEAK_TABLE_SIZE_FE_PADDED: usize = (N_TWEAKS * TWEAK_SLOT_SIZE #[derive(Debug, Clone, PartialEq, Eq)] pub struct SingleMessageInfo { - pub message: [F; MESSAGE_LEN_FE], + pub message: [u8; MESSAGE_LEN_BYTES], pub slot: u32, pub pubkeys: Vec, pub bytecode_claim: Evaluation, // value is trusted to be correct (should be recomputed when receiving a proof from an untrusted source) @@ -55,7 +55,7 @@ impl Serialize for SingleMessageInfo { impl<'de> Deserialize<'de> for SingleMessageInfo { fn deserialize>(d: D) -> Result { let (message, slot, pubkeys, bytecode_claim_point) = - <([F; MESSAGE_LEN_FE], u32, Vec, MultilinearPoint)>::deserialize(d)?; + <([u8; MESSAGE_LEN_BYTES], u32, Vec, MultilinearPoint)>::deserialize(d)?; let bytecode = try_get_aggregation_bytecode().ok_or_else(|| serde::de::Error::custom("bytecode not initialized"))?; if bytecode_claim_point.len() != bytecode.cumulated_n_vars() { @@ -107,7 +107,7 @@ impl SingleMessageInfo { build_single_message_input_data( self.pubkeys.len(), &hash_pubkeys(&self.pubkeys), - &self.message, + &hash_message(&self.message), self.slot, &tweaks_hash, &self.bytecode_claim_flat(), @@ -117,7 +117,7 @@ impl SingleMessageInfo { } pub(crate) fn hash_pubkeys(pub_keys: &[XmssPublicKey]) -> [F; DIGEST_LEN] { - let flat: Vec = pub_keys.iter().flat_map(|pk| pk.flaten().into_iter()).collect(); + let flat: Vec = pub_keys.iter().flat_map(|pk| pk.flatten().into_iter()).collect(); poseidon_hash_slice(&flat) } @@ -165,7 +165,7 @@ fn compute_merkle_chunks_for_slot(slot: u32) -> Vec { pub(crate) fn build_single_message_input_data( n_sigs: usize, pubkeys_hash: &[F; DIGEST_LEN], - message: &[F; MESSAGE_LEN_FE], + hashed_message: &[F; DIGEST_LEN], slot: u32, tweaks_hash: &[F; DIGEST_LEN], bytecode_claim_flat: &[F], @@ -181,7 +181,7 @@ pub(crate) fn build_single_message_input_data( data.extend(std::iter::repeat_n(F::ZERO, claim_padding)); data.extend_from_slice(&fiat_shamir_domain_sep(bytecode)); data.extend_from_slice(pubkeys_hash); - data.extend_from_slice(message); + data.extend_from_slice(hashed_message); data.extend(compute_merkle_chunks_for_slot(slot)); data.extend_from_slice(tweaks_hash); data @@ -197,7 +197,10 @@ fn encode_wots_signature(sig: &XmssSignature) -> ArenaVec { data } -// assumes `bytecode_value` in SingleMessageAggregateSignature::proof is correct (it should not be read / deserialized from an untrusted source) +/// Verify a single-message aggregate against its own `info` (message, slot, pubkeys). +/// +/// Assumes `sig.info.bytecode_claim.value` is correct: it must not come from an untrusted +/// source without recomputation (deserializing via serde recomputes it). pub fn verify_single_message_aggregate(sig: &SingleMessageAggregateSignature) -> Result { check_single_message_pubkeys(&sig.info.pubkeys).map_err(|_| ProofError::InvalidProof)?; verify_inner(sig.info.build_input_data(), sig.proof.proof.clone()) @@ -209,7 +212,7 @@ pub fn verify_single_message_aggregate(sig: &SingleMessageAggregateSignature) -> pub fn aggregate_single_message_signatures( children: &[SingleMessageAggregateSignature], raw_xmss: Vec<(XmssPublicKey, XmssSignature)>, - message: [F; MESSAGE_LEN_FE], + message: [u8; MESSAGE_LEN_BYTES], slot: u32, log_inv_rate: usize, ) -> Result { @@ -226,7 +229,7 @@ pub fn aggregate_single_message_signatures( pub(crate) fn aggregate_single_message_signatures_with_min_padding( children: &[SingleMessageAggregateSignature], mut raw_xmss: Vec<(XmssPublicKey, XmssSignature)>, - message: [F; MESSAGE_LEN_FE], + message: [u8; MESSAGE_LEN_BYTES], slot: u32, log_inv_rate: usize, min_table_log_n_rows: BTreeMap, @@ -298,7 +301,7 @@ pub(crate) fn aggregate_single_message_signatures_with_min_padding( let pub_input_data = build_single_message_input_data( n_sigs, &hash_pubkeys(&global_pub_keys), - message, + &hash_message(message), slot, &tweaks_hash, &reduced_claims.final_claim_flat(), @@ -369,10 +372,10 @@ pub(crate) fn aggregate_single_message_signatures_with_min_padding( let mut pubkeys_blob: ArenaVec = ArenaVec::with_capacity((n_sigs + n_dup) * PUB_KEY_FLAT_SIZE); for pk in &global_pub_keys { - pubkeys_blob.extend_from_slice(&pk.flaten()); + pubkeys_blob.extend_from_slice(&pk.flatten()); } for pk in &dup_pub_keys { - pubkeys_blob.extend_from_slice(&pk.flaten()); + pubkeys_blob.extend_from_slice(&pk.flatten()); } let (merkle_leaf_blobs, merkle_path_blobs) = diff --git a/crates/xmss/Cargo.toml b/crates/xmss/Cargo.toml index d0a1d0edf..5dc286e53 100644 --- a/crates/xmss/Cargo.toml +++ b/crates/xmss/Cargo.toml @@ -6,11 +6,15 @@ edition.workspace = true [lints] workspace = true +[features] +test-utils = ["dep:postcard"] + [dependencies] rand.workspace = true backend.workspace = true serde.workspace = true -postcard.workspace = true +postcard = { workspace = true, optional = true } +ssz.workspace = true [dev-dependencies] postcard.workspace = true \ No newline at end of file diff --git a/crates/xmss/src/lib.rs b/crates/xmss/src/lib.rs index c85871ea0..27de788bd 100644 --- a/crates/xmss/src/lib.rs +++ b/crates/xmss/src/lib.rs @@ -1,19 +1,28 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use backend::{DIGEST_LEN_FE, KoalaBear, POSEIDON1_WIDTH, PrimeCharacteristicRing, poseidon16_compress}; +use backend::{DIGEST_LEN_FE, KoalaBear, POSEIDON1_WIDTH, PrimeCharacteristicRing, PrimeField32, poseidon16_compress}; +#[cfg(any(test, feature = "test-utils"))] pub mod signers_cache; +mod ssz_serialization; +pub use ssz_serialization::{PUB_KEY_SSZ_LEN, SIGNATURE_SSZ_LEN}; mod wots; -pub use wots::*; +// The rest of the WOTS layer (one-time secret keys, chain walking, encoding) is a private +// building block of xmss_sign / xmss_verify; only the signature type is part of the API. +pub use wots::WotsSignature; mod xmss; pub use xmss::*; pub const XMSS_DIGEST_LEN: usize = 4; pub(crate) const TWEAK_LEN: usize = 2; -type F = KoalaBear; -type Digest = [F; XMSS_DIGEST_LEN]; -type PublicParam = [F; PUBLIC_PARAM_LEN_FE]; -type Randomness = [F; RANDOMNESS_LEN_FE]; +/// The base field of the scheme. +pub type F = KoalaBear; +/// A truncated Poseidon digest: hash-chain values and Merkle tree nodes. +pub type Digest = [F; XMSS_DIGEST_LEN]; +/// The per-key public parameter of the tweakable hash. +pub type PublicParam = [F; PUBLIC_PARAM_LEN_FE]; +/// The encoding randomness carried in every signature. +pub type Randomness = [F; RANDOMNESS_LEN_FE]; // WOTS pub const V: usize = 42; @@ -23,7 +32,12 @@ pub const NUM_CHAIN_HASHES: usize = 110; pub const TARGET_SUM: usize = V * (CHAIN_LENGTH - 1) - NUM_CHAIN_HASHES; pub const NUM_ENCODING_FE: usize = V.div_ceil(24 / W); pub const RANDOMNESS_LEN_FE: usize = 6; -pub const MESSAGE_LEN_FE: usize = 8; +/// Byte length of the messages being signed. +pub const MESSAGE_LEN_BYTES: usize = 32; +/// Field elements of the injective base-p embedding of a message (p > 2^30, 9 * 30 >= 8 * 32). +pub(crate) const MESSAGE_EMBEDDING_LEN_FE: usize = 9; +/// Field elements of the hashed message, the form consumed by WOTS encoding (and the snark). +pub(crate) const MESSAGE_LEN_FE: usize = 8; pub const PUBLIC_PARAM_LEN_FE: usize = 4; pub const PUB_KEY_FLAT_SIZE: usize = XMSS_DIGEST_LEN + PUBLIC_PARAM_LEN_FE; pub const WOTS_SIG_SIZE_FE: usize = RANDOMNESS_LEN_FE + V * XMSS_DIGEST_LEN; @@ -38,10 +52,51 @@ pub const TWEAK_TYPE_MERKLE: usize = 2; pub const TWEAK_TYPE_ENCODING: usize = 3; const _: () = assert!(V.is_multiple_of(2)); // For efficiency of the snark (we can batch chains in pairs) +const _: () = assert!(MESSAGE_EMBEDDING_LEN_FE * 30 >= MESSAGE_LEN_BYTES * 8); // Injective embedding +const _: () = assert!(MESSAGE_LEN_FE == DIGEST_LEN_FE); // hash_message output is one Poseidon digest +const _: () = assert!(MESSAGE_EMBEDDING_LEN_FE < POSEIDON1_WIDTH); // Domain sep + embedding fit +// Domain separators, placed in the first lane of a poseidon16_compress input. The window +// [336, 1024) is collision-free with every tweak first lane: chain tweaks with index_hi = 0 +// stay below 336 (sub_position <= V * CHAIN_LENGTH - 1), any other tweak is >= 1024. pub(crate) const PRF_DOMAINSEP_WOTS_SECRET_KEY: u32 = 1000; pub(crate) const PRF_DOMAINSEP_PUBLIC_PARAM: u32 = 1001; pub(crate) const PRF_DOMAINSEP_RANDOM_NODE: u32 = 1002; +pub(crate) const PRF_DOMAINSEP_SIGNATURE_RANDOMNESS: u32 = 1003; +pub(crate) const DOMAINSEP_MESSAGE_HASH: u32 = 1004; + +/// Signing grinds the encoding randomness until the encoding is valid (expected: a few hundred +/// attempts); this bound only exists so a broken configuration fails instead of looping forever. +pub const MAX_SIGNING_ATTEMPTS: usize = 100_000; + +/// Injective embedding of a message into `MESSAGE_EMBEDDING_LEN_FE` field elements: +/// little-endian base-p decomposition of the message read as a little-endian integer +/// (the same convention as leanSig's `encode_message`). +pub(crate) fn encode_message(message: &[u8; MESSAGE_LEN_BYTES]) -> [F; MESSAGE_EMBEDDING_LEN_FE] { + let p = u64::from(F::ORDER_U32); + let mut words: [u32; MESSAGE_LEN_BYTES / 4] = + std::array::from_fn(|i| u32::from_le_bytes(message[4 * i..4 * (i + 1)].try_into().unwrap())); + std::array::from_fn(|_| { + // Long division of the little-endian `words` integer by p; the remainder is the limb. + let mut rem: u64 = 0; + for word in words.iter_mut().rev() { + let cur = (rem << 32) | u64::from(*word); + *word = (cur / p) as u32; + rem = cur % p; + } + F::from_u64(rem) + }) +} + +/// Off-circuit message hash: what gets signed (and what the snark consumes as "message") is +/// this domain-separated Poseidon digest of the 32-byte message. +#[doc(hidden)] +pub fn hash_message(message: &[u8; MESSAGE_LEN_BYTES]) -> [F; DIGEST_LEN_FE] { + let mut input = [F::ZERO; POSEIDON1_WIDTH]; + input[0] = F::from_u32(DOMAINSEP_MESSAGE_HASH); + input[1..1 + MESSAGE_EMBEDDING_LEN_FE].copy_from_slice(&encode_message(message)); + poseidon16_compress(input) +} pub(crate) fn poseidon_prf(domain: u32, seed: &[u8; 32], indices: [usize; 2]) -> [F; DIGEST_LEN_FE] { let mut input = [F::ZERO; 16]; @@ -106,3 +161,37 @@ pub(crate) fn build_right_chain_input(public_param: &PublicParam) -> [F; DIGEST_ right[..PUBLIC_PARAM_LEN_FE].copy_from_slice(public_param); right } + +#[cfg(test)] +mod tests { + use super::*; + + /// Expected limbs computed independently (big-integer base-p decomposition, little-endian). + #[test] + fn encode_message_reference_vectors() { + let cases: [([u8; 32], [u32; MESSAGE_EMBEDDING_LEN_FE]); 3] = [ + ( + std::array::from_fn(|i| i as u8), + [ + 158200685, 22817125, 768861932, 1220633732, 741473605, 1829125427, 227592113, 282695284, 33, + ], + ), + ( + [0xFF; 32], + [ + 1539525976, 1261153412, 1969546126, 1544481308, 1871195519, 936857536, 333911385, 1230415057, 272, + ], + ), + ( + std::array::from_fn(|i| if i % 2 == 0 { 0x00 } else { 0xFF }), + [ + 1914907182, 1596164347, 55024625, 1538471654, 1366473412, 361154807, 606204774, 1101267152, 271, + ], + ), + ]; + for (message, expected) in cases { + assert_eq!(encode_message(&message).map(|l| l.as_canonical_u32()), expected); + } + assert_eq!(encode_message(&[0; 32]), [F::ZERO; MESSAGE_EMBEDDING_LEN_FE]); + } +} diff --git a/crates/xmss/src/signers_cache.rs b/crates/xmss/src/signers_cache.rs index 81140d5f3..5aa05177c 100644 --- a/crates/xmss/src/signers_cache.rs +++ b/crates/xmss/src/signers_cache.rs @@ -1,6 +1,6 @@ use backend::*; +use rand::SeedableRng; use rand::rngs::StdRng; -use rand::{RngExt, SeedableRng}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; @@ -19,11 +19,11 @@ pub fn get_benchmark_signatures() -> &'static Vec<(XmssPublicKey, XmssSignature) pub const BENCHMARK_SLOT: u32 = 111; pub const NUM_BENCHMARK_SIGNERS: usize = 10_000; -pub fn message_for_benchmark() -> [F; MESSAGE_LEN_FE] { - std::array::from_fn(F::from_usize) +pub fn message_for_benchmark() -> [u8; MESSAGE_LEN_BYTES] { + std::array::from_fn(|i| i as u8) } -const CACHE_SCHEMA_VERSION: u32 = 4; +const CACHE_SCHEMA_VERSION: u32 = 6; #[derive(Serialize, Deserialize)] struct SignersCacheFile { @@ -35,7 +35,7 @@ fn cache_footprint(first_pubkey: &XmssPublicKey) -> u128 { let mut input = [F::ZERO; 16]; input[0] = F::from_usize(NUM_BENCHMARK_SIGNERS); input[1] = F::from_u32(BENCHMARK_SLOT); - input[2..2 + MESSAGE_LEN_FE].copy_from_slice(&message_for_benchmark()); + input[2..2 + MESSAGE_LEN_FE].copy_from_slice(&hash_message(&message_for_benchmark())); input[2 + MESSAGE_LEN_FE..][..XMSS_DIGEST_LEN].copy_from_slice(&first_pubkey.merkle_root); let digest = poseidon16_compress(input); digest[..4] @@ -60,10 +60,8 @@ fn cache_path(first_pubkey: &XmssPublicKey) -> PathBuf { fn compute_signer(index: usize) -> (XmssPublicKey, XmssSignature) { let mut rng = StdRng::seed_from_u64(index as u64); - let key_start = BENCHMARK_SLOT; - let key_end = BENCHMARK_SLOT + 1; - let (sk, pk) = xmss_key_gen(rng.random(), key_start, key_end, true).unwrap(); - let sig = xmss_sign(&mut rng, &sk, &message_for_benchmark(), BENCHMARK_SLOT).unwrap(); + let (pk, sk) = xmss_key_gen(&mut rng, BENCHMARK_SLOT as u64, 2).unwrap(); + let sig = xmss_sign(&sk, BENCHMARK_SLOT, &message_for_benchmark()).unwrap(); (pk, sig) } @@ -123,7 +121,7 @@ fn test_signature_cache() { let signatures = get_benchmark_signatures(); parallel::for_each_index(signatures.len(), |i| { let (pk, sig) = &signatures[i]; - xmss_verify(pk, &message_for_benchmark(), sig, BENCHMARK_SLOT) + xmss_verify(pk, BENCHMARK_SLOT, &message_for_benchmark(), sig) .unwrap_or_else(|_| panic!("Signature {} failed to verify", i)); }); } diff --git a/crates/xmss/src/ssz_serialization.rs b/crates/xmss/src/ssz_serialization.rs new file mode 100644 index 000000000..4bac1ccbe --- /dev/null +++ b/crates/xmss/src/ssz_serialization.rs @@ -0,0 +1,146 @@ +//! SSZ encodings (Ethereum consensus-layer compatibility) for the public key and the +//! signature — the two objects that appear in consensus data. The secret key never does, +//! so it only gets serde persistence. Field elements are serialized as their canonical +//! u32, little-endian; non-canonical values are rejected on decode. +//! +//! - `XmssPublicKey`: fixed length, `merkle_root | public_param` (32 bytes). +//! - `XmssSignature`: fixed length, `chain_tips | randomness | merkle_proof` (1208 bytes). + +use backend::{PrimeCharacteristicRing, PrimeField32}; +use ssz::{Decode, DecodeError, Encode}; + +use crate::*; + +const FE_BYTES: usize = 4; +const DIGEST_BYTES: usize = XMSS_DIGEST_LEN * FE_BYTES; + +/// SSZ length of an encoded public key: 32 bytes. +pub const PUB_KEY_SSZ_LEN: usize = PUB_KEY_FLAT_SIZE * FE_BYTES; +/// SSZ length of an encoded signature: 1208 bytes. +pub const SIGNATURE_SSZ_LEN: usize = (WOTS_SIG_SIZE_FE + LOG_LIFETIME * XMSS_DIGEST_LEN) * FE_BYTES; + +fn append_fes(buf: &mut Vec, fes: &[F]) { + for fe in fes { + buf.extend_from_slice(&fe.as_canonical_u32().to_le_bytes()); + } +} + +/// Parses `N` field elements, rejecting non-canonical (>= p) encodings. +fn read_fes(bytes: &[u8]) -> Result<[F; N], DecodeError> { + debug_assert_eq!(bytes.len(), N * FE_BYTES); + let mut out = [F::ZERO; N]; + for (fe, chunk) in out.iter_mut().zip(bytes.as_chunks::().0) { + let value = u32::from_le_bytes(*chunk); + if value >= F::ORDER_U32 { + return Err(DecodeError::BytesInvalid(format!( + "non-canonical field element: {value}" + ))); + } + *fe = F::from_u32(value); + } + Ok(out) +} + +fn check_len(bytes: &[u8], expected: usize) -> Result<(), DecodeError> { + if bytes.len() != expected { + return Err(DecodeError::InvalidByteLength { + len: bytes.len(), + expected, + }); + } + Ok(()) +} + +impl Encode for XmssPublicKey { + fn is_ssz_fixed_len() -> bool { + true + } + + fn ssz_fixed_len() -> usize { + PUB_KEY_SSZ_LEN + } + + fn ssz_bytes_len(&self) -> usize { + PUB_KEY_SSZ_LEN + } + + fn ssz_append(&self, buf: &mut Vec) { + append_fes(buf, &self.merkle_root); + append_fes(buf, &self.public_param); + } +} + +impl Decode for XmssPublicKey { + fn is_ssz_fixed_len() -> bool { + true + } + + fn ssz_fixed_len() -> usize { + PUB_KEY_SSZ_LEN + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + check_len(bytes, PUB_KEY_SSZ_LEN)?; + Ok(Self { + merkle_root: read_fes(&bytes[..XMSS_DIGEST_LEN * FE_BYTES])?, + public_param: read_fes(&bytes[XMSS_DIGEST_LEN * FE_BYTES..])?, + }) + } +} + +impl Encode for XmssSignature { + fn is_ssz_fixed_len() -> bool { + true + } + + fn ssz_fixed_len() -> usize { + SIGNATURE_SSZ_LEN + } + + fn ssz_bytes_len(&self) -> usize { + SIGNATURE_SSZ_LEN + } + + fn ssz_append(&self, buf: &mut Vec) { + for chain_tip in &self.wots_signature.chain_tips { + append_fes(buf, chain_tip); + } + append_fes(buf, &self.wots_signature.randomness); + for neighbour in &self.merkle_proof { + append_fes(buf, neighbour); + } + } +} + +impl Decode for XmssSignature { + fn is_ssz_fixed_len() -> bool { + true + } + + fn ssz_fixed_len() -> usize { + SIGNATURE_SSZ_LEN + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + check_len(bytes, SIGNATURE_SSZ_LEN)?; + let mut digests = bytes.as_chunks::().0.iter(); + let mut chain_tips = [[F::ZERO; XMSS_DIGEST_LEN]; V]; + for chain_tip in &mut chain_tips { + *chain_tip = read_fes(digests.next().unwrap())?; + } + let randomness_start = V * DIGEST_BYTES; + let randomness = read_fes(&bytes[randomness_start..randomness_start + RANDOMNESS_LEN_FE * FE_BYTES])?; + let mut digests = bytes[randomness_start + RANDOMNESS_LEN_FE * FE_BYTES..] + .as_chunks::() + .0 + .iter(); + let mut merkle_proof = [[F::ZERO; XMSS_DIGEST_LEN]; LOG_LIFETIME]; + for neighbour in &mut merkle_proof { + *neighbour = read_fes(digests.next().unwrap())?; + } + Ok(Self { + wots_signature: WotsSignature { chain_tips, randomness }, + merkle_proof, + }) + } +} diff --git a/crates/xmss/src/wots.rs b/crates/xmss/src/wots.rs index a362229e2..8fac38378 100644 --- a/crates/xmss/src/wots.rs +++ b/crates/xmss/src/wots.rs @@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize}; use crate::*; -#[derive(Debug)] +/// No Debug: `pre_images` are the one-time secret keys. pub struct WotsSecretKey { - pub pre_images: [Digest; V], + pre_images: [Digest; V], public_key: WotsPublicKey, } @@ -41,18 +41,7 @@ impl WotsSecretKey { &self.public_key } - pub fn sign_with_randomness( - &self, - message: &[F; MESSAGE_LEN_FE], - slot: u32, - xmss_pub_key: &XmssPublicKey, - randomness: Randomness, - ) -> Option { - let encoding = wots_encode(message, slot, xmss_pub_key, &randomness)?; - Some(self.sign_with_encoding(randomness, &encoding, xmss_pub_key.public_param, slot)) - } - - fn sign_with_encoding( + pub(crate) fn sign_with_encoding( &self, randomness: Randomness, encoding: &[u8; V], @@ -125,22 +114,6 @@ pub fn iterate_hash( }) } -pub fn find_randomness_for_wots_encoding( - message: &[F; MESSAGE_LEN_FE], - slot: u32, - xmss_pub_key: &XmssPublicKey, - rng: &mut impl CryptoRng, -) -> (Randomness, [u8; V], usize) { - let mut num_iters = 0; - loop { - num_iters += 1; - let randomness = rng.random(); - if let Some(encoding) = wots_encode(message, slot, xmss_pub_key, &randomness) { - return (randomness, encoding, num_iters); - } - } -} - pub fn wots_encode( message: &[F; MESSAGE_LEN_FE], slot: u32, @@ -191,3 +164,41 @@ fn is_valid_encoding(encoding: &[u8]) -> bool { } true } + +#[cfg(test)] +mod tests { + use rand::{RngExt, SeedableRng, rngs::StdRng}; + + use super::*; + + /// Measures the average number of randomness attempts before a valid encoding. + #[test] + #[ignore] + fn encoding_grinding_bits() { + let n = 100; + let xmss_pub_key = XmssPublicKey { + merkle_root: Default::default(), + public_param: Default::default(), + }; + let total_iters = parallel::map_reduce( + n, + || 0usize, + |i| { + let message: [F; MESSAGE_LEN_FE] = Default::default(); + let slot = i as u32; + let mut rng = StdRng::seed_from_u64(i as u64); + let mut num_iters = 0; + loop { + num_iters += 1; + let randomness: Randomness = rng.random(); + if wots_encode(&message, slot, &xmss_pub_key, &randomness).is_some() { + break num_iters; + } + } + }, + |a, b| a + b, + ); + let grinding = ((total_iters as f64) / (n as f64)).log2(); + println!("Average grinding bits: {:.1}", grinding); + } +} diff --git a/crates/xmss/src/xmss.rs b/crates/xmss/src/xmss.rs index 015adb41e..1fcc3a893 100644 --- a/crates/xmss/src/xmss.rs +++ b/crates/xmss/src/xmss.rs @@ -1,16 +1,16 @@ use std::sync::Mutex; use backend::*; -use rand::{CryptoRng, SeedableRng, rngs::StdRng}; +use rand::{CryptoRng, RngExt, SeedableRng, rngs::StdRng}; use serde::{Deserialize, Serialize}; +use crate::wots::*; use crate::*; /// Memory-optimized secret key for a range of R = slot_end - slot_start + 1 slots: O(sqrt(R) + /// LOG_LIFETIME) instead of O(R). Stores the top tree (in-range band plus a thin spine) and one /// cached bottom subtree, cut at split_level = log2(R)/2. Out-of-range nodes are deterministic /// gen_random_node fillers; see `xmss_small_memory.tex` for the picture. -#[derive(Debug)] pub struct XmssSecretKey { pub(crate) slot_start: u32, // inclusive pub(crate) slot_end: u32, // inclusive @@ -22,6 +22,16 @@ pub struct XmssSecretKey { pub(crate) cache: Mutex>, } +impl std::fmt::Debug for XmssSecretKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("XmssSecretKey") + .field("slot_start", &self.slot_start) + .field("slot_end", &self.slot_end) + .field("split_level", &self.split_level) + .finish_non_exhaustive() + } +} + /// Bottom subtree covering the last-signed slot; its leaf range is derived from `subtree_index`. #[derive(Debug)] pub(crate) struct BottomSubtree { @@ -29,6 +39,38 @@ pub(crate) struct BottomSubtree { layers: Vec>, } +/// Format version of the persisted secret key; bump on layout changes. +const SECRET_KEY_FORMAT_VERSION: u8 = 1; + +/// Persists (version, seed, slot range, top tree). The top tree is stored so that loading a +/// key is cheap (no re-hashing of the whole range); the derived fields (public_param, +/// split_level) are recomputed and the tree shape revalidated. The bottom-subtree cache +/// restarts empty. +impl Serialize for XmssSecretKey { + fn serialize(&self, s: S) -> Result { + ( + SECRET_KEY_FORMAT_VERSION, + &self.seed, + self.slot_start, + self.slot_end, + &self.top, + ) + .serialize(s) + } +} + +impl<'de> Deserialize<'de> for XmssSecretKey { + fn deserialize>(d: D) -> Result { + let (version, seed, slot_start, slot_end, top) = <(u8, [u8; 32], u32, u32, Vec>)>::deserialize(d)?; + if version != SECRET_KEY_FORMAT_VERSION { + return Err(serde::de::Error::custom(format!( + "unsupported secret key format version {version} (expected {SECRET_KEY_FORMAT_VERSION})" + ))); + } + Self::from_parts(seed, slot_start, slot_end, top).map_err(serde::de::Error::custom) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct XmssSignature { pub wots_signature: WotsSignature, @@ -46,7 +88,7 @@ pub struct XmssPublicKey { } impl XmssPublicKey { - pub fn flaten(&self) -> [F; PUB_KEY_FLAT_SIZE] { + pub fn flatten(&self) -> [F; PUB_KEY_FLAT_SIZE] { let mut output = [F::default(); PUB_KEY_FLAT_SIZE]; output[..XMSS_DIGEST_LEN].copy_from_slice(&self.merkle_root); output[XMSS_DIGEST_LEN..].copy_from_slice(&self.public_param); @@ -82,6 +124,19 @@ pub enum XmssKeyGenError { InvalidRange, } +impl std::fmt::Display for XmssKeyGenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidRange => write!( + f, + "invalid slot range (empty, reversed, or beyond the 2^{LOG_LIFETIME} lifetime)" + ), + } + } +} + +impl std::error::Error for XmssKeyGenError {} + fn fill(sequential: bool, data: &mut [T], f: impl Fn(usize, &mut T) + Sync) { if sequential { data.iter_mut().enumerate().for_each(|(i, out)| f(i, out)); @@ -171,21 +226,40 @@ fn build_subtree_layers( layers } -pub fn xmss_key_gen( +/// Generates a new key pair, active for the `num_active_slots` slots starting at +/// `activation_slot` (both ends must stay within the `2^LOG_LIFETIME` lifetime). +pub fn xmss_key_gen( + rng: &mut R, + activation_slot: u64, + num_active_slots: u64, +) -> Result<(XmssPublicKey, XmssSecretKey), XmssKeyGenError> { + xmss_key_gen_from_seed(rng.random(), activation_slot, num_active_slots) +} + +/// Deterministic [`xmss_key_gen`]: the same (seed, activation range) always regenerates the +/// same key pair. The seed is the key's entire secret material. +pub fn xmss_key_gen_from_seed( seed: [u8; 32], - slot_start: u32, - slot_end: u32, - sequential: bool, -) -> Result<(XmssSecretKey, XmssPublicKey), XmssKeyGenError> { - if slot_start > slot_end || slot_end as u64 >= (1 << LOG_LIFETIME) { + activation_slot: u64, + num_active_slots: u64, +) -> Result<(XmssPublicKey, XmssSecretKey), XmssKeyGenError> { + let activation_end = activation_slot + .checked_add(num_active_slots) + .ok_or(XmssKeyGenError::InvalidRange)?; + if num_active_slots == 0 || activation_end > 1 << LOG_LIFETIME { return Err(XmssKeyGenError::InvalidRange); } + + // The pool forbids nested dispatch: build sequentially when key gen itself already runs + // inside a pool task (e.g. generating many keys in a parallel batch). + let sequential = parallel::is_in_pool_task(); + let public_param: PublicParam = gen_public_param(&seed); - let lo = slot_start as u64; - let hi = slot_end as u64; + let lo = activation_slot; + let hi = activation_end - 1; // ~sqrt(R) leaves per bottom subtree; always <= LOG_LIFETIME/2 since R <= 2^LOG_LIFETIME. - let split_level = log2_ceil_usize((hi - lo + 1) as usize).div_ceil(2); + let split_level = log2_ceil_usize(num_active_slots as usize).div_ceil(2); // Roots of each bottom subtree, built one at a time so peak memory stays O(sqrt(R)). let first_subtree = lo >> split_level; @@ -214,46 +288,74 @@ pub fn xmss_key_gen( public_param, }; let secret_key = XmssSecretKey { - slot_start, - slot_end, + slot_start: activation_slot as u32, + slot_end: hi as u32, public_param, seed, split_level, top, cache: Mutex::new(None), }; - Ok((secret_key, pub_key)) + Ok((pub_key, secret_key)) } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum XmssSignatureError { SlotOutOfRange, - InvalidRandomness, + EncodingAttemptsExceeded, } -/// WARNING: XMSS is statefull signature, you should never sign with the same same `slot` twice. -/// (Even signing twice with the same message, at the same slot, is insecure, due to the non-determinism of the randomness part of the signature) -pub fn xmss_sign( - rng: &mut R, +impl std::fmt::Display for XmssSignatureError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SlotOutOfRange => write!(f, "slot is outside the key's valid range"), + Self::EncodingAttemptsExceeded => { + write!(f, "no valid WOTS encoding found within {MAX_SIGNING_ATTEMPTS} attempts") + } + } + } +} + +impl std::error::Error for XmssSignatureError {} + +/// Deterministic encoding randomness: a seed-keyed Poseidon PRF over (slot, attempt), mixed +/// with the hashed message by one more compression. +fn derive_signature_randomness( + seed: &[u8; 32], + slot: u32, + message_fe: &[F; MESSAGE_LEN_FE], + attempt: usize, +) -> Randomness { + let prf = poseidon_prf(PRF_DOMAINSEP_SIGNATURE_RANDOMNESS, seed, [slot as usize, attempt]); + let mut input = [F::ZERO; POSEIDON1_WIDTH]; + input[..DIGEST_LEN_FE].copy_from_slice(&prf); + input[DIGEST_LEN_FE..].copy_from_slice(message_fe); + poseidon16_compress(input)[..RANDOMNESS_LEN_FE].try_into().unwrap() +} + +/// WARNING: XMSS is a stateful signature scheme, never sign two different messages at the same +/// `slot`. Signing is derandomized (the encoding randomness is derived from the secret key, +/// slot, and message), so calling this twice with the same (slot, message) pair returns the +/// same signature and is harmless. +pub fn xmss_sign( secret_key: &XmssSecretKey, - message: &[F; MESSAGE_LEN_FE], slot: u32, + message: &[u8; MESSAGE_LEN_BYTES], ) -> Result { - let (randomness, _, _) = find_randomness_for_wots_encoding(message, slot, &secret_key.public_key(), rng); - if slot < secret_key.slot_start || slot > secret_key.slot_end { return Err(XmssSignatureError::SlotOutOfRange); } + let message_fe = hash_message(message); + let pub_key = secret_key.public_key(); + let (randomness, encoding) = (0..MAX_SIGNING_ATTEMPTS) + .find_map(|attempt| { + let randomness = derive_signature_randomness(&secret_key.seed, slot, &message_fe, attempt); + wots_encode(&message_fe, slot, &pub_key, &randomness).map(|encoding| (randomness, encoding)) + }) + .ok_or(XmssSignatureError::EncodingAttemptsExceeded)?; let wots_secret_key = gen_wots_secret_key(&secret_key.seed, slot, secret_key.public_param); - let wots_signature = wots_secret_key - .sign_with_randomness(message, slot, &secret_key.public_key(), randomness) - .ok_or(XmssSignatureError::InvalidRandomness)?; - // Cache the bottom subtree covering `slot` (reused across its 2^split_level slots), then read the path. - let subtree_index = (slot as u64) >> secret_key.split_level; - let mut cache = secret_key.cache.lock().unwrap(); - if cache.as_ref().is_none_or(|s| s.subtree_index != subtree_index) { - *cache = Some(secret_key.build_bottom_subtree(subtree_index)); - } + let wots_signature = wots_secret_key.sign_with_encoding(randomness, &encoding, secret_key.public_param, slot); + let cache = secret_key.cached_bottom_subtree(slot); let sub = cache.as_ref().unwrap(); let merkle_proof = std::array::from_fn(|level| { let neighbour_index = ((slot as u64) >> level) ^ 1; @@ -274,7 +376,67 @@ impl XmssSecretKey { } } - /// (Re)build the bottom subtree with the given index. + /// The slots this key can sign for. + pub const fn activation_slots(&self) -> std::ops::RangeInclusive { + self.slot_start..=self.slot_end + } + + /// Warms the signing cache for `slot`: when the next signing slot is known in advance, + /// calling this ahead of time makes the subsequent `xmss_sign` faster. + pub fn prepare(&self, slot: u32) -> Result<(), XmssSignatureError> { + if slot < self.slot_start || slot > self.slot_end { + return Err(XmssSignatureError::SlotOutOfRange); + } + drop(self.cached_bottom_subtree(slot)); + Ok(()) + } + + /// Rebuild a secret key from its persisted parts, recomputing the derived fields and + /// revalidating the top tree's shape (its content is trusted, exactly like the seed). + pub(crate) fn from_parts( + seed: [u8; 32], + slot_start: u32, + slot_end: u32, + top: Vec>, + ) -> Result { + if slot_start > slot_end { + return Err("invalid slot range"); + } + let (lo, hi) = (slot_start as u64, slot_end as u64); + let split_level = log2_ceil_usize((hi - lo + 1) as usize).div_ceil(2); + let expected_layer_lens = + (split_level..=LOG_LIFETIME).map(|level| ((hi >> level) - (lo >> level) + 1) as usize); + if top.len() != LOG_LIFETIME - split_level + 1 + || top + .iter() + .zip(expected_layer_lens) + .any(|(layer, len)| layer.len() != len) + { + return Err("top tree shape does not match the slot range"); + } + Ok(Self { + slot_start, + slot_end, + public_param: gen_public_param(&seed), + seed, + split_level, + top, + cache: Mutex::new(None), + }) + } + + fn cached_bottom_subtree(&self, slot: u32) -> std::sync::MutexGuard<'_, Option> { + let subtree_index = (slot as u64) >> self.split_level; + let mut cache = self.cache.lock().unwrap(); + if cache.as_ref().is_none_or(|s| s.subtree_index != subtree_index) { + *cache = Some(self.build_bottom_subtree(subtree_index)); + } + cache + } + + /// (Re)build the bottom subtree with the given index. Always sequential: signing must + /// never wait on the thread pool while it holds the signing-cache mutex (a pool task + /// blocked on the same key would deadlock the pool, and hence the signer). fn build_bottom_subtree(&self, subtree_index: u64) -> BottomSubtree { let (lo, hi) = subtree_bounds( self.slot_start as u64, @@ -319,15 +481,27 @@ pub enum XmssVerifyError { InvalidMerklePath, } +impl std::fmt::Display for XmssVerifyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidWots => write!(f, "invalid WOTS signature (encoding rejected or wrong chain tips)"), + Self::InvalidMerklePath => write!(f, "merkle path does not lead to the public key's root"), + } + } +} + +impl std::error::Error for XmssVerifyError {} + pub fn xmss_verify( pub_key: &XmssPublicKey, - message: &[F; MESSAGE_LEN_FE], - signature: &XmssSignature, slot: u32, + message: &[u8; MESSAGE_LEN_BYTES], + signature: &XmssSignature, ) -> Result<(), XmssVerifyError> { + let message_fe = hash_message(message); let wots_public_key = signature .wots_signature - .recover_public_key(message, slot, pub_key) + .recover_public_key(&message_fe, slot, pub_key) .ok_or(XmssVerifyError::InvalidWots)?; let mut current_hash = wots_public_key.hash(pub_key.public_param, slot); for (level, neighbour) in signature.merkle_proof.iter().enumerate() { diff --git a/crates/xmss/tests/xmss_tests.rs b/crates/xmss/tests/xmss_tests.rs index 6d2721df0..67555a16a 100644 --- a/crates/xmss/tests/xmss_tests.rs +++ b/crates/xmss/tests/xmss_tests.rs @@ -6,14 +6,11 @@ type F = KoalaBear; #[test] fn test_xmss_serialize_deserialize() { - let keygen_seed: [u8; 32] = std::array::from_fn(|i| i as u8); - let message: [F; MESSAGE_LEN_FE] = std::array::from_fn(|i| F::from_usize(i * 3 + 7)); - let slot_start = 100; - let slot_end = 115; + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| (i * 3 + 7) as u8); let slot = 110; - let (sk, pk) = xmss_key_gen(keygen_seed, slot_start, slot_end, false).unwrap(); - let sig = xmss_sign(&mut StdRng::seed_from_u64(slot as u64), &sk, &message, slot).unwrap(); + let (pk, sk) = xmss_key_gen(&mut StdRng::seed_from_u64(0), 100, 16).unwrap(); + let sig = xmss_sign(&sk, slot, &message).unwrap(); let pk_bytes = postcard::to_allocvec(&pk).unwrap(); let pk2: XmssPublicKey = postcard::from_bytes(&pk_bytes).unwrap(); @@ -23,42 +20,169 @@ fn test_xmss_serialize_deserialize() { let sig2: XmssSignature = postcard::from_bytes(&sig_bytes).unwrap(); assert_eq!(sig, sig2); - xmss_verify(&pk2, &message, &sig2, slot).unwrap(); + xmss_verify(&pk2, slot, &message, &sig2).unwrap(); + + // Secret key: persist and reload, then check the reloaded key behaves identically. + let sk_bytes = postcard::to_allocvec(&sk).unwrap(); + let sk2: XmssSecretKey = postcard::from_bytes(&sk_bytes).unwrap(); + assert_eq!(sk2.public_key(), pk); + assert_eq!(xmss_sign(&sk2, slot, &message).unwrap(), sig); + + // A top tree whose shape does not match the slot range must be rejected. + let (version, seed, start, end, mut top): (u8, [u8; 32], u32, u32, Vec>) = + postcard::from_bytes(&sk_bytes).unwrap(); + top.pop(); + let corrupted = postcard::to_allocvec(&(version, seed, start, end, top)).unwrap(); + assert!(postcard::from_bytes::(&corrupted).is_err()); + + // An unknown format version must be rejected. + let mut wrong_version = sk_bytes.clone(); + wrong_version[0] ^= 1; + assert!(postcard::from_bytes::(&wrong_version).is_err()); } #[test] fn keygen_sign_verify() { - let keygen_seed: [u8; 32] = std::array::from_fn(|i| i as u8); - let message: [F; MESSAGE_LEN_FE] = std::array::from_fn(|i| F::from_usize(i * 3 + 7)); + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| (i * 3 + 7) as u8); for slot in [0, 1234, u32::MAX] { - let (sk, pk) = xmss_key_gen(keygen_seed, slot.saturating_sub(1), slot.saturating_add(2), false).unwrap(); - let sig = xmss_sign(&mut StdRng::seed_from_u64(slot as u64), &sk, &message, slot).unwrap(); - xmss_verify(&pk, &message, &sig, slot).unwrap(); + let activation_slot = (slot as u64).saturating_sub(1); + let num_active_slots = (slot as u64 + 3).min(1 << LOG_LIFETIME) - activation_slot; + let mut rng = StdRng::seed_from_u64(slot as u64); + let (pk, sk) = xmss_key_gen(&mut rng, activation_slot, num_active_slots).unwrap(); + let sig = xmss_sign(&sk, slot, &message).unwrap(); + xmss_verify(&pk, slot, &message, &sig).unwrap(); + + let mut other_message = message; + other_message[0] ^= 1; + assert!(xmss_verify(&pk, slot, &other_message, &sig).is_err()); } } #[test] -#[ignore] -fn encoding_grinding_bits() { - let n = 100; - let xmss_pub_key = XmssPublicKey { - merkle_root: Default::default(), - public_param: Default::default(), - }; - let total_iters = parallel::map_reduce( - n, - || 0usize, - |i| { - let message: [F; MESSAGE_LEN_FE] = Default::default(); - let slot = i as u32; - let mut rng = StdRng::seed_from_u64(i as u64); - let (_randomness, _encoding, num_iters) = - find_randomness_for_wots_encoding(&message, slot, &xmss_pub_key, &mut rng); - num_iters - }, - |a, b| a + b, +fn signing_is_deterministic() { + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| i as u8); + let (_, sk) = xmss_key_gen(&mut StdRng::seed_from_u64(7), 40, 10).unwrap(); + assert_eq!( + xmss_sign(&sk, 42, &message).unwrap(), + xmss_sign(&sk, 42, &message).unwrap() + ); + assert_eq!( + xmss_sign(&sk, 39, &message).unwrap_err(), + XmssSignatureError::SlotOutOfRange ); - let grinding = ((total_iters as f64) / (n as f64)).log2(); - println!("Average grinding bits: {:.1}", grinding); + assert_eq!( + xmss_sign(&sk, 50, &message).unwrap_err(), + XmssSignatureError::SlotOutOfRange + ); +} + +#[test] +fn prepare_warms_the_signing_cache() { + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| i as u8); + let (pk, sk) = xmss_key_gen(&mut StdRng::seed_from_u64(3), 1000, 300).unwrap(); + + // Signatures are unaffected by preparation (it only warms the cache). + let cold = xmss_sign(&sk, 1299, &message).unwrap(); + sk.prepare(1000).unwrap(); // different subtree: rebuilds the cache + sk.prepare(1299).unwrap(); // back again + let warm = xmss_sign(&sk, 1299, &message).unwrap(); + assert_eq!(cold, warm); + xmss_verify(&pk, 1299, &message, &warm).unwrap(); + + assert_eq!(sk.prepare(999).unwrap_err(), XmssSignatureError::SlotOutOfRange); + assert_eq!(sk.prepare(1300).unwrap_err(), XmssSignatureError::SlotOutOfRange); +} + +/// One key shared between signers inside pool tasks and on a plain thread. Bottom-subtree +/// builds during signing are always sequential, so a signer holding the cache mutex never +/// waits on the pool (which would deadlock with the pool tasks blocked on the same key). +/// Derandomization makes the signatures identical regardless of who produced them. +#[test] +fn concurrent_signing_on_shared_key() { + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| i as u8); + let (pk, sk) = xmss_key_gen(&mut StdRng::seed_from_u64(9), 0, 1 << 10).unwrap(); + let (pk, sk) = (&pk, &sk); + + std::thread::scope(|scope| { + let plain_thread = scope.spawn(move || { + (0..1024u32) + .step_by(97) + .map(|slot| (slot, xmss_sign(sk, slot, &message).unwrap())) + .collect::>() + }); + parallel::for_each_index(64, |i| { + let slot = (i * 61 % 1024) as u32; + let sig = xmss_sign(sk, slot, &message).unwrap(); + xmss_verify(pk, slot, &message, &sig).unwrap(); + }); + for (slot, sig) in plain_thread.join().unwrap() { + xmss_verify(pk, slot, &message, &sig).unwrap(); + assert_eq!(sig, xmss_sign(sk, slot, &message).unwrap()); + } + }); +} + +#[test] +fn keygen_from_seed_is_deterministic() { + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| i as u8); + let seed = [42u8; 32]; + + let (pk1, sk1) = xmss_key_gen_from_seed(seed, 100, 16).unwrap(); + let (pk2, sk2) = xmss_key_gen_from_seed(seed, 100, 16).unwrap(); + assert_eq!(pk1, pk2); + assert_eq!(sk1.activation_slots(), 100..=115); + assert_eq!( + xmss_sign(&sk1, 110, &message).unwrap(), + xmss_sign(&sk2, 110, &message).unwrap() + ); + + // Same seed, different activation range: a different key. + let (pk3, _) = xmss_key_gen_from_seed(seed, 100, 32).unwrap(); + assert_ne!(pk1, pk3); +} + +#[test] +fn keygen_rejects_invalid_ranges() { + let mut rng = StdRng::seed_from_u64(0); + assert_eq!(xmss_key_gen(&mut rng, 0, 0).unwrap_err(), XmssKeyGenError::InvalidRange); + assert_eq!( + xmss_key_gen(&mut rng, (1 << LOG_LIFETIME) - 1, 2).unwrap_err(), + XmssKeyGenError::InvalidRange + ); + assert_eq!( + xmss_key_gen(&mut rng, u64::MAX, u64::MAX).unwrap_err(), + XmssKeyGenError::InvalidRange + ); +} + +#[test] +fn ssz_roundtrip() { + use ssz::{Decode, Encode}; + + let message: [u8; MESSAGE_LEN_BYTES] = std::array::from_fn(|i| i as u8); + let slot = 110; + let (pk, sk) = xmss_key_gen(&mut StdRng::seed_from_u64(1), 100, 16).unwrap(); + let sig = xmss_sign(&sk, slot, &message).unwrap(); + + // Public key: fixed 32 bytes. + let pk_bytes = pk.as_ssz_bytes(); + assert_eq!(pk_bytes.len(), PUB_KEY_SSZ_LEN); + assert_eq!(pk_bytes.len(), 32); + assert_eq!(XmssPublicKey::from_ssz_bytes(&pk_bytes).unwrap(), pk); + + // Signature: fixed 1208 bytes. + let sig_bytes = sig.as_ssz_bytes(); + assert_eq!(sig_bytes.len(), SIGNATURE_SSZ_LEN); + assert_eq!(sig_bytes.len(), 1208); + assert_eq!(XmssSignature::from_ssz_bytes(&sig_bytes).unwrap(), sig); + + // Non-canonical field elements are rejected. + let mut bad_pk = pk_bytes.clone(); + bad_pk[..4].copy_from_slice(&u32::MAX.to_le_bytes()); + assert!(XmssPublicKey::from_ssz_bytes(&bad_pk).is_err()); + + // Wrong lengths are rejected. + assert!(XmssPublicKey::from_ssz_bytes(&pk_bytes[1..]).is_err()); + assert!(XmssSignature::from_ssz_bytes(&sig_bytes[..SIGNATURE_SSZ_LEN - 1]).is_err()); } diff --git a/crates/xmss/xmss.md b/crates/xmss/xmss.md index e1538e1ce..5bffdb6dd 100644 --- a/crates/xmss/xmss.md +++ b/crates/xmss/xmss.md @@ -6,16 +6,26 @@ KoalaBear (p = 2^31 - 2^24 + 1). ## Hash function -[Poseidon](https://eprint.iacr.org/2019/458), in compression mode (feedforward addition). Input: 16 field elements. Output: 8 field elements. We denote it `H`. Chain hashes, Merkle hashes, and the final WOTS-pubkey hash truncate the output to 4 field elements (`n`); the encoding step and the intermediate WOTS-pubkey sponge states keep the full 8 elements. +[Poseidon](https://eprint.iacr.org/2019/458), in compression mode (feedforward addition). Input: 16 field elements. Output: 8 field elements. We denote it `H`. Chain hashes, Merkle hashes, and the final WOTS-pubkey hash truncate the output to 4 field elements (`n`); the message hash, the encoding step, and the intermediate WOTS-pubkey sponge states keep the full 8 elements. -## Sizes (in field elements) +## Sizes -- `n = 4`: digest size -- `|pp| = 4`: public parameter -- `|randomness| = 6`: signature randomness -- `|msg| = 8`: message size +- `n = 4`: digest size (field elements) +- `|pp| = 4`: public parameter (field elements) +- `|randomness| = 6`: signature randomness (field elements) +- `|msg| = 32` bytes: raw message size (embedded into 9 field elements, hashed to 8, see below) - `|tweak| = 2`: tweak (domain separation: `encoding`, `chain`, `wots_pk`, `merkle`) +## Message hashing (off-circuit) + +The signed message is 32 raw bytes. It is embedded injectively into 9 field elements, then hashed with a domain separator to 8 field elements: + +`msg_fe = H(1004 | limb_0 … limb_8 | 0^6)` + +Everything below (the WOTS encoding, and hence the snark) operates on `msg_fe`; this hash is computed off-circuit. + +Domain separators (first Poseidon lane): 1000 = WOTS secret key PRF, 1001 = public parameter PRF, 1002 = random filler node PRF, 1003 = signature randomness PRF, 1004 = message hash. The window [336, 1024) cannot collide with any tweak first lane. + ## WOTS (Winternitz One Time Signature) - `v = 42`: number of hash chains @@ -26,23 +36,37 @@ KoalaBear (p = 2^31 - 2^24 + 1). `log_lifetime = 32`: a key is valid for up to `2^32` slots. `log_lifetime` corresponds to the Merkle tree height. +## Signing (derandomized) + +To sign `msg` at `slot`: + +1. `msg_fe = hash_message(msg)`. +2. For `attempt = 0, 1, ...`: `randomness = H(H(1003 | seed | slot | attempt) | msg_fe)`. Keep the first attempt whose WOTS encoding (step 2 of verification) is valid. +3. Walk chain `i` from its secret pre-image for `e_i` steps; the signature is `(randomness, chain_tips, merkle_proof)`. + +Signing is deterministic: re-signing the same `(slot, msg)` pair returns the identical signature (and is therefore harmless). Signing two *different* messages at the same slot remains forbidden — XMSS is a stateful, synchronized scheme. + +The first signature after crossing into a new bottom subtree rebuilds an `O(sqrt(R))` cache; `XmssSecretKey::prepare(slot)` can do this ahead of time, off the signing critical path. + ## Verification -Inputs: public key `(merkle_root, pp)`, message `msg`, slot `s`, signature `(randomness, chain_tips, merkle_proof)`. +Inputs: public key `(merkle_root, pp)`, 32-byte message `msg`, slot `s`, signature `(randomness, chain_tips, merkle_proof)`. + +1. **Hash message** (off-circuit): `msg_fe = hash_message(msg)`. +2. **Encode**: compute the 8-limb digest `D = H(H(msg_fe | randomness | tweak_encoding(s)) | pp | 0000)`. For each limb `D_i`, take the canonical representative `D_i = low + 2^24 · high` (with `low ∈ [0, 2^24)`, `high ∈ [0, 128)`) and reject if `high == 127` (equivalently `D_i == −1`). This guarantees an uniform encoding. Concatenate the 24-bit `low` parts of the 8 limbs in little-endian order to get 192 bits, then take the first `v · w = 126` bits split into `v = 42` little-endian chunks of `w = 3` bits → encoding `(e_0, ..., e_{v-1})` with each `e_i ∈ [0, chain_length)`. Reject if `sum(e_i) ≠ target_sum`. +3. **Recover WOTS public key**: for each `i`, walk chain `i` from `chain_tips[i]` for `chain_length - 1 - e_i` steps, where each step is `H(tweak_chain(i, step, s) | 00 | previous_value | pp | 0000)` truncated to `n`. +4. **Hash WOTS public key**: T-sponge with replacement over the `v` recovered chain ends, with IV `[tweak_wots_pk(s) | 00 | pp]`, ingesting two chain end digests at a time. Output is the Merkle leaf. +5. **Walk Merkle path**: for `level = 0..log_lifetime`, combine the current node with `merkle_proof[level]` (left/right determined by bit `level` of `s`) via `H(tweak_merkle(level+1, parent_index) | 00 | pp | left | right)` truncated to `n`. +6. **Check root**: accept iff the final hash equals `merkle_root`. -1. **Encode**: compute the 8-limb digest `D = H(H(msg | randomness | tweak_encoding(s)) | pp | 0000)`. For each limb `D_i`, take the canonical representative `D_i = low + 2^24 · high` (with `low ∈ [0, 2^24)`, `high ∈ [0, 128)`) and reject if `high == 127` (equivalently `D_i == −1`). This guarantees an uniform encoding. Concatenate the 24-bit `low` parts of the 8 limbs in little-endian order to get 192 bits, then take the first `v · w = 126` bits split into `v = 42` little-endian chunks of `w = 3` bits → encoding `(e_0, ..., e_{v-1})` with each `e_i ∈ [0, chain_length)`. Reject if `sum(e_i) ≠ target_sum`. -2. **Recover WOTS public key**: for each `i`, walk chain `i` from `chain_tips[i]` for `chain_length - 1 - e_i` steps, where each step is `H(tweak_chain(i, step, s) | 00 | previous_value | pp | 0000)` truncated to `n`. -3. **Hash WOTS public key**: T-sponge with replacement over the `v` recovered chain ends, with IV `[tweak_wots_pk(s) | 00 | pp]`, ingesting two chain end digests at a time. Output is the Merkle leaf. -4. **Walk Merkle path**: for `level = 0..log_lifetime`, combine the current node with `merkle_proof[level]` (left/right determined by bit `level` of `s`) via `H(tweak_merkle(level+1, parent_index) | 00 | pp | left | right)` truncated to `n`. -5. **Check root**: accept iff the final hash equals `merkle_root`. +## Data size +- Public key: 32 bytes (`merkle_root | pp`) +- Signature: 1208 bytes (`chain_tips | randomness | merkle_proof`) -> Below IPv6 [MTU](https://fr.wikipedia.org/wiki/Maximum_transmission_unit) (1280 bytes). ## Security -target = 123,9 ≈ 124 bits of classical security in the ROM, and ≈ 62 bits of quantum security in the QROM, with an analysis inspired by the section 3.1 of [Tight adaptive reprogramming in the QROM](https://arxiv.org/pdf/2010.15103). TODO write the complete proof. +target ≈ 124 bits of classical security in the ROM, and ≈ 62 bits of quantum security in the QROM, with an analysis inspired by the section 3.1 of [Tight adaptive reprogramming in the QROM](https://arxiv.org/pdf/2010.15103). TODO write the complete proof. ## Signature size -**1171 bytes** `log2(p).(|randomness| + n.(v + log_lifetime))` - -below IPv6 [MTU](https://fr.wikipedia.org/wiki/Maximum_transmission_unit) (1280 bytes) diff --git a/src/lib.rs b/src/lib.rs index 521bc65d6..aa95c3786 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,10 @@ pub use rec_aggregation::{ merge_single_message_aggregates, split_multi_message_aggregate, verify_multi_message_aggregate, verify_single_message_aggregate, }; -pub use xmss::{MESSAGE_LEN_FE, XmssPublicKey, XmssSecretKey, XmssSignature, xmss_key_gen, xmss_sign, xmss_verify}; +pub use xmss::{ + MESSAGE_LEN_BYTES, PUB_KEY_SSZ_LEN, SIGNATURE_SSZ_LEN, XmssKeyGenError, XmssPublicKey, XmssSecretKey, + XmssSignature, XmssSignatureError, XmssVerifyError, xmss_key_gen, xmss_key_gen_from_seed, xmss_sign, xmss_verify, +}; pub type F = KoalaBear; diff --git a/tests/test_multisignatures.rs b/tests/test_multisignatures.rs index a3c5eee21..fc2fd32bb 100644 --- a/tests/test_multisignatures.rs +++ b/tests/test_multisignatures.rs @@ -24,15 +24,15 @@ fn serialize_arena_tests() -> MutexGuard<'static, ()> { #[test] fn test_xmss_signature() { - let start_slot = 111; - let end_slot = 200; + let activation_slot = 111; + let num_active_slots = 90; let slot: u32 = 124; let mut rng: StdRng = StdRng::seed_from_u64(0); - let message = rng.random(); + let message: [u8; 32] = rng.random(); - let (secret_key, pub_key) = xmss_key_gen(rng.random(), start_slot, end_slot, false).unwrap(); - let signature = xmss_sign(&mut rng, &secret_key, &message, slot).unwrap(); - xmss_verify(&pub_key, &message, &signature, slot).unwrap(); + let (pub_key, secret_key) = xmss_key_gen(&mut rng, activation_slot, num_active_slots).unwrap(); + let signature = xmss_sign(&secret_key, slot, &message).unwrap(); + xmss_verify(&pub_key, slot, &message, &signature).unwrap(); } #[test] @@ -95,14 +95,14 @@ fn test_multi_message_aggregation() { let slot_b = BENCHMARK_SLOT + 1; let mut rng_b: StdRng = StdRng::seed_from_u64(17); - let message_b: [_; 8] = std::array::from_fn(|_| rng_b.random()); + let message_b: [u8; 32] = rng_b.random(); assert!(message_b != message_a && slot_b != slot_a); let raws_b: Vec<_> = (0..2) .map(|_| { - let (sk, pk) = xmss_key_gen(rng_b.random(), slot_b, slot_b, false).unwrap(); - let sig = xmss_sign(&mut rng_b, &sk, &message_b, slot_b).unwrap(); + let (pk, sk) = xmss_key_gen(&mut rng_b, u64::from(slot_b), 1).unwrap(); + let sig = xmss_sign(&sk, slot_b, &message_b).unwrap(); (pk, sig) }) .collect();