Merge pull request #5816 from realbigsean/electra-attestation-slashing-handling

Electra slashing handling
This commit is contained in:
realbigsean
2024-05-24 10:53:26 -04:00
committed by GitHub
4 changed files with 96 additions and 51 deletions

View File

@@ -115,6 +115,22 @@ impl<E: EthSpec> IndexedAttestation<E> {
IndexedAttestation::Electra(att) => att.attesting_indices.first(), IndexedAttestation::Electra(att) => att.attesting_indices.first(),
} }
} }
pub fn to_electra(self) -> Result<IndexedAttestationElectra<E>, ssz_types::Error> {
Ok(match self {
Self::Base(att) => {
let extended_attesting_indices: VariableList<u64, E::MaxValidatorsPerSlot> =
VariableList::new(att.attesting_indices.to_vec())?;
IndexedAttestationElectra {
attesting_indices: extended_attesting_indices,
data: att.data,
signature: att.signature,
}
}
Self::Electra(att) => att,
})
}
} }
impl<'a, E: EthSpec> IndexedAttestationRef<'a, E> { impl<'a, E: EthSpec> IndexedAttestationRef<'a, E> {

View File

@@ -5,6 +5,7 @@ use crate::{
}; };
use flate2::bufread::{ZlibDecoder, ZlibEncoder}; use flate2::bufread::{ZlibDecoder, ZlibEncoder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slog::Logger;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::{btree_map::Entry, BTreeMap, HashSet}; use std::collections::{btree_map::Entry, BTreeMap, HashSet};
use std::io::Read; use std::io::Read;
@@ -485,6 +486,7 @@ pub fn update<E: EthSpec>(
batch: Vec<Arc<IndexedAttesterRecord<E>>>, batch: Vec<Arc<IndexedAttesterRecord<E>>>,
current_epoch: Epoch, current_epoch: Epoch,
config: &Config, config: &Config,
log: &Logger,
) -> Result<HashSet<AttesterSlashing<E>>, Error> { ) -> Result<HashSet<AttesterSlashing<E>>, Error> {
// Split the batch up into horizontal segments. // Split the batch up into horizontal segments.
// Map chunk indexes in the range `0..self.config.chunk_size` to attestations // Map chunk indexes in the range `0..self.config.chunk_size` to attestations
@@ -504,6 +506,7 @@ pub fn update<E: EthSpec>(
&chunk_attestations, &chunk_attestations,
current_epoch, current_epoch,
config, config,
log,
)?; )?;
slashings.extend(update_array::<_, MaxTargetChunk>( slashings.extend(update_array::<_, MaxTargetChunk>(
db, db,
@@ -512,6 +515,7 @@ pub fn update<E: EthSpec>(
&chunk_attestations, &chunk_attestations,
current_epoch, current_epoch,
config, config,
log,
)?); )?);
// Update all current epochs. // Update all current epochs.
@@ -570,6 +574,7 @@ pub fn update_array<E: EthSpec, T: TargetArrayChunk>(
chunk_attestations: &BTreeMap<usize, Vec<Arc<IndexedAttesterRecord<E>>>>, chunk_attestations: &BTreeMap<usize, Vec<Arc<IndexedAttesterRecord<E>>>>,
current_epoch: Epoch, current_epoch: Epoch,
config: &Config, config: &Config,
log: &Logger,
) -> Result<HashSet<AttesterSlashing<E>>, Error> { ) -> Result<HashSet<AttesterSlashing<E>>, Error> {
let mut slashings = HashSet::new(); let mut slashings = HashSet::new();
// Map from chunk index to updated chunk at that index. // Map from chunk index to updated chunk at that index.
@@ -603,9 +608,20 @@ pub fn update_array<E: EthSpec, T: TargetArrayChunk>(
current_epoch, current_epoch,
config, config,
)?; )?;
if let Some(slashing) = slashing_status.into_slashing(&attestation.indexed) { match slashing_status.into_slashing(&attestation.indexed) {
Ok(Some(slashing)) => {
slashings.insert(slashing); slashings.insert(slashing);
} }
Err(e) => {
slog::error!(
log,
"Invalid slashing conversion";
"validator_index" => validator_index,
"error" => e
);
}
Ok(None) => {}
}
} }
} }
} }

View File

