Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2024-02-22 15:43:04 +11:00
190 changed files with 21898 additions and 2040 deletions

View File

@@ -1,4 +1,4 @@
TESTS_TAG := v1.4.0-beta.4
TESTS_TAG := v1.4.0-beta.6
TESTS = general minimal mainnet
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))

View File

@@ -51,7 +51,8 @@ excluded_paths = [
"bls12-381-tests/deserialization_G1",
"bls12-381-tests/deserialization_G2",
"bls12-381-tests/hash_to_G2",
"tests/.*/eip6110"
"tests/.*/eip6110",
"tests/.*/whisk"
]

View File

@@ -1,6 +1,6 @@
#![cfg(feature = "ef_tests")]
use ef_tests::{KzgInclusionMerkleProofValidityHandler, *};
use ef_tests::*;
use types::{MainnetEthSpec, MinimalEthSpec, *};
// Check that the hand-computed multiplications on EthSpec are correctly computed.
@@ -605,6 +605,7 @@ fn merkle_proof_validity() {
}
#[test]
#[cfg(feature = "fake_crypto")]
fn kzg_inclusion_merkle_proof_validity() {
KzgInclusionMerkleProofValidityHandler::<MainnetEthSpec>::default().run();
KzgInclusionMerkleProofValidityHandler::<MinimalEthSpec>::default().run();

View File

@@ -381,7 +381,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let status = self
.ee_a
.execution_layer
.notify_new_payload(valid_payload.clone().try_into().unwrap())
.notify_new_payload(valid_payload.to_ref().try_into().unwrap())
.await
.unwrap();
assert_eq!(status, PayloadStatus::Valid);
@@ -435,7 +435,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let status = self
.ee_a
.execution_layer
.notify_new_payload(invalid_payload.try_into().unwrap())
.notify_new_payload(invalid_payload.to_ref().try_into().unwrap())
.await
.unwrap();
assert!(matches!(
@@ -507,7 +507,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let status = self
.ee_a
.execution_layer
.notify_new_payload(second_payload.clone().try_into().unwrap())
.notify_new_payload(second_payload.to_ref().try_into().unwrap())
.await
.unwrap();
assert_eq!(status, PayloadStatus::Valid);
@@ -559,7 +559,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let status = self
.ee_b
.execution_layer
.notify_new_payload(second_payload.clone().try_into().unwrap())
.notify_new_payload(second_payload.to_ref().try_into().unwrap())
.await
.unwrap();
assert!(matches!(status, PayloadStatus::Syncing));
@@ -597,7 +597,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let status = self
.ee_b
.execution_layer
.notify_new_payload(valid_payload.clone().try_into().unwrap())
.notify_new_payload(valid_payload.to_ref().try_into().unwrap())
.await
.unwrap();
assert_eq!(status, PayloadStatus::Valid);
@@ -611,7 +611,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let status = self
.ee_b
.execution_layer
.notify_new_payload(second_payload.clone().try_into().unwrap())
.notify_new_payload(second_payload.to_ref().try_into().unwrap())
.await
.unwrap();
assert_eq!(status, PayloadStatus::Valid);

View File

@@ -60,7 +60,7 @@ https://prater.checkpoint.sigp.io --execution-endpoint http://localhost:8551
Mainnet:
```
$ lighthouse --network prater bn --execution-jwt /tmp/mockel.jwt --checkpoint-sync-url
$ lighthouse --network mainnet bn --execution-jwt /tmp/mockel.jwt --checkpoint-sync-url
https://checkpoint.sigp.io --execution-endpoint http://localhost:8551
```

View File

@@ -1,5 +1,5 @@
use crate::local_network::LocalNetwork;
use node_test_rig::eth2::types::{BlockId, StateId};
use node_test_rig::eth2::types::{BlockId, FinalityCheckpointsData, StateId};
use std::time::Duration;
use types::{Epoch, EthSpec, ExecPayload, ExecutionBlockHash, Hash256, Slot, Unsigned};
@@ -243,3 +243,93 @@ pub async fn verify_transition_block_finalized<E: EthSpec>(
))
}
}
pub(crate) async fn verify_light_client_updates<E: EthSpec>(
network: LocalNetwork<E>,
start_slot: Slot,
end_slot: Slot,
slot_duration: Duration,
) -> Result<(), String> {
slot_delay(start_slot, slot_duration).await;
// Tolerance of 2 slot allows for 1 single missed slot.
let light_client_update_slot_tolerance = Slot::new(2);
let remote_nodes = network.remote_nodes()?;
let client = remote_nodes.first().unwrap();
let mut have_seen_block = false;
let mut have_achieved_finality = false;
for slot in start_slot.as_u64()..=end_slot.as_u64() {
slot_delay(Slot::new(1), slot_duration).await;
let slot = Slot::new(slot);
let previous_slot = slot - 1;
let previous_slot_block = client
.get_beacon_blocks::<E>(BlockId::Slot(previous_slot))
.await
.map_err(|e| {
format!("Unable to get beacon block for previous slot {previous_slot:?}: {e:?}")
})?;
let previous_slot_has_block = previous_slot_block.is_some();
if !have_seen_block {
// Make sure we have seen the first block in Altair, to make sure we have sync aggregates available.
if previous_slot_has_block {
have_seen_block = true;
}
// Wait for another slot before we check the first update to avoid race condition.
continue;
}
// Make sure previous slot has a block, otherwise skip checking for the signature slot distance
if !previous_slot_has_block {
continue;
}
// Verify light client optimistic update. `signature_slot_distance` should be 1 in the ideal scenario.
let signature_slot = client
.get_beacon_light_client_optimistic_update::<E>()
.await
.map_err(|e| format!("Error while getting light client updates: {:?}", e))?
.ok_or(format!("Light client optimistic update not found {slot:?}"))?
.data
.signature_slot;
let signature_slot_distance = slot - signature_slot;
if signature_slot_distance > light_client_update_slot_tolerance {
return Err(format!("Existing optimistic update too old: signature slot {signature_slot}, current slot {slot:?}"));
}
// Verify light client finality update. `signature_slot_distance` should be 1 in the ideal scenario.
// NOTE: Currently finality updates are produced as long as the finalized block is known, even if the finalized header
// sync committee period does not match the signature slot committee period.
// TODO: This complies with the current spec, but we should check if this is a bug.
if !have_achieved_finality {
let FinalityCheckpointsData { finalized, .. } = client
.get_beacon_states_finality_checkpoints(StateId::Head)
.await
.map_err(|e| format!("Unable to get beacon state finality checkpoint: {e:?}"))?
.ok_or("Unable to get head state".to_string())?
.data;
if !finalized.root.is_zero() {
// Wait for another slot before we check the first finality update to avoid race condition.
have_achieved_finality = true;
}
continue;
}
let signature_slot = client
.get_beacon_light_client_finality_update::<E>()
.await
.map_err(|e| format!("Error while getting light client updates: {:?}", e))?
.ok_or(format!("Light client finality update not found {slot:?}"))?
.data
.signature_slot;
let signature_slot_distance = slot - signature_slot;
if signature_slot_distance > light_client_update_slot_tolerance {
return Err(format!(
"Existing finality update too old: signature slot {signature_slot}, current slot {slot:?}"
));
}
}
Ok(())
}

