mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-17 03:42:46 +00:00
https://github.com/sigp/lighthouse/issues/8012 Replace all instances of `VariableList::from` and `FixedVector::from` to their `try_from` variants. While I tried to use proper error handling in most cases, there were certain situations where adding an `expect` for situations where `try_from` can trivially never fail avoided adding a lot of extra complexity. Co-Authored-By: Mac L <mjladson@pm.me> Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com> Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
1252 lines
47 KiB
Rust
1252 lines
47 KiB
Rust
use super::*;
|
|
use alloy_rlp::RlpEncodable;
|
|
use serde::{Deserialize, Serialize};
|
|
use ssz::{Decode, TryFromIter};
|
|
use ssz_types::{FixedVector, VariableList, typenum::Unsigned};
|
|
use strum::EnumString;
|
|
use superstruct::superstruct;
|
|
use types::beacon_block_body::KzgCommitments;
|
|
use types::blob_sidecar::BlobsList;
|
|
use types::execution_requests::{
|
|
ConsolidationRequests, DepositRequests, RequestType, WithdrawalRequests,
|
|
};
|
|
use types::{Blob, KzgProof};
|
|
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonRequestBody<'a> {
|
|
pub jsonrpc: &'a str,
|
|
pub method: &'a str,
|
|
pub params: serde_json::Value,
|
|
pub id: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
pub struct JsonError {
|
|
pub code: i64,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonResponseBody {
|
|
pub jsonrpc: String,
|
|
#[serde(default)]
|
|
pub error: Option<JsonError>,
|
|
#[serde(default)]
|
|
pub result: serde_json::Value,
|
|
pub id: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
#[serde(transparent)]
|
|
pub struct TransparentJsonPayloadId(#[serde(with = "serde_utils::bytes_8_hex")] pub PayloadId);
|
|
|
|
impl From<PayloadId> for TransparentJsonPayloadId {
|
|
fn from(id: PayloadId) -> Self {
|
|
Self(id)
|
|
}
|
|
}
|
|
|
|
impl From<TransparentJsonPayloadId> for PayloadId {
|
|
fn from(wrapper: TransparentJsonPayloadId) -> Self {
|
|
wrapper.0
|
|
}
|
|
}
|
|
|
|
/// On the request, use a transparent wrapper.
|
|
pub type JsonPayloadIdRequest = TransparentJsonPayloadId;
|
|
|
|
/// On the response, expect without the object wrapper (non-transparent).
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonPayloadIdResponse {
|
|
#[serde(with = "serde_utils::bytes_8_hex")]
|
|
pub payload_id: PayloadId,
|
|
}
|
|
|
|
#[superstruct(
|
|
variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas),
|
|
variant_attributes(
|
|
derive(Debug, PartialEq, Default, Serialize, Deserialize,),
|
|
serde(bound = "E: EthSpec", rename_all = "camelCase"),
|
|
),
|
|
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
|
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
|
)]
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(bound = "E: EthSpec", rename_all = "camelCase", untagged)]
|
|
pub struct JsonExecutionPayload<E: EthSpec> {
|
|
pub parent_hash: ExecutionBlockHash,
|
|
#[serde(with = "serde_utils::address_hex")]
|
|
pub fee_recipient: Address,
|
|
pub state_root: Hash256,
|
|
pub receipts_root: Hash256,
|
|
#[serde(with = "serde_logs_bloom")]
|
|
pub logs_bloom: FixedVector<u8, E::BytesPerLogsBloom>,
|
|
pub prev_randao: Hash256,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub block_number: u64,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub gas_limit: u64,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub gas_used: u64,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub timestamp: u64,
|
|
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
|
pub extra_data: VariableList<u8, E::MaxExtraDataBytes>,
|
|
#[serde(with = "serde_utils::u256_hex_be")]
|
|
pub base_fee_per_gas: Uint256,
|
|
|
|
pub block_hash: ExecutionBlockHash,
|
|
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
|
pub transactions: Transactions<E>,
|
|
#[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))]
|
|
pub withdrawals: VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>,
|
|
#[superstruct(only(Deneb, Electra, Fulu, Gloas))]
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub blob_gas_used: u64,
|
|
#[superstruct(only(Deneb, Electra, Fulu, Gloas))]
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub excess_blob_gas: u64,
|
|
}
|
|
|
|
impl<E: EthSpec> From<ExecutionPayloadBellatrix<E>> for JsonExecutionPayloadBellatrix<E> {
|
|
fn from(payload: ExecutionPayloadBellatrix<E>) -> Self {
|
|
JsonExecutionPayloadBellatrix {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
}
|
|
}
|
|
}
|
|
impl<E: EthSpec> TryFrom<ExecutionPayloadCapella<E>> for JsonExecutionPayloadCapella<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: ExecutionPayloadCapella<E>) -> Result<Self, Self::Error> {
|
|
Ok(JsonExecutionPayloadCapella {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_to_json(payload.withdrawals)?,
|
|
})
|
|
}
|
|
}
|
|
impl<E: EthSpec> TryFrom<ExecutionPayloadDeneb<E>> for JsonExecutionPayloadDeneb<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: ExecutionPayloadDeneb<E>) -> Result<Self, Self::Error> {
|
|
Ok(JsonExecutionPayloadDeneb {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_to_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<ExecutionPayloadElectra<E>> for JsonExecutionPayloadElectra<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: ExecutionPayloadElectra<E>) -> Result<Self, Self::Error> {
|
|
Ok(JsonExecutionPayloadElectra {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_to_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<ExecutionPayloadFulu<E>> for JsonExecutionPayloadFulu<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: ExecutionPayloadFulu<E>) -> Result<Self, Self::Error> {
|
|
Ok(JsonExecutionPayloadFulu {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_to_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<ExecutionPayloadGloas<E>> for JsonExecutionPayloadGloas<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: ExecutionPayloadGloas<E>) -> Result<Self, Self::Error> {
|
|
Ok(JsonExecutionPayloadGloas {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_to_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<ExecutionPayload<E>> for JsonExecutionPayload<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(execution_payload: ExecutionPayload<E>) -> Result<Self, Self::Error> {
|
|
match execution_payload {
|
|
ExecutionPayload::Bellatrix(payload) => {
|
|
Ok(JsonExecutionPayload::Bellatrix(payload.into()))
|
|
}
|
|
ExecutionPayload::Capella(payload) => {
|
|
Ok(JsonExecutionPayload::Capella(payload.try_into()?))
|
|
}
|
|
ExecutionPayload::Deneb(payload) => {
|
|
Ok(JsonExecutionPayload::Deneb(payload.try_into()?))
|
|
}
|
|
ExecutionPayload::Electra(payload) => {
|
|
Ok(JsonExecutionPayload::Electra(payload.try_into()?))
|
|
}
|
|
ExecutionPayload::Fulu(payload) => Ok(JsonExecutionPayload::Fulu(payload.try_into()?)),
|
|
ExecutionPayload::Gloas(payload) => {
|
|
Ok(JsonExecutionPayload::Gloas(payload.try_into()?))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> From<JsonExecutionPayloadBellatrix<E>> for ExecutionPayloadBellatrix<E> {
|
|
fn from(payload: JsonExecutionPayloadBellatrix<E>) -> Self {
|
|
ExecutionPayloadBellatrix {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
}
|
|
}
|
|
}
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadCapella<E>> for ExecutionPayloadCapella<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: JsonExecutionPayloadCapella<E>) -> Result<Self, Self::Error> {
|
|
Ok(ExecutionPayloadCapella {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_from_json(payload.withdrawals)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadDeneb<E>> for ExecutionPayloadDeneb<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: JsonExecutionPayloadDeneb<E>) -> Result<Self, Self::Error> {
|
|
Ok(ExecutionPayloadDeneb {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_from_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadElectra<E>> for ExecutionPayloadElectra<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: JsonExecutionPayloadElectra<E>) -> Result<Self, Self::Error> {
|
|
Ok(ExecutionPayloadElectra {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_from_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadFulu<E>> for ExecutionPayloadFulu<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: JsonExecutionPayloadFulu<E>) -> Result<Self, Self::Error> {
|
|
Ok(ExecutionPayloadFulu {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_from_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadGloas<E>> for ExecutionPayloadGloas<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(payload: JsonExecutionPayloadGloas<E>) -> Result<Self, Self::Error> {
|
|
Ok(ExecutionPayloadGloas {
|
|
parent_hash: payload.parent_hash,
|
|
fee_recipient: payload.fee_recipient,
|
|
state_root: payload.state_root,
|
|
receipts_root: payload.receipts_root,
|
|
logs_bloom: payload.logs_bloom,
|
|
prev_randao: payload.prev_randao,
|
|
block_number: payload.block_number,
|
|
gas_limit: payload.gas_limit,
|
|
gas_used: payload.gas_used,
|
|
timestamp: payload.timestamp,
|
|
extra_data: payload.extra_data,
|
|
base_fee_per_gas: payload.base_fee_per_gas,
|
|
block_hash: payload.block_hash,
|
|
transactions: payload.transactions,
|
|
withdrawals: withdrawals_from_json(payload.withdrawals)?,
|
|
blob_gas_used: payload.blob_gas_used,
|
|
excess_blob_gas: payload.excess_blob_gas,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayload<E>> for ExecutionPayload<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(json_execution_payload: JsonExecutionPayload<E>) -> Result<Self, Self::Error> {
|
|
match json_execution_payload {
|
|
JsonExecutionPayload::Bellatrix(payload) => {
|
|
Ok(ExecutionPayload::Bellatrix(payload.into()))
|
|
}
|
|
JsonExecutionPayload::Capella(payload) => {
|
|
Ok(ExecutionPayload::Capella(payload.try_into()?))
|
|
}
|
|
JsonExecutionPayload::Deneb(payload) => {
|
|
Ok(ExecutionPayload::Deneb(payload.try_into()?))
|
|
}
|
|
JsonExecutionPayload::Electra(payload) => {
|
|
Ok(ExecutionPayload::Electra(payload.try_into()?))
|
|
}
|
|
JsonExecutionPayload::Fulu(payload) => Ok(ExecutionPayload::Fulu(payload.try_into()?)),
|
|
JsonExecutionPayload::Gloas(payload) => {
|
|
Ok(ExecutionPayload::Gloas(payload.try_into()?))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum RequestsError {
|
|
InvalidHex(hex::FromHexError),
|
|
EmptyRequest(usize),
|
|
InvalidOrdering,
|
|
InvalidPrefix(u8),
|
|
DecodeError(String),
|
|
}
|
|
|
|
/// Format of `ExecutionRequests` received over the engine api.
|
|
///
|
|
/// Array of ssz-encoded requests list encoded as hex bytes prefixed
|
|
/// with a `RequestType`
|
|
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(transparent)]
|
|
pub struct JsonExecutionRequests(pub Vec<String>);
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionRequests> for ExecutionRequests<E> {
|
|
type Error = RequestsError;
|
|
|
|
fn try_from(value: JsonExecutionRequests) -> Result<Self, Self::Error> {
|
|
let mut requests = ExecutionRequests::default();
|
|
let mut prev_prefix: Option<RequestType> = None;
|
|
for (i, request) in value.0.into_iter().enumerate() {
|
|
// hex string
|
|
let decoded_bytes = hex::decode(request.strip_prefix("0x").unwrap_or(&request))
|
|
.map_err(RequestsError::InvalidHex)?;
|
|
|
|
// The first byte of each element is the `request_type` and the remaining bytes are the `request_data`.
|
|
// Elements with empty `request_data` **MUST** be excluded from the list.
|
|
let Some((prefix_byte, request_bytes)) = decoded_bytes.split_first() else {
|
|
return Err(RequestsError::EmptyRequest(i));
|
|
};
|
|
if request_bytes.is_empty() {
|
|
return Err(RequestsError::EmptyRequest(i));
|
|
}
|
|
// Elements of the list **MUST** be ordered by `request_type` in ascending order
|
|
let current_prefix = RequestType::from_u8(*prefix_byte)
|
|
.ok_or(RequestsError::InvalidPrefix(*prefix_byte))?;
|
|
if let Some(prev) = prev_prefix
|
|
&& prev.to_u8() >= current_prefix.to_u8()
|
|
{
|
|
return Err(RequestsError::InvalidOrdering);
|
|
}
|
|
prev_prefix = Some(current_prefix);
|
|
|
|
match current_prefix {
|
|
RequestType::Deposit => {
|
|
requests.deposits = DepositRequests::<E>::from_ssz_bytes(request_bytes)
|
|
.map_err(|e| {
|
|
RequestsError::DecodeError(format!(
|
|
"Failed to decode DepositRequest from EL: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
}
|
|
RequestType::Withdrawal => {
|
|
requests.withdrawals = WithdrawalRequests::<E>::from_ssz_bytes(request_bytes)
|
|
.map_err(|e| {
|
|
RequestsError::DecodeError(format!(
|
|
"Failed to decode WithdrawalRequest from EL: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
}
|
|
RequestType::Consolidation => {
|
|
requests.consolidations =
|
|
ConsolidationRequests::<E>::from_ssz_bytes(request_bytes).map_err(|e| {
|
|
RequestsError::DecodeError(format!(
|
|
"Failed to decode ConsolidationRequest from EL: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
}
|
|
}
|
|
}
|
|
Ok(requests)
|
|
}
|
|
}
|
|
|
|
#[superstruct(
|
|
variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas),
|
|
variant_attributes(
|
|
derive(Debug, PartialEq, Serialize, Deserialize),
|
|
serde(bound = "E: EthSpec", rename_all = "camelCase")
|
|
),
|
|
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
|
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
|
)]
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub struct JsonGetPayloadResponse<E: EthSpec> {
|
|
#[superstruct(
|
|
only(Bellatrix),
|
|
partial_getter(rename = "execution_payload_bellatrix")
|
|
)]
|
|
pub execution_payload: JsonExecutionPayloadBellatrix<E>,
|
|
#[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))]
|
|
pub execution_payload: JsonExecutionPayloadCapella<E>,
|
|
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
|
|
pub execution_payload: JsonExecutionPayloadDeneb<E>,
|
|
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
|
pub execution_payload: JsonExecutionPayloadElectra<E>,
|
|
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
|
|
pub execution_payload: JsonExecutionPayloadFulu<E>,
|
|
#[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))]
|
|
pub execution_payload: JsonExecutionPayloadGloas<E>,
|
|
#[serde(with = "serde_utils::u256_hex_be")]
|
|
pub block_value: Uint256,
|
|
#[superstruct(only(Deneb, Electra, Fulu, Gloas))]
|
|
pub blobs_bundle: JsonBlobsBundleV1<E>,
|
|
#[superstruct(only(Deneb, Electra, Fulu, Gloas))]
|
|
pub should_override_builder: bool,
|
|
#[superstruct(only(Electra, Fulu, Gloas))]
|
|
pub execution_requests: JsonExecutionRequests,
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
|
|
type Error = String;
|
|
fn try_from(json_get_payload_response: JsonGetPayloadResponse<E>) -> Result<Self, Self::Error> {
|
|
match json_get_payload_response {
|
|
JsonGetPayloadResponse::Bellatrix(response) => {
|
|
Ok(GetPayloadResponse::Bellatrix(GetPayloadResponseBellatrix {
|
|
execution_payload: response.execution_payload.into(),
|
|
block_value: response.block_value,
|
|
}))
|
|
}
|
|
JsonGetPayloadResponse::Capella(response) => {
|
|
Ok(GetPayloadResponse::Capella(GetPayloadResponseCapella {
|
|
execution_payload: response.execution_payload.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution payload: {:?}", e)
|
|
})?,
|
|
block_value: response.block_value,
|
|
}))
|
|
}
|
|
JsonGetPayloadResponse::Deneb(response) => {
|
|
Ok(GetPayloadResponse::Deneb(GetPayloadResponseDeneb {
|
|
execution_payload: response.execution_payload.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution payload: {:?}", e)
|
|
})?,
|
|
block_value: response.block_value,
|
|
blobs_bundle: response.blobs_bundle.into(),
|
|
should_override_builder: response.should_override_builder,
|
|
}))
|
|
}
|
|
JsonGetPayloadResponse::Electra(response) => {
|
|
Ok(GetPayloadResponse::Electra(GetPayloadResponseElectra {
|
|
execution_payload: response.execution_payload.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution payload: {:?}", e)
|
|
})?,
|
|
block_value: response.block_value,
|
|
blobs_bundle: response.blobs_bundle.into(),
|
|
should_override_builder: response.should_override_builder,
|
|
requests: response.execution_requests.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution requests: {:?}", e)
|
|
})?,
|
|
}))
|
|
}
|
|
JsonGetPayloadResponse::Fulu(response) => {
|
|
Ok(GetPayloadResponse::Fulu(GetPayloadResponseFulu {
|
|
execution_payload: response.execution_payload.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution payload: {:?}", e)
|
|
})?,
|
|
block_value: response.block_value,
|
|
blobs_bundle: response.blobs_bundle.into(),
|
|
should_override_builder: response.should_override_builder,
|
|
requests: response.execution_requests.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution requests: {:?}", e)
|
|
})?,
|
|
}))
|
|
}
|
|
JsonGetPayloadResponse::Gloas(response) => {
|
|
Ok(GetPayloadResponse::Gloas(GetPayloadResponseGloas {
|
|
execution_payload: response.execution_payload.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution payload: {:?}", e)
|
|
})?,
|
|
block_value: response.block_value,
|
|
blobs_bundle: response.blobs_bundle.into(),
|
|
should_override_builder: response.should_override_builder,
|
|
requests: response.execution_requests.try_into().map_err(|e| {
|
|
format!("Failed to convert json to execution requests: {:?}", e)
|
|
})?,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonWithdrawal {
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub index: u64,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub validator_index: u64,
|
|
#[serde(with = "serde_utils::address_hex")]
|
|
pub address: Address,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub amount: u64,
|
|
}
|
|
|
|
impl From<Withdrawal> for JsonWithdrawal {
|
|
fn from(withdrawal: Withdrawal) -> Self {
|
|
Self {
|
|
index: withdrawal.index,
|
|
validator_index: withdrawal.validator_index,
|
|
address: withdrawal.address,
|
|
amount: withdrawal.amount,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<JsonWithdrawal> for Withdrawal {
|
|
fn from(jw: JsonWithdrawal) -> Self {
|
|
Self {
|
|
index: jw.index,
|
|
validator_index: jw.validator_index,
|
|
address: jw.address,
|
|
amount: jw.amount,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper functions to convert between `VariableList<Withdrawal>` and `VariableList<JsonWithdrawal>`.
|
|
fn withdrawals_to_json<N>(
|
|
list: VariableList<Withdrawal, N>,
|
|
) -> Result<VariableList<JsonWithdrawal, N>, ssz_types::Error>
|
|
where
|
|
N: Unsigned,
|
|
{
|
|
VariableList::try_from_iter(list.into_iter().map(Into::into))
|
|
}
|
|
|
|
fn withdrawals_from_json<N>(
|
|
list: VariableList<JsonWithdrawal, N>,
|
|
) -> Result<VariableList<Withdrawal, N>, ssz_types::Error>
|
|
where
|
|
N: Unsigned,
|
|
{
|
|
VariableList::try_from_iter(list.into_iter().map(Into::into))
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, RlpEncodable)]
|
|
pub struct EncodableJsonWithdrawal<'a> {
|
|
pub index: u64,
|
|
pub validator_index: u64,
|
|
pub address: &'a [u8],
|
|
pub amount: u64,
|
|
}
|
|
|
|
impl<'a> From<&'a JsonWithdrawal> for EncodableJsonWithdrawal<'a> {
|
|
fn from(json_withdrawal: &'a JsonWithdrawal) -> Self {
|
|
Self {
|
|
index: json_withdrawal.index,
|
|
validator_index: json_withdrawal.validator_index,
|
|
address: json_withdrawal.address.as_slice(),
|
|
amount: json_withdrawal.amount,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[superstruct(
|
|
variants(V1, V2, V3),
|
|
variant_attributes(
|
|
derive(Debug, Clone, PartialEq, Serialize, Deserialize),
|
|
serde(rename_all = "camelCase")
|
|
),
|
|
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
|
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
|
)]
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub struct JsonPayloadAttributes {
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub timestamp: u64,
|
|
pub prev_randao: Hash256,
|
|
#[serde(with = "serde_utils::address_hex")]
|
|
pub suggested_fee_recipient: Address,
|
|
#[superstruct(only(V2, V3))]
|
|
pub withdrawals: Vec<JsonWithdrawal>,
|
|
#[superstruct(only(V3))]
|
|
pub parent_beacon_block_root: Hash256,
|
|
}
|
|
|
|
impl From<PayloadAttributes> for JsonPayloadAttributes {
|
|
fn from(payload_attributes: PayloadAttributes) -> Self {
|
|
match payload_attributes {
|
|
PayloadAttributes::V1(pa) => Self::V1(JsonPayloadAttributesV1 {
|
|
timestamp: pa.timestamp,
|
|
prev_randao: pa.prev_randao,
|
|
suggested_fee_recipient: pa.suggested_fee_recipient,
|
|
}),
|
|
PayloadAttributes::V2(pa) => Self::V2(JsonPayloadAttributesV2 {
|
|
timestamp: pa.timestamp,
|
|
prev_randao: pa.prev_randao,
|
|
suggested_fee_recipient: pa.suggested_fee_recipient,
|
|
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
|
|
}),
|
|
PayloadAttributes::V3(pa) => Self::V3(JsonPayloadAttributesV3 {
|
|
timestamp: pa.timestamp,
|
|
prev_randao: pa.prev_randao,
|
|
suggested_fee_recipient: pa.suggested_fee_recipient,
|
|
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
|
|
parent_beacon_block_root: pa.parent_beacon_block_root,
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<JsonPayloadAttributes> for PayloadAttributes {
|
|
fn from(json_payload_attributes: JsonPayloadAttributes) -> Self {
|
|
match json_payload_attributes {
|
|
JsonPayloadAttributes::V1(jpa) => Self::V1(PayloadAttributesV1 {
|
|
timestamp: jpa.timestamp,
|
|
prev_randao: jpa.prev_randao,
|
|
suggested_fee_recipient: jpa.suggested_fee_recipient,
|
|
}),
|
|
JsonPayloadAttributes::V2(jpa) => Self::V2(PayloadAttributesV2 {
|
|
timestamp: jpa.timestamp,
|
|
prev_randao: jpa.prev_randao,
|
|
suggested_fee_recipient: jpa.suggested_fee_recipient,
|
|
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
|
|
}),
|
|
JsonPayloadAttributes::V3(jpa) => Self::V3(PayloadAttributesV3 {
|
|
timestamp: jpa.timestamp,
|
|
prev_randao: jpa.prev_randao,
|
|
suggested_fee_recipient: jpa.suggested_fee_recipient,
|
|
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
|
|
parent_beacon_block_root: jpa.parent_beacon_block_root,
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(bound = "E: EthSpec", rename_all = "camelCase")]
|
|
pub struct JsonBlobsBundleV1<E: EthSpec> {
|
|
pub commitments: KzgCommitments<E>,
|
|
pub proofs: KzgProofs<E>,
|
|
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
|
|
pub blobs: BlobsList<E>,
|
|
}
|
|
|
|
impl<E: EthSpec> From<BlobsBundle<E>> for JsonBlobsBundleV1<E> {
|
|
fn from(blobs_bundle: BlobsBundle<E>) -> Self {
|
|
Self {
|
|
commitments: blobs_bundle.commitments,
|
|
proofs: blobs_bundle.proofs,
|
|
blobs: blobs_bundle.blobs,
|
|
}
|
|
}
|
|
}
|
|
impl<E: EthSpec> From<JsonBlobsBundleV1<E>> for BlobsBundle<E> {
|
|
fn from(json_blobs_bundle: JsonBlobsBundleV1<E>) -> Self {
|
|
Self {
|
|
commitments: json_blobs_bundle.commitments,
|
|
proofs: json_blobs_bundle.proofs,
|
|
blobs: json_blobs_bundle.blobs,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[superstruct(
|
|
variants(V1, V2),
|
|
variant_attributes(
|
|
derive(Debug, Clone, PartialEq, Serialize, Deserialize),
|
|
serde(bound = "E: EthSpec", rename_all = "camelCase")
|
|
)
|
|
)]
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct BlobAndProof<E: EthSpec> {
|
|
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
|
|
pub blob: Blob<E>,
|
|
/// KZG proof for the blob (Deneb)
|
|
#[superstruct(only(V1))]
|
|
pub proof: KzgProof,
|
|
/// KZG cell proofs for the extended blob (PeerDAS)
|
|
#[superstruct(only(V2))]
|
|
pub proofs: KzgProofs<E>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonForkchoiceStateV1 {
|
|
pub head_block_hash: ExecutionBlockHash,
|
|
|
|
pub safe_block_hash: ExecutionBlockHash,
|
|
|
|
pub finalized_block_hash: ExecutionBlockHash,
|
|
}
|
|
|
|
impl From<ForkchoiceState> for JsonForkchoiceStateV1 {
|
|
fn from(f: ForkchoiceState) -> Self {
|
|
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
|
let ForkchoiceState {
|
|
head_block_hash,
|
|
safe_block_hash,
|
|
finalized_block_hash,
|
|
} = f;
|
|
|
|
Self {
|
|
head_block_hash,
|
|
safe_block_hash,
|
|
finalized_block_hash,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<JsonForkchoiceStateV1> for ForkchoiceState {
|
|
fn from(j: JsonForkchoiceStateV1) -> Self {
|
|
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
|
let JsonForkchoiceStateV1 {
|
|
head_block_hash,
|
|
safe_block_hash,
|
|
finalized_block_hash,
|
|
} = j;
|
|
|
|
Self {
|
|
head_block_hash,
|
|
safe_block_hash,
|
|
finalized_block_hash,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, EnumString)]
|
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
|
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
|
|
pub enum JsonPayloadStatusV1Status {
|
|
Valid,
|
|
Invalid,
|
|
Syncing,
|
|
Accepted,
|
|
InvalidBlockHash,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonPayloadStatusV1 {
|
|
pub status: JsonPayloadStatusV1Status,
|
|
pub latest_valid_hash: Option<ExecutionBlockHash>,
|
|
pub validation_error: Option<String>,
|
|
}
|
|
|
|
impl From<PayloadStatusV1Status> for JsonPayloadStatusV1Status {
|
|
fn from(e: PayloadStatusV1Status) -> Self {
|
|
match e {
|
|
PayloadStatusV1Status::Valid => JsonPayloadStatusV1Status::Valid,
|
|
PayloadStatusV1Status::Invalid => JsonPayloadStatusV1Status::Invalid,
|
|
PayloadStatusV1Status::Syncing => JsonPayloadStatusV1Status::Syncing,
|
|
PayloadStatusV1Status::Accepted => JsonPayloadStatusV1Status::Accepted,
|
|
PayloadStatusV1Status::InvalidBlockHash => JsonPayloadStatusV1Status::InvalidBlockHash,
|
|
}
|
|
}
|
|
}
|
|
impl From<JsonPayloadStatusV1Status> for PayloadStatusV1Status {
|
|
fn from(j: JsonPayloadStatusV1Status) -> Self {
|
|
match j {
|
|
JsonPayloadStatusV1Status::Valid => PayloadStatusV1Status::Valid,
|
|
JsonPayloadStatusV1Status::Invalid => PayloadStatusV1Status::Invalid,
|
|
JsonPayloadStatusV1Status::Syncing => PayloadStatusV1Status::Syncing,
|
|
JsonPayloadStatusV1Status::Accepted => PayloadStatusV1Status::Accepted,
|
|
JsonPayloadStatusV1Status::InvalidBlockHash => PayloadStatusV1Status::InvalidBlockHash,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PayloadStatusV1> for JsonPayloadStatusV1 {
|
|
fn from(p: PayloadStatusV1) -> Self {
|
|
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
|
let PayloadStatusV1 {
|
|
status,
|
|
latest_valid_hash,
|
|
validation_error,
|
|
} = p;
|
|
|
|
Self {
|
|
status: status.into(),
|
|
latest_valid_hash,
|
|
validation_error,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<JsonPayloadStatusV1> for PayloadStatusV1 {
|
|
fn from(j: JsonPayloadStatusV1) -> Self {
|
|
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
|
let JsonPayloadStatusV1 {
|
|
status,
|
|
latest_valid_hash,
|
|
validation_error,
|
|
} = j;
|
|
|
|
Self {
|
|
status: status.into(),
|
|
latest_valid_hash,
|
|
validation_error,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonForkchoiceUpdatedV1Response {
|
|
pub payload_status: JsonPayloadStatusV1,
|
|
pub payload_id: Option<TransparentJsonPayloadId>,
|
|
}
|
|
|
|
impl From<JsonForkchoiceUpdatedV1Response> for ForkchoiceUpdatedResponse {
|
|
fn from(j: JsonForkchoiceUpdatedV1Response) -> Self {
|
|
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
|
let JsonForkchoiceUpdatedV1Response {
|
|
payload_status: status,
|
|
payload_id,
|
|
} = j;
|
|
|
|
Self {
|
|
payload_status: status.into(),
|
|
payload_id: payload_id.map(Into::into),
|
|
}
|
|
}
|
|
}
|
|
impl From<ForkchoiceUpdatedResponse> for JsonForkchoiceUpdatedV1Response {
|
|
fn from(f: ForkchoiceUpdatedResponse) -> Self {
|
|
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
|
let ForkchoiceUpdatedResponse {
|
|
payload_status: status,
|
|
payload_id,
|
|
} = f;
|
|
|
|
Self {
|
|
payload_status: status.into(),
|
|
payload_id: payload_id.map(Into::into),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
#[serde(bound = "E: EthSpec")]
|
|
pub struct JsonExecutionPayloadBodyV1<E: EthSpec> {
|
|
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
|
pub transactions: Transactions<E>,
|
|
pub withdrawals: Option<VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>>,
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadBodyV1<E>> for ExecutionPayloadBodyV1<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(value: JsonExecutionPayloadBodyV1<E>) -> Result<Self, Self::Error> {
|
|
Ok(Self {
|
|
transactions: value.transactions,
|
|
withdrawals: value.withdrawals.map(withdrawals_from_json).transpose()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> TryFrom<ExecutionPayloadBodyV1<E>> for JsonExecutionPayloadBodyV1<E> {
|
|
type Error = ssz_types::Error;
|
|
|
|
fn try_from(value: ExecutionPayloadBodyV1<E>) -> Result<Self, Self::Error> {
|
|
Ok(Self {
|
|
transactions: value.transactions,
|
|
withdrawals: value.withdrawals.map(withdrawals_to_json).transpose()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct TransitionConfigurationV1 {
|
|
#[serde(with = "serde_utils::u256_hex_be")]
|
|
pub terminal_total_difficulty: Uint256,
|
|
|
|
pub terminal_block_hash: ExecutionBlockHash,
|
|
#[serde(with = "serde_utils::u64_hex_be")]
|
|
pub terminal_block_number: u64,
|
|
}
|
|
|
|
/// Serializes the `logs_bloom` field of an `ExecutionPayload`.
|
|
pub mod serde_logs_bloom {
|
|
use super::*;
|
|
use serde::{Deserializer, Serializer};
|
|
use serde_utils::hex::PrefixedHexVisitor;
|
|
|
|
pub fn serialize<S, U>(bytes: &FixedVector<u8, U>, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
U: Unsigned,
|
|
{
|
|
let mut hex_string: String = "0x".to_string();
|
|
hex_string.push_str(&hex::encode(&bytes[..]));
|
|
|
|
serializer.serialize_str(&hex_string)
|
|
}
|
|
|
|
pub fn deserialize<'de, D, U>(deserializer: D) -> Result<FixedVector<u8, U>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
U: Unsigned,
|
|
{
|
|
let vec = deserializer.deserialize_string(PrefixedHexVisitor)?;
|
|
|
|
FixedVector::new(vec)
|
|
.map_err(|e| serde::de::Error::custom(format!("invalid logs bloom: {:?}", e)))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JsonClientVersionV1 {
|
|
pub code: String,
|
|
// This `default` is required until Geth v1.13.x is no longer supported on mainnet.
|
|
// See: https://github.com/ethereum/go-ethereum/pull/29351
|
|
#[serde(default)]
|
|
pub name: String,
|
|
pub version: String,
|
|
pub commit: String,
|
|
}
|
|
|
|
impl From<ClientVersionV1> for JsonClientVersionV1 {
|
|
fn from(client_version: ClientVersionV1) -> Self {
|
|
Self {
|
|
code: client_version.code.to_string(),
|
|
name: client_version.name,
|
|
version: client_version.version,
|
|
commit: client_version.commit.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<JsonClientVersionV1> for ClientVersionV1 {
|
|
type Error = String;
|
|
|
|
fn try_from(json: JsonClientVersionV1) -> Result<Self, Self::Error> {
|
|
Ok(Self {
|
|
code: json.code.try_into()?,
|
|
name: json.name,
|
|
version: json.version,
|
|
commit: json.commit.try_into()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use ssz::Encode;
|
|
use types::{
|
|
ConsolidationRequest, DepositRequest, MainnetEthSpec, PublicKeyBytes, RequestType,
|
|
SignatureBytes, WithdrawalRequest,
|
|
};
|
|
|
|
use super::*;
|
|
|
|
fn create_request_string<T: Encode>(prefix: u8, request_bytes: &T) -> String {
|
|
format!(
|
|
"0x{:02x}{}",
|
|
prefix,
|
|
hex::encode(request_bytes.as_ssz_bytes())
|
|
)
|
|
}
|
|
|
|
/// Tests all error conditions except ssz decoding errors
|
|
///
|
|
/// ***
|
|
/// Elements of the list MUST be ordered by request_type in ascending order.
|
|
/// Elements with empty request_data MUST be excluded from the list.
|
|
/// If any element is out of order, has a length of 1-byte or shorter,
|
|
/// or more than one element has the same type byte, client software MUST return -32602: Invalid params error.
|
|
/// ***
|
|
#[test]
|
|
fn test_invalid_execution_requests() {
|
|
let deposit_request = DepositRequest {
|
|
pubkey: PublicKeyBytes::empty(),
|
|
withdrawal_credentials: Hash256::random(),
|
|
amount: 32,
|
|
signature: SignatureBytes::empty(),
|
|
index: 0,
|
|
};
|
|
|
|
let consolidation_request = ConsolidationRequest {
|
|
source_address: Address::random(),
|
|
source_pubkey: PublicKeyBytes::empty(),
|
|
target_pubkey: PublicKeyBytes::empty(),
|
|
};
|
|
|
|
let withdrawal_request = WithdrawalRequest {
|
|
amount: 32,
|
|
source_address: Address::random(),
|
|
validator_pubkey: PublicKeyBytes::empty(),
|
|
};
|
|
|
|
// First check a valid request with all requests
|
|
assert!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
|
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
|
]))
|
|
.is_ok()
|
|
);
|
|
|
|
// Single requests
|
|
assert!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
]))
|
|
.is_ok()
|
|
);
|
|
|
|
assert!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
|
]))
|
|
.is_ok()
|
|
);
|
|
|
|
assert!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
|
]))
|
|
.is_ok()
|
|
);
|
|
|
|
// Out of order
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::InvalidOrdering
|
|
));
|
|
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
|
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::InvalidOrdering
|
|
));
|
|
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::InvalidOrdering
|
|
));
|
|
|
|
// Multiple requests of same type
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::InvalidOrdering
|
|
));
|
|
|
|
// Invalid prefix
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(42, &deposit_request),
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::InvalidPrefix(42)
|
|
));
|
|
|
|
// Prefix followed by no data
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
create_request_string(
|
|
RequestType::Consolidation.to_u8(),
|
|
&Vec::<ConsolidationRequest>::new()
|
|
),
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::EmptyRequest(1)
|
|
));
|
|
// Empty request
|
|
assert!(matches!(
|
|
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
|
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
|
"0x".to_string()
|
|
]))
|
|
.unwrap_err(),
|
|
RequestsError::EmptyRequest(1)
|
|
));
|
|
}
|
|
}
|