Skip to content

Commit

Permalink
Merge branch 'master' into willscott/feature/testnet
Browse files Browse the repository at this point in the history
  • Loading branch information
willscott authored Jul 6, 2018
2 parents 069e380 + a82f497 commit 8eeed57
Show file tree
Hide file tree
Showing 17 changed files with 1,057 additions and 27 deletions.
8 changes: 8 additions & 0 deletions clients/utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@ threadpool = { version = "1.7.1", optional = true }
time = { version = "0.1", optional = true }
histogram = { version = "0.6.8", optional = true }
grpcio = { git = "https://github.com/ekiden/grpc-rs", tag = "v0.3.0-ekiden2", features = ["openssl"] }
ekiden-consensus-base = { path = "../../consensus/base", version = "0.2.0-alpha" }
ekiden-consensus-client = { path = "../../consensus/client", version = "0.2.0-alpha" }
ekiden-core = { path = "../../core/common", version = "0.2.0-alpha" }
ekiden-db-trusted = { path = "../../db/trusted", version = "0.2.0-alpha" }
ekiden-di = { path = "../../di", version = "0.2.0-alpha" }
ekiden-epochtime = { path = "../../epochtime", version = "0.2.0-alpha" }
ekiden-ethereum = { path = "../../ethereum", version = "0.2.0-alpha" }
ekiden-instrumentation-prometheus = { path = "../../instrumentation/prometheus", version = "0.2.0-alpha" }
ekiden-scheduler-base = { path = "../../scheduler/base", version = "0.2.0-alpha" }
ekiden-scheduler-client = { path = "../../scheduler/client", version = "0.2.0-alpha" }
ekiden-storage-base = { path = "../../storage/base", version = "0.2.0-alpha" }
ekiden-storage-frontend = { path = "../../storage/frontend", version = "0.2.0-alpha" }
ekiden-registry-base = { path = "../../registry/base", version = "0.2.0-alpha" }
ekiden-registry-client = { path = "../../registry/client", version = "0.2.0-alpha" }
ekiden-rpc-client = { path = "../../rpc/client", version = "0.2.0-alpha" }
pretty_env_logger = "0.2"
log = "0.4"

[dev-dependencies]
ekiden-storage-dummy = { path = "../../storage/dummy", version = "0.2.0-alpha" }
6 changes: 6 additions & 0 deletions clients/utils/src/components.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! DI components for use in clients.
use ekiden_consensus_client;
use ekiden_core;
use ekiden_di::{Component, KnownComponents};
use ekiden_epochtime;
use ekiden_ethereum;
use ekiden_instrumentation_prometheus;
use ekiden_registry_client;
use ekiden_scheduler_client;
use ekiden_storage_frontend;

/// Register known components for dependency injection.
pub fn register_components(known_components: &mut KnownComponents) {
Expand All @@ -26,6 +28,10 @@ pub fn register_components(known_components: &mut KnownComponents) {
ekiden_scheduler_client::SchedulerClient::register(known_components);
// Entity registry.
ekiden_registry_client::EntityRegistryClient::register(known_components);
// Consensus.
ekiden_consensus_client::ConsensusClient::register(known_components);
// Storage.
ekiden_storage_frontend::StorageClient::register(known_components);
}

/// Create known component registry.
Expand Down
249 changes: 249 additions & 0 deletions clients/utils/src/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
//! Read-only database access with best-effort freshness.
use std::sync::Arc;
use std::sync::Mutex;

use ekiden_consensus_base::ConsensusBackend;
use ekiden_core;
use ekiden_core::bytes::B256;
use ekiden_core::bytes::H256;
use ekiden_core::environment::Environment;
use ekiden_core::error::Result;
use ekiden_core::futures::Future;
use ekiden_core::futures::Stream;
use ekiden_db_trusted::patricia_trie::PatriciaTrie;
use ekiden_db_trusted::Database;
use ekiden_di::Container;
use ekiden_storage_base::BackendIdentityMapper;
use ekiden_storage_base::StorageBackend;
use ekiden_storage_base::StorageMapper;

/// An implementation of the read methods of `Database`. Represents a single fixed state.
pub struct Snapshot {
/// The root hash that identifies the state in this snapshot.
root_hash: Option<H256>,
/// This handles access to the database and holds on to the storage mapper reference.
trie: PatriciaTrie,
}

impl Database for Snapshot {
fn contains_key(&self, key: &[u8]) -> bool {
self.get(key).is_some()
}

fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.trie.get(self.root_hash, key)
}

fn insert(&mut self, _key: &[u8], _value: &[u8]) -> Option<Vec<u8>> {
panic!("Can't insert into Snapshot")
}

fn remove(&mut self, _key: &[u8]) -> Option<Vec<u8>> {
panic!("Can't remove from Snapshot")
}

fn clear(&mut self) {
panic!("Can't clear Snapshot")
}
}

