mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Register validator api (#3194)
## Issue Addressed Lays the groundwork for builder API changes by implementing the beacon-API's new `register_validator` endpoint ## Proposed Changes - Add a routine in the VC that runs on startup (re-try until success), once per epoch or whenever `suggested_fee_recipient` is updated, signing `ValidatorRegistrationData` and sending it to the BN. - TODO: `gas_limit` config options https://github.com/ethereum/builder-specs/issues/17 - BN only sends VC registration data to builders on demand, but VC registration data *does update* the BN's prepare proposer cache and send an updated fcU to a local EE. This is necessary for fee recipient consistency between the blinded and full block flow in the event of fallback. Having the BN only send registration data to builders on demand gives feedback directly to the VC about relay status. Also, since the BN has no ability to sign these messages anyways (so couldn't refresh them if it wanted), and validator registration is independent of the BN head, I think this approach makes sense. - Adds upcoming consensus spec changes for this PR https://github.com/ethereum/consensus-specs/pull/2884 - I initially applied the bit mask based on a configured application domain.. but I ended up just hard coding it here instead because that's how it's spec'd in the builder repo. - Should application mask appear in the api? Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
@@ -11,6 +11,7 @@ use eth2::{
|
||||
types::*,
|
||||
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
|
||||
};
|
||||
use execution_layer::test_utils::MockExecutionLayer;
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
use futures::FutureExt;
|
||||
use lighthouse_network::{Enr, EnrExt, PeerId};
|
||||
@@ -24,6 +25,7 @@ use task_executor::test_utils::TestRuntime;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::time::Duration;
|
||||
use tree_hash::TreeHash;
|
||||
use types::application_domain::ApplicationDomain;
|
||||
use types::{
|
||||
AggregateSignature, BeaconState, BitList, Domain, EthSpec, Hash256, Keypair, MainnetEthSpec,
|
||||
RelativeEpoch, SelectionProof, SignedRoot, Slot,
|
||||
@@ -64,6 +66,9 @@ struct ApiTester {
|
||||
network_rx: mpsc::UnboundedReceiver<NetworkMessage<E>>,
|
||||
local_enr: Enr,
|
||||
external_peer_id: PeerId,
|
||||
// This is never directly accessed, but adding it creates a payload cache, which we use in tests here.
|
||||
#[allow(dead_code)]
|
||||
mock_el: Option<MockExecutionLayer<E>>,
|
||||
_runtime: TestRuntime,
|
||||
}
|
||||
|
||||
@@ -80,6 +85,7 @@ impl ApiTester {
|
||||
.spec(spec.clone())
|
||||
.deterministic_keypairs(VALIDATOR_COUNT)
|
||||
.fresh_ephemeral_store()
|
||||
.mock_execution_layer()
|
||||
.build();
|
||||
|
||||
harness.advance_slot();
|
||||
@@ -214,6 +220,7 @@ impl ApiTester {
|
||||
network_rx,
|
||||
local_enr,
|
||||
external_peer_id,
|
||||
mock_el: harness.mock_execution_layer,
|
||||
_runtime: harness.runtime,
|
||||
}
|
||||
}
|
||||
@@ -293,6 +300,7 @@ impl ApiTester {
|
||||
network_rx,
|
||||
local_enr,
|
||||
external_peer_id,
|
||||
mock_el: None,
|
||||
_runtime: harness.runtime,
|
||||
}
|
||||
}
|
||||
@@ -2226,6 +2234,66 @@ impl ApiTester {
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_post_validator_register_validator(self) -> Self {
|
||||
let mut registrations = vec![];
|
||||
let mut fee_recipients = vec![];
|
||||
|
||||
let fork = self.chain.head().unwrap().beacon_state.fork();
|
||||
|
||||
for (val_index, keypair) in self.validator_keypairs.iter().enumerate() {
|
||||
let pubkey = keypair.pk.compress();
|
||||
let fee_recipient = Address::from_low_u64_be(val_index as u64);
|
||||
|
||||
let data = ValidatorRegistrationData {
|
||||
fee_recipient,
|
||||
gas_limit: 0,
|
||||
timestamp: 0,
|
||||
pubkey,
|
||||
};
|
||||
let domain = self.chain.spec.get_domain(
|
||||
Epoch::new(0),
|
||||
Domain::ApplicationMask(ApplicationDomain::Builder),
|
||||
&fork,
|
||||
Hash256::zero(),
|
||||
);
|
||||
let message = data.signing_root(domain);
|
||||
let signature = keypair.sk.sign(message);
|
||||
|
||||
fee_recipients.push(fee_recipient);
|
||||
registrations.push(SignedValidatorRegistrationData {
|
||||
message: data,
|
||||
signature,
|
||||
});
|
||||
}
|
||||
|
||||
self.client
|
||||
.post_validator_register_validator(®istrations)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for (val_index, (_, fee_recipient)) in self
|
||||
.chain
|
||||
.head()
|
||||
.unwrap()
|
||||
.beacon_state
|
||||
.validators()
|
||||
.into_iter()
|
||||
.zip(fee_recipients.into_iter())
|
||||
.enumerate()
|
||||
{
|
||||
let actual = self
|
||||
.chain
|
||||
.execution_layer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_suggested_fee_recipient(val_index as u64)
|
||||
.await;
|
||||
assert_eq!(actual, fee_recipient);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn test_get_lighthouse_health(self) -> Self {
|
||||
self.client.get_lighthouse_health().await.unwrap();
|
||||
@@ -2973,6 +3041,14 @@ async fn get_validator_beacon_committee_subscriptions() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn post_validator_register_validator() {
|
||||
ApiTester::new()
|
||||
.await
|
||||
.test_post_validator_register_validator()
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn lighthouse_endpoints() {
|
||||
ApiTester::new()
|
||||
|
||||
Reference in New Issue
Block a user