Initial layout of beacon block production
This commit is contained in:
parent
aa29a66fac
commit
f8201edddd
23
validator_client/src/beacon_block_node.rs
Normal file
23
validator_client/src/beacon_block_node.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#[Derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum BeaconNodeError {
|
||||||
|
RemoteFailure(String),
|
||||||
|
DecodeFailure,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines the methods required to produce and publish blocks on a Beacon Node. Abstracts the
|
||||||
|
/// actual beacon node.
|
||||||
|
pub trait BeaconNode: Send + Sync {
|
||||||
|
/// Request that the node produces a block.
|
||||||
|
///
|
||||||
|
/// Returns Ok(None) if the Beacon Node is unable to produce at the given slot.
|
||||||
|
fn produce_beacon_block(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
randao_reveal: &Signature,
|
||||||
|
) -> Result<Option<BeaconBlock>, BeaconNodeError>;
|
||||||
|
|
||||||
|
/// Request that the node publishes a block.
|
||||||
|
///
|
||||||
|
/// Returns `true` if the publish was sucessful.
|
||||||
|
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError>;
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
use block_proposer::{BeaconNode, BeaconNodeError, PublishOutcome};
|
|
||||||
use protos::services::{
|
|
||||||
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
|
|
||||||
};
|
|
||||||
use protos::services_grpc::BeaconBlockServiceClient;
|
|
||||||
use ssz::{ssz_encode, Decodable};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use types::{BeaconBlock, Signature, Slot};
|
|
||||||
|
|
||||||
/// A newtype designed to wrap the gRPC-generated service so the `BeaconNode` trait may be
|
|
||||||
/// implemented upon it.
|
|
||||||
pub struct BeaconBlockGrpcClient {
|
|
||||||
client: Arc<BeaconBlockServiceClient>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconBlockGrpcClient {
|
|
||||||
pub fn new(client: Arc<BeaconBlockServiceClient>) -> Self {
|
|
||||||
Self { client }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconNode for BeaconBlockGrpcClient {
|
|
||||||
/// Request a Beacon Node (BN) to produce a new block at the supplied slot.
|
|
||||||
///
|
|
||||||
/// Returns `None` if it is not possible to produce at the supplied slot. For example, if the
|
|
||||||
/// BN is unable to find a parent block.
|
|
||||||
fn produce_beacon_block(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
// TODO: use randao_reveal, when proto APIs have been updated.
|
|
||||||
_randao_reveal: &Signature,
|
|
||||||
) -> Result<Option<BeaconBlock>, BeaconNodeError> {
|
|
||||||
let mut req = ProduceBeaconBlockRequest::new();
|
|
||||||
req.set_slot(slot.as_u64());
|
|
||||||
|
|
||||||
let reply = self
|
|
||||||
.client
|
|
||||||
.produce_beacon_block(&req)
|
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
if reply.has_block() {
|
|
||||||
let block = reply.get_block();
|
|
||||||
let ssz = block.get_ssz();
|
|
||||||
|
|
||||||
let (block, _i) =
|
|
||||||
BeaconBlock::ssz_decode(&ssz, 0).map_err(|_| BeaconNodeError::DecodeFailure)?;
|
|
||||||
|
|
||||||
Ok(Some(block))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request a Beacon Node (BN) to publish a block.
|
|
||||||
///
|
|
||||||
/// Generally, this will be called after a `produce_beacon_block` call with a block that has
|
|
||||||
/// been completed (signed) by the validator client.
|
|
||||||
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError> {
|
|
||||||
let mut req = PublishBeaconBlockRequest::new();
|
|
||||||
|
|
||||||
let ssz = ssz_encode(&block);
|
|
||||||
|
|
||||||
// TODO: this conversion is incomplete; fix it.
|
|
||||||
let mut grpc_block = GrpcBeaconBlock::new();
|
|
||||||
grpc_block.set_ssz(ssz);
|
|
||||||
|
|
||||||
req.set_block(grpc_block);
|
|
||||||
|
|
||||||
let reply = self
|
|
||||||
.client
|
|
||||||
.publish_beacon_block(&req)
|
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
if reply.get_success() {
|
|
||||||
Ok(PublishOutcome::ValidBlock)
|
|
||||||
} else {
|
|
||||||
// TODO: distinguish between different errors
|
|
||||||
Ok(PublishOutcome::InvalidBlock("Publish failed".to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
227
validator_client/src/block_producer_service/block_producer.rs
Normal file
227
validator_client/src/block_producer_service/block_producer.rs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
pub mod test_utils;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
use slot_clock::SlotClock;
|
||||||
|
use ssz::{SignedRoot, TreeHash};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use types::{BeaconBlock, ChainSpec, Domain, Slot};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
SlotClockError,
|
||||||
|
SlotUnknowable,
|
||||||
|
EpochMapPoisoned,
|
||||||
|
SlotClockPoisoned,
|
||||||
|
EpochLengthIsZero,
|
||||||
|
BeaconNodeError(BeaconNodeError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum BlockProducerEvent {
|
||||||
|
/// A new block was produced.
|
||||||
|
BlockProduced(Slot),
|
||||||
|
/// A block was not produced as it would have been slashable.
|
||||||
|
SlashableBlockNotProduced(Slot),
|
||||||
|
/// The Beacon Node was unable to produce a block at that slot.
|
||||||
|
BeaconNodeUnableToProduceBlock(Slot),
|
||||||
|
/// The signer failed to sign the message.
|
||||||
|
SignerRejection(Slot),
|
||||||
|
/// The public key for this validator is not an active validator.
|
||||||
|
ValidatorIsUnknown(Slot),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This struct contains the logic for requesting and signing beacon blocks for a validator. The
|
||||||
|
/// validator can abstractly sign via the Signer trait object.
|
||||||
|
pub struct BlockProducer<B: BeaconNode, S: Signer> {
|
||||||
|
/// The current fork.
|
||||||
|
pub fork: Fork,
|
||||||
|
/// The current slot to produce a block for.
|
||||||
|
pub slot: Slot,
|
||||||
|
/// The current epoch.
|
||||||
|
pub epoch: Epoch,
|
||||||
|
/// The beacon node to connect to.
|
||||||
|
pub beacon_node: Arc<B>,
|
||||||
|
/// The signer to sign the block.
|
||||||
|
pub signer: Arc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BeaconNode, S: Signer> BlockProducer<B, S> {
|
||||||
|
|
||||||
|
/// Produce a block at some slot.
|
||||||
|
///
|
||||||
|
/// Assumes that a block is required at this slot (does not check the duties).
|
||||||
|
///
|
||||||
|
/// Ensures the message is not slashable.
|
||||||
|
///
|
||||||
|
/// !!! UNSAFE !!!
|
||||||
|
///
|
||||||
|
/// The slash-protection code is not yet implemented. There is zero protection against
|
||||||
|
/// slashing.
|
||||||
|
fn produce_block(&mut self) -> Result<BlockProducerEvent, Error> {
|
||||||
|
|
||||||
|
let randao_reveal = {
|
||||||
|
// TODO: add domain, etc to this message. Also ensure result matches `into_to_bytes32`.
|
||||||
|
let message = slot.epoch(self.spec.slots_per_epoch).hash_tree_root();
|
||||||
|
|
||||||
|
match self.signer.sign_randao_reveal(
|
||||||
|
&message,
|
||||||
|
self.spec
|
||||||
|
.get_domain(slot.epoch(self.spec.slots_per_epoch), Domain::Randao, &fork),
|
||||||
|
) {
|
||||||
|
None => return Ok(PollOutcome::SignerRejection(slot)),
|
||||||
|
Some(signature) => signature,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(block) = self
|
||||||
|
.beacon_node
|
||||||
|
.produce_beacon_block(slot, &randao_reveal)?
|
||||||
|
{
|
||||||
|
if self.safe_to_produce(&block) {
|
||||||
|
let domain = self.spec.get_domain(
|
||||||
|
slot.epoch(self.spec.slots_per_epoch),
|
||||||
|
Domain::BeaconBlock,
|
||||||
|
&fork,
|
||||||
|
);
|
||||||
|
if let Some(block) = self.sign_block(block, domain) {
|
||||||
|
self.beacon_node.publish_beacon_block(block)?;
|
||||||
|
Ok(PollOutcome::BlockProduced(slot))
|
||||||
|
} else {
|
||||||
|
Ok(PollOutcome::SignerRejection(slot))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(PollOutcome::SlashableBlockNotProduced(slot))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(PollOutcome::BeaconNodeUnableToProduceBlock(slot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes a block, returning that block signed by the validators private key.
|
||||||
|
///
|
||||||
|
/// Important: this function will not check to ensure the block is not slashable. This must be
|
||||||
|
/// done upstream.
|
||||||
|
fn sign_block(&mut self, mut block: BeaconBlock, domain: u64) -> Option<BeaconBlock> {
|
||||||
|
self.store_produce(&block);
|
||||||
|
|
||||||
|
match self
|
||||||
|
.signer
|
||||||
|
.sign_block_proposal(&block.signed_root()[..], domain)
|
||||||
|
{
|
||||||
|
None => None,
|
||||||
|
Some(signature) => {
|
||||||
|
block.signature = signature;
|
||||||
|
Some(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if signing a block is safe (non-slashable).
|
||||||
|
///
|
||||||
|
/// !!! UNSAFE !!!
|
||||||
|
///
|
||||||
|
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
|
||||||
|
fn safe_to_produce(&self, _block: &BeaconBlock) -> bool {
|
||||||
|
// TODO: ensure the producer doesn't produce slashable blocks.
|
||||||
|
// https://github.com/sigp/lighthouse/issues/160
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record that a block was produced so that slashable votes may not be made in the future.
|
||||||
|
///
|
||||||
|
/// !!! UNSAFE !!!
|
||||||
|
///
|
||||||
|
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
|
||||||
|
fn store_produce(&mut self, _block: &BeaconBlock) {
|
||||||
|
// TODO: record this block production to prevent future slashings.
|
||||||
|
// https://github.com/sigp/lighthouse/issues/160
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BeaconNodeError> for Error {
|
||||||
|
fn from(e: BeaconNodeError) -> Error {
|
||||||
|
Error::BeaconNodeError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Old tests - Re-work for new logic
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::test_utils::{EpochMap, LocalSigner, SimulatedBeaconNode};
|
||||||
|
use super::*;
|
||||||
|
use slot_clock::TestingSlotClock;
|
||||||
|
use types::{
|
||||||
|
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
||||||
|
Keypair,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: implement more thorough testing.
|
||||||
|
// https://github.com/sigp/lighthouse/issues/160
|
||||||
|
//
|
||||||
|
// These tests should serve as a good example for future tests.
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn polling() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
|
|
||||||
|
let spec = Arc::new(ChainSpec::foundation());
|
||||||
|
let slot_clock = Arc::new(TestingSlotClock::new(0));
|
||||||
|
let beacon_node = Arc::new(SimulatedBeaconNode::default());
|
||||||
|
let signer = Arc::new(LocalSigner::new(Keypair::random()));
|
||||||
|
|
||||||
|
let mut epoch_map = EpochMap::new(spec.slots_per_epoch);
|
||||||
|
let produce_slot = Slot::new(100);
|
||||||
|
let produce_epoch = produce_slot.epoch(spec.slots_per_epoch);
|
||||||
|
epoch_map.map.insert(produce_epoch, produce_slot);
|
||||||
|
let epoch_map = Arc::new(epoch_map);
|
||||||
|
|
||||||
|
let mut block_proposer = BlockProducer::new(
|
||||||
|
spec.clone(),
|
||||||
|
epoch_map.clone(),
|
||||||
|
slot_clock.clone(),
|
||||||
|
beacon_node.clone(),
|
||||||
|
signer.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Configure responses from the BeaconNode.
|
||||||
|
beacon_node.set_next_produce_result(Ok(Some(BeaconBlock::random_for_test(&mut rng))));
|
||||||
|
beacon_node.set_next_publish_result(Ok(PublishOutcome::ValidBlock));
|
||||||
|
|
||||||
|
// One slot before production slot...
|
||||||
|
slot_clock.set_slot(produce_slot.as_u64() - 1);
|
||||||
|
assert_eq!(
|
||||||
|
block_proposer.poll(),
|
||||||
|
Ok(PollOutcome::BlockProductionNotRequired(produce_slot - 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
// On the produce slot...
|
||||||
|
slot_clock.set_slot(produce_slot.as_u64());
|
||||||
|
assert_eq!(
|
||||||
|
block_proposer.poll(),
|
||||||
|
Ok(PollOutcome::BlockProduced(produce_slot.into()))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Trying the same produce slot again...
|
||||||
|
slot_clock.set_slot(produce_slot.as_u64());
|
||||||
|
assert_eq!(
|
||||||
|
block_proposer.poll(),
|
||||||
|
Ok(PollOutcome::SlotAlreadyProcessed(produce_slot))
|
||||||
|
);
|
||||||
|
|
||||||
|
// One slot after the produce slot...
|
||||||
|
slot_clock.set_slot(produce_slot.as_u64() + 1);
|
||||||
|
assert_eq!(
|
||||||
|
block_proposer.poll(),
|
||||||
|
Ok(PollOutcome::BlockProductionNotRequired(produce_slot + 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
// In an epoch without known duties...
|
||||||
|
let slot = (produce_epoch.as_u64() + 1) * spec.slots_per_epoch;
|
||||||
|
slot_clock.set_slot(slot);
|
||||||
|
assert_eq!(
|
||||||
|
block_proposer.poll(),
|
||||||
|
Ok(PollOutcome::ProducerDutiesUnknown(Slot::new(slot)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,61 +1,80 @@
|
|||||||
mod beacon_block_grpc_client;
|
use protos::services::{
|
||||||
// mod block_producer_service;
|
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
|
||||||
|
|
||||||
use block_proposer::{
|
|
||||||
BeaconNode, BlockProducer, DutiesReader, PollOutcome as BlockProducerPollOutcome, Signer,
|
|
||||||
};
|
};
|
||||||
use slog::{error, info, warn, Logger};
|
use protos::services_grpc::BeaconBlockServiceClient;
|
||||||
use slot_clock::SlotClock;
|
use ssz::{ssz_encode, Decodable};
|
||||||
use std::time::Duration;
|
use std::sync::Arc;
|
||||||
|
use types::{BeaconBlock, Signature, Slot};
|
||||||
|
|
||||||
pub use self::beacon_block_grpc_client::BeaconBlockGrpcClient;
|
/// A newtype designed to wrap the gRPC-generated service so the `BeaconNode` trait may be
|
||||||
|
/// implemented upon it.
|
||||||
pub struct BlockProducerService<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> {
|
pub struct BeaconBlockGrpcClient {
|
||||||
pub block_producer: BlockProducer<T, U, V, W>,
|
inner: Arc<BeaconBlockServiceClient>,
|
||||||
pub poll_interval_millis: u64,
|
|
||||||
pub log: Logger,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducerService<T, U, V, W> {
|
impl BeaconBlockGrpcClient {
|
||||||
/// Run a loop which polls the block producer each `poll_interval_millis` millseconds.
|
pub fn new(client: Arc<BeaconBlockServiceClient>) -> Self {
|
||||||
///
|
Self { inner: client }
|
||||||
/// Logs the results of the polls.
|
}
|
||||||
pub fn run(&mut self) {
|
}
|
||||||
loop {
|
|
||||||
match self.block_producer.poll() {
|
|
||||||
Err(error) => {
|
|
||||||
error!(self.log, "Block producer poll error"; "error" => format!("{:?}", error))
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::BlockProduced(slot)) => {
|
|
||||||
info!(self.log, "Produced block"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::SlashableBlockNotProduced(slot)) => {
|
|
||||||
warn!(self.log, "Slashable block was not signed"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::BlockProductionNotRequired(slot)) => {
|
|
||||||
info!(self.log, "Block production not required"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::ProducerDutiesUnknown(slot)) => {
|
|
||||||
error!(self.log, "Block production duties unknown"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::SlotAlreadyProcessed(slot)) => {
|
|
||||||
warn!(self.log, "Attempted to re-process slot"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::BeaconNodeUnableToProduceBlock(slot)) => {
|
|
||||||
error!(self.log, "Beacon node unable to produce block"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::SignerRejection(slot)) => {
|
|
||||||
error!(self.log, "The cryptographic signer refused to sign the block"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::ValidatorIsUnknown(slot)) => {
|
|
||||||
error!(self.log, "The Beacon Node does not recognise the validator"; "slot" => slot)
|
|
||||||
}
|
|
||||||
Ok(BlockProducerPollOutcome::UnableToGetFork(slot)) => {
|
|
||||||
error!(self.log, "Unable to get a `Fork` struct to generate signature domains"; "slot" => slot)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_millis(self.poll_interval_millis));
|
impl BeaconNode for BeaconBlockGrpcClient {
|
||||||
|
/// Request a Beacon Node (BN) to produce a new block at the supplied slot.
|
||||||
|
///
|
||||||
|
/// Returns `None` if it is not possible to produce at the supplied slot. For example, if the
|
||||||
|
/// BN is unable to find a parent block.
|
||||||
|
fn produce_beacon_block(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
// TODO: use randao_reveal, when proto APIs have been updated.
|
||||||
|
_randao_reveal: &Signature,
|
||||||
|
) -> Result<Option<BeaconBlock>, BeaconNodeError> {
|
||||||
|
let mut req = ProduceBeaconBlockRequest::new();
|
||||||
|
req.set_slot(slot.as_u64());
|
||||||
|
|
||||||
|
let reply = self
|
||||||
|
.client
|
||||||
|
.produce_beacon_block(&req)
|
||||||
|
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
||||||
|
|
||||||
|
if reply.has_block() {
|
||||||
|
let block = reply.get_block();
|
||||||
|
let ssz = block.get_ssz();
|
||||||
|
|
||||||
|
let (block, _i) =
|
||||||
|
BeaconBlock::ssz_decode(&ssz, 0).map_err(|_| BeaconNodeError::DecodeFailure)?;
|
||||||
|
|
||||||
|
Ok(Some(block))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request a Beacon Node (BN) to publish a block.
|
||||||
|
///
|
||||||
|
/// Generally, this will be called after a `produce_beacon_block` call with a block that has
|
||||||
|
/// been completed (signed) by the validator client.
|
||||||
|
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError> {
|
||||||
|
let mut req = PublishBeaconBlockRequest::new();
|
||||||
|
|
||||||
|
let ssz = ssz_encode(&block);
|
||||||
|
|
||||||
|
// TODO: this conversion is incomplete; fix it.
|
||||||
|
let mut grpc_block = GrpcBeaconBlock::new();
|
||||||
|
grpc_block.set_ssz(ssz);
|
||||||
|
|
||||||
|
req.set_block(grpc_block);
|
||||||
|
|
||||||
|
let reply = self
|
||||||
|
.client
|
||||||
|
.publish_beacon_block(&req)
|
||||||
|
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
||||||
|
|
||||||
|
if reply.get_success() {
|
||||||
|
Ok(PublishOutcome::ValidBlock)
|
||||||
|
} else {
|
||||||
|
// TODO: distinguish between different errors
|
||||||
|
Ok(PublishOutcome::InvalidBlock("Publish failed".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ impl Service {
|
|||||||
// Beacon node gRPC beacon node endpoints.
|
// Beacon node gRPC beacon node endpoints.
|
||||||
let beacon_node_client = {
|
let beacon_node_client = {
|
||||||
let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
|
let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
|
||||||
Arc::new(BeaconNodeServiceClient::new(ch))
|
BeaconNodeServiceClient::new(ch)
|
||||||
};
|
};
|
||||||
|
|
||||||
// retrieve node information and validate the beacon node
|
// retrieve node information and validate the beacon node
|
||||||
@ -287,7 +287,6 @@ impl Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
// Spawn a new thread to perform block production for the validator.
|
// Spawn a new thread to perform block production for the validator.
|
||||||
let producer_thread = {
|
let producer_thread = {
|
||||||
let spec = spec.clone();
|
let spec = spec.clone();
|
||||||
|
Loading…
Reference in New Issue
Block a user