@@ -13,17 +13,19 @@ use parking_lot::Mutex;
1313use serde:: de:: DeserializeOwned ;
1414use slog:: { info, Logger } ;
1515use ssz:: { Decode , Encode } ;
16+ use ssz_derive:: { Decode , Encode } ;
1617use std:: borrow:: { Borrow , Cow } ;
1718use std:: marker:: PhantomData ;
1819use std:: sync:: Arc ;
1920use tree_hash:: TreeHash ;
2021use 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.
2931const 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]>`.
240280fn 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
248288impl < 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+ }
0 commit comments