mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-28 10:13:37 +00:00
Add Electra fork boilerplate (#5122)
* Add Electra fork boilerplate * Remove electra from spec tests * Fix tests * Remove sneaky log file * Fix more tests * Fix even more tests and add suggestions * Remove unrelated lcli addition * Update more tests * Merge branch 'unstable' into electra * Add comment for test-suite lcli override * Merge branch 'unstable' into electra * Cleanup * Merge branch 'unstable' into electra * Apply suggestions * Merge branch 'unstable' into electra * Merge sigp/unstable into electra * Merge branch 'unstable' into electra
This commit is contained in:
@@ -236,4 +236,34 @@ mod test {
|
||||
.unwrap();
|
||||
test_rlp_encoding(&header, None, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rlp_encode_block_electra() {
|
||||
let header = ExecutionBlockHeader {
|
||||
parent_hash: Hash256::from_str("172864416698b842f4c92f7b476be294b4ef720202779df194cd225f531053ab").unwrap(),
|
||||
ommers_hash: Hash256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(),
|
||||
beneficiary: Address::from_str("878705ba3f8bc32fcf7f4caa1a35e72af65cf766").unwrap(),
|
||||
state_root: Hash256::from_str("c6457d0df85c84c62d1c68f68138b6e796e8a44fb44de221386fb2d5611c41e0").unwrap(),
|
||||
transactions_root: Hash256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap(),
|
||||
receipts_root: Hash256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap(),
|
||||
logs_bloom:<[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(),
|
||||
difficulty: 0.into(),
|
||||
number: 97.into(),
|
||||
gas_limit: 27482534.into(),
|
||||
gas_used: 0.into(),
|
||||
timestamp: 1692132829u64,
|
||||
extra_data: hex::decode("d883010d00846765746888676f312e32302e37856c696e7578").unwrap(),
|
||||
mix_hash: Hash256::from_str("0b493c22d2ad4ca76c77ae6ad916af429b42b1dc98fdcb8e5ddbd049bbc5d623").unwrap(),
|
||||
nonce: Hash64::zero(),
|
||||
base_fee_per_gas: 2374u64.into(),
|
||||
withdrawals_root: Some(Hash256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()),
|
||||
blob_gas_used: Some(0x0u64),
|
||||
excess_blob_gas: Some(0x0u64),
|
||||
parent_beacon_block_root: Some(Hash256::from_str("f7d327d2c04e4f12e9cdd492e53d39a1d390f8b1571e3b2a22ac6e1e170e5b1a").unwrap()),
|
||||
};
|
||||
let expected_hash =
|
||||
Hash256::from_str("a7448e600ead0a23d16f96aa46e8dea9eef8a7c5669a5f0a5ff32709afe9c408")
|
||||
.unwrap();
|
||||
test_rlp_encoding(&header, None, expected_hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,11 @@ pub use types::{
|
||||
ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList,
|
||||
Withdrawal, Withdrawals,
|
||||
};
|
||||
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, KzgProofs};
|
||||
|
||||
use types::{
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadMerge,
|
||||
KzgProofs,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
pub mod http;
|
||||
@@ -32,7 +36,8 @@ pub mod json_structures;
|
||||
mod new_payload_request;
|
||||
|
||||
pub use new_payload_request::{
|
||||
NewPayloadRequest, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestMerge,
|
||||
NewPayloadRequest, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestElectra,
|
||||
NewPayloadRequestMerge,
|
||||
};
|
||||
|
||||
pub const LATEST_TAG: &str = "latest";
|
||||
@@ -150,7 +155,7 @@ pub struct ExecutionBlock {
|
||||
|
||||
/// Representation of an execution block with enough detail to reconstruct a payload.
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variants(Merge, Capella, Deneb, Electra),
|
||||
variant_attributes(
|
||||
derive(Clone, Debug, PartialEq, Serialize, Deserialize,),
|
||||
serde(bound = "T: EthSpec", rename_all = "camelCase"),
|
||||
@@ -184,12 +189,12 @@ pub struct ExecutionBlockWithTransactions<T: EthSpec> {
|
||||
#[serde(rename = "hash")]
|
||||
pub block_hash: ExecutionBlockHash,
|
||||
pub transactions: Vec<Transaction>,
|
||||
#[superstruct(only(Capella, Deneb))]
|
||||
#[superstruct(only(Capella, Deneb, Electra))]
|
||||
pub withdrawals: Vec<JsonWithdrawal>,
|
||||
#[superstruct(only(Deneb))]
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub blob_gas_used: u64,
|
||||
#[superstruct(only(Deneb))]
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub excess_blob_gas: u64,
|
||||
}
|
||||
@@ -271,6 +276,34 @@ impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions
|
||||
blob_gas_used: block.blob_gas_used,
|
||||
excess_blob_gas: block.excess_blob_gas,
|
||||
}),
|
||||
ExecutionPayload::Electra(block) => {
|
||||
Self::Electra(ExecutionBlockWithTransactionsElectra {
|
||||
parent_hash: block.parent_hash,
|
||||
fee_recipient: block.fee_recipient,
|
||||
state_root: block.state_root,
|
||||
receipts_root: block.receipts_root,
|
||||
logs_bloom: block.logs_bloom,
|
||||
prev_randao: block.prev_randao,
|
||||
block_number: block.block_number,
|
||||
gas_limit: block.gas_limit,
|
||||
gas_used: block.gas_used,
|
||||
timestamp: block.timestamp,
|
||||
extra_data: block.extra_data,
|
||||
base_fee_per_gas: block.base_fee_per_gas,
|
||||
block_hash: block.block_hash,
|
||||
transactions: block
|
||||
.transactions
|
||||
.iter()
|
||||
.map(|tx| Transaction::decode(&Rlp::new(tx)))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
withdrawals: Vec::from(block.withdrawals)
|
||||
.into_iter()
|
||||
.map(|withdrawal| withdrawal.into())
|
||||
.collect(),
|
||||
blob_gas_used: block.blob_gas_used,
|
||||
excess_blob_gas: block.excess_blob_gas,
|
||||
})
|
||||
}
|
||||
};
|
||||
Ok(json_payload)
|
||||
}
|
||||
@@ -390,7 +423,7 @@ pub struct ProposeBlindedBlockResponse {
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variants(Merge, Capella, Deneb, Electra),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -405,10 +438,12 @@ pub struct GetPayloadResponse<T: EthSpec> {
|
||||
pub execution_payload: ExecutionPayloadCapella<T>,
|
||||
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
|
||||
pub execution_payload: ExecutionPayloadDeneb<T>,
|
||||
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
||||
pub execution_payload: ExecutionPayloadElectra<T>,
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(Deneb))]
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
pub blobs_bundle: BlobsBundle<T>,
|
||||
#[superstruct(only(Deneb), partial_getter(copy))]
|
||||
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
|
||||
pub should_override_builder: bool,
|
||||
}
|
||||
|
||||
@@ -462,6 +497,11 @@ impl<T: EthSpec> From<GetPayloadResponse<T>>
|
||||
inner.block_value,
|
||||
Some(inner.blobs_bundle),
|
||||
),
|
||||
GetPayloadResponse::Electra(inner) => (
|
||||
ExecutionPayload::Electra(inner.execution_payload),
|
||||
inner.block_value,
|
||||
Some(inner.blobs_bundle),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,7 +602,35 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
||||
}))
|
||||
} else {
|
||||
Err(format!(
|
||||
"block {} is post capella but payload body doesn't have withdrawals",
|
||||
"block {} is post-capella but payload body doesn't have withdrawals",
|
||||
header.block_hash
|
||||
))
|
||||
}
|
||||
}
|
||||
ExecutionPayloadHeader::Electra(header) => {
|
||||
if let Some(withdrawals) = self.withdrawals {
|
||||
Ok(ExecutionPayload::Electra(ExecutionPayloadElectra {
|
||||
parent_hash: header.parent_hash,
|
||||
fee_recipient: header.fee_recipient,
|
||||
state_root: header.state_root,
|
||||
receipts_root: header.receipts_root,
|
||||
logs_bloom: header.logs_bloom,
|
||||
prev_randao: header.prev_randao,
|
||||
block_number: header.block_number,
|
||||
gas_limit: header.gas_limit,
|
||||
gas_used: header.gas_used,
|
||||
timestamp: header.timestamp,
|
||||
extra_data: header.extra_data,
|
||||
base_fee_per_gas: header.base_fee_per_gas,
|
||||
block_hash: header.block_hash,
|
||||
transactions: self.transactions,
|
||||
withdrawals,
|
||||
blob_gas_used: header.blob_gas_used,
|
||||
excess_blob_gas: header.excess_blob_gas,
|
||||
}))
|
||||
} else {
|
||||
Err(format!(
|
||||
"block {} is post-capella but payload body doesn't have withdrawals",
|
||||
header.block_hash
|
||||
))
|
||||
}
|
||||
|
||||
@@ -740,6 +740,14 @@ impl HttpJsonRpc {
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
ForkName::Electra => ExecutionBlockWithTransactions::Electra(
|
||||
self.rpc_request(
|
||||
ETH_GET_BLOCK_BY_HASH,
|
||||
params,
|
||||
ETH_GET_BLOCK_BY_HASH_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
ForkName::Base | ForkName::Altair => {
|
||||
return Err(Error::UnsupportedForkVariant(format!(
|
||||
"called get_block_by_hash_with_txns with fork {:?}",
|
||||
@@ -783,7 +791,7 @@ impl HttpJsonRpc {
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn new_payload_v3<T: EthSpec>(
|
||||
pub async fn new_payload_v3_deneb<T: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_deneb: NewPayloadRequestDeneb<'_, T>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
@@ -804,6 +812,27 @@ impl HttpJsonRpc {
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn new_payload_v3_electra<T: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_electra: NewPayloadRequestElectra<'_, T>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([
|
||||
JsonExecutionPayload::V4(new_payload_request_electra.execution_payload.clone().into()),
|
||||
new_payload_request_electra.versioned_hashes,
|
||||
new_payload_request_electra.parent_beacon_block_root,
|
||||
]);
|
||||
|
||||
let response: JsonPayloadStatusV1 = self
|
||||
.rpc_request(
|
||||
ENGINE_NEW_PAYLOAD_V3,
|
||||
params,
|
||||
ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn get_payload_v1<T: EthSpec>(
|
||||
&self,
|
||||
payload_id: PayloadId,
|
||||
@@ -855,7 +884,7 @@ impl HttpJsonRpc {
|
||||
.await?;
|
||||
Ok(JsonGetPayloadResponse::V2(response).into())
|
||||
}
|
||||
ForkName::Base | ForkName::Altair | ForkName::Deneb => Err(
|
||||
ForkName::Base | ForkName::Altair | ForkName::Deneb | ForkName::Electra => Err(
|
||||
Error::UnsupportedForkVariant(format!("called get_payload_v2 with {}", fork_name)),
|
||||
),
|
||||
}
|
||||
@@ -879,6 +908,16 @@ impl HttpJsonRpc {
|
||||
.await?;
|
||||
Ok(JsonGetPayloadResponse::V3(response).into())
|
||||
}
|
||||
ForkName::Electra => {
|
||||
let response: JsonGetPayloadResponseV4<T> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V3,
|
||||
params,
|
||||
ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
Ok(JsonGetPayloadResponse::V4(response).into())
|
||||
}
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => Err(
|
||||
Error::UnsupportedForkVariant(format!("called get_payload_v3 with {}", fork_name)),
|
||||
),
|
||||
@@ -1069,7 +1108,15 @@ impl HttpJsonRpc {
|
||||
}
|
||||
NewPayloadRequest::Deneb(new_payload_request_deneb) => {
|
||||
if engine_capabilities.new_payload_v3 {
|
||||
self.new_payload_v3(new_payload_request_deneb).await
|
||||
self.new_payload_v3_deneb(new_payload_request_deneb).await
|
||||
} else {
|
||||
Err(Error::RequiredMethodUnsupported("engine_newPayloadV3"))
|
||||
}
|
||||
}
|
||||
NewPayloadRequest::Electra(new_payload_request_electra) => {
|
||||
if engine_capabilities.new_payload_v3 {
|
||||
self.new_payload_v3_electra(new_payload_request_electra)
|
||||
.await
|
||||
} else {
|
||||
Err(Error::RequiredMethodUnsupported("engine_newPayloadV3"))
|
||||
}
|
||||
@@ -1095,7 +1142,7 @@ impl HttpJsonRpc {
|
||||
Err(Error::RequiredMethodUnsupported("engine_getPayload"))
|
||||
}
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
ForkName::Deneb | ForkName::Electra => {
|
||||
if engine_capabilities.get_payload_v3 {
|
||||
self.get_payload_v3(fork_name, payload_id).await
|
||||
} else {
|
||||
@@ -1775,7 +1822,7 @@ mod test {
|
||||
"extraData":"0x",
|
||||
"baseFeePerGas":"0x7",
|
||||
"blockHash":"0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c",
|
||||
"transactions":[]
|
||||
"transactions":[],
|
||||
}
|
||||
})],
|
||||
|client| async move {
|
||||
@@ -1799,7 +1846,7 @@ mod test {
|
||||
extra_data: vec![].into(),
|
||||
base_fee_per_gas: Uint256::from(7),
|
||||
block_hash: ExecutionBlockHash::from_str("0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c").unwrap(),
|
||||
transactions: vec![].into(),
|
||||
transactions: vec![].into(),
|
||||
});
|
||||
|
||||
assert_eq!(payload, expected);
|
||||
@@ -1846,7 +1893,7 @@ mod test {
|
||||
"extraData":"0x",
|
||||
"baseFeePerGas":"0x7",
|
||||
"blockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858",
|
||||
"transactions":[]
|
||||
"transactions":[],
|
||||
}],
|
||||
})
|
||||
)
|
||||
|
||||
@@ -60,7 +60,7 @@ pub struct JsonPayloadIdResponse {
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V2, V3),
|
||||
variants(V1, V2, V3, V4),
|
||||
variant_attributes(
|
||||
derive(Debug, PartialEq, Default, Serialize, Deserialize,),
|
||||
serde(bound = "T: EthSpec", rename_all = "camelCase"),
|
||||
@@ -93,12 +93,12 @@ pub struct JsonExecutionPayload<T: EthSpec> {
|
||||
pub block_hash: ExecutionBlockHash,
|
||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
||||
pub transactions: Transactions<T>,
|
||||
#[superstruct(only(V2, V3))]
|
||||
#[superstruct(only(V2, V3, V4))]
|
||||
pub withdrawals: VariableList<JsonWithdrawal, T::MaxWithdrawalsPerPayload>,
|
||||
#[superstruct(only(V3))]
|
||||
#[superstruct(only(V3, V4))]
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub blob_gas_used: u64,
|
||||
#[superstruct(only(V3))]
|
||||
#[superstruct(only(V3, V4))]
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub excess_blob_gas: u64,
|
||||
}
|
||||
@@ -178,12 +178,42 @@ impl<T: EthSpec> From<ExecutionPayloadDeneb<T>> for JsonExecutionPayloadV3<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<ExecutionPayloadElectra<T>> for JsonExecutionPayloadV4<T> {
|
||||
fn from(payload: ExecutionPayloadElectra<T>) -> Self {
|
||||
JsonExecutionPayloadV4 {
|
||||
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: payload
|
||||
.withdrawals
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
blob_gas_used: payload.blob_gas_used,
|
||||
excess_blob_gas: payload.excess_blob_gas,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayload<T> {
|
||||
fn from(execution_payload: ExecutionPayload<T>) -> Self {
|
||||
match execution_payload {
|
||||
ExecutionPayload::Merge(payload) => JsonExecutionPayload::V1(payload.into()),
|
||||
ExecutionPayload::Capella(payload) => JsonExecutionPayload::V2(payload.into()),
|
||||
ExecutionPayload::Deneb(payload) => JsonExecutionPayload::V3(payload.into()),
|
||||
ExecutionPayload::Electra(payload) => JsonExecutionPayload::V4(payload.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,6 +264,7 @@ impl<T: EthSpec> From<JsonExecutionPayloadV2<T>> for ExecutionPayloadCapella<T>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
|
||||
fn from(payload: JsonExecutionPayloadV3<T>) -> Self {
|
||||
ExecutionPayloadDeneb {
|
||||
@@ -263,18 +294,48 @@ impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<JsonExecutionPayloadV4<T>> for ExecutionPayloadElectra<T> {
|
||||
fn from(payload: JsonExecutionPayloadV4<T>) -> Self {
|
||||
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: payload
|
||||
.withdrawals
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
blob_gas_used: payload.blob_gas_used,
|
||||
excess_blob_gas: payload.excess_blob_gas,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<JsonExecutionPayload<T>> for ExecutionPayload<T> {
|
||||
fn from(json_execution_payload: JsonExecutionPayload<T>) -> Self {
|
||||
match json_execution_payload {
|
||||
JsonExecutionPayload::V1(payload) => ExecutionPayload::Merge(payload.into()),
|
||||
JsonExecutionPayload::V2(payload) => ExecutionPayload::Capella(payload.into()),
|
||||
JsonExecutionPayload::V3(payload) => ExecutionPayload::Deneb(payload.into()),
|
||||
JsonExecutionPayload::V4(payload) => ExecutionPayload::Electra(payload.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V2, V3),
|
||||
variants(V1, V2, V3, V4),
|
||||
variant_attributes(
|
||||
derive(Debug, PartialEq, Serialize, Deserialize),
|
||||
serde(bound = "T: EthSpec", rename_all = "camelCase")
|
||||
@@ -291,11 +352,13 @@ pub struct JsonGetPayloadResponse<T: EthSpec> {
|
||||
pub execution_payload: JsonExecutionPayloadV2<T>,
|
||||
#[superstruct(only(V3), partial_getter(rename = "execution_payload_v3"))]
|
||||
pub execution_payload: JsonExecutionPayloadV3<T>,
|
||||
#[superstruct(only(V4), partial_getter(rename = "execution_payload_v4"))]
|
||||
pub execution_payload: JsonExecutionPayloadV4<T>,
|
||||
#[serde(with = "serde_utils::u256_hex_be")]
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(V3))]
|
||||
#[superstruct(only(V3, V4))]
|
||||
pub blobs_bundle: JsonBlobsBundleV1<T>,
|
||||
#[superstruct(only(V3))]
|
||||
#[superstruct(only(V3, V4))]
|
||||
pub should_override_builder: bool,
|
||||
}
|
||||
|
||||
@@ -322,6 +385,14 @@ impl<T: EthSpec> From<JsonGetPayloadResponse<T>> for GetPayloadResponse<T> {
|
||||
should_override_builder: response.should_override_builder,
|
||||
})
|
||||
}
|
||||
JsonGetPayloadResponse::V4(response) => {
|
||||
GetPayloadResponse::Electra(GetPayloadResponseElectra {
|
||||
execution_payload: response.execution_payload.into(),
|
||||
block_value: response.block_value,
|
||||
blobs_bundle: response.blobs_bundle.into(),
|
||||
should_override_builder: response.should_override_builder,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ use types::{
|
||||
BeaconBlockRef, BeaconStateError, EthSpec, ExecutionBlockHash, ExecutionPayload,
|
||||
ExecutionPayloadRef, Hash256, VersionedHash,
|
||||
};
|
||||
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge};
|
||||
use types::{
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadMerge,
|
||||
};
|
||||
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variants(Merge, Capella, Deneb, Electra),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -31,9 +33,11 @@ pub struct NewPayloadRequest<'block, E: EthSpec> {
|
||||
pub execution_payload: &'block ExecutionPayloadCapella<E>,
|
||||
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
|
||||
pub execution_payload: &'block ExecutionPayloadDeneb<E>,
|
||||
#[superstruct(only(Deneb))]
|
||||
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
||||
pub execution_payload: &'block ExecutionPayloadElectra<E>,
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
pub versioned_hashes: Vec<VersionedHash>,
|
||||
#[superstruct(only(Deneb))]
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
pub parent_beacon_block_root: Hash256,
|
||||
}
|
||||
|
||||
@@ -43,6 +47,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Merge(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Capella(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Deneb(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Electra(payload) => payload.execution_payload.parent_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +56,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Merge(payload) => payload.execution_payload.block_hash,
|
||||
Self::Capella(payload) => payload.execution_payload.block_hash,
|
||||
Self::Deneb(payload) => payload.execution_payload.block_hash,
|
||||
Self::Electra(payload) => payload.execution_payload.block_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +65,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Merge(payload) => payload.execution_payload.block_number,
|
||||
Self::Capella(payload) => payload.execution_payload.block_number,
|
||||
Self::Deneb(payload) => payload.execution_payload.block_number,
|
||||
Self::Electra(payload) => payload.execution_payload.block_number,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +74,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Merge(request) => ExecutionPayloadRef::Merge(request.execution_payload),
|
||||
Self::Capella(request) => ExecutionPayloadRef::Capella(request.execution_payload),
|
||||
Self::Deneb(request) => ExecutionPayloadRef::Deneb(request.execution_payload),
|
||||
Self::Electra(request) => ExecutionPayloadRef::Electra(request.execution_payload),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +83,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Merge(request) => ExecutionPayload::Merge(request.execution_payload.clone()),
|
||||
Self::Capella(request) => ExecutionPayload::Capella(request.execution_payload.clone()),
|
||||
Self::Deneb(request) => ExecutionPayload::Deneb(request.execution_payload.clone()),
|
||||
Self::Electra(request) => ExecutionPayload::Electra(request.execution_payload.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +166,16 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
|
||||
.collect(),
|
||||
parent_beacon_block_root: block_ref.parent_root,
|
||||
})),
|
||||
BeaconBlockRef::Electra(block_ref) => Ok(Self::Electra(NewPayloadRequestElectra {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
versioned_hashes: block_ref
|
||||
.body
|
||||
.blob_kzg_commitments
|
||||
.iter()
|
||||
.map(kzg_commitment_to_versioned_hash)
|
||||
.collect(),
|
||||
parent_beacon_block_root: block_ref.parent_root,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,6 +192,7 @@ impl<'a, E: EthSpec> TryFrom<ExecutionPayloadRef<'a, E>> for NewPayloadRequest<'
|
||||
execution_payload: payload,
|
||||
})),
|
||||
ExecutionPayloadRef::Deneb(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
ExecutionPayloadRef::Electra(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ use types::{
|
||||
};
|
||||
use types::{
|
||||
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella,
|
||||
ExecutionPayloadMerge, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot,
|
||||
ExecutionPayloadElectra, ExecutionPayloadMerge, FullPayload, ProposerPreparationData,
|
||||
PublicKeyBytes, Signature, Slot,
|
||||
};
|
||||
|
||||
mod block_hash;
|
||||
@@ -111,6 +112,12 @@ impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalCont
|
||||
kzg_commitments: builder_bid.blob_kzg_commitments,
|
||||
blobs_and_proofs: None,
|
||||
},
|
||||
BuilderBid::Electra(builder_bid) => BlockProposalContents::PayloadAndBlobs {
|
||||
payload: ExecutionPayloadHeader::Electra(builder_bid.header).into(),
|
||||
block_value: builder_bid.value,
|
||||
kzg_commitments: builder_bid.blob_kzg_commitments,
|
||||
blobs_and_proofs: None,
|
||||
},
|
||||
};
|
||||
Ok(ProvenancedPayload::Builder(
|
||||
BlockProposalContentsType::Blinded(block_proposal_contents),
|
||||
@@ -1771,6 +1778,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
ForkName::Merge => ExecutionPayloadMerge::default().into(),
|
||||
ForkName::Capella => ExecutionPayloadCapella::default().into(),
|
||||
ForkName::Deneb => ExecutionPayloadDeneb::default().into(),
|
||||
ForkName::Electra => ExecutionPayloadElectra::default().into(),
|
||||
ForkName::Base | ForkName::Altair => {
|
||||
return Err(Error::InvalidForkForPayload);
|
||||
}
|
||||
@@ -1839,6 +1847,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
ForkName::Merge => Ok(Some(ExecutionPayloadMerge::default().into())),
|
||||
ForkName::Capella => Ok(Some(ExecutionPayloadCapella::default().into())),
|
||||
ForkName::Deneb => Ok(Some(ExecutionPayloadDeneb::default().into())),
|
||||
ForkName::Electra => Ok(Some(ExecutionPayloadElectra::default().into())),
|
||||
ForkName::Base | ForkName::Altair => Err(ApiError::UnsupportedForkVariant(
|
||||
format!("called get_payload_by_hash_from_engine with {}", fork),
|
||||
)),
|
||||
@@ -1938,6 +1947,35 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
excess_blob_gas: deneb_block.excess_blob_gas,
|
||||
})
|
||||
}
|
||||
ExecutionBlockWithTransactions::Electra(electra_block) => {
|
||||
let withdrawals = VariableList::new(
|
||||
electra_block
|
||||
.withdrawals
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
)
|
||||
.map_err(ApiError::DeserializeWithdrawals)?;
|
||||
ExecutionPayload::Electra(ExecutionPayloadElectra {
|
||||
parent_hash: electra_block.parent_hash,
|
||||
fee_recipient: electra_block.fee_recipient,
|
||||
state_root: electra_block.state_root,
|
||||
receipts_root: electra_block.receipts_root,
|
||||
logs_bloom: electra_block.logs_bloom,
|
||||
prev_randao: electra_block.prev_randao,
|
||||
block_number: electra_block.block_number,
|
||||
gas_limit: electra_block.gas_limit,
|
||||
gas_used: electra_block.gas_used,
|
||||
timestamp: electra_block.timestamp,
|
||||
extra_data: electra_block.extra_data,
|
||||
base_fee_per_gas: electra_block.base_fee_per_gas,
|
||||
block_hash: electra_block.block_hash,
|
||||
transactions: convert_transactions(electra_block.transactions)?,
|
||||
withdrawals,
|
||||
blob_gas_used: electra_block.blob_gas_used,
|
||||
excess_blob_gas: electra_block.excess_blob_gas,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(payload))
|
||||
|
||||
@@ -22,8 +22,8 @@ use tree_hash::TreeHash;
|
||||
use tree_hash_derive::TreeHash;
|
||||
use types::{
|
||||
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella,
|
||||
ExecutionPayloadDeneb, ExecutionPayloadHeader, ExecutionPayloadMerge, ForkName, Hash256,
|
||||
Transaction, Transactions, Uint256,
|
||||
ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadHeader, ExecutionPayloadMerge,
|
||||
ForkName, Hash256, Transaction, Transactions, Uint256,
|
||||
};
|
||||
|
||||
use super::DEFAULT_TERMINAL_BLOCK;
|
||||
@@ -130,8 +130,9 @@ pub struct ExecutionBlockGenerator<T: EthSpec> {
|
||||
/*
|
||||
* Post-merge fork triggers
|
||||
*/
|
||||
pub shanghai_time: Option<u64>, // withdrawals
|
||||
pub shanghai_time: Option<u64>, // capella
|
||||
pub cancun_time: Option<u64>, // deneb
|
||||
pub prague_time: Option<u64>, // electra
|
||||
/*
|
||||
* deneb stuff
|
||||
*/
|
||||
@@ -153,6 +154,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
terminal_block_hash: ExecutionBlockHash,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
kzg: Option<Kzg>,
|
||||
) -> Self {
|
||||
let mut gen = Self {
|
||||
@@ -168,6 +170,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
payload_ids: <_>::default(),
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
blobs_bundles: <_>::default(),
|
||||
kzg: kzg.map(Arc::new),
|
||||
rng: make_rng(),
|
||||
@@ -203,11 +206,14 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
}
|
||||
|
||||
pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName {
|
||||
match self.cancun_time {
|
||||
Some(fork_time) if timestamp >= fork_time => ForkName::Deneb,
|
||||
_ => match self.shanghai_time {
|
||||
Some(fork_time) if timestamp >= fork_time => ForkName::Capella,
|
||||
_ => ForkName::Merge,
|
||||
match self.prague_time {
|
||||
Some(fork_time) if timestamp >= fork_time => ForkName::Electra,
|
||||
_ => match self.cancun_time {
|
||||
Some(fork_time) if timestamp >= fork_time => ForkName::Deneb,
|
||||
_ => match self.shanghai_time {
|
||||
Some(fork_time) if timestamp >= fork_time => ForkName::Capella,
|
||||
_ => ForkName::Merge,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -514,6 +520,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
|
||||
let execution_payload =
|
||||
self.build_new_execution_payload(head_block_hash, &parent, id, &attributes)?;
|
||||
|
||||
self.payload_ids.insert(id, execution_payload);
|
||||
|
||||
Some(id)
|
||||
@@ -601,30 +608,52 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
PayloadAttributes::V3(pa) => ExecutionPayload::Deneb(ExecutionPayloadDeneb {
|
||||
parent_hash: head_block_hash,
|
||||
fee_recipient: pa.suggested_fee_recipient,
|
||||
receipts_root: Hash256::repeat_byte(42),
|
||||
state_root: Hash256::repeat_byte(43),
|
||||
logs_bloom: vec![0; 256].into(),
|
||||
prev_randao: pa.prev_randao,
|
||||
block_number: parent.block_number() + 1,
|
||||
gas_limit: GAS_LIMIT,
|
||||
gas_used: GAS_USED,
|
||||
timestamp: pa.timestamp,
|
||||
extra_data: "block gen was here".as_bytes().to_vec().into(),
|
||||
base_fee_per_gas: Uint256::one(),
|
||||
block_hash: ExecutionBlockHash::zero(),
|
||||
transactions: vec![].into(),
|
||||
withdrawals: pa.withdrawals.clone().into(),
|
||||
blob_gas_used: 0,
|
||||
excess_blob_gas: 0,
|
||||
}),
|
||||
PayloadAttributes::V3(pa) => match self.get_fork_at_timestamp(pa.timestamp) {
|
||||
ForkName::Deneb => ExecutionPayload::Deneb(ExecutionPayloadDeneb {
|
||||
parent_hash: head_block_hash,
|
||||
fee_recipient: pa.suggested_fee_recipient,
|
||||
receipts_root: Hash256::repeat_byte(42),
|
||||
state_root: Hash256::repeat_byte(43),
|
||||
logs_bloom: vec![0; 256].into(),
|
||||
prev_randao: pa.prev_randao,
|
||||
block_number: parent.block_number() + 1,
|
||||
gas_limit: GAS_LIMIT,
|
||||
gas_used: GAS_USED,
|
||||
timestamp: pa.timestamp,
|
||||
extra_data: "block gen was here".as_bytes().to_vec().into(),
|
||||
base_fee_per_gas: Uint256::one(),
|
||||
block_hash: ExecutionBlockHash::zero(),
|
||||
transactions: vec![].into(),
|
||||
withdrawals: pa.withdrawals.clone().into(),
|
||||
blob_gas_used: 0,
|
||||
excess_blob_gas: 0,
|
||||
}),
|
||||
ForkName::Electra => ExecutionPayload::Electra(ExecutionPayloadElectra {
|
||||
parent_hash: head_block_hash,
|
||||
fee_recipient: pa.suggested_fee_recipient,
|
||||
receipts_root: Hash256::repeat_byte(42),
|
||||
state_root: Hash256::repeat_byte(43),
|
||||
logs_bloom: vec![0; 256].into(),
|
||||
prev_randao: pa.prev_randao,
|
||||
block_number: parent.block_number() + 1,
|
||||
gas_limit: GAS_LIMIT,
|
||||
gas_used: GAS_USED,
|
||||
timestamp: pa.timestamp,
|
||||
extra_data: "block gen was here".as_bytes().to_vec().into(),
|
||||
base_fee_per_gas: Uint256::one(),
|
||||
block_hash: ExecutionBlockHash::zero(),
|
||||
transactions: vec![].into(),
|
||||
withdrawals: pa.withdrawals.clone().into(),
|
||||
blob_gas_used: 0,
|
||||
excess_blob_gas: 0,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
match execution_payload.fork_name() {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {}
|
||||
ForkName::Deneb => {
|
||||
ForkName::Deneb | ForkName::Electra => {
|
||||
// get random number between 0 and Max Blobs
|
||||
let mut rng = self.rng.lock();
|
||||
let num_blobs = rng.gen::<usize>() % (T::max_blobs_per_block() + 1);
|
||||
@@ -758,6 +787,11 @@ pub fn generate_genesis_header<T: EthSpec>(
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
Some(header)
|
||||
}
|
||||
ForkName::Electra => {
|
||||
let mut header = ExecutionPayloadHeader::Electra(<_>::default());
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
Some(header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,6 +864,7 @@ mod test {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
for i in 0..=TERMINAL_BLOCK {
|
||||
|
||||
@@ -105,14 +105,18 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
.map(|jep| JsonExecutionPayload::V1(jep))
|
||||
})
|
||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
|
||||
ENGINE_NEW_PAYLOAD_V3 => get_param::<JsonExecutionPayloadV3<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V3(jep))
|
||||
ENGINE_NEW_PAYLOAD_V3 => get_param::<JsonExecutionPayloadV4<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V4(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV2<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V2(jep))
|
||||
get_param::<JsonExecutionPayloadV3<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V3(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V1(jep))
|
||||
get_param::<JsonExecutionPayloadV2<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V2(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V1(jep))
|
||||
})
|
||||
})
|
||||
})
|
||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
|
||||
@@ -123,7 +127,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
.execution_block_generator
|
||||
.read()
|
||||
.get_fork_at_timestamp(*request.timestamp());
|
||||
// validate method called correctly according to shanghai fork time
|
||||
// validate method called correctly according to fork time
|
||||
match fork {
|
||||
ForkName::Merge => {
|
||||
if matches!(request, JsonExecutionPayload::V2(_)) {
|
||||
@@ -156,14 +160,14 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
ForkName::Deneb => {
|
||||
if method == ENGINE_NEW_PAYLOAD_V1 || method == ENGINE_NEW_PAYLOAD_V2 {
|
||||
return Err((
|
||||
format!("{} called after deneb fork!", method),
|
||||
format!("{} called after Deneb fork!", method),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V1(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV1` after deneb fork!",
|
||||
"{} called with `ExecutionPayloadV1` after Deneb fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
@@ -172,7 +176,42 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
if matches!(request, JsonExecutionPayload::V2(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV2` after deneb fork!",
|
||||
"{} called with `ExecutionPayloadV2` after Deneb fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
}
|
||||
ForkName::Electra => {
|
||||
if method == ENGINE_NEW_PAYLOAD_V1 || method == ENGINE_NEW_PAYLOAD_V2 {
|
||||
return Err((
|
||||
format!("{} called after Electra fork!", method),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V1(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV1` after Electra fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V2(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV2` after Electra fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V3(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV3` after Electra fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
@@ -245,7 +284,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
// validate method called correctly according to deneb fork time
|
||||
// validate method called correctly according to cancun fork time
|
||||
if ctx
|
||||
.execution_block_generator
|
||||
.read()
|
||||
@@ -254,7 +293,20 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
&& (method == ENGINE_GET_PAYLOAD_V1 || method == ENGINE_GET_PAYLOAD_V2)
|
||||
{
|
||||
return Err((
|
||||
format!("{} called after deneb fork!", method),
|
||||
format!("{} called after Deneb fork!", method),
|
||||
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
// validate method called correctly according to prague fork time
|
||||
if ctx
|
||||
.execution_block_generator
|
||||
.read()
|
||||
.get_fork_at_timestamp(response.timestamp())
|
||||
== ForkName::Electra
|
||||
&& method == ENGINE_GET_PAYLOAD_V1
|
||||
{
|
||||
return Err((
|
||||
format!("{} called after Electra fork!", method),
|
||||
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
@@ -295,6 +347,20 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
JsonExecutionPayload::V4(execution_payload) => {
|
||||
serde_json::to_value(JsonGetPayloadResponseV4 {
|
||||
execution_payload,
|
||||
block_value: DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI.into(),
|
||||
blobs_bundle: maybe_blobs
|
||||
.ok_or((
|
||||
"No blobs returned despite V4 Payload".to_string(),
|
||||
GENERIC_ERROR_CODE,
|
||||
))?
|
||||
.into(),
|
||||
should_override_builder: false,
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
@@ -328,7 +394,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
.map(|opt| opt.map(JsonPayloadAttributes::V1))
|
||||
.transpose()
|
||||
}
|
||||
ForkName::Capella | ForkName::Deneb => {
|
||||
ForkName::Capella | ForkName::Deneb | ForkName::Electra => {
|
||||
get_param::<Option<JsonPayloadAttributesV2>>(params, 1)
|
||||
.map(|opt| opt.map(JsonPayloadAttributes::V2))
|
||||
.transpose()
|
||||
@@ -392,7 +458,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
ForkName::Deneb | ForkName::Electra => {
|
||||
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
|
||||
return Err((
|
||||
format!("{} called after Deneb fork!", method),
|
||||
|
||||
@@ -15,7 +15,8 @@ use task_executor::TaskExecutor;
|
||||
use tempfile::NamedTempFile;
|
||||
use tree_hash::TreeHash;
|
||||
use types::builder_bid::{
|
||||
BuilderBid, BuilderBidCapella, BuilderBidDeneb, BuilderBidMerge, SignedBuilderBid,
|
||||
BuilderBid, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, BuilderBidMerge,
|
||||
SignedBuilderBid,
|
||||
};
|
||||
use types::{
|
||||
Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload,
|
||||
@@ -85,6 +86,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.fee_recipient = fee_recipient;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.fee_recipient = fee_recipient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +103,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.gas_limit = gas_limit;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.gas_limit = gas_limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +124,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +141,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.prev_randao = prev_randao;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.prev_randao = prev_randao;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +158,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.block_number = block_number;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.block_number = block_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +175,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.timestamp = timestamp;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +192,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.withdrawals_root = withdrawals_root;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.withdrawals_root = withdrawals_root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +342,9 @@ pub fn serve<E: EthSpec>(
|
||||
SignedBlindedBeaconBlock::Deneb(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
SignedBlindedBeaconBlock::Electra(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
};
|
||||
|
||||
let fork_name = builder.spec.fork_name_at_slot::<E>(slot);
|
||||
@@ -455,7 +480,7 @@ pub fn serve<E: EthSpec>(
|
||||
.map_err(|_| reject("couldn't get prev randao"))?;
|
||||
let expected_withdrawals = match fork {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
|
||||
ForkName::Capella | ForkName::Deneb => Some(
|
||||
ForkName::Capella | ForkName::Deneb | ForkName::Electra => Some(
|
||||
builder
|
||||
.beacon_client
|
||||
.get_expected_withdrawals(&StateId::Head)
|
||||
@@ -477,7 +502,7 @@ pub fn serve<E: EthSpec>(
|
||||
expected_withdrawals,
|
||||
None,
|
||||
),
|
||||
ForkName::Deneb => PayloadAttributes::new(
|
||||
ForkName::Deneb | ForkName::Electra => PayloadAttributes::new(
|
||||
timestamp,
|
||||
*prev_randao,
|
||||
fee_recipient,
|
||||
@@ -521,6 +546,17 @@ pub fn serve<E: EthSpec>(
|
||||
) = payload_response.into();
|
||||
|
||||
match fork {
|
||||
ForkName::Electra => BuilderBid::Electra(BuilderBidElectra {
|
||||
header: payload
|
||||
.as_electra()
|
||||
.map_err(|_| reject("incorrect payload variant"))?
|
||||
.into(),
|
||||
blob_kzg_commitments: maybe_blobs_bundle
|
||||
.map(|b| b.commitments)
|
||||
.unwrap_or_default(),
|
||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||
pubkey: builder.builder_sk.public_key().compress(),
|
||||
}),
|
||||
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
|
||||
header: payload
|
||||
.as_deneb()
|
||||
@@ -560,6 +596,17 @@ pub fn serve<E: EthSpec>(
|
||||
Option<BlobsBundle<E>>,
|
||||
) = payload_response.into();
|
||||
match fork {
|
||||
ForkName::Electra => BuilderBid::Electra(BuilderBidElectra {
|
||||
header: payload
|
||||
.as_electra()
|
||||
.map_err(|_| reject("incorrect payload variant"))?
|
||||
.into(),
|
||||
blob_kzg_commitments: maybe_blobs_bundle
|
||||
.map(|b| b.commitments)
|
||||
.unwrap_or_default(),
|
||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||
pubkey: builder.builder_sk.public_key().compress(),
|
||||
}),
|
||||
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
|
||||
header: payload
|
||||
.as_deneb()
|
||||
|
||||
@@ -27,6 +27,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
DEFAULT_TERMINAL_BLOCK,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
|
||||
spec,
|
||||
None,
|
||||
@@ -39,6 +40,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
terminal_block: u64,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
jwt_key: Option<JwtKey>,
|
||||
spec: ChainSpec,
|
||||
kzg: Option<Kzg>,
|
||||
@@ -54,6 +56,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
spec.terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
kzg,
|
||||
);
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ pub struct MockExecutionConfig {
|
||||
pub terminal_block_hash: ExecutionBlockHash,
|
||||
pub shanghai_time: Option<u64>,
|
||||
pub cancun_time: Option<u64>,
|
||||
pub prague_time: Option<u64>,
|
||||
}
|
||||
|
||||
impl Default for MockExecutionConfig {
|
||||
@@ -78,6 +79,7 @@ impl Default for MockExecutionConfig {
|
||||
server_config: Config::default(),
|
||||
shanghai_time: None,
|
||||
cancun_time: None,
|
||||
prague_time: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +101,8 @@ impl<T: EthSpec> MockServer<T> {
|
||||
ExecutionBlockHash::zero(),
|
||||
None, // FIXME(capella): should this be the default?
|
||||
None, // FIXME(deneb): should this be the default?
|
||||
None, // FIXME(deneb): should this be the default?
|
||||
None, // FIXME(electra): should this be the default?
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,6 +119,7 @@ impl<T: EthSpec> MockServer<T> {
|
||||
server_config,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
} = config;
|
||||
let last_echo_request = Arc::new(RwLock::new(None));
|
||||
let preloaded_responses = Arc::new(Mutex::new(vec![]));
|
||||
@@ -125,6 +129,7 @@ impl<T: EthSpec> MockServer<T> {
|
||||
terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
kzg,
|
||||
);
|
||||
|
||||
@@ -187,6 +192,7 @@ impl<T: EthSpec> MockServer<T> {
|
||||
terminal_block_hash: ExecutionBlockHash,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
kzg: Option<Kzg>,
|
||||
) -> Self {
|
||||
Self::new_with_config(
|
||||
@@ -199,6 +205,7 @@ impl<T: EthSpec> MockServer<T> {
|
||||
terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
},
|
||||
kzg,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user