From bc978a9d8c363cfcc82d7049dfb7e46a1c9827be Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 8 Jan 2024 16:27:58 +1100 Subject: [PATCH] Use blocks v3 endpoint in the VC (#4813) Squashed commit of the following: commit f579a043067eec26072baba2bf3a8505c33f8159 Author: Jimmy Chen Date: Mon Jan 8 15:46:10 2024 +1100 Revert changes on making block errors irrecoverable. commit 32cad117601a47b52807f77ad418515f252f1a88 Author: Jimmy Chen Date: Mon Jan 8 15:06:42 2024 +1100 Remove duplicate `current_slot` parameter in `publish_block` function, and remove unnecessary clone. commit b93f16d282edac636739252dd7af6111a2c3e2d6 Author: Michael Sproul Date: Mon Jan 8 12:59:28 2024 +1100 Update CLI flags & book commit a9304c00fddb6108730c0642146c9c8d993b07f8 Author: Michael Sproul Date: Mon Jan 8 12:36:45 2024 +1100 Don't activate at fork; support builder_proposals commit 56c565b66d3819bbbca177af982df72dc797b406 Merge: 3f86e3d6b db05d3767 Author: Michael Sproul Date: Mon Jan 8 12:22:59 2024 +1100 Merge remote-tracking branch 'origin/unstable' into block-v3-vc commit 3f86e3d6b31a0cc565ad57909540b3ca0f5e9e03 Author: Eitan Seri-Levi Date: Tue Jan 2 15:08:40 2024 +0200 merge conflicts commit 2e912f876fdc151a0b38480b126a45afcaf7d35d Merge: 8d5d5a109 01994c438 Author: Eitan Seri-Levi Date: Tue Jan 2 15:01:10 2024 +0200 merge conflicts commit 8d5d5a109bda09c0327aeec35647e51d7462ac35 Author: Eitan Seri-Levi Date: Mon Dec 4 14:44:35 2023 -0800 fixed issues from merge commit bc1a5943676e970211dbe1f0e56ea3a6780643d8 Author: Eitan Seri-Levi Date: Sun Dec 3 13:49:33 2023 -0800 comments commit f0f2442c2dcbc641a2b4bfe1172940f43ee22712 Author: Eitan Seri-Levi Date: Sun Dec 3 13:47:50 2023 -0800 comments commit d108edcc2278c17be9f080a727ad52671ceb044d Author: Eitan Seri-Levi Date: Sun Dec 3 12:13:55 2023 -0800 publish_block_v3 should return irrecoveerable errors commit 6dc3273b734b1ec3e8772908b0f6870fb56fd667 Merge: 13942bf7f 44aaf13ff Author: Eitan Seri-Levi Date: Sun Dec 3 12:11:37 2023 -0800 todos and flag comments commit 13942bf7fe8c6cd4e82271d79e75c83a7cee3deb Author: Eitan Seri-Levi Date: Fri Nov 24 07:19:52 2023 -0800 remove block v3 fallback to v2 commit ad978f6b436e089e9cb64b1e5945b72269f0e017 Author: Eitan Seri-Levi Date: Wed Nov 22 14:24:38 2023 -0800 revert commit 8f3ab53e3f8061719dc626830166c5aaba399e2d Merge: d9a50adfe e856a904e Author: Eitan Seri-Levi Date: Wed Nov 22 14:24:04 2023 -0800 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-vc commit d9a50adfebf488ab399ea8b4d3932d711595786a Author: Eitan Seri-Levi Date: Wed Nov 22 14:22:33 2023 -0800 changes based on feedback commit 282f92bf383a1c9083ccafbe0458a51d7cfa1414 Author: Eitan Seri-Levi Date: Fri Nov 3 13:04:11 2023 +0200 lint commit 412d7ca4c07d818115eea9fb2eaf343539d5f19f Author: Eitan Seri-Levi Date: Fri Nov 3 12:15:32 2023 +0200 cleanup commit 1a50c7f631e8401548784feccca768d1e1a87166 Author: Eitan Seri-Levi Date: Fri Nov 3 12:04:24 2023 +0200 refactor commit fb5e60b384afa775ae542757ed4022047101ad59 Author: Eitan Seri-Levi Date: Fri Nov 3 11:47:35 2023 +0200 refactor commit d34635648c340697b7ec883c154fa8ea5090b410 Author: Eitan Seri-Levi Date: Fri Nov 3 11:18:29 2023 +0200 fix merge commit e2d4fea5de1a27d4078992a0013830de47d16200 Author: Eitan Seri-Levi Date: Fri Nov 3 11:16:12 2023 +0200 fix merge commit be8a2be4dabf0bc0410739206a0a78af1b974300 Merge: 4715e26ab 36d884981 Author: Eitan Seri-Levi Date: Fri Nov 3 11:09:27 2023 +0200 merge conflicts commit 4715e26ab1bbe1f8367e97bdfe797b5b3a3b989b Merge: d2e779017 a9f9dc241 Author: Eitan Seri-Levi Date: Sun Oct 29 16:44:32 2023 +0200 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-vc commit d2e77901735c23732f81daf5d7cfe94c2f562fdb Author: Eitan Seri-Levi Date: Sun Oct 29 16:44:27 2023 +0200 resolve merge conflicts commit 229893ae6293596a10f116ed28f4f5f1aa46ecf1 Merge: 9562d8cc2 cb5f56ec2 Author: Eitan Seri-Levi Date: Thu Oct 26 19:36:25 2023 +0300 Merge branch 'block-v3-endpoint' of https://github.com/eserilev/lighthouse into block-v3-vc commit cb5f56ec2653a8fe60ec317e6ae25c217013962d Author: Eitan Seri-Levi Date: Thu Oct 26 19:23:05 2023 +0300 fmt commit 219ad178c75f4910dc6313df02653e9f0b515d8d Author: Eitan Seri-Levi Date: Thu Oct 26 19:19:54 2023 +0300 header bugfix commit 34a08a183d210441b4c70cd91b552c4669bb12d6 Author: Eitan Seri-Levi Date: Sun Oct 22 01:24:41 2023 +0300 refactor commit 59e1a18288a10d978f8f4aff932aec835f9461c3 Author: Eitan Seri-Levi Date: Sun Oct 22 00:06:05 2023 +0300 remove comments commit 7dc7eced97eae79285fd9fedb1bd016e06365acc Merge: 4d3621e8e 074c4951f Author: Eitan Seri-Levi Date: Sat Oct 21 22:40:15 2023 +0300 resolve merge conflicts commit 9562d8cc2bb9d17756be6d6df9e39c198751a228 Merge: a8ded5621 4d3621e8e Author: Eitan Seri-Levi Date: Sun Oct 15 00:18:22 2023 +0300 Merge branch 'block-v3-endpoint' of https://github.com/eserilev/lighthouse into block-v3-vc commit 4d3621e8e0a29b6cc77f6ac2a0da8fda21850ffa Author: Eitan Seri-Levi Date: Sun Oct 15 00:09:31 2023 +0300 update commit 1667b32aa82b3e1046a2732c51975040ca7541c7 Author: Eitan Seri-Levi Date: Sat Oct 14 23:23:22 2023 +0300 refactor based on feedback commit a8ded56213ba29e2cbffcc96e2021b5401119b28 Author: Eitan Seri-Levi Date: Mon Oct 9 16:34:26 2023 +0300 add produce-block-v3 commit 33f34116515ab65493382bd1d84767574f7ad007 Author: Eitan Seri-Levi Date: Sat Oct 7 18:52:14 2023 +0300 add v3 logic into vc commit dc746c4cc138735cde309b3a973c8b07d636334c Merge: 1d7cbf01c c3321dddb Author: Eitan Seri-Levi Date: Sat Oct 7 18:24:09 2023 +0300 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-endpoint commit d2c7dba1cefe4a007bde21802915d473f020873b Author: Eitan Seri-Levi Date: Sat Oct 7 18:23:49 2023 +0300 add block v3 to vc commit 1b20398a7fb9f4f379118f0f86947f464f420cf8 Merge: 1d7cbf01c c3321dddb Author: Eitan Seri-Levi Date: Sat Oct 7 18:23:23 2023 +0300 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-vc commit 1d7cbf01ccb4c9b3b3c3c681dda50ac7835cd98f Author: Eitan Seri-Levi Date: Fri Oct 6 11:26:24 2023 +0300 clone epoch participation outside of the loop commit 0a70967c5e3c61d7288c5249d4f084b0d6144fad Author: Eitan Seri-Levi Date: Fri Oct 6 01:57:28 2023 +0300 fmt commit 518c6a003f981b423f921a1dfdf4870f532153df Author: Eitan Seri-Levi Date: Fri Oct 6 01:57:04 2023 +0300 clone get_epoch_participation result commit 4b1f396f2ae999e5d092fdd205fddca6e00bd413 Author: Eitan Seri-Levi Date: Thu Oct 5 23:14:37 2023 +0300 prevent setting the participation flag commit 8a674dda718981dc76f26d59b3313919f8c58b5e Author: Eitan Seri-Levi Date: Sun Oct 1 17:34:17 2023 +0300 update header name to consensus block value commit 33254e7847c81e3ca80f8a3077168484d1d3ac13 Merge: 6878abe0f 441fc1691 Author: Eitan Seri-Levi Date: Sun Oct 1 17:24:32 2023 +0300 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-endpoint commit 6878abe0fff5aa8715d09814331a6eb2098af4fc Author: Eitan Seri-Levi Date: Thu Sep 28 17:03:45 2023 +0300 lint commit 91d37e95a42ff2d12248f749a33cf14f8be4cc1c Author: Eitan Seri-Levi Date: Thu Sep 28 16:51:01 2023 +0300 add consensus block value commit df250835c60554c20d4df0a17ddcb52a70d2f743 Author: Eitan Seri-Levi Date: Wed Sep 27 23:23:20 2023 +0300 refactor commit c68e7afcefbd318d6424054486409b633788f7e1 Author: Eitan Seri-Levi Date: Wed Sep 27 23:03:15 2023 +0300 merge unstable commit 6441c52a67eaab7f83c3d1b4f4c0a0ecef14efd9 Merge: 36f863a64 0f05499e3 Author: Eitan Seri-Levi Date: Mon Sep 25 11:37:36 2023 +0300 initial merge commit 36f863a649807173e8cb755a5e80003d9c3f7833 Author: Eitan Seri-Levi Date: Wed Sep 6 09:16:20 2023 +0300 linting commit d5cb547a001b7119bb1af103934220e748b99426 Author: Eitan Seri-Levi Date: Wed Sep 6 08:53:19 2023 +0300 block value fix commit a59a9668959f487b75c0e6f5924f9abf4c2d116a Author: Eitan Seri-Levi Date: Wed Sep 6 08:05:18 2023 +0300 add block value commit 8021687e412facf164b4a18686dedb7f783c64c4 Author: Eitan Seri-Levi Date: Wed Sep 6 07:31:43 2023 +0300 fixed logs commit f285d164bc0a115afed854e3f28ae39d8952723f Author: Eitan Seri-Levi Date: Wed Sep 6 00:27:39 2023 +0300 remove dead code commit 0f0c41e7d933927b0b68cfe46fe7d17c51e486aa Author: Eitan Seri-Levi Date: Tue Sep 5 23:46:25 2023 +0300 cleanup commit 2599fecdc86cc318a1f0f7239e83aebc5dbf92c8 Author: Eitan Seri-Levi Date: Tue Sep 5 23:35:01 2023 +0300 cleanup commit 0edfcbd0e797167ed0ab4b619b2bc954c4fa39d7 Author: Eitan Seri-Levi Date: Tue Sep 5 23:20:06 2023 +0300 fix clippy issues commit ae2b1d3d85265ac475510abaf7d528ffdff2b837 Author: Eitan Seri-Levi Date: Tue Sep 5 18:27:35 2023 +0300 blinded payload test case passing commit 5be31132df9597077fe192ddd13c7c6e99f79687 Author: Eitan Seri-Levi Date: Tue Sep 5 01:28:56 2023 +0300 initial v3 test commit 877973e204c901e9e1a146829c358f2f8f875a10 Author: Eitan Seri-Levi Date: Tue Sep 5 01:07:04 2023 +0300 revert cargo lock commit 03f442fd6648c23a0ba8e1c80859b5dfb7365c34 Author: Eitan Seri-Levi Date: Tue Sep 5 01:04:36 2023 +0300 tests passing commit 682eb619ea9380bab7196f1a0ae825bf981004e1 Author: Eitan Seri-Levi Date: Tue Sep 5 01:03:54 2023 +0300 tests passing commit 272cb76e4ecf8c34ee2ccbecb3afce7258929483 Author: Eitan Seri-Levi Date: Mon Sep 4 20:56:36 2023 +0300 debugging commit 133c289182c4b558f6a2c6b61ec401bf567f5929 Merge: 17f00f36b 2841f6068 Author: Eitan Seri-Levi Date: Mon Sep 4 16:52:08 2023 +0300 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-endpoint commit 17f00f36b4a78ec6591a364bba6f2bc74961cce7 Author: Eitan Seri-Levi Date: Thu Aug 31 20:57:11 2023 +0300 merge v2 flow with v3 commit 50b0d1395f3ca93c022addbec2add11cec000c05 Merge: 54860ac67 74eb26764 Author: Eitan Seri-Levi Date: Thu Aug 31 19:46:09 2023 +0300 merge conflicts commit 54860ac67153751b96842f8a20ef79a8f1391640 Author: Eitan Seri-Levi Date: Mon Aug 21 12:49:50 2023 +0300 some debugging commit bac744e2571434485847ad85d7fbe1dab997e88c Author: Eitan Seri-Levi Date: Thu Aug 17 00:48:11 2023 +0300 added v3 endpoint commit 281d88da2f5cb96260c481479928ebed9448ed06 Merge: 1802e45d8 59c24bcd2 Author: Eitan Seri-Levi Date: Thu Aug 17 00:14:23 2023 +0300 Merge branch 'unstable' of https://github.com/sigp/lighthouse into block-v3-endpoint commit 1802e45d81192d59fa57fa8c19e8099308e1e864 Author: Eitan Seri-Levi Date: Thu Aug 17 00:14:20 2023 +0300 add new endpoint version commit ae690fb5ce23e6c520283aae348888f548cd8875 Author: Eitan Seri-Levi Date: Wed Aug 16 18:40:35 2023 +0300 add api logic commit 48661b4eb30def829d6af70b170628d11c604395 Author: Eitan Seri-Levi Date: Wed Aug 16 18:34:53 2023 +0300 add api logic commit 5d984f0c583873b0abd60064e147a183e01d2c71 Author: Eitan Seri-Levi Date: Wed Aug 16 14:20:03 2023 +0300 the full flow... commit a55e91db9d197214c11334663666f102b775c4ad Author: Eitan Seri-Levi Date: Tue Aug 15 23:14:02 2023 +0300 continue refactor commit 6515ce48f1f549eeb404c22a7661150f25ed7876 Author: Eitan Seri-Levi Date: Tue Aug 15 00:46:58 2023 +0300 block v3 flow commit cd36be539a23ee3f2a4422386bcbf0bc564bcf2c Author: Eitan Seri-Levi Date: Tue Aug 15 00:44:11 2023 +0300 block v3 flow commit 2cf2dec525cb286b58ee0ac6ddf672574bcdb2a7 Author: Eitan Seri-Levi Date: Mon Aug 14 20:36:34 2023 +0300 block v3 endpoint init --- book/src/help_vc.md | 4 + lighthouse/tests/validator_client.rs | 15 + validator_client/src/block_service.rs | 403 ++++++++++++++++++------ validator_client/src/cli.rs | 9 + validator_client/src/config.rs | 7 + validator_client/src/validator_store.rs | 6 + 6 files changed, 347 insertions(+), 97 deletions(-) diff --git a/book/src/help_vc.md b/book/src/help_vc.md index 4471b0e1044..62b64efd411 100644 --- a/book/src/help_vc.md +++ b/book/src/help_vc.md @@ -56,6 +56,10 @@ FLAGS: machine. Note that logs can often contain sensitive information about your validator and so this flag should be used with caution. For Windows users, the log file permissions will be inherited from the parent folder. --metrics Enable the Prometheus metrics HTTP server. Disabled by default. + --produce-block-v3 + Enable block production via the block v3 endpoint for this validator 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 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. diff --git a/lighthouse/tests/validator_client.rs b/lighthouse/tests/validator_client.rs index 4234de613da..025188fed4e 100644 --- a/lighthouse/tests/validator_client.rs +++ b/lighthouse/tests/validator_client.rs @@ -421,6 +421,21 @@ fn no_doppelganger_protection_flag() { .run() .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] +fn no_produce_block_v3_flag() { + CommandLineTest::new() + .run() + .with_config(|config| assert!(!config.produce_block_v3)); +} + #[test] fn no_gas_limit_flag() { CommandLineTest::new() diff --git a/validator_client/src/block_service.rs b/validator_client/src/block_service.rs index 00d9b2e86db..b65e301de86 100644 --- a/validator_client/src/block_service.rs +++ b/validator_client/src/block_service.rs @@ -28,7 +28,10 @@ use types::{ #[derive(Debug)] pub enum BlockError { + /// A recoverable error that can be retried, as the validator has not signed anything. Recoverable(String), + /// An irrecoverable error has occurred during block proposal and should not be retried, as a + /// block may have already been signed. Irrecoverable(String), } @@ -320,89 +323,212 @@ impl BlockService { ) } - 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; + if self.validator_store.produce_block_v3() { + for validator_pubkey in proposers { + let builder_proposals = self + .validator_store + .get_builder_proposals(&validator_pubkey); + // Translate `builder_proposals` to a boost factor. Builder proposals set to `true` + // requires no boost factor, it just means "use a builder proposal if the BN returns + // one". On the contrary, `builder_proposals: false` indicates a preference for + // local payloads, so we set the builder boost factor to 0. + let builder_boost_factor = if !builder_proposals { Some(0) } else { None }; + let service = self.clone(); + let log = log.clone(); + self.inner.context.executor.spawn( + async move { + let result = service + .publish_block_v3(slot, validator_pubkey, builder_boost_factor) + .await; + match result { - Err(BlockError::Recoverable(e)) => { + Ok(_) => {} + Err(BlockError::Recoverable(e)) | Err(BlockError::Irrecoverable(e)) => { error!( log, "Error whilst producing block"; "error" => ?e, "block_slot" => ?slot, - "info" => "blinded proposal failed, attempting full block" + "info" => "block v3 proposal failed, this error may or may not result in a missed 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!( + } + } + }, + "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" => "full block attempted after a blinded failure", + "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!( + 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"; - "error" => ?e, + "message" => ?e, "block_slot" => ?slot, - "info" => "this error may or may not result in a missed block", - ) + "info" => "proposal did not use a builder", + ); } - 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", - ); + }, + "block service", + ) + } } Ok(()) } - /// Produce a block at the given slot for validator_pubkey - async fn publish_block( + #[allow(clippy::too_many_arguments)] + async fn sign_and_publish_block( &self, + proposer_fallback: ProposerFallback, + slot: Slot, + graffiti: Option, + validator_pubkey: &PublicKeyBytes, + unsigned_block: UnsignedBlock, + ) -> Result<(), BlockError> { + let log = self.context.log(); + let signing_timer = metrics::start_timer(&metrics::BLOCK_SIGNING_TIMES); + + let res = match unsigned_block { + UnsignedBlock::Full(block_contents) => { + let (block, maybe_blobs) = block_contents.deconstruct(); + self.validator_store + .sign_block(*validator_pubkey, block, slot) + .await + .map(|b| SignedBlock::Full(PublishBlockRequest::new(b, maybe_blobs))) + } + UnsignedBlock::Blinded(block) => self + .validator_store + .sign_block(*validator_pubkey, block, slot) + .await + .map(SignedBlock::Blinded), + }; + + let signed_block = match res { + Ok(block) => block, + 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 signing_time_ms = + Duration::from_secs_f64(signing_timer.map_or(0.0, |t| t.stop_and_record())).as_millis(); + + info!( + log, + "Publishing signed block"; + "slot" => slot.as_u64(), + "signing_time_ms" => signing_time_ms, + ); + + // Publish block with first available beacon node. + // + // Try the proposer nodes first, since we've likely gone to efforts to + // protect them from DoS attacks and they're most likely to successfully + // publish a block. + proposer_fallback + .request_proposers_first( + RequireSynced::No, + OfflineOnFailure::Yes, + |beacon_node| async { + self.publish_signed_block_contents(&signed_block, beacon_node) + .await + }, + ) + .await?; + + info!( + log, + "Successfully published block"; + "block_type" => ?signed_block.block_type(), + "deposits" => signed_block.num_deposits(), + "attestations" => signed_block.num_attestations(), + "graffiti" => ?graffiti.map(|g| g.as_utf8_lossy()), + "slot" => signed_block.slot().as_u64(), + ); + Ok(()) + } + + async fn publish_block_v3( + self, slot: Slot, validator_pubkey: PublicKeyBytes, - builder_proposal: bool, + builder_boost_factor: Option, ) -> Result<(), BlockError> { let log = self.context.log(); let _timer = metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::BEACON_BLOCK]); - let current_slot = self.slot_clock.now().ok_or_else(|| { - BlockError::Recoverable("Unable to determine current slot from clock".to_string()) - })?; - let randao_reveal = match self .validator_store .randao_reveal(validator_pubkey, slot.epoch(E::slots_per_epoch())) @@ -440,7 +566,6 @@ impl BlockService { let randao_reveal_ref = &randao_reveal; let self_ref = &self; let proposer_index = self.validator_store.validator_index(&validator_pubkey); - let validator_pubkey_ref = &validator_pubkey; let proposer_fallback = ProposerFallback { beacon_nodes: self.beacon_nodes.clone(), proposer_nodes: self.proposer_nodes.clone(), @@ -460,40 +585,63 @@ impl BlockService { .request_proposers_last( RequireSynced::No, OfflineOnFailure::Yes, - move |beacon_node| { - Self::get_validator_block( + |beacon_node| async move { + let _get_timer = metrics::start_timer_vec( + &metrics::BLOCK_SERVICE_TIMES, + &[metrics::BEACON_BLOCK_HTTP_GET], + ); + let block_response = Self::get_validator_block_v3( beacon_node, slot, randao_reveal_ref, graffiti, proposer_index, - builder_proposal, + builder_boost_factor, log, ) + .await + .map_err(|e| { + BlockError::Recoverable(format!( + "Error from beacon node when producing block: {:?}", + e + )) + }); + + Ok::<_, BlockError>(block_response) }, ) + .await??; + + self_ref + .sign_and_publish_block( + proposer_fallback, + slot, + graffiti, + &validator_pubkey, + unsigned_block, + ) .await?; - let signing_timer = metrics::start_timer(&metrics::BLOCK_SIGNING_TIMES); + Ok(()) + } - let res = match unsigned_block { - UnsignedBlock::Full(block_contents) => { - let (block, maybe_blobs) = block_contents.deconstruct(); - self_ref - .validator_store - .sign_block(*validator_pubkey_ref, block, current_slot) - .await - .map(|b| SignedBlock::Full(PublishBlockRequest::new(b, maybe_blobs))) - } - UnsignedBlock::Blinded(block) => self_ref - .validator_store - .sign_block(*validator_pubkey_ref, block, current_slot) - .await - .map(SignedBlock::Blinded), - }; + /// 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 signed_block = match res { - Ok(block) => 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. @@ -514,41 +662,59 @@ impl BlockService { } }; - let signing_time_ms = - Duration::from_secs_f64(signing_timer.map_or(0.0, |t| t.stop_and_record())).as_millis(); + 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, - "Publishing signed block"; + "Requesting unsigned block"; "slot" => slot.as_u64(), - "signing_time_ms" => signing_time_ms, ); - // Publish block with first available beacon node. + // Request block from first responsive beacon node. // - // Try the proposer nodes first, since we've likely gone to efforts to - // protect them from DoS attacks and they're most likely to successfully - // publish a block. - proposer_fallback - .request_proposers_first( + // 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, - |beacon_node| async { - self.publish_signed_block_contents(&signed_block, beacon_node) - .await + move |beacon_node| { + Self::get_validator_block( + beacon_node, + slot, + randao_reveal_ref, + graffiti, + proposer_index, + builder_proposal, + log, + ) }, ) .await?; - info!( - log, - "Successfully published block"; - "block_type" => ?signed_block.block_type(), - "deposits" => signed_block.num_deposits(), - "attestations" => signed_block.num_attestations(), - "graffiti" => ?graffiti.map(|g| g.as_utf8_lossy()), - "slot" => signed_block.slot().as_u64(), - ); + self_ref + .sign_and_publish_block( + proposer_fallback, + slot, + graffiti, + &validator_pubkey, + unsigned_block, + ) + .await?; Ok(()) } @@ -585,6 +751,49 @@ impl BlockService { Ok::<_, BlockError>(()) } + async fn get_validator_block_v3( + beacon_node: &BeaconNodeHttpClient, + slot: Slot, + randao_reveal_ref: &SignatureBytes, + graffiti: Option, + proposer_index: Option, + builder_boost_factor: Option, + log: &Logger, + ) -> Result, BlockError> { + let (block_response, _) = beacon_node + .get_validator_blocks_v3::( + slot, + randao_reveal_ref, + graffiti.as_ref(), + builder_boost_factor, + ) + .await + .map_err(|e| { + BlockError::Recoverable(format!( + "Error from beacon node when producing block: {:?}", + e + )) + })?; + + let unsigned_block = match block_response.data { + eth2::types::ProduceBlockV3Response::Full(block) => UnsignedBlock::Full(block), + eth2::types::ProduceBlockV3Response::Blinded(block) => UnsignedBlock::Blinded(block), + }; + + 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) + } + async fn get_validator_block( beacon_node: &BeaconNodeHttpClient, slot: Slot, diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index 6957934fb8d..cd3ad494dad 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -136,6 +136,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .value_name("FEE-RECIPIENT") .takes_value(true) ) + .arg( + Arg::with_name("produce-block-v3") + .long("produce-block-v3") + .help("Enable block production via the block v3 endpoint for this validator 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.") + .takes_value(false) + ) /* REST API related arguments */ .arg( Arg::with_name("http") diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index 95d42d6d83a..4b7da764287 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -75,6 +75,8 @@ pub struct Config { pub enable_latency_measurement_service: bool, /// Defines the number of validators per `validator/register_validator` request sent to the BN. pub validator_registration_batch_size: usize, + /// Enables block production via the block v3 endpoint. This configuration option can be removed post deneb. + pub produce_block_v3: bool, } impl Default for Config { @@ -115,6 +117,7 @@ impl Default for Config { broadcast_topics: vec![ApiTopic::Subscriptions], enable_latency_measurement_service: true, validator_registration_batch_size: 500, + produce_block_v3: false, } } } @@ -339,6 +342,10 @@ impl Config { config.builder_proposals = true; } + if cli_args.is_present("produce-block-v3") { + config.produce_block_v3 = true; + } + config.gas_limit = cli_args .value_of("gas-limit") .map(|gas_limit| { diff --git a/validator_client/src/validator_store.rs b/validator_client/src/validator_store.rs index 60155d8efb7..19726c2aec1 100644 --- a/validator_client/src/validator_store.rs +++ b/validator_client/src/validator_store.rs @@ -97,6 +97,7 @@ pub struct ValidatorStore { fee_recipient_process: Option
, gas_limit: Option, builder_proposals: bool, + produce_block_v3: bool, task_executor: TaskExecutor, _phantom: PhantomData, } @@ -128,6 +129,7 @@ impl ValidatorStore { fee_recipient_process: config.fee_recipient, gas_limit: config.gas_limit, builder_proposals: config.builder_proposals, + produce_block_v3: config.produce_block_v3, task_executor, _phantom: PhantomData, } @@ -336,6 +338,10 @@ impl ValidatorStore { 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 /// by doppelganger protection. fn doppelganger_checked_signing_method(