diff --git a/.travis.yml b/.travis.yml index b7124b2df..d051971bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ env: global: # GH_TOKEN - secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw= - - TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer" + - TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer -p bigint" - ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}" - KCOV_FEATURES="" - KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests target/kcov" diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 2edbf87ae..2db4ffcc0 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -214,8 +214,8 @@ impl Account { } /// Commit the `storage_overlay` to the backing DB and update `storage_root`. - pub fn commit_storage(&mut self, db: &mut AccountDBMut) { - let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root) + pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) { + let mut t = trie_factory.from_existing(db, &mut self.storage_root) .expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \ SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \ using it will not fail."); @@ -275,7 +275,7 @@ mod tests { let rlp = { let mut a = Account::new_contract(69.into(), 0.into()); a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); - a.commit_storage(&mut db); + a.commit_storage(&Default::default(), &mut db); a.init_code(vec![]); a.commit_code(&mut db); a.rlp() @@ -313,7 +313,7 @@ mod tests { let mut db = AccountDBMut::new(&mut db, &Address::new()); a.set_storage(0.into(), 0x1234.into()); assert_eq!(a.storage_root(), None); - a.commit_storage(&mut db); + a.commit_storage(&Default::default(), &mut db); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); } @@ -323,11 +323,11 @@ mod tests { let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); a.set_storage(0.into(), 0x1234.into()); - a.commit_storage(&mut db); + a.commit_storage(&Default::default(), &mut db); a.set_storage(1.into(), 0x1234.into()); - a.commit_storage(&mut db); + a.commit_storage(&Default::default(), &mut db); a.set_storage(1.into(), 0.into()); - a.commit_storage(&mut db); + a.commit_storage(&Default::default(), &mut db); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); } diff --git a/ethcore/src/basic_authority.rs b/ethcore/src/basic_authority.rs index 4426d3059..e487548a3 100644 --- a/ethcore/src/basic_authority.rs +++ b/ethcore/src/basic_authority.rs @@ -254,7 +254,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, None, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); assert!(b.try_seal(engine.deref(), seal).is_ok()); diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 13a2024d9..9453e1350 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -222,6 +222,7 @@ impl<'x> OpenBlock<'x> { pub fn new( engine: &'x Engine, vm_factory: &'x EvmFactory, + trie_factory: TrieFactory, tracing: bool, db: Box, parent: &Header, @@ -231,7 +232,7 @@ impl<'x> OpenBlock<'x> { gas_range_target: (U256, U256), extra_data: Bytes, ) -> Result { - let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())); + let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory)); let mut r = OpenBlock { block: ExecutedBlock::new(state, tracing), engine: engine, @@ -481,16 +482,17 @@ pub fn enact( parent: &Header, last_hashes: LastHashes, dao_rescue_block_gas_limit: Option, - vm_factory: &EvmFactory + vm_factory: &EvmFactory, + trie_factory: TrieFactory, ) -> Result { { if ::log::max_log_level() >= ::log::LogLevel::Trace { - let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce())); + let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone())); trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author())); } } - let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone())); + let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone())); b.set_difficulty(*header.difficulty()); b.set_gas_limit(*header.gas_limit()); b.set_timestamp(header.timestamp()); @@ -509,11 +511,12 @@ pub fn enact_bytes( parent: &Header, last_hashes: LastHashes, dao_rescue_block_gas_limit: Option, - vm_factory: &EvmFactory + vm_factory: &EvmFactory, + trie_factory: TrieFactory, ) -> Result { let block = BlockView::new(block_bytes); let header = block.header(); - enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory) + enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory, trie_factory) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header @@ -526,10 +529,11 @@ pub fn enact_verified( parent: &Header, last_hashes: LastHashes, dao_rescue_block_gas_limit: Option, - vm_factory: &EvmFactory + vm_factory: &EvmFactory, + trie_factory: TrieFactory, ) -> Result { let view = BlockView::new(&block.bytes); - enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory) + enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory, trie_factory) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards @@ -542,10 +546,11 @@ pub fn enact_and_seal( parent: &Header, last_hashes: LastHashes, dao_rescue_block_gas_limit: Option, - vm_factory: &EvmFactory + vm_factory: &EvmFactory, + trie_factory: TrieFactory, ) -> Result { let header = BlockView::new(block_bytes).header_view(); - Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)).seal(engine, header.seal()))) + Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory, trie_factory)).seal(engine, header.seal()))) } #[cfg(test)] @@ -565,7 +570,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); let _ = b.seal(engine.deref(), vec![]); } @@ -581,7 +586,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() + let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() .close_and_lock().seal(engine.deref(), vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); @@ -589,7 +594,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); - let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap(); + let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default(), Default::default()).unwrap(); assert_eq!(e.rlp_bytes(), orig_bytes); @@ -609,7 +614,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); let vm_factory = Default::default(); - let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle1_header = Header::new(); uncle1_header.extra_data = b"uncle1".to_vec(); let mut uncle2_header = Header::new(); @@ -624,7 +629,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); - let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap(); + let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default(), Default::default()).unwrap(); let bytes = e.rlp_bytes(); assert_eq!(bytes, orig_bytes); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 522f1c457..6f3249f4c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -96,6 +96,7 @@ pub struct Client { panic_handler: Arc, verifier: Box, vm_factory: Arc, + trie_factory: TrieFactory, miner: Arc, io_channel: IoChannel, queue_transactions: AtomicUsize, @@ -177,6 +178,7 @@ impl Client { panic_handler: panic_handler, verifier: verification::new(config.verifier_type), vm_factory: Arc::new(EvmFactory::new(config.vm_type)), + trie_factory: TrieFactory::new(config.trie_spec), miner: miner, io_channel: message_channel, queue_transactions: AtomicUsize::new(0), @@ -235,7 +237,7 @@ impl Client { let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let db = self.state_db.lock().unwrap().boxed_clone(); - let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory); + let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory, self.trie_factory.clone()); if let Err(e) = enact_result { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -350,7 +352,7 @@ impl Client { imported } - fn commit_block(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain { + fn commit_block(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain { let number = block.header().number(); // Are we committing an era? let ancient = if number >= HISTORY { @@ -420,13 +422,17 @@ impl Client { let root = HeaderView::new(&header).state_root(); - State::from_existing(db, root, self.engine.account_start_nonce()).ok() + State::from_existing(db, root, self.engine.account_start_nonce(), self.trie_factory.clone()).ok() }) } /// Get a copy of the best block's state. pub fn state(&self) -> State { - State::from_existing(self.state_db.lock().unwrap().boxed_clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce()) + State::from_existing( + self.state_db.lock().unwrap().boxed_clone(), + HeaderView::new(&self.best_block_header()).state_root(), + self.engine.account_start_nonce(), + self.trie_factory.clone()) .expect("State root of best block header always valid.") } @@ -815,6 +821,7 @@ impl MiningBlockChainClient for Client { let mut open_block = OpenBlock::new( engine, &self.vm_factory, + self.trie_factory.clone(), false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. self.state_db.lock().unwrap().boxed_clone(), &self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"), diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 52a875a2f..6cb34c151 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -20,6 +20,7 @@ pub use trace::{Config as TraceConfig, Switch}; pub use evm::VMType; pub use verification::VerifierType; use util::journaldb; +use util::trie::TrieSpec; /// Client state db compaction profile #[derive(Debug, PartialEq)] @@ -45,6 +46,8 @@ pub struct ClientConfig { pub tracing: TraceConfig, /// VM type. pub vm_type: VMType, + /// Trie type. + pub trie_spec: TrieSpec, /// The JournalDB ("pruning") algorithm to use. pub pruning: journaldb::Algorithm, /// The name of the client instance. diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 84c2a9608..c438c33ca 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -325,7 +325,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); } @@ -340,7 +340,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let mut b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle = Header::new(); let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106"); uncle.author = uncle_author.clone(); diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index ed75576c6..d40ee8983 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -67,7 +67,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); - let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce()).unwrap(); + let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap(); assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64)); assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64)); assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64)); diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index fe1877116..215506607 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -267,6 +267,10 @@ impl Miner { let difficulty = *block.block().fields().header.difficulty(); let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h); sealing_work.push(block); + // If push notifications are enabled we assume all work items are used. + if self.work_poster.is_some() && is_new { + sealing_work.use_last_ref(); + } (Some((pow_hash, difficulty, number)), is_new) } else { (None, false) diff --git a/ethcore/src/miner/work_notify.rs b/ethcore/src/miner/work_notify.rs index a153be79f..ea2f6140e 100644 --- a/ethcore/src/miner/work_notify.rs +++ b/ethcore/src/miner/work_notify.rs @@ -52,10 +52,10 @@ impl WorkPoster { } fn create_client() -> Client { - let client = Client::::configure() + Client::::configure() .keep_alive(true) - .build().expect("Error creating HTTP client") as Client; - client + .build() + .expect("Error creating HTTP client") } pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { @@ -63,8 +63,10 @@ impl WorkPoster { let target = Ethash::difficulty_to_boundary(&difficulty); let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number); let seed_hash = H256::from_slice(&seed_hash[..]); - let body = format!(r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#, - pow_hash.hex(), seed_hash.hex(), target.hex(), number); + let body = format!( + r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#, + pow_hash.hex(), seed_hash.hex(), target.hex(), number + ); let mut client = self.client.lock().unwrap(); for u in &self.urls { if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) { @@ -104,12 +106,12 @@ impl hyper::client::Handler for PostHandler { } fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder) -> Next { - Next::end() + Next::end() } - fn on_error(&mut self, err: hyper::Error) -> Next { + fn on_error(&mut self, err: hyper::Error) -> Next { trace!("Error posting work data: {}", err); Next::end() - } + } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 0c086ffc3..92a501e80 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -42,6 +42,7 @@ pub struct State { cache: RefCell>>, snapshots: RefCell>>>>, account_start_nonce: U256, + trie_factory: TrieFactory, } const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ @@ -50,11 +51,11 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v impl State { /// Creates new state with empty state root #[cfg(test)] - pub fn new(mut db: Box, account_start_nonce: U256) -> State { + pub fn new(mut db: Box, account_start_nonce: U256, trie_factory: TrieFactory) -> State { let mut root = H256::new(); { // init trie and reset root too null - let _ = SecTrieDBMut::new(db.as_hashdb_mut(), &mut root); + let _ = trie_factory.create(db.as_hashdb_mut(), &mut root); } State { @@ -63,22 +64,26 @@ impl State { cache: RefCell::new(HashMap::new()), snapshots: RefCell::new(Vec::new()), account_start_nonce: account_start_nonce, + trie_factory: trie_factory, } } /// Creates new state with existing state root - pub fn from_existing(db: Box, root: H256, account_start_nonce: U256) -> Result { + pub fn from_existing(db: Box, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result { if !db.as_hashdb().contains(&root) { - Err(TrieError::InvalidStateRoot) - } else { - Ok(State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - snapshots: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - }) + return Err(TrieError::InvalidStateRoot); } + + let state = State { + db: db, + root: root, + cache: RefCell::new(HashMap::new()), + snapshots: RefCell::new(Vec::new()), + account_start_nonce: account_start_nonce, + trie_factory: trie_factory, + }; + + Ok(state) } /// Create a recoverable snaphot of this state @@ -156,7 +161,7 @@ impl State { /// Determine whether an account exists. pub fn exists(&self, a: &Address) -> bool { - let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); self.cache.borrow().get(&a).unwrap_or(&None).is_some() || db.contains(&a) } @@ -242,7 +247,10 @@ impl State { for a in &addresses { if self.code(a).map_or(false, |c| c.sha3() == broken_dao) { // Figure out if the balance has been reduced. - let maybe_original = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR).get(&a).map(Account::from_rlp); + let maybe_original = self.trie_factory + .readonly(self.db.as_hashdb(), &self.root) + .expect(SEC_TRIE_DB_UNWRAP_STR) + .get(&a).map(Account::from_rlp); if maybe_original.map_or(false, |original| *original.balance() > self.balance(a)) { return Err(Error::Transaction(TransactionError::DAORescue)); } @@ -262,14 +270,14 @@ impl State { /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. #[cfg_attr(feature="dev", allow(match_ref_pats))] - pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap>) { + pub fn commit_into(trie_factory: &TrieFactory, db: &mut HashDB, root: &mut H256, accounts: &mut HashMap>) { // first, commit the sub trees. // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? for (address, ref mut a) in accounts.iter_mut() { match a { &mut&mut Some(ref mut account) => { let mut account_db = AccountDBMut::new(db, address); - account.commit_storage(&mut account_db); + account.commit_storage(trie_factory, &mut account_db); account.commit_code(&mut account_db); } &mut&mut None => {} @@ -277,7 +285,7 @@ impl State { } { - let mut trie = SecTrieDBMut::from_existing(db, root).unwrap(); + let mut trie = trie_factory.from_existing(db, root).unwrap(); for (address, ref a) in accounts.iter() { match **a { Some(ref account) => trie.insert(address, &account.rlp()), @@ -290,7 +298,7 @@ impl State { /// Commits our cached account changes into the trie. pub fn commit(&mut self) { assert!(self.snapshots.borrow().is_empty()); - Self::commit_into(self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut()); + Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut()); } #[cfg(test)] @@ -340,7 +348,7 @@ impl State { fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option { let have_key = self.cache.borrow().contains_key(a); if !have_key { - let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); self.insert_cache(a, db.get(&a).map(Account::from_rlp)) } if require_code { @@ -361,7 +369,7 @@ impl State { fn require_or_from<'a, F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&self, a: &Address, require_code: bool, default: F, not_default: G) -> &'a mut Account { let have_key = self.cache.borrow().contains_key(a); if !have_key { - let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); self.insert_cache(a, db.get(&a).map(Account::from_rlp)) } else { self.note_cache(a); @@ -396,6 +404,7 @@ impl Clone for State { cache: RefCell::new(self.cache.borrow().clone()), snapshots: RefCell::new(self.snapshots.borrow().clone()), account_start_nonce: self.account_start_nonce.clone(), + trie_factory: self.trie_factory.clone(), } } } @@ -1179,7 +1188,7 @@ fn code_from_database() { state.drop() }; - let state = State::from_existing(db, root, U256::from(0u8)).unwrap(); + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); } @@ -1194,7 +1203,7 @@ fn storage_at_from_database() { state.drop() }; - let s = State::from_existing(db, root, U256::from(0u8)).unwrap(); + let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64))); } @@ -1211,7 +1220,7 @@ fn get_from_database() { state.drop() }; - let state = State::from_existing(db, root, U256::from(0u8)).unwrap(); + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.nonce(&a), U256::from(1u64)); } @@ -1244,7 +1253,7 @@ fn remove_from_database() { }; let (root, db) = { - let mut state = State::from_existing(db, root, U256::from(0u8)).unwrap(); + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.exists(&a), true); assert_eq!(state.nonce(&a), U256::from(1u64)); state.kill_account(&a); @@ -1254,7 +1263,7 @@ fn remove_from_database() { state.drop() }; - let state = State::from_existing(db, root, U256::from(0u8)).unwrap(); + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.exists(&a), false); assert_eq!(state.nonce(&a), U256::from(0u64)); } diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 70a644896..ad8058ac6 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -175,6 +175,7 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe let mut b = OpenBlock::new( test_engine.deref(), &vm_factory, + Default::default(), false, db, &last_header, @@ -315,7 +316,7 @@ pub fn get_temp_state() -> GuardedTempResult { let journal_db = get_temp_journal_db_in(temp.as_path()); GuardedTempResult { _temp: temp, - result: Some(State::new(journal_db, U256::from(0u8))) + result: Some(State::new(journal_db, U256::from(0), Default::default())), } } @@ -325,7 +326,7 @@ pub fn get_temp_journal_db_in(path: &Path) -> Box { pub fn get_temp_state_in(path: &Path) -> State { let journal_db = get_temp_journal_db_in(path); - State::new(journal_db, U256::from(0u8)) + State::new(journal_db, U256::from(0), Default::default()) } pub fn get_good_dummy_block_seq(count: usize) -> Vec { diff --git a/ipc/codegen/Cargo.toml b/ipc/codegen/Cargo.toml index 4d83b65e1..649882c51 100644 --- a/ipc/codegen/Cargo.toml +++ b/ipc/codegen/Cargo.toml @@ -22,5 +22,5 @@ aster = { version = "0.17", default-features = false } clippy = { version = "^0.*", optional = true } quasi = { version = "0.11", default-features = false } quasi_macros = { version = "0.11", optional = true } -syntex = { version = "*", optional = true } -syntex_syntax = { version = "*", optional = true } +syntex = { version = "0.33", optional = true } +syntex_syntax = { version = "0.33", optional = true } diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs index 9dd6a7b32..672132c9f 100644 --- a/ipc/codegen/src/codegen.rs +++ b/ipc/codegen/src/codegen.rs @@ -497,9 +497,9 @@ fn client_generics(builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> .build() } -fn client_qualified_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P { +fn client_qualified_ident(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P { let generics = client_generics(builder, interface_map); - aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(builder)) + aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item)) .with_generics(generics).build() .build() } @@ -515,7 +515,7 @@ fn client_phantom_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMa /// for say `Service` it generates `ServiceClient` fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) { let generics = client_generics(builder, interface_map); - let client_short_ident = interface_map.ident_map.client_ident(builder); + let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item); let phantom = client_phantom_ident(builder, interface_map); let client_struct_item = quote_item!(cx, @@ -547,9 +547,9 @@ fn push_with_socket_client_implementation( push: &mut FnMut(Annotatable)) { let generics = client_generics(builder, interface_map); - let client_ident = client_qualified_ident(builder, interface_map); + let client_ident = client_qualified_ident(cx, builder, interface_map); let where_clause = &generics.where_clause; - let client_short_ident = interface_map.ident_map.client_ident(builder); + let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item); let implement = quote_item!(cx, impl $generics ::ipc::WithSocket for $client_ident $where_clause { @@ -578,7 +578,7 @@ fn push_client_implementation( .collect::>>(); let generics = client_generics(builder, interface_map); - let client_ident = client_qualified_ident(builder, interface_map); + let client_ident = client_qualified_ident(cx, builder, interface_map); let where_clause = &generics.where_clause; let handshake_item = quote_impl_item!(cx, @@ -682,6 +682,52 @@ fn implement_handshake_arm( } +fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { + match lit.node { + ast::LitKind::Str(ref s, _) => Ok(format!("{}", s)), + _ => { + cx.span_err( + lit.span, + &format!("ipc client_ident annotation `{}` must be a string, not `{}`", + name, + ::syntax::print::pprust::lit_to_string(lit))); + + return Err(()); + } + } +} + +pub fn get_ipc_meta_items(attr: &ast::Attribute) -> Option<&[P]> { + match attr.node.value.node { + ast::MetaItemKind::List(ref name, ref items) if name == &"ipc" => { + Some(items) + } + _ => None + } +} + +fn client_ident_renamed(cx: &ExtCtxt, item: &ast::Item) -> Option { + for meta_items in item.attrs().iter().filter_map(get_ipc_meta_items) { + for meta_item in meta_items { + let span = meta_item.span; + match meta_item.node { + ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"client_ident" => { + if let Ok(s) = get_str_from_lit(cx, name, lit) { + return Some(s); + } + } + _ => { + cx.span_err( + meta_item.span, + &format!("unknown client_ident container attribute `{}`", + ::syntax::print::pprust::meta_item_to_string(meta_item))); + } + } + } + } + None +} + struct InterfaceMap { pub original_item: Item, pub item: P, @@ -700,8 +746,13 @@ impl IdentMap { builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path))) } - fn client_ident(&self, builder: &aster::AstBuilder) -> Ident { - builder.id(format!("{}Client", self.original_path.segments[0].identifier)) + fn client_ident(&self, cx: &ExtCtxt, builder: &aster::AstBuilder, item: &ast::Item) -> Ident { + if let Some(new_name) = client_ident_renamed(cx, item) { + builder.id(new_name) + } + else { + builder.id(format!("{}Client", self.original_path.segments[0].identifier)) + } } fn qualified_ident(&self, builder: &aster::AstBuilder) -> Ident { diff --git a/ipc/codegen/src/lib.rs b/ipc/codegen/src/lib.rs index 126aa3a85..afa7979d0 100644 --- a/ipc/codegen/src/lib.rs +++ b/ipc/codegen/src/lib.rs @@ -50,11 +50,36 @@ include!("lib.rs.in"); #[cfg(feature = "with-syntex")] pub fn register(reg: &mut syntex::Registry) { + use syntax::{ast, fold}; + + #[cfg(feature = "with-syntex")] + fn strip_attributes(krate: ast::Crate) -> ast::Crate { + struct StripAttributeFolder; + impl fold::Folder for StripAttributeFolder { + fn fold_attribute(&mut self, attr: ast::Attribute) -> Option { + match attr.node.value.node { + ast::MetaItemKind::List(ref n, _) if n == &"ipc" => { return None; } + _ => {} + } + + Some(attr) + } + + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + fold::noop_fold_mac(mac, self) + } + } + + fold::Folder::fold_crate(&mut StripAttributeFolder, krate) + } + reg.add_attr("feature(custom_derive)"); reg.add_attr("feature(custom_attribute)"); reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation); reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation); + + reg.add_post_expansion_pass(strip_attributes); } #[cfg(not(feature = "with-syntex"))] @@ -67,4 +92,6 @@ pub fn register(reg: &mut rustc_plugin::Registry) { syntax::parse::token::intern("derive_Binary"), syntax::ext::base::MultiDecorator( Box::new(serialization::expand_serialization_implementation))); + + reg.register_attribute("ipc".to_owned(), AttributeType::Normal); } diff --git a/ipc/rpc/src/binary.rs b/ipc/rpc/src/binary.rs index 62a3c43b0..f33d640f4 100644 --- a/ipc/rpc/src/binary.rs +++ b/ipc/rpc/src/binary.rs @@ -19,7 +19,7 @@ use util::bytes::Populatable; use util::numbers::{U256, U512, H256, H2048, Address}; use std::mem; -use std::collections::VecDeque; +use std::collections::{VecDeque, BTreeMap}; use std::ops::Range; #[derive(Debug)] @@ -139,6 +139,92 @@ impl BinaryConvertable for Result BinaryConvertable for BTreeMap where K : BinaryConvertable + Ord, V: BinaryConvertable { + fn size(&self) -> usize { + 0usize + match K::len_params() { + 0 => mem::size_of::() * self.len(), + _ => self.iter().fold(0usize, |acc, (k, _)| acc + k.size()) + } + match V::len_params() { + 0 => mem::size_of::() * self.len(), + _ => self.iter().fold(0usize, |acc, (_, v)| acc + v.size()) + } + } + + fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> { + let mut offset = 0usize; + for (key, val) in self.iter() { + let key_size = match K::len_params() { + 0 => mem::size_of::(), + _ => { let size = key.size(); length_stack.push_back(size); size } + }; + let val_size = match K::len_params() { + 0 => mem::size_of::(), + _ => { let size = val.size(); length_stack.push_back(size); size } + }; + + if key_size > 0 { + let item_end = offset + key_size; + try!(key.to_bytes(&mut buffer[offset..item_end], length_stack)); + offset = item_end; + } + + if val_size > 0 { + let item_end = offset + key_size; + try!(val.to_bytes(&mut buffer[offset..item_end], length_stack)); + offset = item_end; + } + } + Ok(()) + } + + fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result { + let mut index = 0; + let mut result = Self::new(); + + if buffer.len() == 0 { return Ok(result); } + + loop { + let key_size = match K::len_params() { + 0 => mem::size_of::(), + _ => try!(length_stack.pop_front().ok_or(BinaryConvertError)), + }; + let key = if key_size == 0 { + try!(K::from_empty_bytes()) + } else { + try!(K::from_bytes(&buffer[index..index+key_size], length_stack)) + }; + index = index + key_size; + + let val_size = match V::len_params() { + 0 => mem::size_of::(), + _ => try!(length_stack.pop_front().ok_or(BinaryConvertError)), + }; + let val = if val_size == 0 { + try!(V::from_empty_bytes()) + } else { + try!(V::from_bytes(&buffer[index..index+val_size], length_stack)) + }; + result.insert(key, val); + index = index + val_size; + + if index == buffer.len() { break; } + if index > buffer.len() { + return Err(BinaryConvertError) + } + } + + Ok(result) + } + + fn from_empty_bytes() -> Result { + Ok(Self::new()) + } + + fn len_params() -> usize { + 1 + } +} + impl BinaryConvertable for Vec where T: BinaryConvertable { fn size(&self) -> usize { match T::len_params() { @@ -652,3 +738,18 @@ fn serialize_err_opt_vec_in_out() { assert!(vec.is_ok()); } + +#[test] +fn serialize_btree() { + use std::io::{Cursor, SeekFrom, Seek}; + + let mut buff = Cursor::new(Vec::new()); + let mut btree = BTreeMap::new(); + btree.insert(1u64, 5u64); + serialize_into(&btree, &mut buff).unwrap(); + + buff.seek(SeekFrom::Start(0)).unwrap(); + let res = deserialize_from::, _>(&mut buff).unwrap(); + + assert_eq!(res[&1u64], 5u64); +} diff --git a/ipc/tests/Cargo.toml b/ipc/tests/Cargo.toml index f7a5ee9bd..d9c80145d 100644 --- a/ipc/tests/Cargo.toml +++ b/ipc/tests/Cargo.toml @@ -16,5 +16,5 @@ ethcore-ipc-nano = { path = "../nano" } ethcore-util = { path = "../../util" } [build-dependencies] -syntex = "*" +syntex = "0.33" ethcore-ipc-codegen = { path = "../codegen" } diff --git a/ipc/tests/build.rs b/ipc/tests/build.rs index e498e3405..6538f56e1 100644 --- a/ipc/tests/build.rs +++ b/ipc/tests/build.rs @@ -58,6 +58,23 @@ pub fn main() { registry.expand("", &src, &dst).unwrap(); } + // rpc pass + if { + let src = Path::new("with_attrs.rs.in"); + let dst = Path::new(&out_dir).join("with_attrs_ipc.rs"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &src, &dst).is_ok() + } + // serialization pass + { + let src = Path::new(&out_dir).join("with_attrs_ipc.rs"); + let dst = Path::new(&out_dir).join("with_attrs_cg.rs"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } + // rpc pass { let src = Path::new("binary.rs.in"); diff --git a/ipc/tests/examples.rs b/ipc/tests/examples.rs index 99cf385fa..b4159f196 100644 --- a/ipc/tests/examples.rs +++ b/ipc/tests/examples.rs @@ -86,7 +86,7 @@ mod tests { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0], - service_client.socket().borrow().write_buffer.clone()); + service_client.socket().write().unwrap().write_buffer.clone()); assert_eq!(10, result); } @@ -103,7 +103,7 @@ mod tests { 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, 10, 0, 0, 0], service_client.socket().borrow().write_buffer.clone()); + 5, 0, 0, 0, 10, 0, 0, 0], service_client.socket().write().unwrap().write_buffer.clone()); assert_eq!(10, result); } @@ -145,7 +145,7 @@ mod tests { // items 3, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0], - service_client.socket().borrow().write_buffer.clone()); + service_client.socket().write().unwrap().write_buffer.clone()); assert_eq!(true, result); } diff --git a/ipc/tests/over_nano.rs b/ipc/tests/over_nano.rs index 36ad17f2a..bdeaec3d5 100644 --- a/ipc/tests/over_nano.rs +++ b/ipc/tests/over_nano.rs @@ -18,6 +18,7 @@ mod tests { use super::super::service::*; + use super::super::with_attrs::PrettyNamedClient; use nanoipc; use std::sync::Arc; use std::io::Write; @@ -43,6 +44,12 @@ mod tests { assert!(client.is_ok()); } + #[test] + fn can_create_renamed_client() { + let client = nanoipc::init_duplex_client::>("ipc:///tmp/parity-nano-test10.ipc"); + assert!(client.is_ok()); + } + #[test] fn can_call_handshake() { let url = "ipc:///tmp/parity-test-nano-20.ipc"; diff --git a/ipc/tests/run.rs b/ipc/tests/run.rs index cdda5275b..ab0041899 100644 --- a/ipc/tests/run.rs +++ b/ipc/tests/run.rs @@ -28,3 +28,4 @@ mod examples; mod over_nano; mod nested; mod binary; +mod with_attrs; diff --git a/ipc/tests/with_attrs.rs b/ipc/tests/with_attrs.rs new file mode 100644 index 000000000..e9b3e0d76 --- /dev/null +++ b/ipc/tests/with_attrs.rs @@ -0,0 +1,18 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues +include!(concat!(env!("OUT_DIR"), "/with_attrs_cg.rs")); diff --git a/ipc/tests/with_attrs.rs.in b/ipc/tests/with_attrs.rs.in new file mode 100644 index 000000000..0168f3a72 --- /dev/null +++ b/ipc/tests/with_attrs.rs.in @@ -0,0 +1,34 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::RwLock; +use std::ops::*; +use ipc::IpcConfig; +use std::mem; +use ipc::binary::BinaryConvertError; +use std::collections::VecDeque; + +pub struct BadlyNamedService; + +#[derive(Ipc)] +#[ipc(client_ident="PrettyNamedClient")] +impl BadlyNamedService { + fn is_zero(&self, x: u64) -> bool { + x == 0 + } +} + +impl ::ipc::IpcConfig for BadlyNamedService {} diff --git a/parity/cli.rs b/parity/cli.rs index 7ebbcb0aa..ef9ce8ce3 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -203,6 +203,7 @@ Database Options: --db-compaction TYPE Database compaction type. TYPE may be one of: ssd - suitable for SSDs and fast HDDs; hdd - suitable for slow HDDs [default: ssd]. + --fat-db Fat database. Import/Export Options: --from BLOCK Export from block BLOCK, which may be an index or @@ -362,6 +363,7 @@ pub struct Args { pub flag_ipcapi: Option, pub flag_db_cache_size: Option, pub flag_db_compaction: String, + pub flag_fat_db: bool, } pub fn print_version() { diff --git a/parity/configuration.rs b/parity/configuration.rs index fb31bf7ad..96524f5ea 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -333,6 +333,14 @@ impl Configuration { _ => { die!("Invalid pruning method given."); } }; + if self.args.flag_fat_db { + if let journaldb::Algorithm::Archive = client_config.pruning { + client_config.trie_spec = TrieSpec::Fat; + } else { + die!("Fatdb is not supported. Please rerun with --pruning=archive") + } + } + // forced state db cache size if provided client_config.db_cache_size = self.args.flag_db_cache_size.and_then(|cs| Some(cs / 4)); diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index 2b9863135..5304781d4 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -43,7 +43,7 @@ use std::cmp; use std::str::{FromStr}; use std::convert::From; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::ops::*; use std::cmp::*; @@ -1031,7 +1031,7 @@ macro_rules! construct_uint { // shift for i in word_shift..$n_words { - ret[i] += original[i - word_shift] << bit_shift; + ret[i] = original[i - word_shift] << bit_shift; } // carry if bit_shift > 0 { @@ -1052,14 +1052,18 @@ macro_rules! construct_uint { let word_shift = shift / 64; let bit_shift = shift % 64; + // shift for i in word_shift..$n_words { - // Shift - ret[i - word_shift] += original[i] >> bit_shift; - // Carry - if bit_shift > 0 && i < $n_words - 1 { - ret[i - word_shift] += original[i + 1] << (64 - bit_shift); + ret[i - word_shift] = original[i] >> bit_shift; + } + + // Carry + if bit_shift > 0 { + for i in word_shift+1..$n_words { + ret[i - word_shift - 1] += original[i] << (64 - bit_shift); } } + $name(ret) } } diff --git a/util/src/hashdb.rs b/util/src/hashdb.rs index 1ec3069d8..51b9d445e 100644 --- a/util/src/hashdb.rs +++ b/util/src/hashdb.rs @@ -104,6 +104,21 @@ pub trait HashDB: AsHashDB { /// } /// ``` fn remove(&mut self, key: &H256); + + /// Insert auxiliary data into hashdb. + fn insert_aux(&mut self, _hash: Vec, _value: Vec) { + unimplemented!(); + } + + /// Get auxiliary data from hashdb. + fn get_aux(&self, _hash: &[u8]) -> Option> { + unimplemented!(); + } + + /// Removes auxiliary data from hashdb. + fn remove_aux(&mut self, _hash: &[u8]) { + unimplemented!(); + } } /// Upcast trait. diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index a01ed807f..40172cccd 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -26,6 +26,13 @@ use kvdb::{Database, DBTransaction, DatabaseConfig}; #[cfg(test)] use std::env; +/// Suffix appended to auxiliary keys to distinguish them from normal keys. +/// Would be nich to use rocksdb columns for this eventually. +const AUX_FLAG: u8 = 255; + +/// Database version. +const DB_VERSION : u32 = 0x103; + /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay /// and latent-removal semantics. /// @@ -39,8 +46,6 @@ pub struct ArchiveDB { latest_era: Option, } -const DB_VERSION : u32 = 0x103; - impl ArchiveDB { /// Create a new instance from file pub fn new(path: &str, config: DatabaseConfig) -> ArchiveDB { @@ -115,12 +120,35 @@ impl HashDB for ArchiveDB { fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) } + fn emplace(&mut self, key: H256, value: Bytes) { self.overlay.emplace(key, value); } + fn remove(&mut self, key: &H256) { self.overlay.remove(key); } + + fn insert_aux(&mut self, hash: Vec, value: Vec) { + self.overlay.insert_aux(hash, value); + } + + fn get_aux(&self, hash: &[u8]) -> Option> { + if let Some(res) = self.overlay.get_aux(hash) { + return Some(res) + } + + let mut db_hash = hash.to_vec(); + db_hash.push(AUX_FLAG); + + self.backing.get(&db_hash) + .expect("Low-level database error. Some issue with your hard disk?") + .map(|v| v.to_vec()) + } + + fn remove_aux(&mut self, hash: &[u8]) { + self.overlay.remove_aux(hash); + } } impl JournalDB for ArchiveDB { @@ -144,6 +172,7 @@ impl JournalDB for ArchiveDB { let batch = DBTransaction::new(); let mut inserts = 0usize; let mut deletes = 0usize; + for i in self.overlay.drain().into_iter() { let (key, (value, rc)) = i; if rc > 0 { @@ -156,6 +185,12 @@ impl JournalDB for ArchiveDB { deletes += 1; } } + + for (mut key, value) in self.overlay.drain_aux().into_iter() { + key.push(AUX_FLAG); + batch.put(&key, &value).expect("Low-level database error. Some issue with your hard disk?"); + } + if self.latest_era.map_or(true, |e| now > e) { try!(batch.put(&LATEST_ERA_KEY, &encode(&now))); self.latest_era = Some(now); diff --git a/util/src/memorydb.rs b/util/src/memorydb.rs index ea77f4aa2..f63dfd992 100644 --- a/util/src/memorydb.rs +++ b/util/src/memorydb.rs @@ -74,6 +74,7 @@ use std::default::Default; pub struct MemoryDB { data: HashMap, static_null_rlp: (Bytes, i32), + aux: HashMap, } impl Default for MemoryDB { @@ -88,6 +89,7 @@ impl MemoryDB { MemoryDB { data: HashMap::new(), static_null_rlp: (vec![0x80u8; 1], 1), + aux: HashMap::new(), } } @@ -134,9 +136,12 @@ impl MemoryDB { /// Return the internal map of hashes to data, clearing the current state. pub fn drain(&mut self) -> HashMap { - let mut data = HashMap::new(); - mem::swap(&mut self.data, &mut data); - data + mem::replace(&mut self.data, HashMap::new()) + } + + /// Return the internal map of auxiliary data, clearing the current state. + pub fn drain_aux(&mut self) -> HashMap { + mem::replace(&mut self.aux, HashMap::new()) } /// Denote than an existing value has the given key. Used when a key gets removed without @@ -233,6 +238,18 @@ impl HashDB for MemoryDB { self.data.insert(key.clone(), (Bytes::new(), -1)); } } + + fn insert_aux(&mut self, hash: Vec, value: Vec) { + self.aux.insert(hash, value); + } + + fn get_aux(&self, hash: &[u8]) -> Option> { + self.aux.get(hash).cloned() + } + + fn remove_aux(&mut self, hash: &[u8]) { + self.aux.remove(hash); + } } #[test] diff --git a/util/src/network/host.rs b/util/src/network/host.rs index a48d1544c..a8bbc17e5 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -539,7 +539,7 @@ impl Host where Message: Send + Sync + Clone { self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone()); if self.first_time.load(AtomicOrdering::Relaxed) { - info!("Public node URL: {}", paint(White.bold(), format!("{}", self.external_url().unwrap()))); + info!("Public node URL: {}", paint(White.bold(), self.external_url().unwrap())); self.first_time.store(false, AtomicOrdering::Relaxed); } diff --git a/util/src/trie/fatdb.rs b/util/src/trie/fatdb.rs new file mode 100644 index 000000000..555e4644f --- /dev/null +++ b/util/src/trie/fatdb.rs @@ -0,0 +1,112 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use hash::H256; +use sha3::Hashable; +use hashdb::HashDB; +use super::{TrieDB, Trie, TrieDBIterator, TrieError}; + +/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. +/// Additionaly it stores inserted hash-key mappings for later retrieval. +/// +/// Use it as a `Trie` or `TrieMut` trait object. +pub struct FatDB<'db> { + raw: TrieDB<'db>, +} + +impl<'db> FatDB<'db> { + /// Create a new trie with the backing database `db` and empty `root` + /// Initialise to the state entailed by the genesis block. + /// This guarantees the trie is built correctly. + pub fn new(db: &'db HashDB, root: &'db H256) -> Result { + let fatdb = FatDB { + raw: try!(TrieDB::new(db, root)) + }; + + Ok(fatdb) + } + + /// Get the backing database. + pub fn db(&self) -> &HashDB { + self.raw.db() + } + + /// Iterator over all key / vlaues in the trie. + pub fn iter(&self) -> FatDBIterator { + FatDBIterator::new(&self.raw) + } +} + +impl<'db> Trie for FatDB<'db> { + fn iter<'a>(&'a self) -> Box, &[u8])> + 'a> { + Box::new(FatDB::iter(self)) + } + + fn root(&self) -> &H256 { + self.raw.root() + } + + fn contains(&self, key: &[u8]) -> bool { + self.raw.contains(&key.sha3()) + } + + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key { + self.raw.get(&key.sha3()) + } +} + +/// Itarator over inserted pairs of key values. +pub struct FatDBIterator<'db> { + trie_iterator: TrieDBIterator<'db>, + trie: &'db TrieDB<'db>, +} + +impl<'db> FatDBIterator<'db> { + /// Creates new iterator. + pub fn new(trie: &'db TrieDB) -> Self { + FatDBIterator { + trie_iterator: TrieDBIterator::new(trie), + trie: trie, + } + } +} + +impl<'db> Iterator for FatDBIterator<'db> { + type Item = (Vec, &'db [u8]); + + fn next(&mut self) -> Option { + self.trie_iterator.next() + .map(|(hash, value)| { + (self.trie.db().get_aux(&hash).expect("Missing fatdb hash"), value) + }) + } +} + +#[test] +fn fatdb_to_trie() { + use memorydb::MemoryDB; + use trie::{FatDBMut, TrieMut}; + + let mut memdb = MemoryDB::new(); + let mut root = H256::default(); + { + let mut t = FatDBMut::new(&mut memdb, &mut root); + t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); + } + let t = FatDB::new(&memdb, &root).unwrap(); + assert_eq!(t.get(&[0x01u8, 0x23]).unwrap(), &[0x01u8, 0x23]); + assert_eq!(t.iter().collect::>(), vec![(vec![0x01u8, 0x23], &[0x01u8, 0x23] as &[u8])]); +} diff --git a/util/src/trie/fatdbmut.rs b/util/src/trie/fatdbmut.rs new file mode 100644 index 000000000..3e72c0653 --- /dev/null +++ b/util/src/trie/fatdbmut.rs @@ -0,0 +1,94 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use hash::H256; +use sha3::Hashable; +use hashdb::HashDB; +use super::{TrieDBMut, Trie, TrieMut, TrieError}; + +/// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. +/// Additionaly it stores inserted hash-key mappings for later retrieval. +/// +/// Use it as a `Trie` or `TrieMut` trait object. +pub struct FatDBMut<'db> { + raw: TrieDBMut<'db>, +} + +impl<'db> FatDBMut<'db> { + /// Create a new trie with the backing database `db` and empty `root` + /// Initialise to the state entailed by the genesis block. + /// This guarantees the trie is built correctly. + pub fn new(db: &'db mut HashDB, root: &'db mut H256) -> Self { + FatDBMut { raw: TrieDBMut::new(db, root) } + } + + /// Create a new trie with the backing database `db` and `root`. + /// + /// Returns an error if root does not exist. + pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Result { + Ok(FatDBMut { raw: try!(TrieDBMut::from_existing(db, root)) }) + } + + /// Get the backing database. + pub fn db(&self) -> &HashDB { + self.raw.db() + } + + /// Get the backing database. + pub fn db_mut(&mut self) -> &mut HashDB { + self.raw.db_mut() + } +} + +impl<'db> TrieMut for FatDBMut<'db> { + fn root(&self) -> &H256 { + self.raw.root() + } + + fn contains(&self, key: &[u8]) -> bool { + self.raw.contains(&key.sha3()) + } + + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key { + self.raw.get(&key.sha3()) + } + + fn insert(&mut self, key: &[u8], value: &[u8]) { + let hash = key.sha3(); + self.raw.insert(&hash, value); + let db = self.raw.db_mut(); + db.insert_aux(hash.to_vec(), key.to_vec()); + } + + fn remove(&mut self, key: &[u8]) { + self.raw.remove(&key.sha3()); + } +} + +#[test] +fn fatdb_to_trie() { + use memorydb::MemoryDB; + use super::TrieDB; + + let mut memdb = MemoryDB::new(); + let mut root = H256::default(); + { + let mut t = FatDBMut::new(&mut memdb, &mut root); + t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); + } + let t = TrieDB::new(&memdb, &root).unwrap(); + assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap(), &[0x01u8, 0x23]); +} diff --git a/util/src/trie/mod.rs b/util/src/trie/mod.rs index 79135162c..f6e731370 100644 --- a/util/src/trie/mod.rs +++ b/util/src/trie/mod.rs @@ -17,6 +17,8 @@ //! Trie interface and implementation. use std::fmt; +use hash::H256; +use hashdb::HashDB; /// Export the trietraits module. pub mod trietraits; @@ -35,12 +37,18 @@ pub mod sectriedb; /// Export the sectriedbmut module. pub mod sectriedbmut; +mod fatdb; + +mod fatdbmut; + pub use self::trietraits::{Trie, TrieMut}; pub use self::standardmap::{Alphabet, StandardMap, ValueMode}; pub use self::triedbmut::TrieDBMut; -pub use self::triedb::TrieDB; +pub use self::triedb::{TrieDB, TrieDBIterator}; pub use self::sectriedbmut::SecTrieDBMut; pub use self::sectriedb::SecTrieDB; +pub use self::fatdb::{FatDB, FatDBIterator}; +pub use self::fatdbmut::FatDBMut; /// Trie Errors #[derive(Debug)] @@ -53,4 +61,63 @@ impl fmt::Display for TrieError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Trie Error: Invalid state root.") } -} \ No newline at end of file +} + +/// Trie types +#[derive(Debug, Clone)] +pub enum TrieSpec { + /// Generic trie. + Generic, + /// Secure trie. + Secure, + /// Secure trie with fat database. + Fat, +} + +impl Default for TrieSpec { + fn default() -> TrieSpec { + TrieSpec::Secure + } +} + +/// Trie factory. +#[derive(Default, Clone)] +pub struct TrieFactory { + spec: TrieSpec, +} + +impl TrieFactory { + /// Creates new factory. + pub fn new(spec: TrieSpec) -> Self { + TrieFactory { + spec: spec, + } + } + + /// Create new immutable instance of Trie. + pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result, TrieError> { + match self.spec { + TrieSpec::Generic => Ok(Box::new(try!(TrieDB::new(db, root)))), + TrieSpec::Secure => Ok(Box::new(try!(SecTrieDB::new(db, root)))), + TrieSpec::Fat => Ok(Box::new(try!(FatDB::new(db, root)))), + } + } + + /// Create new mutable instance of Trie. + pub fn create<'db>(&self, db: &'db mut HashDB, root: &'db mut H256) -> Box { + match self.spec { + TrieSpec::Generic => Box::new(TrieDBMut::new(db, root)), + TrieSpec::Secure => Box::new(SecTrieDBMut::new(db, root)), + TrieSpec::Fat => Box::new(FatDBMut::new(db, root)), + } + } + + /// Create new mutable instance of trie and check for errors. + pub fn from_existing<'db>(&self, db: &'db mut HashDB, root: &'db mut H256) -> Result, TrieError> { + match self.spec { + TrieSpec::Generic => Ok(Box::new(try!(TrieDBMut::from_existing(db, root)))), + TrieSpec::Secure => Ok(Box::new(try!(SecTrieDBMut::from_existing(db, root)))), + TrieSpec::Fat => Ok(Box::new(try!(FatDBMut::from_existing(db, root)))), + } + } +} diff --git a/util/src/trie/sectriedb.rs b/util/src/trie/sectriedb.rs index eda4ac58b..99770e382 100644 --- a/util/src/trie/sectriedb.rs +++ b/util/src/trie/sectriedb.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use hash::*; -use sha3::*; +use hash::H256; +use sha3::Hashable; use hashdb::HashDB; use super::triedb::TrieDB; use super::trietraits::Trie; @@ -50,6 +50,10 @@ impl<'db> SecTrieDB<'db> { } impl<'db> Trie for SecTrieDB<'db> { + fn iter<'a>(&'a self) -> Box, &[u8])> + 'a> { + Box::new(TrieDB::iter(&self.raw)) + } + fn root(&self) -> &H256 { self.raw.root() } fn contains(&self, key: &[u8]) -> bool { @@ -68,7 +72,7 @@ fn trie_to_sectrie() { use super::trietraits::TrieMut; let mut memdb = MemoryDB::new(); - let mut root = H256::new(); + let mut root = H256::default(); { let mut t = TrieDBMut::new(&mut memdb, &mut root); t.insert(&(&[0x01u8, 0x23]).sha3(), &[0x01u8, 0x23]); diff --git a/util/src/trie/sectriedbmut.rs b/util/src/trie/sectriedbmut.rs index 9c8231af4..09ff48f7a 100644 --- a/util/src/trie/sectriedbmut.rs +++ b/util/src/trie/sectriedbmut.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use hash::*; -use sha3::*; +use hash::H256; +use sha3::Hashable; use hashdb::HashDB; use super::triedbmut::TrieDBMut; use super::trietraits::{Trie, TrieMut}; @@ -44,13 +44,13 @@ impl<'db> SecTrieDBMut<'db> { } /// Get the backing database. - pub fn db(&'db self) -> &'db HashDB { self.raw.db() } + pub fn db(&self) -> &HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&'db mut self) -> &'db mut HashDB { self.raw.db_mut() } + pub fn db_mut(&mut self) -> &mut HashDB { self.raw.db_mut() } } -impl<'db> Trie for SecTrieDBMut<'db> { +impl<'db> TrieMut for SecTrieDBMut<'db> { fn root(&self) -> &H256 { self.raw.root() } fn contains(&self, key: &[u8]) -> bool { @@ -60,9 +60,7 @@ impl<'db> Trie for SecTrieDBMut<'db> { fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key { self.raw.get(&key.sha3()) } -} -impl<'db> TrieMut for SecTrieDBMut<'db> { fn insert(&mut self, key: &[u8], value: &[u8]) { self.raw.insert(&key.sha3(), value); } @@ -78,7 +76,7 @@ fn sectrie_to_trie() { use super::triedb::*; let mut memdb = MemoryDB::new(); - let mut root = H256::new(); + let mut root = H256::default(); { let mut t = SecTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index 9cd3a8c41..c88ec422e 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -18,7 +18,7 @@ use common::*; use hashdb::*; use nibbleslice::*; use rlp::*; -use super::trietraits::Trie; +use super::trietraits::{Trie}; use super::node::Node; use super::TrieError; @@ -257,7 +257,7 @@ pub struct TrieDBIterator<'a> { impl<'a> TrieDBIterator<'a> { /// Create a new iterator. - fn new(db: &'a TrieDB) -> TrieDBIterator<'a> { + pub fn new(db: &'a TrieDB) -> TrieDBIterator<'a> { let mut r = TrieDBIterator { db: db, trail: vec![], @@ -331,10 +331,16 @@ impl<'a> Iterator for TrieDBIterator<'a> { impl<'db> TrieDB<'db> { /// Get all keys/values stored in the trie. - pub fn iter(&self) -> TrieDBIterator { TrieDBIterator::new(self) } + pub fn iter(&self) -> TrieDBIterator { + TrieDBIterator::new(self) + } } impl<'db> Trie for TrieDB<'db> { + fn iter<'a>(&'a self) -> Box, &[u8])> + 'a> { + Box::new(TrieDB::iter(self)) + } + fn root(&self) -> &H256 { &self.root } fn contains(&self, key: &[u8]) -> bool { diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index 65d5bbd2b..6d34096fc 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -99,12 +99,12 @@ impl<'db> TrieDBMut<'db> { } /// Get the backing database. - pub fn db(&'db self) -> &'db HashDB { + pub fn db(&self) -> &HashDB { self.db } /// Get the backing database. - pub fn db_mut(&'db mut self) -> &'db mut HashDB { + pub fn db_mut(&mut self) -> &mut HashDB { self.db } @@ -642,7 +642,7 @@ impl<'db> TrieDBMut<'db> { } } -impl<'db> Trie for TrieDBMut<'db> { +impl<'db> TrieMut for TrieDBMut<'db> { fn root(&self) -> &H256 { &self.root } fn contains(&self, key: &[u8]) -> bool { @@ -652,9 +652,7 @@ impl<'db> Trie for TrieDBMut<'db> { fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key { self.do_lookup(&NibbleSlice::new(key)) } -} -impl<'db> TrieMut for TrieDBMut<'db> { fn insert(&mut self, key: &[u8], value: &[u8]) { match value.is_empty() { false => self.insert_ns(&NibbleSlice::new(key), value), diff --git a/util/src/trie/trietraits.rs b/util/src/trie/trietraits.rs index 160f9466f..fb53350c9 100644 --- a/util/src/trie/trietraits.rs +++ b/util/src/trie/trietraits.rs @@ -30,10 +30,25 @@ pub trait Trie { /// What is the value of the given key in this trie? fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key; + + /// Returns an iterator over elements of trie. + fn iter<'a>(&'a self) -> Box, &[u8])> + 'a>; } /// A key-value datastore implemented as a database-backed modified Merkle tree. -pub trait TrieMut: Trie { +pub trait TrieMut { + /// Return the root of the trie. + fn root(&self) -> &H256; + + /// Is the trie empty? + fn is_empty(&self) -> bool { *self.root() == SHA3_NULL_RLP } + + /// Does the trie contain a given key? + fn contains(&self, key: &[u8]) -> bool; + + /// What is the value of the given key in this trie? + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key; + /// Insert a `key`/`value` pair into the trie. An `empty` value is equivalent to removing /// `key` from the trie. fn insert(&mut self, key: &[u8], value: &[u8]); @@ -42,4 +57,3 @@ pub trait TrieMut: Trie { /// value. fn remove(&mut self, key: &[u8]); } -