Add Fulu boilerplate (#6695)

* Add Fulu boilerplate

* Add more boilerplate

* Change fulu_time to osaka_time

* Merge branch 'unstable' into fulu-boilerplate

* Fix tests

* Merge branch 'unstable' into fulu-boilerplate

* More test fixes

* Apply suggestions

* Remove `get_payload` boilerplate

* Add lightclient fulu types and fix beacon-chain-tests

* Disable Fulu in ef-tests

* Reduce boilerplate for future forks

* Small fixes

* One more fix

* Apply suggestions

* Merge branch 'unstable' into fulu-boilerplate

* Fix lints
This commit is contained in:
Mac L
2025-01-10 09:25:23 +04:00
committed by GitHub
parent 722573f7ed
commit ecdf2d891f
91 changed files with 2365 additions and 674 deletions

View File

@@ -19,7 +19,7 @@ use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use types::{
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra,
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadHeader, FixedBytesExtended, ForkName, Hash256, Transaction, Transactions,
Uint256,
};
@@ -147,6 +147,7 @@ pub struct ExecutionBlockGenerator<E: EthSpec> {
pub shanghai_time: Option<u64>, // capella
pub cancun_time: Option<u64>, // deneb
pub prague_time: Option<u64>, // electra
pub osaka_time: Option<u64>, // fulu
/*
* deneb stuff
*/
@@ -162,6 +163,7 @@ fn make_rng() -> Arc<Mutex<StdRng>> {
}
impl<E: EthSpec> ExecutionBlockGenerator<E> {
#[allow(clippy::too_many_arguments)]
pub fn new(
terminal_total_difficulty: Uint256,
terminal_block_number: u64,
@@ -169,6 +171,7 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
osaka_time: Option<u64>,
kzg: Option<Arc<Kzg>>,
) -> Self {
let mut gen = Self {
@@ -185,6 +188,7 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
shanghai_time,
cancun_time,
prague_time,
osaka_time,
blobs_bundles: <_>::default(),
kzg,
rng: make_rng(),
@@ -233,13 +237,16 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
}
pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName {
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::Bellatrix,
match self.osaka_time {
Some(fork_time) if timestamp >= fork_time => ForkName::Fulu,
_ => 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::Bellatrix,
},
},
},
}
@@ -664,6 +671,25 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
blob_gas_used: 0,
excess_blob_gas: 0,
}),
ForkName::Fulu => ExecutionPayload::Fulu(ExecutionPayloadFulu {
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: DEFAULT_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::from(1u64),
block_hash: ExecutionBlockHash::zero(),
transactions: vec![].into(),
withdrawals: pa.withdrawals.clone().into(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
_ => unreachable!(),
},
};
@@ -811,6 +837,12 @@ pub fn generate_genesis_header<E: EthSpec>(
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
ForkName::Fulu => {
let mut header = ExecutionPayloadHeader::Fulu(<_>::default());
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
}
}
@@ -883,6 +915,7 @@ mod test {
None,
None,
None,
None,
);
for i in 0..=TERMINAL_BLOCK {

View File

@@ -99,7 +99,8 @@ pub async fn handle_rpc<E: EthSpec>(
ENGINE_NEW_PAYLOAD_V1
| ENGINE_NEW_PAYLOAD_V2
| ENGINE_NEW_PAYLOAD_V3
| ENGINE_NEW_PAYLOAD_V4 => {
| ENGINE_NEW_PAYLOAD_V4
| ENGINE_NEW_PAYLOAD_V5 => {
let request = match method {
ENGINE_NEW_PAYLOAD_V1 => JsonExecutionPayload::V1(
get_param::<JsonExecutionPayloadV1<E>>(params, 0)
@@ -121,6 +122,9 @@ pub async fn handle_rpc<E: EthSpec>(
ENGINE_NEW_PAYLOAD_V4 => get_param::<JsonExecutionPayloadV4<E>>(params, 0)
.map(|jep| JsonExecutionPayload::V4(jep))
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
ENGINE_NEW_PAYLOAD_V5 => get_param::<JsonExecutionPayloadV5<E>>(params, 0)
.map(|jep| JsonExecutionPayload::V5(jep))
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
_ => unreachable!(),
};
@@ -222,6 +226,54 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
}
ForkName::Fulu => {
if method == ENGINE_NEW_PAYLOAD_V1
|| method == ENGINE_NEW_PAYLOAD_V2
|| method == ENGINE_NEW_PAYLOAD_V3
|| method == ENGINE_NEW_PAYLOAD_V4
{
return Err((
format!("{} called after Fulu fork!", method),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V1(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV1` after Fulu fork!",
method
),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V2(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV2` after Fulu fork!",
method
),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V3(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV3` after Fulu fork!",
method
),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V4(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV4` after Fulu fork!",
method
),
GENERIC_ERROR_CODE,
));
}
}
_ => unreachable!(),
};
@@ -260,7 +312,8 @@ pub async fn handle_rpc<E: EthSpec>(
ENGINE_GET_PAYLOAD_V1
| ENGINE_GET_PAYLOAD_V2
| ENGINE_GET_PAYLOAD_V3
| ENGINE_GET_PAYLOAD_V4 => {
| ENGINE_GET_PAYLOAD_V4
| ENGINE_GET_PAYLOAD_V5 => {
let request: JsonPayloadIdRequest =
get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
let id = request.into();
@@ -320,6 +373,23 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
// validate method called correctly according to fulu fork time
if ctx
.execution_block_generator
.read()
.get_fork_at_timestamp(response.timestamp())
== ForkName::Fulu
&& (method == ENGINE_GET_PAYLOAD_V1
|| method == ENGINE_GET_PAYLOAD_V2
|| method == ENGINE_GET_PAYLOAD_V3
|| method == ENGINE_GET_PAYLOAD_V4)
{
return Err((
format!("{} called after Fulu fork!", method),
FORK_REQUEST_MISMATCH_ERROR_CODE,
));
}
match method {
ENGINE_GET_PAYLOAD_V1 => {
Ok(serde_json::to_value(JsonExecutionPayload::from(response)).unwrap())
@@ -380,6 +450,24 @@ pub async fn handle_rpc<E: EthSpec>(
}
_ => unreachable!(),
}),
ENGINE_GET_PAYLOAD_V5 => Ok(match JsonExecutionPayload::from(response) {
JsonExecutionPayload::V5(execution_payload) => {
serde_json::to_value(JsonGetPayloadResponseV5 {
execution_payload,
block_value: Uint256::from(DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI),
blobs_bundle: maybe_blobs
.ok_or((
"No blobs returned despite V5 Payload".to_string(),
GENERIC_ERROR_CODE,
))?
.into(),
should_override_builder: false,
execution_requests: Default::default(),
})
.unwrap()
}
_ => unreachable!(),
}),
_ => unreachable!(),
}
}
@@ -411,7 +499,10 @@ pub async fn handle_rpc<E: EthSpec>(
.map(|opt| opt.map(JsonPayloadAttributes::V1))
.transpose()
}
ForkName::Capella | ForkName::Deneb | ForkName::Electra => {
ForkName::Capella
| ForkName::Deneb
| ForkName::Electra
| ForkName::Fulu => {
get_param::<Option<JsonPayloadAttributesV2>>(params, 1)
.map(|opt| opt.map(JsonPayloadAttributes::V2))
.transpose()
@@ -475,7 +566,7 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
}
ForkName::Deneb | ForkName::Electra => {
ForkName::Deneb | ForkName::Electra | ForkName::Fulu => {
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
return Err((
format!("{} called after Deneb fork!", method),

View File

@@ -16,7 +16,7 @@ use tempfile::NamedTempFile;
use tree_hash::TreeHash;
use types::builder_bid::{
BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra,
SignedBuilderBid,
BuilderBidFulu, SignedBuilderBid,
};
use types::{
Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload,
@@ -95,6 +95,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.fee_recipient = fee_recipient;
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.fee_recipient = fee_recipient;
}
}
}
@@ -112,6 +115,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.gas_limit = gas_limit;
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.gas_limit = gas_limit;
}
}
}
@@ -133,6 +139,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
}
}
@@ -150,6 +159,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.prev_randao = prev_randao;
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.prev_randao = prev_randao;
}
}
}
@@ -167,6 +179,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.block_number = block_number;
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.block_number = block_number;
}
}
}
@@ -184,6 +199,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.timestamp = timestamp;
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.timestamp = timestamp;
}
}
}
@@ -201,6 +219,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.withdrawals_root = withdrawals_root;
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.withdrawals_root = withdrawals_root;
}
}
}
@@ -230,6 +251,10 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
header.extra_data = extra_data;
header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root());
}
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.extra_data = extra_data;
header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root());
}
}
}
}
@@ -378,6 +403,9 @@ pub fn serve<E: EthSpec>(
SignedBlindedBeaconBlock::Electra(block) => {
block.message.body.execution_payload.tree_hash_root()
}
SignedBlindedBeaconBlock::Fulu(block) => {
block.message.body.execution_payload.tree_hash_root()
}
};
let payload = builder
.el
@@ -536,7 +564,7 @@ pub fn serve<E: EthSpec>(
expected_withdrawals,
None,
),
ForkName::Deneb | ForkName::Electra => PayloadAttributes::new(
ForkName::Deneb | ForkName::Electra | ForkName::Fulu => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
@@ -592,6 +620,17 @@ pub fn serve<E: EthSpec>(
) = payload_response.into();
match fork {
ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu {
header: payload
.as_fulu()
.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::Electra => BuilderBid::Electra(BuilderBidElectra {
header: payload
.as_electra()
@@ -644,6 +683,17 @@ pub fn serve<E: EthSpec>(
Option<ExecutionRequests<E>>,
) = payload_response.into();
match fork {
ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu {
header: payload
.as_fulu()
.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::Electra => BuilderBid::Electra(BuilderBidElectra {
header: payload
.as_electra()

View File

@@ -28,6 +28,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
None,
None,
None,
None,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec,
None,
@@ -41,6 +42,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
osaka_time: Option<u64>,
jwt_key: Option<JwtKey>,
spec: ChainSpec,
kzg: Option<Arc<Kzg>>,
@@ -57,6 +59,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
shanghai_time,
cancun_time,
prague_time,
osaka_time,
kzg,
);

View File

@@ -44,6 +44,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
new_payload_v2: true,
new_payload_v3: true,
new_payload_v4: true,
new_payload_v5: true,
forkchoice_updated_v1: true,
forkchoice_updated_v2: true,
forkchoice_updated_v3: true,
@@ -53,6 +54,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
get_payload_v2: true,
get_payload_v3: true,
get_payload_v4: true,
get_payload_v5: true,
get_client_version_v1: true,
get_blobs_v1: true,
};
@@ -82,6 +84,7 @@ pub struct MockExecutionConfig {
pub shanghai_time: Option<u64>,
pub cancun_time: Option<u64>,
pub prague_time: Option<u64>,
pub osaka_time: Option<u64>,
}
impl Default for MockExecutionConfig {
@@ -95,6 +98,7 @@ impl Default for MockExecutionConfig {
shanghai_time: None,
cancun_time: None,
prague_time: None,
osaka_time: None,
}
}
}
@@ -117,6 +121,7 @@ impl<E: EthSpec> MockServer<E> {
None, // FIXME(capella): should this be the default?
None, // FIXME(deneb): should this be the default?
None, // FIXME(electra): should this be the default?
None, // FIXME(fulu): should this be the default?
None,
)
}
@@ -135,6 +140,7 @@ impl<E: EthSpec> MockServer<E> {
shanghai_time,
cancun_time,
prague_time,
osaka_time,
} = config;
let last_echo_request = Arc::new(RwLock::new(None));
let preloaded_responses = Arc::new(Mutex::new(vec![]));
@@ -145,6 +151,7 @@ impl<E: EthSpec> MockServer<E> {
shanghai_time,
cancun_time,
prague_time,
osaka_time,
kzg,
);
@@ -208,6 +215,7 @@ impl<E: EthSpec> MockServer<E> {
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
osaka_time: Option<u64>,
kzg: Option<Arc<Kzg>>,
) -> Self {
Self::new_with_config(
@@ -221,6 +229,7 @@ impl<E: EthSpec> MockServer<E> {
shanghai_time,
cancun_time,
prague_time,
osaka_time,
},
kzg,
)