Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions beacon_node/beacon_chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ derivative = "2.1.1"
itertools = "0.9.0"
regex = "1.3.9"
exit-future = "0.2.0"
eth2 = { path = "../../common/eth2" }
125 changes: 125 additions & 0 deletions beacon_node/beacon_chain/src/eth1_chain.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::metrics;
use eth1::{Config as Eth1Config, Eth1Block, Service as HttpService};
use eth2::lighthouse::Eth1SyncStatusData;
use eth2_hashing::hash;
use slog::{debug, error, trace, Logger};
use ssz::{Decode, Encode};
Expand All @@ -9,6 +10,7 @@ use std::cmp::Ordering;
use std::collections::HashMap;
use std::iter::DoubleEndedIterator;
use std::marker::PhantomData;
use std::time::{SystemTime, UNIX_EPOCH};
use store::{DBColumn, Error as StoreError, StoreItem};
use task_executor::TaskExecutor;
use types::{
Expand All @@ -19,6 +21,11 @@ use types::{
type BlockNumber = u64;
type Eth1DataVoteCount = HashMap<(Eth1Data, BlockNumber), u64>;

/// We will declare ourself synced with the Eth1 chain, even if we are this many blocks behind.
///
/// This number (8) was chosen somewhat arbitrarily.
const ETH1_SYNC_TOLERANCE: u64 = 8;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason for 8? Just curious

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, no reason. I added a note to that measure :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, 8 just felt like a decent number to me. I can't back it up, though 😅


#[derive(Debug)]
pub enum Error {
/// Unable to return an Eth1Data for the given epoch.
Expand Down Expand Up @@ -53,6 +60,84 @@ impl From<safe_arith::ArithError> for Error {
}
}

/// Returns an `Eth1SyncStatusData` given some parameters:
///
/// - `latest_cached_block`: The latest eth1 block in our cache, if any.
/// - `head_block`: The block at the very head of our eth1 node (ignoring follow distance, etc).
/// - `genesis_time`: beacon chain genesis time.
/// - `current_slot`: current beacon chain slot.
/// - `spec`: current beacon chain specification.
fn get_sync_status<T: EthSpec>(
latest_cached_block: Option<&Eth1Block>,
head_block: Option<&Eth1Block>,
genesis_time: u64,
current_slot: Slot,
spec: &ChainSpec,
) -> Option<Eth1SyncStatusData> {
let period = T::SlotsPerEth1VotingPeriod::to_u64();
// Since `period` is a "constant", we assume it is set sensibly.
let voting_period_start_slot = (current_slot / period) * period;
let voting_period_start_timestamp = {
let period_start = slot_start_seconds::<T>(
genesis_time,
spec.milliseconds_per_slot,
voting_period_start_slot,
);
let eth1_follow_distance_seconds = spec
.seconds_per_eth1_block
.saturating_mul(spec.eth1_follow_distance);

period_start.saturating_sub(eth1_follow_distance_seconds)
};

let latest_cached_block_number = latest_cached_block.map(|b| b.number);
let latest_cached_block_timestamp = latest_cached_block.map(|b| b.timestamp);
let head_block_number = head_block.map(|b| b.number);
let head_block_timestamp = head_block.map(|b| b.timestamp);

let eth1_node_sync_status_percentage = if let Some(head_block) = head_block {
let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?.as_secs();
let head_age = now.saturating_sub(head_block.timestamp);

if head_age < ETH1_SYNC_TOLERANCE * spec.seconds_per_eth1_block {
// Always indicate we are fully synced if it's within the sync threshold.
100.0
} else {
let blocks_behind = head_age
.checked_div(spec.seconds_per_eth1_block)
.unwrap_or(0);

let part = f64::from(head_block.number as u32);
let whole = f64::from(head_block.number.saturating_add(blocks_behind) as u32);

if whole > 0.0 {
(part / whole) * 100.0
} else {
// Avoids a divide-by-zero.
0.0
}
}
} else {
// Always return 0% synced if the head block of the eth1 chain is unknown.
0.0
};

// Lighthouse is "cached and ready" when it has cached enough blocks to cover the start of the
// current voting period.
let lighthouse_is_cached_and_ready =
latest_cached_block_timestamp.map_or(false, |t| t >= voting_period_start_timestamp);

Some(Eth1SyncStatusData {
head_block_number,
head_block_timestamp,
latest_cached_block_number,
latest_cached_block_timestamp,
voting_period_start_timestamp,
eth1_node_sync_status_percentage,
lighthouse_is_cached_and_ready,
})
}

#[derive(Encode, Decode, Clone)]
pub struct SszEth1 {
use_dummy_backend: bool,
Expand Down Expand Up @@ -143,6 +228,22 @@ where
}
}

/// Returns a status indicating how synced our caches are with the eth1 chain.
pub fn sync_status(
&self,
genesis_time: u64,
current_slot: Slot,
spec: &ChainSpec,
) -> Option<Eth1SyncStatusData> {
get_sync_status::<E>(
self.backend.latest_cached_block().as_ref(),
self.backend.head_block().as_ref(),
genesis_time,
current_slot,
spec,
)
}

/// Instantiate `Eth1Chain` from a persisted `SszEth1`.
///
/// The `Eth1Chain` will have the same caches as the persisted `SszEth1`.
Expand Down Expand Up @@ -195,6 +296,14 @@ pub trait Eth1ChainBackend<T: EthSpec>: Sized + Send + Sync {
spec: &ChainSpec,
) -> Result<Vec<Deposit>, Error>;

/// Returns the latest block stored in the cache. Used to obtain an idea of how up-to-date the
/// beacon node eth1 cache is.
fn latest_cached_block(&self) -> Option<Eth1Block>;

/// Returns the block at the head of the chain (ignoring follow distance, etc). Used to obtain
/// an idea of how up-to-date the remote eth1 node is.
fn head_block(&self) -> Option<Eth1Block>;

/// Encode the `Eth1ChainBackend` instance to bytes.
fn as_bytes(&self) -> Vec<u8>;

Expand Down Expand Up @@ -241,6 +350,14 @@ impl<T: EthSpec> Eth1ChainBackend<T> for DummyEth1ChainBackend<T> {
Ok(vec![])
}

fn latest_cached_block(&self) -> Option<Eth1Block> {
None
}

fn head_block(&self) -> Option<Eth1Block> {
None
}

/// Return empty Vec<u8> for dummy backend.
fn as_bytes(&self) -> Vec<u8> {
Vec::new()
Expand Down Expand Up @@ -400,6 +517,14 @@ impl<T: EthSpec> Eth1ChainBackend<T> for CachingEth1Backend<T> {
}
}

fn latest_cached_block(&self) -> Option<Eth1Block> {
self.core.latest_cached_block()
}

fn head_block(&self) -> Option<Eth1Block> {
self.core.head_block()
}

/// Return encoded byte representation of the block and deposit caches.
fn as_bytes(&self) -> Vec<u8> {
self.core.as_bytes()
Expand Down
3 changes: 2 additions & 1 deletion beacon_node/client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ where
chain: self.beacon_chain.clone(),
network_tx: self.network_send.clone(),
network_globals: self.network_globals.clone(),
eth1_service: self.eth1_service.clone(),
log: log.clone(),
});

