From 3dc1efb405496a1aab51d27e2f12d1a00e08904e Mon Sep 17 00:00:00 2001 From: bomanaps Date: Fri, 7 Aug 2026 17:12:39 +0100 Subject: [PATCH 1/2] track leanVM main, add gossip arrival metrics --- lean_client/Cargo.lock | 714 +++++------------- lean_client/Cargo.toml | 6 +- lean_client/metrics/src/lib.rs | 6 +- lean_client/metrics/src/metrics.rs | 190 +++++ lean_client/networking/src/network/service.rs | 4 + lean_client/src/main.rs | 18 +- lean_client/xmss/Cargo.toml | 8 +- lean_client/xmss/src/aggregated_signature.rs | 63 +- lean_client/xmss/src/lib.rs | 6 +- lean_client/xmss/src/multi_message.rs | 43 +- lean_client/xmss/src/public_key.rs | 18 +- lean_client/xmss/src/secret_key.rs | 47 +- lean_client/xmss/src/signature.rs | 117 +-- 13 files changed, 527 insertions(+), 713 deletions(-) diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index fb127322..077f2b77 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "generic-array", ] @@ -46,6 +46,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "air" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "field", + "koala-bear", + "poly", +] + [[package]] name = "alloy-primitives" version = "1.5.7" @@ -70,7 +80,7 @@ dependencies = [ "ruint", "rustc-hash", "serde", - "sha3 0.10.8", + "sha3", ] [[package]] @@ -577,19 +587,18 @@ dependencies = [ [[package]] name = "backend" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "mt-air", - "mt-fiat-shamir", - "mt-field", - "mt-koala-bear", - "mt-poly", - "mt-sumcheck", - "mt-symetric", - "mt-utils", - "mt-whir", +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "air", + "fiat-shamir", + "field", + "koala-bear", "parallel", - "tracing", + "poly", + "sumcheck", + "symetric", + "utils", + "whir", "zk-alloc", ] @@ -693,15 +702,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" -dependencies = [ - "hybrid-array", -] - [[package]] name = "bls" version = "0.0.0" @@ -877,7 +877,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "inout", "zeroize", ] @@ -964,12 +964,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - [[package]] name = "const-str" version = "0.4.3" @@ -1019,7 +1013,7 @@ dependencies = [ "tracing", "try_from_iterator", "typenum", - "xmss", + "xmss 0.0.0", ] [[package]] @@ -1143,15 +1137,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" -dependencies = [ - "hybrid-array", -] - [[package]] name = "ctr" version = "0.9.2" @@ -1257,20 +1242,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "data-encoding" version = "2.10.0" @@ -1329,7 +1300,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid 0.9.6", + "const-oid", "zeroize", ] @@ -1412,23 +1383,12 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", - "const-oid 0.9.6", - "crypto-common 0.1.7", + "block-buffer", + "const-oid", + "crypto-common", "subtle", ] -[[package]] -name = "digest" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" -dependencies = [ - "block-buffer 0.12.0", - "const-oid 0.10.2", - "crypto-common 0.2.1", -] - [[package]] name = "discv5" version = "0.10.4" @@ -1602,7 +1562,7 @@ dependencies = [ "log", "rand 0.8.5", "serde", - "sha3 0.10.8", + "sha3", "zeroize", ] @@ -1832,6 +1792,30 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-shamir" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "field", + "koala-bear", + "parallel", + "serde", + "symetric", + "utils", +] + +[[package]] +name = "field" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "paste", + "rand 0.10.1", + "serde", + "utils", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1887,7 +1871,7 @@ dependencies = [ "ssz", "test-generator", "tracing", - "xmss", + "xmss 0.0.0", ] [[package]] @@ -2164,12 +2148,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashbrown" version = "0.15.5" @@ -2226,7 +2204,7 @@ dependencies = [ "hash32", "rustc_version 0.4.1", "serde", - "spin 0.9.8", + "spin", "stable_deref_trait", ] @@ -2385,7 +2363,7 @@ dependencies = [ "tower-http", "tracing", "validator", - "xmss", + "xmss 0.0.0", ] [[package]] @@ -2422,15 +2400,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hybrid-array" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" -dependencies = [ - "typenum", -] - [[package]] name = "hyper" version = "1.9.0" @@ -2900,16 +2869,6 @@ dependencies = [ "cpufeatures 0.2.17", ] -[[package]] -name = "keccak" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", -] - [[package]] name = "keccak-asm" version = "0.1.6" @@ -2920,12 +2879,40 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "koala-bear" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "field", + "paste", + "rand 0.10.1", + "serde", + "utils", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lean-multisig" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "backend", + "clap", + "lean_vm", + "rec_aggregation", + "serde_json", + "sub_protocols", + "system-info", + "xmss 0.1.0", + "zk-alloc", +] + [[package]] name = "lean_client" version = "0.1.0" @@ -2954,110 +2941,46 @@ dependencies = [ "tracing", "tracing-subscriber", "validator", - "xmss", + "xmss 0.0.0", ] [[package]] name = "lean_compiler" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "backend", "include_dir", "lean_vm", "pest", "pest_derive", - "rand 0.10.1", "sub_protocols", - "tracing", - "utils", + "xmss 0.1.0", ] [[package]] name = "lean_prover" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "backend", - "itertools 0.14.0", "lean_compiler", "lean_vm", - "pest", - "pest_derive", "rand 0.10.1", "serde", "sub_protocols", "tracing", - "utils", + "xmss 0.1.0", ] [[package]] name = "lean_vm" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "backend", - "itertools 0.14.0", - "leansig_wrapper", - "pest", - "pest_derive", - "rand 0.10.1", - "serde", "tracing", - "utils", -] - -[[package]] -name = "leansig" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanSig?branch=devnet4#15cbdd43ec8525aa43fea2f42cafc5ed366084ae" -dependencies = [ - "dashmap", - "ethereum_ssz", - "num-bigint", - "num-traits", - "p3-baby-bear", - "p3-field", - "p3-koala-bear", - "p3-symmetric", - "rand 0.10.1", - "rayon", - "serde", - "sha3 0.10.8", - "thiserror 2.0.18", -] - -[[package]] -name = "leansig_fast_keygen" -version = "0.1.0" -source = "git+https://github.com/TomWambsgans/leanSig?branch=devnet4-fast-keygen#0fa9e19b8946ef50a34f3d50d82918b98bcfa4a5" -dependencies = [ - "dashmap", - "ethereum_ssz", - "num-bigint", - "num-traits", - "p3-baby-bear", - "p3-field", - "p3-koala-bear", - "p3-symmetric", - "rand 0.10.1", - "rayon", - "serde", - "sha3 0.10.8", - "thiserror 2.0.18", -] - -[[package]] -name = "leansig_wrapper" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "backend", - "ethereum_ssz", - "leansig", - "leansig_fast_keygen", - "p3-field", - "rand 0.10.1", + "xmss 0.1.0", ] [[package]] @@ -3474,15 +3397,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lz4_flex" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a" -dependencies = [ - "twox-hash", -] - [[package]] name = "match-lookup" version = "0.1.2" @@ -3579,127 +3493,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" -[[package]] -name = "mt-air" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "mt-field", - "mt-poly", -] - -[[package]] -name = "mt-fiat-shamir" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "mt-field", - "mt-koala-bear", - "mt-symetric", - "mt-utils", - "parallel", - "serde", - "tracing", -] - -[[package]] -name = "mt-field" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "itertools 0.14.0", - "mt-utils", - "num-bigint", - "parallel", - "paste", - "rand 0.10.1", - "serde", - "tracing", -] - -[[package]] -name = "mt-koala-bear" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "itertools 0.14.0", - "mt-field", - "mt-utils", - "num-bigint", - "paste", - "rand 0.10.1", - "serde", - "tracing", -] - -[[package]] -name = "mt-poly" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "itertools 0.14.0", - "mt-field", - "mt-utils", - "parallel", - "rand 0.10.1", - "serde", - "system-info", - "zk-alloc", -] - -[[package]] -name = "mt-sumcheck" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "mt-air", - "mt-fiat-shamir", - "mt-field", - "mt-poly", - "parallel", - "tracing", - "zk-alloc", -] - -[[package]] -name = "mt-symetric" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "mt-field", - "mt-koala-bear", - "parallel", - "zk-alloc", -] - -[[package]] -name = "mt-utils" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "serde", -] - -[[package]] -name = "mt-whir" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" -dependencies = [ - "itertools 0.14.0", - "mt-fiat-shamir", - "mt-field", - "mt-koala-bear", - "mt-poly", - "mt-sumcheck", - "mt-symetric", - "mt-utils", - "parallel", - "rand 0.10.1", - "system-info", - "tracing", - "zk-alloc", -] - [[package]] name = "multiaddr" version = "0.18.2" @@ -3981,179 +3774,10 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "p3-baby-bear" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "p3-challenger", - "p3-field", - "p3-mds", - "p3-monty-31", - "p3-poseidon1", - "p3-poseidon2", - "p3-symmetric", - "rand 0.10.1", -] - -[[package]] -name = "p3-challenger" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "p3-field", - "p3-maybe-rayon", - "p3-monty-31", - "p3-symmetric", - "p3-util", - "tracing", -] - -[[package]] -name = "p3-dft" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "itertools 0.14.0", - "p3-field", - "p3-matrix", - "p3-maybe-rayon", - "p3-util", - "spin 0.12.0", - "tracing", -] - -[[package]] -name = "p3-field" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "itertools 0.14.0", - "num-bigint", - "p3-maybe-rayon", - "p3-util", - "paste", - "rand 0.10.1", - "serde", - "tracing", -] - -[[package]] -name = "p3-koala-bear" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "p3-challenger", - "p3-field", - "p3-mds", - "p3-monty-31", - "p3-poseidon1", - "p3-poseidon2", - "p3-symmetric", - "rand 0.10.1", -] - -[[package]] -name = "p3-matrix" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "itertools 0.14.0", - "p3-field", - "p3-maybe-rayon", - "p3-util", - "rand 0.10.1", - "serde", - "tracing", -] - -[[package]] -name = "p3-maybe-rayon" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" - -[[package]] -name = "p3-mds" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "p3-dft", - "p3-field", - "p3-symmetric", - "p3-util", - "rand 0.10.1", -] - -[[package]] -name = "p3-monty-31" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "itertools 0.14.0", - "num-bigint", - "p3-dft", - "p3-field", - "p3-matrix", - "p3-maybe-rayon", - "p3-mds", - "p3-poseidon1", - "p3-poseidon2", - "p3-symmetric", - "p3-util", - "paste", - "rand 0.10.1", - "serde", - "spin 0.12.0", - "tracing", -] - -[[package]] -name = "p3-poseidon1" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "p3-field", - "p3-mds", - "p3-symmetric", - "rand 0.10.1", -] - -[[package]] -name = "p3-poseidon2" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "p3-field", - "p3-mds", - "p3-symmetric", - "p3-util", - "rand 0.10.1", -] - -[[package]] -name = "p3-symmetric" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "itertools 0.14.0", - "p3-field", - "p3-util", - "serde", -] - -[[package]] -name = "p3-util" -version = "0.5.1" -source = "git+https://github.com/Plonky3/Plonky3.git#4e27646d9c7f650d7f2657f01f5e634463b09105" -dependencies = [ - "serde", - "transpose", -] - [[package]] name = "parallel" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "system-info", ] @@ -4355,6 +3979,21 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "poly" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "field", + "koala-bear", + "parallel", + "rand 0.10.1", + "serde", + "system-info", + "utils", + "zk-alloc", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -4881,25 +4520,21 @@ dependencies = [ [[package]] name = "rec_aggregation" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "backend", "include_dir", "lean_compiler", "lean_prover", "lean_vm", - "leansig_wrapper", - "lz4_flex", "objc2", "objc2-foundation", + "parallel", "postcard", - "rand 0.10.1", "serde", - "sha3 0.11.0", "sub_protocols", "tracing", - "utils", - "zk-alloc", + "xmss 0.1.0", ] [[package]] @@ -5489,17 +5124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest 0.10.7", - "keccak 0.1.6", -] - -[[package]] -name = "sha3" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" -dependencies = [ - "digest 0.11.2", - "keccak 0.2.0", + "keccak", ] [[package]] @@ -5624,7 +5249,7 @@ dependencies = [ "serde", "serde_json", "ssz", - "xmss", + "xmss 0.0.0", ] [[package]] @@ -5636,15 +5261,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spin" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1527984ca054dfca79333baec451042863f485fbee01b7bf6d911de915cac865" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" @@ -5722,12 +5338,6 @@ dependencies = [ "triomphe", ] -[[package]] -name = "strength_reduce" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" - [[package]] name = "strsim" version = "0.11.1" @@ -5781,12 +5391,11 @@ dependencies = [ [[package]] name = "sub_protocols" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "backend", "lean_vm", "tracing", - "utils", ] [[package]] @@ -5795,6 +5404,32 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "sumcheck" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "air", + "fiat-shamir", + "field", + "koala-bear", + "parallel", + "poly", + "tracing", + "zk-alloc", +] + +[[package]] +name = "symetric" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "field", + "koala-bear", + "parallel", + "zk-alloc", +] + [[package]] name = "syn" version = "0.15.44" @@ -5872,7 +5507,7 @@ dependencies = [ [[package]] name = "system-info" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "libc", ] @@ -6273,16 +5908,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "transpose" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" -dependencies = [ - "num-integer", - "strength_reduce", -] - [[package]] name = "triomphe" version = "0.1.15" @@ -6304,12 +5929,6 @@ name = "try_from_iterator" version = "0.0.0" source = "git+https://github.com/grandinetech/grandine?rev=c4b676e3daa0ddcb86d0bcb321b7166d59f920f9#c4b676e3daa0ddcb86d0bcb321b7166d59f920f9" -[[package]] -name = "twox-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" - [[package]] name = "typenum" version = "1.19.0" @@ -6419,7 +6038,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "subtle", ] @@ -6468,10 +6087,10 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utils" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ - "backend", - "tracing", + "parallel", + "serde", "tracing-forest", "tracing-subscriber", ] @@ -6506,7 +6125,7 @@ dependencies = [ "tracing", "try_from_iterator", "typenum", - "xmss", + "xmss 0.0.0", "zeroize", ] @@ -6693,6 +6312,25 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "whir" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "fiat-shamir", + "field", + "koala-bear", + "parallel", + "poly", + "rand 0.10.1", + "sumcheck", + "symetric", + "system-info", + "tracing", + "utils", + "zk-alloc", +] + [[package]] name = "widestring" version = "1.2.1" @@ -7085,23 +6723,31 @@ name = "xmss" version = "0.0.0" dependencies = [ "anyhow", - "backend", "bls", "derive_more", "ethereum-types", "ethereum_ssz", "hex", - "leansig", - "leansig_wrapper", + "lean-multisig", "metrics", + "postcard", "rand 0.10.1", "rand_chacha 0.10.0", - "rayon", - "rec_aggregation", "serde", "ssz", "typenum", - "zeroize", +] + +[[package]] +name = "xmss" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" +dependencies = [ + "backend", + "ethereum_ssz", + "postcard", + "rand 0.10.1", + "serde", ] [[package]] @@ -7287,7 +6933,7 @@ dependencies = [ [[package]] name = "zk-alloc" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanVM.git?rev=e2592df4e30fdddbbf8ae26a333116c68cec7026#e2592df4e30fdddbbf8ae26a333116c68cec7026" +source = "git+https://github.com/leanEthereum/leanVM.git?rev=a5909d18647de6aed38640c098d9177fab2bf36a#a5909d18647de6aed38640c098d9177fab2bf36a" dependencies = [ "libc", "parallel", diff --git a/lean_client/Cargo.toml b/lean_client/Cargo.toml index 53e70523..029190c4 100644 --- a/lean_client/Cargo.toml +++ b/lean_client/Cargo.toml @@ -253,10 +253,8 @@ indexmap = "2" http-body-util = "0.1" http_api_utils = { git = "https://github.com/grandinetech/grandine", rev = "c4b676e3daa0ddcb86d0bcb321b7166d59f920f9" } k256 = "0.13" -rec_aggregation = { git = "https://github.com/leanEthereum/leanVM.git", rev = "e2592df4e30fdddbbf8ae26a333116c68cec7026" } -backend = { git = "https://github.com/leanEthereum/leanVM.git", rev = "e2592df4e30fdddbbf8ae26a333116c68cec7026" } -leansig = { git = "https://github.com/leanEthereum/leanSig", branch = "devnet4" } -leansig_wrapper = { git = "https://github.com/leanEthereum/leanVM.git", rev = "e2592df4e30fdddbbf8ae26a333116c68cec7026" } +lean_multisig = { package = "lean-multisig", git = "https://github.com/leanEthereum/leanVM.git", rev = "a5909d18647de6aed38640c098d9177fab2bf36a" } +postcard = { version = "1.1.3", features = ["alloc"] } libp2p = { git = "https://github.com/libp2p/rust-libp2p.git", rev = "91e8931e275bcd1c72791d18b09fea8b77209baf", default-features = false, features = [ 'dns', 'gossipsub', diff --git a/lean_client/metrics/src/lib.rs b/lean_client/metrics/src/lib.rs index ab3d6cfe..ef32be49 100644 --- a/lean_client/metrics/src/lib.rs +++ b/lean_client/metrics/src/lib.rs @@ -3,5 +3,9 @@ mod metrics; mod server; pub use helpers::{set_gauge_u64, stop_and_discard, stop_and_record}; -pub use metrics::{DisconnectReason, METRICS, Metrics}; +pub use metrics::{ + DisconnectReason, METRICS, Metrics, observe_gossip_aggregation_arrival, + observe_gossip_attestation_arrival, observe_gossip_block_arrival, set_gossip_arrival_clock, + unix_now_ms, +}; pub use server::{MetricsServerConfig, run_server}; diff --git a/lean_client/metrics/src/metrics.rs b/lean_client/metrics/src/metrics.rs index 5d4afdf8..e777187e 100644 --- a/lean_client/metrics/src/metrics.rs +++ b/lean_client/metrics/src/metrics.rs @@ -266,6 +266,13 @@ pub struct Metrics { pub lean_gossip_block_size_bytes: Histogram, pub lean_gossip_attestation_size_bytes: Histogram, pub lean_gossip_aggregation_size_bytes: Histogram, + + pub lean_gossip_block_arrival_delay_seconds: Histogram, + pub lean_gossip_attestation_arrival_delay_seconds: Histogram, + pub lean_gossip_aggregation_arrival_delay_seconds: Histogram, + pub lean_gossip_block_arrival_total: IntCounterVec, + pub lean_gossip_attestation_arrival_total: IntCounterVec, + pub lean_gossip_aggregation_arrival_total: IntCounterVec, } impl Metrics { @@ -781,6 +788,54 @@ impl Metrics { 1_048_576.0, ] ))?, + + // Gossip Arrival Metrics + lean_gossip_block_arrival_delay_seconds: Histogram::with_opts(histogram_opts!( + "lean_gossip_block_arrival_delay_seconds", + "Absolute delay between a gossip block's arrival and the start of the interval \ + it was due in", + gossip_arrival_delay_buckets() + ))?, + lean_gossip_attestation_arrival_delay_seconds: Histogram::with_opts(histogram_opts!( + "lean_gossip_attestation_arrival_delay_seconds", + "Absolute delay between a gossip attestation's arrival and the start of the \ + interval it was due in", + gossip_arrival_delay_buckets() + ))?, + lean_gossip_aggregation_arrival_delay_seconds: Histogram::with_opts(histogram_opts!( + "lean_gossip_aggregation_arrival_delay_seconds", + "Absolute delay between an aggregate becoming available, whether received on \ + gossip or produced locally, and the most recent aggregation-interval boundary \ + at or before it. Local aggregation starts at that boundary, so a locally \ + produced aggregate reports its proving latency past it", + gossip_arrival_delay_buckets() + ))?, + lean_gossip_block_arrival_total: IntCounterVec::new( + opts!( + "lean_gossip_block_arrival_total", + "Gossip blocks by arrival position relative to the interval they were due in" + ), + &["position"], + )?, + lean_gossip_attestation_arrival_total: IntCounterVec::new( + opts!( + "lean_gossip_attestation_arrival_total", + "Gossip attestations by arrival position relative to the interval they were \ + due in" + ), + &["position"], + )?, + lean_gossip_aggregation_arrival_total: IntCounterVec::new( + opts!( + "lean_gossip_aggregation_arrival_total", + "Aggregates, received on gossip or produced locally, by arrival position \ + relative to the most recent aggregation-interval boundary. Anchored to the \ + latest such boundary rather than the aggregate's own data slot, so an \ + arrival can never precede it: only `inside` and `after` occur, never \ + `before`." + ), + &["position"], + )?, }) } @@ -1012,6 +1067,31 @@ impl Metrics { default_registry.register(Box::new(self.lean_gossip_attestation_size_bytes.clone()))?; default_registry.register(Box::new(self.lean_gossip_aggregation_size_bytes.clone()))?; + // Gossip Arrival Metrics + default_registry.register(Box::new( + self.lean_gossip_block_arrival_delay_seconds.clone(), + ))?; + default_registry.register(Box::new( + self.lean_gossip_attestation_arrival_delay_seconds.clone(), + ))?; + default_registry.register(Box::new( + self.lean_gossip_aggregation_arrival_delay_seconds.clone(), + ))?; + default_registry.register(Box::new(self.lean_gossip_block_arrival_total.clone()))?; + default_registry.register(Box::new(self.lean_gossip_attestation_arrival_total.clone()))?; + default_registry.register(Box::new(self.lean_gossip_aggregation_arrival_total.clone()))?; + + for position in ["before", "inside", "after"] { + self.lean_gossip_block_arrival_total + .with_label_values(&[position]); + self.lean_gossip_attestation_arrival_total + .with_label_values(&[position]); + } + for position in ["inside", "after"] { + self.lean_gossip_aggregation_arrival_total + .with_label_values(&[position]); + } + Ok(()) } @@ -1101,3 +1181,113 @@ pub enum DisconnectReason { LocalClose, Error, } + +fn gossip_arrival_delay_buckets() -> Vec { + vec![0.05, 0.1, 0.2, 0.4, 0.8, 1.2, 1.6, 2.4, 4.0, 8.0, 16.0] +} + +const BLOCK_INTERVAL_INDEX: u64 = 0; +const ATTESTATION_INTERVAL_INDEX: u64 = 1; +const AGGREGATION_INTERVAL_INDEX: u64 = 2; + +#[derive(Clone, Copy, Debug)] +struct GossipArrivalClock { + genesis_ms: u64, + millis_per_interval: u64, + millis_per_slot: u64, +} + +static GOSSIP_ARRIVAL_CLOCK: OnceCell = OnceCell::new(); + +pub fn set_gossip_arrival_clock( + genesis_ms: u64, + millis_per_interval: u64, + intervals_per_slot: u64, +) { + let _ = GOSSIP_ARRIVAL_CLOCK.set(GossipArrivalClock { + genesis_ms, + millis_per_interval, + millis_per_slot: millis_per_interval * intervals_per_slot, + }); +} + +pub fn unix_now_ms() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map(|d| d.as_millis() as u64) + .unwrap_or(0) +} + +fn interval_delta_ms( + clock: GossipArrivalClock, + arrival_ms: u64, + anchor_slot: u64, + interval_index: u64, +) -> i64 { + let expected_ms = clock.genesis_ms + + anchor_slot * clock.millis_per_slot + + interval_index * clock.millis_per_interval; + arrival_ms as i64 - expected_ms as i64 +} + +fn latest_interval_delta_ms( + clock: GossipArrivalClock, + arrival_ms: u64, + interval_index: u64, +) -> i64 { + let since_genesis = arrival_ms.saturating_sub(clock.genesis_ms) as i64; + let anchor_offset = (interval_index * clock.millis_per_interval) as i64; + (since_genesis - anchor_offset).rem_euclid(clock.millis_per_slot as i64) +} + +fn position_from_delta(clock: GossipArrivalClock, delta_ms: i64) -> &'static str { + if delta_ms < 0 { + "before" + } else if delta_ms < clock.millis_per_interval as i64 { + "inside" + } else { + "after" + } +} + +pub fn observe_gossip_block_arrival(arrival_ms: u64, block_slot: u64) { + let (Some(clock), Some(metrics)) = (GOSSIP_ARRIVAL_CLOCK.get(), METRICS.get()) else { + return; + }; + let delta_ms = interval_delta_ms(*clock, arrival_ms, block_slot, BLOCK_INTERVAL_INDEX); + metrics + .lean_gossip_block_arrival_delay_seconds + .observe(delta_ms.unsigned_abs() as f64 / 1000.0); + metrics + .lean_gossip_block_arrival_total + .with_label_values(&[position_from_delta(*clock, delta_ms)]) + .inc(); +} + +pub fn observe_gossip_attestation_arrival(arrival_ms: u64, data_slot: u64) { + let (Some(clock), Some(metrics)) = (GOSSIP_ARRIVAL_CLOCK.get(), METRICS.get()) else { + return; + }; + let delta_ms = interval_delta_ms(*clock, arrival_ms, data_slot, ATTESTATION_INTERVAL_INDEX); + metrics + .lean_gossip_attestation_arrival_delay_seconds + .observe(delta_ms.unsigned_abs() as f64 / 1000.0); + metrics + .lean_gossip_attestation_arrival_total + .with_label_values(&[position_from_delta(*clock, delta_ms)]) + .inc(); +} + +pub fn observe_gossip_aggregation_arrival(arrival_ms: u64) { + let (Some(clock), Some(metrics)) = (GOSSIP_ARRIVAL_CLOCK.get(), METRICS.get()) else { + return; + }; + let delta_ms = latest_interval_delta_ms(*clock, arrival_ms, AGGREGATION_INTERVAL_INDEX); + metrics + .lean_gossip_aggregation_arrival_delay_seconds + .observe(delta_ms.unsigned_abs() as f64 / 1000.0); + metrics + .lean_gossip_aggregation_arrival_total + .with_label_values(&[position_from_delta(*clock, delta_ms)]) + .inc(); +} diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index c3cd3fde..a96e40b8 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -639,12 +639,14 @@ where .. } => { let data_len = message.data.len(); + let arrival_ms = metrics::unix_now_ms(); match GossipsubMessage::decode(&message.topic, &message.data) { Ok(GossipsubMessage::Block(signed_block)) => { METRICS .get() .map(|m| m.lean_gossip_block_size_bytes.observe(data_len as f64)); let slot = signed_block.block.slot.0; + metrics::observe_gossip_block_arrival(arrival_ms, slot); info!(slot, block_root = %signed_block.block.hash_tree_root(), "received block via gossip"); if let Err(err) = self @@ -677,6 +679,7 @@ where "received attestation via subnet gossip" ); let slot = attestation.message.slot.0; + metrics::observe_gossip_attestation_arrival(arrival_ms, slot); if let Err(err) = self .chain_message_sink @@ -702,6 +705,7 @@ where "received aggregated attestation via gossip" ); let slot = signed_aggregated_attestation.data.slot.0; + metrics::observe_gossip_aggregation_arrival(arrival_ms); if let Err(err) = self .chain_message_sink diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 82d12ba2..4055511e 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -430,6 +430,13 @@ struct Args { #[arg(long, default_value_t = false)] enable_proposer_aggregation: bool, + /// Run the leanVM prover on its bump arena: faster proving, but the arena + /// never returns pages to the OS, so RSS ratchets to the allocation + /// high-water mark for the process lifetime. Off by default (system + /// allocator: slower proving, bounded memory). + #[arg(long, default_value_t = false)] + prover_arena: bool, + #[cfg(shadow_mode)] #[command(flatten)] shadow: ShadowOptions, @@ -461,10 +468,6 @@ struct ShadowOptions { #[cfg_attr(shadow_mode, tokio::main(flavor = "current_thread"))] #[cfg_attr(not(shadow_mode), tokio::main)] async fn main() -> Result<()> { - let rayon_threads = num_cpus::get().saturating_sub(3).max(1); - xmss::configure_rayon_pool(rayon_threads); - xmss::setup_aggregation(); - tracing_subscriber::fmt() .with_ansi(std::io::stdout().is_terminal()) .with_env_filter( @@ -482,6 +485,9 @@ async fn main() -> Result<()> { let args = Args::parse(); + xmss::set_prover_arena(args.prover_arena); + xmss::setup_aggregation(); + #[cfg(shadow_mode)] { let s = &args.shadow; @@ -642,6 +648,8 @@ async fn main() -> Result<()> { let config = Config { genesis_time }; + metrics::set_gossip_arrival_clock(genesis_time * 1000, MILLIS_PER_INTERVAL, INTERVALS_PER_SLOT); + // ── Anchor state: download checkpoint or use genesis ───────────────────────────────────── // For checkpoint sync: state is downloaded now; the anchor block is fetched from the // network after the network service starts. `checkpoint_block_root` holds the expected @@ -1583,6 +1591,7 @@ async fn main() -> Result<()> { aggregator.as_mut().unwrap().recv().await }, if aggregator.is_some() => { if let Some((aggregations, consumed_data_roots)) = maybe_agg { + let arrival_ms = metrics::unix_now_ms(); let mut to_publish = Vec::with_capacity(aggregations.len()); { let mut s = store.write(); @@ -1603,6 +1612,7 @@ async fn main() -> Result<()> { }); } for aggregation in to_publish { + metrics::observe_gossip_aggregation_arrival(arrival_ms); if let Err(e) = chain_outbound_sender.send( OutboundP2pRequest::GossipAggregation(aggregation) ) { diff --git a/lean_client/xmss/Cargo.toml b/lean_client/xmss/Cargo.toml index 4c941a4a..3663422d 100644 --- a/lean_client/xmss/Cargo.toml +++ b/lean_client/xmss/Cargo.toml @@ -12,17 +12,13 @@ derive_more = { workspace = true } eth_ssz = { workspace = true } ethereum-types = { workspace = true } hex = { workspace = true } -rec_aggregation = { workspace = true } -backend = { workspace = true } -leansig = { workspace = true } -leansig_wrapper = { workspace = true } +lean_multisig = { workspace = true } metrics = { workspace = true } +postcard = { workspace = true } rand = { workspace = true } -rayon = { workspace = true } ssz = { workspace = true } typenum = { workspace = true } serde = { workspace = true } -zeroize = { workspace = true, features = ["derive"] } [dev-dependencies] rand_chacha = { workspace = true } \ No newline at end of file diff --git a/lean_client/xmss/src/aggregated_signature.rs b/lean_client/xmss/src/aggregated_signature.rs index c462e378..6564ce85 100644 --- a/lean_client/xmss/src/aggregated_signature.rs +++ b/lean_client/xmss/src/aggregated_signature.rs @@ -1,15 +1,20 @@ use core::fmt::{self, Display}; -use std::{str::FromStr, sync::Once}; +use std::{ + str::FromStr, + sync::{ + Mutex, MutexGuard, Once, + atomic::{AtomicBool, Ordering}, + }, +}; use crate::{PublicKey, Signature}; use anyhow::{Context, Error, Result, anyhow, bail}; use ethereum_types::H256; -use leansig_wrapper::XmssPublicKey; -use metrics::{METRICS, stop_and_discard}; -use rec_aggregation::{ - SingleMessageAggregateSignature, aggregate_single_message_signatures, - init_aggregation_bytecode, verify_single_message_aggregate, +use lean_multisig::{ + SingleMessageAggregateSignature, XmssPublicKey, aggregate_single_message_signatures, + verify_single_message_aggregate, }; +use metrics::{METRICS, stop_and_discard}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; use ssz::{ByteList, ReadError, Size, SszHash, SszRead, SszSize, SszWrite, U1, WriteError}; use typenum::U524288; @@ -19,7 +24,7 @@ type AggregatedSignatureSizeLimit = U524288; /// Cryptographic proof that a set of validators signed a message. /// -/// Wire form: the lean-multisig `compress_without_pubkeys()` output. Pubkeys +/// Wire form: the lean-multisig `to_bytes_without_pubkeys()` output. Pubkeys /// are not baked into the bytes — verifiers must supply them externally, /// matching `SingleMessageAggregate.proof: ByteList512KiB` in leanSpec /// (`forks/lstar/containers/aggregation.py`) and zeam's `xmss_verify_type_1` @@ -52,22 +57,36 @@ impl SszHash for AggregatedSignature { } } +static USE_ARENA: AtomicBool = AtomicBool::new(false); + +/// Select the prover allocator before the first crypto operation. The leanVM arena +/// is faster but never returns pages to the OS, so RSS ratchets to the allocation +/// high-water mark; the system allocator (default) trades throughput for bounded +/// memory. No effect once `setup_aggregation` has run. +pub fn set_prover_arena(enable: bool) { + USE_ARENA.store(enable, Ordering::Relaxed); +} + pub fn setup_aggregation() { static SETUP: Once = Once::new(); SETUP.call_once(|| { - init_aggregation_bytecode(); - backend::precompute_dft_twiddles::(1 << 24); + if USE_ARENA.load(Ordering::Relaxed) { + lean_multisig::setup_prover(); + } else { + lean_multisig::setup_prover_without_arena(); + } }); } -/// Cap the global rayon pool so the leansig prover doesn't oversubscribe physical -/// cores. Idempotent; safe to call once at process startup before any aggregation. -pub fn configure_rayon_pool(num_threads: usize) { - drop( - rayon::ThreadPoolBuilder::new() - .num_threads(num_threads) - .build_global(), - ); +/// Claims the exclusive right to prove. leanVM allows one proof at a time per +/// process; a second concurrent one panics. Verification needs no permit. The +/// permit guards no data, so a poisoned lock is recovered rather than propagated: +/// one panicking prover must not brick every later proof. +pub(crate) fn acquire_prover() -> MutexGuard<'static, ()> { + static PROVER_PERMIT: Mutex<()> = Mutex::new(()); + PROVER_PERMIT + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) } impl AggregatedSignature { @@ -159,6 +178,8 @@ impl AggregatedSignature { .map(|(pks, agg)| agg.as_lean(sorted_dedup_lean_pubkeys(pks))) .collect::>>()?; + let _permit = acquire_prover(); + let agg = aggregate_single_message_signatures( &children_arg, raw_xmss, @@ -173,7 +194,7 @@ impl AggregatedSignature { .inc_by(sig_count as u64) }); - let bytes = agg.compress_without_pubkeys(); + let bytes = agg.to_bytes_without_pubkeys(); Ok(Self(ByteList::try_from(bytes).context( "aggregated proof too large - exceeds 512 KiB cap", )?)) @@ -211,10 +232,10 @@ impl AggregatedSignature { let agg = self.as_lean(expected_pubkeys)?; - if agg.info.without_pubkeys.message != *message.as_fixed_bytes() { + if agg.info.core.message != *message.as_fixed_bytes() { bail!("aggregated signature bound to a different message than expected"); } - if agg.info.without_pubkeys.slot != slot { + if agg.info.core.slot != slot { bail!("aggregated signature bound to a different slot than expected"); } @@ -243,7 +264,7 @@ impl AggregatedSignature { &self, pubkeys: Vec, ) -> Result { - SingleMessageAggregateSignature::decompress_without_pubkeys(self.0.as_bytes(), pubkeys) + SingleMessageAggregateSignature::from_bytes_without_pubkeys(self.0.as_bytes(), pubkeys) .ok_or_else(|| anyhow!("invalid aggregated XMSS signature")) } diff --git a/lean_client/xmss/src/lib.rs b/lean_client/xmss/src/lib.rs index a35c1285..06dda6e6 100644 --- a/lean_client/xmss/src/lib.rs +++ b/lean_client/xmss/src/lib.rs @@ -1,3 +1,7 @@ +// Enables the `bls/blst` backend feature for this crate's dependency subgraph +// (grandine `types` needs a backend selected even in solo `-p xmss` builds). +use bls as _; + mod aggregated_signature; mod multi_message; mod public_key; @@ -7,7 +11,7 @@ mod signature; #[cfg(shadow_mode)] pub mod shadow_cost; -pub use aggregated_signature::{AggregatedSignature, configure_rayon_pool, setup_aggregation}; +pub use aggregated_signature::{AggregatedSignature, set_prover_arena, setup_aggregation}; pub use multi_message::MultiMessageAggregate; pub use public_key::PublicKey; pub use secret_key::SecretKey; diff --git a/lean_client/xmss/src/multi_message.rs b/lean_client/xmss/src/multi_message.rs index aeaef5c5..bd537ef7 100644 --- a/lean_client/xmss/src/multi_message.rs +++ b/lean_client/xmss/src/multi_message.rs @@ -3,16 +3,18 @@ use std::str::FromStr; use anyhow::{Context, Error, Result, anyhow, bail}; use ethereum_types::H256; -use leansig_wrapper::XmssPublicKey; -use rec_aggregation::{ - MultiMessageAggregateSignature, merge_single_message_aggregates, - split_multi_message_aggregate_by_message, verify_multi_message_aggregate, +use lean_multisig::{ + MultiMessageAggregateSignature, XmssPublicKey, merge_single_message_aggregates, + split_multi_message_aggregate, verify_multi_message_aggregate, }; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; use ssz::{ByteList, Ssz}; use typenum::U524288; -use crate::{AggregatedSignature, PublicKey, aggregated_signature::setup_aggregation}; +use crate::{ + AggregatedSignature, PublicKey, + aggregated_signature::{acquire_prover, setup_aggregation}, +}; type MultiMessageAggregateSizeLimit = U524288; @@ -64,8 +66,10 @@ impl MultiMessageAggregate { }) .collect::>>()?; + let _permit = acquire_prover(); + let merged = merge_single_message_aggregates(parts_lean, log_inv_rate)?; - let bytes = merged.compress_without_pubkeys(); + let bytes = merged.to_bytes_without_pubkeys(); Self::new(&bytes) } @@ -91,7 +95,7 @@ impl MultiMessageAggregate { let pubkeys_per_info = sorted_dedup_lean_pubkeys(pubkeys_per_message); - let sig = MultiMessageAggregateSignature::decompress_without_pubkeys( + let sig = MultiMessageAggregateSignature::from_bytes_without_pubkeys( self.proof.as_bytes(), pubkeys_per_info, ) @@ -105,10 +109,10 @@ impl MultiMessageAggregate { ); } for (i, (expected_message, expected_slot)) in messages.iter().enumerate() { - if sig.info[i].without_pubkeys.message != *expected_message.as_fixed_bytes() { + if sig.info[i].core.message != *expected_message.as_fixed_bytes() { bail!("component {i} bound to a different message than expected"); } - if sig.info[i].without_pubkeys.slot != *expected_slot { + if sig.info[i].core.slot != *expected_slot { bail!("component {i} bound to a different slot than expected"); } } @@ -138,15 +142,28 @@ impl MultiMessageAggregate { } let pubkeys_per_info = sorted_dedup_lean_pubkeys(pubkeys_per_message); - let sig = MultiMessageAggregateSignature::decompress_without_pubkeys( + let sig = MultiMessageAggregateSignature::from_bytes_without_pubkeys( self.proof.as_bytes(), pubkeys_per_info, ) .ok_or_else(|| anyhow!("invalid multi-message aggregate bytes"))?; - let recovered = - split_multi_message_aggregate_by_message(sig, *message.as_fixed_bytes(), log_inv_rate)?; - let bytes = recovered.compress_without_pubkeys(); + let matches: Vec = sig + .info + .iter() + .enumerate() + .filter_map(|(i, info)| (info.core.message == *message.as_fixed_bytes()).then_some(i)) + .collect(); + let index = match matches.as_slice() { + [i] => *i, + [] => bail!("split-by-message target not found in multi-message components"), + _ => bail!("split-by-message target matched multiple components"), + }; + + let _permit = acquire_prover(); + + let recovered = split_multi_message_aggregate(sig, index, log_inv_rate)?; + let bytes = recovered.to_bytes_without_pubkeys(); AggregatedSignature::new(&bytes) } } diff --git a/lean_client/xmss/src/public_key.rs b/lean_client/xmss/src/public_key.rs index 3999174b..ce444bcc 100644 --- a/lean_client/xmss/src/public_key.rs +++ b/lean_client/xmss/src/public_key.rs @@ -5,16 +5,16 @@ use core::{ }; use anyhow::{Error, anyhow}; -use eth_ssz::DecodeError; -use leansig_wrapper::{XmssPublicKey, xmss_public_key_from_ssz, xmss_public_key_to_ssz}; +use eth_ssz::{Decode as _, DecodeError, Encode as _}; +use lean_multisig::XmssPublicKey; use serde::{ Deserialize, Serialize, de::{self, Visitor}, }; use ssz::{BytesToDepth, MerkleTree, SszHash, SszRead, SszSize, SszWrite}; -use typenum::{U1, U52, Unsigned}; +use typenum::{U1, U32, Unsigned}; -type PublicKeySize = U52; +type PublicKeySize = U32; type LeanSigPublicKey = XmssPublicKey; @@ -61,26 +61,26 @@ impl SszHash for PublicKey { impl PublicKey { pub fn new(bytes: &[u8]) -> Result { - xmss_public_key_from_ssz(bytes) + XmssPublicKey::from_ssz_bytes(bytes) .map_err(|_| DecodeError::BytesInvalid("invalid xmss public key".to_string()))?; Ok(Self(bytes.try_into().expect( - "slice of length != 52 shouldn't deserialize as valid leansig public key", + "slice of length != 32 shouldn't deserialize as valid xmss public key", ))) } pub(crate) fn from_lean(key: LeanSigPublicKey) -> Self { - let bytes = xmss_public_key_to_ssz(&key); + let bytes = key.as_ssz_bytes(); Self( bytes .as_slice() .try_into() - .expect("slice of length != 52 shouldn't deserialize as valid leansig public key"), + .expect("slice of length != 32 shouldn't deserialize as valid xmss public key"), ) } pub(crate) fn as_lean(&self) -> LeanSigPublicKey { - xmss_public_key_from_ssz(&self.0).expect("PublicKey was instantiated incorrectly") + XmssPublicKey::from_ssz_bytes(&self.0).expect("PublicKey was instantiated incorrectly") } } diff --git a/lean_client/xmss/src/secret_key.rs b/lean_client/xmss/src/secret_key.rs index f8cb3c49..a26b9dc9 100644 --- a/lean_client/xmss/src/secret_key.rs +++ b/lean_client/xmss/src/secret_key.rs @@ -1,13 +1,6 @@ -use std::sync::Mutex; - use anyhow::{Error, Result, anyhow}; use derive_more::Debug; -use leansig::serialization::Serializable; -use leansig::signature::generalized_xmss::instantiations_aborting::lifetime_2_to_the_32::{ - SIGAbortingTargetSumLifetime32Dim46Base8 as XmssScheme, - SecretKeyAbortingTargetSumLifetime32Dim46Base8 as XmssSecretKey, -}; -use leansig::signature::{SignatureScheme, SignatureSchemeSecretKey}; +use lean_multisig::{XmssSecretKey, xmss_key_gen, xmss_sign}; use rand::CryptoRng; use ssz::H256; @@ -15,36 +8,20 @@ use crate::{PublicKey, Signature}; // TODO(zeroize): upstream `XmssSecretKey` does not derive `Zeroize`, so we cannot // derive `ZeroizeOnDrop` on the wrapper. Acceptable for devnet bring-up; before -// mainnet, either upstream a `Zeroize` derive on `GeneralizedXMSSSecretKey` or -// implement `Drop` here manually (zeroize the inner buffers via accessor). +// mainnet, either upstream a `Zeroize` derive on `XmssSecretKey` or implement +// `Drop` here manually (zeroize the inner buffers via accessor). #[derive(Debug)] #[debug("[REDACTED]")] -pub struct SecretKey(Mutex); +pub struct SecretKey(XmssSecretKey); impl SecretKey { pub fn sign(&self, message: H256, epoch: u32) -> Result { - let mut sk = self - .0 - .lock() - .map_err(|_| anyhow!("failed to acquire secret key lock"))?; - let target = epoch as u64; - - if !sk.get_activation_interval().contains(&target) { + if !self.0.activation_slots().contains(&epoch) { return Err(anyhow!("epoch {epoch} outside key activation window")); } - while !sk.get_prepared_interval().contains(&target) { - let before = sk.get_prepared_interval(); - sk.advance_preparation(); - if sk.get_prepared_interval() == before { - return Err(anyhow!( - "advance_preparation made no progress for epoch {epoch}" - )); - } - } - - let sig = XmssScheme::sign(&sk, epoch, message.as_fixed_bytes()) - .map_err(|_| anyhow!("failed to sign message"))?; + let sig = xmss_sign(&self.0, epoch, message.as_fixed_bytes()) + .map_err(|err| anyhow!("failed to sign message: {err:?}"))?; Ok(Signature::from_lean(sig)) } @@ -53,9 +30,9 @@ impl SecretKey { activation_epoch: u32, num_active_epochs: u32, ) -> (PublicKey, SecretKey) { - let (pk, sk) = - XmssScheme::key_gen(rng, activation_epoch as usize, num_active_epochs as usize); - (PublicKey::from_lean(pk), SecretKey(Mutex::new(sk))) + let (pk, sk) = xmss_key_gen(rng, activation_epoch as u64, num_active_epochs as u64) + .expect("activation range must fit within the 2^32 key lifetime"); + (PublicKey::from_lean(pk), SecretKey(sk)) } } @@ -63,8 +40,8 @@ impl TryFrom<&[u8]> for SecretKey { type Error = Error; fn try_from(value: &[u8]) -> Result { - let sk = XmssSecretKey::from_bytes(value) + let sk = postcard::from_bytes::(value) .map_err(|_| anyhow!("value is not valid secret key"))?; - Ok(Self(Mutex::new(sk))) + Ok(Self(sk)) } } diff --git a/lean_client/xmss/src/signature.rs b/lean_client/xmss/src/signature.rs index 13611dfc..efc7176a 100644 --- a/lean_client/xmss/src/signature.rs +++ b/lean_client/xmss/src/signature.rs @@ -6,16 +6,16 @@ use core::{ use crate::public_key::PublicKey; use anyhow::{Error, Result, anyhow}; -use eth_ssz::DecodeError; -use leansig_wrapper::{XmssSignature, xmss_signature_from_ssz, xmss_signature_to_ssz, xmss_verify}; +use eth_ssz::{Decode as _, DecodeError, Encode as _}; +use lean_multisig::{XmssSignature, xmss_verify}; use metrics::METRICS; -use serde::de; +use serde::de::{self, Visitor}; use serde::{Deserialize, Serialize}; use ssz::{ByteVector, H256, Ssz}; -use typenum::{Sum, U8, U32, U64, U128, U256, U2048}; +use typenum::{Sum, U8, U16, U32, U128, U1024}; -// 2536 = 2048 + 256 + 128 + 64 + 32 + 8 -type SignatureSize = Sum, U128>, U64>, U32>, U8>; +// 1208 = 1024 + 128 + 32 + 16 + 8 +type SignatureSize = Sum, U32>, U16>, U8>; type LeanSigSignature = XmssSignature; @@ -26,11 +26,11 @@ pub struct Signature(ByteVector); impl Signature { pub fn new(inner: &[u8]) -> Result { - xmss_signature_from_ssz(inner) + XmssSignature::from_ssz_bytes(inner) .map_err(|_| DecodeError::BytesInvalid("invalid xmss signature".to_string()))?; Ok(Self(inner.try_into().expect( - "slice of length != 2536 shouldn't deserialize as valid leansig signature", + "slice of length != 1208 shouldn't deserialize as valid xmss signature", ))) } @@ -47,31 +47,31 @@ impl Signature { }); Ok(()) } - Err(()) => { + Err(err) => { METRICS.get().map(|metrics| { metrics .lean_pq_sig_attestation_signatures_invalid_total .inc(); }); - Err(anyhow!("invalid signature")) + Err(anyhow!("invalid signature: {err:?}")) } } } pub(crate) fn from_lean(signature: LeanSigSignature) -> Self { - let bytes = xmss_signature_to_ssz(&signature); + let bytes = signature.as_ssz_bytes(); Self( bytes .as_slice() .try_into() - .expect("slice of length != 2536 shouldn't deserialize as valid leansig signature"), + .expect("slice of length != 1208 shouldn't deserialize as valid xmss signature"), ) } pub(crate) fn as_lean(&self) -> LeanSigSignature { - xmss_signature_from_ssz(self.0.as_bytes()) - .expect("signature internal representation must be valid leansig signature") + XmssSignature::from_ssz_bytes(self.0.as_bytes()) + .expect("signature internal representation must be valid xmss signature") } } @@ -121,84 +121,31 @@ impl<'de> Deserialize<'de> for Signature { where D: serde::Deserializer<'de>, { - #[derive(Deserialize)] - struct DataWrapper { - data: T, - } + struct SignatureVisitor; - #[derive(Deserialize)] - struct XmssSignatureJson { - path: XmssPath, - rho: DataWrapper>, - hashes: DataWrapper>>>, - } + impl Visitor<'_> for SignatureVisitor { + type Value = Signature; - #[derive(Deserialize)] - struct XmssPath { - siblings: DataWrapper>>>, - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "hex-encoded xmss signature") + } - let xmss_sig = XmssSignatureJson::deserialize(deserializer)?; - let mut rho_bytes = Vec::new(); - for val in &xmss_sig.rho.data { - rho_bytes.extend_from_slice(&val.to_le_bytes()); - } - let rho_len = rho_bytes.len(); // Should be 28 (7 * 4) - - // 2. Serialize Path/Siblings (Variable length) - let mut path_bytes = Vec::new(); - // Prepend 4 bytes (containing 4) as an offset which would come with real SSZ serialization - let inner_offset: u32 = 4; - path_bytes.extend_from_slice(&inner_offset.to_le_bytes()); // [04 00 00 00] - for sibling in &xmss_sig.path.siblings.data { - for val in &sibling.data { - path_bytes.extend_from_slice(&val.to_le_bytes()); + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + v.parse().map_err(de::Error::custom) } - } - // 3. Serialize Hashes (Variable length) - let mut hashes_bytes = Vec::new(); - for hash in &xmss_sig.hashes.data { - for val in &hash.data { - hashes_bytes.extend_from_slice(&val.to_le_bytes()); + fn visit_string(self, v: String) -> Result + where + E: de::Error, + { + self.visit_str(&v) } } - // --- STEP 2: CALCULATE OFFSETS --- - - // The fixed part contains: - // 1. Path Offset (4 bytes) - // 2. Rho Data (rho_len bytes) - // 3. Hashes Offset (4 bytes) - let fixed_part_size = 4 + rho_len + 4; - - // Offset to 'path' starts immediately after the fixed part - let offset_path = fixed_part_size as u32; - - // Offset to 'hashes' starts after 'path' data - let offset_hashes = offset_path + (path_bytes.len() as u32); - - // --- STEP 3: CONSTRUCT FINAL SSZ BYTES --- - - let mut ssz_bytes = Vec::new(); - - // 1. Write Offset to Path (u32, Little Endian) - ssz_bytes.extend_from_slice(&offset_path.to_le_bytes()); - - // 2. Write Rho Data (Fixed) - ssz_bytes.extend_from_slice(&rho_bytes); - - // 3. Write Offset to Hashes (u32, Little Endian) - ssz_bytes.extend_from_slice(&offset_hashes.to_le_bytes()); - - // 4. Write Path Data (Variable) - ssz_bytes.extend_from_slice(&path_bytes); - - // 5. Write Hashes Data (Variable) - ssz_bytes.extend_from_slice(&hashes_bytes); - - Signature::try_from(ssz_bytes.as_slice()) - .map_err(|err| de::Error::custom(format!("invalid signature: {err:?}"))) + deserializer.deserialize_str(SignatureVisitor) } } @@ -209,6 +156,6 @@ mod test { #[test] fn valid_signature_size() { - assert_eq!(SignatureSize::U64, 2536); + assert_eq!(SignatureSize::U64, 1208); } } From e53c4226da9b751ef697c7a3cc81a34ddc8647b1 Mon Sep 17 00:00:00 2001 From: bomanaps Date: Sat, 15 Aug 2026 01:22:05 +0100 Subject: [PATCH 2/2] address review comment on dep naming, seqCst and permit --- lean_client/Cargo.toml | 2 +- lean_client/xmss/Cargo.toml | 2 +- lean_client/xmss/src/aggregated_signature.rs | 21 +++++++------------- lean_client/xmss/src/multi_message.rs | 6 +++--- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/lean_client/Cargo.toml b/lean_client/Cargo.toml index 029190c4..ef2e7312 100644 --- a/lean_client/Cargo.toml +++ b/lean_client/Cargo.toml @@ -253,7 +253,7 @@ indexmap = "2" http-body-util = "0.1" http_api_utils = { git = "https://github.com/grandinetech/grandine", rev = "c4b676e3daa0ddcb86d0bcb321b7166d59f920f9" } k256 = "0.13" -lean_multisig = { package = "lean-multisig", git = "https://github.com/leanEthereum/leanVM.git", rev = "a5909d18647de6aed38640c098d9177fab2bf36a" } +lean-multisig = { git = "https://github.com/leanEthereum/leanVM.git", rev = "a5909d18647de6aed38640c098d9177fab2bf36a" } postcard = { version = "1.1.3", features = ["alloc"] } libp2p = { git = "https://github.com/libp2p/rust-libp2p.git", rev = "91e8931e275bcd1c72791d18b09fea8b77209baf", default-features = false, features = [ 'dns', diff --git a/lean_client/xmss/Cargo.toml b/lean_client/xmss/Cargo.toml index 3663422d..65dfda08 100644 --- a/lean_client/xmss/Cargo.toml +++ b/lean_client/xmss/Cargo.toml @@ -12,7 +12,7 @@ derive_more = { workspace = true } eth_ssz = { workspace = true } ethereum-types = { workspace = true } hex = { workspace = true } -lean_multisig = { workspace = true } +lean-multisig = { workspace = true } metrics = { workspace = true } postcard = { workspace = true } rand = { workspace = true } diff --git a/lean_client/xmss/src/aggregated_signature.rs b/lean_client/xmss/src/aggregated_signature.rs index 6564ce85..3871aa9d 100644 --- a/lean_client/xmss/src/aggregated_signature.rs +++ b/lean_client/xmss/src/aggregated_signature.rs @@ -2,7 +2,7 @@ use core::fmt::{self, Display}; use std::{ str::FromStr, sync::{ - Mutex, MutexGuard, Once, + Mutex, Once, atomic::{AtomicBool, Ordering}, }, }; @@ -64,13 +64,13 @@ static USE_ARENA: AtomicBool = AtomicBool::new(false); /// high-water mark; the system allocator (default) trades throughput for bounded /// memory. No effect once `setup_aggregation` has run. pub fn set_prover_arena(enable: bool) { - USE_ARENA.store(enable, Ordering::Relaxed); + USE_ARENA.store(enable, Ordering::SeqCst); } pub fn setup_aggregation() { static SETUP: Once = Once::new(); SETUP.call_once(|| { - if USE_ARENA.load(Ordering::Relaxed) { + if USE_ARENA.load(Ordering::SeqCst) { lean_multisig::setup_prover(); } else { lean_multisig::setup_prover_without_arena(); @@ -78,16 +78,9 @@ pub fn setup_aggregation() { }); } -/// Claims the exclusive right to prove. leanVM allows one proof at a time per -/// process; a second concurrent one panics. Verification needs no permit. The -/// permit guards no data, so a poisoned lock is recovered rather than propagated: -/// one panicking prover must not brick every later proof. -pub(crate) fn acquire_prover() -> MutexGuard<'static, ()> { - static PROVER_PERMIT: Mutex<()> = Mutex::new(()); - PROVER_PERMIT - .lock() - .unwrap_or_else(|poisoned| poisoned.into_inner()) -} +/// leanVM allows one proof at a time per process; a second concurrent one +/// panics. Hold this lock across every prove call; verification needs no permit. +pub(crate) static PROVER_PERMIT: Mutex<()> = Mutex::new(()); impl AggregatedSignature { pub fn new(bytes: &[u8]) -> Result { @@ -178,7 +171,7 @@ impl AggregatedSignature { .map(|(pks, agg)| agg.as_lean(sorted_dedup_lean_pubkeys(pks))) .collect::>>()?; - let _permit = acquire_prover(); + let _permit = PROVER_PERMIT.lock().unwrap(); let agg = aggregate_single_message_signatures( &children_arg, diff --git a/lean_client/xmss/src/multi_message.rs b/lean_client/xmss/src/multi_message.rs index bd537ef7..c71323fd 100644 --- a/lean_client/xmss/src/multi_message.rs +++ b/lean_client/xmss/src/multi_message.rs @@ -13,7 +13,7 @@ use typenum::U524288; use crate::{ AggregatedSignature, PublicKey, - aggregated_signature::{acquire_prover, setup_aggregation}, + aggregated_signature::{PROVER_PERMIT, setup_aggregation}, }; type MultiMessageAggregateSizeLimit = U524288; @@ -66,7 +66,7 @@ impl MultiMessageAggregate { }) .collect::>>()?; - let _permit = acquire_prover(); + let _permit = PROVER_PERMIT.lock().unwrap(); let merged = merge_single_message_aggregates(parts_lean, log_inv_rate)?; let bytes = merged.to_bytes_without_pubkeys(); @@ -160,7 +160,7 @@ impl MultiMessageAggregate { _ => bail!("split-by-message target matched multiple components"), }; - let _permit = acquire_prover(); + let _permit = PROVER_PERMIT.lock().unwrap(); let recovered = split_multi_message_aggregate(sig, index, log_inv_rate)?; let bytes = recovered.to_bytes_without_pubkeys();