mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-19 13:58:28 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
@@ -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))
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user