default vc to block v3 endpoint and deprecate block-v3 flag (#5292)

* default vc to block v3 endpoint and deprecate block-v3 flag

* kick off ci

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into default-vc-to-block-v3-endpoint

* fix formatting in cli docs

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into default-vc-to-block-v3-endpoint

* Resolve merge conflicts

* resolve conflicts

* merge

* merge

* revert

* retry

* fix

* Merge branch 'unstable' into default-vc-to-block-v3-endpoint

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into default-vc-to-block-v3-endpoint

* fix issues w/ fallback sim
This commit is contained in:
Eitan Seri-Levi
2024-07-25 22:53:18 -07:00
committed by GitHub
parent b949db0a8b
commit f60503cd40
8 changed files with 41 additions and 289 deletions

View File

@@ -265,10 +265,7 @@ Flags:
If this flag is set, Lighthouse will always prefer blocks constructed If this flag is set, Lighthouse will always prefer blocks constructed
by builders, regardless of payload value. by builders, regardless of payload value.
--produce-block-v3 --produce-block-v3
Enable block production via the block v3 endpoint for this validator This flag is deprecated and is no longer in use.
client. This should only be enabled when paired with a beacon node
that has this endpoint implemented. This flag will be enabled by
default in future.
--unencrypted-http-transport --unencrypted-http-transport
This is a safety flag to ensure that the user is aware that the http This is a safety flag to ensure that the user is aware that the http
transport is unencrypted and using a custom HTTP address is unsafe. transport is unencrypted and using a custom HTTP address is unsafe.

View File

@@ -423,19 +423,12 @@ fn no_doppelganger_protection_flag() {
.run() .run()
.with_config(|config| assert!(!config.enable_doppelganger_protection)); .with_config(|config| assert!(!config.enable_doppelganger_protection));
} }
#[test]
fn produce_block_v3_flag() {
CommandLineTest::new()
.flag("produce-block-v3", None)
.run()
.with_config(|config| assert!(config.produce_block_v3));
}
#[test] #[test]
fn no_produce_block_v3_flag() { fn produce_block_v3_flag() {
CommandLineTest::new() // The flag is DEPRECATED but providing it should not trigger an error.
.run() // We can delete this test when deleting the flag entirely.
.with_config(|config| assert!(!config.produce_block_v3)); CommandLineTest::new().flag("produce-block-v3", None).run();
} }
#[test] #[test]

View File

