Merge latest master in v0.2.0
This commit is contained in:
@@ -140,6 +140,8 @@ pub struct HeadInfo {
|
||||
pub current_justified_checkpoint: types::Checkpoint,
|
||||
pub finalized_checkpoint: types::Checkpoint,
|
||||
pub fork: Fork,
|
||||
pub genesis_time: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub trait BeaconChainTypes: Send + Sync + 'static {
|
||||
@@ -176,6 +178,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
pub(crate) canonical_head: TimeoutRwLock<BeaconSnapshot<T::EthSpec>>,
|
||||
/// The root of the genesis block.
|
||||
pub genesis_block_root: Hash256,
|
||||
/// The root of the list of genesis validators, used during syncing.
|
||||
pub genesis_validators_root: Hash256,
|
||||
/// A state-machine that is updated with information from the network and chooses a canonical
|
||||
/// head block.
|
||||
pub fork_choice: ForkChoice<T>,
|
||||
@@ -468,6 +472,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
current_justified_checkpoint: head.beacon_state.current_justified_checkpoint.clone(),
|
||||
finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(),
|
||||
fork: head.beacon_state.fork.clone(),
|
||||
genesis_time: head.beacon_state.genesis_time,
|
||||
genesis_validators_root: head.beacon_state.genesis_validators_root,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -814,7 +820,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
root: target_root,
|
||||
},
|
||||
},
|
||||
signature: AggregateSignature::new(),
|
||||
signature: AggregateSignature::empty_signature(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1084,11 +1090,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.try_read_for(VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT)
|
||||
.ok_or_else(|| Error::ValidatorPubkeyCacheLockTimeout)?;
|
||||
|
||||
let fork = self
|
||||
let (fork, genesis_validators_root) = self
|
||||
.canonical_head
|
||||
.try_read_for(HEAD_LOCK_TIMEOUT)
|
||||
.ok_or_else(|| Error::CanonicalHeadLockTimeout)
|
||||
.map(|head| head.beacon_state.fork.clone())?;
|
||||
.map(|head| {
|
||||
(
|
||||
head.beacon_state.fork.clone(),
|
||||
head.beacon_state.genesis_validators_root,
|
||||
)
|
||||
})?;
|
||||
|
||||
let signature_set = indexed_attestation_signature_set_from_pubkeys(
|
||||
|validator_index| {
|
||||
@@ -1099,6 +1110,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
&attestation.signature,
|
||||
&indexed_attestation,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
&self.spec,
|
||||
)
|
||||
.map_err(Error::SignatureSetError)?;
|
||||
@@ -1175,10 +1187,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let index = attestation.data.index;
|
||||
let slot = attestation.data.slot;
|
||||
|
||||
match self
|
||||
.op_pool
|
||||
.insert_attestation(attestation, &fork, &self.spec)
|
||||
{
|
||||
match self.op_pool.insert_attestation(
|
||||
attestation,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
&self.spec,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@@ -1674,6 +1688,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let mut block = SignedBeaconBlock {
|
||||
message: BeaconBlock {
|
||||
slot: state.slot,
|
||||
proposer_index: state.get_beacon_proposer_index(state.slot, &self.spec)? as u64,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBody {
|
||||
@@ -2014,7 +2029,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// If we are unable to read the slot clock we assume that it is prior to genesis and
|
||||
// therefore use the genesis slot.
|
||||
let slot = self.slot().unwrap_or_else(|_| self.spec.genesis_slot);
|
||||
self.spec.enr_fork_id(slot)
|
||||
|
||||
self.spec.enr_fork_id(slot, self.genesis_validators_root)
|
||||
}
|
||||
|
||||
/// Calculates the `Duration` to the next fork, if one exists.
|
||||
|
||||
@@ -187,6 +187,19 @@ where
|
||||
.map_err(|e| format!("DB error whilst reading eth1 cache: {:?}", e))
|
||||
}
|
||||
|
||||
/// Returns true if `self.store` contains a persisted beacon chain.
|
||||
pub fn store_contains_beacon_chain(&self) -> Result<bool, String> {
|
||||
let store = self
|
||||
.store
|
||||
.clone()
|
||||
.ok_or_else(|| "load_from_store requires a store.".to_string())?;
|
||||
|
||||
Ok(store
|
||||
.get::<PersistedBeaconChain>(&Hash256::from_slice(&BEACON_CHAIN_DB_KEY))
|
||||
.map_err(|e| format!("DB error when reading persisted beacon chain: {:?}", e))?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
/// Attempt to load an existing chain from the builder's `Store`.
|
||||
///
|
||||
/// May initialize several components; including the op_pool and finalized checkpoints.
|
||||
@@ -418,6 +431,7 @@ where
|
||||
// TODO: allow for persisting and loading the pool from disk.
|
||||
naive_aggregation_pool: <_>::default(),
|
||||
eth1_chain: self.eth1_chain,
|
||||
genesis_validators_root: canonical_head.beacon_state.genesis_validators_root,
|
||||
canonical_head: TimeoutRwLock::new(canonical_head.clone()),
|
||||
genesis_block_root: self
|
||||
.genesis_block_root
|
||||
|
||||
@@ -274,11 +274,12 @@ mod tests {
|
||||
a
|
||||
}
|
||||
|
||||
fn sign(a: &mut Attestation<E>, i: usize) {
|
||||
fn sign(a: &mut Attestation<E>, i: usize, genesis_validators_root: Hash256) {
|
||||
a.sign(
|
||||
&generate_deterministic_keypair(i).sk,
|
||||
i,
|
||||
&Fork::default(),
|
||||
genesis_validators_root,
|
||||
&E::default_spec(),
|
||||
)
|
||||
.expect("should sign attestation");
|
||||
@@ -302,7 +303,7 @@ mod tests {
|
||||
"should not accept attestation without any signatures"
|
||||
);
|
||||
|
||||
sign(&mut a, 0);
|
||||
sign(&mut a, 0, Hash256::random());
|
||||
|
||||
assert_eq!(
|
||||
pool.insert(&a),
|
||||
@@ -324,7 +325,7 @@ mod tests {
|
||||
"retrieved attestation should equal the one inserted"
|
||||
);
|
||||
|
||||
sign(&mut a, 1);
|
||||
sign(&mut a, 1, Hash256::random());
|
||||
|
||||
assert_eq!(
|
||||
pool.insert(&a),
|
||||
@@ -338,8 +339,9 @@ mod tests {
|
||||
let mut a_0 = get_attestation(Slot::new(0));
|
||||
let mut a_1 = a_0.clone();
|
||||
|
||||
sign(&mut a_0, 0);
|
||||
sign(&mut a_1, 1);
|
||||
let genesis_validators_root = Hash256::random();
|
||||
sign(&mut a_0, 0, genesis_validators_root);
|
||||
sign(&mut a_1, 1, genesis_validators_root);
|
||||
|
||||
let pool = NaiveAggregationPool::default();
|
||||
|
||||
@@ -374,7 +376,7 @@ mod tests {
|
||||
let mut a_different = a_0.clone();
|
||||
let different_root = Hash256::from_low_u64_be(1337);
|
||||
unset_bit(&mut a_different, 0);
|
||||
sign(&mut a_different, 2);
|
||||
sign(&mut a_different, 2, genesis_validators_root);
|
||||
assert!(a_different.data.beacon_block_root != different_root);
|
||||
a_different.data.beacon_block_root = different_root;
|
||||
|
||||
@@ -396,7 +398,7 @@ mod tests {
|
||||
#[test]
|
||||
fn auto_pruning() {
|
||||
let mut base = get_attestation(Slot::new(0));
|
||||
sign(&mut base, 0);
|
||||
sign(&mut base, 0, Hash256::random());
|
||||
|
||||
let pool = NaiveAggregationPool::default();
|
||||
|
||||
@@ -450,7 +452,7 @@ mod tests {
|
||||
#[test]
|
||||
fn max_attestations() {
|
||||
let mut base = get_attestation(Slot::new(0));
|
||||
sign(&mut base, 0);
|
||||
sign(&mut base, 0, Hash256::random());
|
||||
|
||||
let pool = NaiveAggregationPool::default();
|
||||
|
||||
|
||||
@@ -307,7 +307,9 @@ where
|
||||
|
||||
let randao_reveal = {
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let domain = self.spec.get_domain(epoch, Domain::Randao, fork);
|
||||
let domain =
|
||||
self.spec
|
||||
.get_domain(epoch, Domain::Randao, fork, state.genesis_validators_root);
|
||||
let message = epoch.signing_root(domain);
|
||||
Signature::new(message.as_bytes(), sk)
|
||||
};
|
||||
@@ -317,7 +319,7 @@ where
|
||||
.produce_block_on_state(state, slot, randao_reveal)
|
||||
.expect("should produce block");
|
||||
|
||||
let signed_block = block.sign(sk, &state.fork, &self.spec);
|
||||
let signed_block = block.sign(sk, &state.fork, state.genesis_validators_root, &self.spec);
|
||||
|
||||
(signed_block, state)
|
||||
}
|
||||
@@ -402,6 +404,7 @@ where
|
||||
attestation.data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = attestation.data.signing_root(domain);
|
||||
|
||||
@@ -106,7 +106,7 @@ fn produces_attestations() {
|
||||
);
|
||||
assert_eq!(
|
||||
attestation.signature,
|
||||
AggregateSignature::new(),
|
||||
AggregateSignature::empty_signature(),
|
||||
"bad signature"
|
||||
);
|
||||
assert_eq!(data.index, index, "bad index");
|
||||
|
||||
@@ -89,12 +89,12 @@ fn update_proposal_signatures(
|
||||
.get(proposer_index)
|
||||
.expect("proposer keypair should be available");
|
||||
|
||||
snapshot.beacon_block =
|
||||
snapshot
|
||||
.beacon_block
|
||||
.message
|
||||
.clone()
|
||||
.sign(&keypair.sk, &state.fork, spec);
|
||||
snapshot.beacon_block = snapshot.beacon_block.message.clone().sign(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +352,6 @@ fn invalid_signatures() {
|
||||
*/
|
||||
let mut snapshots = CHAIN_SEGMENT.clone();
|
||||
let proposer_slashing = ProposerSlashing {
|
||||
proposer_index: 0,
|
||||
signed_header_1: SignedBeaconBlockHeader {
|
||||
message: snapshots[block_index].beacon_block.message.block_header(),
|
||||
signature: junk_signature(),
|
||||
|
||||
@@ -153,6 +153,26 @@ where
|
||||
Ok((builder, spec, context))
|
||||
})
|
||||
.and_then(move |(builder, spec, context)| {
|
||||
let chain_exists = builder
|
||||
.store_contains_beacon_chain()
|
||||
.unwrap_or_else(|_| false);
|
||||
|
||||
// If the client is expect to resume but there's no beacon chain in the database,
|
||||
// use the `DepositContract` method. This scenario is quite common when the client
|
||||
// is shutdown before finding genesis via eth1.
|
||||
//
|
||||
// Alternatively, if there's a beacon chain in the database then always resume
|
||||
// using it.
|
||||
let client_genesis = if client_genesis == ClientGenesis::Resume && !chain_exists {
|
||||
info!(context.log, "Defaulting to deposit contract genesis");
|
||||
|
||||
ClientGenesis::DepositContract
|
||||
} else if chain_exists {
|
||||
ClientGenesis::Resume
|
||||
} else {
|
||||
client_genesis
|
||||
};
|
||||
|
||||
let genesis_state_future: Box<dyn Future<Item = _, Error = _> + Send> =
|
||||
match client_genesis {
|
||||
ClientGenesis::Interop {
|
||||
|
||||
@@ -12,7 +12,7 @@ const TESTNET_SPEC_CONSTANTS: &str = "minimal";
|
||||
const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db";
|
||||
|
||||
/// Defines how the client should initialize the `BeaconChain` and other components.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ClientGenesis {
|
||||
/// Reads the genesis state and other persisted data from the `Store`.
|
||||
Resume,
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
use crate::router::processor::FUTURE_SLOT_TOLERANCE;
|
||||
use crate::sync::manager::SyncMessage;
|
||||
use crate::sync::range_sync::BatchId;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockError};
|
||||
use eth2_libp2p::PeerId;
|
||||
use slog::{crit, debug, error, trace, warn};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::sync::mpsc;
|
||||
use types::SignedBeaconBlock;
|
||||
|
||||
/// Id associated to a block processing request, either a batch or a single block.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ProcessId {
|
||||
/// Processing Id of a range syncing batch.
|
||||
RangeBatchId(BatchId),
|
||||
/// Processing Id of the parent lookup of a block
|
||||
ParentLookup(PeerId),
|
||||
}
|
||||
|
||||
/// The result of a block processing request.
|
||||
// TODO: When correct batch error handling occurs, we will include an error type.
|
||||
#[derive(Debug)]
|
||||
pub enum BatchProcessResult {
|
||||
/// The batch was completed successfully.
|
||||
Success,
|
||||
/// The batch processing failed.
|
||||
Failed,
|
||||
}
|
||||
|
||||
/// Spawns a thread handling the block processing of a request: range syncing or parent lookup.
|
||||
pub fn spawn_block_processor<T: BeaconChainTypes>(
|
||||
chain: Weak<BeaconChain<T>>,
|
||||
process_id: ProcessId,
|
||||
downloaded_blocks: Vec<SignedBeaconBlock<T::EthSpec>>,
|
||||
mut sync_send: mpsc::UnboundedSender<SyncMessage<T::EthSpec>>,
|
||||
log: slog::Logger,
|
||||
) {
|
||||
std::thread::spawn(move || {
|
||||
match process_id {
|
||||
// this a request from the range sync
|
||||
ProcessId::RangeBatchId(batch_id) => {
|
||||
debug!(log, "Processing batch"; "id" => *batch_id, "blocks" => downloaded_blocks.len());
|
||||
let result = match process_blocks(chain, downloaded_blocks.iter(), &log) {
|
||||
Ok(_) => {
|
||||
debug!(log, "Batch processed"; "id" => *batch_id );
|
||||
BatchProcessResult::Success
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(log, "Batch processing failed"; "id" => *batch_id, "error" => e);
|
||||
BatchProcessResult::Failed
|
||||
}
|
||||
};
|
||||
|
||||
let msg = SyncMessage::BatchProcessed {
|
||||
batch_id: batch_id,
|
||||
downloaded_blocks: downloaded_blocks,
|
||||
result,
|
||||
};
|
||||
sync_send.try_send(msg).unwrap_or_else(|_| {
|
||||
debug!(
|
||||
log,
|
||||
"Block processor could not inform range sync result. Likely shutting down."
|
||||
);
|
||||
});
|
||||
}
|
||||
// this a parent lookup request from the sync manager
|
||||
ProcessId::ParentLookup(peer_id) => {
|
||||
debug!(log, "Processing parent lookup"; "last_peer_id" => format!("{}", peer_id), "blocks" => downloaded_blocks.len());
|
||||
// parent blocks are ordered from highest slot to lowest, so we need to process in
|
||||
// reverse
|
||||
match process_blocks(chain, downloaded_blocks.iter().rev(), &log) {
|
||||
Err(e) => {
|
||||
warn!(log, "Parent lookup failed"; "last_peer_id" => format!("{}", peer_id), "error" => e);
|
||||
sync_send
|
||||
.try_send(SyncMessage::ParentLookupFailed(peer_id))
|
||||
.unwrap_or_else(|_| {
|
||||
// on failure, inform to downvote the peer
|
||||
debug!(
|
||||
log,
|
||||
"Block processor could not inform parent lookup result. Likely shutting down."
|
||||
);
|
||||
});
|
||||
}
|
||||
Ok(_) => {
|
||||
debug!(log, "Parent lookup processed successfully");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Helper function to process blocks batches which only consumes the chain and blocks to process.
|
||||
// TODO: Verify the fork choice logic and the correct error handling from `process_chain_segment`.
|
||||
// Ensure fork-choice doesn't need to be run during the failed errors.
|
||||
fn process_blocks<
|
||||
'a,
|
||||
T: BeaconChainTypes,
|
||||
I: Iterator<Item = &'a SignedBeaconBlock<T::EthSpec>>,
|
||||
>(
|
||||
chain: Weak<BeaconChain<T>>,
|
||||
downloaded_blocks: I,
|
||||
log: &slog::Logger,
|
||||
) -> Result<(), String> {
|
||||
if let Some(chain) = chain.upgrade() {
|
||||
let blocks = downloaded_blocks.cloned().collect::<Vec<_>>();
|
||||
match chain.process_chain_segment(blocks) {
|
||||
Ok(roots) => {
|
||||
if roots.is_empty() {
|
||||
debug!(log, "All blocks already known");
|
||||
} else {
|
||||
debug!(
|
||||
log, "Imported blocks from network";
|
||||
"count" => roots.len(),
|
||||
);
|
||||
// Batch completed successfully with at least one block, run fork choice.
|
||||
// TODO: Verify this logic
|
||||
run_fork_choice(chain, log);
|
||||
}
|
||||
}
|
||||
Err(BlockError::ParentUnknown(parent)) => {
|
||||
// blocks should be sequential and all parents should exist
|
||||
warn!(
|
||||
log, "Parent block is unknown";
|
||||
"parent_root" => format!("{}", parent),
|
||||
);
|
||||
return Err(format!("Block has an unknown parent: {}", parent));
|
||||
}
|
||||
Err(BlockError::BlockIsAlreadyKnown) => {
|
||||
// TODO: Check handling of this
|
||||
crit!(log, "Unknown handling of block error");
|
||||
}
|
||||
Err(BlockError::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
}) => {
|
||||
if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot {
|
||||
// The block is too far in the future, drop it.
|
||||
warn!(
|
||||
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,
|
||||
);
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
debug!(
|
||||
log, "Block is slightly ahead of our slot clock, ignoring.";
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
);
|
||||
}
|
||||
return Err(format!(
|
||||
"Block with slot {} is higher than the current slot {}",
|
||||
block_slot, present_slot
|
||||
));
|
||||
}
|
||||
Err(BlockError::WouldRevertFinalizedSlot { .. }) => {
|
||||
//TODO: Check handling. Run fork choice?
|
||||
debug!(
|
||||
log, "Finalized or earlier block processed";
|
||||
);
|
||||
// block reached our finalized slot or was earlier, move to the next block
|
||||
// TODO: How does this logic happen for the chain segment. We would want to
|
||||
// continue processing in this case.
|
||||
}
|
||||
Err(BlockError::GenesisBlock) => {
|
||||
debug!(
|
||||
log, "Genesis block was processed";
|
||||
);
|
||||
// TODO: Similarly here. Prefer to continue processing.
|
||||
}
|
||||
Err(BlockError::BeaconChainError(e)) => {
|
||||
// TODO: Run fork choice?
|
||||
warn!(
|
||||
log, "BlockProcessingFailure";
|
||||
"msg" => "unexpected condition in processing block.",
|
||||
"outcome" => format!("{:?}", e)
|
||||
);
|
||||
return Err(format!("Internal error whilst processing block: {:?}", e));
|
||||
}
|
||||
other => {
|
||||
// TODO: Run fork choice?
|
||||
warn!(
|
||||
log, "Invalid block received";
|
||||
"msg" => "peer sent invalid block",
|
||||
"outcome" => format!("{:?}", other),
|
||||
);
|
||||
return Err(format!("Peer sent invalid block. Reason: {:?}", other));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs fork-choice on a given chain. This is used during block processing after one successful
|
||||
/// block import.
|
||||
fn run_fork_choice<T: BeaconChainTypes>(chain: Arc<BeaconChain<T>>, log: &slog::Logger) {
|
||||
match chain.fork_choice() {
|
||||
Ok(()) => trace!(
|
||||
log,
|
||||
"Fork choice success";
|
||||
"location" => "batch processing"
|
||||
),
|
||||
Err(e) => error!(
|
||||
log,
|
||||
"Fork choice failed";
|
||||
"error" => format!("{:?}", e),
|
||||
"location" => "batch import error"
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,9 @@
|
||||
//! if an attestation references an unknown block) this manager can search for the block and
|
||||
//! subsequently search for parents if needed.
|
||||
|
||||
use super::block_processor::{spawn_block_processor, BatchProcessResult, ProcessId};
|
||||
use super::network_context::SyncNetworkContext;
|
||||
use super::range_sync::{Batch, BatchProcessResult, RangeSync};
|
||||
use super::range_sync::{BatchId, RangeSync};
|
||||
use crate::router::processor::PeerSyncInfo;
|
||||
use crate::service::NetworkMessage;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
@@ -99,10 +100,13 @@ pub enum SyncMessage<T: EthSpec> {
|
||||
|
||||
/// A batch has been processed by the block processor thread.
|
||||
BatchProcessed {
|
||||
process_id: u64,
|
||||
batch: Box<Batch<T>>,
|
||||
batch_id: BatchId,
|
||||
downloaded_blocks: Vec<SignedBeaconBlock<T>>,
|
||||
result: BatchProcessResult,
|
||||
},
|
||||
|
||||
/// A parent lookup has failed for a block given by this `peer_id`.
|
||||
ParentLookupFailed(PeerId),
|
||||
}
|
||||
|
||||
/// Maintains a sequential list of parents to lookup and the lookup's current state.
|
||||
@@ -172,6 +176,9 @@ pub struct SyncManager<T: BeaconChainTypes> {
|
||||
|
||||
/// The logger for the import manager.
|
||||
log: Logger,
|
||||
|
||||
/// The sending part of input_channel
|
||||
sync_send: mpsc::UnboundedSender<SyncMessage<T::EthSpec>>,
|
||||
}
|
||||
|
||||
/// Spawns a new `SyncManager` thread which has a weak reference to underlying beacon
|
||||
@@ -202,6 +209,7 @@ pub fn spawn<T: BeaconChainTypes>(
|
||||
single_block_lookups: FnvHashMap::default(),
|
||||
full_peers: HashSet::new(),
|
||||
log: log.clone(),
|
||||
sync_send: sync_send.clone(),
|
||||
};
|
||||
|
||||
// spawn the sync manager thread
|
||||
@@ -590,8 +598,6 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
// If the last block in the queue has an unknown parent, we continue the parent
|
||||
// lookup-search.
|
||||
|
||||
let total_blocks_to_process = parent_request.downloaded_blocks.len();
|
||||
|
||||
if let Some(chain) = self.chain.upgrade() {
|
||||
let newest_block = parent_request
|
||||
.downloaded_blocks
|
||||
@@ -606,7 +612,15 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
return;
|
||||
}
|
||||
Ok(BlockProcessingOutcome::Processed { .. })
|
||||
| Ok(BlockProcessingOutcome::BlockIsAlreadyKnown { .. }) => {}
|
||||
| Ok(BlockProcessingOutcome::BlockIsAlreadyKnown { .. }) => {
|
||||
spawn_block_processor(
|
||||
self.chain.clone(),
|
||||
ProcessId::ParentLookup(parent_request.last_submitted_peer.clone()),
|
||||
parent_request.downloaded_blocks,
|
||||
self.sync_send.clone(),
|
||||
self.log.clone(),
|
||||
);
|
||||
}
|
||||
Ok(outcome) => {
|
||||
// all else we consider the chain a failure and downvote the peer that sent
|
||||
// us the last block
|
||||
@@ -634,64 +648,6 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
// chain doesn't exist, drop the parent queue and return
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: Shift this to a block processing thread
|
||||
|
||||
// the last received block has been successfully processed, process all other blocks in the
|
||||
// chain
|
||||
while let Some(block) = parent_request.downloaded_blocks.pop() {
|
||||
// check if the chain exists
|
||||
if let Some(chain) = self.chain.upgrade() {
|
||||
match BlockProcessingOutcome::shim(chain.process_block(block)) {
|
||||
Ok(BlockProcessingOutcome::Processed { .. })
|
||||
| Ok(BlockProcessingOutcome::BlockIsAlreadyKnown { .. }) => {} // continue to the next block
|
||||
|
||||
// all else is considered a failure
|
||||
Ok(outcome) => {
|
||||
// the previous blocks have failed, notify the user the chain lookup has
|
||||
// failed and drop the parent queue
|
||||
debug!(
|
||||
self.log, "Invalid parent chain. Past blocks failure";
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
"peer" => format!("{:?}", parent_request.last_submitted_peer),
|
||||
);
|
||||
self.network
|
||||
.downvote_peer(parent_request.last_submitted_peer.clone());
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
self.log, "Parent chain processing error.";
|
||||
"error" => format!("{:?}", e)
|
||||
);
|
||||
self.network
|
||||
.downvote_peer(parent_request.last_submitted_peer.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// chain doesn't exist, end the processing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// at least one block has been processed, run fork-choice
|
||||
if let Some(chain) = self.chain.upgrade() {
|
||||
match chain.fork_choice() {
|
||||
Ok(()) => trace!(
|
||||
self.log,
|
||||
"Fork choice success";
|
||||
"block_imports" => total_blocks_to_process - parent_request.downloaded_blocks.len(),
|
||||
"location" => "parent request"
|
||||
),
|
||||
Err(e) => error!(
|
||||
self.log,
|
||||
"Fork choice failed";
|
||||
"error" => format!("{:?}", e),
|
||||
"location" => "parent request"
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,17 +738,20 @@ impl<T: BeaconChainTypes> Future for SyncManager<T> {
|
||||
self.inject_error(peer_id, request_id);
|
||||
}
|
||||
SyncMessage::BatchProcessed {
|
||||
process_id,
|
||||
batch,
|
||||
batch_id,
|
||||
downloaded_blocks,
|
||||
result,
|
||||
} => {
|
||||
self.range_sync.handle_block_process_result(
|
||||
&mut self.network,
|
||||
process_id,
|
||||
*batch,
|
||||
batch_id,
|
||||
downloaded_blocks,
|
||||
result,
|
||||
);
|
||||
}
|
||||
SyncMessage::ParentLookupFailed(peer_id) => {
|
||||
self.network.downvote_peer(peer_id);
|
||||
}
|
||||
},
|
||||
Ok(Async::NotReady) => break,
|
||||
Ok(Async::Ready(None)) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Syncing for lighthouse.
|
||||
//!
|
||||
//! Stores the various syncing methods for the beacon chain.
|
||||
mod block_processor;
|
||||
pub mod manager;
|
||||
mod network_context;
|
||||
mod range_sync;
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
use super::batch::Batch;
|
||||
use crate::router::processor::FUTURE_SLOT_TOLERANCE;
|
||||
use crate::sync::manager::SyncMessage;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockError};
|
||||
use slog::{debug, error, trace, warn};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
/// The result of attempting to process a batch of blocks.
|
||||
// TODO: When correct batch error handling occurs, we will include an error type.
|
||||
#[derive(Debug)]
|
||||
pub enum BatchProcessResult {
|
||||
/// The batch was completed successfully.
|
||||
Success,
|
||||
/// The batch processing failed.
|
||||
Failed,
|
||||
}
|
||||
|
||||
// TODO: Refactor to async fn, with stable futures
|
||||
pub fn spawn_batch_processor<T: BeaconChainTypes>(
|
||||
chain: Weak<BeaconChain<T>>,
|
||||
process_id: u64,
|
||||
batch: Batch<T::EthSpec>,
|
||||
mut sync_send: mpsc::UnboundedSender<SyncMessage<T::EthSpec>>,
|
||||
log: slog::Logger,
|
||||
) {
|
||||
std::thread::spawn(move || {
|
||||
debug!(log, "Processing batch"; "id" => *batch.id);
|
||||
let result = match process_batch(chain, &batch, &log) {
|
||||
Ok(_) => BatchProcessResult::Success,
|
||||
Err(_) => BatchProcessResult::Failed,
|
||||
};
|
||||
|
||||
debug!(log, "Batch processed"; "id" => *batch.id, "result" => format!("{:?}", result));
|
||||
|
||||
sync_send
|
||||
.try_send(SyncMessage::BatchProcessed {
|
||||
process_id,
|
||||
batch: Box::new(batch),
|
||||
result,
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
debug!(
|
||||
log,
|
||||
"Batch result could not inform sync. Likely shutting down."
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to process block batches which only consumes the chain and blocks to process
|
||||
fn process_batch<T: BeaconChainTypes>(
|
||||
chain: Weak<BeaconChain<T>>,
|
||||
batch: &Batch<T::EthSpec>,
|
||||
log: &slog::Logger,
|
||||
) -> Result<(), String> {
|
||||
if let Some(chain) = chain.upgrade() {
|
||||
match chain.process_chain_segment(batch.downloaded_blocks.clone()) {
|
||||
Ok(roots) => {
|
||||
trace!(
|
||||
log, "Imported blocks from network";
|
||||
"count" => roots.len(),
|
||||
);
|
||||
}
|
||||
Err(BlockError::ParentUnknown(parent)) => {
|
||||
// blocks should be sequential and all parents should exist
|
||||
warn!(
|
||||
log, "Parent block is unknown";
|
||||
"parent_root" => format!("{}", parent),
|
||||
);
|
||||
|
||||
return Err(format!("Block has an unknown parent: {}", parent));
|
||||
}
|
||||
Err(BlockError::BlockIsAlreadyKnown) => {
|
||||
// this block is already known to us, move to the next
|
||||
debug!(
|
||||
log, "Imported a block that is already known";
|
||||
);
|
||||
}
|
||||
Err(BlockError::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
}) => {
|
||||
if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot {
|
||||
// The block is too far in the future, drop it.
|
||||
warn!(
|
||||
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,
|
||||
);
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
debug!(
|
||||
log, "Block is slightly ahead of our slot clock, ignoring.";
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
);
|
||||
}
|
||||
|
||||
return Err(format!(
|
||||
"Block with slot {} is higher than the current slot {}",
|
||||
block_slot, present_slot
|
||||
));
|
||||
}
|
||||
Err(BlockError::WouldRevertFinalizedSlot { .. }) => {
|
||||
debug!(
|
||||
log, "Finalized or earlier block processed";
|
||||
);
|
||||
// block reached our finalized slot or was earlier, move to the next block
|
||||
}
|
||||
Err(BlockError::GenesisBlock) => {
|
||||
debug!(
|
||||
log, "Genesis block was processed";
|
||||
);
|
||||
}
|
||||
Err(BlockError::BeaconChainError(e)) => {
|
||||
warn!(
|
||||
log, "BlockProcessingFailure";
|
||||
"msg" => "unexpected condition in processing block.",
|
||||
"outcome" => format!("{:?}", e)
|
||||
);
|
||||
|
||||
return Err(format!("Internal error whilst processing block: {:?}", e));
|
||||
}
|
||||
other => {
|
||||
warn!(
|
||||
log, "Invalid block received";
|
||||
"msg" => "peer sent invalid block",
|
||||
"outcome" => format!("{:?}", other),
|
||||
);
|
||||
|
||||
return Err(format!("Peer sent invalid block. Reason: {:?}", other));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Ok(()); // terminate early due to dropped beacon chain
|
||||
}
|
||||
|
||||
// Batch completed successfully, run fork choice.
|
||||
if let Some(chain) = chain.upgrade() {
|
||||
run_fork_choice(chain, log);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs fork-choice on a given chain. This is used during block processing after one successful
|
||||
/// block import.
|
||||
fn run_fork_choice<T: BeaconChainTypes>(chain: Arc<BeaconChain<T>>, log: &slog::Logger) {
|
||||
match chain.fork_choice() {
|
||||
Ok(()) => trace!(
|
||||
log,
|
||||
"Fork choice success";
|
||||
"location" => "batch processing"
|
||||
),
|
||||
Err(e) => error!(
|
||||
log,
|
||||
"Fork choice failed";
|
||||
"error" => format!("{:?}", e),
|
||||
"location" => "batch import error"
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::batch::{Batch, BatchId, PendingBatches};
|
||||
use super::batch_processing::{spawn_batch_processor, BatchProcessResult};
|
||||
use crate::sync::block_processor::{spawn_block_processor, BatchProcessResult, ProcessId};
|
||||
use crate::sync::network_context::SyncNetworkContext;
|
||||
use crate::sync::SyncMessage;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
@@ -76,7 +76,7 @@ pub struct SyncingChain<T: BeaconChainTypes> {
|
||||
|
||||
/// A random id given to a batch process request. This is None if there is no ongoing batch
|
||||
/// process.
|
||||
current_processing_id: Option<u64>,
|
||||
current_processing_batch: Option<Batch<T::EthSpec>>,
|
||||
|
||||
/// A send channel to the sync manager. This is given to the batch processor thread to report
|
||||
/// back once batch processing has completed.
|
||||
@@ -120,7 +120,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
to_be_downloaded_id: BatchId(1),
|
||||
to_be_processed_id: BatchId(1),
|
||||
state: ChainSyncingState::Stopped,
|
||||
current_processing_id: None,
|
||||
current_processing_batch: None,
|
||||
sync_send,
|
||||
chain,
|
||||
log,
|
||||
@@ -167,15 +167,16 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
// An entire batch of blocks has been received. This functions checks to see if it can be processed,
|
||||
// remove any batches waiting to be verified and if this chain is syncing, request new
|
||||
// blocks for the peer.
|
||||
debug!(self.log, "Completed batch received"; "id"=> *batch.id, "blocks"=>batch.downloaded_blocks.len(), "awaiting_batches" => self.completed_batches.len());
|
||||
debug!(self.log, "Completed batch received"; "id"=> *batch.id, "blocks" => &batch.downloaded_blocks.len(), "awaiting_batches" => self.completed_batches.len());
|
||||
|
||||
// verify the range of received blocks
|
||||
// Note that the order of blocks is verified in block processing
|
||||
if let Some(last_slot) = batch.downloaded_blocks.last().map(|b| b.slot()) {
|
||||
// the batch is non-empty
|
||||
if batch.start_slot > batch.downloaded_blocks[0].slot() || batch.end_slot < last_slot {
|
||||
let first_slot = batch.downloaded_blocks[0].slot();
|
||||
if batch.start_slot > first_slot || batch.end_slot < last_slot {
|
||||
warn!(self.log, "BlocksByRange response returned out of range blocks";
|
||||
"response_initial_slot" => batch.downloaded_blocks[0].slot(),
|
||||
"response_initial_slot" => first_slot,
|
||||
"requested_initial_slot" => batch.start_slot);
|
||||
network.downvote_peer(batch.current_peer);
|
||||
self.to_be_processed_id = batch.id; // reset the id back to here, when incrementing, it will check against completed batches
|
||||
@@ -218,7 +219,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
}
|
||||
|
||||
// Only process one batch at a time
|
||||
if self.current_processing_id.is_some() {
|
||||
if self.current_processing_batch.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -238,14 +239,14 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
}
|
||||
|
||||
/// Sends a batch to the batch processor.
|
||||
fn process_batch(&mut self, batch: Batch<T::EthSpec>) {
|
||||
// only spawn one instance at a time
|
||||
let processing_id: u64 = rand::random();
|
||||
self.current_processing_id = Some(processing_id);
|
||||
spawn_batch_processor(
|
||||
fn process_batch(&mut self, mut batch: Batch<T::EthSpec>) {
|
||||
let downloaded_blocks = std::mem::replace(&mut batch.downloaded_blocks, Vec::new());
|
||||
let batch_id = ProcessId::RangeBatchId(batch.id.clone());
|
||||
self.current_processing_batch = Some(batch);
|
||||
spawn_block_processor(
|
||||
self.chain.clone(),
|
||||
processing_id,
|
||||
batch,
|
||||
batch_id,
|
||||
downloaded_blocks,
|
||||
self.sync_send.clone(),
|
||||
self.log.clone(),
|
||||
);
|
||||
@@ -256,30 +257,41 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
pub fn on_batch_process_result(
|
||||
&mut self,
|
||||
network: &mut SyncNetworkContext<T::EthSpec>,
|
||||
processing_id: u64,
|
||||
batch: &mut Option<Batch<T::EthSpec>>,
|
||||
batch_id: BatchId,
|
||||
downloaded_blocks: &mut Option<Vec<SignedBeaconBlock<T::EthSpec>>>,
|
||||
result: &BatchProcessResult,
|
||||
) -> Option<ProcessingResult> {
|
||||
if Some(processing_id) != self.current_processing_id {
|
||||
// batch process doesn't belong to this chain
|
||||
if let Some(current_batch) = &self.current_processing_batch {
|
||||
if current_batch.id != batch_id {
|
||||
// batch process does not belong to this chain
|
||||
return None;
|
||||
}
|
||||
// Continue. This is our processing request
|
||||
} else {
|
||||
// not waiting on a processing result
|
||||
return None;
|
||||
}
|
||||
|
||||
// Consume the batch option
|
||||
let batch = batch.take().or_else(|| {
|
||||
// claim the result by consuming the option
|
||||
let downloaded_blocks = downloaded_blocks.take().or_else(|| {
|
||||
// if taken by another chain, we are no longer waiting on a result.
|
||||
self.current_processing_batch = None;
|
||||
crit!(self.log, "Processed batch taken by another chain");
|
||||
None
|
||||
})?;
|
||||
|
||||
// No longer waiting on a processing result
|
||||
let mut batch = self.current_processing_batch.take().unwrap();
|
||||
// These are the blocks of this batch
|
||||
batch.downloaded_blocks = downloaded_blocks;
|
||||
|
||||
// double check batches are processed in order TODO: Remove for prod
|
||||
if batch.id != self.to_be_processed_id {
|
||||
crit!(self.log, "Batch processed out of order";
|
||||
"processed_batch_id" => *batch.id,
|
||||
"expected_id" => *self.to_be_processed_id);
|
||||
"processed_batch_id" => *batch.id,
|
||||
"expected_id" => *self.to_be_processed_id);
|
||||
}
|
||||
|
||||
self.current_processing_id = None;
|
||||
|
||||
let res = match result {
|
||||
BatchProcessResult::Success => {
|
||||
*self.to_be_processed_id += 1;
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
//! peers.
|
||||
|
||||
mod batch;
|
||||
mod batch_processing;
|
||||
mod chain;
|
||||
mod chain_collection;
|
||||
mod range;
|
||||
|
||||
pub use batch::Batch;
|
||||
pub use batch_processing::BatchProcessResult;
|
||||
pub use batch::BatchId;
|
||||
pub use range::RangeSync;
|
||||
|
||||
@@ -41,8 +41,9 @@
|
||||
|
||||
use super::chain::ProcessingResult;
|
||||
use super::chain_collection::{ChainCollection, SyncState};
|
||||
use super::{Batch, BatchProcessResult};
|
||||
use super::BatchId;
|
||||
use crate::router::processor::PeerSyncInfo;
|
||||
use crate::sync::block_processor::BatchProcessResult;
|
||||
use crate::sync::manager::SyncMessage;
|
||||
use crate::sync::network_context::SyncNetworkContext;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
@@ -130,8 +131,8 @@ impl<T: BeaconChainTypes> RangeSync<T> {
|
||||
},
|
||||
None => {
|
||||
return warn!(self.log,
|
||||
"Beacon chain dropped. Peer not considered for sync";
|
||||
"peer_id" => format!("{:?}", peer_id));
|
||||
"Beacon chain dropped. Peer not considered for sync";
|
||||
"peer_id" => format!("{:?}", peer_id));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -256,15 +257,15 @@ impl<T: BeaconChainTypes> RangeSync<T> {
|
||||
pub fn handle_block_process_result(
|
||||
&mut self,
|
||||
network: &mut SyncNetworkContext<T::EthSpec>,
|
||||
processing_id: u64,
|
||||
batch: Batch<T::EthSpec>,
|
||||
batch_id: BatchId,
|
||||
downloaded_blocks: Vec<SignedBeaconBlock<T::EthSpec>>,
|
||||
result: BatchProcessResult,
|
||||
) {
|
||||
// build an option for passing the batch to each chain
|
||||
let mut batch = Some(batch);
|
||||
// build an option for passing the downloaded_blocks to each chain
|
||||
let mut downloaded_blocks = Some(downloaded_blocks);
|
||||
|
||||
match self.chains.finalized_request(|chain| {
|
||||
chain.on_batch_process_result(network, processing_id, &mut batch, &result)
|
||||
chain.on_batch_process_result(network, batch_id, &mut downloaded_blocks, &result)
|
||||
}) {
|
||||
Some((index, ProcessingResult::RemoveChain)) => {
|
||||
let chain = self.chains.remove_finalized_chain(index);
|
||||
@@ -293,7 +294,12 @@ impl<T: BeaconChainTypes> RangeSync<T> {
|
||||
Some((_, ProcessingResult::KeepChain)) => {}
|
||||
None => {
|
||||
match self.chains.head_request(|chain| {
|
||||
chain.on_batch_process_result(network, processing_id, &mut batch, &result)
|
||||
chain.on_batch_process_result(
|
||||
network,
|
||||
batch_id,
|
||||
&mut downloaded_blocks,
|
||||
&result,
|
||||
)
|
||||
}) {
|
||||
Some((index, ProcessingResult::RemoveChain)) => {
|
||||
let chain = self.chains.remove_head_chain(index);
|
||||
@@ -308,7 +314,7 @@ impl<T: BeaconChainTypes> RangeSync<T> {
|
||||
None => {
|
||||
// This can happen if a chain gets purged due to being out of date whilst a
|
||||
// batch process is in progress.
|
||||
debug!(self.log, "No chains match the block processing id"; "id" => processing_id);
|
||||
debug!(self.log, "No chains match the block processing id"; "id" => *batch_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +438,15 @@ pub fn get_genesis_time<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
) -> ApiResult {
|
||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.genesis_time)
|
||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_time)
|
||||
}
|
||||
|
||||
/// Read the `genesis_validators_root` from the current beacon chain state.
|
||||
pub fn get_genesis_validators_root<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
) -> ApiResult {
|
||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_validators_root)
|
||||
}
|
||||
|
||||
pub fn proposer_slashing<T: BeaconChainTypes>(
|
||||
|
||||
@@ -4,11 +4,12 @@ use crate::{ApiError, ApiResult, BoxFut, UrlQuery};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use futures::{Future, Stream};
|
||||
use hyper::{Body, Request};
|
||||
use rest_types::{IndividualVotesRequest, IndividualVotesResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use state_processing::per_epoch_processing::{TotalBalances, ValidatorStatus, ValidatorStatuses};
|
||||
use state_processing::per_epoch_processing::{TotalBalances, ValidatorStatuses};
|
||||
use std::sync::Arc;
|
||||
use types::{Epoch, EthSpec, PublicKeyBytes};
|
||||
use types::EthSpec;
|
||||
|
||||
/// The results of validators voting during an epoch.
|
||||
///
|
||||
@@ -37,13 +38,13 @@ pub struct VoteCount {
|
||||
impl Into<VoteCount> for TotalBalances {
|
||||
fn into(self) -> VoteCount {
|
||||
VoteCount {
|
||||
current_epoch_active_gwei: self.current_epoch,
|
||||
previous_epoch_active_gwei: self.previous_epoch,
|
||||
current_epoch_attesting_gwei: self.current_epoch_attesters,
|
||||
current_epoch_target_attesting_gwei: self.current_epoch_target_attesters,
|
||||
previous_epoch_attesting_gwei: self.previous_epoch_attesters,
|
||||
previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters,
|
||||
previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters,
|
||||
current_epoch_active_gwei: self.current_epoch(),
|
||||
previous_epoch_active_gwei: self.previous_epoch(),
|
||||
current_epoch_attesting_gwei: self.current_epoch_attesters(),
|
||||
current_epoch_target_attesting_gwei: self.current_epoch_target_attesters(),
|
||||
previous_epoch_attesting_gwei: self.previous_epoch_attesters(),
|
||||
previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters(),
|
||||
previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,68 +71,6 @@ pub fn get_vote_count<T: BeaconChainTypes>(
|
||||
ResponseBuilder::new(&req)?.body(&report)
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)]
|
||||
pub struct IndividualVotesRequest {
|
||||
pub epoch: Epoch,
|
||||
pub pubkeys: Vec<PublicKeyBytes>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)]
|
||||
pub struct IndividualVote {
|
||||
/// True if the validator has been slashed, ever.
|
||||
pub is_slashed: bool,
|
||||
/// True if the validator can withdraw in the current epoch.
|
||||
pub is_withdrawable_in_current_epoch: bool,
|
||||
/// True if the validator was active in the state's _current_ epoch.
|
||||
pub is_active_in_current_epoch: bool,
|
||||
/// True if the validator was active in the state's _previous_ epoch.
|
||||
pub is_active_in_previous_epoch: bool,
|
||||
/// The validator's effective balance in the _current_ epoch.
|
||||
pub current_epoch_effective_balance_gwei: u64,
|
||||
/// True if the validator had an attestation included in the _current_ epoch.
|
||||
pub is_current_epoch_attester: bool,
|
||||
/// True if the validator's beacon block root attestation for the first slot of the _current_
|
||||
/// epoch matches the block root known to the state.
|
||||
pub is_current_epoch_target_attester: bool,
|
||||
/// True if the validator had an attestation included in the _previous_ epoch.
|
||||
pub is_previous_epoch_attester: bool,
|
||||
/// True if the validator's beacon block root attestation for the first slot of the _previous_
|
||||
/// epoch matches the block root known to the state.
|
||||
pub is_previous_epoch_target_attester: bool,
|
||||
/// True if the validator's beacon block root attestation in the _previous_ epoch at the
|
||||
/// attestation's slot (`attestation_data.slot`) matches the block root known to the state.
|
||||
pub is_previous_epoch_head_attester: bool,
|
||||
}
|
||||
|
||||
impl Into<IndividualVote> for ValidatorStatus {
|
||||
fn into(self) -> IndividualVote {
|
||||
IndividualVote {
|
||||
is_slashed: self.is_slashed,
|
||||
is_withdrawable_in_current_epoch: self.is_withdrawable_in_current_epoch,
|
||||
is_active_in_current_epoch: self.is_active_in_current_epoch,
|
||||
is_active_in_previous_epoch: self.is_active_in_previous_epoch,
|
||||
current_epoch_effective_balance_gwei: self.current_epoch_effective_balance,
|
||||
is_current_epoch_attester: self.is_current_epoch_attester,
|
||||
is_current_epoch_target_attester: self.is_current_epoch_target_attester,
|
||||
is_previous_epoch_attester: self.is_previous_epoch_attester,
|
||||
is_previous_epoch_target_attester: self.is_previous_epoch_target_attester,
|
||||
is_previous_epoch_head_attester: self.is_previous_epoch_head_attester,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)]
|
||||
pub struct IndividualVotesResponse {
|
||||
/// The epoch which is considered the "current" epoch.
|
||||
pub epoch: Epoch,
|
||||
/// The validators public key.
|
||||
pub pubkey: PublicKeyBytes,
|
||||
/// The index of the validator in state.validators.
|
||||
pub validator_index: Option<usize>,
|
||||
/// Voting statistics for the validator, if they voted in the given epoch.
|
||||
pub vote: Option<IndividualVote>,
|
||||
}
|
||||
|
||||
pub fn post_individual_votes<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
@@ -156,12 +95,16 @@ pub fn post_individual_votes<T: BeaconChainTypes>(
|
||||
// This is the last slot of the given epoch (one prior to the first slot of the next epoch).
|
||||
let target_slot = (epoch + 1).start_slot(T::EthSpec::slots_per_epoch()) - 1;
|
||||
|
||||
let (_root, state) = state_at_slot(&beacon_chain, target_slot)?;
|
||||
let (_root, mut state) = state_at_slot(&beacon_chain, target_slot)?;
|
||||
let spec = &beacon_chain.spec;
|
||||
|
||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||
validator_statuses.process_attestations(&state, spec)?;
|
||||
|
||||
state.update_pubkey_cache().map_err(|e| {
|
||||
ApiError::ServerError(format!("Unable to build pubkey cache: {:?}", e))
|
||||
})?;
|
||||
|
||||
body.pubkeys
|
||||
.into_iter()
|
||||
.map(|pubkey| {
|
||||
|
||||
@@ -82,6 +82,9 @@ pub fn route<T: BeaconChainTypes>(
|
||||
(&Method::GET, "/beacon/genesis_time") => {
|
||||
into_boxfut(beacon::get_genesis_time::<T>(req, beacon_chain))
|
||||
}
|
||||
(&Method::GET, "/beacon/genesis_validators_root") => {
|
||||
into_boxfut(beacon::get_genesis_validators_root::<T>(req, beacon_chain))
|
||||
}
|
||||
(&Method::GET, "/beacon/validators") => {
|
||||
into_boxfut(beacon::get_validators::<T>(req, beacon_chain))
|
||||
}
|
||||
|
||||
@@ -209,19 +209,12 @@ fn return_validator_duties<T: BeaconChainTypes>(
|
||||
// The `beacon_chain` can return a validator index that does not exist in all states.
|
||||
// Therefore, we must check to ensure that the validator index is valid for our
|
||||
// `state`.
|
||||
let validator_index = if let Some(i) = beacon_chain
|
||||
let validator_index = beacon_chain
|
||||
.validator_index(&validator_pubkey)
|
||||
.map_err(|e| {
|
||||
ApiError::ServerError(format!("Unable to get validator index: {:?}", e))
|
||||
})? {
|
||||
if i < state.validators.len() {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ApiError::ServerError(format!("Unable to get validator index: {:?}", e))
|
||||
})?
|
||||
.filter(|i| *i < state.validators.len());
|
||||
|
||||
if let Some(validator_index) = validator_index {
|
||||
let duties = state
|
||||
@@ -554,6 +547,7 @@ pub fn publish_aggregate_and_proofs<T: BeaconChainTypes>(
|
||||
// TODO: More efficient way of getting a fork?
|
||||
let fork = &beacon_chain.head()?.beacon_state.fork;
|
||||
|
||||
// TODO: Update to shift this task to dedicated task using await
|
||||
signed_proofs.par_iter().try_for_each(|signed_proof| {
|
||||
let agg_proof = &signed_proof.message;
|
||||
let validator_pubkey = &beacon_chain.validator_pubkey(agg_proof.aggregator_index as usize)?.ok_or_else(|| {
|
||||
@@ -573,7 +567,7 @@ pub fn publish_aggregate_and_proofs<T: BeaconChainTypes>(
|
||||
* I (Paul H) will pick this up in a future PR.
|
||||
*/
|
||||
|
||||
if signed_proof.is_valid(validator_pubkey, fork, &beacon_chain.spec) {
|
||||
if signed_proof.is_valid(validator_pubkey, fork, beacon_chain.genesis_validators_root, &beacon_chain.spec) {
|
||||
let attestation = &agg_proof.aggregate;
|
||||
|
||||
match beacon_chain.process_attestation(attestation.clone(), AttestationType::Aggregated) {
|
||||
|
||||
@@ -48,17 +48,15 @@ fn get_randao_reveal<T: BeaconChainTypes>(
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Signature {
|
||||
let fork = beacon_chain
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_state
|
||||
.fork;
|
||||
let head = beacon_chain.head().expect("should get head");
|
||||
let fork = head.beacon_state.fork;
|
||||
let genesis_validators_root = head.beacon_state.genesis_validators_root;
|
||||
let proposer_index = beacon_chain
|
||||
.block_proposer(slot)
|
||||
.expect("should get proposer index");
|
||||
let keypair = generate_deterministic_keypair(proposer_index);
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, &fork);
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, &fork, genesis_validators_root);
|
||||
let message = epoch.signing_root(domain);
|
||||
Signature::new(message.as_bytes(), &keypair.sk)
|
||||
}
|
||||
@@ -69,16 +67,14 @@ fn sign_block<T: BeaconChainTypes>(
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedBeaconBlock<T::EthSpec> {
|
||||
let fork = beacon_chain
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_state
|
||||
.fork;
|
||||
let head = beacon_chain.head().expect("should get head");
|
||||
let fork = head.beacon_state.fork;
|
||||
let genesis_validators_root = head.beacon_state.genesis_validators_root;
|
||||
let proposer_index = beacon_chain
|
||||
.block_proposer(block.slot)
|
||||
.expect("should get proposer index");
|
||||
let keypair = generate_deterministic_keypair(proposer_index);
|
||||
block.sign(&keypair.sk, &fork, spec)
|
||||
block.sign(&keypair.sk, &fork, genesis_validators_root, spec)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -94,6 +90,7 @@ fn validator_produce_attestation() {
|
||||
.client
|
||||
.beacon_chain()
|
||||
.expect("client should have beacon chain");
|
||||
let genesis_validators_root = beacon_chain.genesis_validators_root;
|
||||
let state = beacon_chain.head().expect("should get head").beacon_state;
|
||||
|
||||
let validator_index = 0;
|
||||
@@ -192,6 +189,7 @@ fn validator_produce_attestation() {
|
||||
.attestation_committee_position
|
||||
.expect("should have committee position"),
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
.expect("should sign attestation");
|
||||
@@ -228,6 +226,7 @@ fn validator_produce_attestation() {
|
||||
aggregated_attestation,
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
@@ -635,6 +634,31 @@ fn genesis_time() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_validators_root() {
|
||||
let mut env = build_env();
|
||||
|
||||
let node = build_node(&mut env, testing_client_config());
|
||||
let remote_node = node.remote_node().expect("should produce remote node");
|
||||
|
||||
let genesis_validators_root = env
|
||||
.runtime()
|
||||
.block_on(remote_node.http.beacon().get_genesis_validators_root())
|
||||
.expect("should fetch genesis time from http api");
|
||||
|
||||
assert_eq!(
|
||||
node.client
|
||||
.beacon_chain()
|
||||
.expect("should have beacon chain")
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_state
|
||||
.genesis_validators_root,
|
||||
genesis_validators_root,
|
||||
"should match genesis time from head state"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork() {
|
||||
let mut env = build_env();
|
||||
@@ -974,6 +998,7 @@ fn proposer_slashing() {
|
||||
proposer_index as u64,
|
||||
&key,
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
@@ -998,6 +1023,7 @@ fn proposer_slashing() {
|
||||
proposer_index as u64,
|
||||
&key,
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
invalid_proposer_slashing.signed_header_2 = invalid_proposer_slashing.signed_header_1.clone();
|
||||
@@ -1052,6 +1078,7 @@ fn attester_slashing() {
|
||||
&validator_indices[..],
|
||||
&secret_keys[..],
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
@@ -1077,6 +1104,7 @@ fn attester_slashing() {
|
||||
&validator_indices[..],
|
||||
&secret_keys[..],
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
invalid_attester_slashing.attestation_2 = invalid_attester_slashing.attestation_1.clone();
|
||||
|
||||
+1
-10
@@ -218,7 +218,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
Arg::with_name("eth1-endpoint")
|
||||
.long("eth1-endpoint")
|
||||
.value_name("HTTP-ENDPOINT")
|
||||
.help("Specifies the server for a web3 connection to the Eth1 chain.")
|
||||
.help("Specifies the server for a web3 connection to the Eth1 chain. Also enables the --eth1 flag.")
|
||||
.takes_value(true)
|
||||
.default_value("http://127.0.0.1:8545")
|
||||
)
|
||||
@@ -339,14 +339,5 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
.required(true)
|
||||
.help("A file from which to read the state"))
|
||||
)
|
||||
/*
|
||||
* `prysm`
|
||||
*
|
||||
* Connect to the Prysmatic Labs testnet.
|
||||
*/
|
||||
.subcommand(SubCommand::with_name("prysm")
|
||||
.about("Connect to the Prysmatic Labs testnet on Goerli. Not guaranteed to be \
|
||||
up-to-date or functioning.")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+50
-96
@@ -1,5 +1,6 @@
|
||||
use clap::ArgMatches;
|
||||
use client::{config::DEFAULT_DATADIR, ClientConfig, ClientGenesis, Eth2Config};
|
||||
use environment::ETH2_CONFIG_FILENAME;
|
||||
use eth2_config::{read_from_file, write_to_file};
|
||||
use eth2_libp2p::{Enr, Multiaddr};
|
||||
use eth2_testnet_config::Eth2TestnetConfig;
|
||||
@@ -14,14 +15,12 @@ use std::path::PathBuf;
|
||||
use types::EthSpec;
|
||||
|
||||
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
|
||||
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
|
||||
pub const BEACON_NODE_DIR: &str = "beacon";
|
||||
pub const NETWORK_DIR: &str = "network";
|
||||
|
||||
type Result<T> = std::result::Result<T, String>;
|
||||
type Config = (ClientConfig, Eth2Config, Logger);
|
||||
|
||||
/// Gets the fully-initialized global client and eth2 configuration objects.
|
||||
/// Gets the fully-initialized global client.
|
||||
///
|
||||
/// The top-level `clap` arguments should be provided as `cli_args`.
|
||||
///
|
||||
@@ -29,26 +28,17 @@ type Config = (ClientConfig, Eth2Config, Logger);
|
||||
/// may be influenced by other external services like the contents of the file system or the
|
||||
/// response of some remote server.
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn get_configs<E: EthSpec>(
|
||||
pub fn get_config<E: EthSpec>(
|
||||
cli_args: &ArgMatches,
|
||||
mut eth2_config: Eth2Config,
|
||||
eth2_config: Eth2Config,
|
||||
core_log: Logger,
|
||||
) -> Result<Config> {
|
||||
) -> Result<ClientConfig> {
|
||||
let log = core_log.clone();
|
||||
|
||||
let mut client_config = ClientConfig::default();
|
||||
|
||||
client_config.spec_constants = eth2_config.spec_constants.clone();
|
||||
|
||||
// Read the `--datadir` flag.
|
||||
//
|
||||
// If it's not present, try and find the home directory (`~`) and push the default data
|
||||
// directory onto it.
|
||||
client_config.data_dir = cli_args
|
||||
.value_of("datadir")
|
||||
.map(|path| PathBuf::from(path).join(BEACON_NODE_DIR))
|
||||
.or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR)))
|
||||
.unwrap_or_else(|| PathBuf::from("."));
|
||||
client_config.data_dir = get_data_dir(cli_args);
|
||||
|
||||
// Load the client config, if it exists .
|
||||
let path = client_config.data_dir.join(CLIENT_CONFIG_FILENAME);
|
||||
@@ -58,32 +48,7 @@ pub fn get_configs<E: EthSpec>(
|
||||
.ok_or_else(|| format!("{:?} file does not exist", path))?;
|
||||
}
|
||||
|
||||
// Load the eth2 config, if it exists .
|
||||
let path = client_config.data_dir.join(ETH2_CONFIG_FILENAME);
|
||||
if path.exists() {
|
||||
let loaded_eth2_config: Eth2Config = read_from_file(path.clone())
|
||||
.map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))?
|
||||
.ok_or_else(|| format!("{:?} file does not exist", path))?;
|
||||
|
||||
// The loaded spec must be using the same spec constants (e.g., minimal, mainnet) as the
|
||||
// client expects.
|
||||
if loaded_eth2_config.spec_constants == client_config.spec_constants {
|
||||
eth2_config = loaded_eth2_config
|
||||
} else {
|
||||
return Err(
|
||||
format!(
|
||||
"Eth2 config loaded from disk does not match client spec version. Got {} expected {}",
|
||||
&loaded_eth2_config.spec_constants,
|
||||
&client_config.spec_constants
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the `--testnet-dir` flag.
|
||||
if let Some(val) = cli_args.value_of("testnet-dir") {
|
||||
client_config.testnet_dir = Some(PathBuf::from(val));
|
||||
}
|
||||
client_config.testnet_dir = get_testnet_dir(cli_args);
|
||||
|
||||
/*
|
||||
* Networking
|
||||
@@ -251,12 +216,13 @@ pub fn get_configs<E: EthSpec>(
|
||||
|
||||
// Defines the URL to reach the eth1 node.
|
||||
if let Some(val) = cli_args.value_of("eth1-endpoint") {
|
||||
client_config.sync_eth1_chain = true;
|
||||
client_config.eth1.endpoint = val.to_string();
|
||||
}
|
||||
|
||||
match cli_args.subcommand() {
|
||||
("testnet", Some(sub_cmd_args)) => {
|
||||
process_testnet_subcommand(&mut client_config, &mut eth2_config, sub_cmd_args)?
|
||||
process_testnet_subcommand(&mut client_config, ð2_config, sub_cmd_args)?
|
||||
}
|
||||
// No sub-command assumes a resume operation.
|
||||
_ => {
|
||||
@@ -272,7 +238,7 @@ pub fn get_configs<E: EthSpec>(
|
||||
"Starting from an empty database";
|
||||
"data_dir" => format!("{:?}", client_config.data_dir)
|
||||
);
|
||||
init_new_client::<E>(&mut client_config, &mut eth2_config)?
|
||||
init_new_client::<E>(&mut client_config, ð2_config)?
|
||||
} else {
|
||||
info!(
|
||||
log,
|
||||
@@ -342,7 +308,42 @@ pub fn get_configs<E: EthSpec>(
|
||||
client_config.websocket_server.port = 0;
|
||||
}
|
||||
|
||||
Ok((client_config, eth2_config, log))
|
||||
Ok(client_config)
|
||||
}
|
||||
|
||||
/// Gets the datadir which should be used.
|
||||
pub fn get_data_dir(cli_args: &ArgMatches) -> PathBuf {
|
||||
// Read the `--datadir` flag.
|
||||
//
|
||||
// If it's not present, try and find the home directory (`~`) and push the default data
|
||||
// directory onto it.
|
||||
cli_args
|
||||
.value_of("datadir")
|
||||
.map(|path| PathBuf::from(path).join(BEACON_NODE_DIR))
|
||||
.or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR)))
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
}
|
||||
|
||||
/// Gets the testnet dir which should be used.
|
||||
pub fn get_testnet_dir(cli_args: &ArgMatches) -> Option<PathBuf> {
|
||||
// Read the `--testnet-dir` flag.
|
||||
if let Some(val) = cli_args.value_of("testnet-dir") {
|
||||
Some(PathBuf::from(val))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_eth2_testnet_config<E: EthSpec>(
|
||||
testnet_dir: &Option<PathBuf>,
|
||||
) -> Result<Eth2TestnetConfig<E>> {
|
||||
Ok(if let Some(testnet_dir) = testnet_dir {
|
||||
Eth2TestnetConfig::load(testnet_dir.clone())
|
||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))?
|
||||
} else {
|
||||
Eth2TestnetConfig::hard_coded()
|
||||
.map_err(|e| format!("Unable to load hard-coded testnet dir: {}", e))?
|
||||
})
|
||||
}
|
||||
|
||||
/// Load from an existing database.
|
||||
@@ -377,37 +378,17 @@ fn load_from_datadir(client_config: &mut ClientConfig) -> Result<()> {
|
||||
/// Create a new client with the default configuration.
|
||||
fn init_new_client<E: EthSpec>(
|
||||
client_config: &mut ClientConfig,
|
||||
eth2_config: &mut Eth2Config,
|
||||
eth2_config: &Eth2Config,
|
||||
) -> Result<()> {
|
||||
let eth2_testnet_config: Eth2TestnetConfig<E> =
|
||||
if let Some(testnet_dir) = &client_config.testnet_dir {
|
||||
Eth2TestnetConfig::load(testnet_dir.clone())
|
||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))?
|
||||
} else {
|
||||
Eth2TestnetConfig::hard_coded()
|
||||
.map_err(|e| format!("Unable to load hard-coded testnet dir: {}", e))?
|
||||
};
|
||||
|
||||
eth2_config.spec = eth2_testnet_config
|
||||
.yaml_config
|
||||
.as_ref()
|
||||
.ok_or_else(|| "The testnet directory must contain a spec config".to_string())?
|
||||
.apply_to_chain_spec::<E>(ð2_config.spec)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"The loaded config is not compatible with the {} spec",
|
||||
ð2_config.spec_constants
|
||||
)
|
||||
})?;
|
||||
|
||||
let spec = &mut eth2_config.spec;
|
||||
get_eth2_testnet_config(&client_config.testnet_dir)?;
|
||||
|
||||
client_config.eth1.deposit_contract_address =
|
||||
format!("{:?}", eth2_testnet_config.deposit_contract_address()?);
|
||||
client_config.eth1.deposit_contract_deploy_block =
|
||||
eth2_testnet_config.deposit_contract_deploy_block;
|
||||
|
||||
client_config.eth1.follow_distance = spec.eth1_follow_distance / 2;
|
||||
client_config.eth1.follow_distance = eth2_config.spec.eth1_follow_distance / 2;
|
||||
client_config.eth1.lowest_cached_block_number = client_config
|
||||
.eth1
|
||||
.deposit_contract_deploy_block
|
||||
@@ -470,7 +451,7 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config
|
||||
/// Process the `testnet` CLI subcommand arguments, updating the `builder`.
|
||||
fn process_testnet_subcommand(
|
||||
client_config: &mut ClientConfig,
|
||||
eth2_config: &mut Eth2Config,
|
||||
eth2_config: &Eth2Config,
|
||||
cli_args: &ArgMatches,
|
||||
) -> Result<()> {
|
||||
// Specifies that a random datadir should be used.
|
||||
@@ -501,15 +482,6 @@ fn process_testnet_subcommand(
|
||||
client_config.network.propagation_percentage = Some(percentage);
|
||||
}
|
||||
|
||||
// Modify the `SECONDS_PER_SLOT` "constant".
|
||||
if let Some(slot_time) = cli_args.value_of("slot-time") {
|
||||
let slot_time = slot_time
|
||||
.parse::<u64>()
|
||||
.map_err(|e| format!("Unable to parse slot-time: {:?}", e))?;
|
||||
|
||||
eth2_config.spec.milliseconds_per_slot = slot_time;
|
||||
}
|
||||
|
||||
// Start matching on the second subcommand (e.g., `testnet bootstrap ...`).
|
||||
match cli_args.subcommand() {
|
||||
("recent", Some(cli_args)) => {
|
||||
@@ -570,24 +542,6 @@ fn process_testnet_subcommand(
|
||||
|
||||
client_config.genesis = start_method;
|
||||
}
|
||||
("prysm", Some(_)) => {
|
||||
let mut spec = &mut eth2_config.spec;
|
||||
|
||||
spec.min_deposit_amount = 100;
|
||||
spec.max_effective_balance = 3_200_000_000;
|
||||
spec.ejection_balance = 1_600_000_000;
|
||||
spec.effective_balance_increment = 100_000_000;
|
||||
spec.min_genesis_time = 0;
|
||||
spec.genesis_fork_version = [0, 0, 0, 2];
|
||||
|
||||
client_config.eth1.deposit_contract_address =
|
||||
"0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string();
|
||||
client_config.eth1.deposit_contract_deploy_block = 1_487_270;
|
||||
client_config.eth1.follow_distance = 16;
|
||||
client_config.dummy_eth1_backend = false;
|
||||
|
||||
client_config.genesis = ClientGenesis::DepositContract;
|
||||
}
|
||||
(cmd, Some(_)) => {
|
||||
return Err(format!(
|
||||
"Invalid valid method specified: {}. See 'testnet --help'.",
|
||||
|
||||
+5
-12
@@ -7,6 +7,7 @@ mod config;
|
||||
pub use beacon_chain;
|
||||
pub use cli::cli_app;
|
||||
pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis};
|
||||
pub use config::{get_data_dir, get_eth2_testnet_config, get_testnet_dir};
|
||||
pub use eth2_config::Eth2Config;
|
||||
|
||||
use beacon_chain::{
|
||||
@@ -14,7 +15,7 @@ use beacon_chain::{
|
||||
slot_clock::SystemTimeSlotClock,
|
||||
};
|
||||
use clap::ArgMatches;
|
||||
use config::get_configs;
|
||||
use config::get_config;
|
||||
use environment::RuntimeContext;
|
||||
use futures::{Future, IntoFuture};
|
||||
use slog::{info, warn};
|
||||
@@ -51,20 +52,12 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
|
||||
/// given `matches` and potentially configuration files on the local filesystem or other
|
||||
/// configurations hosted remotely.
|
||||
pub fn new_from_cli<'a, 'b>(
|
||||
mut context: RuntimeContext<E>,
|
||||
context: RuntimeContext<E>,
|
||||
matches: &ArgMatches<'b>,
|
||||
) -> impl Future<Item = Self, Error = String> + 'a {
|
||||
let log = context.log.clone();
|
||||
|
||||
// TODO: the eth2 config in the env is being modified.
|
||||
//
|
||||
// See https://github.com/sigp/lighthouse/issues/602
|
||||
get_configs::<E>(&matches, context.eth2_config.clone(), log)
|
||||
get_config::<E>(&matches, context.eth2_config.clone(), context.log.clone())
|
||||
.into_future()
|
||||
.and_then(move |(client_config, eth2_config, _log)| {
|
||||
context.eth2_config = eth2_config;
|
||||
Self::new(context, client_config)
|
||||
})
|
||||
.and_then(move |client_config| Self::new(context, client_config))
|
||||
}
|
||||
|
||||
/// Starts a new beacon node `Client` in the given `environment`.
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
leveldb_store::LevelDB, DBColumn, Error, PartialBeaconState, SimpleStoreItem, Store, StoreItem,
|
||||
};
|
||||
use lru::LruCache;
|
||||
use parking_lot::RwLock;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use slog::{debug, trace, warn, Logger};
|
||||
use ssz::{Decode, Encode};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -45,7 +45,7 @@ pub struct HotColdDB<E: EthSpec> {
|
||||
/// The hot database also contains all blocks.
|
||||
pub(crate) hot_db: LevelDB<E>,
|
||||
/// LRU cache of deserialized blocks. Updated whenever a block is loaded.
|
||||
block_cache: RwLock<LruCache<Hash256, SignedBeaconBlock<E>>>,
|
||||
block_cache: Mutex<LruCache<Hash256, SignedBeaconBlock<E>>>,
|
||||
/// Chain spec.
|
||||
spec: ChainSpec,
|
||||
/// Logger.
|
||||
@@ -109,7 +109,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
self.put(block_root, &block)?;
|
||||
|
||||
// Update cache.
|
||||
self.block_cache.write().put(*block_root, block);
|
||||
self.block_cache.lock().put(*block_root, block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -119,7 +119,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
metrics::inc_counter(&metrics::BEACON_BLOCK_GET_COUNT);
|
||||
|
||||
// Check the cache.
|
||||
if let Some(block) = self.block_cache.write().get(block_root) {
|
||||
if let Some(block) = self.block_cache.lock().get(block_root) {
|
||||
metrics::inc_counter(&metrics::BEACON_BLOCK_CACHE_HIT_COUNT);
|
||||
return Ok(Some(block.clone()));
|
||||
}
|
||||
@@ -128,7 +128,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
match self.get::<SignedBeaconBlock<E>>(block_root)? {
|
||||
Some(block) => {
|
||||
// Add to cache.
|
||||
self.block_cache.write().put(*block_root, block.clone());
|
||||
self.block_cache.lock().put(*block_root, block.clone());
|
||||
Ok(Some(block))
|
||||
}
|
||||
None => Ok(None),
|
||||
@@ -137,7 +137,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
|
||||
/// Delete a block from the store and the block cache.
|
||||
fn delete_block(&self, block_root: &Hash256) -> Result<(), Error> {
|
||||
self.block_cache.write().pop(block_root);
|
||||
self.block_cache.lock().pop(block_root);
|
||||
self.delete::<SignedBeaconBlock<E>>(block_root)
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ impl<E: EthSpec> HotColdDB<E> {
|
||||
split: RwLock::new(Split::default()),
|
||||
cold_db: LevelDB::open(cold_path)?,
|
||||
hot_db: LevelDB::open(hot_path)?,
|
||||
block_cache: RwLock::new(LruCache::new(config.block_cache_size)),
|
||||
block_cache: Mutex::new(LruCache::new(config.block_cache_size)),
|
||||
config,
|
||||
spec,
|
||||
log,
|
||||
|
||||
@@ -11,7 +11,7 @@ use types::*;
|
||||
///
|
||||
/// Utilises lazy-loading from separate storage for its vector fields.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
|
||||
pub struct PartialBeaconState<T>
|
||||
where
|
||||
@@ -19,6 +19,7 @@ where
|
||||
{
|
||||
// Versioning
|
||||
pub genesis_time: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
pub slot: Slot,
|
||||
pub fork: Fork,
|
||||
|
||||
@@ -72,6 +73,7 @@ impl<T: EthSpec> PartialBeaconState<T> {
|
||||
// TODO: could use references/Cow for fields to avoid cloning
|
||||
PartialBeaconState {
|
||||
genesis_time: s.genesis_time,
|
||||
genesis_validators_root: s.genesis_validators_root,
|
||||
slot: s.slot,
|
||||
fork: s.fork.clone(),
|
||||
|
||||
@@ -181,6 +183,7 @@ impl<E: EthSpec> TryInto<BeaconState<E>> for PartialBeaconState<E> {
|
||||
|
||||
Ok(BeaconState {
|
||||
genesis_time: self.genesis_time,
|
||||
genesis_validators_root: self.genesis_validators_root,
|
||||
slot: self.slot,
|
||||
fork: self.fork,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user