Expand Down Expand Up @@ -590,7 +591,7 @@ where
})?
};

self.eth1_service = None;
self.eth1_service = Some(backend.core.clone());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea why we set this to None earlier? curious again :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. no I can't 😅


// Starts the service that connects to an eth1 node and periodically updates caches.
backend.start(context.executor);
Expand Down
1 change: 1 addition & 0 deletions beacon_node/eth1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ libflate = "1.0.2"
lighthouse_metrics = { path = "../../common/lighthouse_metrics"}
lazy_static = "1.4.0"
task_executor = { path = "../../common/task_executor" }
eth2 = { path = "../../common/eth2" }
36 changes: 13 additions & 23 deletions beacon_node/eth1/src/block_cache.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ssz_derive::{Decode, Encode};
use std::ops::RangeInclusive;
use types::{Eth1Data, Hash256};

pub use eth2::lighthouse::Eth1Block;

#[derive(Debug, PartialEq, Clone)]
pub enum Error {
Expand All @@ -15,28 +16,6 @@ pub enum Error {
Internal(String),
}

/// A block of the eth1 chain.
///
/// Contains all information required to add a `BlockCache` entry.
#[derive(Debug, PartialEq, Clone, Eq, Hash, Encode, Decode)]
pub struct Eth1Block {
pub hash: Hash256,
pub timestamp: u64,
pub number: u64,
pub deposit_root: Option<Hash256>,
pub deposit_count: Option<u64>,
}

impl Eth1Block {
pub fn eth1_data(self) -> Option<Eth1Data> {
Some(Eth1Data {
deposit_root: self.deposit_root?,
deposit_count: self.deposit_count?,
block_hash: self.hash,
})
}
}

/// Stores block and deposit contract information and provides queries based upon the block
/// timestamp.
#[derive(Debug, PartialEq, Clone, Default, Encode, Decode)]
Expand All @@ -55,6 +34,16 @@ impl BlockCache {
self.blocks.is_empty()
}

/// Returns the earliest (lowest timestamp) block, if any.
pub fn earliest_block(&self) -> Option<&Eth1Block> {
self.blocks.first()
}

/// Returns the latest (highest timestamp) block, if any.
pub fn latest_block(&self) -> Option<&Eth1Block> {
self.blocks.last()
}

/// Returns the timestamp of the earliest block in the cache (if any).
pub fn earliest_block_timestamp(&self) -> Option<u64> {
self.blocks.first().map(|block| block.timestamp)
Expand Down Expand Up @@ -181,6 +170,7 @@ impl BlockCache {
#[cfg(test)]
mod tests {
use super::*;
use types::Hash256;

fn get_block(i: u64, interval_secs: u64) -> Eth1Block {
Eth1Block {
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/eth1/src/deposit_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ pub mod tests {
block_number: 42,
data: EXAMPLE_LOG.to_vec(),
};
DepositLog::from_log(&log, &spec).expect("should decode log")
log.to_deposit_log(&spec).expect("should decode log")
}

#[test]
Expand Down
27 changes: 8 additions & 19 deletions beacon_node/eth1/src/deposit_log.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use super::http::Log;
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use state_processing::per_block_processing::signature_sets::{
deposit_pubkey_signature_message, deposit_signature_set,
};
use types::{ChainSpec, DepositData, Hash256, PublicKeyBytes, SignatureBytes};

pub use eth2::lighthouse::DepositLog;

/// The following constants define the layout of bytes in the deposit contract `DepositEvent`. The
/// event bytes are formatted according to the Ethereum ABI.
const PUBKEY_START: usize = 192;
Expand All @@ -19,22 +20,10 @@ const SIG_LEN: usize = 96;
const INDEX_START: usize = SIG_START + 96 + 32;
const INDEX_LEN: usize = 8;

/// A fully parsed eth1 deposit contract log.
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
pub struct DepositLog {
pub deposit_data: DepositData,
/// The block number of the log that included this `DepositData`.
pub block_number: u64,
/// The index included with the deposit log.
pub index: u64,
/// True if the signature is valid.
pub signature_is_valid: bool,
}

impl DepositLog {
impl Log {
/// Attempts to parse a raw `Log` from the deposit contract into a `DepositLog`.
pub fn from_log(log: &Log, spec: &ChainSpec) -> Result<Self, String> {
let bytes = &log.data;
pub fn to_deposit_log(&self, spec: &ChainSpec) -> Result<DepositLog, String> {
let bytes = &self.data;

let pubkey = bytes
.get(PUBKEY_START..PUBKEY_START + PUBKEY_LEN)
Expand Down Expand Up @@ -68,7 +57,7 @@ impl DepositLog {

Ok(DepositLog {
deposit_data,
block_number: log.block_number,
block_number: self.block_number,
index: u64::from_ssz_bytes(index).map_err(|e| format!("Invalid index ssz: {:?}", e))?,
signature_is_valid,
})
Expand All @@ -77,7 +66,6 @@ impl DepositLog {

#[cfg(test)]
pub mod tests {
use super::*;
use crate::http::Log;
use types::{EthSpec, MainnetEthSpec};

Expand Down Expand Up @@ -113,6 +101,7 @@ pub mod tests {
block_number: 42,
data: EXAMPLE_LOG.to_vec(),
};
DepositLog::from_log(&log, &MainnetEthSpec::default_spec()).expect("should decode log");
log.to_deposit_log(&MainnetEthSpec::default_spec())
.expect("should decode log");
}
}
Loading