Allow producing block with execution payload

This commit is contained in:
Paul Hauner
2021-09-27 17:03:47 +10:00
parent 6cf83db13f
commit b6909b824d
7 changed files with 97 additions and 10 deletions

View File

@@ -60,7 +60,9 @@ use slot_clock::SlotClock;
use state_processing::{
common::get_indexed_attestation,
per_block_processing,
per_block_processing::errors::AttestationValidationError,
per_block_processing::{
compute_timestamp_at_slot, errors::AttestationValidationError, is_merge_complete,
},
per_slot_processing,
state_advance::{complete_state_advance, partial_state_advance},
BlockSignatureStrategy, SigVerifiedOp,
@@ -2790,12 +2792,44 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}))
};
// Closure to fetch a sync aggregate in cases where it is required.
let get_execution_payload = || -> Result<ExecutionPayload<_>, BlockProductionError> {
// TODO: actually get the payload from eth1 node..
Ok(ExecutionPayload::default())
let get_execution_payload = |latest_execution_payload_header: &ExecutionPayloadHeader<
T::EthSpec,
>|
-> Result<ExecutionPayload<_>, BlockProductionError> {
let execution_layer = self
.execution_layer
.as_ref()
.ok_or(BlockProductionError::ExecutionLayerMissing)?;
let parent_hash;
if !is_merge_complete(&state) {
let terminal_pow_block_hash = execution_layer
.block_on(|execution_layer| {
execution_layer.get_pow_block_hash_at_total_difficulty()
})
.map_err(BlockProductionError::TerminalPoWBlockLookupFailed)?;
if let Some(terminal_pow_block_hash) = terminal_pow_block_hash {
parent_hash = terminal_pow_block_hash;
} else {
return Ok(<_>::default());
}
} else {
parent_hash = latest_execution_payload_header.block_hash;
}
let timestamp =
compute_timestamp_at_slot(&state, &self.spec).map_err(BeaconStateError::from)?;
let random = *state.get_randao_mix(state.current_epoch())?;
execution_layer
.block_on(|execution_layer| {
execution_layer.get_payload(parent_hash, timestamp, random)
})
.map_err(BlockProductionError::GetPayloadFailed)
};
let inner_block = match state {
let inner_block = match &state {
BeaconState::Base(_) => BeaconBlock::Base(BeaconBlockBase {
slot,
proposer_index,
@@ -2832,9 +2866,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
},
})
}
BeaconState::Merge(_) => {
BeaconState::Merge(state) => {
let sync_aggregate = get_sync_aggregate()?;
let execution_payload = get_execution_payload()?;
let execution_payload =
get_execution_payload(&state.latest_execution_payload_header)?;
BeaconBlock::Merge(BeaconBlockMerge {
slot,
proposer_index,

View File

@@ -177,6 +177,9 @@ pub enum BlockProductionError {
produce_at_slot: Slot,
state_slot: Slot,
},
ExecutionLayerMissing,
TerminalPoWBlockLookupFailed(execution_layer::Error),
GetPayloadFailed(execution_layer::Error),
}
easy_from_to!(BlockProcessingError, BlockProductionError);

View File

@@ -156,6 +156,7 @@ where
let execution_layer = ExecutionLayer::from_urls(
execution_endpoints,
terminal_total_difficulty,
config.fee_recipient,
context.executor.clone(),
context.log().clone(),
)

View File

@@ -4,7 +4,7 @@ use sensitive_url::SensitiveUrl;
use serde_derive::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use types::{Graffiti, PublicKeyBytes, Uint256};
use types::{Address, Graffiti, PublicKeyBytes, Uint256};
/// Default directory name for the freezer database under the top-level data dir.
const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db";
@@ -76,6 +76,7 @@ pub struct Config {
pub eth1: eth1::Config,
pub execution_endpoints: Option<Vec<SensitiveUrl>>,
pub terminal_total_difficulty_override: Option<Uint256>,
pub fee_recipient: Option<Address>,
pub http_api: http_api::Config,
pub http_metrics: http_metrics::Config,
pub monitoring_api: Option<monitoring_api::Config>,
@@ -98,6 +99,7 @@ impl Default for Config {
eth1: <_>::default(),
execution_endpoints: None,
terminal_total_difficulty_override: None,
fee_recipient: None,
disabled_forks: Vec::new(),
graffiti: Graffiti::default(),
http_api: <_>::default(),

View File

@@ -20,6 +20,7 @@ pub enum Error {
EngineErrors(Vec<EngineError>),
NotSynced,
ShuttingDown,
FeeRecipientUnspecified,
}
impl From<ApiError> for Error {
@@ -31,6 +32,7 @@ impl From<ApiError> for Error {
struct Inner {
engines: Engines<HttpJsonRpc>,
terminal_total_difficulty: Uint256,
fee_recipient: Option<Address>,
executor: TaskExecutor,
log: Logger,
}
@@ -44,6 +46,7 @@ impl ExecutionLayer {
pub fn from_urls(
urls: Vec<SensitiveUrl>,
terminal_total_difficulty: Uint256,
fee_recipient: Option<Address>,
executor: TaskExecutor,
log: Logger,
) -> Result<Self, Error> {
@@ -62,6 +65,7 @@ impl ExecutionLayer {
log: log.clone(),
},
terminal_total_difficulty,
fee_recipient,
executor,
log,
};
@@ -85,6 +89,12 @@ impl ExecutionLayer {
self.inner.terminal_total_difficulty
}
fn fee_recipient(&self) -> Result<Address, Error> {
self.inner
.fee_recipient
.ok_or(Error::FeeRecipientUnspecified)
}
fn log(&self) -> &Logger {
&self.inner.log
}
@@ -118,10 +128,11 @@ impl ExecutionLayer {
parent_hash: Hash256,
timestamp: u64,
random: Hash256,
fee_recipient: Address,
) -> Result<PayloadId, Error> {
let fee_recipient = self.fee_recipient()?;
self.engines()
.first_success(|engine| {
// TODO(paul): put these in a cache.
engine
.api
.prepare_payload(parent_hash, timestamp, random, fee_recipient)
@@ -130,6 +141,27 @@ impl ExecutionLayer {
.map_err(Error::EngineErrors)
}
pub async fn get_payload<T: EthSpec>(
&self,
parent_hash: Hash256,
timestamp: u64,
random: Hash256,
) -> Result<ExecutionPayload<T>, Error> {
let fee_recipient = self.fee_recipient()?;
self.engines()
.first_success(|engine| async move {
// TODO(paul): make a cache for these IDs.
let payload_id = engine
.api
.prepare_payload(parent_hash, timestamp, random, fee_recipient)
.await?;
engine.api.get_payload(payload_id).await
})
.await
.map_err(Error::EngineErrors)
}
pub async fn execute_payload<T: EthSpec>(
&self,
execution_payload: &ExecutionPayload<T>,
@@ -226,7 +258,7 @@ impl ExecutionLayer {
}
}
pub async fn get_pow_block_at_total_difficulty(&self) -> Result<Option<Hash256>, Error> {
pub async fn get_pow_block_hash_at_total_difficulty(&self) -> Result<Option<Hash256>, Error> {
self.engines()
.first_success(|engine| async move {
let mut ttd_exceeding_block = None;
@@ -239,6 +271,7 @@ impl ExecutionLayer {
if block.total_difficulty >= self.terminal_total_difficulty() {
ttd_exceeding_block = Some(block.block_hash);
// TODO(paul): add a LRU cache for these lookups.
block = engine.api.get_block_by_hash(block.parent_hash).await?;
} else {
return Ok::<_, ApiError>(ttd_exceeding_block);

View File

@@ -380,6 +380,17 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Be extremely careful with the use of this flag.")
.takes_value(true)
)
.arg(
Arg::with_name("fee-recipient")
.long("fee-recipient")
.help("Once the merge has happened, this address will receive transaction fees \
collected from any blocks produced by this node. Defaults to a junk \
address whilst the merge is in development stages. THE DEFAULT VALUE \
WILL BE REMOVED BEFORE THE MERGE ENTERS PRODUCTION")
// TODO: remove this default value. It's just there to make life easy during merge
// testnets.
.default_value("0x000000000000000000000000000000000000000000000001"),
)
/*
* Database purging and compaction.

View File

@@ -237,6 +237,8 @@ pub fn get_config<E: EthSpec>(
client_config.terminal_total_difficulty_override = Some(terminal_total_difficulty);
}
client_config.fee_recipient = clap_utils::parse_optional(cli_args, "fee-recipient")?;
if let Some(freezer_dir) = cli_args.value_of("freezer-dir") {
client_config.freezer_db_path = Some(PathBuf::from(freezer_dir));
}