From 33d3a4d6339d258e753fcf9c2a2d0a7163e8b7b1 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 11 Jan 2016 11:51:31 +0100 Subject: [PATCH] Engine and Spec are now thread safe --- | 0 src/bin/client.rs | 5 +++-- src/blockchain.rs | 1 - src/builtin.rs | 5 +++++ src/client.rs | 8 +++++--- src/engine.rs | 2 +- src/ethereum/ethash.rs | 4 ++++ src/ethereum/mod.rs | 6 +++--- src/spec.rs | 41 +++++++++++++++++++++++++++++++---------- src/sync/mod.rs | 5 +++-- 10 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 diff --git a/ b/ new file mode 100644 index 000000000..e69de29bb diff --git a/src/bin/client.rs b/src/bin/client.rs index 09e414476..f8b29ac1c 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -11,15 +11,16 @@ use util::network::{NetworkService}; use ethcore::client::Client; use ethcore::sync::EthSync; use ethcore::ethereum; +use ethcore::ethereum::ethash::Ethash; fn main() { ::env_logger::init().ok(); let mut service = NetworkService::start().unwrap(); //TODO: replace with proper genesis and chain params. - let frontier = ethereum::new_frontier(); + let engine = Ethash::new_arc(ethereum::new_frontier()); let mut dir = env::temp_dir(); dir.push(H32::random().hex()); - let client = Arc::new(Client::new(&frontier.genesis_block(), &dir)); + let client = Arc::new(Client::new(engine, &dir)); EthSync::register(&mut service, client); loop { let mut cmd = String::new(); diff --git a/src/blockchain.rs b/src/blockchain.rs index 59a82858e..c102ba1b4 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -1,6 +1,5 @@ //! Fast access to blockchain data. -use std::sync::*; use util::*; use rocksdb::{DB, WriteBatch, Writable}; use header::*; diff --git a/src/builtin.rs b/src/builtin.rs index 309b8f3e5..0c1e60d5f 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -12,6 +12,11 @@ pub struct Builtin { pub execute: Box, } +// Rust does not mark closurer that do not capture as Sync +// We promise that all builtins are thread safe since they only operate on given input. +unsafe impl Sync for Builtin {} +unsafe impl Send for Builtin {} + impl fmt::Debug for Builtin { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") diff --git a/src/client.rs b/src/client.rs index 7f92a9a4b..019bdf797 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; use util::*; use blockchain::BlockChain; use views::BlockView; use error::ImportError; use header::BlockNumber; +use engine::Engine; /// General block status pub enum BlockStatus { @@ -95,13 +95,15 @@ pub trait BlockChainClient : Sync { /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. pub struct Client { chain: Arc, + _engine: Arc, } impl Client { - pub fn new(genesis: &[u8], path: &Path) -> Client { - let chain = Arc::new(BlockChain::new(genesis, path)); + pub fn new(engine: Arc, path: &Path) -> Client { + let chain = Arc::new(BlockChain::new(&engine.spec().genesis_block(), path)); Client { chain: chain.clone(), + _engine: engine.clone(), } } } diff --git a/src/engine.rs b/src/engine.rs index bfaf46348..64f85e079 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,7 +4,7 @@ use spec::Spec; /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. -pub trait Engine { +pub trait Engine : Sync + Send { /// The name of this engine. fn name(&self) -> &str; /// The version of this engine. Should be of the form diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 828441fd6..d260e21b4 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -13,6 +13,10 @@ impl Ethash { pub fn new_boxed(spec: Spec) -> Box { Box::new(Ethash{spec: spec}) } + + pub fn new_arc(spec: Spec) -> Arc { + Arc::new(Ethash{spec: spec}) + } } impl Engine for Ethash { diff --git a/src/ethereum/mod.rs b/src/ethereum/mod.rs index ed1950d64..b3efe4a3d 100644 --- a/src/ethereum/mod.rs +++ b/src/ethereum/mod.rs @@ -52,7 +52,7 @@ mod tests { fn morden() { let morden = new_morden(); - assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); + assert_eq!(morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); let genesis = morden.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); @@ -63,10 +63,10 @@ mod tests { fn frontier() { let frontier = new_frontier(); - assert_eq!(*frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()); + assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()); let genesis = frontier.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap()); let _ = frontier.to_engine(); } -} \ No newline at end of file +} diff --git a/src/spec.rs b/src/spec.rs index 0d821a6ad..acdfd2ecf 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -40,6 +40,27 @@ fn json_to_rlp_map(json: &Json) -> HashMap { }) } +//TODO: add code and data +#[derive(Debug)] +/// Genesis account data. Does no thave a DB overlay cache +pub struct GenesisAccount { + // Balance of the account. + balance: U256, + // Nonce of the account. + nonce: U256, +} + +impl GenesisAccount { + pub fn rlp(&self) -> Bytes { + let mut stream = RlpStream::new_list(4); + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&SHA3_NULL_RLP); + stream.append(&SHA3_EMPTY); + stream.out() + } +} + /// Parameters for a block chain; includes both those intrinsic to the design of the /// chain and those to be interpreted by the active chain engine. #[derive(Debug)] @@ -62,12 +83,12 @@ pub struct Spec { pub gas_used: U256, pub timestamp: u64, pub extra_data: Bytes, - pub genesis_state: HashMap, + pub genesis_state: HashMap, pub seal_fields: usize, pub seal_rlp: Bytes, // May be prepopulated if we know this in advance. - state_root_memo: RefCell>, + state_root_memo: RwLock>, } impl Spec { @@ -82,11 +103,11 @@ impl Spec { } /// Return the state root for the genesis state, memoising accordingly. - pub fn state_root(&self) -> Ref { - if self.state_root_memo.borrow().is_none() { - *self.state_root_memo.borrow_mut() = Some(sec_trie_root(self.genesis_state.iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect())); + pub fn state_root(&self) -> H256 { + if self.state_root_memo.read().unwrap().is_none() { + *self.state_root_memo.write().unwrap() = Some(sec_trie_root(self.genesis_state.iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect())); } - Ref::map(self.state_root_memo.borrow(), |x|x.as_ref().unwrap()) + self.state_root_memo.read().unwrap().as_ref().unwrap().clone() } pub fn genesis_header(&self) -> Header { @@ -113,7 +134,7 @@ impl Spec { let r = Rlp::new(&seal); (0..self.seal_fields).map(|i| r.at(i).as_raw().to_vec()).collect() }, - hash: RefCell::new(None) + hash: RefCell::new(None), } } @@ -149,7 +170,7 @@ impl Spec { // let nonce = if let Some(&Json::String(ref n)) = acc.find("nonce") {U256::from_dec_str(n).unwrap_or(U256::from(0))} else {U256::from(0)}; // TODO: handle code & data if they exist. if balance.is_some() || nonce.is_some() { - state.insert(addr, Account::new_basic(balance.unwrap_or(U256::from(0)), nonce.unwrap_or(U256::from(0)))); + state.insert(addr, GenesisAccount { balance: balance.unwrap_or(U256::from(0)), nonce: nonce.unwrap_or(U256::from(0)) }); } } } @@ -186,7 +207,7 @@ impl Spec { genesis_state: state, seal_fields: seal_fields, seal_rlp: seal_rlp, - state_root_memo: RefCell::new(genesis.find("stateRoot").and_then(|_| genesis["stateRoot"].as_string()).map(|s| H256::from_str(&s[2..]).unwrap())), + state_root_memo: RwLock::new(genesis.find("stateRoot").and_then(|_| genesis["stateRoot"].as_string()).map(|s| H256::from_str(&s[2..]).unwrap())), } } @@ -228,7 +249,7 @@ mod tests { fn test_chain() { let test_spec = Spec::new_test(); - assert_eq!(*test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); + assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); let genesis = test_spec.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); diff --git a/src/sync/mod.rs b/src/sync/mod.rs index a070b2d59..aca40ffba 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -13,12 +13,13 @@ /// use ethcore::client::Client; /// use ethcore::sync::EthSync; /// use ethcore::ethereum; +/// use ethcore::ethereum::ethash::Ethash; /// /// fn main() { /// let mut service = NetworkService::start().unwrap(); -/// let frontier = ethereum::new_frontier(); +/// let engine = Ethash::new_arc(ethereum::new_frontier()); /// let dir = env::temp_dir(); -/// let client = Arc::new(Client::new(&frontier.genesis_block(), &dir)); +/// let client = Arc::new(Client::new(engine, &dir)); /// EthSync::register(&mut service, client); /// } /// ```