@@ -77,7 +77,6 @@ pub fn cli_app() -> Command {
) )
.arg( .arg(
Arg::new("vc-count") Arg::new("vc-count")
.short('c')
.long("vc-count") .long("vc-count")
.action(ArgAction::Set) .action(ArgAction::Set)
.default_value("3") .default_value("3")

View File

@@ -323,105 +323,32 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
) )
} }
if self.validator_store.produce_block_v3() { for validator_pubkey in proposers {
for validator_pubkey in proposers { let builder_boost_factor = self.get_builder_boost_factor(&validator_pubkey);
let builder_boost_factor = self.get_builder_boost_factor(&validator_pubkey); let service = self.clone();
let service = self.clone(); let log = log.clone();
let log = log.clone(); self.inner.context.executor.spawn(
self.inner.context.executor.spawn( async move {
async move { let result = service
let result = service .publish_block(slot, validator_pubkey, builder_boost_factor)
.publish_block_v3(slot, validator_pubkey, builder_boost_factor) .await;
.await;
match result { match result {
Ok(_) => {} Ok(_) => {}
Err(BlockError::Recoverable(e)) | Err(BlockError::Irrecoverable(e)) => { Err(BlockError::Recoverable(e)) | Err(BlockError::Irrecoverable(e)) => {
error!( error!(
log, log,
"Error whilst producing block"; "Error whilst producing block";
"error" => ?e, "error" => ?e,
"block_slot" => ?slot, "block_slot" => ?slot,
"info" => "block v3 proposal failed, this error may or may not result in a missed block" "info" => "block v3 proposal failed, this error may or may not result in a missed block"
); );
}
} }
}, }
"block service", },
) "block service",
} )
} else {
for validator_pubkey in proposers {
let builder_proposals = self
.validator_store
.get_builder_proposals(&validator_pubkey);
let service = self.clone();
let log = log.clone();
self.inner.context.executor.spawn(
async move {
if builder_proposals {
let result = service
.publish_block(slot, validator_pubkey, true)
.await;
match result {
Err(BlockError::Recoverable(e)) => {
error!(
log,
"Error whilst producing block";
"error" => ?e,
"block_slot" => ?slot,
"info" => "blinded proposal failed, attempting full block"
);
if let Err(e) = service
.publish_block(slot, validator_pubkey, false)
.await
{
// Log a `crit` since a full block
// (non-builder) proposal failed.
crit!(
log,
"Error whilst producing block";
"error" => ?e,
"block_slot" => ?slot,
"info" => "full block attempted after a blinded failure",
);
}
}
Err(BlockError::Irrecoverable(e)) => {
// Only log an `error` since it's common for
// builders to timeout on their response, only
// to publish the block successfully themselves.
error!(
log,
"Error whilst producing block";
"error" => ?e,
"block_slot" => ?slot,
"info" => "this error may or may not result in a missed block",
)
}
Ok(_) => {}
};
} else if let Err(e) = service
.publish_block(slot, validator_pubkey, false)
.await
{
// Log a `crit` since a full block (non-builder)
// proposal failed.
crit!(
log,
"Error whilst producing block";
"message" => ?e,
"block_slot" => ?slot,
"info" => "proposal did not use a builder",
);
}
},
"block service",
)
}
} }
Ok(()) Ok(())
} }
@@ -513,7 +440,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
Ok(()) Ok(())
} }
async fn publish_block_v3( async fn publish_block(
self, self,
slot: Slot, slot: Slot,
validator_pubkey: PublicKeyBytes, validator_pubkey: PublicKeyBytes,
@@ -584,7 +511,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
&metrics::BLOCK_SERVICE_TIMES, &metrics::BLOCK_SERVICE_TIMES,
&[metrics::BEACON_BLOCK_HTTP_GET], &[metrics::BEACON_BLOCK_HTTP_GET],
); );
let block_response = Self::get_validator_block_v3( Self::get_validator_block(
beacon_node, beacon_node,
slot, slot,
randao_reveal_ref, randao_reveal_ref,
@@ -599,103 +526,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
"Error from beacon node when producing block: {:?}", "Error from beacon node when producing block: {:?}",
e e
)) ))
}); })
Ok::<_, BlockError>(block_response)
},
)
.await??;
self_ref
.sign_and_publish_block(
proposer_fallback,
slot,
graffiti,
&validator_pubkey,
unsigned_block,
)
.await?;
Ok(())
}
/// Produce a block at the given slot for validator_pubkey
async fn publish_block(
&self,
slot: Slot,
validator_pubkey: PublicKeyBytes,
builder_proposal: bool,
) -> Result<(), BlockError> {
let log = self.context.log();
let _timer =
metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::BEACON_BLOCK]);
let randao_reveal = match self
.validator_store
.randao_reveal(validator_pubkey, slot.epoch(E::slots_per_epoch()))
.await
{
Ok(signature) => signature.into(),
Err(ValidatorStoreError::UnknownPubkey(pubkey)) => {
// A pubkey can be missing when a validator was recently removed
// via the API.
warn!(
log,
"Missing pubkey for block";
"info" => "a validator may have recently been removed from this VC",
"pubkey" => ?pubkey,
"slot" => ?slot
);
return Ok(());
}
Err(e) => {
return Err(BlockError::Recoverable(format!(
"Unable to sign block: {:?}",
e
)))
}
};
let graffiti = determine_graffiti(
&validator_pubkey,
log,
self.graffiti_file.clone(),
self.validator_store.graffiti(&validator_pubkey),
self.graffiti,
);
let randao_reveal_ref = &randao_reveal;
let self_ref = &self;
let proposer_index = self.validator_store.validator_index(&validator_pubkey);
let proposer_fallback = ProposerFallback {
beacon_nodes: self.beacon_nodes.clone(),
proposer_nodes: self.proposer_nodes.clone(),
};
info!(
log,
"Requesting unsigned block";
"slot" => slot.as_u64(),
);
// Request block from first responsive beacon node.
//
// Try the proposer nodes last, since it's likely that they don't have a
// great view of attestations on the network.
let unsigned_block = proposer_fallback
.request_proposers_last(
RequireSynced::No,
OfflineOnFailure::Yes,
move |beacon_node| {
Self::get_validator_block(
beacon_node,
slot,
randao_reveal_ref,
graffiti,
proposer_index,
builder_proposal,
log,
)
}, },
) )
.await?; .await?;
@@ -745,7 +576,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
Ok::<_, BlockError>(()) Ok::<_, BlockError>(())
} }
async fn get_validator_block_v3( async fn get_validator_block(
beacon_node: &BeaconNodeHttpClient, beacon_node: &BeaconNodeHttpClient,
slot: Slot, slot: Slot,
randao_reveal_ref: &SignatureBytes, randao_reveal_ref: &SignatureBytes,
@@ -788,65 +619,6 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
Ok::<_, BlockError>(unsigned_block) Ok::<_, BlockError>(unsigned_block)
} }
async fn get_validator_block(
beacon_node: &BeaconNodeHttpClient,
slot: Slot,
randao_reveal_ref: &SignatureBytes,
graffiti: Option<Graffiti>,
proposer_index: Option<u64>,
builder_proposal: bool,
log: &Logger,
) -> Result<UnsignedBlock<E>, BlockError> {
let unsigned_block = if !builder_proposal {
let _get_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES,
&[metrics::BEACON_BLOCK_HTTP_GET],
);
UnsignedBlock::Full(
beacon_node
.get_validator_blocks::<E>(slot, randao_reveal_ref, graffiti.as_ref())
.await
.map_err(|e| {
BlockError::Recoverable(format!(
"Error from beacon node when producing block: {:?}",
e
))
})?
.data,
)
} else {
let _get_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES,
&[metrics::BLINDED_BEACON_BLOCK_HTTP_GET],
);
UnsignedBlock::Blinded(
beacon_node
.get_validator_blinded_blocks::<E>(slot, randao_reveal_ref, graffiti.as_ref())
.await
.map_err(|e| {
BlockError::Recoverable(format!(
"Error from beacon node when producing block: {:?}",
e
))
})?
.data,
)
};
info!(
log,
"Received unsigned block";
"slot" => slot.as_u64(),
);
if proposer_index != Some(unsigned_block.proposer_index()) {
return Err(BlockError::Recoverable(
"Proposer index does not match block proposer. Beacon chain re-orged".to_string(),
));
}
Ok::<_, BlockError>(unsigned_block)
}
/// Returns the builder boost factor of the given public key. /// Returns the builder boost factor of the given public key.
/// The priority order for fetching this value is: /// The priority order for fetching this value is:
/// ///

