Skip to content

Commit cf030d0

Browse files
authored
Merge pull request #5975 from michaelsproul/electra-slasher-no-migration
Avoid changing slasher schema for Electra
2 parents 5517c78 + 8fc5333 commit cf030d0

File tree

15 files changed

+198
-67
lines changed

15 files changed

+198
-67
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

beacon_node/beacon_chain/tests/block_verification.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
use beacon_chain::block_verification_types::{AsBlock, ExecutedBlock, RpcBlock};
44
use beacon_chain::{
5-
test_utils::{AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType},
5+
test_utils::{
6+
test_spec, AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType,
7+
},
68
AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, ExecutionPendingBlock,
79
};
810
use beacon_chain::{
@@ -1210,8 +1212,14 @@ async fn block_gossip_verification() {
12101212
#[tokio::test]
12111213
async fn verify_block_for_gossip_slashing_detection() {
12121214
let slasher_dir = tempdir().unwrap();
1215+
let spec = Arc::new(test_spec::<E>());
12131216
let slasher = Arc::new(
1214-
Slasher::open(SlasherConfig::new(slasher_dir.path().into()), test_logger()).unwrap(),
1217+
Slasher::open(
1218+
SlasherConfig::new(slasher_dir.path().into()),
1219+
spec,
1220+
test_logger(),
1221+
)
1222+
.unwrap(),
12151223
);
12161224

12171225
let inner_slasher = slasher.clone();

beacon_node/src/lib.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
8080

8181
let builder = ClientBuilder::new(context.eth_spec_instance.clone())
8282
.runtime_context(context)
83-
.chain_spec(spec)
83+
.chain_spec(spec.clone())
8484
.beacon_processor(client_config.beacon_processor.clone())
8585
.http_api_config(client_config.http_api.clone())
8686
.disk_store(
@@ -113,8 +113,12 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
113113
_ => {}
114114
}
115115
let slasher = Arc::new(
116-
Slasher::open(slasher_config, log.new(slog::o!("service" => "slasher")))
117-
.map_err(|e| format!("Slasher open error: {:?}", e))?,
116+
Slasher::open(
117+
slasher_config,
118+
Arc::new(spec),
119+
log.new(slog::o!("service" => "slasher")),
120+
)
121+
.map_err(|e| format!("Slasher open error: {:?}", e))?,
118122
);
119123
builder.slasher(slasher)
120124
} else {

consensus/types/src/indexed_attestation.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -240,38 +240,6 @@ mod quoted_variable_list_u64 {
240240
}
241241
}
242242

243-
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
244-
#[ssz(enum_behaviour = "union")]
245-
pub enum IndexedAttestationOnDisk<E: EthSpec> {
246-
Base(IndexedAttestationBase<E>),
247-
Electra(IndexedAttestationElectra<E>),
248-
}
249-
250-
#[derive(Debug, Clone, Encode, PartialEq)]
251-
#[ssz(enum_behaviour = "union")]
252-
pub enum IndexedAttestationRefOnDisk<'a, E: EthSpec> {
253-
Base(&'a IndexedAttestationBase<E>),
254-
Electra(&'a IndexedAttestationElectra<E>),
255-
}
256-
257-
impl<'a, E: EthSpec> From<&'a IndexedAttestation<E>> for IndexedAttestationRefOnDisk<'a, E> {
258-
fn from(attestation: &'a IndexedAttestation<E>) -> Self {
259-
match attestation {
260-
IndexedAttestation::Base(attestation) => Self::Base(attestation),
261-
IndexedAttestation::Electra(attestation) => Self::Electra(attestation),
262-
}
263-
}
264-
}
265-
266-
impl<E: EthSpec> From<IndexedAttestationOnDisk<E>> for IndexedAttestation<E> {
267-
fn from(attestation: IndexedAttestationOnDisk<E>) -> Self {
268-
match attestation {
269-
IndexedAttestationOnDisk::Base(attestation) => Self::Base(attestation),
270-
IndexedAttestationOnDisk::Electra(attestation) => Self::Electra(attestation),
271-
}
272-
}
273-
}
274-
275243
#[cfg(test)]
276244
mod tests {
277245
use super::*;

consensus/types/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,7 @@ pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedRe
176176
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
177177
pub use crate::historical_batch::HistoricalBatch;
178178
pub use crate::indexed_attestation::{
179-
IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra,
180-
IndexedAttestationOnDisk, IndexedAttestationRef, IndexedAttestationRefOnDisk,
179+
IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef,
181180
};
182181
pub use crate::light_client_bootstrap::{
183182
LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella,

slasher/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ tree_hash = { workspace = true }
2929
tree_hash_derive = { workspace = true }
3030
types = { workspace = true }
3131
strum = { workspace = true }
32+
ssz_types = { workspace = true }
3233

3334
# MDBX is pinned at the last version with Windows and macOS support.
3435
mdbx = { package = "libmdbx", git = "https://github.com/sigp/libmdbx-rs", tag = "v0.1.4", optional = true }

slasher/src/database.rs

Lines changed: 139 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ use parking_lot::Mutex;
1313
use serde::de::DeserializeOwned;
1414
use slog::{info, Logger};
1515
use ssz::{Decode, Encode};
16+
use ssz_derive::{Decode, Encode};
1617
use std::borrow::{Borrow, Cow};
1718
use std::marker::PhantomData;
1819
use std::sync::Arc;
1920
use tree_hash::TreeHash;
2021
use types::{
21-
Epoch, EthSpec, Hash256, IndexedAttestation, IndexedAttestationOnDisk,
22-
IndexedAttestationRefOnDisk, ProposerSlashing, SignedBeaconBlockHeader, Slot,
22+
AggregateSignature, AttestationData, ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation,
23+
IndexedAttestationBase, IndexedAttestationElectra, ProposerSlashing, SignedBeaconBlockHeader,
24+
Slot, VariableList,
2325
};
2426

2527
/// Current database schema version, to check compatibility of on-disk DB with software.
26-
pub const CURRENT_SCHEMA_VERSION: u64 = 4;
28+
pub const CURRENT_SCHEMA_VERSION: u64 = 3;
2729

2830
/// Metadata about the slashing database itself.
2931
const METADATA_DB: &str = "metadata";
@@ -70,6 +72,7 @@ pub struct SlasherDB<E: EthSpec> {
7072
/// LRU cache mapping indexed attestation IDs to their attestation data roots.
7173
attestation_root_cache: Mutex<LruCache<IndexedAttestationId, Hash256>>,
7274
pub(crate) config: Arc<Config>,
75+
pub(crate) spec: Arc<ChainSpec>,
7376
_phantom: PhantomData<E>,
7477
}
7578

@@ -236,6 +239,43 @@ impl AsRef<[u8]> for IndexedAttestationId {
236239
}
237240
}
238241

242+
/// Indexed attestation that abstracts over Phase0 and Electra variants by using a plain `Vec` for
243+
/// the attesting indices.
244+
///
245+
/// This allows us to avoid rewriting the entire indexed attestation database at Electra, which
246+
/// saves a lot of execution time. The bytes that it encodes to are the same as the bytes that a
247+
/// regular IndexedAttestation encodes to, because SSZ doesn't care about the length-bound.
248+
#[derive(Debug, PartialEq, Decode, Encode)]
249+
pub struct IndexedAttestationOnDisk {
250+
attesting_indices: Vec<u64>,
251+
data: AttestationData,
252+
signature: AggregateSignature,
253+
}
254+
255+
impl IndexedAttestationOnDisk {
256+
fn into_indexed_attestation<E: EthSpec>(
257+
self,
258+
spec: &ChainSpec,
259+
) -> Result<IndexedAttestation<E>, Error> {
260+
let fork_at_target_epoch = spec.fork_name_at_epoch(self.data.target.epoch);
261+
if fork_at_target_epoch.electra_enabled() {
262+
let attesting_indices = VariableList::new(self.attesting_indices)?;
263+
Ok(IndexedAttestation::Electra(IndexedAttestationElectra {
264+
attesting_indices,
265+
data: self.data,
266+
signature: self.signature,
267+
}))
268+
} else {
269+
let attesting_indices = VariableList::new(self.attesting_indices)?;
270+
Ok(IndexedAttestation::Base(IndexedAttestationBase {
271+
attesting_indices,
272+
data: self.data,
273+
signature: self.signature,
274+
}))
275+
}
276+
}
277+
}
278+
239279
/// Bincode deserialization specialised to `Cow<[u8]>`.
240280
fn bincode_deserialize<T: DeserializeOwned>(bytes: Cow<[u8]>) -> Result<T, Error> {
241281
Ok(bincode::deserialize(bytes.borrow())?)
@@ -246,7 +286,7 @@ fn ssz_decode<T: Decode>(bytes: Cow<[u8]>) -> Result<T, Error> {
246286
}
247287

248288
impl<E: EthSpec> SlasherDB<E> {
249-
pub fn open(config: Arc<Config>, log: Logger) -> Result<Self, Error> {
289+
pub fn open(config: Arc<Config>, spec: Arc<ChainSpec>, log: Logger) -> Result<Self, Error> {
250290
info!(log, "Opening slasher database"; "backend" => %config.backend);
251291

252292
std::fs::create_dir_all(&config.database_path)?;
@@ -269,6 +309,7 @@ impl<E: EthSpec> SlasherDB<E> {
269309
databases,
270310
attestation_root_cache,
271311
config,
312+
spec,
272313
_phantom: PhantomData,
273314
};
274315

@@ -458,9 +499,8 @@ impl<E: EthSpec> SlasherDB<E> {
458499
};
459500

460501
let attestation_key = IndexedAttestationId::new(indexed_att_id);
461-
let indexed_attestation_on_disk: IndexedAttestationRefOnDisk<E> =
462-
indexed_attestation.into();
463-
let data = indexed_attestation_on_disk.as_ssz_bytes();
502+
// IndexedAttestationOnDisk and IndexedAttestation have compatible encodings.
503+
let data = indexed_attestation.as_ssz_bytes();
464504

465505
cursor.put(attestation_key.as_ref(), &data)?;
466506
drop(cursor);
@@ -484,8 +524,8 @@ impl<E: EthSpec> SlasherDB<E> {
484524
.ok_or(Error::MissingIndexedAttestation {
485525
id: indexed_attestation_id.as_u64(),
486526
})?;
487-
let indexed_attestation: IndexedAttestationOnDisk<E> = ssz_decode(bytes)?;
488-
Ok(indexed_attestation.into())
527+
let indexed_attestation_on_disk: IndexedAttestationOnDisk = ssz_decode(bytes)?;
528+
indexed_attestation_on_disk.into_indexed_attestation(&self.spec)
489529
}
490530

491531
fn get_attestation_data_root(
@@ -775,3 +815,93 @@ impl<E: EthSpec> SlasherDB<E> {
775815
Ok(())
776816
}
777817
}
818+
819+
#[cfg(test)]
820+
mod test {
821+
use super::*;
822+
use types::{Checkpoint, ForkName, MainnetEthSpec, Unsigned};
823+
824+
type E = MainnetEthSpec;
825+
826+
fn indexed_attestation_on_disk_roundtrip_test(
827+
spec: &ChainSpec,
828+
make_attestation: fn(
829+
Vec<u64>,
830+
AttestationData,
831+
AggregateSignature,
832+
) -> IndexedAttestation<E>,
833+
committee_len: u64,
834+
) {
835+
let attestation_data = AttestationData {
836+
slot: Slot::new(1000),
837+
index: 0,
838+
beacon_block_root: Hash256::repeat_byte(0xaa),
839+
source: Checkpoint {
840+
epoch: Epoch::new(0),
841+
root: Hash256::repeat_byte(0xbb),
842+
},
843+
target: Checkpoint {
844+
epoch: Epoch::new(31),
845+
root: Hash256::repeat_byte(0xcc),
846+
},
847+
};
848+
849+
let attesting_indices = (0..committee_len).collect::<Vec<_>>();
850+
let signature = AggregateSignature::infinity();
851+
852+
let fork_attestation = make_attestation(
853+
attesting_indices.clone(),
854+
attestation_data.clone(),
855+
signature.clone(),
856+
);
857+
858+
let on_disk = IndexedAttestationOnDisk {
859+
attesting_indices,
860+
data: attestation_data,
861+
signature,
862+
};
863+
let encoded = on_disk.as_ssz_bytes();
864+
assert_eq!(encoded, fork_attestation.as_ssz_bytes());
865+
866+
let decoded_on_disk = IndexedAttestationOnDisk::from_ssz_bytes(&encoded).unwrap();
867+
assert_eq!(decoded_on_disk, on_disk);
868+
869+
let decoded = on_disk.into_indexed_attestation(spec).unwrap();
870+
assert_eq!(decoded, fork_attestation);
871+
}
872+
873+
/// Check that `IndexedAttestationOnDisk` and `IndexedAttestation` have compatible encodings.
874+
#[test]
875+
fn indexed_attestation_on_disk_roundtrip_base() {
876+
let spec = ForkName::Base.make_genesis_spec(E::default_spec());
877+
let make_attestation = |attesting_indices, data, signature| {
878+
IndexedAttestation::<E>::Base(IndexedAttestationBase {
879+
attesting_indices: VariableList::new(attesting_indices).unwrap(),
880+
data,
881+
signature,
882+
})
883+
};
884+
indexed_attestation_on_disk_roundtrip_test(
885+
&spec,
886+
make_attestation,
887+
<E as EthSpec>::MaxValidatorsPerCommittee::to_u64(),
888+
)
889+
}
890+
891+
#[test]
892+
fn indexed_attestation_on_disk_roundtrip_electra() {
893+
let spec = ForkName::Electra.make_genesis_spec(E::default_spec());
894+
let make_attestation = |attesting_indices, data, signature| {
895+
IndexedAttestation::<E>::Electra(IndexedAttestationElectra {
896+
attesting_indices: VariableList::new(attesting_indices).unwrap(),
897+
data,
898+
signature,
899+
})
900+
};
901+
indexed_attestation_on_disk_roundtrip_test(
902+
&spec,
903+
make_attestation,
904+
<E as EthSpec>::MaxValidatorsPerSlot::to_u64(),
905+
)
906+
}
907+
}

slasher/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub enum Error {
1313
DatabaseIOError(io::Error),
1414
DatabasePermissionsError(filesystem::Error),
1515
SszDecodeError(ssz::DecodeError),
16+
SszTypesError(ssz_types::Error),
1617
BincodeError(bincode::Error),
1718
ArithError(safe_arith::ArithError),
1819
ChunkIndexOutOfBounds(usize),
@@ -100,6 +101,12 @@ impl From<ssz::DecodeError> for Error {
100101
}
101102
}
102103

104+
impl From<ssz_types::Error> for Error {
105+
fn from(e: ssz_types::Error) -> Self {
106+
Error::SszTypesError(e)
107+
}
108+
}
109+
103110
impl From<bincode::Error> for Error {
104111
fn from(e: bincode::Error) -> Self {
105112
Error::BincodeError(e)

slasher/src/migrate.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ impl<E: EthSpec> SlasherDB<E> {
1717
software_schema_version: CURRENT_SCHEMA_VERSION,
1818
}),
1919
(x, y) if x == y => Ok(self),
20-
(3, 4) => {
21-
// TODO(electra): db migration due to `IndexedAttestationOnDisk`
22-
Ok(self)
23-
}
2420
(_, _) => Err(Error::IncompatibleSchemaVersion {
2521
database_schema_version: schema_version,
2622
software_schema_version: CURRENT_SCHEMA_VERSION,

slasher/src/slasher.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use slog::{debug, error, info, Logger};
1313
use std::collections::HashSet;
1414
use std::sync::Arc;
1515
use types::{
16-
AttesterSlashing, Epoch, EthSpec, IndexedAttestation, ProposerSlashing, SignedBeaconBlockHeader,
16+
AttesterSlashing, ChainSpec, Epoch, EthSpec, IndexedAttestation, ProposerSlashing,
17+
SignedBeaconBlockHeader,
1718
};
1819

1920
#[derive(Debug)]
@@ -28,10 +29,10 @@ pub struct Slasher<E: EthSpec> {
2829
}
2930

3031
impl<E: EthSpec> Slasher<E> {
31-
pub fn open(config: Config, log: Logger) -> Result<Self, Error> {
32+
pub fn open(config: Config, spec: Arc<ChainSpec>, log: Logger) -> Result<Self, Error> {
3233
config.validate()?;
3334
let config = Arc::new(config);
34-
let db = SlasherDB::open(config.clone(), log.clone())?;
35+
let db = SlasherDB::open(config.clone(), spec, log.clone())?;
3536
let attester_slashings = Mutex::new(HashSet::new());
3637
let proposer_slashings = Mutex::new(HashSet::new());
3738
let attestation_queue = AttestationQueue::default();

0 commit comments

Comments
 (0)