Builder Specs v0.2.0 (#3134)

## Issue Addressed

https://github.com/sigp/lighthouse/issues/3091

Extends https://github.com/sigp/lighthouse/pull/3062, adding pre-bellatrix block support on blinded endpoints and allowing the normal proposal flow (local payload construction) on blinded endpoints. This resulted in better fallback logic because the VC will not have to switch endpoints on failure in the BN <> Builder API, the BN can just fallback immediately and without repeating block processing that it shouldn't need to. We can also keep VC fallback from the VC<>BN API's blinded endpoint to full endpoint.

## Proposed Changes

- Pre-bellatrix blocks on blinded endpoints
- Add a new `PayloadCache` to the execution layer
- Better fallback-from-builder logic

## Todos

- [x] Remove VC transition logic
- [x] Add logic to only enable builder flow after Merge transition finalization
- [x] Tests
- [x] Fix metrics
- [x] Rustdocs


Co-authored-by: Mac L <mjladson@pm.me>
Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
realbigsean
2022-07-30 00:22:37 +00:00
parent 25f0e261cb
commit 6c2d8b2262
61 changed files with 3522 additions and 687 deletions

View File

@@ -17,8 +17,9 @@ use state_processing::per_block_processing::{
use std::fmt::Debug;
use std::path::Path;
use types::{
Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec, ForkName,
FullPayload, ProposerSlashing, SignedVoluntaryExit, SyncAggregate,
Attestation, AttesterSlashing, BeaconBlock, BeaconState, BlindedPayload, ChainSpec, Deposit,
EthSpec, ExecutionPayload, ForkName, FullPayload, ProposerSlashing, SignedVoluntaryExit,
SyncAggregate,
};
#[derive(Debug, Clone, Default, Deserialize)]
@@ -255,6 +256,40 @@ impl<E: EthSpec> Operation<E> for FullPayload<E> {
}
}
}
impl<E: EthSpec> Operation<E> for BlindedPayload<E> {
fn handler_name() -> String {
"execution_payload".into()
}
fn filename() -> String {
"execution_payload.ssz_snappy".into()
}
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
fork_name != ForkName::Base && fork_name != ForkName::Altair
}
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
ssz_decode_file::<ExecutionPayload<E>>(path).map(Into::into)
}
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
extra: &Operations<E, Self>,
) -> Result<(), BlockProcessingError> {
let valid = extra
.execution_metadata
.as_ref()
.map_or(false, |e| e.execution_valid);
if valid {
process_execution_payload(state, self, spec)
} else {
Err(BlockProcessingError::ExecutionInvalid)
}
}
}
impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {

View File

@@ -56,6 +56,7 @@ type_name!(Eth1Data);
type_name_generic!(ExecutionPayload);
type_name_generic!(FullPayload, "ExecutionPayload");
type_name_generic!(ExecutionPayloadHeader);
type_name_generic!(BlindedPayload, "ExecutionPayloadHeader");
type_name!(Fork);
type_name!(ForkData);
type_name_generic!(HistoricalBatch);

View File

@@ -71,11 +71,17 @@ fn operations_sync_aggregate() {
}
#[test]
fn operations_execution_payload() {
fn operations_execution_payload_full() {
OperationsHandler::<MinimalEthSpec, FullPayload<_>>::default().run();
OperationsHandler::<MainnetEthSpec, FullPayload<_>>::default().run();
}
#[test]
fn operations_execution_payload_blinded() {
OperationsHandler::<MinimalEthSpec, BlindedPayload<_>>::default().run();
OperationsHandler::<MainnetEthSpec, BlindedPayload<_>>::default().run();
}
#[test]
fn sanity_blocks() {
SanityBlocksHandler::<MinimalEthSpec>::default().run();

View File

@@ -3,7 +3,9 @@ use crate::execution_engine::{
};
use crate::transactions::transactions;
use ethers_providers::Middleware;
use execution_layer::{ExecutionLayer, PayloadAttributes, PayloadStatus};
use execution_layer::{
BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes, PayloadStatus,
};
use fork_choice::ForkchoiceUpdateParameters;
use reqwest::{header::CONTENT_TYPE, Client};
use sensitive_url::SensitiveUrl;
@@ -14,7 +16,7 @@ use task_executor::TaskExecutor;
use tokio::time::sleep;
use types::{
Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, FullPayload, Hash256,
MainnetEthSpec, Slot, Uint256,
MainnetEthSpec, PublicKeyBytes, Slot, Uint256,
};
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(20);
@@ -305,6 +307,11 @@ impl<E: GenericExecutionEngine> TestRig<E> {
// in CI.
sleep(Duration::from_secs(3)).await;
let builder_params = BuilderParams {
pubkey: PublicKeyBytes::empty(),
slot: Slot::new(0),
chain_health: ChainHealth::Healthy,
};
let valid_payload = self
.ee_a
.execution_layer
@@ -313,9 +320,9 @@ impl<E: GenericExecutionEngine> TestRig<E> {
timestamp,
prev_randao,
proposer_index,
None,
Slot::new(0),
forkchoice_update_params,
builder_params,
&self.spec,
)
.await
.unwrap()
@@ -413,6 +420,11 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let timestamp = valid_payload.timestamp + 1;
let prev_randao = Hash256::zero();
let proposer_index = 0;
let builder_params = BuilderParams {
pubkey: PublicKeyBytes::empty(),
slot: Slot::new(0),
chain_health: ChainHealth::Healthy,
};
let second_payload = self
.ee_a
.execution_layer
@@ -421,9 +433,9 @@ impl<E: GenericExecutionEngine> TestRig<E> {
timestamp,
prev_randao,
proposer_index,
None,
Slot::new(0),
forkchoice_update_params,
builder_params,
&self.spec,
)
.await
.unwrap()

View File

@@ -302,6 +302,7 @@ mod tests {
let slot_clock =
TestingSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(1));
let config = validator_client::Config::default();
let validator_store = ValidatorStore::<_, E>::new(
initialized_validators,
@@ -310,7 +311,7 @@ mod tests {
spec,
None,
slot_clock,
None,
&config,
executor,
log.clone(),
);
@@ -359,6 +360,8 @@ mod tests {
voting_public_key: validator_pubkey.clone(),
graffiti: None,
suggested_fee_recipient: None,
gas_limit: None,
builder_proposals: None,
description: String::default(),
signing_definition: SigningDefinition::LocalKeystore {
voting_keystore_path: signer_rig.keystore_path.clone(),
@@ -375,6 +378,8 @@ mod tests {
voting_public_key: validator_pubkey.clone(),
graffiti: None,
suggested_fee_recipient: None,
gas_limit: None,
builder_proposals: None,
description: String::default(),
signing_definition: SigningDefinition::Web3Signer(Web3SignerDefinition {
url: signer_rig.url.to_string(),
@@ -450,8 +455,6 @@ mod tests {
}
}
//TODO: remove this once the consensys web3signer includes the `validator_registration` method
#[allow(dead_code)]
fn get_validator_registration(pubkey: PublicKeyBytes) -> ValidatorRegistrationData {
let fee_recipient = Address::repeat_byte(42);
ValidatorRegistrationData {
@@ -513,16 +516,17 @@ mod tests {
.await
.unwrap()
})
//TODO: uncomment this once the consensys web3signer includes the `validator_registration` method
//
// .await
// .assert_signatures_match("validator_registration", |pubkey, validator_store| async move {
// let val_reg_data = get_validator_registration(pubkey);
// validator_store
// .sign_validator_registration_data(val_reg_data)
// .await
// .unwrap()
// })
.await
.assert_signatures_match(
"validator_registration",
|pubkey, validator_store| async move {
let val_reg_data = get_validator_registration(pubkey);
validator_store
.sign_validator_registration_data(val_reg_data)
.await
.unwrap()
},
)
.await;
}
@@ -599,16 +603,17 @@ mod tests {
.unwrap()
},
)
//TODO: uncomment this once the consensys web3signer includes the `validator_registration` method
//
// .await
// .assert_signatures_match("validator_registration", |pubkey, validator_store| async move {
// let val_reg_data = get_validator_registration(pubkey);
// validator_store
// .sign_validator_registration_data(val_reg_data)
// .await
// .unwrap()
// })
.await
.assert_signatures_match(
"validator_registration",
|pubkey, validator_store| async move {
let val_reg_data = get_validator_registration(pubkey);
validator_store
.sign_validator_registration_data(val_reg_data)
.await
.unwrap()
},
)
.await;
}