diff --git a/Cargo.lock b/Cargo.lock index 69332e109..f365e260c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,6 +685,8 @@ dependencies = [ "futures-util", "mev-share-sse", "reth", + "serde", + "serde_json", "tokio", "tracing", ] diff --git a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events.rs b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events.rs deleted file mode 100644 index fca3fb876..000000000 --- a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Support for the Beacon API events -//! -//! See also [ethereum-beacon-API eventstream](https://ethereum.github.io/beacon-APIs/#/Events/eventstream) - -use crate::engine::PayloadAttributes; -use alloy_primitives::B256; -use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, DisplayFromStr}; - -/// Event for the `payload_attributes` topic of the beacon API node event stream. -/// -/// This event gives block builders and relays sufficient information to construct or verify a block -/// at `proposal_slot`. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PayloadAttributesEvent { - /// the identifier of the beacon hard fork at `proposal_slot`, e.g `"bellatrix"`, `"capella"`. - pub version: String, - /// Wrapped data of the event. - pub data: PayloadAttributesData, -} - -impl PayloadAttributesEvent { - /// Returns the payload attributes - pub fn attributes(&self) -> &PayloadAttributes { - &self.data.payload_attributes - } -} - -/// Data of the event that contains the payload attributes -#[serde_as] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct PayloadAttributesData { - /// The slot at which a block using these payload attributes may be built - #[serde_as(as = "DisplayFromStr")] - pub proposal_slot: u64, - /// the beacon block root of the parent block to be built upon. - pub parent_block_root: B256, - /// he execution block number of the parent block. - #[serde_as(as = "DisplayFromStr")] - pub parent_block_number: u64, - /// the execution block hash of the parent block. - pub parent_block_hash: B256, - /// The execution block number of the parent block. - /// the validator index of the proposer at `proposal_slot` on the chain identified by - /// `parent_block_root`. - #[serde_as(as = "DisplayFromStr")] - pub proposer_index: u64, - /// Beacon API encoding of `PayloadAttributesV` as defined by the `execution-apis` - /// specification - /// - /// Note: this uses the beacon API format which uses snake-case and quoted decimals rather than - /// big-endian hex. - #[serde(with = "crate::eth::engine::payload::beacon_api_payload_attributes")] - pub payload_attributes: PayloadAttributes, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn serde_payload_attributes_event() { - let s = r#"{"version":"capella","data":{"proposal_slot":"173332","proposer_index":"649112","parent_block_root":"0x5a49069647f6bf8f25d76b55ce920947654ade4ba1c6ab826d16712dd62b42bf","parent_block_number":"161093","parent_block_hash":"0x608b3d140ecb5bbcd0019711ac3704ece7be8e6d100816a55db440c1bcbb0251","payload_attributes":{"timestamp":"1697982384","prev_randao":"0x3142abd98055871ebf78f0f8e758fd3a04df3b6e34d12d09114f37a737f8f01e","suggested_fee_recipient":"0x0000000000000000000000000000000000000001","withdrawals":[{"index":"2461612","validator_index":"853570","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"45016211"},{"index":"2461613","validator_index":"853571","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5269785"},{"index":"2461614","validator_index":"853572","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5275106"},{"index":"2461615","validator_index":"853573","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5235962"},{"index":"2461616","validator_index":"853574","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5252171"},{"index":"2461617","validator_index":"853575","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5221319"},{"index":"2461618","validator_index":"853576","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5260879"},{"index":"2461619","validator_index":"853577","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5285244"},{"index":"2461620","validator_index":"853578","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5266681"},{"index":"2461621","validator_index":"853579","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5271322"},{"index":"2461622","validator_index":"853580","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5231327"},{"index":"2461623","validator_index":"853581","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5276761"},{"index":"2461624","validator_index":"853582","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5246244"},{"index":"2461625","validator_index":"853583","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5261011"},{"index":"2461626","validator_index":"853584","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5276477"},{"index":"2461627","validator_index":"853585","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5275319"}]}}}"#; - - let event = serde_json::from_str::(s).unwrap(); - let input = serde_json::from_str::(s).unwrap(); - let json = serde_json::to_value(event).unwrap(); - assert_eq!(input, json); - } -} diff --git a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/attestation.rs b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/attestation.rs new file mode 100644 index 000000000..c789a4671 --- /dev/null +++ b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/attestation.rs @@ -0,0 +1,30 @@ +use alloy_primitives::B256; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AttestationData { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + #[serde_as(as = "DisplayFromStr")] + pub index: u64, + pub beacon_block_root: B256, + pub source: Source, + pub target: Target, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Source { + #[serde_as(as = "DisplayFromStr")] + pub epoch: u64, + pub root: B256, +} +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Target { + #[serde_as(as = "DisplayFromStr")] + pub epoch: u64, + pub root: B256, +} diff --git a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/light_client_finality.rs b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/light_client_finality.rs new file mode 100644 index 000000000..10928c7a7 --- /dev/null +++ b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/light_client_finality.rs @@ -0,0 +1,54 @@ +use alloy_primitives::{Bytes, B256}; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct LightClientFinalityData { + pub attested_header: AttestedHeader, + pub finalized_header: FinalizedHeader, + pub finality_branch: Vec, + pub sync_aggregate: SyncAggregate, + #[serde_as(as = "DisplayFromStr")] + pub signature_slot: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AttestedHeader { + pub beacon: Beacon, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Beacon { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + #[serde_as(as = "DisplayFromStr")] + pub proposer_index: u64, + pub parent_root: B256, + pub state_root: B256, + pub body_root: B256, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct FinalizedHeader { + pub beacon: Beacon2, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Beacon2 { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + #[serde_as(as = "DisplayFromStr")] + pub proposer_index: u64, + pub parent_root: B256, + pub state_root: B256, + pub body_root: B256, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SyncAggregate { + pub sync_committee_bits: Bytes, + pub sync_committee_signature: Bytes, +} diff --git a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/light_client_optimistic.rs b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/light_client_optimistic.rs new file mode 100644 index 000000000..31f676a7b --- /dev/null +++ b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/light_client_optimistic.rs @@ -0,0 +1,35 @@ +use alloy_primitives::{Bytes, B256}; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct LightClientOptimisticData { + pub attested_header: AttestedHeader, + pub sync_aggregate: SyncAggregate, + #[serde_as(as = "DisplayFromStr")] + pub signature_slot: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AttestedHeader { + pub beacon: Beacon, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Beacon { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + #[serde_as(as = "DisplayFromStr")] + pub proposer_index: u64, + pub parent_root: B256, + pub state_root: B256, + pub body_root: B256, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SyncAggregate { + pub sync_committee_bits: Bytes, + pub sync_committee_signature: Bytes, +} diff --git a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs new file mode 100644 index 000000000..d4f1bc8f4 --- /dev/null +++ b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs @@ -0,0 +1,403 @@ +//! Support for the Beacon API events +//! +//! See also [ethereum-beacon-API eventstream](https://ethereum.github.io/beacon-APIs/#/Events/eventstream) + +use crate::engine::PayloadAttributes; +use alloy_primitives::{Address, Bytes, B256}; +use attestation::AttestationData; +use light_client_finality::LightClientFinalityData; +use light_client_optimistic::LightClientOptimisticData; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; + +pub mod attestation; +pub mod light_client_finality; +pub mod light_client_optimistic; + +/// Topic variant for the eventstream API +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BeaconNodeEventTopic { + PayloadAttributes, + Head, + Block, + Attestation, + VoluntaryExit, + BlsToExecutionChange, + FinalizedCheckpoint, + ChainReorg, + ContributionAndProof, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + BlobSidecar, +} + +impl BeaconNodeEventTopic { + /// Returns the identifier value for the eventstream query + pub fn query_value(&self) -> &'static str { + match self { + BeaconNodeEventTopic::PayloadAttributes => "payload_attributes", + BeaconNodeEventTopic::Head => "head", + BeaconNodeEventTopic::Block => "block", + BeaconNodeEventTopic::Attestation => "attestation", + BeaconNodeEventTopic::VoluntaryExit => "voluntary_exit", + BeaconNodeEventTopic::BlsToExecutionChange => "bls_to_execution_change", + BeaconNodeEventTopic::FinalizedCheckpoint => "finalized_checkpoint", + BeaconNodeEventTopic::ChainReorg => "chain_reorg", + BeaconNodeEventTopic::ContributionAndProof => "contribution_and_proof", + BeaconNodeEventTopic::LightClientFinalityUpdate => "light_client_finality_update", + BeaconNodeEventTopic::LightClientOptimisticUpdate => "light_client_optimistic_update", + BeaconNodeEventTopic::BlobSidecar => "blob_sidecar", + } + } +} + +/// Event for the `payload_attributes` topic of the beacon API node event stream. +/// +/// This event gives block builders and relays sufficient information to construct or verify a block +/// at `proposal_slot`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PayloadAttributesEvent { + /// the identifier of the beacon hard fork at `proposal_slot`, e.g `"bellatrix"`, `"capella"`. + pub version: String, + /// Wrapped data of the event. + pub data: PayloadAttributesData, +} + +/// Event for the `Head` topic of the beacon API node event stream. +/// +/// The node has finished processing, resulting in a new head. previous_duty_dependent_root is +/// \`get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch - 1) - 1)\` and +/// current_duty_dependent_root is \`get_block_root_at_slot(state, +/// compute_start_slot_at_epoch(epoch) +/// - 1)\`. Both dependent roots use the genesis block root in the case of underflow. +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct HeadEvent { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + pub block: B256, + pub state: B256, + pub epoch_transition: bool, + pub previous_duty_dependent_root: B256, + pub current_duty_dependent_root: B256, + pub execution_optimistic: bool, +} + +/// Event for the `Block` topic of the beacon API node event stream. +/// +/// The node has received a valid block (from P2P or API) +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlockEvent { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + pub block: B256, + pub execution_optimistic: bool, +} + +/// Event for the `Attestation` topic of the beacon API node event stream. +/// +/// The node has received a valid attestation (from P2P or API) +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AttestationEvent { + pub aggregation_bits: Bytes, + pub signature: Bytes, + pub data: AttestationData, +} + +/// Event for the `VoluntaryExit` topic of the beacon API node event stream. +/// +/// The node has received a valid voluntary exit (from P2P or API) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct VoluntaryExitEvent { + pub message: VoluntaryExitMessage, + pub signature: Bytes, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct VoluntaryExitMessage { + #[serde_as(as = "DisplayFromStr")] + pub epoch: u64, + #[serde_as(as = "DisplayFromStr")] + pub validator_index: u64, +} + +/// Event for the `BlsToExecutionChange` topic of the beacon API node event stream. +/// +/// The node has received a BLS to execution change (from P2P or API) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlsToExecutionChangeEvent { + pub message: BlsToExecutionChangeMessage, + pub signature: Bytes, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlsToExecutionChangeMessage { + #[serde_as(as = "DisplayFromStr")] + pub validator_index: u64, + pub from_bls_pubkey: String, + pub to_execution_address: Address, +} + +/// Event for the `Deposit` topic of the beacon API node event stream. +/// +/// Finalized checkpoint has been updated +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct FinalizedCheckpointEvent { + pub block: B256, + pub state: B256, + #[serde_as(as = "DisplayFromStr")] + pub epoch: u64, + pub execution_optimistic: bool, +} + +/// Event for the `ChainReorg` topic of the beacon API node event stream. +/// +/// The node has reorganized its chain +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ChainReorgEvent { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + #[serde_as(as = "DisplayFromStr")] + pub depth: u64, + pub old_head_block: B256, + pub new_head_block: B256, + pub old_head_state: B256, + pub new_head_state: B256, + #[serde_as(as = "DisplayFromStr")] + pub epoch: u64, + pub execution_optimistic: bool, +} + +/// Event for the `ContributionAndProof` topic of the beacon API node event stream. +/// +/// The node has received a valid sync committee SignedContributionAndProof (from P2P or API) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ContributionAndProofEvent { + pub message: ContributionAndProofMessage, + pub signature: Bytes, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ContributionAndProofMessage { + #[serde_as(as = "DisplayFromStr")] + pub aggregator_index: u64, + pub contribution: Contribution, + pub selection_proof: Bytes, +} + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Contribution { + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + pub beacon_block_root: B256, + #[serde_as(as = "DisplayFromStr")] + pub subcommittee_index: u64, + pub aggregation_bits: Bytes, + pub signature: Bytes, +} + +/// Event for the `LightClientFinalityUpdate` topic of the beacon API node event stream. +/// +/// The node's latest known `LightClientFinalityUpdate` has been updated +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct LightClientFinalityUpdateEvent { + pub version: String, + pub data: LightClientFinalityData, +} + +/// Event for the `LightClientOptimisticUpdate` topic of the beacon API node event stream. +/// +/// The node's latest known `LightClientOptimisticUpdate` has been updated +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct LightClientOptimisticUpdateEvent { + pub version: String, + pub data: LightClientOptimisticData, +} + +/// Event for the `BlobSidecar` topic of the beacon API node event stream. +/// +/// The node has received a BlobSidecar (from P2P or API) that passes all gossip validations on the +/// blob_sidecar_{subnet_id} topic +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlobSidecarEvent { + pub block_root: B256, + #[serde_as(as = "DisplayFromStr")] + pub index: u64, + #[serde_as(as = "DisplayFromStr")] + pub slot: u64, + pub kzg_commitment: Bytes, + pub versioned_hash: B256, +} + +impl PayloadAttributesEvent { + /// Returns the payload attributes + pub fn attributes(&self) -> &PayloadAttributes { + &self.data.payload_attributes + } +} + +/// Data of the event that contains the payload attributes +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PayloadAttributesData { + /// The slot at which a block using these payload attributes may be built + #[serde_as(as = "DisplayFromStr")] + pub proposal_slot: u64, + /// the beacon block root of the parent block to be built upon. + pub parent_block_root: B256, + /// the execution block number of the parent block. + #[serde_as(as = "DisplayFromStr")] + pub parent_block_number: u64, + /// the execution block hash of the parent block. + pub parent_block_hash: B256, + /// The execution block number of the parent block. + /// the validator index of the proposer at `proposal_slot` on the chain identified by + /// `parent_block_root`. + #[serde_as(as = "DisplayFromStr")] + pub proposer_index: u64, + /// Beacon API encoding of `PayloadAttributesV` as defined by the `execution-apis` + /// specification + /// + /// Note: this uses the beacon API format which uses snake-case and quoted decimals rather than + /// big-endian hex. + #[serde(with = "crate::eth::engine::payload::beacon_api_payload_attributes")] + pub payload_attributes: PayloadAttributes, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serde_payload_attributes_event() { + let s = r#"{"version":"capella","data":{"proposal_slot":"173332","proposer_index":"649112","parent_block_root":"0x5a49069647f6bf8f25d76b55ce920947654ade4ba1c6ab826d16712dd62b42bf","parent_block_number":"161093","parent_block_hash":"0x608b3d140ecb5bbcd0019711ac3704ece7be8e6d100816a55db440c1bcbb0251","payload_attributes":{"timestamp":"1697982384","prev_randao":"0x3142abd98055871ebf78f0f8e758fd3a04df3b6e34d12d09114f37a737f8f01e","suggested_fee_recipient":"0x0000000000000000000000000000000000000001","withdrawals":[{"index":"2461612","validator_index":"853570","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"45016211"},{"index":"2461613","validator_index":"853571","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5269785"},{"index":"2461614","validator_index":"853572","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5275106"},{"index":"2461615","validator_index":"853573","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5235962"},{"index":"2461616","validator_index":"853574","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5252171"},{"index":"2461617","validator_index":"853575","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5221319"},{"index":"2461618","validator_index":"853576","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5260879"},{"index":"2461619","validator_index":"853577","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5285244"},{"index":"2461620","validator_index":"853578","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5266681"},{"index":"2461621","validator_index":"853579","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5271322"},{"index":"2461622","validator_index":"853580","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5231327"},{"index":"2461623","validator_index":"853581","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5276761"},{"index":"2461624","validator_index":"853582","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5246244"},{"index":"2461625","validator_index":"853583","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5261011"},{"index":"2461626","validator_index":"853584","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5276477"},{"index":"2461627","validator_index":"853585","address":"0x778f5f13c4be78a3a4d7141bcb26999702f407cf","amount":"5275319"}]}}}"#; + + let event: PayloadAttributesEvent = + serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + #[test] + fn serde_head_event() { + let s = r#"{"slot":"10", "block":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", "state":"0x600e852a08c1200654ddf11025f1ceacb3c2e74bdd5c630cde0838b2591b69f9", "epoch_transition":false, "previous_duty_dependent_root":"0x5e0043f107cb57913498fbf2f99ff55e730bf1e151f02f221e977c91a90a0e91", "current_duty_dependent_root":"0x5e0043f107cb57913498fbf2f99ff55e730bf1e151f02f221e977c91a90a0e91", "execution_optimistic": false}"#; + + let event: HeadEvent = serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_block_event() { + let s = r#"{"slot":"10", "block":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", "execution_optimistic": false}"#; + + let event: BlockEvent = serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + #[test] + fn serde_attestation_event() { + let s = r#"{"aggregation_bits":"0x01", "signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", "data":{"slot":"1", "index":"1", "beacon_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "source":{"epoch":"1", "root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}, "target":{"epoch":"1", "root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}}}"#; + + let event: AttestationEvent = serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_voluntary_exit_event() { + let s = r#"{"message":{"epoch":"1", "validator_index":"1"}, "signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; + + let event: VoluntaryExitEvent = serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_bls_to_execution_change_event() { + let s = r#"{"message":{"validator_index":"1", "from_bls_pubkey":"0x933ad9491b62059dd065b560d256d8957a8c402cc6e8d8ee7290ae11e8f7329267a8811c397529dac52ae1342ba58c95", "to_execution_address":"0x9be8d619c56699667c1fedcd15f6b14d8b067f72"}, "signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; + + let event: BlsToExecutionChangeEvent = + serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_finalize_checkpoint_event() { + let s = r#"{"block":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", "state":"0x600e852a08c1200654ddf11025f1ceacb3c2e74bdd5c630cde0838b2591b69f9", "epoch":"2", "execution_optimistic": false }"#; + + let event: FinalizedCheckpointEvent = + serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_chain_reorg_event() { + let s = r#"{"slot":"200", "depth":"50", "old_head_block":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", "new_head_block":"0x76262e91970d375a19bfe8a867288d7b9cde43c8635f598d93d39d041706fc76", "old_head_state":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", "new_head_state":"0x600e852a08c1200654ddf11025f1ceacb3c2e74bdd5c630cde0838b2591b69f9", "epoch":"2", "execution_optimistic": false}"#; + + let event: ChainReorgEvent = serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_contribution_and_proof_event() { + let s = r#"{"message": {"aggregator_index": "997", "contribution": {"slot": "168097", "beacon_block_root": "0x56f1fd4262c08fa81e27621c370e187e621a67fc80fe42340b07519f84b42ea1", "subcommittee_index": "0", "aggregation_bits": "0xffffffffffffffffffffffffffffffff", "signature": "0x85ab9018e14963026476fdf784cc674da144b3dbdb47516185438768774f077d882087b90ad642469902e782a8b43eed0cfc1b862aa9a473b54c98d860424a702297b4b648f3f30bdaae8a8b7627d10d04cb96a2cc8376af3e54a9aa0c8145e3"}, "selection_proof": "0x87c305f04bfe5db27c2b19fc23e00d7ac496ec7d3e759cbfdd1035cb8cf6caaa17a36a95a08ba78c282725e7b66a76820ca4eb333822bd399ceeb9807a0f2926c67ce67cfe06a0b0006838203b493505a8457eb79913ce1a3bcd1cc8e4ef30ed"}, "signature": "0xac118511474a94f857300b315c50585c32a713e4452e26a6bb98cdb619936370f126ed3b6bb64469259ee92e69791d9e12d324ce6fd90081680ce72f39d85d50b0ff977260a8667465e613362c6d6e6e745e1f9323ec1d6f16041c4e358839ac"}"#; + + let event: ContributionAndProofEvent = + serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_light_client_finality_update_event() { + let s = r#"{"version":"phase0", "data": {"attested_header": {"beacon": {"slot":"1", "proposer_index":"1", "parent_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "body_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}}, "finalized_header": {"beacon": {"slot":"1", "proposer_index":"1", "parent_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "body_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}}, "finality_branch": ["0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"], "sync_aggregate": {"sync_committee_bits":"0x01", "sync_committee_signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}, "signature_slot":"1"}}"#; + + let event: LightClientFinalityUpdateEvent = + serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + #[test] + fn serde_light_client_optimistic_update_event() { + let s = r#"{"version":"phase0", "data": {"attested_header": {"beacon": {"slot":"1", "proposer_index":"1", "parent_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "body_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}}, "sync_aggregate": {"sync_committee_bits":"0x01", "sync_committee_signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}, "signature_slot":"1"}}"#; + + let event: LightClientOptimisticUpdateEvent = + serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } + + #[test] + fn serde_blob_sidecar_event() { + let s = r#"{"block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "index": "1", "slot": "1", "kzg_commitment": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", "versioned_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}"#; + + let event: BlobSidecarEvent = serde_json::from_str::(s).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + let json = serde_json::to_value(event).unwrap(); + assert_eq!(input, json); + } +} diff --git a/examples/beacon-api-sse/Cargo.toml b/examples/beacon-api-sse/Cargo.toml index 8f2f2c611..892c2e229 100644 --- a/examples/beacon-api-sse/Cargo.toml +++ b/examples/beacon-api-sse/Cargo.toml @@ -9,6 +9,8 @@ license.workspace = true reth.workspace = true eyre.workspace = true clap.workspace = true +serde.workspace = true +serde_json.workspace = true tracing.workspace = true futures-util.workspace = true tokio = { workspace = true, features = ["time"] }