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:
Mac L
2024-04-02 23:35:02 +11:00
committed by GitHub
parent 3058b96f25
commit f8fdb71f50
105 changed files with 2079 additions and 405 deletions

View File

@@ -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 {

View File

@@ -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),

View File

@@ -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()

View File

@@ -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,
);

View File

@@ -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,
)