View File

@@ -220,6 +220,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
fork,
sync_aggregate,
transition,
light_client_update,
) = futures::join!(
// Check that the chain finalizes at the first given opportunity.
checks::verify_first_finalization(network.clone(), slot_duration),
@@ -272,6 +273,13 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
Epoch::new(TERMINAL_BLOCK / MinimalEthSpec::slots_per_epoch()),
slot_duration,
post_merge_sim
),
checks::verify_light_client_updates(
network.clone(),
// Sync aggregate available from slot 1 after Altair fork transition.
Epoch::new(ALTAIR_FORK_EPOCH).start_slot(MinimalEthSpec::slots_per_epoch()) + 1,
Epoch::new(END_EPOCH).start_slot(MinimalEthSpec::slots_per_epoch()),
slot_duration
)
);
@@ -282,6 +290,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
fork?;
sync_aggregate?;
transition?;
light_client_update?;
// The `final_future` either completes immediately or never completes, depending on the value
// of `continue_after_checks`.
@@ -380,6 +389,9 @@ async fn create_local_network<E: EthSpec>(
beacon_config.network.target_peers = node_count + proposer_nodes - 1;
beacon_config.network.enr_address = (Some(Ipv4Addr::LOCALHOST), None);
beacon_config.network.enable_light_client_server = true;
beacon_config.chain.enable_light_client_server = true;
beacon_config.http_api.enable_light_client_server = true;
if post_merge_sim {
let el_config = execution_layer::Config {

View File

@@ -45,7 +45,7 @@ mod tests {
initialized_validators::{
load_pem_certificate, load_pkcs12_identity, InitializedValidators,
},
validator_store::ValidatorStore,
validator_store::{Error as ValidatorStoreError, ValidatorStore},
SlashingDatabase, SLASHING_PROTECTION_FILENAME,
};
@@ -157,6 +157,18 @@ mod tests {
}
}
#[derive(Debug, Clone, Copy)]
struct SlashingProtectionConfig {
/// Whether to enable slashing protection for web3signer keys locally within Lighthouse.
local: bool,
}
impl Default for SlashingProtectionConfig {
fn default() -> Self {
SlashingProtectionConfig { local: true }
}
}
impl Web3SignerRig {
pub async fn new(network: &str, listen_address: &str, listen_port: u16) -> Self {
GET_WEB3SIGNER_BIN
@@ -231,6 +243,8 @@ mod tests {
))
.arg("eth2")
.arg(format!("--network={}", network))
// Can't *easily* test `--slashing-protection-enabled=true` because web3signer
// requires a Postgres instance.
.arg("--slashing-protection-enabled=false")
.stdout(stdio())
.stderr(stdio())
@@ -294,17 +308,25 @@ mod tests {
_validator_dir: TempDir,
runtime: Arc<tokio::runtime::Runtime>,
_runtime_shutdown: exit_future::Signal,
using_web3signer: bool,
}
impl ValidatorStoreRig {
pub async fn new(validator_definitions: Vec<ValidatorDefinition>, spec: ChainSpec) -> Self {
pub async fn new(
validator_definitions: Vec<ValidatorDefinition>,
slashing_protection_config: SlashingProtectionConfig,
using_web3signer: bool,
spec: ChainSpec,
) -> Self {
let log = environment::null_logger().unwrap();
let validator_dir = TempDir::new().unwrap();
let config = validator_client::Config::default();
let validator_definitions = ValidatorDefinitions::from(validator_definitions);
let initialized_validators = InitializedValidators::from_definitions(
validator_definitions,
validator_dir.path().into(),
config.clone(),
log.clone(),
)
.await
@@ -331,7 +353,10 @@ mod tests {
let slot_clock =
TestingSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(1));
let config = validator_client::Config::default();
let config = validator_client::Config {
enable_web3signer_slashing_protection: slashing_protection_config.local,
..Default::default()
};
let validator_store = ValidatorStore::<_, E>::new(
initialized_validators,
@@ -350,6 +375,7 @@ mod tests {
_validator_dir: validator_dir,
runtime,
_runtime_shutdown: runtime_shutdown,
using_web3signer,
}
}
@@ -378,7 +404,12 @@ mod tests {
}
impl TestingRig {
pub async fn new(network: &str, spec: ChainSpec, listen_port: u16) -> Self {
pub async fn new(
network: &str,
slashing_protection_config: SlashingProtectionConfig,
spec: ChainSpec,
listen_port: u16,
) -> Self {
let signer_rig =
Web3SignerRig::new(network, WEB3SIGNER_LISTEN_ADDRESS, listen_port).await;
let validator_pubkey = signer_rig.keypair.pk.clone();
@@ -400,7 +431,13 @@ mod tests {
voting_keystore_password: Some(KEYSTORE_PASSWORD.to_string().into()),
},
};
ValidatorStoreRig::new(vec![validator_definition], spec.clone()).await
ValidatorStoreRig::new(
vec![validator_definition],
slashing_protection_config,
false,
spec.clone(),
)
.await
};
let remote_signer_validator_store = {
@@ -422,7 +459,13 @@ mod tests {
client_identity_password: Some(client_identity_password()),
}),
};
ValidatorStoreRig::new(vec![validator_definition], spec).await
ValidatorStoreRig::new(
vec![validator_definition],
slashing_protection_config,
true,
spec,
)
.await
};
Self {
@@ -465,6 +508,36 @@ mod tests {
assert!(prev_signature.is_some(), "sanity check");
self
}
/// Assert that a slashable message fails to be signed locally and is either signed or not
/// by the web3signer rig depending on the value of `web3signer_should_sign`.
pub async fn assert_slashable_message_should_sign<F, R>(
self,
case_name: &str,
generate_sig: F,
web3signer_should_sign: bool,
) -> Self
where
F: Fn(PublicKeyBytes, Arc<ValidatorStore<TestingSlotClock, E>>) -> R,
R: Future<Output = Result<(), ValidatorStoreError>>,
{
for validator_rig in &self.validator_rigs {
let result =
generate_sig(self.validator_pubkey, validator_rig.validator_store.clone())
.await;
if !validator_rig.using_web3signer || !web3signer_should_sign {
let err = result.unwrap_err();
assert!(
matches!(err, ValidatorStoreError::Slashable(_)),
"should not sign slashable {case_name}"
);
} else {
assert_eq!(result, Ok(()), "should sign slashable {case_name}");
}
}
self
}
}
/// Get a generic, arbitrary attestation for signing.
@@ -503,64 +576,69 @@ mod tests {
let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap();
let spec = &network_config.chain_spec::<E>().unwrap();
TestingRig::new(network, spec.clone(), listen_port)
.await
.assert_signatures_match("randao_reveal", |pubkey, validator_store| async move {
TestingRig::new(
network,
SlashingProtectionConfig::default(),
spec.clone(),
listen_port,
)
.await
.assert_signatures_match("randao_reveal", |pubkey, validator_store| async move {
validator_store
.randao_reveal(pubkey, Epoch::new(0))
.await
.unwrap()
})
.await
.assert_signatures_match("beacon_block_base", |pubkey, validator_store| async move {
let block = BeaconBlock::Base(BeaconBlockBase::empty(spec));
let block_slot = block.slot();
validator_store
.sign_block(pubkey, block, block_slot)
.await
.unwrap()
})
.await
.assert_signatures_match("attestation", |pubkey, validator_store| async move {
let mut attestation = get_attestation();
validator_store
.sign_attestation(pubkey, 0, &mut attestation, Epoch::new(0))
.await
.unwrap();
attestation
})
.await
.assert_signatures_match("signed_aggregate", |pubkey, validator_store| async move {
let attestation = get_attestation();
validator_store
.produce_signed_aggregate_and_proof(
pubkey,
0,
attestation,
SelectionProof::from(Signature::empty()),
)
.await
.unwrap()
})
.await
.assert_signatures_match("selection_proof", |pubkey, validator_store| async move {
validator_store
.produce_selection_proof(pubkey, Slot::new(0))
.await
.unwrap()
})
.await
.assert_signatures_match(
"validator_registration",
|pubkey, validator_store| async move {
let val_reg_data = get_validator_registration(pubkey);
validator_store
.randao_reveal(pubkey, Epoch::new(0))
.sign_validator_registration_data(val_reg_data)
.await
.unwrap()
})
.await
.assert_signatures_match("beacon_block_base", |pubkey, validator_store| async move {
let block = BeaconBlock::Base(BeaconBlockBase::empty(spec));
let block_slot = block.slot();
validator_store
.sign_block(pubkey, block, block_slot)
.await
.unwrap()
})
.await
.assert_signatures_match("attestation", |pubkey, validator_store| async move {
let mut attestation = get_attestation();
validator_store
.sign_attestation(pubkey, 0, &mut attestation, Epoch::new(0))
.await
.unwrap();
attestation
})
.await
.assert_signatures_match("signed_aggregate", |pubkey, validator_store| async move {
let attestation = get_attestation();
validator_store
.produce_signed_aggregate_and_proof(
pubkey,
0,
attestation,
SelectionProof::from(Signature::empty()),
)
.await
.unwrap()
})
.await
.assert_signatures_match("selection_proof", |pubkey, validator_store| async move {
validator_store
.produce_selection_proof(pubkey, Slot::new(0))
.await
.unwrap()
})
.await
.assert_signatures_match(
"validator_registration",
|pubkey, validator_store| async move {
let val_reg_data = get_validator_registration(pubkey);
validator_store
.sign_validator_registration_data(val_reg_data)
.await
.unwrap()
},
)
.await;
},
)
.await;
}
/// Test all the Altair types.
@@ -572,82 +650,78 @@ mod tests {
.unwrap()
.start_slot(E::slots_per_epoch());
TestingRig::new(network, spec.clone(), listen_port)
.await
.assert_signatures_match(
"beacon_block_altair",
|pubkey, validator_store| async move {
let mut altair_block = BeaconBlockAltair::empty(spec);
altair_block.slot = altair_fork_slot;
validator_store
.sign_block(pubkey, BeaconBlock::Altair(altair_block), altair_fork_slot)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"sync_selection_proof",
|pubkey, validator_store| async move {
validator_store
.produce_sync_selection_proof(
&pubkey,
altair_fork_slot,
SyncSubnetId::from(0),
)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"sync_committee_signature",
|pubkey, validator_store| async move {
validator_store
.produce_sync_committee_signature(
altair_fork_slot,
Hash256::zero(),
0,
&pubkey,
)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"signed_contribution_and_proof",
|pubkey, validator_store| async move {
let contribution = SyncCommitteeContribution {
slot: altair_fork_slot,
beacon_block_root: <_>::default(),
subcommittee_index: <_>::default(),
aggregation_bits: <_>::default(),
signature: AggregateSignature::empty(),
};
validator_store
.produce_signed_contribution_and_proof(
0,
pubkey,
contribution,
SyncSelectionProof::from(Signature::empty()),
)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"validator_registration",
|pubkey, validator_store| async move {
let val_reg_data = get_validator_registration(pubkey);
validator_store
.sign_validator_registration_data(val_reg_data)
.await
.unwrap()
},
)
.await;
TestingRig::new(
network,
SlashingProtectionConfig::default(),
spec.clone(),
listen_port,
)
.await
.assert_signatures_match(
"beacon_block_altair",
|pubkey, validator_store| async move {
let mut altair_block = BeaconBlockAltair::empty(spec);
altair_block.slot = altair_fork_slot;
validator_store
.sign_block(pubkey, BeaconBlock::Altair(altair_block), altair_fork_slot)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"sync_selection_proof",
|pubkey, validator_store| async move {
validator_store
.produce_sync_selection_proof(&pubkey, altair_fork_slot, SyncSubnetId::from(0))
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"sync_committee_signature",
|pubkey, validator_store| async move {
validator_store
.produce_sync_committee_signature(altair_fork_slot, Hash256::zero(), 0, &pubkey)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"signed_contribution_and_proof",
|pubkey, validator_store| async move {
let contribution = SyncCommitteeContribution {
slot: altair_fork_slot,
beacon_block_root: <_>::default(),
subcommittee_index: <_>::default(),
aggregation_bits: <_>::default(),
signature: AggregateSignature::empty(),
};
validator_store
.produce_signed_contribution_and_proof(
0,
pubkey,
contribution,
SyncSelectionProof::from(Signature::empty()),
)
.await
.unwrap()
},
)
.await
.assert_signatures_match(
"validator_registration",
|pubkey, validator_store| async move {
let val_reg_data = get_validator_registration(pubkey);
validator_store
.sign_validator_registration_data(val_reg_data)
.await
.unwrap()
},
)
.await;
}
/// Test all the Merge types.
@@ -659,17 +733,154 @@ mod tests {
.unwrap()
.start_slot(E::slots_per_epoch());
TestingRig::new(network, spec.clone(), listen_port)
.await
.assert_signatures_match("beacon_block_merge", |pubkey, validator_store| async move {
let mut merge_block = BeaconBlockMerge::empty(spec);
merge_block.slot = merge_fork_slot;
TestingRig::new(
network,
SlashingProtectionConfig::default(),
spec.clone(),
listen_port,
)
.await
.assert_signatures_match("beacon_block_merge", |pubkey, validator_store| async move {
let mut merge_block = BeaconBlockMerge::empty(spec);
merge_block.slot = merge_fork_slot;
validator_store
.sign_block(pubkey, BeaconBlock::Merge(merge_block), merge_fork_slot)
.await
.unwrap()
})
.await;
}
async fn test_lighthouse_slashing_protection(
slashing_protection_config: SlashingProtectionConfig,
listen_port: u16,
) {
// Run these tests on mainnet.
let network = "mainnet";
let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap();
let spec = &network_config.chain_spec::<E>().unwrap();
let merge_fork_slot = spec
.bellatrix_fork_epoch
.unwrap()
.start_slot(E::slots_per_epoch());
// The slashable message should only be signed by the web3signer validator if slashing
// protection is disabled in Lighthouse.
let slashable_message_should_sign = !slashing_protection_config.local;
let first_attestation = || {
let mut attestation = get_attestation();
attestation.data.source.epoch = Epoch::new(1);
attestation.data.target.epoch = Epoch::new(4);
attestation
};
let double_vote_attestation = || {
let mut attestation = first_attestation();
attestation.data.beacon_block_root = Hash256::from_low_u64_be(1);
attestation
};
let surrounding_attestation = || {
let mut attestation = first_attestation();
attestation.data.source.epoch = Epoch::new(0);
attestation.data.target.epoch = Epoch::new(5);
attestation
};
let surrounded_attestation = || {
let mut attestation = first_attestation();
attestation.data.source.epoch = Epoch::new(2);
attestation.data.target.epoch = Epoch::new(3);
attestation
};
let first_block = || {
let mut merge_block = BeaconBlockMerge::empty(spec);
merge_block.slot = merge_fork_slot;
BeaconBlock::Merge(merge_block)
};
let double_vote_block = || {
let mut block = first_block();
*block.state_root_mut() = Hash256::repeat_byte(0xff);
block
};
let current_epoch = Epoch::new(5);
TestingRig::new(
network,
slashing_protection_config,
spec.clone(),
listen_port,
)
.await
.assert_signatures_match("first_attestation", |pubkey, validator_store| async move {
let mut attestation = first_attestation();
validator_store
.sign_attestation(pubkey, 0, &mut attestation, current_epoch)
.await
.unwrap();
attestation
})
.await
.assert_slashable_message_should_sign(
"double_vote_attestation",
move |pubkey, validator_store| async move {
let mut attestation = double_vote_attestation();
validator_store
.sign_block(pubkey, BeaconBlock::Merge(merge_block), merge_fork_slot)
.sign_attestation(pubkey, 0, &mut attestation, current_epoch)
.await
.unwrap()
})
.await;
},
slashable_message_should_sign,
)
.await
.assert_slashable_message_should_sign(
"surrounding_attestation",
move |pubkey, validator_store| async move {
let mut attestation = surrounding_attestation();
validator_store
.sign_attestation(pubkey, 0, &mut attestation, current_epoch)
.await
},
slashable_message_should_sign,
)
.await
.assert_slashable_message_should_sign(
"surrounded_attestation",
move |pubkey, validator_store| async move {
let mut attestation = surrounded_attestation();
validator_store
.sign_attestation(pubkey, 0, &mut attestation, current_epoch)
.await
},
slashable_message_should_sign,
)
.await
.assert_signatures_match("first_block", |pubkey, validator_store| async move {
let block = first_block();
let slot = block.slot();
validator_store
.sign_block(pubkey, block, slot)
.await
.unwrap()
})
.await
.assert_slashable_message_should_sign(
"double_vote_block",
move |pubkey, validator_store| async move {
let block = double_vote_block();
let slot = block.slot();
validator_store
.sign_block(pubkey, block, slot)
.await
.map(|_| ())
},
slashable_message_should_sign,
)
.await;
}
#[tokio::test]
@@ -706,4 +917,14 @@ mod tests {
async fn sepolia_merge_types() {
test_merge_types("sepolia", 4252).await
}
#[tokio::test]
async fn slashing_protection_disabled_locally() {
test_lighthouse_slashing_protection(SlashingProtectionConfig { local: false }, 4253).await
}
#[tokio::test]
async fn slashing_protection_enabled_locally() {
test_lighthouse_slashing_protection(SlashingProtectionConfig { local: true }, 4254).await
}
}