/// A holder of a (i) a consensus backend and (ii) a storage mapper, the two of which it uses to
/// create `Snapshot`s of recent (best-effort) states on demand.
pub struct Manager {
/// The latest root hash that we're aware of.
root_hash: Arc<Mutex<Option<H256>>>,
/// The storage mapper that we give to snapshots.
mapper: Arc<StorageMapper>,
/// For killing our consensus follower task.
blocks_kill_handle: ekiden_core::futures::KillHandle,
}

impl Manager {
pub fn new(
env: &Environment,
contract_id: B256,
consensus: &ConsensusBackend,
mapper: Arc<StorageMapper>,
) -> Self {
let root_hash = Arc::new(Mutex::new(None));
let root_hash_2 = root_hash.clone();
let (watch_blocks, blocks_kill_handle) = ekiden_core::futures::killable(
consensus.get_blocks(contract_id).for_each(move |block| {
let mut guard = root_hash.lock().unwrap();
*guard = Some(block.header.state_root);
Ok(())
}),
);
env.spawn(Box::new(watch_blocks.then(|r| {
match r {
// Block stream ended.
Ok(Ok(())) => {
warn!("manager block stream ended");
}
// Kill handle dropped.
Ok(Err(_ /* ekiden_core::futures::killable::Killed */)) => {}
// Block stream errored.
Err(e) => {
error!("manager block stream error: {}", e);
}
}
Ok(())
})));
Self {
root_hash: root_hash_2,
mapper,
blocks_kill_handle,
}
}

/// Make a `Manager` from an injected `ConsensusBackend` and an identity map over an injected
/// `StorageBackend`.
pub fn new_from_injected(contract_id: B256, container: &mut Container) -> Result<Self> {
let env: Arc<Environment> = container.inject()?;
let consensus: Arc<ConsensusBackend> = container.inject()?;
let storage: Arc<StorageBackend> = container.inject()?;
let mapper = Arc::new(BackendIdentityMapper::new(storage));
Ok(Self::new(
env.as_ref(),
contract_id,
consensus.as_ref(),
mapper,
))
}

pub fn get_snapshot(&self) -> Snapshot {
Snapshot {
root_hash: self.root_hash.lock().unwrap().clone(),
trie: PatriciaTrie::new(self.mapper.clone()),
}
}
}

impl Drop for Manager {
fn drop(&mut self) {
self.blocks_kill_handle.kill();
}
}

#[cfg(test)]
mod tests {
use std;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;

extern crate grpcio;

use ekiden_consensus_base::backend::ConsensusBackend;
use ekiden_consensus_base::backend::Event;
use ekiden_consensus_base::block::Block;
use ekiden_consensus_base::commitment::Commitment;
use ekiden_consensus_base::commitment::Reveal;
use ekiden_consensus_base::header::Header;
use ekiden_core;
use ekiden_core::bytes::B256;
use ekiden_core::environment::GrpcEnvironment;
use ekiden_core::futures::BoxFuture;
use ekiden_core::futures::BoxStream;
use ekiden_core::futures::Stream;
use ekiden_db_trusted::patricia_trie::PatriciaTrie;
use ekiden_db_trusted::Database;
use ekiden_storage_base::mapper::BackendIdentityMapper;
extern crate ekiden_storage_dummy;
use self::ekiden_storage_dummy::DummyStorageBackend;

/// A ConsensusBackend that adapts a simple `Block` stream.
struct MockConsensus {
blocks_rx: Mutex<Option<ekiden_core::futures::sync::mpsc::UnboundedReceiver<Block>>>,
}

impl ConsensusBackend for MockConsensus {
fn get_blocks(&self, _contract_id: B256) -> BoxStream<Block> {
Box::new(
self.blocks_rx
.lock()
.unwrap()
.take()
.expect("MockConsensus only supports one block stream")
.map_err(|()| unimplemented!()),
)
}

fn get_events(&self, _contract_id: B256) -> BoxStream<Event> {
unimplemented!()
}

fn commit(&self, _contract_id: B256, _commitment: Commitment) -> BoxFuture<()> {
unimplemented!()
}

fn reveal(&self, _contract_id: B256, _reveal: Reveal) -> BoxFuture<()> {
unimplemented!()
}

fn commit_many(&self, _contract_id: B256, _commitments: Vec<Commitment>) -> BoxFuture<()> {
unimplemented!()
}

fn reveal_many(&self, _contract_id: B256, _reveals: Vec<Reveal>) -> BoxFuture<()> {
unimplemented!()
}
}

#[test]
fn play() {
let grpc_environment = grpcio::EnvBuilder::new().build();
let environment = Arc::new(GrpcEnvironment::new(grpc_environment));
let contract_id = B256::from(*b"dummy contract------------------");
let storage = Arc::new(DummyStorageBackend::new());
let (blocks_tx, blocks_rx) = ekiden_core::futures::sync::mpsc::unbounded();
let consensus = Arc::new(MockConsensus {
blocks_rx: Mutex::new(Some(blocks_rx)),
});
let mapper = Arc::new(BackendIdentityMapper::new(storage));
let trie = PatriciaTrie::new(mapper.clone());
let manager = super::Manager::new(
environment.as_ref(),
contract_id,
consensus.as_ref(),
mapper,
);

let root_hash_before = trie.insert(None, b"changeme", b"before");
blocks_tx
.unbounded_send(Block {
header: Header {
state_root: root_hash_before,
..Default::default()
},
..Default::default()
})
.unwrap();
// Give the manager some time to pickup the new block.
std::thread::sleep(Duration::from_millis(1000));

// Check that a snapshot can read data.
let snapshot_before = manager.get_snapshot();
assert_eq!(&snapshot_before.get(b"changeme").unwrap(), b"before");

let root_hash_after = trie.insert(Some(root_hash_before), b"changeme", b"after");
blocks_tx
.unbounded_send(Block {
header: Header {
state_root: root_hash_after,
..Default::default()
},
..Default::default()
})
.unwrap();
std::thread::sleep(Duration::from_millis(1000));

// Check that a new snapshot has new data.
let snapshot_after = manager.get_snapshot();
assert_eq!(&snapshot_after.get(b"changeme").unwrap(), b"after");

// Check that the old snapshot is still consistent.
assert_eq!(&snapshot_before.get(b"changeme").unwrap(), b"before");
}
}
8 changes: 8 additions & 0 deletions clients/utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#[cfg(feature = "benchmark")]
extern crate histogram;
#[macro_use]
extern crate log;
extern crate pretty_env_logger;
#[cfg(feature = "benchmark")]
extern crate threadpool;
#[cfg(feature = "benchmark")]
extern crate time;

