Skip to content

Commit 28bfa8a

Browse files
authored
Merge of #7786
2 parents 1a6eeb2 + d107ff7 commit 28bfa8a

File tree

3 files changed

+184
-66
lines changed

3 files changed

+184
-66
lines changed

testing/ef_tests/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# To download/extract nightly tests, run:
22
# CONSENSUS_SPECS_TEST_VERSION=nightly make
3-
CONSENSUS_SPECS_TEST_VERSION ?= v1.6.0-alpha.1
3+
CONSENSUS_SPECS_TEST_VERSION ?= v1.6.0-alpha.3
44
REPO_NAME := consensus-spec-tests
55
OUTPUT_DIR := ./$(REPO_NAME)
66

testing/ef_tests/src/cases/fork_choice.rs

Lines changed: 183 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use beacon_chain::chain_config::{
88
DisallowedReOrgOffsets, DEFAULT_RE_ORG_HEAD_THRESHOLD,
99
DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION, DEFAULT_RE_ORG_PARENT_THRESHOLD,
1010
};
11+
use beacon_chain::data_column_verification::GossipVerifiedDataColumn;
1112
use beacon_chain::slot_clock::SlotClock;
1213
use beacon_chain::{
1314
attestation_verification::{
@@ -26,8 +27,9 @@ use std::sync::Arc;
2627
use std::time::Duration;
2728
use types::{
2829
Attestation, AttestationRef, AttesterSlashing, AttesterSlashingRef, BeaconBlock, BeaconState,
29-
BlobSidecar, BlobsList, BlockImportSource, Checkpoint, ExecutionBlockHash, Hash256,
30-
IndexedAttestation, KzgProof, ProposerPreparationData, SignedBeaconBlock, Slot, Uint256,
30+
BlobSidecar, BlobsList, BlockImportSource, Checkpoint, DataColumnSidecarList,
31+
ExecutionBlockHash, Hash256, IndexedAttestation, KzgProof, ProposerPreparationData,
32+
SignedBeaconBlock, Slot, Uint256,
3133
};
3234

3335
// When set to true, cache any states fetched from the db.
@@ -91,14 +93,14 @@ impl From<PayloadStatus> for PayloadStatusV1 {
9193

9294
#[derive(Debug, Clone, Deserialize)]
9395
#[serde(untagged, deny_unknown_fields)]
94-
pub enum Step<TBlock, TBlobs, TAttestation, TAttesterSlashing, TPowBlock> {
96+
pub enum Step<TBlock, TBlobs, TColumns, TAttestation, TAttesterSlashing, TPowBlock> {
9597
Tick {
9698
tick: u64,
9799
},
98100
ValidBlock {
99101
block: TBlock,
100102
},
101-
MaybeValidBlock {
103+
MaybeValidBlockAndBlobs {
102104
block: TBlock,
103105
blobs: Option<TBlobs>,
104106
proofs: Option<Vec<KzgProof>>,
@@ -120,6 +122,11 @@ pub enum Step<TBlock, TBlobs, TAttestation, TAttesterSlashing, TPowBlock> {
120122
Checks {
121123
checks: Box<Checks>,
122124
},
125+
MaybeValidBlockAndColumns {
126+
block: TBlock,
127+
columns: Option<TColumns>,
128+
valid: bool,
129+
},
123130
}
124131

125132
#[derive(Debug, Clone, Deserialize)]
@@ -136,7 +143,14 @@ pub struct ForkChoiceTest<E: EthSpec> {
136143
pub anchor_block: BeaconBlock<E>,
137144
#[allow(clippy::type_complexity)]
138145
pub steps: Vec<
139-
Step<SignedBeaconBlock<E>, BlobsList<E>, Attestation<E>, AttesterSlashing<E>, PowBlock>,
146+
Step<
147+
SignedBeaconBlock<E>,
148+
BlobsList<E>,
149+
DataColumnSidecarList<E>,
150+
Attestation<E>,
151+
AttesterSlashing<E>,
152+
PowBlock,
153+
>,
140154
>,
141155
}
142156

@@ -150,7 +164,7 @@ impl<E: EthSpec> LoadCase for ForkChoiceTest<E> {
150164
.expect("path must be valid OsStr")
151165
.to_string();
152166
let spec = &testing_spec::<E>(fork_name);
153-
let steps: Vec<Step<String, String, String, String, String>> =
167+
let steps: Vec<Step<String, String, Vec<String>, String, String, String>> =
154168
yaml_decode_file(&path.join("steps.yaml"))?;
155169
// Resolve the object names in `steps.yaml` into actual decoded block/attestation objects.
156170
let steps = steps
@@ -163,7 +177,7 @@ impl<E: EthSpec> LoadCase for ForkChoiceTest<E> {
163177
})
164178
.map(|block| Step::ValidBlock { block })
165179
}
166-
Step::MaybeValidBlock {
180+
Step::MaybeValidBlockAndBlobs {
167181
block,
168182
blobs,
169183
proofs,
@@ -176,7 +190,7 @@ impl<E: EthSpec> LoadCase for ForkChoiceTest<E> {
176190
let blobs = blobs
177191
.map(|blobs| ssz_decode_file(&path.join(format!("{blobs}.ssz_snappy"))))
178192
.transpose()?;
179-
Ok(Step::MaybeValidBlock {
193+
Ok(Step::MaybeValidBlockAndBlobs {
180194
block,
181195
blobs,
182196
proofs,
@@ -223,6 +237,31 @@ impl<E: EthSpec> LoadCase for ForkChoiceTest<E> {
223237
payload_status,
224238
}),
225239
Step::Checks { checks } => Ok(Step::Checks { checks }),
240+
Step::MaybeValidBlockAndColumns {
241+
block,
242+
columns,
243+
valid,
244+
} => {
245+
let block =
246+
ssz_decode_file_with(&path.join(format!("{block}.ssz_snappy")), |bytes| {
247+
SignedBeaconBlock::from_ssz_bytes(bytes, spec)
248+
})?;
249+
let columns = columns
250+
.map(|columns_vec| {
251+
columns_vec
252+
.into_iter()
253+
.map(|column| {
254+
ssz_decode_file(&path.join(format!("{column}.ssz_snappy")))
255+
})
256+
.collect::<Result<Vec<_>, _>>()
257+
})
258+
.transpose()?;
259+
Ok(Step::MaybeValidBlockAndColumns {
260+
block,
261+
columns,
262+
valid,
263+
})
264+
}
226265
})
227266
.collect::<Result<_, _>>()?;
228267
let anchor_state = ssz_decode_state(&path.join("anchor_state.ssz_snappy"), spec)?;
@@ -263,14 +302,19 @@ impl<E: EthSpec> Case for ForkChoiceTest<E> {
263302
match step {
264303
Step::Tick { tick } => tester.set_tick(*tick),
265304
Step::ValidBlock { block } => {
266-
tester.process_block(block.clone(), None, None, true)?
305+
tester.process_block_and_blobs(block.clone(), None, None, true)?
267306
}
268-
Step::MaybeValidBlock {
307+
Step::MaybeValidBlockAndBlobs {
269308
block,
270309
blobs,
271310
proofs,
272311
valid,
273-
} => tester.process_block(block.clone(), blobs.clone(), proofs.clone(), *valid)?,
312+
} => tester.process_block_and_blobs(
313+
block.clone(),
314+
blobs.clone(),
315+
proofs.clone(),
316+
*valid,
317+
)?,
274318
Step::Attestation { attestation } => tester.process_attestation(attestation)?,
275319
Step::AttesterSlashing { attester_slashing } => {
276320
tester.process_attester_slashing(attester_slashing.to_ref())
@@ -344,6 +388,14 @@ impl<E: EthSpec> Case for ForkChoiceTest<E> {
344388
tester.check_expected_proposer_head(*expected_proposer_head)?;
345389
}
346390
}
391+
392+
Step::MaybeValidBlockAndColumns {
393+
block,
394+
columns,
395+
valid,
396+
} => {
397+
tester.process_block_and_columns(block.clone(), columns.clone(), *valid)?;
398+
}
347399
}
348400
}
349401

@@ -384,6 +436,7 @@ impl<E: EthSpec> Tester<E> {
384436
.genesis_state_ephemeral_store(case.anchor_state.clone())
385437
.mock_execution_layer()
386438
.recalculate_fork_times_with_genesis(0)
439+
.import_all_data_columns(true)
387440
.mock_execution_layer_all_payloads_valid()
388441
.build();
389442

@@ -454,7 +507,66 @@ impl<E: EthSpec> Tester<E> {
454507
.unwrap();
455508
}
456509

457-
pub fn process_block(
510+
pub fn process_block_and_columns(
511+
&self,
512+
block: SignedBeaconBlock<E>,
513+
columns: Option<DataColumnSidecarList<E>>,
514+
valid: bool,
515+
) -> Result<(), Error> {
516+
let block_root = block.canonical_root();
517+
let mut data_column_success = true;
518+
519+
if let Some(columns) = columns.clone() {
520+
let gossip_verified_data_columns = columns
521+
.into_iter()
522+
.map(|column| {
523+
GossipVerifiedDataColumn::new(column.clone(), column.index, &self.harness.chain)
524+
.unwrap_or_else(|_| {
525+
data_column_success = false;
526+
GossipVerifiedDataColumn::__new_for_testing(column)
527+
})
528+
})
529+
.collect();
530+
531+
let result = self.block_on_dangerous(
532+
self.harness
533+
.chain
534+
.process_gossip_data_columns(gossip_verified_data_columns, || Ok(())),
535+
)?;
536+
if valid {
537+
assert!(result.is_ok());
538+
}
539+
};
540+
541+
let block = Arc::new(block);
542+
let result: Result<Result<Hash256, ()>, _> = self
543+
.block_on_dangerous(self.harness.chain.process_block(
544+
block_root,
545+
RpcBlock::new_without_blobs(Some(block_root), block.clone()),
546+
NotifyExecutionLayer::Yes,
547+
BlockImportSource::Lookup,
548+
|| Ok(()),
549+
))?
550+
.map(|avail: AvailabilityProcessingStatus| avail.try_into());
551+
let success = data_column_success && result.as_ref().is_ok_and(|inner| inner.is_ok());
552+
if success != valid {
553+
return Err(Error::DidntFail(format!(
554+
"block with root {} was valid={} whilst test expects valid={}. result: {:?}",
555+
block_root,
556+
result.is_ok(),
557+
valid,
558+
result
559+
)));
560+
}
561+
562+
if !valid && columns.is_none() {
563+
self.apply_invalid_block(&block)?;
564+
}
565+
566+
Ok(())
567+
}
568+
569+
pub fn process_block_and_blobs(
458570
&self,
459571
block: SignedBeaconBlock<E>,
460572
blobs: Option<BlobsList<E>>,
@@ -537,66 +649,73 @@ impl<E: EthSpec> Tester<E> {
537649
)));
538650
}
539651

540-
// Apply invalid blocks directly against the fork choice `on_block` function. This ensures
541-
// that the block is being rejected by `on_block`, not just some upstream block processing
542-
// function. When blobs exist, we don't do this.
543652
if !valid && blobs.is_none() {
544-
// A missing parent block whilst `valid == false` means the test should pass.
545-
if let Some(parent_block) = self
653+
self.apply_invalid_block(&block)?;
654+
}
655+
656+
Ok(())
657+
}
658+
659+
// Apply invalid blocks directly against the fork choice `on_block` function. This ensures
660+
// that the block is being rejected by `on_block`, not just some upstream block processing
661+
// function. When data columns or blobs exist, we don't do this.
662+
fn apply_invalid_block(&self, block: &Arc<SignedBeaconBlock<E>>) -> Result<(), Error> {
663+
let block_root = block.canonical_root();
664+
// A missing parent block whilst `valid == false` means the test should pass.
665+
if let Some(parent_block) = self
666+
.harness
667+
.chain
668+
.get_blinded_block(&block.parent_root())
669+
.unwrap()
670+
{
671+
let parent_state_root = parent_block.state_root();
672+
673+
let mut state = self
546674
.harness
547675
.chain
548-
.get_blinded_block(&block.parent_root())
676+
.get_state(
677+
&parent_state_root,
678+
Some(parent_block.slot()),
679+
CACHE_STATE_IN_TESTS,
680+
)
549681
.unwrap()
550-
{
551-
let parent_state_root = parent_block.state_root();
682+
.unwrap();
552683

553-
let mut state = self
554-
.harness
555-
.chain
556-
.get_state(
557-
&parent_state_root,
558-
Some(parent_block.slot()),
559-
CACHE_STATE_IN_TESTS,
560-
)
561-
.unwrap()
562-
.unwrap();
563-
564-
complete_state_advance(
565-
&mut state,
566-
Some(parent_state_root),
567-
block.slot(),
568-
&self.harness.chain.spec,
569-
)
684+
complete_state_advance(
685+
&mut state,
686+
Some(parent_state_root),
687+
block.slot(),
688+
&self.harness.chain.spec,
689+
)
690+
.unwrap();
691+
692+
let block_delay = self
693+
.harness
694+
.chain
695+
.slot_clock
696+
.seconds_from_current_slot_start()
570697
.unwrap();
571698

572-
let block_delay = self
573-
.harness
574-
.chain
575-
.slot_clock
576-
.seconds_from_current_slot_start()
577-
.unwrap();
699+
let result = self
700+
.harness
701+
.chain
702+
.canonical_head
703+
.fork_choice_write_lock()
704+
.on_block(
705+
self.harness.chain.slot().unwrap(),
706+
block.message(),
707+
block_root,
708+
block_delay,
709+
&state,
710+
PayloadVerificationStatus::Irrelevant,
711+
&self.harness.chain.spec,
712+
);
578713

579-
let result = self
580-
.harness
581-
.chain
582-
.canonical_head
583-
.fork_choice_write_lock()
584-
.on_block(
585-
self.harness.chain.slot().unwrap(),
586-
block.message(),
587-
block_root,
588-
block_delay,
589-
&state,
590-
PayloadVerificationStatus::Irrelevant,
591-
&self.harness.chain.spec,
592-
);
593-
594-
if result.is_ok() {
595-
return Err(Error::DidntFail(format!(
596-
"block with root {} should fail on_block",
597-
block_root,
598-
)));
599-
}
714+
if result.is_ok() {
715+
return Err(Error::DidntFail(format!(
716+
"block with root {} should fail on_block",
717+
block_root,
718+
)));
600719
}
601720
}
602721

testing/ef_tests/src/handler.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ pub trait Handler {
9393
.filter_map(as_directory)
9494
.map(|test_case_dir| {
9595
let path = test_case_dir.path();
96-
9796
let case = Self::Case::load_from_dir(&path, fork_name).expect("test should load");
9897
(path, case)
9998
})

0 commit comments

Comments
 (0)