Optimise slow block verification tests (#9274)

Reduce CI time for `block_verification` tests that exceed 120s on Fulu/Gloas forks.

- Cache the chain segment across tests using `LazyLock<OnceCell>` (was rebuilt 10+ times independently)
- Reduce chain length from 320 to 192 slots (10 epochs to 6 epochs)
- Reduce `BLOCK_INDICES` from 7 to 3
- Reduce `chain_segment_varying_chunk_size` from 5 to 3

AI assisted, self reviewed.

Before vs After comparison (1 sample only):

* **Before**: 19 tests exceeded the 120s slow threshold.
* **After**: zero exceed it. Overall: 1,890s down to 797s (-58%).

```
| Test                                                 |  Before |   After |  Change |
|------------------------------------------------------|---------|---------|---------|
| chain_segment_varying_chunk_size                     |   239s  |     98s |    -59% |
| invalid_signature_attester_slashing                  |   175s  |     64s |    -64% |
| invalid_signature_exit                               |   173s  |     62s |    -64% |
| invalid_signature_deposit                            |   170s  |     60s |    -65% |
| invalid_signature_attestation                        |   165s  |     62s |    -63% |
| invalid_signature_proposer_slashing                  |   161s  |     56s |    -66% |
| block_gossip_verification                            |   154s  |     91s |    -41% |
| invalid_signature_block_proposal                     |   151s  |     58s |    -61% |
| invalid_signature_randao_reveal                      |   149s  |     54s |    -64% |
| invalid_signature_gossip_block                       |   135s  |     46s |    -66% |
|------------------------------------------------------|---------|---------|---------|
| TOTAL                                                |  1890s  |    797s |    -58% |
```


  


Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>

Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com>
This commit is contained in:
Jimmy Chen
2026-06-06 06:51:33 +10:00
committed by GitHub
parent abe7ca20a9
commit 65f1a832e4

View File

@@ -35,20 +35,30 @@ type E = MainnetEthSpec;
// Gloas requires >= 1 validator per slot for PTC committee computation, so >= 32 for MainnetEthSpec.
const VALIDATOR_COUNT: usize = 32;
const CHAIN_SEGMENT_LENGTH: usize = 64 * 5;
const BLOCK_INDICES: &[usize] = &[0, 1, 32, 64, 68 + 1, 129, CHAIN_SEGMENT_LENGTH - 1];
const CHAIN_SEGMENT_LENGTH: usize = 32 * 6;
const BLOCK_INDICES: &[usize] = &[1, 32, 64];
/// A cached set of keys.
static KEYPAIRS: LazyLock<Vec<Keypair>> =
LazyLock::new(|| types::test_utils::generate_deterministic_keypairs(VALIDATOR_COUNT));
// TODO(#8633): Delete this unnecessary enum and refactor this file to use `AvailableBlockData` instead.
#[derive(Clone)]
enum DataSidecars<E: EthSpec> {
Blobs(BlobSidecarList<E>),
DataColumns(Vec<CustodyDataColumn<E>>),
}
async fn get_chain_segment() -> (Vec<BeaconSnapshot<E>>, Vec<Option<DataSidecars<E>>>) {
type ChainSegmentData = (Vec<BeaconSnapshot<E>>, Vec<Option<DataSidecars<E>>>);
static CHAIN_SEGMENT: LazyLock<tokio::sync::OnceCell<ChainSegmentData>> =
LazyLock::new(tokio::sync::OnceCell::new);
async fn get_chain_segment() -> &'static ChainSegmentData {
CHAIN_SEGMENT.get_or_init(build_chain_segment).await
}
async fn build_chain_segment() -> ChainSegmentData {
// The assumption that you can re-import a block based on what you have in your DB
// is no longer true, as fullnodes stores less than what they sample.
// We use a supernode here to build a chain segment.
@@ -365,9 +375,9 @@ async fn chain_segment_full_segment() {
}
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
store_envelopes_for_chain_segment(&chain_segment, &harness);
store_envelopes_for_chain_segment(chain_segment, &harness);
let blocks: Vec<RangeSyncBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone())
chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone())
.into_iter()
.collect();
@@ -391,7 +401,7 @@ async fn chain_segment_full_segment() {
.into_block_error()
.expect("should import chain segment");
update_fork_choice_with_envelopes(&chain_segment, &harness);
update_fork_choice_with_envelopes(chain_segment, &harness);
harness.chain.recompute_head_at_current_slot().await;
assert_eq!(
@@ -410,13 +420,13 @@ async fn chain_segment_varying_chunk_size() {
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let blocks: Vec<RangeSyncBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone())
chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone())
.into_iter()
.collect();
for chunk_size in &[1, 2, 31, 32, 33] {
for chunk_size in &[1, 32, 33] {
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
store_envelopes_for_chain_segment(&chain_segment, &harness);
store_envelopes_for_chain_segment(chain_segment, &harness);
harness
.chain
@@ -432,7 +442,7 @@ async fn chain_segment_varying_chunk_size() {
.unwrap_or_else(|_| panic!("should import chain segment of len {}", chunk_size));
}
update_fork_choice_with_envelopes(&chain_segment, &harness);
update_fork_choice_with_envelopes(chain_segment, &harness);
harness.chain.recompute_head_at_current_slot().await;
assert_eq!(
@@ -457,7 +467,7 @@ async fn chain_segment_non_linear_parent_roots() {
* Test with a block removed.
*/
let mut blocks: Vec<RangeSyncBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone())
chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone())
.into_iter()
.collect();
blocks.remove(2);
@@ -478,7 +488,7 @@ async fn chain_segment_non_linear_parent_roots() {
* Test with a modified parent root.
*/
let mut blocks: Vec<RangeSyncBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone())
chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone())
.into_iter()
.collect();
@@ -520,7 +530,7 @@ async fn chain_segment_non_linear_slots() {
*/
let mut blocks: Vec<RangeSyncBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone())
chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone())
.into_iter()
.collect();
let (mut block, signature) = blocks[3].as_block().clone().deconstruct();
@@ -550,7 +560,7 @@ async fn chain_segment_non_linear_slots() {
*/
let mut blocks: Vec<RangeSyncBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone())
chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone())
.into_iter()
.collect();
let (mut block, signature) = blocks[3].as_block().clone().deconstruct();
@@ -694,7 +704,7 @@ async fn invalid_signature_gossip_block() {
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
for &block_index in BLOCK_INDICES {
// Ensure the block will be rejected if imported on its own (without gossip checking).
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let (block, _) = snapshots[block_index]
.beacon_block
@@ -753,7 +763,7 @@ async fn invalid_signature_block_proposal() {
}
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let (block, _) = snapshots[block_index]
.beacon_block
@@ -794,9 +804,10 @@ async fn invalid_signature_randao_reveal() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let mut chain_segment_blobs = ref_blobs.clone();
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let (mut block, signature) = snapshots[block_index]
.beacon_block
@@ -809,7 +820,7 @@ async fn invalid_signature_randao_reveal() {
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
update_proposal_signatures(&mut snapshots, &harness);
assert_invalid_signature(
&chain_segment,
chain_segment,
&chain_segment_blobs,
&harness,
block_index,
@@ -826,9 +837,10 @@ async fn invalid_signature_proposer_slashing() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let mut chain_segment_blobs = ref_blobs.clone();
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let (mut block, signature) = snapshots[block_index]
.beacon_block
@@ -855,7 +867,7 @@ async fn invalid_signature_proposer_slashing() {
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
update_proposal_signatures(&mut snapshots, &harness);
assert_invalid_signature(
&chain_segment,
chain_segment,
&chain_segment_blobs,
&harness,
block_index,
@@ -872,9 +884,10 @@ async fn invalid_signature_attester_slashing() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let mut chain_segment_blobs = ref_blobs.clone();
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let fork_name = harness.chain.spec.fork_name_at_slot::<E>(Slot::new(0));
@@ -980,7 +993,7 @@ async fn invalid_signature_attester_slashing() {
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
update_proposal_signatures(&mut snapshots, &harness);
assert_invalid_signature(
&chain_segment,
chain_segment,
&chain_segment_blobs,
&harness,
block_index,
@@ -997,11 +1010,12 @@ async fn invalid_signature_attestation() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let mut chain_segment_blobs = ref_blobs.clone();
let mut checked_attestation = false;
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let (mut block, signature) = snapshots[block_index]
.beacon_block
@@ -1049,7 +1063,7 @@ async fn invalid_signature_attestation() {
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
update_proposal_signatures(&mut snapshots, &harness);
assert_invalid_signature(
&chain_segment,
chain_segment,
&chain_segment_blobs,
&harness,
block_index,
@@ -1069,10 +1083,11 @@ async fn invalid_signature_attestation() {
#[tokio::test]
async fn invalid_signature_deposit() {
let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let mut chain_segment_blobs = ref_blobs.clone();
for &block_index in BLOCK_INDICES {
// Note: an invalid deposit signature is permitted!
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let deposit = Deposit {
proof: vec![Hash256::zero(); DEPOSIT_TREE_DEPTH + 1]
@@ -1126,9 +1141,10 @@ async fn invalid_signature_exit() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let mut chain_segment_blobs = ref_blobs.clone();
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let harness = get_invalid_sigs_harness(chain_segment).await;
let mut snapshots = chain_segment.clone();
let epoch = snapshots[block_index].beacon_state.current_epoch();
let (mut block, signature) = snapshots[block_index]
@@ -1152,7 +1168,7 @@ async fn invalid_signature_exit() {
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
update_proposal_signatures(&mut snapshots, &harness);
assert_invalid_signature(
&chain_segment,
chain_segment,
&chain_segment_blobs,
&harness,
block_index,
@@ -1173,7 +1189,8 @@ fn unwrap_err<T, U>(result: Result<T, U>) -> U {
#[tokio::test]
async fn block_gossip_verification() {
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
let (chain_segment, ref_blobs) = get_chain_segment().await;
let chain_segment_blobs = ref_blobs.clone();
let block_index = CHAIN_SEGMENT_LENGTH - 2;