extern crate ekiden_consensus_base;
extern crate ekiden_consensus_client;
extern crate ekiden_core;
extern crate ekiden_db_trusted;
extern crate ekiden_di;
extern crate ekiden_epochtime;
extern crate ekiden_ethereum;
Expand All @@ -16,6 +20,8 @@ extern crate ekiden_registry_base;
extern crate ekiden_registry_client;
extern crate ekiden_scheduler_base;
extern crate ekiden_scheduler_client;
extern crate ekiden_storage_base;
extern crate ekiden_storage_frontend;

#[cfg(feature = "benchmark")]
pub mod benchmark;
Expand All @@ -24,3 +30,5 @@ pub mod components;
#[doc(hidden)]
#[macro_use]
pub mod macros;

pub mod db;
11 changes: 7 additions & 4 deletions db/trusted/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::sync::{Mutex, MutexGuard};
use ekiden_common::bytes::H256;
use ekiden_common::error::Result;
use ekiden_common::hash::empty_hash;
use ekiden_storage_base::mapper::BackendIdentityMapper;
#[cfg(not(target_env = "sgx"))]
use ekiden_storage_dummy::DummyStorageBackend;
use ekiden_storage_lru::LruCacheStorageBackend;
Expand Down Expand Up @@ -41,12 +42,14 @@ impl DatabaseHandle {
let backend = Arc::new(DummyStorageBackend::new());
#[cfg(target_env = "sgx")]
let backend = Arc::new(UntrustedStorageBackend::new());
let cached_backend = Arc::new(LruCacheStorageBackend::new(
backend,
Self::STORAGE_CACHE_SIZE,
));
let mapper = Arc::new(BackendIdentityMapper::new(cached_backend));

DatabaseHandle {
state: PatriciaTrie::new(Arc::new(LruCacheStorageBackend::new(
backend,
Self::STORAGE_CACHE_SIZE,
))),
state: PatriciaTrie::new(mapper),
root_hash: None,
}
}
Expand Down
10 changes: 7 additions & 3 deletions db/trusted/src/patricia_trie/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,14 +604,16 @@ mod test {

use self::test::Bencher;

use ekiden_storage_base::BackendIdentityMapper;
use ekiden_storage_dummy::DummyStorageBackend;

use super::*;

#[test]
fn test_basic_ops() {
let storage = Arc::new(DummyStorageBackend::new());
let tree = PatriciaTrie::new(storage);
let mapper = Arc::new(BackendIdentityMapper::new(storage));
let tree = PatriciaTrie::new(mapper);

assert_eq!(tree.get(None, b"foo"), None);
let new_root = tree.insert(None, b"foo", b"bar");
Expand Down Expand Up @@ -683,7 +685,8 @@ mod test {
#[test]
fn test_feather() {
let storage = Arc::new(DummyStorageBackend::new());
let tree = PatriciaTrie::new(storage);
let mapper = Arc::new(BackendIdentityMapper::new(storage));
let tree = PatriciaTrie::new(mapper);
let mut root_hash = None;
let mut key_buf = *b"\x83gStateDbhaccountsx(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
let val = b"don't embed don't embed don't embed don't embed don't embed";
Expand All @@ -702,7 +705,8 @@ mod test {
#[bench]
fn bench_feather(b: &mut Bencher) {
let storage = Arc::new(DummyStorageBackend::new());
let tree = PatriciaTrie::new(storage);
let mapper = Arc::new(BackendIdentityMapper::new(storage));
let tree = PatriciaTrie::new(mapper);
let mut root_hash = None;
let mut key_buf = *b"\x83gStateDbhaccountsx(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
let val = b"don't embed don't embed don't embed don't embed don't embed";
Expand Down
Loading

0 comments on commit 8eeed57

Please sign in to comment.