@@ -53,55 +53,56 @@ impl<E: EthSpec> AttesterSlashingStatus<E> {
pub fn into_slashing( pub fn into_slashing(
self, self,
new_attestation: &IndexedAttestation<E>, new_attestation: &IndexedAttestation<E>,
) -> Option<AttesterSlashing<E>> { ) -> Result<Option<AttesterSlashing<E>>, String> {
use AttesterSlashingStatus::*; use AttesterSlashingStatus::*;
// The surrounding attestation must be in `attestation_1` to be valid. // The surrounding attestation must be in `attestation_1` to be valid.
match self { Ok(match self {
NotSlashable => None, NotSlashable => None,
AlreadyDoubleVoted => None, AlreadyDoubleVoted => None,
DoubleVote(existing) | SurroundedByExisting(existing) => { DoubleVote(existing) | SurroundedByExisting(existing) => match *existing {
match (*existing, new_attestation) { IndexedAttestation::Base(existing_att) => {
// TODO(electra) - determine when we would convert a Base attestation to Electra / how to handle mismatched attestations here
(IndexedAttestation::Base(existing_att), IndexedAttestation::Base(new_att)) => {
Some(AttesterSlashing::Base(AttesterSlashingBase { Some(AttesterSlashing::Base(AttesterSlashingBase {
attestation_1: existing_att, attestation_1: existing_att,
attestation_2: new_att.clone(), attestation_2: new_attestation
.as_base()
.map_err(|e| format!("{e:?}"))?
.clone(),
})) }))
} }
( IndexedAttestation::Electra(existing_att) => {
IndexedAttestation::Electra(existing_att), Some(AttesterSlashing::Electra(AttesterSlashingElectra {
IndexedAttestation::Electra(new_att),
) => Some(AttesterSlashing::Electra(AttesterSlashingElectra {
attestation_1: existing_att, attestation_1: existing_att,
attestation_2: new_att.clone(), // A double vote should never convert, a surround vote where the surrounding
})), // vote is electra may convert.
_ => panic!("attestations must be of the same type"), attestation_2: new_attestation
} .clone()
} .to_electra()
// TODO(electra): fix this once we superstruct IndexedAttestation (return the correct type) .map_err(|e| format!("{e:?}"))?,
SurroundsExisting(existing) => match (*existing, new_attestation) {
(IndexedAttestation::Base(existing_att), IndexedAttestation::Base(new_att)) => {
Some(AttesterSlashing::Base(AttesterSlashingBase {
attestation_1: new_att.clone(),
attestation_2: existing_att,
})) }))
} }
(
IndexedAttestation::Electra(existing_att),
IndexedAttestation::Electra(new_att),
) => Some(AttesterSlashing::Electra(AttesterSlashingElectra {
attestation_1: new_att.clone(),
attestation_2: existing_att,
})),
_ => panic!("attestations must be of the same type"),
}, },
/* SurroundsExisting(existing) => {
match new_attestation {
IndexedAttestation::Base(new_attestation) => {
Some(AttesterSlashing::Base(AttesterSlashingBase { Some(AttesterSlashing::Base(AttesterSlashingBase {
attestation_1: new_attestation.clone(), attestation_1: existing
attestation_2: *existing, .as_base()
})), .map_err(|e| format!("{e:?}"))?
*/ .clone(),
attestation_2: new_attestation.clone(),
}))
} }
IndexedAttestation::Electra(new_attestation) => {
Some(AttesterSlashing::Electra(AttesterSlashingElectra {
attestation_1: existing.to_electra().map_err(|e| format!("{e:?}"))?,
// A double vote should never convert, a surround vote where the surrounding
// vote is electra may convert.
attestation_2: new_attestation.clone(),
}))
}
}
}
})
} }
} }

View File

@@ -247,6 +247,7 @@ impl<E: EthSpec> Slasher<E> {
batch, batch,
current_epoch, current_epoch,
&self.config, &self.config,
&self.log,
) { ) {
Ok(slashings) => { Ok(slashings) => {
if !slashings.is_empty() { if !slashings.is_empty() {
@@ -294,7 +295,8 @@ impl<E: EthSpec> Slasher<E> {
indexed_attestation_id, indexed_attestation_id,
)?; )?;
if let Some(slashing) = slashing_status.into_slashing(attestation) { match slashing_status.into_slashing(attestation) {
Ok(Some(slashing)) => {
debug!( debug!(
self.log, self.log,
"Found double-vote slashing"; "Found double-vote slashing";
@@ -303,6 +305,16 @@ impl<E: EthSpec> Slasher<E> {
); );
slashings.insert(slashing); slashings.insert(slashing);
} }
Err(e) => {
error!(
self.log,
"Invalid slashing conversion";
"validator_index" => validator_index,
"error" => e
);
}
Ok(None) => {}
}
} }
Ok(slashings) Ok(slashings)