don't register exited or slashed validators with the builder api (#3473)

## Issue Addressed

#3465

## Proposed Changes

Filter out any validator registrations for validators that are not `active` or `pending`.  I'm adding this filtering the beacon node because all the information is readily available there. In other parts of the VC we are usually sending per-validator requests based on duties from the BN. And duties will only be provided for active validators so we don't have this type of filtering elsewhere in the VC.



Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
realbigsean 2022-08-24 23:34:58 +00:00
parent 8c69d57c2c
commit cb132c622d
2 changed files with 132 additions and 8 deletions

View File

@ -25,6 +25,7 @@ use beacon_chain::{
BeaconChainTypes, ProduceBlockVerification, WhenSlotSkipped,
};
pub use block_id::BlockId;
use eth2::types::ValidatorStatus;
use eth2::types::{self as api_types, EndpointVersion, ValidatorId};
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
use lighthouse_version::version_with_platform;
@ -2481,19 +2482,47 @@ pub fn serve<T: BeaconChainTypes>(
"count" => register_val_data.len(),
);
let preparation_data = register_val_data
.iter()
let head_snapshot = chain.head_snapshot();
let spec = &chain.spec;
let (preparation_data, filtered_registration_data): (
Vec<ProposerPreparationData>,
Vec<SignedValidatorRegistrationData>,
) = register_val_data
.into_iter()
.filter_map(|register_data| {
chain
.validator_index(&register_data.message.pubkey)
.ok()
.flatten()
.map(|validator_index| ProposerPreparationData {
validator_index: validator_index as u64,
fee_recipient: register_data.message.fee_recipient,
.and_then(|validator_index| {
let validator = head_snapshot
.beacon_state
.get_validator(validator_index)
.ok()?;
let validator_status = ValidatorStatus::from_validator(
validator,
current_epoch,
spec.far_future_epoch,
)
.superstatus();
let is_active_or_pending =
matches!(validator_status, ValidatorStatus::Pending)
|| matches!(validator_status, ValidatorStatus::Active);
// Filter out validators who are not 'active' or 'pending'.
is_active_or_pending.then(|| {
(
ProposerPreparationData {
validator_index: validator_index as u64,
fee_recipient: register_data.message.fee_recipient,
},
register_data,
)
})
})
})
.collect::<Vec<_>>();
.unzip();
// Update the prepare beacon proposer cache based on this request.
execution_layer
@ -2522,11 +2551,11 @@ pub fn serve<T: BeaconChainTypes>(
info!(
log,
"Forwarding register validator request to connected builder";
"count" => register_val_data.len(),
"count" => filtered_registration_data.len(),
);
builder
.post_builder_validators(&register_val_data)
.post_builder_validators(&filtered_registration_data)
.await
.map(|resp| warp::reply::json(&resp))
.map_err(|e| {

View File

@ -2459,6 +2459,93 @@ impl ApiTester {
self
}
pub async fn test_post_validator_register_validator_slashed(self) -> Self {
// slash a validator
self.client
.post_beacon_pool_attester_slashings(&self.attester_slashing)
.await
.unwrap();
self.harness
.extend_chain(
1,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
)
.await;
let mut registrations = vec![];
let mut fee_recipients = vec![];
let genesis_epoch = self.chain.spec.genesis_slot.epoch(E::slots_per_epoch());
let fork = Fork {
current_version: self.chain.spec.genesis_fork_version,
previous_version: self.chain.spec.genesis_fork_version,
epoch: genesis_epoch,
};
let expected_gas_limit = 11_111_111;
for (val_index, keypair) in self.validator_keypairs().iter().enumerate() {
let pubkey = keypair.pk.compress();
let fee_recipient = Address::from_low_u64_be(val_index as u64);
let data = ValidatorRegistrationData {
fee_recipient,
gas_limit: expected_gas_limit,
timestamp: 0,
pubkey,
};
let domain = self.chain.spec.get_domain(
genesis_epoch,
Domain::ApplicationMask(ApplicationDomain::Builder),
&fork,
Hash256::zero(),
);
let message = data.signing_root(domain);
let signature = keypair.sk.sign(message);
let signed = SignedValidatorRegistrationData {
message: data,
signature,
};
fee_recipients.push(fee_recipient);
registrations.push(signed);
}
self.client
.post_validator_register_validator(&registrations)
.await
.unwrap();
for (val_index, (_, fee_recipient)) in self
.chain
.head_snapshot()
.beacon_state
.validators()
.into_iter()
.zip(fee_recipients.into_iter())
.enumerate()
{
let actual = self
.chain
.execution_layer
.as_ref()
.unwrap()
.get_suggested_fee_recipient(val_index as u64)
.await;
if val_index == 0 || val_index == 1 {
assert_eq!(actual, Address::from_low_u64_be(val_index as u64));
} else {
assert_eq!(actual, fee_recipient);
}
}
self
}
// Helper function for tests that require a valid RANDAO signature.
async fn get_test_randao(&self, slot: Slot, epoch: Epoch) -> (u64, SignatureBytes) {
let fork = self.chain.canonical_head.cached_head().head_fork();
@ -3964,6 +4051,14 @@ async fn post_validator_register_validator() {
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_validator_slashed() {
ApiTester::new()
.await
.test_post_validator_register_validator_slashed()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_valid() {
ApiTester::new_mev_tester()