View File

@@ -170,10 +170,7 @@ pub fn cli_app() -> Command {
.arg( .arg(
Arg::new("produce-block-v3") Arg::new("produce-block-v3")
.long("produce-block-v3") .long("produce-block-v3")
.help("Enable block production via the block v3 endpoint for this validator client. \ .help("This flag is deprecated and is no longer in use.")
This should only be enabled when paired with a beacon node \
that has this endpoint implemented. This flag will be enabled by default in \
future.")
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.help_heading(FLAG_HEADER) .help_heading(FLAG_HEADER)
.display_order(0) .display_order(0)

View File

@@ -79,8 +79,6 @@ pub struct Config {
pub validator_registration_batch_size: usize, pub validator_registration_batch_size: usize,
/// Enable slashing protection even while using web3signer keys. /// Enable slashing protection even while using web3signer keys.
pub enable_web3signer_slashing_protection: bool, pub enable_web3signer_slashing_protection: bool,
/// Enables block production via the block v3 endpoint. This configuration option can be removed post deneb.
pub produce_block_v3: bool,
/// Specifies the boost factor, a percentage multiplier to apply to the builder's payload value. /// Specifies the boost factor, a percentage multiplier to apply to the builder's payload value.
pub builder_boost_factor: Option<u64>, pub builder_boost_factor: Option<u64>,
/// If true, Lighthouse will prefer builder proposals, if available. /// If true, Lighthouse will prefer builder proposals, if available.
@@ -130,7 +128,6 @@ impl Default for Config {
enable_latency_measurement_service: true, enable_latency_measurement_service: true,
validator_registration_batch_size: 500, validator_registration_batch_size: 500,
enable_web3signer_slashing_protection: true, enable_web3signer_slashing_protection: true,
produce_block_v3: false,
builder_boost_factor: None, builder_boost_factor: None,
prefer_builder_proposals: false, prefer_builder_proposals: false,
distributed: false, distributed: false,
@@ -380,14 +377,18 @@ impl Config {
config.builder_proposals = true; config.builder_proposals = true;
} }
if cli_args.get_flag("produce-block-v3") {
config.produce_block_v3 = true;
}
if cli_args.get_flag("prefer-builder-proposals") { if cli_args.get_flag("prefer-builder-proposals") {
config.prefer_builder_proposals = true; config.prefer_builder_proposals = true;
} }
if cli_args.get_flag("produce-block-v3") {
warn!(
log,
"produce-block-v3 flag";
"note" => "deprecated flag has no effect and should be removed"
);
}
config.gas_limit = cli_args config.gas_limit = cli_args
.get_one::<String>("gas-limit") .get_one::<String>("gas-limit")
.map(|gas_limit| { .map(|gas_limit| {

View File

@@ -11,7 +11,6 @@ pub const UNREGISTERED: &str = "unregistered";
pub const FULL_UPDATE: &str = "full_update"; pub const FULL_UPDATE: &str = "full_update";
pub const BEACON_BLOCK: &str = "beacon_block"; pub const BEACON_BLOCK: &str = "beacon_block";
pub const BEACON_BLOCK_HTTP_GET: &str = "beacon_block_http_get"; pub const BEACON_BLOCK_HTTP_GET: &str = "beacon_block_http_get";
pub const BLINDED_BEACON_BLOCK_HTTP_GET: &str = "blinded_beacon_block_http_get";
pub const BEACON_BLOCK_HTTP_POST: &str = "beacon_block_http_post"; pub const BEACON_BLOCK_HTTP_POST: &str = "beacon_block_http_post";
pub const BLINDED_BEACON_BLOCK_HTTP_POST: &str = "blinded_beacon_block_http_post"; pub const BLINDED_BEACON_BLOCK_HTTP_POST: &str = "blinded_beacon_block_http_post";
pub const ATTESTATIONS: &str = "attestations"; pub const ATTESTATIONS: &str = "attestations";

View File

@@ -71,7 +71,6 @@ pub struct ValidatorStore<T, E: EthSpec> {
gas_limit: Option<u64>, gas_limit: Option<u64>,
builder_proposals: bool, builder_proposals: bool,
enable_web3signer_slashing_protection: bool, enable_web3signer_slashing_protection: bool,
produce_block_v3: bool,
prefer_builder_proposals: bool, prefer_builder_proposals: bool,
builder_boost_factor: Option<u64>, builder_boost_factor: Option<u64>,
task_executor: TaskExecutor, task_executor: TaskExecutor,
@@ -106,7 +105,6 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
gas_limit: config.gas_limit, gas_limit: config.gas_limit,
builder_proposals: config.builder_proposals, builder_proposals: config.builder_proposals,
enable_web3signer_slashing_protection: config.enable_web3signer_slashing_protection, enable_web3signer_slashing_protection: config.enable_web3signer_slashing_protection,
produce_block_v3: config.produce_block_v3,
prefer_builder_proposals: config.prefer_builder_proposals, prefer_builder_proposals: config.prefer_builder_proposals,
builder_boost_factor: config.builder_boost_factor, builder_boost_factor: config.builder_boost_factor,
task_executor, task_executor,
@@ -321,10 +319,6 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
self.spec.fork_at_epoch(epoch) self.spec.fork_at_epoch(epoch)
} }
pub fn produce_block_v3(&self) -> bool {
self.produce_block_v3
}
/// Returns a `SigningMethod` for `validator_pubkey` *only if* that validator is considered safe /// Returns a `SigningMethod` for `validator_pubkey` *only if* that validator is considered safe
/// by doppelganger protection. /// by doppelganger protection.
fn doppelganger_checked_signing_method( fn doppelganger_checked_signing_method(