Merge latest interop
This commit is contained in:
commit
2a1d6587a7
@ -18,6 +18,7 @@ rayon = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_yaml = "0.8"
|
||||
serde_json = "^1.0"
|
||||
slog = { version = "^2.2.3" , features = ["max_level_trace"] }
|
||||
sloggers = { version = "^0.3" }
|
||||
slot_clock = { path = "../../eth2/utils/slot_clock" }
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::checkpoint::CheckPoint;
|
||||
use crate::errors::{BeaconChainError as Error, BlockProductionError};
|
||||
use crate::eth1_chain::{Eth1Chain, Eth1ChainBackend};
|
||||
use crate::fork_choice::{Error as ForkChoiceError, ForkChoice};
|
||||
use crate::iter::{ReverseBlockRootIterator, ReverseStateRootIterator};
|
||||
use crate::metrics;
|
||||
@ -45,11 +46,14 @@ pub enum BlockProcessingOutcome {
|
||||
block_slot: Slot,
|
||||
},
|
||||
/// The block state_root does not match the generated state.
|
||||
StateRootMismatch,
|
||||
StateRootMismatch { block: Hash256, local: Hash256 },
|
||||
/// The block was a genesis block, these blocks cannot be re-imported.
|
||||
GenesisBlock,
|
||||
/// The slot is finalized, no need to import.
|
||||
FinalizedSlot,
|
||||
WouldRevertFinalizedSlot {
|
||||
block_slot: Slot,
|
||||
finalized_slot: Slot,
|
||||
},
|
||||
/// Block is already known, no need to re-import.
|
||||
BlockIsAlreadyKnown,
|
||||
/// The block could not be applied to the state, it is invalid.
|
||||
@ -76,25 +80,31 @@ pub enum AttestationProcessingOutcome {
|
||||
Invalid(AttestationValidationError),
|
||||
}
|
||||
|
||||
pub enum StateCow<'a, T: EthSpec> {
|
||||
/// Effectively a `Cow<BeaconState>`, however when it is `Borrowed` it holds a `RwLockReadGuard` (a
|
||||
/// read-lock on some read/write-locked state).
|
||||
///
|
||||
/// Only has a small subset of the functionality of a `std::borrow::Cow`.
|
||||
pub enum BeaconStateCow<'a, T: EthSpec> {
|
||||
Borrowed(RwLockReadGuard<'a, CheckPoint<T>>),
|
||||
Owned(BeaconState<T>),
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> AsRef<BeaconState<T>> for StateCow<'a, T> {
|
||||
fn as_ref(&self) -> &BeaconState<T> {
|
||||
impl<'a, T: EthSpec> BeaconStateCow<'a, T> {
|
||||
pub fn maybe_as_mut_ref(&mut self) -> Option<&mut BeaconState<T>> {
|
||||
match self {
|
||||
StateCow::Borrowed(checkpoint) => &checkpoint.beacon_state,
|
||||
StateCow::Owned(state) => &state,
|
||||
BeaconStateCow::Borrowed(_) => None,
|
||||
BeaconStateCow::Owned(ref mut state) => Some(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> StateCow<'a, T> {
|
||||
pub fn as_mut_ref(&mut self) -> Option<&mut BeaconState<T>> {
|
||||
impl<'a, T: EthSpec> std::ops::Deref for BeaconStateCow<'a, T> {
|
||||
type Target = BeaconState<T>;
|
||||
|
||||
fn deref(&self) -> &BeaconState<T> {
|
||||
match self {
|
||||
StateCow::Borrowed(_) => None,
|
||||
StateCow::Owned(ref mut state) => Some(state),
|
||||
BeaconStateCow::Borrowed(checkpoint) => &checkpoint.beacon_state,
|
||||
BeaconStateCow::Owned(state) => &state,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,6 +113,7 @@ pub trait BeaconChainTypes: Send + Sync + 'static {
|
||||
type Store: store::Store;
|
||||
type SlotClock: slot_clock::SlotClock;
|
||||
type LmdGhost: LmdGhost<Self::Store, Self::EthSpec>;
|
||||
type Eth1Chain: Eth1ChainBackend<Self::EthSpec>;
|
||||
type EthSpec: types::EthSpec;
|
||||
}
|
||||
|
||||
@ -117,6 +128,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
/// Stores all operations (e.g., `Attestation`, `Deposit`, etc) that are candidates for
|
||||
/// inclusion in a block.
|
||||
pub op_pool: OperationPool<T::EthSpec>,
|
||||
/// Provides information from the Ethereum 1 (PoW) chain.
|
||||
pub eth1_chain: Eth1Chain<T>,
|
||||
/// Stores a "snapshot" of the chain at the time the head-of-the-chain block was received.
|
||||
canonical_head: RwLock<CheckPoint<T::EthSpec>>,
|
||||
/// The root of the genesis block.
|
||||
@ -132,6 +145,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Instantiate a new Beacon Chain, from genesis.
|
||||
pub fn from_genesis(
|
||||
store: Arc<T::Store>,
|
||||
eth1_backend: T::Eth1Chain,
|
||||
mut genesis_state: BeaconState<T::EthSpec>,
|
||||
mut genesis_block: BeaconBlock<T::EthSpec>,
|
||||
spec: ChainSpec,
|
||||
@ -176,6 +190,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
spec,
|
||||
slot_clock,
|
||||
op_pool: OperationPool::new(),
|
||||
eth1_chain: Eth1Chain::new(eth1_backend),
|
||||
canonical_head,
|
||||
genesis_block_root,
|
||||
fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root),
|
||||
@ -187,6 +202,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Attempt to load an existing instance from the given `store`.
|
||||
pub fn from_store(
|
||||
store: Arc<T::Store>,
|
||||
eth1_backend: T::Eth1Chain,
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Option<BeaconChain<T>>, Error> {
|
||||
@ -223,6 +239,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
slot_clock,
|
||||
fork_choice: ForkChoice::new(store.clone(), last_finalized_block, last_finalized_root),
|
||||
op_pool,
|
||||
eth1_chain: Eth1Chain::new(eth1_backend),
|
||||
canonical_head: RwLock::new(p.canonical_head),
|
||||
genesis_block_root: p.genesis_block_root,
|
||||
store,
|
||||
@ -373,11 +390,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
///
|
||||
/// Returns `None` when the state is not found in the database or there is an error skipping
|
||||
/// to a future state.
|
||||
pub fn state_at_slot(&self, slot: Slot) -> Result<StateCow<T::EthSpec>, Error> {
|
||||
pub fn state_at_slot(&self, slot: Slot) -> Result<BeaconStateCow<T::EthSpec>, Error> {
|
||||
let head_state = &self.head().beacon_state;
|
||||
|
||||
if slot == head_state.slot {
|
||||
Ok(StateCow::Borrowed(self.head()))
|
||||
Ok(BeaconStateCow::Borrowed(self.head()))
|
||||
} else if slot > head_state.slot {
|
||||
let head_state_slot = head_state.slot;
|
||||
let mut state = head_state.clone();
|
||||
@ -397,7 +414,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(StateCow::Owned(state))
|
||||
Ok(BeaconStateCow::Owned(state))
|
||||
} else {
|
||||
let state_root = self
|
||||
.rev_iter_state_roots()
|
||||
@ -405,7 +422,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.map(|(root, _slot)| root)
|
||||
.ok_or_else(|| Error::NoStateForSlot(slot))?;
|
||||
|
||||
Ok(StateCow::Owned(
|
||||
Ok(BeaconStateCow::Owned(
|
||||
self.store
|
||||
.get(&state_root)?
|
||||
.ok_or_else(|| Error::NoStateForSlot(slot))?,
|
||||
@ -421,7 +438,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
///
|
||||
/// Returns `None` when there is an error skipping to a future state or the slot clock cannot
|
||||
/// be read.
|
||||
pub fn state_now(&self) -> Result<StateCow<T::EthSpec>, Error> {
|
||||
pub fn state_now(&self) -> Result<BeaconStateCow<T::EthSpec>, Error> {
|
||||
self.state_at_slot(self.slot()?)
|
||||
}
|
||||
|
||||
@ -473,25 +490,24 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let head_state = &self.head().beacon_state;
|
||||
|
||||
let mut state = if epoch(slot) == epoch(head_state.slot) {
|
||||
StateCow::Borrowed(self.head())
|
||||
BeaconStateCow::Borrowed(self.head())
|
||||
} else {
|
||||
self.state_at_slot(slot)?
|
||||
};
|
||||
|
||||
if let Some(state) = state.as_mut_ref() {
|
||||
if let Some(state) = state.maybe_as_mut_ref() {
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
}
|
||||
|
||||
if epoch(state.as_ref().slot) != epoch(slot) {
|
||||
if epoch(state.slot) != epoch(slot) {
|
||||
return Err(Error::InvariantViolated(format!(
|
||||
"Epochs in consistent in proposer lookup: state: {}, requested: {}",
|
||||
epoch(state.as_ref().slot),
|
||||
epoch(state.slot),
|
||||
epoch(slot)
|
||||
)));
|
||||
}
|
||||
|
||||
state
|
||||
.as_ref()
|
||||
.get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -509,26 +525,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let head_state = &self.head().beacon_state;
|
||||
|
||||
let mut state = if epoch == as_epoch(head_state.slot) {
|
||||
StateCow::Borrowed(self.head())
|
||||
BeaconStateCow::Borrowed(self.head())
|
||||
} else {
|
||||
self.state_at_slot(epoch.start_slot(T::EthSpec::slots_per_epoch()))?
|
||||
};
|
||||
|
||||
if let Some(state) = state.as_mut_ref() {
|
||||
if let Some(state) = state.maybe_as_mut_ref() {
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
}
|
||||
|
||||
if as_epoch(state.as_ref().slot) != epoch {
|
||||
if as_epoch(state.slot) != epoch {
|
||||
return Err(Error::InvariantViolated(format!(
|
||||
"Epochs in consistent in attestation duties lookup: state: {}, requested: {}",
|
||||
as_epoch(state.as_ref().slot),
|
||||
as_epoch(state.slot),
|
||||
epoch
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(attestation_duty) = state
|
||||
.as_ref()
|
||||
.get_attestation_duties(validator_index, RelativeEpoch::Current)?
|
||||
if let Some(attestation_duty) =
|
||||
state.get_attestation_duties(validator_index, RelativeEpoch::Current)?
|
||||
{
|
||||
Ok(Some((attestation_duty.slot, attestation_duty.shard)))
|
||||
} else {
|
||||
@ -549,12 +564,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let head_block_root = self.head().beacon_block_root;
|
||||
let head_block_slot = self.head().beacon_block.slot;
|
||||
|
||||
self.produce_attestation_data_for_block(
|
||||
shard,
|
||||
head_block_root,
|
||||
head_block_slot,
|
||||
state.as_ref(),
|
||||
)
|
||||
self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, &*state)
|
||||
}
|
||||
|
||||
/// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`.
|
||||
@ -876,7 +886,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
match self.state_now() {
|
||||
Ok(state) => self
|
||||
.op_pool
|
||||
.insert_voluntary_exit(exit, state.as_ref(), &self.spec),
|
||||
.insert_voluntary_exit(exit, &*state, &self.spec),
|
||||
Err(e) => {
|
||||
error!(
|
||||
&self.log,
|
||||
@ -892,9 +902,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Accept some transfer and queue it for inclusion in an appropriate block.
|
||||
pub fn process_transfer(&self, transfer: Transfer) -> Result<(), TransferValidationError> {
|
||||
match self.state_now() {
|
||||
Ok(state) => self
|
||||
.op_pool
|
||||
.insert_transfer(transfer, state.as_ref(), &self.spec),
|
||||
Ok(state) => self.op_pool.insert_transfer(transfer, &*state, &self.spec),
|
||||
Err(e) => {
|
||||
error!(
|
||||
&self.log,
|
||||
@ -915,7 +923,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
match self.state_now() {
|
||||
Ok(state) => {
|
||||
self.op_pool
|
||||
.insert_proposer_slashing(proposer_slashing, state.as_ref(), &self.spec)
|
||||
.insert_proposer_slashing(proposer_slashing, &*state, &self.spec)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@ -937,7 +945,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
match self.state_now() {
|
||||
Ok(state) => {
|
||||
self.op_pool
|
||||
.insert_attester_slashing(attester_slashing, state.as_ref(), &self.spec)
|
||||
.insert_attester_slashing(attester_slashing, &*state, &self.spec)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@ -968,14 +976,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
|
||||
if block.slot <= finalized_slot {
|
||||
return Ok(BlockProcessingOutcome::FinalizedSlot);
|
||||
}
|
||||
|
||||
if block.slot == 0 {
|
||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||
}
|
||||
|
||||
if block.slot <= finalized_slot {
|
||||
return Ok(BlockProcessingOutcome::WouldRevertFinalizedSlot {
|
||||
block_slot: block.slot,
|
||||
finalized_slot: finalized_slot,
|
||||
});
|
||||
}
|
||||
|
||||
let block_root_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_BLOCK_ROOT);
|
||||
|
||||
let block_root = block.canonical_root();
|
||||
@ -1073,7 +1084,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
if block.state_root != state_root {
|
||||
return Ok(BlockProcessingOutcome::StateRootMismatch);
|
||||
return Ok(BlockProcessingOutcome::StateRootMismatch {
|
||||
block: block.state_root,
|
||||
local: state_root,
|
||||
});
|
||||
}
|
||||
|
||||
metrics::stop_timer(state_root_timer);
|
||||
@ -1158,7 +1172,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.state_at_slot(slot - 1)
|
||||
.map_err(|_| BlockProductionError::UnableToProduceAtSlot(slot))?;
|
||||
|
||||
self.produce_block_on_state(state.as_ref().clone(), slot, randao_reveal)
|
||||
self.produce_block_on_state(state.clone(), slot, randao_reveal)
|
||||
}
|
||||
|
||||
/// Produce a block for some `slot` upon the given `state`.
|
||||
@ -1207,16 +1221,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
body: BeaconBlockBody {
|
||||
randao_reveal,
|
||||
// TODO: replace with real data.
|
||||
eth1_data: Eth1Data {
|
||||
deposit_count: state.eth1_data.deposit_count,
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
},
|
||||
eth1_data: self.eth1_chain.eth1_data_for_block_production(&state)?,
|
||||
graffiti,
|
||||
proposer_slashings: proposer_slashings.into(),
|
||||
attester_slashings: attester_slashings.into(),
|
||||
attestations: self.op_pool.get_attestations(&state, &self.spec).into(),
|
||||
deposits: self.op_pool.get_deposits(&state).into(),
|
||||
deposits: self.eth1_chain.deposits_for_block_inclusion(&state)?.into(),
|
||||
voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(),
|
||||
transfers: self.op_pool.get_transfers(&state, &self.spec).into(),
|
||||
},
|
||||
|
@ -4,16 +4,17 @@ use lighthouse_bootstrap::Bootstrapper;
|
||||
use merkle_proof::MerkleTree;
|
||||
use rayon::prelude::*;
|
||||
use slog::Logger;
|
||||
use ssz::Encode;
|
||||
use ssz::{Decode, Encode};
|
||||
use state_processing::initialize_beacon_state_from_eth1;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypairs, BeaconBlock, BeaconState, ChainSpec, Deposit,
|
||||
DepositData, Domain, EthSpec, Fork, Hash256, PublicKey, Signature,
|
||||
BeaconBlock, BeaconState, ChainSpec, Deposit, DepositData, Domain, EthSpec, Fork, Hash256,
|
||||
Keypair, PublicKey, Signature,
|
||||
};
|
||||
|
||||
enum BuildStrategy<T: BeaconChainTypes> {
|
||||
@ -32,21 +33,21 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
|
||||
|
||||
impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
|
||||
pub fn recent_genesis(
|
||||
validator_count: usize,
|
||||
keypairs: &[Keypair],
|
||||
minutes: u64,
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Self, String> {
|
||||
Self::quick_start(recent_genesis_time(minutes), validator_count, spec, log)
|
||||
Self::quick_start(recent_genesis_time(minutes), keypairs, spec, log)
|
||||
}
|
||||
|
||||
pub fn quick_start(
|
||||
genesis_time: u64,
|
||||
validator_count: usize,
|
||||
keypairs: &[Keypair],
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Self, String> {
|
||||
let genesis_state = interop_genesis_state(validator_count, genesis_time, &spec)?;
|
||||
let genesis_state = interop_genesis_state(keypairs, genesis_time, &spec)?;
|
||||
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
@ -61,8 +62,32 @@ impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
|
||||
pub fn ssz_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
|
||||
let mut file = File::open(file.clone())
|
||||
.map_err(|e| format!("Unable to open SSZ genesis state file {:?}: {:?}", file, e))?;
|
||||
|
||||
let mut bytes = vec![];
|
||||
file.read_to_end(&mut bytes)
|
||||
.map_err(|e| format!("Failed to read SSZ file: {:?}", e))?;
|
||||
|
||||
let genesis_state = BeaconState::from_ssz_bytes(&bytes)
|
||||
.map_err(|e| format!("Unable to parse SSZ genesis state file: {:?}", e))?;
|
||||
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
|
||||
pub fn json_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
|
||||
let file = File::open(file.clone())
|
||||
.map_err(|e| format!("Unable to open JSON genesis state file {:?}: {:?}", file, e))?;
|
||||
|
||||
let genesis_state = serde_json::from_reader(file)
|
||||
.map_err(|e| format!("Unable to parse JSON genesis state file: {:?}", e))?;
|
||||
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
|
||||
pub fn http_bootstrap(server: &str, spec: ChainSpec, log: Logger) -> Result<Self, String> {
|
||||
let bootstrapper = Bootstrapper::from_server_string(server.to_string())
|
||||
let bootstrapper = Bootstrapper::connect(server.to_string(), &log)
|
||||
.map_err(|e| format!("Failed to initialize bootstrap client: {}", e))?;
|
||||
|
||||
let (genesis_state, genesis_block) = bootstrapper
|
||||
@ -102,16 +127,23 @@ impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self, store: Arc<T::Store>) -> Result<BeaconChain<T>, String> {
|
||||
pub fn build(
|
||||
self,
|
||||
store: Arc<T::Store>,
|
||||
eth1_backend: T::Eth1Chain,
|
||||
) -> Result<BeaconChain<T>, String> {
|
||||
Ok(match self.build_strategy {
|
||||
BuildStrategy::LoadFromStore => BeaconChain::from_store(store, self.spec, self.log)
|
||||
.map_err(|e| format!("Error loading BeaconChain from database: {:?}", e))?
|
||||
.ok_or_else(|| format!("Unable to find exising BeaconChain in database."))?,
|
||||
BuildStrategy::LoadFromStore => {
|
||||
BeaconChain::from_store(store, eth1_backend, self.spec, self.log)
|
||||
.map_err(|e| format!("Error loading BeaconChain from database: {:?}", e))?
|
||||
.ok_or_else(|| format!("Unable to find exising BeaconChain in database."))?
|
||||
}
|
||||
BuildStrategy::FromGenesis {
|
||||
genesis_block,
|
||||
genesis_state,
|
||||
} => BeaconChain::from_genesis(
|
||||
store,
|
||||
eth1_backend,
|
||||
genesis_state.as_ref().clone(),
|
||||
genesis_block.as_ref().clone(),
|
||||
self.spec,
|
||||
@ -135,12 +167,11 @@ fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -
|
||||
/// Reference:
|
||||
/// https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start
|
||||
fn interop_genesis_state<T: EthSpec>(
|
||||
validator_count: usize,
|
||||
keypairs: &[Keypair],
|
||||
genesis_time: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<T>, String> {
|
||||
let keypairs = generate_deterministic_keypairs(validator_count);
|
||||
let eth1_block_hash = Hash256::from_slice(&[42; 32]);
|
||||
let eth1_block_hash = Hash256::from_slice(&[0x42; 32]);
|
||||
let eth1_timestamp = 2_u64.pow(40);
|
||||
let amount = spec.max_effective_balance;
|
||||
|
||||
@ -155,7 +186,7 @@ fn interop_genesis_state<T: EthSpec>(
|
||||
.map(|keypair| {
|
||||
let mut data = DepositData {
|
||||
withdrawal_credentials: withdrawal_credentials(&keypair.pk),
|
||||
pubkey: keypair.pk.into(),
|
||||
pubkey: keypair.pk.clone().into(),
|
||||
amount,
|
||||
signature: Signature::empty_signature().into(),
|
||||
};
|
||||
@ -212,6 +243,9 @@ fn interop_genesis_state<T: EthSpec>(
|
||||
|
||||
state.genesis_time = genesis_time;
|
||||
|
||||
// Invalid all the caches after all the manual state surgery.
|
||||
state.drop_all_caches();
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
@ -237,7 +271,7 @@ fn recent_genesis_time(minutes: u64) -> u64 {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use types::{EthSpec, MinimalEthSpec};
|
||||
use types::{test_utils::generate_deterministic_keypairs, EthSpec, MinimalEthSpec};
|
||||
|
||||
type TestEthSpec = MinimalEthSpec;
|
||||
|
||||
@ -247,12 +281,14 @@ mod test {
|
||||
let genesis_time = 42;
|
||||
let spec = &TestEthSpec::default_spec();
|
||||
|
||||
let state = interop_genesis_state::<TestEthSpec>(validator_count, genesis_time, spec)
|
||||
let keypairs = generate_deterministic_keypairs(validator_count);
|
||||
|
||||
let state = interop_genesis_state::<TestEthSpec>(&keypairs, genesis_time, spec)
|
||||
.expect("should build state");
|
||||
|
||||
assert_eq!(
|
||||
state.eth1_data.block_hash,
|
||||
Hash256::from_slice(&[42; 32]),
|
||||
Hash256::from_slice(&[0x42; 32]),
|
||||
"eth1 block hash should be co-ordinated junk"
|
||||
);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::eth1_chain::Error as Eth1ChainError;
|
||||
use crate::fork_choice::Error as ForkChoiceError;
|
||||
use state_processing::per_block_processing::errors::AttestationValidationError;
|
||||
use state_processing::BlockProcessingError;
|
||||
@ -33,6 +34,7 @@ pub enum BeaconChainError {
|
||||
MissingBeaconBlock(Hash256),
|
||||
MissingBeaconState(Hash256),
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
UnableToAdvanceState(String),
|
||||
NoStateForAttestation {
|
||||
beacon_block_root: Hash256,
|
||||
},
|
||||
@ -42,6 +44,7 @@ pub enum BeaconChainError {
|
||||
}
|
||||
|
||||
easy_from_to!(SlotProcessingError, BeaconChainError);
|
||||
easy_from_to!(AttestationValidationError, BeaconChainError);
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockProductionError {
|
||||
@ -50,10 +53,11 @@ pub enum BlockProductionError {
|
||||
UnableToProduceAtSlot(Slot),
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
BlockProcessingError(BlockProcessingError),
|
||||
Eth1ChainError(Eth1ChainError),
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
easy_from_to!(BlockProcessingError, BlockProductionError);
|
||||
easy_from_to!(BeaconStateError, BlockProductionError);
|
||||
easy_from_to!(SlotProcessingError, BlockProductionError);
|
||||
easy_from_to!(AttestationValidationError, BeaconChainError);
|
||||
easy_from_to!(Eth1ChainError, BlockProductionError);
|
||||
|
110
beacon_node/beacon_chain/src/eth1_chain.rs
Normal file
110
beacon_node/beacon_chain/src/eth1_chain.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::BeaconChainTypes;
|
||||
use eth2_hashing::hash;
|
||||
use std::marker::PhantomData;
|
||||
use types::{BeaconState, Deposit, Eth1Data, EthSpec, Hash256};
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Holds an `Eth1ChainBackend` and serves requests from the `BeaconChain`.
|
||||
pub struct Eth1Chain<T: BeaconChainTypes> {
|
||||
backend: T::Eth1Chain,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> Eth1Chain<T> {
|
||||
pub fn new(backend: T::Eth1Chain) -> Self {
|
||||
Self { backend }
|
||||
}
|
||||
|
||||
/// Returns the `Eth1Data` that should be included in a block being produced for the given
|
||||
/// `state`.
|
||||
pub fn eth1_data_for_block_production(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
) -> Result<Eth1Data> {
|
||||
self.backend.eth1_data(state)
|
||||
}
|
||||
|
||||
/// Returns a list of `Deposits` that may be included in a block.
|
||||
///
|
||||
/// Including all of the returned `Deposits` in a block should _not_ cause it to become
|
||||
/// invalid.
|
||||
pub fn deposits_for_block_inclusion(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
) -> Result<Vec<Deposit>> {
|
||||
let deposits = self.backend.queued_deposits(state)?;
|
||||
|
||||
// TODO: truncate deposits if required.
|
||||
|
||||
Ok(deposits)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Unable to return an Eth1Data for the given epoch.
|
||||
EpochUnavailable,
|
||||
/// An error from the backend service (e.g., the web3 data fetcher).
|
||||
BackendError(String),
|
||||
}
|
||||
|
||||
pub trait Eth1ChainBackend<T: EthSpec>: Sized + Send + Sync {
|
||||
fn new(server: String) -> Result<Self>;
|
||||
|
||||
/// Returns the `Eth1Data` that should be included in a block being produced for the given
|
||||
/// `state`.
|
||||
fn eth1_data(&self, beacon_state: &BeaconState<T>) -> Result<Eth1Data>;
|
||||
|
||||
/// Returns all `Deposits` between `state.eth1_deposit_index` and
|
||||
/// `state.eth1_data.deposit_count`.
|
||||
///
|
||||
/// # Note:
|
||||
///
|
||||
/// It is possible that not all returned `Deposits` can be included in a block. E.g., there may
|
||||
/// be more than `MAX_DEPOSIT_COUNT` or the churn may be too high.
|
||||
fn queued_deposits(&self, beacon_state: &BeaconState<T>) -> Result<Vec<Deposit>>;
|
||||
}
|
||||
|
||||
pub struct InteropEth1ChainBackend<T: EthSpec> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Eth1ChainBackend<T> for InteropEth1ChainBackend<T> {
|
||||
fn new(_server: String) -> Result<Self> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
fn eth1_data(&self, state: &BeaconState<T>) -> Result<Eth1Data> {
|
||||
let current_epoch = state.current_epoch();
|
||||
let slots_per_voting_period = T::slots_per_eth1_voting_period() as u64;
|
||||
let current_voting_period: u64 = current_epoch.as_u64() / slots_per_voting_period;
|
||||
|
||||
let deposit_root = hash(&int_to_bytes32(current_voting_period));
|
||||
let block_hash = hash(&deposit_root);
|
||||
|
||||
Ok(Eth1Data {
|
||||
deposit_root: Hash256::from_slice(&deposit_root),
|
||||
deposit_count: state.eth1_deposit_index,
|
||||
block_hash: Hash256::from_slice(&block_hash),
|
||||
})
|
||||
}
|
||||
|
||||
fn queued_deposits(&self, _: &BeaconState<T>) -> Result<Vec<Deposit>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Default for InteropEth1ChainBackend<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `int` as little-endian bytes with a length of 32.
|
||||
fn int_to_bytes32(int: u64) -> Vec<u8> {
|
||||
let mut vec = int.to_le_bytes().to_vec();
|
||||
vec.resize(32, 0);
|
||||
vec
|
||||
}
|
@ -6,6 +6,7 @@ mod beacon_chain;
|
||||
mod beacon_chain_builder;
|
||||
mod checkpoint;
|
||||
mod errors;
|
||||
mod eth1_chain;
|
||||
mod fork_choice;
|
||||
mod iter;
|
||||
mod metrics;
|
||||
@ -18,6 +19,7 @@ pub use self::beacon_chain::{
|
||||
pub use self::checkpoint::CheckPoint;
|
||||
pub use self::errors::{BeaconChainError, BlockProductionError};
|
||||
pub use beacon_chain_builder::BeaconChainBuilder;
|
||||
pub use eth1_chain::{Eth1ChainBackend, InteropEth1ChainBackend};
|
||||
pub use lmd_ghost;
|
||||
pub use metrics::scrape_for_metrics;
|
||||
pub use parking_lot;
|
||||
|
@ -1,22 +1,28 @@
|
||||
use crate::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
use crate::{
|
||||
AttestationProcessingOutcome, BeaconChain, BeaconChainBuilder, BeaconChainTypes,
|
||||
BlockProcessingOutcome, InteropEth1ChainBackend,
|
||||
};
|
||||
use lmd_ghost::LmdGhost;
|
||||
use rayon::prelude::*;
|
||||
use sloggers::{null::NullLoggerBuilder, Build};
|
||||
use sloggers::{terminal::TerminalLoggerBuilder, types::Severity, Build};
|
||||
use slot_clock::TestingSlotClock;
|
||||
use state_processing::per_slot_processing;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use store::MemoryStore;
|
||||
use store::Store;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
test_utils::TestingBeaconStateBuilder, AggregateSignature, Attestation,
|
||||
AttestationDataAndCustodyBit, BeaconBlock, BeaconState, BitList, ChainSpec, Domain, EthSpec,
|
||||
Hash256, Keypair, RelativeEpoch, SecretKey, Signature, Slot,
|
||||
AggregateSignature, Attestation, AttestationDataAndCustodyBit, BeaconBlock, BeaconState,
|
||||
BitList, ChainSpec, Domain, EthSpec, Hash256, Keypair, RelativeEpoch, SecretKey, Signature,
|
||||
Slot,
|
||||
};
|
||||
|
||||
pub use types::test_utils::generate_deterministic_keypairs;
|
||||
|
||||
pub use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
|
||||
|
||||
pub const HARNESS_GENESIS_TIME: u64 = 1567552690; // 4th September 2019
|
||||
|
||||
/// Indicates how the `BeaconChainHarness` should produce blocks.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BlockStrategy {
|
||||
@ -60,6 +66,7 @@ where
|
||||
type Store = MemoryStore;
|
||||
type SlotClock = TestingSlotClock;
|
||||
type LmdGhost = L;
|
||||
type Eth1Chain = InteropEth1ChainBackend<E>;
|
||||
type EthSpec = E;
|
||||
}
|
||||
|
||||
@ -83,40 +90,21 @@ where
|
||||
E: EthSpec,
|
||||
{
|
||||
/// Instantiate a new harness with `validator_count` initial validators.
|
||||
pub fn new(validator_count: usize) -> Self {
|
||||
let state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
|
||||
validator_count,
|
||||
&E::default_spec(),
|
||||
);
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
Self::from_state_and_keypairs(genesis_state, keypairs)
|
||||
}
|
||||
|
||||
/// Instantiate a new harness with an initial validator for each key supplied.
|
||||
pub fn from_keypairs(keypairs: Vec<Keypair>) -> Self {
|
||||
let state_builder = TestingBeaconStateBuilder::from_keypairs(keypairs, &E::default_spec());
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
Self::from_state_and_keypairs(genesis_state, keypairs)
|
||||
}
|
||||
|
||||
/// Instantiate a new harness with the given genesis state and a keypair for each of the
|
||||
/// initial validators in the given state.
|
||||
pub fn from_state_and_keypairs(genesis_state: BeaconState<E>, keypairs: Vec<Keypair>) -> Self {
|
||||
pub fn new(keypairs: Vec<Keypair>) -> Self {
|
||||
let spec = E::default_spec();
|
||||
|
||||
let log = TerminalLoggerBuilder::new()
|
||||
.level(Severity::Warning)
|
||||
.build()
|
||||
.expect("logger should build");
|
||||
|
||||
let store = Arc::new(MemoryStore::open());
|
||||
|
||||
let mut genesis_block = BeaconBlock::empty(&spec);
|
||||
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
|
||||
|
||||
let builder = NullLoggerBuilder;
|
||||
let log = builder.build().expect("logger should build");
|
||||
|
||||
let chain =
|
||||
BeaconChain::from_genesis(store, genesis_state, genesis_block, spec.clone(), log)
|
||||
.expect("Terminate if beacon chain generation fails");
|
||||
BeaconChainBuilder::quick_start(HARNESS_GENESIS_TIME, &keypairs, spec.clone(), log)
|
||||
.unwrap_or_else(|e| panic!("Failed to create beacon chain builder: {}", e))
|
||||
.build(store.clone(), InteropEth1ChainBackend::default())
|
||||
.unwrap_or_else(|e| panic!("Failed to build beacon chain: {}", e));
|
||||
|
||||
Self {
|
||||
chain,
|
||||
@ -156,7 +144,10 @@ where
|
||||
BlockStrategy::ForkCanonicalChainAt { previous_slot, .. } => previous_slot,
|
||||
};
|
||||
|
||||
self.get_state_at_slot(state_slot)
|
||||
self.chain
|
||||
.state_at_slot(state_slot)
|
||||
.expect("should find state for slot")
|
||||
.clone()
|
||||
};
|
||||
|
||||
// Determine the first slot where a block should be built.
|
||||
@ -194,21 +185,6 @@ where
|
||||
head_block_root.expect("did not produce any blocks")
|
||||
}
|
||||
|
||||
fn get_state_at_slot(&self, state_slot: Slot) -> BeaconState<E> {
|
||||
let state_root = self
|
||||
.chain
|
||||
.rev_iter_state_roots()
|
||||
.find(|(_hash, slot)| *slot == state_slot)
|
||||
.map(|(hash, _slot)| hash)
|
||||
.expect("could not find state root");
|
||||
|
||||
self.chain
|
||||
.store
|
||||
.get(&state_root)
|
||||
.expect("should read db")
|
||||
.expect("should find state root")
|
||||
}
|
||||
|
||||
/// Returns a newly created block, signed by the proposer for the given slot.
|
||||
fn build_block(
|
||||
&self,
|
||||
@ -282,9 +258,14 @@ where
|
||||
)
|
||||
.into_iter()
|
||||
.for_each(|attestation| {
|
||||
self.chain
|
||||
match self
|
||||
.chain
|
||||
.process_attestation(attestation)
|
||||
.expect("should process attestation");
|
||||
.expect("should not error during attestation processing")
|
||||
{
|
||||
AttestationProcessingOutcome::Processed => (),
|
||||
other => panic!("did not successfully process attestation: {:?}", other),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,14 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use beacon_chain::test_utils::{
|
||||
AttestationStrategy, BeaconChainHarness, BlockStrategy, CommonTypes, PersistedBeaconChain,
|
||||
BEACON_CHAIN_DB_KEY,
|
||||
};
|
||||
use beacon_chain::AttestationProcessingOutcome;
|
||||
use beacon_chain::{
|
||||
test_utils::{
|
||||
AttestationStrategy, BeaconChainHarness, BlockStrategy, CommonTypes, PersistedBeaconChain,
|
||||
BEACON_CHAIN_DB_KEY,
|
||||
},
|
||||
BlockProcessingOutcome,
|
||||
};
|
||||
use lmd_ghost::ThreadSafeReducedTree;
|
||||
use rand::Rng;
|
||||
use store::{MemoryStore, Store};
|
||||
@ -25,7 +28,7 @@ lazy_static! {
|
||||
type TestForkChoice = ThreadSafeReducedTree<MemoryStore, MinimalEthSpec>;
|
||||
|
||||
fn get_harness(validator_count: usize) -> BeaconChainHarness<TestForkChoice, MinimalEthSpec> {
|
||||
let harness = BeaconChainHarness::from_keypairs(KEYPAIRS[0..validator_count].to_vec());
|
||||
let harness = BeaconChainHarness::new(KEYPAIRS[0..validator_count].to_vec());
|
||||
|
||||
harness.advance_slot();
|
||||
|
||||
@ -461,3 +464,48 @@ fn free_attestations_added_to_fork_choice_all_updated() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_skip_slot_test(skip_slots: u64) {
|
||||
let num_validators = 8;
|
||||
let harness_a = get_harness(num_validators);
|
||||
let harness_b = get_harness(num_validators);
|
||||
|
||||
for _ in 0..skip_slots {
|
||||
harness_a.advance_slot();
|
||||
harness_b.advance_slot();
|
||||
}
|
||||
|
||||
harness_a.extend_chain(
|
||||
1,
|
||||
BlockStrategy::OnCanonicalHead,
|
||||
// No attestation required for test.
|
||||
AttestationStrategy::SomeValidators(vec![]),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
harness_a.chain.head().beacon_block.slot,
|
||||
Slot::new(skip_slots + 1)
|
||||
);
|
||||
assert_eq!(harness_b.chain.head().beacon_block.slot, Slot::new(0));
|
||||
|
||||
assert_eq!(
|
||||
harness_b
|
||||
.chain
|
||||
.process_block(harness_a.chain.head().beacon_block.clone()),
|
||||
Ok(BlockProcessingOutcome::Processed {
|
||||
block_root: harness_a.chain.head().beacon_block_root
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
harness_b.chain.head().beacon_block.slot,
|
||||
Slot::new(skip_slots + 1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn produces_and_processes_with_genesis_skip_slots() {
|
||||
for i in 0..MinimalEthSpec::slots_per_epoch() * 4 {
|
||||
run_skip_slot_test(i)
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ pub struct Config {
|
||||
/// files. It can only be configured via the CLI.
|
||||
#[serde(skip)]
|
||||
pub beacon_chain_start_method: BeaconChainStartMethod,
|
||||
pub eth1_backend_method: Eth1BackendMethod,
|
||||
pub network: network::NetworkConfig,
|
||||
pub rpc: rpc::RPCConfig,
|
||||
pub rest_api: rest_api::ApiConfig,
|
||||
@ -54,6 +55,10 @@ pub enum BeaconChainStartMethod {
|
||||
},
|
||||
/// Create a new beacon chain by loading a YAML-encoded genesis state from a file.
|
||||
Yaml { file: PathBuf },
|
||||
/// Create a new beacon chain by loading a SSZ-encoded genesis state from a file.
|
||||
Ssz { file: PathBuf },
|
||||
/// Create a new beacon chain by loading a JSON-encoded genesis state from a file.
|
||||
Json { file: PathBuf },
|
||||
/// Create a new beacon chain by using a HTTP server (running our REST-API) to load genesis and
|
||||
/// finalized states and blocks.
|
||||
HttpBootstrap { server: String, port: Option<u16> },
|
||||
@ -65,6 +70,22 @@ impl Default for BeaconChainStartMethod {
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines which Eth1 backend the client should use.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Eth1BackendMethod {
|
||||
/// Use the mocked eth1 backend used in interop testing
|
||||
Interop,
|
||||
/// Use a web3 connection to a running Eth1 node.
|
||||
Web3 { server: String },
|
||||
}
|
||||
|
||||
impl Default for Eth1BackendMethod {
|
||||
fn default() -> Self {
|
||||
Eth1BackendMethod::Interop
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@ -77,6 +98,7 @@ impl Default for Config {
|
||||
rest_api: <_>::default(),
|
||||
spec_constants: TESTNET_SPEC_CONSTANTS.into(),
|
||||
beacon_chain_start_method: <_>::default(),
|
||||
eth1_backend_method: <_>::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ pub mod error;
|
||||
pub mod notifier;
|
||||
|
||||
use beacon_chain::{
|
||||
lmd_ghost::ThreadSafeReducedTree, slot_clock::SystemTimeSlotClock, store::Store, BeaconChain,
|
||||
BeaconChainBuilder,
|
||||
lmd_ghost::ThreadSafeReducedTree, slot_clock::SystemTimeSlotClock, store::Store,
|
||||
test_utils::generate_deterministic_keypairs, BeaconChain, BeaconChainBuilder,
|
||||
};
|
||||
use exit_future::Signal;
|
||||
use futures::{future::Future, Stream};
|
||||
@ -21,14 +21,14 @@ use tokio::runtime::TaskExecutor;
|
||||
use tokio::timer::Interval;
|
||||
use types::EthSpec;
|
||||
|
||||
pub use beacon_chain::BeaconChainTypes;
|
||||
pub use config::{BeaconChainStartMethod, Config as ClientConfig};
|
||||
pub use beacon_chain::{BeaconChainTypes, Eth1ChainBackend, InteropEth1ChainBackend};
|
||||
pub use config::{BeaconChainStartMethod, Config as ClientConfig, Eth1BackendMethod};
|
||||
pub use eth2_config::Eth2Config;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientType<S: Store, E: EthSpec> {
|
||||
_phantom_t: PhantomData<S>,
|
||||
_phantom_u: PhantomData<E>,
|
||||
_phantom_s: PhantomData<S>,
|
||||
_phantom_e: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<S, E> BeaconChainTypes for ClientType<S, E>
|
||||
@ -39,6 +39,7 @@ where
|
||||
type Store = S;
|
||||
type SlotClock = SystemTimeSlotClock;
|
||||
type LmdGhost = ThreadSafeReducedTree<S, E>;
|
||||
type Eth1Chain = InteropEth1ChainBackend<E>;
|
||||
type EthSpec = E;
|
||||
}
|
||||
|
||||
@ -105,7 +106,7 @@ where
|
||||
"method" => "recent"
|
||||
);
|
||||
BeaconChainBuilder::recent_genesis(
|
||||
*validator_count,
|
||||
&generate_deterministic_keypairs(*validator_count),
|
||||
*minutes,
|
||||
spec.clone(),
|
||||
log.clone(),
|
||||
@ -124,7 +125,7 @@ where
|
||||
);
|
||||
BeaconChainBuilder::quick_start(
|
||||
*genesis_time,
|
||||
*validator_count,
|
||||
&generate_deterministic_keypairs(*validator_count),
|
||||
spec.clone(),
|
||||
log.clone(),
|
||||
)?
|
||||
@ -138,6 +139,24 @@ where
|
||||
);
|
||||
BeaconChainBuilder::yaml_state(file, spec.clone(), log.clone())?
|
||||
}
|
||||
BeaconChainStartMethod::Ssz { file } => {
|
||||
info!(
|
||||
log,
|
||||
"Starting beacon chain";
|
||||
"file" => format!("{:?}", file),
|
||||
"method" => "ssz"
|
||||
);
|
||||
BeaconChainBuilder::ssz_state(file, spec.clone(), log.clone())?
|
||||
}
|
||||
BeaconChainStartMethod::Json { file } => {
|
||||
info!(
|
||||
log,
|
||||
"Starting beacon chain";
|
||||
"file" => format!("{:?}", file),
|
||||
"method" => "json"
|
||||
);
|
||||
BeaconChainBuilder::json_state(file, spec.clone(), log.clone())?
|
||||
}
|
||||
BeaconChainStartMethod::HttpBootstrap { server, port } => {
|
||||
info!(
|
||||
log,
|
||||
@ -150,9 +169,11 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let eth1_backend = T::Eth1Chain::new(String::new()).map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let beacon_chain: Arc<BeaconChain<T>> = Arc::new(
|
||||
beacon_chain_builder
|
||||
.build(store)
|
||||
.build(store, eth1_backend)
|
||||
.map_err(error::Error::from)?,
|
||||
);
|
||||
|
||||
|
@ -914,7 +914,7 @@ fn process_blocks<T: BeaconChainTypes>(
|
||||
BlockProcessingOutcome::ParentUnknown { parent } => {
|
||||
// blocks should be sequential and all parents should exist
|
||||
trace!(
|
||||
log, "ParentBlockUnknown";
|
||||
log, "Parent block is unknown";
|
||||
"parent_root" => format!("{}", parent),
|
||||
"baby_block_slot" => block.slot,
|
||||
);
|
||||
@ -923,34 +923,53 @@ fn process_blocks<T: BeaconChainTypes>(
|
||||
block.slot
|
||||
));
|
||||
}
|
||||
BlockProcessingOutcome::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
} => {
|
||||
if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot {
|
||||
// The block is too far in the future, drop it.
|
||||
BlockProcessingOutcome::BlockIsAlreadyKnown => {
|
||||
// this block is already known to us, move to the next
|
||||
debug!(
|
||||
log, "Imported a block that is already known";
|
||||
"parent_root" => format!("{}", parent),
|
||||
"baby_block_slot" => block.slot,
|
||||
);
|
||||
BlockProcessingOutcome::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
} => {
|
||||
if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot {
|
||||
// The block is too far in the future, drop it.
|
||||
trace!(
|
||||
self.log, "Block is ahead of our slot clock";
|
||||
"msg" => "block for future slot rejected, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
);
|
||||
return Err(format!(
|
||||
"Block at slot {} is too far in the future",
|
||||
block.slot
|
||||
));
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
trace!(
|
||||
self.log, "Block is slightly ahead of our slot clock, ignoring.";
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
);
|
||||
}
|
||||
}
|
||||
BlockProcessingOutcome::WouldRevertFinalizedSlot { .. } => {
|
||||
trace!(
|
||||
log, "FutureBlock";
|
||||
"msg" => "block for future slot rejected, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
self.log, "Finalized or earlier block processed";
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
);
|
||||
return Err(format!(
|
||||
"Block at slot {} is too far in the future",
|
||||
block.slot
|
||||
));
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
// block reached our finalized slot or was earlier, move to the next block
|
||||
}
|
||||
BlockProcessingOutcome::GenesisBlock => {
|
||||
trace!(
|
||||
log, "QueuedFutureBlock";
|
||||
"msg" => "queuing future block, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
self.log, "Genesis block was processed";
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
);
|
||||
}
|
||||
}
|
||||
BlockProcessingOutcome::FinalizedSlot => {
|
||||
trace!(
|
||||
log, "Finalized or earlier block processed";
|
||||
@ -959,8 +978,8 @@ fn process_blocks<T: BeaconChainTypes>(
|
||||
// block reached our finalized slot or was earlier, move to the next block
|
||||
}
|
||||
_ => {
|
||||
trace!(
|
||||
log, "InvalidBlock";
|
||||
warn!(
|
||||
log, "Invalid block received";
|
||||
"msg" => "peer sent invalid block",
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
);
|
||||
@ -968,7 +987,7 @@ fn process_blocks<T: BeaconChainTypes>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace!(
|
||||
warn!(
|
||||
log, "BlockProcessingFailure";
|
||||
"msg" => "unexpected condition in processing block.",
|
||||
"outcome" => format!("{:?}", processing_result)
|
||||
|
@ -385,7 +385,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
"msg" => "Failed to return all requested hashes",
|
||||
"start_slot" => req.start_slot,
|
||||
"current_slot" => self.chain.best_slot(),
|
||||
"current_slot" => format!("{:?}", self.chain.slot()),
|
||||
"requested" => req.count,
|
||||
"returned" => blocks.len(),
|
||||
);
|
||||
@ -523,6 +523,12 @@ impl NetworkContext {
|
||||
}
|
||||
|
||||
pub fn disconnect(&mut self, peer_id: PeerId, reason: GoodbyeReason) {
|
||||
warn!(
|
||||
&self.log,
|
||||
"Disconnecting peer (RPC)";
|
||||
"reason" => format!("{:?}", reason),
|
||||
"peer_id" => format!("{:?}", peer_id),
|
||||
);
|
||||
self.send_rpc_request(None, peer_id, RPCRequest::Goodbye(reason))
|
||||
// TODO: disconnect peers.
|
||||
}
|
||||
|
@ -14,9 +14,12 @@ store = { path = "../store" }
|
||||
version = { path = "../version" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "^1.0"
|
||||
serde_yaml = "0.8"
|
||||
slog = "^2.2.3"
|
||||
slog-term = "^2.4.0"
|
||||
slog-async = "^2.3.0"
|
||||
eth2_ssz = { path = "../../eth2/utils/ssz" }
|
||||
eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" }
|
||||
state_processing = { path = "../../eth2/state_processing" }
|
||||
types = { path = "../../eth2/types" }
|
||||
clap = "2.32.0"
|
||||
|
@ -1,8 +1,9 @@
|
||||
use super::{success_response, ApiResult};
|
||||
use super::{success_response, ApiResult, ResponseBuilder};
|
||||
use crate::{helpers::*, ApiError, UrlQuery};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use hyper::{Body, Request};
|
||||
use serde::Serialize;
|
||||
use ssz_derive::Encode;
|
||||
use std::sync::Arc;
|
||||
use store::Store;
|
||||
use types::{BeaconBlock, BeaconState, EthSpec, Hash256, Slot};
|
||||
@ -33,7 +34,7 @@ pub fn get_head<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult
|
||||
Ok(success_response(Body::from(json)))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, Encode)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BlockResponse<T: EthSpec> {
|
||||
pub root: Hash256,
|
||||
@ -77,11 +78,7 @@ pub fn get_block<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult
|
||||
beacon_block: block,
|
||||
};
|
||||
|
||||
let json: String = serde_json::to_string(&response).map_err(|e| {
|
||||
ApiError::ServerError(format!("Unable to serialize BlockResponse: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(success_response(Body::from(json)))
|
||||
ResponseBuilder::new(&req).body(&response)
|
||||
}
|
||||
|
||||
/// HTTP handler to return a `BeaconBlock` root at a given `slot`.
|
||||
@ -104,7 +101,22 @@ pub fn get_block_root<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
||||
Ok(success_response(Body::from(json)))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
/// HTTP handler to return a `BeaconState` at a given `root` or `slot`.
|
||||
///
|
||||
/// Will not return a state if the request slot is in the future. Will return states higher than
|
||||
/// the current head by skipping slots.
|
||||
pub fn get_genesis_state<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
||||
let beacon_chain = req
|
||||
.extensions()
|
||||
.get::<Arc<BeaconChain<T>>>()
|
||||
.ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?;
|
||||
|
||||
let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?;
|
||||
|
||||
ResponseBuilder::new(&req).body(&state)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Encode)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct StateResponse<T: EthSpec> {
|
||||
pub root: Hash256,
|
||||
@ -144,11 +156,7 @@ pub fn get_state<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult
|
||||
beacon_state: state,
|
||||
};
|
||||
|
||||
let json: String = serde_json::to_string(&response).map_err(|e| {
|
||||
ApiError::ServerError(format!("Unable to serialize StateResponse: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(success_response(Body::from(json)))
|
||||
ResponseBuilder::new(&req).body(&response)
|
||||
}
|
||||
|
||||
/// HTTP handler to return a `BeaconState` root at a given `slot`.
|
||||
|
@ -8,6 +8,7 @@ mod helpers;
|
||||
mod metrics;
|
||||
mod network;
|
||||
mod node;
|
||||
mod response_builder;
|
||||
mod spec;
|
||||
mod url_query;
|
||||
mod validator;
|
||||
@ -18,6 +19,7 @@ use eth2_config::Eth2Config;
|
||||
use hyper::rt::Future;
|
||||
use hyper::service::service_fn_ok;
|
||||
use hyper::{Body, Method, Response, Server, StatusCode};
|
||||
use response_builder::ResponseBuilder;
|
||||
use slog::{info, o, warn};
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
@ -145,6 +147,7 @@ pub fn start_server<T: BeaconChainTypes>(
|
||||
beacon::get_latest_finalized_checkpoint::<T>(req)
|
||||
}
|
||||
(&Method::GET, "/beacon/state") => beacon::get_state::<T>(req),
|
||||
(&Method::GET, "/beacon/state/genesis") => beacon::get_genesis_state::<T>(req),
|
||||
(&Method::GET, "/beacon/state_root") => beacon::get_state_root::<T>(req),
|
||||
|
||||
//TODO: Add aggreggate/filtered state lookups here, e.g. /beacon/validators/balances
|
||||
|
50
beacon_node/rest_api/src/response_builder.rs
Normal file
50
beacon_node/rest_api/src/response_builder.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use super::{ApiError, ApiResult};
|
||||
use http::header;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::Serialize;
|
||||
use ssz::Encode;
|
||||
|
||||
pub enum Encoding {
|
||||
JSON,
|
||||
SSZ,
|
||||
YAML,
|
||||
}
|
||||
|
||||
pub struct ResponseBuilder {
|
||||
encoding: Encoding,
|
||||
}
|
||||
|
||||
impl ResponseBuilder {
|
||||
pub fn new(req: &Request<Body>) -> Self {
|
||||
let encoding = match req.headers().get(header::CONTENT_TYPE) {
|
||||
Some(h) if h == "application/ssz" => Encoding::SSZ,
|
||||
Some(h) if h == "application/yaml" => Encoding::YAML,
|
||||
_ => Encoding::JSON,
|
||||
};
|
||||
|
||||
Self { encoding }
|
||||
}
|
||||
|
||||
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
||||
let body: Body = match self.encoding {
|
||||
Encoding::JSON => Body::from(serde_json::to_string(&item).map_err(|e| {
|
||||
ApiError::ServerError(format!(
|
||||
"Unable to serialize response body as JSON: {:?}",
|
||||
e
|
||||
))
|
||||
})?),
|
||||
Encoding::SSZ => Body::from(item.as_ssz_bytes()),
|
||||
Encoding::YAML => Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
||||
ApiError::ServerError(format!(
|
||||
"Unable to serialize response body as YAML: {:?}",
|
||||
e
|
||||
))
|
||||
})?),
|
||||
};
|
||||
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.body(Body::from(body))
|
||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ impl<T: BeaconChainTypes> ValidatorService for ValidatorServiceInstance<T> {
|
||||
let slot = epoch.start_slot(T::EthSpec::slots_per_epoch());
|
||||
|
||||
let mut state = if let Ok(state) = self.chain.state_at_slot(slot) {
|
||||
state.as_ref().clone()
|
||||
state.clone()
|
||||
} else {
|
||||
let log_clone = self.log.clone();
|
||||
let f = sink
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clap::ArgMatches;
|
||||
use client::{BeaconChainStartMethod, ClientConfig, Eth2Config};
|
||||
use client::{BeaconChainStartMethod, ClientConfig, Eth1BackendMethod, Eth2Config};
|
||||
use eth2_config::{read_from_file, write_to_file};
|
||||
use lighthouse_bootstrap::Bootstrapper;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
@ -25,6 +25,14 @@ type Config = (ClientConfig, Eth2Config);
|
||||
pub fn get_configs(cli_args: &ArgMatches, log: &Logger) -> Result<Config> {
|
||||
let mut builder = ConfigBuilder::new(cli_args, log)?;
|
||||
|
||||
if let Some(server) = cli_args.value_of("eth1-server") {
|
||||
builder.set_eth1_backend_method(Eth1BackendMethod::Web3 {
|
||||
server: server.into(),
|
||||
})
|
||||
} else {
|
||||
builder.set_eth1_backend_method(Eth1BackendMethod::Interop)
|
||||
}
|
||||
|
||||
match cli_args.subcommand() {
|
||||
("testnet", Some(sub_cmd_args)) => {
|
||||
process_testnet_subcommand(&mut builder, sub_cmd_args, log)?
|
||||
@ -70,11 +78,15 @@ fn process_testnet_subcommand(
|
||||
builder.set_random_datadir()?;
|
||||
}
|
||||
|
||||
if cli_args.is_present("force") {
|
||||
builder.clean_datadir()?;
|
||||
}
|
||||
|
||||
let is_bootstrap = cli_args.subcommand_name() == Some("bootstrap");
|
||||
|
||||
if let Some(path_string) = cli_args.value_of("eth2-config") {
|
||||
if is_bootstrap {
|
||||
return Err("Cannot supply --eth2-config when using bootsrap".to_string());
|
||||
return Err("Cannot supply --eth2-config when using bootstrap".to_string());
|
||||
}
|
||||
|
||||
let path = path_string
|
||||
@ -85,6 +97,18 @@ fn process_testnet_subcommand(
|
||||
builder.update_spec_from_subcommand(&cli_args)?;
|
||||
}
|
||||
|
||||
if let Some(slot_time) = cli_args.value_of("slot-time") {
|
||||
if is_bootstrap {
|
||||
return Err("Cannot supply --slot-time flag whilst using bootstrap.".into());
|
||||
}
|
||||
|
||||
let slot_time = slot_time
|
||||
.parse::<u64>()
|
||||
.map_err(|e| format!("Unable to parse slot-time: {:?}", e))?;
|
||||
|
||||
builder.set_slot_time(slot_time);
|
||||
}
|
||||
|
||||
if let Some(path_string) = cli_args.value_of("client-config") {
|
||||
let path = path_string
|
||||
.parse::<PathBuf>()
|
||||
@ -92,10 +116,6 @@ fn process_testnet_subcommand(
|
||||
builder.load_client_config(path)?;
|
||||
}
|
||||
|
||||
if cli_args.is_present("force") {
|
||||
builder.clean_datadir()?;
|
||||
}
|
||||
|
||||
info!(
|
||||
log,
|
||||
"Creating new datadir";
|
||||
@ -117,6 +137,7 @@ fn process_testnet_subcommand(
|
||||
.and_then(|s| s.parse::<u16>().ok());
|
||||
|
||||
builder.import_bootstrap_libp2p_address(server, port)?;
|
||||
builder.import_bootstrap_enr_address(server)?;
|
||||
builder.import_bootstrap_eth2_config(server)?;
|
||||
|
||||
builder.set_beacon_chain_start_method(BeaconChainStartMethod::HttpBootstrap {
|
||||
@ -160,6 +181,32 @@ fn process_testnet_subcommand(
|
||||
genesis_time,
|
||||
})
|
||||
}
|
||||
("file", Some(cli_args)) => {
|
||||
let file = cli_args
|
||||
.value_of("file")
|
||||
.ok_or_else(|| "No filename specified")?
|
||||
.parse::<PathBuf>()
|
||||
.map_err(|e| format!("Unable to parse filename: {:?}", e))?;
|
||||
|
||||
let format = cli_args
|
||||
.value_of("format")
|
||||
.ok_or_else(|| "No file format specified")?;
|
||||
|
||||
let start_method = match format {
|
||||
"yaml" => BeaconChainStartMethod::Yaml { file },
|
||||
"ssz" => BeaconChainStartMethod::Ssz { file },
|
||||
"json" => BeaconChainStartMethod::Json { file },
|
||||
other => return Err(format!("Unknown genesis file format: {}", other)),
|
||||
};
|
||||
|
||||
builder.set_beacon_chain_start_method(start_method)
|
||||
}
|
||||
(cmd, Some(_)) => {
|
||||
return Err(format!(
|
||||
"Invalid valid method specified: {}. See 'testnet --help'.",
|
||||
cmd
|
||||
))
|
||||
}
|
||||
_ => return Err("No testnet method specified. See 'testnet --help'.".into()),
|
||||
};
|
||||
|
||||
@ -250,7 +297,12 @@ impl<'a> ConfigBuilder<'a> {
|
||||
self.client_config.beacon_chain_start_method = method;
|
||||
}
|
||||
|
||||
/// Import the libp2p address for `server` into the list of bootnodes in `self`.
|
||||
/// Sets the method for starting the beacon chain.
|
||||
pub fn set_eth1_backend_method(&mut self, method: Eth1BackendMethod) {
|
||||
self.client_config.eth1_backend_method = method;
|
||||
}
|
||||
|
||||
/// Import the libp2p address for `server` into the list of libp2p nodes to connect with.
|
||||
///
|
||||
/// If `port` is `Some`, it is used as the port for the `Multiaddr`. If `port` is `None`,
|
||||
/// attempts to connect to the `server` via HTTP and retrieve it's libp2p listen port.
|
||||
@ -259,7 +311,7 @@ impl<'a> ConfigBuilder<'a> {
|
||||
server: &str,
|
||||
port: Option<u16>,
|
||||
) -> Result<()> {
|
||||
let bootstrapper = Bootstrapper::from_server_string(server.to_string())?;
|
||||
let bootstrapper = Bootstrapper::connect(server.to_string(), &self.log)?;
|
||||
|
||||
if let Some(server_multiaddr) = bootstrapper.best_effort_multiaddr(port) {
|
||||
info!(
|
||||
@ -282,6 +334,28 @@ impl<'a> ConfigBuilder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Import the enr address for `server` into the list of initial enrs (boot nodes).
|
||||
pub fn import_bootstrap_enr_address(&mut self, server: &str) -> Result<()> {
|
||||
let bootstrapper = Bootstrapper::connect(server.to_string(), &self.log)?;
|
||||
|
||||
if let Ok(enr) = bootstrapper.enr() {
|
||||
info!(
|
||||
self.log,
|
||||
"Loaded bootstrapper libp2p address";
|
||||
"enr" => format!("{:?}", enr)
|
||||
);
|
||||
|
||||
self.client_config.network.boot_nodes.push(enr);
|
||||
} else {
|
||||
warn!(
|
||||
self.log,
|
||||
"Unable to estimate a bootstrapper enr address, this node may not find any peers."
|
||||
);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the config data_dir to be an random directory.
|
||||
///
|
||||
/// Useful for easily spinning up ephemeral testnets.
|
||||
@ -296,7 +370,7 @@ impl<'a> ConfigBuilder<'a> {
|
||||
|
||||
/// Imports an `Eth2Config` from `server`, returning an error if this fails.
|
||||
pub fn import_bootstrap_eth2_config(&mut self, server: &str) -> Result<()> {
|
||||
let bootstrapper = Bootstrapper::from_server_string(server.to_string())?;
|
||||
let bootstrapper = Bootstrapper::connect(server.to_string(), &self.log)?;
|
||||
|
||||
self.update_eth2_config(bootstrapper.eth2_config()?);
|
||||
|
||||
@ -307,6 +381,10 @@ impl<'a> ConfigBuilder<'a> {
|
||||
self.eth2_config = eth2_config;
|
||||
}
|
||||
|
||||
fn set_slot_time(&mut self, milliseconds_per_slot: u64) {
|
||||
self.eth2_config.spec.milliseconds_per_slot = milliseconds_per_slot;
|
||||
}
|
||||
|
||||
/// Reads the subcommand and tries to update `self.eth2_config` based up on the `--spec` flag.
|
||||
///
|
||||
/// Returns an error if the `--spec` flag is not present in the given `cli_args`.
|
||||
|
@ -163,6 +163,16 @@ fn main() {
|
||||
.takes_value(true),
|
||||
)
|
||||
|
||||
/*
|
||||
* Eth1 Integration
|
||||
*/
|
||||
.arg(
|
||||
Arg::with_name("eth1-server")
|
||||
.long("eth1-server")
|
||||
.value_name("SERVER")
|
||||
.help("Specifies the server for a web3 connection to the Eth1 chain.")
|
||||
.takes_value(true)
|
||||
)
|
||||
/*
|
||||
* Database parameters.
|
||||
*/
|
||||
@ -235,6 +245,13 @@ fn main() {
|
||||
backup directory.")
|
||||
.conflicts_with("random-datadir")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("slot-time")
|
||||
.long("slot-time")
|
||||
.short("t")
|
||||
.value_name("MILLISECONDS")
|
||||
.help("Defines the slot time when creating a new testnet.")
|
||||
)
|
||||
/*
|
||||
* `boostrap`
|
||||
*
|
||||
@ -246,6 +263,7 @@ fn main() {
|
||||
.arg(Arg::with_name("server")
|
||||
.value_name("HTTP_SERVER")
|
||||
.required(true)
|
||||
.default_value("http://localhost:5052")
|
||||
.help("A HTTP server, with a http:// prefix"))
|
||||
.arg(Arg::with_name("libp2p-port")
|
||||
.short("p")
|
||||
@ -298,9 +316,14 @@ fn main() {
|
||||
*
|
||||
* Start a new node, using a genesis state loaded from a YAML file
|
||||
*/
|
||||
.subcommand(SubCommand::with_name("yaml")
|
||||
.about("Creates a new datadir where the genesis state is read from YAML. Will fail to parse \
|
||||
a YAML state that was generated to a different spec than that specified by --spec.")
|
||||
.subcommand(SubCommand::with_name("file")
|
||||
.about("Creates a new datadir where the genesis state is read from YAML. May fail to parse \
|
||||
a file that was generated to a different spec than that specified by --spec.")
|
||||
.arg(Arg::with_name("format")
|
||||
.value_name("FORMAT")
|
||||
.required(true)
|
||||
.possible_values(&["yaml", "ssz", "json"])
|
||||
.help("The encoding of the state in the file."))
|
||||
.arg(Arg::with_name("file")
|
||||
.value_name("YAML_FILE")
|
||||
.required(true)
|
||||
|
@ -1,4 +1,7 @@
|
||||
use client::{error, notifier, BeaconChainTypes, Client, ClientConfig, ClientType, Eth2Config};
|
||||
use client::{
|
||||
error, notifier, BeaconChainTypes, Client, ClientConfig, ClientType, Eth1BackendMethod,
|
||||
Eth2Config,
|
||||
};
|
||||
use futures::sync::oneshot;
|
||||
use futures::Future;
|
||||
use slog::{error, info};
|
||||
@ -47,55 +50,30 @@ pub fn run_beacon_node(
|
||||
"spec_constants" => &spec_constants,
|
||||
);
|
||||
|
||||
macro_rules! run_client {
|
||||
($store: ty, $eth_spec: ty) => {
|
||||
run::<ClientType<$store, $eth_spec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
if let Eth1BackendMethod::Web3 { .. } = client_config.eth1_backend_method {
|
||||
return Err("Starting from web3 backend is not supported for interop.".into());
|
||||
}
|
||||
|
||||
match (db_type.as_str(), spec_constants.as_str()) {
|
||||
("disk", "minimal") => run::<ClientType<DiskStore, MinimalEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
),
|
||||
("memory", "minimal") => run::<ClientType<MemoryStore, MinimalEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
),
|
||||
("disk", "mainnet") => run::<ClientType<DiskStore, MainnetEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
),
|
||||
("memory", "mainnet") => run::<ClientType<MemoryStore, MainnetEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
),
|
||||
("disk", "interop") => run::<ClientType<DiskStore, InteropEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
),
|
||||
("memory", "interop") => run::<ClientType<MemoryStore, InteropEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
eth2_config,
|
||||
executor,
|
||||
runtime,
|
||||
log,
|
||||
),
|
||||
("disk", "minimal") => run_client!(DiskStore, MinimalEthSpec),
|
||||
("disk", "mainnet") => run_client!(DiskStore, MainnetEthSpec),
|
||||
("disk", "interop") => run_client!(DiskStore, InteropEthSpec),
|
||||
("memory", "minimal") => run_client!(MemoryStore, MinimalEthSpec),
|
||||
("memory", "mainnet") => run_client!(MemoryStore, MainnetEthSpec),
|
||||
("memory", "interop") => run_client!(MemoryStore, InteropEthSpec),
|
||||
(db_type, spec) => {
|
||||
error!(log, "Unknown runtime configuration"; "spec_constants" => spec, "db_type" => db_type);
|
||||
Err("Unknown specification and/or db_type.".into())
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
* [Introduction](./intro.md)
|
||||
* [Development Environment](./setup.md)
|
||||
* [Testnets](./testnets.md)
|
||||
* [Simple Local Testnet](./simple-testnet.md)
|
||||
* [Interop](./interop.md)
|
||||
* [Interop Tips & Tricks](./interop-tips.md)
|
||||
* [Simple Local Testnet](./simple-testnet.md)
|
||||
* [Interop](./interop.md)
|
||||
* [Environment](./interop-environment.md)
|
||||
* [CLI Overview](./interop-cli.md)
|
||||
* [Scenarios](./interop-scenarios.md)
|
||||
* [Cheat-sheet](./interop-cheat-sheet.md)
|
||||
|
139
book/src/interop-cheat-sheet.md
Normal file
139
book/src/interop-cheat-sheet.md
Normal file
@ -0,0 +1,139 @@
|
||||
# Interop Cheat-sheet
|
||||
|
||||
This document contains a list of tips and tricks that may be useful during
|
||||
interop testing.
|
||||
|
||||
- When starting a beacon node:
|
||||
- [Specify a boot node by multiaddr](#boot-node-multiaddr)
|
||||
- [Specify a boot node by ENR](#boot-node-enr)
|
||||
- [Avoid port clashes when starting multiple nodes](#port-bump)
|
||||
- [Specify a custom slot time](#slot-time)
|
||||
- Using the beacon node HTTP API:
|
||||
- [Curl a node's ENR](#http-enr)
|
||||
- [Curl a node's connected peers](#http-peer-ids)
|
||||
- [Curl a node's local peer id](#http-peer-id)
|
||||
- [Curl a node's listening multiaddrs](#http-listen-addresses)
|
||||
- [Curl a node's beacon chain head](#http-head)
|
||||
- [Curl a node's finalized checkpoint](#http-finalized)
|
||||
|
||||
## Category: CLI
|
||||
|
||||
The `--help` command provides detail on the CLI interface. Here are some
|
||||
interop-specific CLI commands.
|
||||
|
||||
<a name="boot-node-multiaddr"></a>
|
||||
### Specify a boot node by multiaddr
|
||||
|
||||
You can specify a static list of multiaddrs when booting Lighthouse using
|
||||
the `--libp2p-addresses` command.
|
||||
|
||||
#### Example:
|
||||
|
||||
```
|
||||
$ ./beacon_node --libp2p-addresses /ip4/192.168.0.1/tcp/9000
|
||||
```
|
||||
|
||||
<a name="boot-node-enr"></a>
|
||||
### Specify a boot node by ENR
|
||||
|
||||
You can specify a static list of Discv5 addresses when booting Lighthouse using
|
||||
the `--boot-nodes` command.
|
||||
|
||||
#### Example:
|
||||
|
||||
```
|
||||
$ ./beacon_node --boot-nodes -IW4QB2Hi8TPuEzQ41Cdf1r2AUU1FFVFDBJdJyOkWk2qXpZfFZQy2YnJIyoT_5fnbtrXUouoskmydZl4pIg90clIkYUDgmlwhH8AAAGDdGNwgiMog3VkcIIjKIlzZWNwMjU2azGhAjg0-DsTkQynhJCRnLLttBK1RS78lmUkLa-wgzAi-Ob5
|
||||
```
|
||||
|
||||
<a name="port-bump"></a>
|
||||
### Avoid port clashes when starting nodes
|
||||
|
||||
Starting a second Lighthouse node on the same machine will fail due to TCP/UDP
|
||||
port collisions. Use the `-b` (`--port-bump`) flag to increase all listening
|
||||
ports by some `n`.
|
||||
|
||||
#### Example:
|
||||
|
||||
Increase all ports by `10` (using multiples of `10` is recommended).
|
||||
|
||||
```
|
||||
$ ./beacon_node -b 10
|
||||
```
|
||||
|
||||
<a name="slot-time"></a>
|
||||
### Start a testnet with a custom slot time
|
||||
|
||||
Lighthouse can run at quite low slot times when there are few validators (e.g.,
|
||||
`500 ms` slot times should be fine for 8 validators).
|
||||
|
||||
#### Example
|
||||
|
||||
The `-t` (`--slot-time`) flag specifies the milliseconds per slot.
|
||||
|
||||
```
|
||||
$ ./beacon_node testnet -t 500 recent 8
|
||||
```
|
||||
|
||||
> Note: `bootstrap` loads the slot time via HTTP and therefore conflicts with
|
||||
> this flag.
|
||||
|
||||
## Category: HTTP API
|
||||
|
||||
Examples assume there is a Lighthouse node exposing a HTTP API on
|
||||
`localhost:5052`. Responses are JSON.
|
||||
|
||||
<a name="http-enr"></a>
|
||||
### Get the node's ENR
|
||||
|
||||
```
|
||||
$ curl localhost:5052/network/enr
|
||||
|
||||
"-IW4QFyf1VlY5pZs0xZuvKMRZ9_cdl9WMCDAAJXZiZiuGcfRYoU40VPrYDLQj5prneJIz3zcbTjHp9BbThc-yiymJO8HgmlwhH8AAAGDdGNwgiMog3VkcIIjKIlzZWNwMjU2azGhAjg0-DsTkQynhJCRnLLttBK1RS78lmUkLa-wgzAi-Ob5"%
|
||||
```
|
||||
|
||||
<a name="http-peer-ids"></a>
|
||||
### Get a list of connected peer ids
|
||||
|
||||
```
|
||||
$ curl localhost:5052/network/peers
|
||||
|
||||
["QmeMFRTWfo3KbVG7dEBXGhyRMa29yfmnJBXW84rKuGEhuL"]%
|
||||
```
|
||||
|
||||
<a name="http-peer-id"></a>
|
||||
### Get the node's peer id
|
||||
|
||||
```
|
||||
curl localhost:5052/network/peer_id
|
||||
|
||||
"QmRD1qs2AqNNRdBcGHUGpUGkpih5cmdL32mhh22Sy79xsJ"%
|
||||
```
|
||||
|
||||
<a name="http-listen-addresses"></a>
|
||||
### Get the list of listening libp2p addresses
|
||||
|
||||
Lists all the libp2p multiaddrs that the node is listening on.
|
||||
|
||||
```
|
||||
curl localhost:5052/network/listen_addresses
|
||||
|
||||
["/ip4/127.0.0.1/tcp/9000","/ip4/192.168.1.121/tcp/9000","/ip4/172.17.0.1/tcp/9000","/ip4/172.42.0.1/tcp/9000","/ip6/::1/tcp/9000","/ip6/fdd3:c293:1bc::203/tcp/9000","/ip6/fdd3:c293:1bc:0:9aa9:b2ea:c610:44db/tcp/9000"]%
|
||||
```
|
||||
|
||||
<a name="http-head"></a>
|
||||
### Get the node's beacon chain head
|
||||
|
||||
```
|
||||
curl localhost:5052/beacon/head
|
||||
|
||||
{"slot":0,"block_root":"0x827bf71805540aa13f6d8c7d18b41b287b2094a4d7a28cbb8deb061dbf5df4f5","state_root":"0x90a78d73294bc9c7519a64e1912161be0e823eb472012ff54204e15a4d717fa5"}%
|
||||
```
|
||||
|
||||
<a name="http-finalized"></a>
|
||||
### Get the node's finalized checkpoint
|
||||
|
||||
```
|
||||
curl localhost:5052/beacon/latest_finalized_checkpoint
|
||||
|
||||
{"epoch":0,"root":"0x0000000000000000000000000000000000000000000000000000000000000000"}%
|
||||
```
|
29
book/src/interop-cli.md
Normal file
29
book/src/interop-cli.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Interop CLI Overview
|
||||
|
||||
The Lighthouse CLI has two primary tasks:
|
||||
|
||||
- **Resuming** an existing database with `$ ./beacon_node`.
|
||||
- **Creating** a new testnet database using `$ ./beacon_node testnet`.
|
||||
|
||||
_See [Scenarios](./interop-scenarios.md) for methods we've anticipated will be
|
||||
used interop._
|
||||
|
||||
## Creating a new database
|
||||
|
||||
There are several methods for creating a new beacon node database:
|
||||
|
||||
- `quick`: using the `(validator_client, genesis_time)` tuple.
|
||||
- `recent`: as above but `genesis_time` is set to the start of some recent time
|
||||
window.
|
||||
- `file`: loads the genesis file from disk in one of multiple formats.
|
||||
- `bootstrap`: a Lighthouse-specific method where we connect to a running node
|
||||
and download it's specification and genesis state via the HTTP API.
|
||||
|
||||
See `$ ./beacon_node testnet --help` for more detail.
|
||||
|
||||
## Resuming from an existing database
|
||||
|
||||
Once a database has been created, it can be resumed by running `$ ./beacon_node`.
|
||||
|
||||
Presently, this command will fail if no existing database is found. You must
|
||||
use the `$ ./beacon_node testnet` command to create a new database.
|
30
book/src/interop-environment.md
Normal file
30
book/src/interop-environment.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Interop Environment
|
||||
|
||||
All that is required for inter-op is a built and tested [development
|
||||
environment](./setup.md).
|
||||
|
||||
## Repositories
|
||||
|
||||
You will only require the [sigp/lighthouse](http://github.com/sigp/lighthouse)
|
||||
library.
|
||||
|
||||
To allow for faster build/test iterations we will use the
|
||||
[`interop`](https://github.com/sigp/lighthouse/tree/interop) branch of
|
||||
[sigp/lighthouse](https://github.com/sigp/lighthouse/tree/interop) for
|
||||
September 2019 interop. **Please use ensure you `git checkout interop` after
|
||||
cloning the repo.**
|
||||
|
||||
## File System
|
||||
|
||||
When lighthouse boots, it will create the following
|
||||
directories:
|
||||
|
||||
- `~/.lighthouse`: database and configuration for the beacon node.
|
||||
- `~/.lighthouse-validator`: database and configuration for the validator
|
||||
client.
|
||||
|
||||
After building the binaries with `cargo build --release --all`, there will be a
|
||||
`target/release` directory in the root of the Lighthouse repository. This is
|
||||
where the `beacon_node` and `validator_client` binaries are located.
|
||||
|
||||
You do not need to create any of these directories manually.
|
98
book/src/interop-scenarios.md
Normal file
98
book/src/interop-scenarios.md
Normal file
@ -0,0 +1,98 @@
|
||||
# Interop Scenarios
|
||||
|
||||
Here we demonstrate some expected interop scenarios.
|
||||
|
||||
All scenarios assume a working [development environment](./setup.md) and
|
||||
commands are based in the `target/release` directory (this is the build dir for
|
||||
`cargo`).
|
||||
|
||||
Additional functions can be found in the [interop
|
||||
cheat-sheet](./interop-cheat-sheet.md).
|
||||
|
||||
### Table of contents
|
||||
|
||||
- [Starting from a`validator_count, genesis_time` tuple](#quick-start)
|
||||
- [Starting a node from a genesis state file](#state-file)
|
||||
- [Starting a validator client](#val-client)
|
||||
- [Exporting a genesis state file](#export) from a running Lighthouse
|
||||
node
|
||||
|
||||
|
||||
<a name="quick-start"></a>
|
||||
### Start beacon node given a validator count and genesis_time
|
||||
|
||||
|
||||
To start a brand-new beacon node (with no history) use:
|
||||
|
||||
```
|
||||
$ ./beacon_node testnet -f quick 8 1567222226
|
||||
```
|
||||
> Notes:
|
||||
>
|
||||
> - This method conforms the ["Quick-start
|
||||
genesis"](https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#quick-start-genesis)
|
||||
method in the `ethereum/eth2.0-pm` repository.
|
||||
> - The `-f` flag ignores any existing database or configuration, backing them
|
||||
> up before re-initializing.
|
||||
> - `8` is the validator count and `1567222226` is the genesis time.
|
||||
> - See `$ ./beacon_node testnet quick --help` for more configuration options.
|
||||
|
||||
<a name="state-file"></a>
|
||||
### Start Beacon Node given a genesis state file
|
||||
|
||||
A genesis state can be read from file using the `testnet file` subcommand.
|
||||
There are three supported formats:
|
||||
|
||||
- `ssz` (default)
|
||||
- `json`
|
||||
- `yaml`
|
||||
|
||||
Start a new node using `/tmp/genesis.ssz` as the genesis state:
|
||||
|
||||
```
|
||||
$ ./beacon_node testnet --spec minimal -f file ssz /tmp/genesis.ssz
|
||||
```
|
||||
|
||||
> Notes:
|
||||
>
|
||||
> - The `-f` flag ignores any existing database or configuration, backing them
|
||||
> up before re-initializing.
|
||||
> - See `$ ./beacon_node testnet file --help` for more configuration options.
|
||||
> - The `--spec` flag is required to allow SSZ parsing of fixed-length lists.
|
||||
|
||||
<a name="val-client"></a>
|
||||
### Start an auto-configured validator client
|
||||
|
||||
To start a brand-new validator client (with no history) use:
|
||||
|
||||
```
|
||||
$ ./validator_client testnet -b insecure 0 8
|
||||
```
|
||||
|
||||
> Notes:
|
||||
>
|
||||
> - The `-b` flag means the validator client will "bootstrap" specs and config
|
||||
> from the beacon node.
|
||||
> - The `insecure` command dictates that the [interop keypairs](https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#pubkeyprivkey-generation)
|
||||
> will be used.
|
||||
> - The `0 8` indicates that this validator client should manage 8 validators,
|
||||
> starting at validator 0 (the first deposited validator).
|
||||
> - The validator client will try to connect to the beacon node at `localhost`.
|
||||
> See `--help` to configure that address and other features.
|
||||
> - The validator client will operate very unsafely in `testnet` mode, happily
|
||||
> swapping between chains and creating double-votes.
|
||||
|
||||
<a name="export"></a>
|
||||
### Exporting a genesis file
|
||||
|
||||
Genesis states can downloaded from a running Lighthouse node via the HTTP API. Three content-types are supported:
|
||||
|
||||
- `application/json`
|
||||
- `application/yaml`
|
||||
- `application/ssz`
|
||||
|
||||
Using `curl`, a genesis state can be downloaded to `/tmp/genesis.ssz`:
|
||||
|
||||
```
|
||||
$ curl --header "Content-Type: application/ssz" "localhost:5052/beacon/state/genesis" -o /tmp/genesis.ssz
|
||||
```
|
@ -1,104 +1 @@
|
||||
# Interop Tips & Tricks
|
||||
|
||||
This document contains a list of tips and tricks that may be useful during
|
||||
interop testing.
|
||||
|
||||
## Command-line Interface
|
||||
|
||||
The `--help` command provides detail on the CLI interface. Here are some
|
||||
interop-specific CLI commands.
|
||||
|
||||
### Specify a boot node by multiaddr
|
||||
|
||||
You can specify a static list of multiaddrs when booting Lighthouse using
|
||||
the `--libp2p-addresses` command.
|
||||
|
||||
#### Example:
|
||||
|
||||
Runs an 8 validator quick-start chain, peering with `/ip4/192.168.0.1/tcp/9000` on boot.
|
||||
|
||||
```
|
||||
$ ./beacon_node --libp2p-addresses /ip4/192.168.0.1/tcp/9000 testnet -f quick 8 1567222226
|
||||
```
|
||||
|
||||
### Specify a boot node by ENR
|
||||
|
||||
You can specify a static list of Discv5 addresses when booting Lighthouse using
|
||||
the `--boot-nodes` command.
|
||||
|
||||
#### Example:
|
||||
|
||||
Runs an 8 validator quick-start chain, peering with `-IW4QB2...` on boot.
|
||||
|
||||
```
|
||||
$ ./beacon_node --boot-nodes -IW4QB2Hi8TPuEzQ41Cdf1r2AUU1FFVFDBJdJyOkWk2qXpZfFZQy2YnJIyoT_5fnbtrXUouoskmydZl4pIg90clIkYUDgmlwhH8AAAGDdGNwgiMog3VkcIIjKIlzZWNwMjU2azGhAjg0-DsTkQynhJCRnLLttBK1RS78lmUkLa-wgzAi-Ob5 testnet -f quick 8 1567222226
|
||||
```
|
||||
|
||||
### Avoid port clashes when starting nodes
|
||||
|
||||
Starting a second Lighthouse node on the same machine will fail due to TCP/UDP
|
||||
port collisions. Use the `-b` (`--port-bump`) flag to increase all listening
|
||||
ports by some `n`.
|
||||
|
||||
#### Example:
|
||||
|
||||
Increase all ports by `10` (using multiples of `10` is recommended).
|
||||
|
||||
```
|
||||
$ ./beacon_node -b 10 testnet -f quick 8 1567222226
|
||||
```
|
||||
|
||||
## HTTP API
|
||||
|
||||
Examples assume there is a Lighthouse node exposing a HTTP API on
|
||||
`localhost:5052`. Responses are JSON.
|
||||
|
||||
### Get the node's ENR
|
||||
|
||||
```
|
||||
$ curl localhost:5052/network/enr
|
||||
|
||||
"-IW4QFyf1VlY5pZs0xZuvKMRZ9_cdl9WMCDAAJXZiZiuGcfRYoU40VPrYDLQj5prneJIz3zcbTjHp9BbThc-yiymJO8HgmlwhH8AAAGDdGNwgiMog3VkcIIjKIlzZWNwMjU2azGhAjg0-DsTkQynhJCRnLLttBK1RS78lmUkLa-wgzAi-Ob5"%
|
||||
```
|
||||
|
||||
### Get a list of connected peer ids
|
||||
|
||||
```
|
||||
$ curl localhost:5052/network/peers
|
||||
|
||||
["QmeMFRTWfo3KbVG7dEBXGhyRMa29yfmnJBXW84rKuGEhuL"]%
|
||||
```
|
||||
|
||||
### Get the node's peer id
|
||||
|
||||
```
|
||||
curl localhost:5052/network/peer_id
|
||||
|
||||
"QmRD1qs2AqNNRdBcGHUGpUGkpih5cmdL32mhh22Sy79xsJ"%
|
||||
```
|
||||
|
||||
### Get the list of listening libp2p addresses
|
||||
|
||||
Lists all the libp2p multiaddrs that the node is listening on.
|
||||
|
||||
```
|
||||
curl localhost:5052/network/listen_addresses
|
||||
|
||||
["/ip4/127.0.0.1/tcp/9000","/ip4/192.168.1.121/tcp/9000","/ip4/172.17.0.1/tcp/9000","/ip4/172.42.0.1/tcp/9000","/ip6/::1/tcp/9000","/ip6/fdd3:c293:1bc::203/tcp/9000","/ip6/fdd3:c293:1bc:0:9aa9:b2ea:c610:44db/tcp/9000"]%
|
||||
```
|
||||
|
||||
### Get the node's beacon chain head
|
||||
|
||||
```
|
||||
curl localhost:5052/beacon/head
|
||||
|
||||
{"slot":0,"block_root":"0x827bf71805540aa13f6d8c7d18b41b287b2094a4d7a28cbb8deb061dbf5df4f5","state_root":"0x90a78d73294bc9c7519a64e1912161be0e823eb472012ff54204e15a4d717fa5"}%
|
||||
```
|
||||
|
||||
### Get the node's finalized checkpoint
|
||||
|
||||
```
|
||||
curl localhost:5052/beacon/latest_finalized_checkpoint
|
||||
|
||||
{"epoch":0,"root":"0x0000000000000000000000000000000000000000000000000000000000000000"}%
|
||||
```
|
||||
|
@ -3,84 +3,9 @@
|
||||
This guide is intended for other Ethereum 2.0 client developers performing
|
||||
inter-operability testing with Lighthouse.
|
||||
|
||||
To allow for faster iteration cycles without the "merging to master" overhead,
|
||||
we will use the [`interop`](https://github.com/sigp/lighthouse/tree/interop)
|
||||
branch of [sigp/lighthouse](https://github.com/sigp/lighthouse/tree/interop)
|
||||
for September 2019 interop. **Please use ensure you `git checkout interop`
|
||||
after cloning the repo.**
|
||||
## Chapters
|
||||
|
||||
## Environment
|
||||
|
||||
All that is required for inter-op is a built and tested [development
|
||||
environment](setup). When lighthouse boots, it will create the following
|
||||
directories:
|
||||
|
||||
- `~/.lighthouse`: database and configuration for the beacon node.
|
||||
- `~/.lighthouse-validator`: database and configuration for the validator
|
||||
client.
|
||||
|
||||
After building the binaries with `cargo build --release --all`, there will be a
|
||||
`target/release` directory in the root of the Lighthouse repository. This is
|
||||
where the `beacon_node` and `validator_client` binaries are located.
|
||||
|
||||
## Interop Procedure
|
||||
|
||||
The following scenarios are documented:
|
||||
|
||||
- [Starting a "quick-start" beacon node](#quick-start-beacon-node) from a
|
||||
`(validator_count, genesis)` tuple.
|
||||
- [Starting a validator client](#validator-client) with `n` interop keypairs.
|
||||
- [Starting a node from a genesis state file](#starting-from-a-genesis-file).
|
||||
- [Exporting a genesis state file](#exporting-a-genesis-file) from a running Lighthouse
|
||||
node.
|
||||
|
||||
First, setup a Lighthouse development environment and navigate to the
|
||||
`target/release` directory (this is where the binaries are located).
|
||||
|
||||
#### Quick-start Beacon Node
|
||||
|
||||
|
||||
To start the node (each time creating a fresh database and configuration in
|
||||
`~/.lighthouse`), use:
|
||||
|
||||
```
|
||||
$ ./beacon_node testnet -f quick 8 1567222226
|
||||
```
|
||||
> Notes:
|
||||
>
|
||||
> - This method conforms the ["Quick-start
|
||||
genesis"](https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#quick-start-genesis)
|
||||
method in the `ethereum/eth2.0-pm` repository.
|
||||
> - The `-f` flag ignores any existing database or configuration, backing them
|
||||
> up before re-initializing.
|
||||
> - `8` is the validator count and `1567222226` is the genesis time.
|
||||
> - See `$ ./beacon_node testnet quick --help` for more configuration options.
|
||||
|
||||
#### Validator Client
|
||||
|
||||
Start the validator client with:
|
||||
|
||||
```
|
||||
$ ./validator_client testnet -b insecure 0 8
|
||||
```
|
||||
> Notes:
|
||||
>
|
||||
> - The `-b` flag means the validator client will "bootstrap" specs and config
|
||||
> from the beacon node.
|
||||
> - The `insecure` command dictates that the [interop
|
||||
> keypairs](https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#pubkeyprivkey-generation)
|
||||
> will be used.
|
||||
> - The `0 8` indicates that this validator client should manage 8 validators,
|
||||
> starting at validator 0 (the first deposited validator).
|
||||
> - The validator client will try to connect to the beacon node at `localhost`.
|
||||
> See `--help` to configure that address and other features.
|
||||
> - The validator client will operate very unsafely in `testnet` mode, happily
|
||||
> swapping between chains and creating double-votes.
|
||||
|
||||
#### Starting from a genesis file
|
||||
|
||||
**TODO**
|
||||
|
||||
#### Exporting a genesis file
|
||||
|
||||
**TODO**
|
||||
- Read about the required [development environment](./interop-environment.md).
|
||||
- Get an [overview](./interop-cli.md) of the Lighthouse CLI.
|
||||
- See how we expect to handle some [interop scenarios](./interop-scenarios.md).
|
||||
- See the [interop cheat-sheet](./interop-cheat-sheet.md) for useful CLI tips.
|
||||
|
@ -17,31 +17,11 @@ Foundation, Consensys and other individuals and organisations.
|
||||
|
||||
## Developer Resources
|
||||
|
||||
Documentation is provided for **researchers and developers** working on
|
||||
Ethereum 2.0 and assumes prior knowledge on the topic.
|
||||
Documentation is presently targeted at **researchers and developers**. It
|
||||
assumes significant prior knowledge of Ethereum 2.0.
|
||||
|
||||
- Get started with [development environment setup](setup.html).
|
||||
- [Run a simple testnet](simple-testnet.html) in Only Three CLI Commands™.
|
||||
- Read about our interop workflow.
|
||||
- API?
|
||||
Topics:
|
||||
|
||||
## Release
|
||||
|
||||
Ethereum 2.0 is not fully specified or implemented and as such, Lighthouse is
|
||||
still **under development**.
|
||||
|
||||
We are on-track to provide a public, multi-client testnet in late-2019 and an
|
||||
initial production-grade blockchain in 2020.
|
||||
|
||||
## Features
|
||||
|
||||
Lighthouse has been in development since mid-2018 and has an extensive feature
|
||||
set:
|
||||
|
||||
- Libp2p networking stack, featuring Discovery v5.
|
||||
- Optimized `BeaconChain` state machine, up-to-date and
|
||||
passing all tests.
|
||||
- RESTful HTTP API.
|
||||
- Documented and feature-rich CLI interface.
|
||||
- Capable of running small, local testnets with 250ms slot times.
|
||||
- Detailed metrics exposed in the Prometheus format.
|
||||
- Get started with [development environment setup](./setup.md).
|
||||
- See the [interop docs](./interop.md).
|
||||
- [Run a simple testnet](./simple-testnet.md) in Only Three CLI Commands™.
|
||||
|
@ -66,7 +66,7 @@ In a new terminal terminal, run:
|
||||
|
||||
|
||||
```
|
||||
$ ./beacon_node -b 10 testnet -r bootstrap http://localhost:5052
|
||||
$ ./beacon_node -b 10 testnet -r bootstrap
|
||||
```
|
||||
|
||||
> Notes:
|
||||
@ -74,7 +74,8 @@ $ ./beacon_node -b 10 testnet -r bootstrap http://localhost:5052
|
||||
> - The `-b` (or `--port-bump`) increases all the listening TCP/UDP ports of
|
||||
> the new node to `10` higher. Your first node's HTTP server was at TCP
|
||||
> `5052` but this one will be at `5062`.
|
||||
> - The `-r` flag creates a new data directory in your home with a random
|
||||
> string appended, to avoid conflicting with any other running node.
|
||||
> - The HTTP address is the API of the first node. The new node will download
|
||||
> configuration via HTTP before starting sync via libp2p.
|
||||
> - The `-r` flag creates a new data directory with a random string appended
|
||||
> (avoids data directory collisions between nodes).
|
||||
> - The default bootstrap HTTP address is `http://localhost:5052`. The new node
|
||||
> will download configuration via HTTP before starting sync via libp2p.
|
||||
> - See `$ ./beacon_node testnet bootstrap --help` for more configuration.
|
||||
|
@ -4,7 +4,8 @@
|
||||
extern crate lazy_static;
|
||||
|
||||
use beacon_chain::test_utils::{
|
||||
AttestationStrategy, BeaconChainHarness as BaseBeaconChainHarness, BlockStrategy,
|
||||
generate_deterministic_keypairs, AttestationStrategy,
|
||||
BeaconChainHarness as BaseBeaconChainHarness, BlockStrategy,
|
||||
};
|
||||
use lmd_ghost::{LmdGhost, ThreadSafeReducedTree as BaseThreadSafeReducedTree};
|
||||
use rand::{prelude::*, rngs::StdRng};
|
||||
@ -51,7 +52,7 @@ struct ForkedHarness {
|
||||
impl ForkedHarness {
|
||||
/// A new standard instance of with constant parameters.
|
||||
pub fn new() -> Self {
|
||||
let harness = BeaconChainHarness::new(VALIDATOR_COUNT);
|
||||
let harness = BeaconChainHarness::new(generate_deterministic_keypairs(VALIDATOR_COUNT));
|
||||
|
||||
// Move past the zero slot.
|
||||
harness.advance_slot();
|
||||
|
@ -31,3 +31,4 @@ tree_hash_derive = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.6.0"
|
||||
serde_json = "^1.0"
|
||||
|
@ -120,6 +120,13 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
fn epochs_per_historical_vector() -> usize {
|
||||
Self::EpochsPerHistoricalVector::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `SLOTS_PER_ETH1_VOTING_PERIOD` constant for this specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn slots_per_eth1_voting_period() -> usize {
|
||||
Self::EpochsPerHistoricalVector::to_usize()
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to inherit some type values from another EthSpec.
|
||||
|
@ -9,7 +9,7 @@ fn default_values() {
|
||||
let cache = CommitteeCache::default();
|
||||
|
||||
assert_eq!(cache.is_initialized_at(Epoch::new(0)), false);
|
||||
assert_eq!(cache.active_validator_indices(), &[]);
|
||||
assert!(&cache.active_validator_indices().is_empty());
|
||||
assert_eq!(cache.get_crosslink_committee_for_shard(0), None);
|
||||
assert_eq!(cache.get_attestation_duties(0), None);
|
||||
assert_eq!(cache.active_validator_count(), 0);
|
||||
|
@ -39,15 +39,15 @@ impl TestingProposerSlashingBuilder {
|
||||
..header_1.clone()
|
||||
};
|
||||
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
|
||||
header_1.signature = {
|
||||
let message = header_1.signed_root();
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
|
||||
header_2.signature = {
|
||||
let message = header_2.signed_root();
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
|
||||
|
@ -13,3 +13,4 @@ reqwest = "0.9"
|
||||
url = "1.2"
|
||||
types = { path = "../../types" }
|
||||
serde = "1.0"
|
||||
slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_trace"] }
|
||||
|
@ -5,11 +5,16 @@ use eth2_libp2p::{
|
||||
};
|
||||
use reqwest::{Error as HttpError, Url};
|
||||
use serde::Deserialize;
|
||||
use slog::{error, Logger};
|
||||
use std::borrow::Cow;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::time::Duration;
|
||||
use types::{BeaconBlock, BeaconState, Checkpoint, EthSpec, Hash256, Slot};
|
||||
use url::Host;
|
||||
|
||||
pub const RETRY_SLEEP_MILLIS: u64 = 100;
|
||||
pub const RETRY_WARN_INTERVAL: u64 = 30;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
InvalidUrl,
|
||||
@ -31,11 +36,35 @@ pub struct Bootstrapper {
|
||||
}
|
||||
|
||||
impl Bootstrapper {
|
||||
/// Parses the given `server` as a URL, instantiating `Self`.
|
||||
pub fn from_server_string(server: String) -> Result<Self, String> {
|
||||
Ok(Self {
|
||||
/// Parses the given `server` as a URL, instantiating `Self` and blocking until a connection
|
||||
/// can be made with the server.
|
||||
///
|
||||
/// Never times out.
|
||||
pub fn connect(server: String, log: &Logger) -> Result<Self, String> {
|
||||
let bootstrapper = Self {
|
||||
url: Url::parse(&server).map_err(|e| format!("Invalid bootstrap server url: {}", e))?,
|
||||
})
|
||||
};
|
||||
|
||||
let mut retry_count = 0;
|
||||
loop {
|
||||
match bootstrapper.enr() {
|
||||
Ok(_) => break,
|
||||
Err(_) => {
|
||||
if retry_count % RETRY_WARN_INTERVAL == 0 {
|
||||
error!(
|
||||
log,
|
||||
"Failed to contact bootstrap server";
|
||||
"retry_count" => retry_count,
|
||||
"retry_delay_millis" => RETRY_SLEEP_MILLIS,
|
||||
);
|
||||
}
|
||||
retry_count += 1;
|
||||
std::thread::sleep(Duration::from_millis(RETRY_SLEEP_MILLIS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bootstrapper)
|
||||
}
|
||||
|
||||
/// Build a multiaddr using the HTTP server URL that is not guaranteed to be correct.
|
||||
|
@ -247,10 +247,13 @@ fn process_testnet_subcommand(
|
||||
) -> Result<(ClientConfig, Eth2Config)> {
|
||||
let eth2_config = if cli_args.is_present("bootstrap") {
|
||||
info!(log, "Connecting to bootstrap server");
|
||||
let bootstrapper = Bootstrapper::from_server_string(format!(
|
||||
"http://{}:{}",
|
||||
client_config.server, client_config.server_http_port
|
||||
))?;
|
||||
let bootstrapper = Bootstrapper::connect(
|
||||
format!(
|
||||
"http://{}:{}",
|
||||
client_config.server, client_config.server_http_port
|
||||
),
|
||||
&log,
|
||||
)?;
|
||||
|
||||
let eth2_config = bootstrapper.eth2_config()?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user