1use crate::{header::BeaconBlockHeader, BlsPublicKey, BlsSignature};
6use alloy_primitives::{Bytes, B256};
7use serde::{Deserialize, Serialize};
8use serde_with::{serde_as, DisplayFromStr};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct BlockResponse<T = serde_json::Value> {
15 pub version: String,
18 #[serde(default)]
21 pub execution_optimistic: bool,
22 #[serde(default)]
26 pub finalized: bool,
27 pub data: SignedBeaconBlock<T>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct SignedBeaconBlock<T = serde_json::Value> {
37 pub message: BeaconBlock<T>,
39 pub signature: BlsSignature,
41}
42
43#[serde_as]
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49pub struct BeaconBlock<T = serde_json::Value> {
50 #[serde_as(as = "DisplayFromStr")]
52 pub slot: u64,
53 #[serde_as(as = "DisplayFromStr")]
55 pub proposer_index: u64,
56 pub parent_root: B256,
58 pub state_root: B256,
60 pub body: T,
62}
63
64#[serde_as]
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
69#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
70pub struct Eth1Data {
71 pub deposit_root: B256,
73 #[serde_as(as = "DisplayFromStr")]
75 pub deposit_count: u64,
76 pub block_hash: B256,
78}
79
80#[serde_as]
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
85#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
86pub struct Checkpoint {
87 #[serde_as(as = "DisplayFromStr")]
89 pub epoch: u64,
90 pub root: B256,
92}
93
94#[serde_as]
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
99#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
100pub struct AttestationData {
101 #[serde_as(as = "DisplayFromStr")]
103 pub slot: u64,
104 #[serde_as(as = "DisplayFromStr")]
106 pub index: u64,
107 pub beacon_block_root: B256,
109 pub source: Checkpoint,
111 pub target: Checkpoint,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
119#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
120pub struct Attestation {
121 pub aggregation_bits: Bytes,
123 pub data: AttestationData,
125 pub signature: BlsSignature,
127}
128
129#[serde_as]
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
135pub struct IndexedAttestation {
136 #[serde_as(as = "Vec<DisplayFromStr>")]
138 pub attesting_indices: Vec<u64>,
139 pub data: AttestationData,
141 pub signature: BlsSignature,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
149#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
150pub struct ProposerSlashing {
151 pub signed_header_1: SignedBeaconBlockHeader,
153 pub signed_header_2: SignedBeaconBlockHeader,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
161#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
162pub struct SignedBeaconBlockHeader {
163 pub message: BeaconBlockHeader,
165 pub signature: BlsSignature,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
174pub struct AttesterSlashing {
175 pub attestation_1: IndexedAttestation,
177 pub attestation_2: IndexedAttestation,
179}
180
181#[serde_as]
185#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
187pub struct DepositData {
188 pub pubkey: BlsPublicKey,
190 pub withdrawal_credentials: B256,
192 #[serde_as(as = "DisplayFromStr")]
194 pub amount: u64,
195 pub signature: BlsSignature,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
203#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
204pub struct Deposit {
205 pub proof: Vec<B256>,
207 pub data: DepositData,
209}
210
211#[serde_as]
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
217pub struct VoluntaryExit {
218 #[serde_as(as = "DisplayFromStr")]
220 pub epoch: u64,
221 #[serde_as(as = "DisplayFromStr")]
223 pub validator_index: u64,
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
230#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
231pub struct SignedVoluntaryExit {
232 pub message: VoluntaryExit,
234 pub signature: BlsSignature,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
242#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
243pub struct SyncAggregate {
244 pub sync_committee_bits: Bytes,
246 pub sync_committee_signature: BlsSignature,
248}
249
250#[serde_as]
254#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
255#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
256pub struct BlsToExecutionChange {
257 #[serde_as(as = "DisplayFromStr")]
259 pub validator_index: u64,
260 pub from_bls_pubkey: BlsPublicKey,
262 pub to_execution_address: alloy_primitives::Address,
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
271pub struct SignedBlsToExecutionChange {
272 pub message: BlsToExecutionChange,
274 pub signature: BlsSignature,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
282#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
283pub struct BeaconBlockBodyPhase0 {
284 pub randao_reveal: BlsSignature,
286 pub eth1_data: Eth1Data,
288 pub graffiti: B256,
290 pub proposer_slashings: Vec<ProposerSlashing>,
292 pub attester_slashings: Vec<AttesterSlashing>,
294 pub attestations: Vec<Attestation>,
296 pub deposits: Vec<Deposit>,
298 pub voluntary_exits: Vec<SignedVoluntaryExit>,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
306#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
307pub struct BeaconBlockBodyAltair {
308 pub randao_reveal: BlsSignature,
310 pub eth1_data: Eth1Data,
312 pub graffiti: B256,
314 pub proposer_slashings: Vec<ProposerSlashing>,
316 pub attester_slashings: Vec<AttesterSlashing>,
318 pub attestations: Vec<Attestation>,
320 pub deposits: Vec<Deposit>,
322 pub voluntary_exits: Vec<SignedVoluntaryExit>,
324 pub sync_aggregate: SyncAggregate,
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
332pub struct BeaconBlockBodyBellatrix<T = serde_json::Value> {
333 pub randao_reveal: BlsSignature,
335 pub eth1_data: Eth1Data,
337 pub graffiti: B256,
339 pub proposer_slashings: Vec<ProposerSlashing>,
341 pub attester_slashings: Vec<AttesterSlashing>,
343 pub attestations: Vec<Attestation>,
345 pub deposits: Vec<Deposit>,
347 pub voluntary_exits: Vec<SignedVoluntaryExit>,
349 pub sync_aggregate: SyncAggregate,
351 pub execution_payload: T,
353}
354
355#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
359pub struct BeaconBlockBodyCapella<T = serde_json::Value> {
360 pub randao_reveal: BlsSignature,
362 pub eth1_data: Eth1Data,
364 pub graffiti: B256,
366 pub proposer_slashings: Vec<ProposerSlashing>,
368 pub attester_slashings: Vec<AttesterSlashing>,
370 pub attestations: Vec<Attestation>,
372 pub deposits: Vec<Deposit>,
374 pub voluntary_exits: Vec<SignedVoluntaryExit>,
376 pub sync_aggregate: SyncAggregate,
378 pub execution_payload: T,
380 pub bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
382}
383
384#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
388pub struct BeaconBlockBodyDeneb<T = serde_json::Value> {
389 pub randao_reveal: BlsSignature,
391 pub eth1_data: Eth1Data,
393 pub graffiti: B256,
395 pub proposer_slashings: Vec<ProposerSlashing>,
397 pub attester_slashings: Vec<AttesterSlashing>,
399 pub attestations: Vec<Attestation>,
401 pub deposits: Vec<Deposit>,
403 pub voluntary_exits: Vec<SignedVoluntaryExit>,
405 pub sync_aggregate: SyncAggregate,
407 pub execution_payload: T,
409 pub bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
411 pub blob_kzg_commitments: Vec<Bytes>,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
419pub struct BeaconBlockBodyElectra<T = serde_json::Value> {
420 pub randao_reveal: BlsSignature,
422 pub eth1_data: Eth1Data,
424 pub graffiti: B256,
426 pub proposer_slashings: Vec<ProposerSlashing>,
428 pub attester_slashings: Vec<AttesterSlashing>,
430 pub attestations: Vec<Attestation>,
432 pub deposits: Vec<Deposit>,
434 pub voluntary_exits: Vec<SignedVoluntaryExit>,
436 pub sync_aggregate: SyncAggregate,
438 pub execution_payload: T,
440 pub bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
442 pub blob_kzg_commitments: Vec<Bytes>,
444 #[serde(default, skip_serializing_if = "Option::is_none")]
446 pub execution_requests: Option<serde_json::Value>,
447}
448
449pub type SignedBeaconBlockPhase0 = SignedBeaconBlock<BeaconBlockBodyPhase0>;
451pub type SignedBeaconBlockAltair = SignedBeaconBlock<BeaconBlockBodyAltair>;
453pub type SignedBeaconBlockBellatrix<T = serde_json::Value> =
455 SignedBeaconBlock<BeaconBlockBodyBellatrix<T>>;
456pub type SignedBeaconBlockCapella<T = serde_json::Value> =
458 SignedBeaconBlock<BeaconBlockBodyCapella<T>>;
459pub type SignedBeaconBlockDeneb<T = serde_json::Value> = SignedBeaconBlock<BeaconBlockBodyDeneb<T>>;
461pub type SignedBeaconBlockElectra<T = serde_json::Value> =
463 SignedBeaconBlock<BeaconBlockBodyElectra<T>>;
464
465#[cfg(test)]
466mod tests {
467 use super::*;
468
469 #[test]
470 fn serde_block_response_phase0() {
471 let s = r#"{
472 "version": "phase0",
473 "execution_optimistic": false,
474 "finalized": true,
475 "data": {
476 "message": {
477 "slot": "1",
478 "proposer_index": "1",
479 "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
480 "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
481 "body": {
482 "randao_reveal": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
483 "eth1_data": {
484 "deposit_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
485 "deposit_count": "1",
486 "block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
487 },
488 "graffiti": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
489 "proposer_slashings": [],
490 "attester_slashings": [],
491 "attestations": [],
492 "deposits": [],
493 "voluntary_exits": []
494 }
495 },
496 "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
497 }
498 }"#;
499 let resp: BlockResponse<BeaconBlockBodyPhase0> = serde_json::from_str(s).unwrap();
500 assert_eq!(resp.version, "phase0");
501 assert!(resp.finalized);
502 assert_eq!(resp.data.message.slot, 1);
503 }
504
505 #[test]
506 fn serde_block_response_altair() {
507 let s = r#"{
508 "version": "altair",
509 "execution_optimistic": false,
510 "finalized": true,
511 "data": {
512 "message": {
513 "slot": "100",
514 "proposer_index": "42",
515 "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
516 "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
517 "body": {
518 "randao_reveal": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
519 "eth1_data": {
520 "deposit_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
521 "deposit_count": "100",
522 "block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
523 },
524 "graffiti": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
525 "proposer_slashings": [],
526 "attester_slashings": [],
527 "attestations": [],
528 "deposits": [],
529 "voluntary_exits": [],
530 "sync_aggregate": {
531 "sync_committee_bits": "0x01",
532 "sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
533 }
534 }
535 },
536 "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
537 }
538 }"#;
539 let resp: BlockResponse<BeaconBlockBodyAltair> = serde_json::from_str(s).unwrap();
540 assert_eq!(resp.version, "altair");
541 assert_eq!(resp.data.message.slot, 100);
542 assert_eq!(resp.data.message.proposer_index, 42);
543 }
544
545 #[test]
546 fn serde_signed_beacon_block_generic() {
547 let s = r#"{
548 "message": {
549 "slot": "12225729",
550 "proposer_index": "496520",
551 "parent_root": "0x462f4abf9b6881724e6489085b3bb3931312e31ffb43f7cec3d0ee624dc2b58e",
552 "state_root": "0x2c6e3ff0b0f7bc33b30a020e75e69c2bba26fb42a7e234e8275e655170925a71",
553 "body": {
554 "randao_reveal": "0x825dc181628713b55f40ed3f489be0c60f0513f88eecb25c7aa512ad24b912b3929bdf1930b50af4c18fb8b5f490352218a1c25adc01f7c3aaa50f982d762f589b4f5b6806e1d37e3f70af7afe990d1b1e8e337ac67b53bb7896f2052ecfccc1",
555 "eth1_data": {
556 "deposit_root": "0x2ebc563cabdbbacbc56f0de1d2d1c2d5315a4b071fcd8566aabbf0a45161c64e",
557 "deposit_count": "2045305",
558 "block_hash": "0x0958d83550263ff0d9f9a0bc5ea3cd2a136e0933b6f43cbb17f36e4da8d809b1"
559 },
560 "graffiti": "0x52502d4e502076312e31372e3000000000000000000000000000000000000000",
561 "proposer_slashings": [],
562 "attester_slashings": [],
563 "attestations": [],
564 "deposits": [],
565 "voluntary_exits": [],
566 "sync_aggregate": {
567 "sync_committee_bits": "0x71b7f7596e64ef7f7ef4f938e9f68abfbfe95bff09393315bb93bbec7f7ef27effa4c7f25ba7cbdb87efbbf73fdaebb9efefeb3ef7fff8effafdd7aff5677bfc",
568 "sync_committee_signature": "0xb45afdccf46b3518c295407594d82fcfd7fbff767f1b7bb2e7c9bdc8a0229232d201247b449d4bddf01fc974ce0b57601987fb401bb346062e53981cfb81dd6f9c519d645248a46ceba695c2d9630cfc68b26efc35f6ca14c49af9170581ad90"
569 },
570 "execution_payload": {}
571 }
572 },
573 "signature": "0x8a9cfe747dbb5d6ee1538638b2adfc304c8bcbeb03f489756ca7dc7a12081df892f38b924d19c9f5530c746b86a34beb019070bb7707de5a8efc8bdab8ca5668d7bb0e31c5ffd24913d23c80a6f6f70ba89e280dd46d19d6128ac7f42ffee93e"
574 }"#;
575 let block: SignedBeaconBlock = serde_json::from_str(s).unwrap();
576 assert_eq!(block.message.slot, 12225729);
577 assert_eq!(block.message.proposer_index, 496520);
578 }
579
580 #[test]
581 fn serde_eth1_data() {
582 let s = r#"{
583 "deposit_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
584 "deposit_count": "1",
585 "block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
586 }"#;
587 let eth1_data: Eth1Data = serde_json::from_str(s).unwrap();
588 assert_eq!(eth1_data.deposit_count, 1);
589 }
590
591 #[test]
592 fn serde_attestation_data() {
593 let s = r#"{
594 "slot": "1",
595 "index": "1",
596 "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
597 "source": {
598 "epoch": "1",
599 "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
600 },
601 "target": {
602 "epoch": "1",
603 "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
604 }
605 }"#;
606 let data: AttestationData = serde_json::from_str(s).unwrap();
607 assert_eq!(data.slot, 1);
608 assert_eq!(data.index, 1);
609 }
610
611 #[test]
612 fn serde_voluntary_exit() {
613 let s = r#"{
614 "message": {
615 "epoch": "1",
616 "validator_index": "1"
617 },
618 "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
619 }"#;
620 let exit: SignedVoluntaryExit = serde_json::from_str(s).unwrap();
621 assert_eq!(exit.message.epoch, 1);
622 assert_eq!(exit.message.validator_index, 1);
623 }
624
625 #[test]
626 fn serde_sync_aggregate() {
627 let s = r#"{
628 "sync_committee_bits": "0x01",
629 "sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
630 }"#;
631 let _aggregate: SyncAggregate = serde_json::from_str(s).unwrap();
632 }
633
634 #[test]
635 fn serde_bls_to_execution_change() {
636 let s = r#"{
637 "message": {
638 "validator_index": "1",
639 "from_bls_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
640 "to_execution_address": "0xabcf8e0d4e9587369b2301d0790347320302cc09"
641 },
642 "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
643 }"#;
644 let change: SignedBlsToExecutionChange = serde_json::from_str(s).unwrap();
645 assert_eq!(change.message.validator_index, 1);
646 }
647
648 #[test]
649 fn serde_proposer_slashing() {
650 let s = r#"{
651 "signed_header_1": {
652 "message": {
653 "slot": "1",
654 "proposer_index": "1",
655 "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
656 "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
657 "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
658 },
659 "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
660 },
661 "signed_header_2": {
662 "message": {
663 "slot": "1",
664 "proposer_index": "1",
665 "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
666 "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
667 "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
668 },
669 "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
670 }
671 }"#;
672 let slashing: ProposerSlashing = serde_json::from_str(s).unwrap();
673 assert_eq!(slashing.signed_header_1.message.slot, 1);
674 }
675
676 #[cfg(feature = "ssz")]
677 mod ssz_tests {
678 use super::*;
679 use ssz::{Decode, Encode};
680
681 #[test]
682 fn ssz_roundtrip_eth1_data() {
683 let eth1_data = Eth1Data {
684 deposit_root: B256::repeat_byte(0x11),
685 deposit_count: 42,
686 block_hash: B256::repeat_byte(0x22),
687 };
688 let encoded = eth1_data.as_ssz_bytes();
689 let decoded = Eth1Data::from_ssz_bytes(&encoded).unwrap();
690 assert_eq!(eth1_data, decoded);
691 }
692
693 #[test]
694 fn ssz_roundtrip_checkpoint() {
695 let checkpoint = Checkpoint { epoch: 100, root: B256::repeat_byte(0x33) };
696 let encoded = checkpoint.as_ssz_bytes();
697 let decoded = Checkpoint::from_ssz_bytes(&encoded).unwrap();
698 assert_eq!(checkpoint, decoded);
699 }
700
701 #[test]
702 fn ssz_roundtrip_attestation_data() {
703 let data = AttestationData {
704 slot: 1000,
705 index: 5,
706 beacon_block_root: B256::repeat_byte(0x44),
707 source: Checkpoint { epoch: 10, root: B256::repeat_byte(0x55) },
708 target: Checkpoint { epoch: 11, root: B256::repeat_byte(0x66) },
709 };
710 let encoded = data.as_ssz_bytes();
711 let decoded = AttestationData::from_ssz_bytes(&encoded).unwrap();
712 assert_eq!(data, decoded);
713 }
714
715 #[test]
716 fn ssz_roundtrip_voluntary_exit() {
717 let exit = VoluntaryExit { epoch: 50, validator_index: 123 };
718 let encoded = exit.as_ssz_bytes();
719 let decoded = VoluntaryExit::from_ssz_bytes(&encoded).unwrap();
720 assert_eq!(exit, decoded);
721 }
722
723 #[test]
724 fn ssz_roundtrip_signed_voluntary_exit() {
725 use crate::BlsSignature;
726 let exit = SignedVoluntaryExit {
727 message: VoluntaryExit { epoch: 50, validator_index: 123 },
728 signature: BlsSignature::repeat_byte(0x77),
729 };
730 let encoded = exit.as_ssz_bytes();
731 let decoded = SignedVoluntaryExit::from_ssz_bytes(&encoded).unwrap();
732 assert_eq!(exit, decoded);
733 }
734
735 #[test]
736 fn ssz_roundtrip_deposit_data() {
737 use crate::BlsPublicKey;
738 let data = DepositData {
739 pubkey: BlsPublicKey::repeat_byte(0x88),
740 withdrawal_credentials: B256::repeat_byte(0x99),
741 amount: 32_000_000_000,
742 signature: crate::BlsSignature::repeat_byte(0xaa),
743 };
744 let encoded = data.as_ssz_bytes();
745 let decoded = DepositData::from_ssz_bytes(&encoded).unwrap();
746 assert_eq!(data, decoded);
747 }
748
749 #[test]
750 fn ssz_roundtrip_bls_to_execution_change() {
751 use crate::BlsPublicKey;
752 let change = BlsToExecutionChange {
753 validator_index: 456,
754 from_bls_pubkey: BlsPublicKey::repeat_byte(0xbb),
755 to_execution_address: alloy_primitives::Address::repeat_byte(0xcc),
756 };
757 let encoded = change.as_ssz_bytes();
758 let decoded = BlsToExecutionChange::from_ssz_bytes(&encoded).unwrap();
759 assert_eq!(change, decoded);
760 }
761
762 #[test]
763 fn ssz_roundtrip_signed_bls_to_execution_change() {
764 use crate::{BlsPublicKey, BlsSignature};
765 let change = SignedBlsToExecutionChange {
766 message: BlsToExecutionChange {
767 validator_index: 789,
768 from_bls_pubkey: BlsPublicKey::repeat_byte(0xdd),
769 to_execution_address: alloy_primitives::Address::repeat_byte(0xee),
770 },
771 signature: BlsSignature::repeat_byte(0xff),
772 };
773 let encoded = change.as_ssz_bytes();
774 let decoded = SignedBlsToExecutionChange::from_ssz_bytes(&encoded).unwrap();
775 assert_eq!(change, decoded);
776 }
777
778 #[test]
779 fn ssz_roundtrip_sync_aggregate() {
780 let aggregate = SyncAggregate {
781 sync_committee_bits: Bytes::from_static(&[0x01, 0x02, 0x03]),
782 sync_committee_signature: crate::BlsSignature::repeat_byte(0x11),
783 };
784 let encoded = aggregate.as_ssz_bytes();
785 let decoded = SyncAggregate::from_ssz_bytes(&encoded).unwrap();
786 assert_eq!(aggregate, decoded);
787 }
788
789 #[test]
790 fn ssz_roundtrip_beacon_block_body_phase0() {
791 let body = BeaconBlockBodyPhase0 {
792 randao_reveal: crate::BlsSignature::repeat_byte(0x22),
793 eth1_data: Eth1Data {
794 deposit_root: B256::repeat_byte(0x33),
795 deposit_count: 100,
796 block_hash: B256::repeat_byte(0x44),
797 },
798 graffiti: B256::repeat_byte(0x55),
799 proposer_slashings: vec![],
800 attester_slashings: vec![],
801 attestations: vec![],
802 deposits: vec![],
803 voluntary_exits: vec![],
804 };
805 let encoded = body.as_ssz_bytes();
806 let decoded = BeaconBlockBodyPhase0::from_ssz_bytes(&encoded).unwrap();
807 assert_eq!(body, decoded);
808 }
809
810 #[test]
811 fn ssz_roundtrip_beacon_block_body_altair() {
812 let body = BeaconBlockBodyAltair {
813 randao_reveal: crate::BlsSignature::repeat_byte(0x66),
814 eth1_data: Eth1Data {
815 deposit_root: B256::repeat_byte(0x77),
816 deposit_count: 200,
817 block_hash: B256::repeat_byte(0x88),
818 },
819 graffiti: B256::repeat_byte(0x99),
820 proposer_slashings: vec![],
821 attester_slashings: vec![],
822 attestations: vec![],
823 deposits: vec![],
824 voluntary_exits: vec![],
825 sync_aggregate: SyncAggregate {
826 sync_committee_bits: Bytes::from_static(&[0xaa]),
827 sync_committee_signature: crate::BlsSignature::repeat_byte(0xbb),
828 },
829 };
830 let encoded = body.as_ssz_bytes();
831 let decoded = BeaconBlockBodyAltair::from_ssz_bytes(&encoded).unwrap();
832 assert_eq!(body, decoded);
833 }
834 }
835}