Strict fee recipient (#3363)

## Issue Addressed

Resolves #3267
Resolves #3156 

## Proposed Changes

- Move the log for fee recipient checks from proposer cache insertion into block proposal so we are directly checking what we get from the EE
- Only log when there is a discrepancy with the local EE, not when using the builder API. In the `builder-api` branch there is an `info` log when there is a discrepancy, I think it is more likely there will be a difference in fee recipient with the builder api because proposer payments might be made via a transaction in the block. Not really sure what patterns will become commong.
- Upgrade the log from a `warn` to an `error` - not actually sure which we want, but I think this is worth an error because the local EE with default transaction ordering I think should pretty much always use the provided fee recipient
- add a `strict-fee-recipient` flag to the VC so we only sign blocks with matching fee recipients. Falls back from the builder API to the local API if there is a discrepancy .




Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
realbigsean
2022-07-26 02:17:24 +00:00
parent b82e2dfc51
commit 904dd62524
8 changed files with 91 additions and 18 deletions

View File

@@ -45,6 +45,7 @@ pub struct BlockServiceBuilder<T, E: EthSpec> {
graffiti: Option<Graffiti>,
graffiti_file: Option<GraffitiFile>,
private_tx_proposals: bool,
strict_fee_recipient: bool,
}
impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
@@ -57,6 +58,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
graffiti: None,
graffiti_file: None,
private_tx_proposals: false,
strict_fee_recipient: false,
}
}
@@ -95,6 +97,11 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
self
}
pub fn strict_fee_recipient(mut self, strict_fee_recipient: bool) -> Self {
self.strict_fee_recipient = strict_fee_recipient;
self
}
pub fn build(self) -> Result<BlockService<T, E>, String> {
Ok(BlockService {
inner: Arc::new(Inner {
@@ -113,6 +120,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
graffiti: self.graffiti,
graffiti_file: self.graffiti_file,
private_tx_proposals: self.private_tx_proposals,
strict_fee_recipient: self.strict_fee_recipient,
}),
})
}
@@ -127,6 +135,7 @@ pub struct Inner<T, E: EthSpec> {
graffiti: Option<Graffiti>,
graffiti_file: Option<GraffitiFile>,
private_tx_proposals: bool,
strict_fee_recipient: bool,
}
/// Attempts to produce attestations for any block producer(s) at the start of the epoch.
@@ -328,6 +337,9 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
let self_ref = &self;
let proposer_index = self.validator_store.validator_index(&validator_pubkey);
let validator_pubkey_ref = &validator_pubkey;
let fee_recipient = self.validator_store.get_fee_recipient(&validator_pubkey);
let strict_fee_recipient = self.strict_fee_recipient;
// Request block from first responsive beacon node.
let block = self
.beacon_nodes
@@ -372,6 +384,17 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
};
drop(get_timer);
// Ensure the correctness of the execution payload's fee recipient.
if strict_fee_recipient {
if let Ok(execution_payload) = block.body().execution_payload() {
if Some(execution_payload.fee_recipient()) != fee_recipient {
return Err(BlockError::Recoverable(
"Incorrect fee recipient used by builder".to_string(),
));
}
}
}
if proposer_index != Some(block.proposer_index()) {
return Err(BlockError::Recoverable(
"Proposer index does not match block proposer. Beacon chain re-orged"

View File

@@ -258,4 +258,17 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
execution payload construction during proposals.")
.takes_value(false),
)
.arg(
Arg::with_name("strict-fee-recipient")
.long("strict-fee-recipient")
.help("If this flag is set, Lighthouse will refuse to sign any block whose \
`fee_recipient` does not match the `suggested_fee_recipient` sent by this validator. \
This applies to both the normal block proposal flow, as well as block proposals \
through the builder API. Proposals through the builder API are more likely to have a \
discrepancy in `fee_recipient` so you should be aware of how your connected relay \
sends proposer payments before using this flag. If this flag is used, a fee recipient \
mismatch in the builder API flow will result in a fallback to the local execution engine \
for payload construction, where a strict fee recipient check will still be applied.")
.takes_value(false),
)
}

View File

@@ -56,6 +56,9 @@ pub struct Config {
/// A list of custom certificates that the validator client will additionally use when
/// connecting to a beacon node over SSL/TLS.
pub beacon_nodes_tls_certs: Option<Vec<PathBuf>>,
/// Enabling this will make sure the validator client never signs a block whose `fee_recipient`
/// does not match the `suggested_fee_recipient`.
pub strict_fee_recipient: bool,
}
impl Default for Config {
@@ -89,6 +92,7 @@ impl Default for Config {
enable_doppelganger_protection: false,
beacon_nodes_tls_certs: None,
private_tx_proposals: false,
strict_fee_recipient: false,
}
}
}
@@ -300,6 +304,10 @@ impl Config {
config.private_tx_proposals = true;
}
if cli_args.is_present("strict-fee-recipient") {
config.strict_fee_recipient = true;
}
Ok(config)
}
}

View File

@@ -414,6 +414,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
.graffiti(config.graffiti)
.graffiti_file(config.graffiti_file.clone())
.private_tx_proposals(config.private_tx_proposals)
.strict_fee_recipient(config.strict_fee_recipient)
.build()?;
let attestation_service = AttestationServiceBuilder::new()