diff --git a/Cargo.lock b/Cargo.lock index cb0944df8..36a320342 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,7 +425,7 @@ dependencies = [ "either", "futures", "futures-utils-wasm", - "lru", + "lru 0.16.3", "parking_lot", "pin-project", "reqwest", @@ -791,7 +791,7 @@ dependencies = [ "fork", "futures", "hex", - "lru", + "lru 0.16.3", "metrics", "openssl", "parking_lot", @@ -1465,7 +1465,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "beacon_node_fallback" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "clap", @@ -1565,7 +1565,7 @@ dependencies = [ [[package]] name = "bls" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "alloy-primitives 1.5.7", "blst", @@ -1662,6 +1662,25 @@ dependencies = [ "serde", ] +[[package]] +name = "builder_types" +version = "0.1.0" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" +dependencies = [ + "bls", + "context_deserialize", + "ethereum_serde_utils 0.8.0", + "ethereum_ssz", + "ethereum_ssz_derive", + "sensitive_url", + "serde", + "ssz_types", + "tree_hash", + "tree_hash_derive", + "typenum", + "types", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -2693,7 +2712,7 @@ dependencies = [ [[package]] name = "eip_3076" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "ethereum_serde_utils 0.8.0", @@ -2917,9 +2936,10 @@ dependencies = [ [[package]] name = "eth2" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", + "builder_types", "context_deserialize", "educe", "eip_3076", @@ -2947,7 +2967,7 @@ dependencies = [ [[package]] name = "eth2_config" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "paste", "types", @@ -2956,7 +2976,7 @@ dependencies = [ [[package]] name = "eth2_interop_keypairs" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "ethereum_hashing 0.8.0", @@ -2969,7 +2989,7 @@ dependencies = [ [[package]] name = "eth2_key_derivation" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "num-bigint-dig", @@ -2981,7 +3001,7 @@ dependencies = [ [[package]] name = "eth2_keystore" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "aes", "bls", @@ -3005,7 +3025,7 @@ dependencies = [ [[package]] name = "eth2_network_config" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bytes", "discv5", @@ -3228,7 +3248,7 @@ dependencies = [ [[package]] name = "filesystem" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "winapi", "windows-acl", @@ -3255,7 +3275,7 @@ dependencies = [ [[package]] name = "fixed_bytes" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "alloy-primitives 1.5.7", "safe_arith", @@ -3331,7 +3351,7 @@ dependencies = [ [[package]] name = "fork_choice" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "ethereum_ssz", "ethereum_ssz_derive", @@ -3610,7 +3630,7 @@ dependencies = [ [[package]] name = "graffiti_file" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "serde", @@ -3668,6 +3688,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", "serde", ] @@ -3697,7 +3719,7 @@ dependencies = [ [[package]] name = "health_metrics" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "eth2", "metrics", @@ -4243,7 +4265,7 @@ dependencies = [ [[package]] name = "int_to_bytes" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bytes", ] @@ -4466,7 +4488,7 @@ dependencies = [ [[package]] name = "kzg" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "educe", "ethereum_hashing 0.8.0", @@ -4992,7 +5014,7 @@ dependencies = [ [[package]] name = "logging" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "chrono", "logroller", @@ -5020,6 +5042,15 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "lru" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "lru" version = "0.16.3" @@ -5038,7 +5069,7 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lru_cache" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "fnv", ] @@ -5119,7 +5150,7 @@ dependencies = [ [[package]] name = "merkle_proof" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "alloy-primitives 1.5.7", "ethereum_hashing 0.8.0", @@ -5218,7 +5249,7 @@ dependencies = [ [[package]] name = "metrics" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "prometheus", ] @@ -5465,7 +5496,7 @@ dependencies = [ [[package]] name = "network_utils" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "discv5", "libp2p-identity", @@ -6043,7 +6074,7 @@ dependencies = [ [[package]] name = "pretty_reqwest_error" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "reqwest", "sensitive_url", @@ -6264,7 +6295,7 @@ dependencies = [ [[package]] name = "proto_array" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "ethereum_ssz", "ethereum_ssz_derive", @@ -7545,7 +7576,7 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slashing_protection" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "eip_3076", @@ -7565,7 +7596,7 @@ dependencies = [ [[package]] name = "slot_clock" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "metrics", "parking_lot", @@ -7736,7 +7767,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "state_processing" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "educe", @@ -7747,9 +7778,11 @@ dependencies = [ "int_to_bytes", "integer-sqrt", "itertools 0.14.0", + "lru 0.13.0", "merkle_proof", "metrics", "milhouse", + "parking_lot", "rand 0.9.2", "rayon", "safe_arith", @@ -7841,7 +7874,7 @@ dependencies = [ [[package]] name = "swap_or_not_shuffle" version = "0.2.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "alloy-primitives 1.5.7", "ethereum_hashing 0.8.0", @@ -7944,7 +7977,7 @@ checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" [[package]] name = "task_executor" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "async-channel 1.9.0", "futures", @@ -8476,7 +8509,7 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "types" version = "0.2.1" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "alloy-primitives 1.5.7", "alloy-rlp", @@ -8665,7 +8698,7 @@ dependencies = [ [[package]] name = "validator_metrics" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "metrics", ] @@ -8673,7 +8706,7 @@ dependencies = [ [[package]] name = "validator_services" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "beacon_node_fallback", "bls", @@ -8698,7 +8731,7 @@ dependencies = [ [[package]] name = "validator_store" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "bls", "eth2", @@ -9467,7 +9500,7 @@ dependencies = [ [[package]] name = "workspace_members" version = "0.1.0" -source = "git+https://github.com/sigp/lighthouse?rev=e58ec88fe#e58ec88fe9a8f755a0d92a80b25fd76100d08d49" +source = "git+https://github.com/sigp/lighthouse?rev=1256bd99e1b6c5d1290241849354b7e27dbc0ed9#1256bd99e1b6c5d1290241849354b7e27dbc0ed9" dependencies = [ "cargo_metadata", "quote", diff --git a/Cargo.toml b/Cargo.toml index 20232afb7..eb43e6a88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,23 +75,23 @@ ssv_types = { path = "anchor/common/ssv_types" } subnet_service = { path = "anchor/subnet_service" } version = { path = "anchor/common/version" } -# Lighthouse latest from unstable (e58ec88fe) -beacon_node_fallback = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -bls = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -eth2 = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -eth2_keystore = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -eth2_network_config = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -health_metrics = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -metrics = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -network_utils = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -slashing_protection = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -slot_clock = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -task_executor = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -types = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -validator_metrics = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -validator_services = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -validator_store = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } -workspace_members = { git = "https://github.com/sigp/lighthouse", rev = "e58ec88fe" } +# Lighthouse latest from unstable (1256bd99e1b6c5d1290241849354b7e27dbc0ed9) +beacon_node_fallback = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +bls = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +eth2 = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +eth2_keystore = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +eth2_network_config = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +health_metrics = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +metrics = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +network_utils = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +slashing_protection = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +slot_clock = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +task_executor = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +types = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +validator_metrics = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +validator_services = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +validator_store = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } +workspace_members = { git = "https://github.com/sigp/lighthouse", rev = "1256bd99e1b6c5d1290241849354b7e27dbc0ed9" } alloy = { version = "1.2.1", features = [ "sol-types", diff --git a/anchor/validator_store/src/lib.rs b/anchor/validator_store/src/lib.rs index 2d813a76a..61ac418e6 100644 --- a/anchor/validator_store/src/lib.rs +++ b/anchor/validator_store/src/lib.rs @@ -312,7 +312,7 @@ pub struct AnchorValidatorStore< voting_context_tx: watch::Sender>>, /// Watch channel for `VotingAssignments` (cached at slot start) voting_assignments_tx: watch::Sender>>, - /// Watch channel for `AggregationAssignments` (cached at 2/3 slot) + /// Watch channel for `AggregationAssignments` (cached at the aggregation deadline) aggregation_assignments_tx: watch::Sender>>>, gas_limit: u64, // MEV configuration is applied at the operator level and applies to all validators this @@ -678,7 +678,7 @@ impl + 'static> AnchorValidator Ok(drain_signatures(pending).await) } - /// Run `AggregatorCommittee` QBFT consensus for a committee at 2/3 slot. + /// Run `AggregatorCommittee` QBFT consensus at the fork-specific aggregation deadline. /// /// Called once per `(committee, slot)` by the post-consensus execution in /// [`crate::aggregator_post_consensus`], which supplies the value this operator proposes. @@ -693,7 +693,7 @@ impl + 'static> AnchorValidator metrics::start_timer_vec(&metrics::CONSENSUS_TIMES, &[metrics::AGGREGATOR_COMMITTEE]); let timeout_mode = TimeoutMode::SlotTime { round_deadline_origin: self - .get_instant_in_slot(slot, self.spec.get_slot_duration() * 2 / 3)?, + .get_instant_in_slot(slot, self.spec.get_aggregate_attestation_due::(slot))?, }; let completed = self @@ -1331,8 +1331,7 @@ impl + 'static> AnchorValidator /// Get aggregator voting assignments, waiting if not yet available for this slot. /// /// This method waits until `AggregationAssignments` for the requested slot becomes available. - /// Called by `run_aggregator_post_consensus` (via `run_aggregator_committee_consensus`) - /// at 2/3 slot. + /// Consumed by aggregation duties at the fork-specific aggregation deadline. /// /// Returns an error if the requested slot has already passed or if the watch channel is closed. pub async fn get_aggregation_assignments( @@ -1361,10 +1360,10 @@ impl + 'static> AnchorValidator } } - /// Update aggregator voting assignments (called by `MetadataService` Phase 3 at 2/3 slot). + /// Update aggregator voting assignments at the fork-specific aggregation deadline. /// /// This publishes the `AggregationAssignments` to all subscribers via the watch channel. - /// At 2/3 slot, selection proofs have been computed by Lighthouse, so + /// Once selection proofs have been computed by Lighthouse, /// `DutyAndProof.selection_proof.is_some()` accurately indicates `is_aggregator`. /// /// It also starts each Boole+ committee's post-consensus signing execution, before publishing, @@ -1664,7 +1663,8 @@ impl + 'static> AnchorValidator let timeout_mode = TimeoutMode::SlotTime { round_deadline_origin: self.get_instant_in_slot( message.aggregate().data().slot, - self.spec.get_slot_duration() * 2 / 3, + self.spec + .get_aggregate_attestation_due::(message.aggregate().data().slot), )?, }; @@ -1769,7 +1769,7 @@ impl + 'static> AnchorValidator selection_proof: contribution.selection_proof, }; - // Get aggregator voting assignments from Phase 3 (published at 2/3 slot) + // Get aggregator voting assignments from Phase 3 at the aggregation deadline let aggregator_info = self.get_aggregation_assignments(slot).await?; let signing_data = match aggregator_info @@ -1808,7 +1808,7 @@ impl + 'static> AnchorValidator ); let timeout_mode = TimeoutMode::SlotTime { round_deadline_origin: self - .get_instant_in_slot(slot, self.spec.get_slot_duration() * 2 / 3)?, + .get_instant_in_slot(slot, self.spec.get_contribution_message_due::(slot))?, }; let completed = self @@ -2762,13 +2762,13 @@ impl VotingAssignments { } } -/// Aggregator-specific voting assignments, cached at 2/3 slot when selection proofs are known. +/// Aggregator-specific voting assignments, cached at the aggregation deadline. /// /// This struct is separate from `VotingAssignments` because: /// - `VotingAssignments` is cached at slot start, before selection proofs are computed -/// - `AggregationAssignments` is cached at 2/3 slot, after Lighthouse fills in selection proofs +/// - `AggregationAssignments` is cached at the aggregation deadline, after selection proofs /// -/// At 2/3 slot, `DutyAndProof.selection_proof.is_some()` indicates `is_aggregator = true`. +/// `DutyAndProof.selection_proof.is_some()` indicates `is_aggregator = true`. /// /// Also tracks multi-subnet sync aggregators. When a validator aggregates for multiple /// sync subnets, `produce_signed_contribution_and_proof` is called multiple times (once @@ -4939,18 +4939,29 @@ mod tests { fn attestation_due_switches_at_gloas_boundary() { use types::MainnetEthSpec; + // Arrange let mut spec = ChainSpec::mainnet(); let gloas_activation_epoch = Epoch::new(100); spec.gloas_fork_epoch = Some(gloas_activation_epoch); let first_gloas_slot = gloas_activation_epoch.start_slot(MainnetEthSpec::slots_per_epoch()); let last_pre_gloas_slot = first_gloas_slot - 1; + // Act let pre = spec.get_attestation_due::(last_pre_gloas_slot); let post = spec.get_attestation_due::(first_gloas_slot); + // Assert assert_ne!(pre, post); - assert_eq!(pre, spec.unaggregated_attestation_due); - assert_eq!(post, spec.unaggregated_attestation_due_gloas); + assert_eq!( + pre, + spec.compute_slot_component_duration(spec.attestation_due_bps) + .expect("mainnet attestation BPS should be valid"), + ); + assert_eq!( + post, + spec.compute_slot_component_duration(spec.attestation_due_bps_gloas) + .expect("mainnet Gloas attestation BPS should be valid"), + ); } // ==================== SlotVote accessor tests ==================== diff --git a/anchor/validator_store/src/metadata_service.rs b/anchor/validator_store/src/metadata_service.rs index caa95f8df..68cffe3ae 100644 --- a/anchor/validator_store/src/metadata_service.rs +++ b/anchor/validator_store/src/metadata_service.rs @@ -1,5 +1,6 @@ use std::{ collections::{HashMap, HashSet}, + future::Future, sync::Arc, time::Duration, }; @@ -324,6 +325,34 @@ async fn run_slot_start_publisher(slot_clock: T, mut publish: impl } } +/// Drive aggregation publication at the upcoming slot's fork-specific deadline. +pub(super) async fn run_aggregation_publisher>( + slot_clock: T, + spec: Arc, + mut publish: impl FnMut() -> F, +) { + loop { + // Sample once so the target slot and remaining delay agree across a slot boundary. + let delay = slot_clock.now_duration().and_then(|now| { + let next_slot = slot_clock + .slot_of(now) + .map_or_else(|| slot_clock.genesis_slot(), |slot| slot + 1); + slot_clock + .start_of(next_slot)? + .checked_add(spec.get_aggregate_attestation_due::(next_slot))? + .checked_sub(now) + }); + + if let Some(delay) = delay { + sleep(delay).await; + publish().await; + } else { + error!("Failed to read slot clock"); + sleep(slot_clock.slot_duration()).await; + } + } +} + impl MetadataService { pub fn new( duties_service: Arc, T>>, @@ -453,7 +482,7 @@ impl MetadataService { ); // ═══════════════════════════════════════════════════════════════════════ - // PHASE 3: AggregationAssignments (2/3 slot) + // PHASE 3: AggregationAssignments (fork-specific aggregation deadline) // Re-fetches `duties_service.attesters()` after selection proofs are computed. // At this point, `DutyAndProof.selection_proof.is_some()` accurately indicates // `is_aggregator` for attestation duties. @@ -461,21 +490,16 @@ impl MetadataService { let self_clone_phase3 = self.clone(); executor.spawn( async move { - loop { - if let Some(duration_to_next_slot) = - self_clone_phase3.slot_clock.duration_to_next_slot() - { - // Sleep until 2/3 into slot - sleep(duration_to_next_slot + slot_duration * 2 / 3).await; - + run_aggregation_publisher::( + self_clone_phase3.slot_clock.clone(), + self_clone_phase3.spec.clone(), + || async { if let Err(err) = self_clone_phase3.update_aggregation_assignments().await { error!(err, "Failed to update aggregator voting assignments"); } - } else { - error!("Failed to read slot clock"); - sleep(slot_duration).await; - } - } + }, + ) + .await; }, "aggregation_assignments_service", ); @@ -636,7 +660,8 @@ impl MetadataService { } } - /// Phase 3: Build and publish `AggregationAssignments` at 2/3 slot. + /// Phase 3: Build and publish `AggregationAssignments` at the fork-specific aggregation + /// deadline. /// /// Uses single-pass data transformation to minimize iterations: /// - ONE pass over attesters (those with `selection_proof`) to build all attester-related data @@ -761,7 +786,7 @@ impl MetadataService { .update_aggregation_assignments(aggregator_info); self.spawn_aggregate_publisher(slot, new_executions); - trace!(%slot, "Published AggregationAssignments at 2/3 slot"); + trace!(%slot, "Published AggregationAssignments"); Ok(()) } diff --git a/anchor/validator_store/src/testing/aggregation_timing.rs b/anchor/validator_store/src/testing/aggregation_timing.rs new file mode 100644 index 000000000..f302c888b --- /dev/null +++ b/anchor/validator_store/src/testing/aggregation_timing.rs @@ -0,0 +1,258 @@ +//! Regression coverage for issue #1292 at the first Gloas slot. +//! +//! These tests exercise Anchor's actual consensus callers and Phase 3 scheduler. The scheduler +//! test supplies assignments directly, so it does not cover beacon API fetching or duty discovery. + +use std::{collections::HashMap, sync::Arc, time::Duration}; + +use bls::{FixedBytesExtended, Signature}; +use fork::Fork; +use futures::{FutureExt, StreamExt}; +use qbft_manager::TimeoutMode; +use slot_clock::{ManualSlotClock, SlotClock}; +use ssv_types::{ + OperatorId, + consensus::{AggregatorCommitteeConsensusData, AssignedAggregator}, +}; +use ssz_types::VariableList; +use tokio::time::Instant; +use types::{Attestation, Checkpoint, Epoch, EthSpec, Hash256, MainnetEthSpec, Slot}; +use validator_store::ValidatorStore; + +use super::common::*; +use crate::{AggregationAssignments, metadata_service::run_aggregation_publisher}; + +const OUR_OPERATOR_ID: OperatorId = OperatorId(1); +const COMMITTEE_INDEX: usize = 0; +const VALIDATOR_INDEX: usize = 0; +const VALIDATOR_COUNT: usize = 1; +const SYNC_SUBCOMMITTEE: u64 = 0; +const GLOAS_EPOCH: Epoch = Epoch::new(1); +const PRE_GLOAS_OFFSET: Duration = Duration::from_millis(8_000); +const GLOAS_OFFSET: Duration = Duration::from_millis(6_000); +const BEFORE_DEADLINE: Duration = Duration::from_millis(1); + +fn boundary_slots() -> [(Slot, Duration); 2] { + let first_gloas_slot = GLOAS_EPOCH.start_slot(MainnetEthSpec::slots_per_epoch()); + [ + (first_gloas_slot - 1, PRE_GLOAS_OFFSET), + (first_gloas_slot, GLOAS_OFFSET), + ] +} + +fn timing_harness(active_fork: Fork) -> ValidatorStoreTestHarness { + ValidatorStoreTestHarness::new_with_options( + vec![create_primary_committee_setup(VALIDATOR_COUNT)], + OUR_OPERATOR_ID, + HarnessOptions { + spec: gloas_at_epoch_spec(GLOAS_EPOCH), + active_fork, + ..Default::default() + }, + ) +} + +/// Legacy aggregate callbacks must use the duty slot's fork for their cumulative round origin. +#[tokio::test(start_paused = true)] +async fn legacy_aggregate_origin_switches_at_gloas_boundary() { + for (slot, expected_offset) in boundary_slots() { + // Arrange: use the same mainnet spec on both sides of the fork boundary. + let harness = timing_harness(Fork::Alan); + harness.slot_clock.set_slot(slot.as_u64()); + let mut aggregate = harness.create_aggregate(COMMITTEE_INDEX, VALIDATOR_INDEX); + aggregate.aggregate = Attestation::empty_for_signing( + COMMITTEE_INDEX as u64, + VALIDATOR_COUNT, + slot, + Hash256::zero(), + Checkpoint::default(), + Checkpoint { + epoch: slot.epoch(MainnetEthSpec::slots_per_epoch()), + root: Hash256::zero(), + }, + false, + &harness.spec, + ) + .expect("test aggregate should match the duty slot's fork"); + let slot_start = Instant::now(); + + // Act: run the real Lighthouse callback through signing and the consensus decider. + let results = harness.collect_aggregates(vec![aggregate]).await; + + // Assert: completion and the exact origin supplied to QBFT, without invoking a getter + // to calculate the expected value. + assert_eq!(results.len(), 1); + assert_eq!(results[0].as_ref().expect("aggregate should sign").len(), 1); + assert_eq!( + harness.captured_consensus_timeouts.lock().as_slice(), + [TimeoutMode::SlotTime { + round_deadline_origin: slot_start + expected_offset, + }], + "legacy aggregate origin is wrong for slot {slot}", + ); + } +} + +/// Boole's shared committee decision must use the same origin as its Phase 3 scheduler. +#[tokio::test(start_paused = true)] +async fn boole_aggregator_committee_origin_switches_at_gloas_boundary() { + for (slot, expected_offset) in boundary_slots() { + // Arrange: one selected sync contributor gives the committee a valid worklist. + let harness = timing_harness(Fork::Boole); + harness.slot_clock.set_slot(slot.as_u64()); + let validator = harness.validator_metadata(COMMITTEE_INDEX, VALIDATOR_INDEX); + let (_, cluster) = harness + .validator_store + .get_validator_and_cluster(validator.public_key) + .expect("test validator should belong to its cluster"); + let mut contribution = + harness.create_contribution(COMMITTEE_INDEX, VALIDATOR_INDEX, SYNC_SUBCOMMITTEE); + contribution.contribution.slot = slot; + let data = AggregatorCommitteeConsensusData { + version: harness + .spec + .fork_name_at_slot::(slot) + .into(), + aggregators: VariableList::empty(), + aggregator_committee_indexes: VariableList::empty(), + aggregated_attestations: VariableList::empty(), + contributors: VariableList::new(vec![AssignedAggregator { + validator_index: validator + .index + .expect("test validator should have an index"), + selection_proof: Signature::empty(), + committee_index: SYNC_SUBCOMMITTEE, + }]) + .expect("one contributor should fit"), + sync_committee_contributions: VariableList::new(vec![contribution.contribution]) + .expect("one contribution should fit"), + }; + let slot_start = Instant::now(); + + // Act: call the consensus path used by the Boole post-consensus execution. + let decided = harness + .validator_store + .run_aggregator_committee_consensus(cluster.committee_id(), slot, &cluster, &data) + .await + .expect("committee consensus should succeed"); + + // Assert: both the returned worklist and the captured origin come from the real caller. + assert_eq!(decided, data); + assert_eq!( + harness.captured_consensus_timeouts.lock().as_slice(), + [TimeoutMode::SlotTime { + round_deadline_origin: slot_start + expected_offset, + }], + "Boole committee origin is wrong for slot {slot}", + ); + } +} + +/// The scheduler is armed before the fork, so reading the current slot's offset instead of the +/// upcoming slot's offset would leave the first Gloas contribution waiting until eight seconds. +/// Driving both clocks together also checks that the published metadata belongs to the new slot. +#[tokio::test(start_paused = true)] +async fn phase3_scheduler_releases_legacy_contributions_at_the_gloas_boundary() { + // Arrange: start one slot before the last pre-Gloas duty, with no assignments cached. + let harness = timing_harness(Fork::Alan); + let [(last_pre_gloas_slot, _), _] = boundary_slots(); + harness + .slot_clock + .set_slot((last_pre_gloas_slot - 1).as_u64()); + let publisher_store = Arc::clone(&harness.validator_store); + let publisher_clock = harness.slot_clock.clone(); + let publisher = tokio::spawn(run_aggregation_publisher::( + harness.slot_clock.clone(), + Arc::clone(&harness.spec), + move || { + let store = Arc::clone(&publisher_store); + let slot = publisher_clock + .now() + .expect("manual clock should be readable"); + async move { + let executions = store.update_aggregation_assignments(AggregationAssignments { + slot, + aggregator_committees: HashMap::new(), + multi_sync_aggregators: HashMap::new(), + consensus_data_by_ssv_committee: HashMap::new(), + }); + assert!( + executions.is_empty(), + "legacy metadata has no Boole worklist" + ); + } + }, + )); + tokio::task::yield_now().await; + + for (index, (slot, expected_offset)) in boundary_slots().into_iter().enumerate() { + let to_slot_start = harness + .slot_clock + .duration_to_next_slot() + .expect("manual clock should have a next slot"); + advance_in_lockstep(&harness.slot_clock, to_slot_start).await; + let slot_start = Instant::now(); + let mut contribution = + harness.create_contribution(COMMITTEE_INDEX, VALIDATOR_INDEX, SYNC_SUBCOMMITTEE); + contribution.contribution.slot = slot; + let callback_store = Arc::clone(&harness.validator_store); + let callback = tokio::spawn(async move { + callback_store + .sign_sync_committee_contributions(vec![contribution]) + .collect::() + .await + }); + tokio::task::yield_now().await; + + // Act: reach just before the expected trigger, then cross it by one millisecond. + advance_in_lockstep(&harness.slot_clock, expected_offset - BEFORE_DEADLINE).await; + + // Assert: the real contribution callback still awaits Phase 3 metadata before the trigger. + assert!( + !callback.is_finished(), + "slot {slot} contribution started early" + ); + assert_eq!(harness.captured_consensus_timeouts.lock().len(), index); + assert!( + harness + .validator_store + .get_aggregation_assignments(slot) + .now_or_never() + .is_none(), + "slot {slot} assignments were published before their deadline", + ); + + advance_in_lockstep(&harness.slot_clock, BEFORE_DEADLINE).await; + let assignments = harness + .validator_store + .get_aggregation_assignments(slot) + .now_or_never() + .expect("Phase 3 must publish at the duty slot's aggregation deadline") + .expect("published assignments should match the requested slot"); + assert_eq!(assignments.slot, slot); + let results = callback + .await + .expect("contribution callback should complete"); + assert_eq!(results.len(), 1); + assert_eq!( + results[0].as_ref().expect("contribution should sign").len(), + 1 + ); + assert_eq!( + harness.captured_consensus_timeouts.lock()[index], + TimeoutMode::SlotTime { + round_deadline_origin: slot_start + expected_offset, + }, + "legacy contribution origin is wrong for slot {slot}", + ); + } + + publisher.abort(); +} + +/// Wall time moves first so a woken scheduler reads the slot that the virtual timer reached. +async fn advance_in_lockstep(slot_clock: &ManualSlotClock, duration: Duration) { + slot_clock.advance_time(duration); + tokio::time::advance(duration).await; + tokio::task::yield_now().await; +} diff --git a/anchor/validator_store/src/testing/common.rs b/anchor/validator_store/src/testing/common.rs index 026c6ee00..18dfb9956 100644 --- a/anchor/validator_store/src/testing/common.rs +++ b/anchor/validator_store/src/testing/common.rs @@ -110,6 +110,7 @@ pub(super) async fn run_sign_attestations( pub(super) struct MockConsensusDecider { forced_gloas_index: Option, fixed_decision: Option<(Vec, Arc)>, + captured_timeouts: Arc>>, } impl MockConsensusDecider { @@ -142,9 +143,10 @@ impl ConsensusDecider for MockConsensusDecider { _id: D::Id, initial: D, _validator: Box>, - _timeout_mode: TimeoutMode, + timeout_mode: TimeoutMode, _committee_members: &IndexSet, ) -> Result, QbftError> { + self.captured_timeouts.lock().push(timeout_mode); // `D: QbftDecidable` requires `'static`, so this downcast is sound. Only the Gloas // seed type carries `attestation_data_index`; every other `D` falls through to the echo. if let Some(index) = self.forced_gloas_index { @@ -534,6 +536,8 @@ pub(super) struct ValidatorStoreTestHarness { committee_setups: Vec, pub(super) captured_calls: CapturedCalls, pub(super) captured_disseminations: CapturedDisseminations, + /// Timeout origins supplied by the real signing and committee consensus callers. + pub(super) captured_consensus_timeouts: Arc>>, /// The dissemination handoff store the store awaits on; tests insert into it to stand in /// for the message receiver. pub(super) dissemination_store: Arc, @@ -716,6 +720,7 @@ impl ValidatorStoreTestHarness { let (is_synced_tx, is_synced_rx) = watch::channel(true); let decider = options.decider; + let captured_consensus_timeouts = Arc::clone(&decider.captured_timeouts); let spec = Arc::clone(&options.spec); let genesis_validators_root = Hash256::zero(); @@ -746,6 +751,7 @@ impl ValidatorStoreTestHarness { committee_setups, captured_calls, captured_disseminations, + captured_consensus_timeouts, dissemination_store, signature_collection_fails, signature_collection_hangs, diff --git a/anchor/validator_store/src/testing/mod.rs b/anchor/validator_store/src/testing/mod.rs index 6a1ff7fb8..b03bb3649 100644 --- a/anchor/validator_store/src/testing/mod.rs +++ b/anchor/validator_store/src/testing/mod.rs @@ -1,5 +1,6 @@ mod common; +mod aggregation_timing; mod aggregator_post_consensus; mod committee_aggregate; mod committee_attestation;