Persist eth1 cache (#760)

* Add intermediate structures for bytes conversion

* Expose byte conversion methods from `Eth1Service`

* Add eth1 ssz containers

* Fix type errors

* Load eth1 cache on restart

* Fix compile errors

* Update Cargo.lock

* Add comments and minor formatting

* Add test for eth1 cache persistence

* Restrict Deposit and Block cache field visibility

* Add checks

* Fix `SszDepositCache` check

* Implement Encode/Decode directly on `BlockCache`
This commit is contained in:
Pawan Dhananjay
2020-01-20 02:22:59 +05:30
committed by Paul Hauner
parent a8da36b913
commit 661ef65de8
15 changed files with 315 additions and 36 deletions

View File

@@ -1,3 +1,4 @@
use ssz_derive::{Decode, Encode};
use std::ops::RangeInclusive;
use types::{Eth1Data, Hash256};
@@ -17,7 +18,7 @@ pub enum Error {
/// A block of the eth1 chain.
///
/// Contains all information required to add a `BlockCache` entry.
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
#[derive(Debug, PartialEq, Clone, Eq, Hash, Encode, Decode)]
pub struct Eth1Block {
pub hash: Hash256,
pub timestamp: u64,
@@ -38,7 +39,7 @@ impl Eth1Block {
/// Stores block and deposit contract information and provides queries based upon the block
/// timestamp.
#[derive(Debug, PartialEq, Clone, Default)]
#[derive(Debug, PartialEq, Clone, Default, Encode, Decode)]
pub struct BlockCache {
blocks: Vec<Eth1Block>,
}

View File

@@ -1,5 +1,6 @@
use crate::DepositLog;
use eth2_hashing::hash;
use ssz_derive::{Decode, Encode};
use tree_hash::TreeHash;
use types::{Deposit, Hash256, DEPOSIT_TREE_DEPTH};
@@ -79,6 +80,48 @@ impl DepositDataTree {
}
}
#[derive(Encode, Decode, Clone)]
pub struct SszDepositCache {
logs: Vec<DepositLog>,
leaves: Vec<Hash256>,
deposit_contract_deploy_block: u64,
deposit_roots: Vec<Hash256>,
}
impl SszDepositCache {
pub fn from_deposit_cache(cache: &DepositCache) -> Self {
Self {
logs: cache.logs.clone(),
leaves: cache.leaves.clone(),
deposit_contract_deploy_block: cache.deposit_contract_deploy_block,
deposit_roots: cache.deposit_roots.clone(),
}
}
pub fn to_deposit_cache(&self) -> Result<DepositCache, String> {
let deposit_tree =
DepositDataTree::create(&self.leaves, self.leaves.len(), DEPOSIT_TREE_DEPTH);
// Check for invalid SszDepositCache conditions
if self.leaves.len() != self.logs.len() {
return Err("Invalid SszDepositCache: logs and leaves should have equal length".into());
}
// `deposit_roots` also includes the zero root
if self.leaves.len() + 1 != self.deposit_roots.len() {
return Err(
"Invalid SszDepositCache: deposit_roots length must be only one more than leaves"
.into(),
);
}
Ok(DepositCache {
logs: self.logs.clone(),
leaves: self.leaves.clone(),
deposit_contract_deploy_block: self.deposit_contract_deploy_block,
deposit_tree,
deposit_roots: self.deposit_roots.clone(),
})
}
}
/// Mirrors the merkle tree of deposits in the eth1 deposit contract.
///
/// Provides `Deposit` objects with merkle proofs included.

View File

@@ -1,5 +1,6 @@
use super::http::Log;
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use types::{DepositData, Hash256, PublicKeyBytes, SignatureBytes};
/// The following constants define the layout of bytes in the deposit contract `DepositEvent`. The
@@ -16,7 +17,7 @@ const INDEX_START: usize = SIG_START + 96 + 32;
const INDEX_LEN: usize = 8;
/// A fully parsed eth1 deposit contract log.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
pub struct DepositLog {
pub deposit_data: DepositData,
/// The block number of the log that included this `DepositData`.

View File

@@ -1,6 +1,11 @@
use crate::Config;
use crate::{block_cache::BlockCache, deposit_cache::DepositCache};
use crate::{
block_cache::BlockCache,
deposit_cache::{DepositCache, SszDepositCache},
};
use parking_lot::RwLock;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
#[derive(Default)]
pub struct DepositUpdater {
@@ -34,4 +39,47 @@ impl Inner {
self.block_cache.write().truncate(block_cache_truncation);
}
}
/// Encode the eth1 block and deposit cache as bytes.
pub fn as_bytes(&self) -> Vec<u8> {
let ssz_eth1_cache = SszEth1Cache::from_inner(&self);
ssz_eth1_cache.as_ssz_bytes()
}
/// Recover `Inner` given byte representation of eth1 deposit and block caches.
pub fn from_bytes(bytes: &[u8], config: Config) -> Result<Self, String> {
let ssz_cache = SszEth1Cache::from_ssz_bytes(bytes)
.map_err(|e| format!("Ssz decoding error: {:?}", e))?;
Ok(ssz_cache.to_inner(config)?)
}
}
#[derive(Encode, Decode, Clone)]
pub struct SszEth1Cache {
block_cache: BlockCache,
deposit_cache: SszDepositCache,
last_processed_block: Option<u64>,
}
impl SszEth1Cache {
pub fn from_inner(inner: &Inner) -> Self {
let deposit_updater = inner.deposit_cache.read();
let block_cache = inner.block_cache.read();
Self {
block_cache: (*block_cache).clone(),
deposit_cache: SszDepositCache::from_deposit_cache(&deposit_updater.cache),
last_processed_block: deposit_updater.last_processed_block,
}
}
pub fn to_inner(&self, config: Config) -> Result<Inner, String> {
Ok(Inner {
block_cache: RwLock::new(self.block_cache.clone()),
deposit_cache: RwLock::new(DepositUpdater {
cache: self.deposit_cache.to_deposit_cache()?,
last_processed_block: self.last_processed_block,
}),
config: RwLock::new(config),
})
}
}

View File

@@ -12,4 +12,5 @@ mod service;
pub use block_cache::{BlockCache, Eth1Block};
pub use deposit_cache::DepositCache;
pub use deposit_log::DepositLog;
pub use inner::SszEth1Cache;
pub use service::{BlockCacheUpdateOutcome, Config, DepositCacheUpdateOutcome, Error, Service};

View File

@@ -151,6 +151,20 @@ impl Service {
}
}
/// Return byte representation of deposit and block caches.
pub fn as_bytes(&self) -> Vec<u8> {
self.inner.as_bytes()
}
/// Recover the deposit and block caches from encoded bytes.
pub fn from_bytes(bytes: &[u8], config: Config, log: Logger) -> Result<Self, String> {
let inner = Inner::from_bytes(bytes, config)?;
Ok(Self {
inner: Arc::new(inner),
log,
})
}
/// Provides access to the block cache.
pub fn blocks(&self) -> &RwLock<BlockCache> {
&self.inner.block_cache