Merge branch 'master' into personal-rpc-cli
Conflicts: parity/main.rs
This commit is contained in:
commit
08b4488ef3
@ -34,9 +34,6 @@ Then, download and build Parity:
|
|||||||
git clone https://github.com/ethcore/parity
|
git clone https://github.com/ethcore/parity
|
||||||
cd parity
|
cd parity
|
||||||
|
|
||||||
# parity should be built with rust beta
|
|
||||||
multirust override beta
|
|
||||||
|
|
||||||
# build in release mode
|
# build in release mode
|
||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
@ -171,7 +171,7 @@ pub struct SealedBlock {
|
|||||||
|
|
||||||
impl<'x> OpenBlock<'x> {
|
impl<'x> OpenBlock<'x> {
|
||||||
/// Create a new OpenBlock ready for transaction pushing.
|
/// Create a new OpenBlock ready for transaction pushing.
|
||||||
pub fn new(engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes, author: Address, extra_data: Bytes) -> Self {
|
pub fn new(engine: &'x Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes, author: Address, extra_data: Bytes) -> Self {
|
||||||
let mut r = OpenBlock {
|
let mut r = OpenBlock {
|
||||||
block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())),
|
block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
@ -317,7 +317,7 @@ impl ClosedBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Drop this object and return the underlieing database.
|
/// Drop this object and return the underlieing database.
|
||||||
pub fn drain(self) -> JournalDB { self.block.state.drop().1 }
|
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SealedBlock {
|
impl SealedBlock {
|
||||||
@ -331,7 +331,7 @@ impl SealedBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Drop this object and return the underlieing database.
|
/// Drop this object and return the underlieing database.
|
||||||
pub fn drain(self) -> JournalDB { self.block.state.drop().1 }
|
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsBlock for SealedBlock {
|
impl IsBlock for SealedBlock {
|
||||||
@ -339,10 +339,10 @@ impl IsBlock for SealedBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by block header, transactions and uncles
|
/// Enact the block given by block header, transactions and uncles
|
||||||
pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
||||||
{
|
{
|
||||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||||
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
|
let s = State::from_existing(db.spawn(), parent.state_root().clone(), engine.account_start_nonce());
|
||||||
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
|
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,20 +357,20 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||||
pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
||||||
let block = BlockView::new(block_bytes);
|
let block = BlockView::new(block_bytes);
|
||||||
let header = block.header();
|
let header = block.header();
|
||||||
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes)
|
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||||
pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
||||||
let view = BlockView::new(&block.bytes);
|
let view = BlockView::new(&block.bytes);
|
||||||
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
|
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
||||||
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<SealedBlock, Error> {
|
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<SealedBlock, Error> {
|
||||||
let header = BlockView::new(block_bytes).header_view();
|
let header = BlockView::new(block_bytes).header_view();
|
||||||
Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(engine, header.seal())))
|
Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(engine, header.seal())))
|
||||||
}
|
}
|
||||||
@ -389,7 +389,7 @@ mod tests {
|
|||||||
let genesis_header = engine.spec().genesis_header();
|
let genesis_header = engine.spec().genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||||
let last_hashes = vec![genesis_header.hash()];
|
let last_hashes = vec![genesis_header.hash()];
|
||||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
||||||
let b = b.close();
|
let b = b.close();
|
||||||
@ -404,14 +404,14 @@ mod tests {
|
|||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(engine.deref(), vec![]).unwrap();
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(engine.deref(), vec![]).unwrap();
|
||||||
let orig_bytes = b.rlp_bytes();
|
let orig_bytes = b.rlp_bytes();
|
||||||
let orig_db = b.drain();
|
let orig_db = b.drain();
|
||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||||
let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, vec![genesis_header.hash()]).unwrap();
|
let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, vec![genesis_header.hash()]).unwrap();
|
||||||
|
|
||||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||||
|
@ -412,6 +412,7 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Optimise memory footprint of the heap fields.
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
{
|
{
|
||||||
self.verification.unverified.lock().unwrap().shrink_to_fit();
|
self.verification.unverified.lock().unwrap().shrink_to_fit();
|
||||||
|
@ -101,7 +101,7 @@ impl ClientReport {
|
|||||||
pub struct Client<V = CanonVerifier> where V: Verifier {
|
pub struct Client<V = CanonVerifier> where V: Verifier {
|
||||||
chain: Arc<BlockChain>,
|
chain: Arc<BlockChain>,
|
||||||
engine: Arc<Box<Engine>>,
|
engine: Arc<Box<Engine>>,
|
||||||
state_db: Mutex<JournalDB>,
|
state_db: Mutex<Box<JournalDB>>,
|
||||||
block_queue: BlockQueue,
|
block_queue: BlockQueue,
|
||||||
report: RwLock<ClientReport>,
|
report: RwLock<ClientReport>,
|
||||||
import_lock: Mutex<()>,
|
import_lock: Mutex<()>,
|
||||||
@ -131,7 +131,8 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
let mut dir = path.to_path_buf();
|
let mut dir = path.to_path_buf();
|
||||||
dir.push(H64::from(spec.genesis_header().hash()).hex());
|
dir.push(H64::from(spec.genesis_header().hash()).hex());
|
||||||
//TODO: sec/fat: pruned/full versioning
|
//TODO: sec/fat: pruned/full versioning
|
||||||
dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, if config.prefer_journal { "pruned" } else { "archive" }));
|
// version here is a bit useless now, since it's controlled only be the pruning algo.
|
||||||
|
dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, config.pruning));
|
||||||
let path = dir.as_path();
|
let path = dir.as_path();
|
||||||
let gb = spec.genesis_block();
|
let gb = spec.genesis_block();
|
||||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, path));
|
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, path));
|
||||||
@ -139,8 +140,10 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
state_path.push("state");
|
state_path.push("state");
|
||||||
|
|
||||||
let engine = Arc::new(try!(spec.to_engine()));
|
let engine = Arc::new(try!(spec.to_engine()));
|
||||||
let mut state_db = JournalDB::from_prefs(state_path.to_str().unwrap(), config.prefer_journal);
|
let state_path_str = state_path.to_str().unwrap();
|
||||||
if state_db.is_empty() && engine.spec().ensure_db_good(&mut state_db) {
|
let mut state_db = journaldb::new(state_path_str, config.pruning);
|
||||||
|
|
||||||
|
if state_db.is_empty() && engine.spec().ensure_db_good(state_db.as_hashdb_mut()) {
|
||||||
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +215,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
// Enact Verified Block
|
// Enact Verified Block
|
||||||
let parent = chain_has_parent.unwrap();
|
let parent = chain_has_parent.unwrap();
|
||||||
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
||||||
let db = self.state_db.lock().unwrap().clone();
|
let db = self.state_db.lock().unwrap().spawn();
|
||||||
|
|
||||||
let enact_result = enact_verified(&block, engine, db, &parent, last_hashes);
|
let enact_result = enact_verified(&block, engine, db, &parent, last_hashes);
|
||||||
if let Err(e) = enact_result {
|
if let Err(e) = enact_result {
|
||||||
@ -311,7 +314,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
/// Get a copy of the best block's state.
|
/// Get a copy of the best block's state.
|
||||||
pub fn state(&self) -> State {
|
pub fn state(&self) -> State {
|
||||||
State::from_existing(self.state_db.lock().unwrap().clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
|
State::from_existing(self.state_db.lock().unwrap().spawn(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get info on the cache.
|
/// Get info on the cache.
|
||||||
@ -380,7 +383,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
let h = self.chain.best_block_hash();
|
let h = self.chain.best_block_hash();
|
||||||
let mut b = OpenBlock::new(
|
let mut b = OpenBlock::new(
|
||||||
self.engine.deref().deref(),
|
self.engine.deref().deref(),
|
||||||
self.state_db.lock().unwrap().clone(),
|
self.state_db.lock().unwrap().spawn(),
|
||||||
match self.chain.block_header(&h) { Some(ref x) => x, None => {return;} },
|
match self.chain.block_header(&h) { Some(ref x) => x, None => {return;} },
|
||||||
self.build_last_hashes(h.clone()),
|
self.build_last_hashes(h.clone()),
|
||||||
self.author(),
|
self.author(),
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
pub use block_queue::BlockQueueConfig;
|
pub use block_queue::BlockQueueConfig;
|
||||||
pub use blockchain::BlockChainConfig;
|
pub use blockchain::BlockChainConfig;
|
||||||
|
use util::journaldb;
|
||||||
|
|
||||||
/// Client configuration. Includes configs for all sub-systems.
|
/// Client configuration. Includes configs for all sub-systems.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -24,8 +25,8 @@ pub struct ClientConfig {
|
|||||||
pub queue: BlockQueueConfig,
|
pub queue: BlockQueueConfig,
|
||||||
/// Blockchain configuration.
|
/// Blockchain configuration.
|
||||||
pub blockchain: BlockChainConfig,
|
pub blockchain: BlockChainConfig,
|
||||||
/// Prefer journal rather than archive.
|
/// The JournalDB ("pruning") algorithm to use.
|
||||||
pub prefer_journal: bool,
|
pub pruning: journaldb::Algorithm,
|
||||||
/// The name of the client instance.
|
/// The name of the client instance.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ mod tests {
|
|||||||
let genesis_header = engine.spec().genesis_header();
|
let genesis_header = engine.spec().genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||||
let last_hashes = vec![genesis_header.hash()];
|
let last_hashes = vec![genesis_header.hash()];
|
||||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
||||||
let b = b.close();
|
let b = b.close();
|
||||||
@ -311,7 +311,7 @@ mod tests {
|
|||||||
let genesis_header = engine.spec().genesis_header();
|
let genesis_header = engine.spec().genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||||
let last_hashes = vec![genesis_header.hash()];
|
let last_hashes = vec![genesis_header.hash()];
|
||||||
let mut b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
let mut b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
||||||
let mut uncle = Header::new();
|
let mut uncle = Header::new();
|
||||||
|
@ -61,7 +61,7 @@ mod tests {
|
|||||||
let genesis_header = engine.spec().genesis_header();
|
let genesis_header = engine.spec().genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce());
|
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce());
|
||||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
|
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("0000000000000000000000000000000000000002")), U256::from(1u64));
|
||||||
|
@ -31,7 +31,7 @@ pub type ApplyResult = Result<Receipt, Error>;
|
|||||||
|
|
||||||
/// Representation of the entire state of all accounts in the system.
|
/// Representation of the entire state of all accounts in the system.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
db: JournalDB,
|
db: Box<JournalDB>,
|
||||||
root: H256,
|
root: H256,
|
||||||
cache: RefCell<HashMap<Address, Option<Account>>>,
|
cache: RefCell<HashMap<Address, Option<Account>>>,
|
||||||
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
|
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
|
||||||
@ -41,11 +41,11 @@ pub struct State {
|
|||||||
impl State {
|
impl State {
|
||||||
/// Creates new state with empty state root
|
/// Creates new state with empty state root
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new(mut db: JournalDB, account_start_nonce: U256) -> State {
|
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256) -> State {
|
||||||
let mut root = H256::new();
|
let mut root = H256::new();
|
||||||
{
|
{
|
||||||
// init trie and reset root too null
|
// init trie and reset root too null
|
||||||
let _ = SecTrieDBMut::new(&mut db, &mut root);
|
let _ = SecTrieDBMut::new(db.as_hashdb_mut(), &mut root);
|
||||||
}
|
}
|
||||||
|
|
||||||
State {
|
State {
|
||||||
@ -58,10 +58,10 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new state with existing state root
|
/// Creates new state with existing state root
|
||||||
pub fn from_existing(db: JournalDB, root: H256, account_start_nonce: U256) -> State {
|
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256) -> State {
|
||||||
{
|
{
|
||||||
// trie should panic! if root does not exist
|
// trie should panic! if root does not exist
|
||||||
let _ = SecTrieDB::new(&db, &root);
|
let _ = SecTrieDB::new(db.as_hashdb(), &root);
|
||||||
}
|
}
|
||||||
|
|
||||||
State {
|
State {
|
||||||
@ -126,7 +126,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy the current object and return root and database.
|
/// Destroy the current object and return root and database.
|
||||||
pub fn drop(self) -> (H256, JournalDB) {
|
pub fn drop(self) -> (H256, Box<JournalDB>) {
|
||||||
(self.root, self.db)
|
(self.root, self.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ impl State {
|
|||||||
|
|
||||||
/// Determine whether an account exists.
|
/// Determine whether an account exists.
|
||||||
pub fn exists(&self, a: &Address) -> bool {
|
pub fn exists(&self, a: &Address) -> bool {
|
||||||
self.cache.borrow().get(&a).unwrap_or(&None).is_some() || SecTrieDB::new(&self.db, &self.root).contains(&a)
|
self.cache.borrow().get(&a).unwrap_or(&None).is_some() || SecTrieDB::new(self.db.as_hashdb(), &self.root).contains(&a)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the balance of account `a`.
|
/// Get the balance of account `a`.
|
||||||
@ -163,7 +163,7 @@ impl State {
|
|||||||
|
|
||||||
/// Mutate storage of account `address` so that it is `value` for `key`.
|
/// Mutate storage of account `address` so that it is `value` for `key`.
|
||||||
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
|
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
|
||||||
self.get(address, false).as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::new(&self.db, address), key))
|
self.get(address, false).as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::new(self.db.as_hashdb(), address), key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutate storage of account `a` so that it is `value` for `key`.
|
/// Mutate storage of account `a` so that it is `value` for `key`.
|
||||||
@ -253,7 +253,7 @@ impl State {
|
|||||||
/// Commits our cached account changes into the trie.
|
/// Commits our cached account changes into the trie.
|
||||||
pub fn commit(&mut self) {
|
pub fn commit(&mut self) {
|
||||||
assert!(self.snapshots.borrow().is_empty());
|
assert!(self.snapshots.borrow().is_empty());
|
||||||
Self::commit_into(&mut self.db, &mut self.root, self.cache.borrow_mut().deref_mut());
|
Self::commit_into(self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -285,11 +285,11 @@ impl State {
|
|||||||
fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> {
|
fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> {
|
||||||
let have_key = self.cache.borrow().contains_key(a);
|
let have_key = self.cache.borrow().contains_key(a);
|
||||||
if !have_key {
|
if !have_key {
|
||||||
self.insert_cache(a, SecTrieDB::new(&self.db, &self.root).get(&a).map(Account::from_rlp))
|
self.insert_cache(a, SecTrieDB::new(self.db.as_hashdb(), &self.root).get(&a).map(Account::from_rlp))
|
||||||
}
|
}
|
||||||
if require_code {
|
if require_code {
|
||||||
if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() {
|
if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() {
|
||||||
account.cache_code(&AccountDB::new(&self.db, a));
|
account.cache_code(&AccountDB::new(self.db.as_hashdb(), a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe { ::std::mem::transmute(self.cache.borrow().get(a).unwrap()) }
|
unsafe { ::std::mem::transmute(self.cache.borrow().get(a).unwrap()) }
|
||||||
@ -305,7 +305,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 {
|
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);
|
let have_key = self.cache.borrow().contains_key(a);
|
||||||
if !have_key {
|
if !have_key {
|
||||||
self.insert_cache(a, SecTrieDB::new(&self.db, &self.root).get(&a).map(Account::from_rlp))
|
self.insert_cache(a, SecTrieDB::new(self.db.as_hashdb(), &self.root).get(&a).map(Account::from_rlp))
|
||||||
} else {
|
} else {
|
||||||
self.note_cache(a);
|
self.note_cache(a);
|
||||||
}
|
}
|
||||||
@ -318,7 +318,7 @@ impl State {
|
|||||||
|
|
||||||
unsafe { ::std::mem::transmute(self.cache.borrow_mut().get_mut(a).unwrap().as_mut().map(|account| {
|
unsafe { ::std::mem::transmute(self.cache.borrow_mut().get_mut(a).unwrap().as_mut().map(|account| {
|
||||||
if require_code {
|
if require_code {
|
||||||
account.cache_code(&AccountDB::new(&self.db, a));
|
account.cache_code(&AccountDB::new(self.db.as_hashdb(), a));
|
||||||
}
|
}
|
||||||
account
|
account
|
||||||
}).unwrap()) }
|
}).unwrap()) }
|
||||||
|
@ -250,9 +250,9 @@ pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_journal_db() -> GuardedTempResult<JournalDB> {
|
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let journal_db = JournalDB::new(temp.as_str());
|
let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge);
|
||||||
GuardedTempResult {
|
GuardedTempResult {
|
||||||
_temp: temp,
|
_temp: temp,
|
||||||
result: Some(journal_db)
|
result: Some(journal_db)
|
||||||
@ -268,8 +268,8 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_journal_db_in(path: &Path) -> JournalDB {
|
pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> {
|
||||||
JournalDB::new(path.to_str().unwrap())
|
journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_state_in(path: &Path) -> State {
|
pub fn get_temp_state_in(path: &Path) -> State {
|
||||||
|
128
parity/main.rs
128
parity/main.rs
@ -37,7 +37,7 @@ extern crate rpassword;
|
|||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
extern crate ethcore_rpc as rpc;
|
extern crate ethcore_rpc as rpc;
|
||||||
|
|
||||||
use std::net::{SocketAddr};
|
use std::net::{SocketAddr, IpAddr};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -71,28 +71,31 @@ Parity. Ethereum Client.
|
|||||||
Copyright 2015, 2016 Ethcore (UK) Limited
|
Copyright 2015, 2016 Ethcore (UK) Limited
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
parity daemon <pid-file> [options] [ --no-bootstrap | <enode>... ]
|
parity daemon <pid-file> [options]
|
||||||
parity account (new | list)
|
parity account (new | list)
|
||||||
parity [options] [ --no-bootstrap | <enode>... ]
|
parity [options]
|
||||||
|
|
||||||
Protocol Options:
|
Protocol Options:
|
||||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
||||||
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
||||||
--testnet Equivalent to --chain testnet (geth-compatible).
|
--testnet Equivalent to --chain testnet (geth-compatible).
|
||||||
--networkid INDEX Override the network identifier from the chain we are on.
|
--networkid INDEX Override the network identifier from the chain we are on.
|
||||||
--pruning Client should prune the state/storage trie.
|
--pruning METHOD Configure pruning of the state/storage trie. METHOD may be one of: archive,
|
||||||
|
light (experimental) [default: archive].
|
||||||
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
||||||
|
--db-path PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
||||||
|
--pruning Client should prune the state/storage trie.
|
||||||
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
||||||
--identity NAME Specify your node's name.
|
--identity NAME Specify your node's name.
|
||||||
|
|
||||||
Networking Options:
|
Networking Options:
|
||||||
--no-bootstrap Don't bother trying to connect to any nodes initially.
|
--port PORT Override the port on which the node should listen [default: 30303].
|
||||||
--listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304].
|
|
||||||
--public-address URL Specify the IP/port on which peers may connect.
|
|
||||||
--address URL Equivalent to --listen-address URL --public-address URL.
|
|
||||||
--peers NUM Try to maintain that many peers [default: 25].
|
--peers NUM Try to maintain that many peers [default: 25].
|
||||||
|
--nat METHOD Specify method to use for determining public address. Must be one of: any, none,
|
||||||
|
upnp, extip:(IP) [default: any].
|
||||||
|
--bootnodes NODES Specify additional comma-separated bootnodes.
|
||||||
|
--no-bootstrap Don't bother trying to connect to standard bootnodes.
|
||||||
--no-discovery Disable new peer discovery.
|
--no-discovery Disable new peer discovery.
|
||||||
--no-upnp Disable trying to figure out the correct public adderss over UPnP.
|
|
||||||
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
||||||
|
|
||||||
API and Console Options:
|
API and Console Options:
|
||||||
@ -102,6 +105,7 @@ API and Console Options:
|
|||||||
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
||||||
--jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited
|
--jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited
|
||||||
list of API name. Possible name are web3, eth and net. [default: web3,eth,net,personal].
|
list of API name. Possible name are web3, eth and net. [default: web3,eth,net,personal].
|
||||||
|
|
||||||
--rpc Equivalent to --jsonrpc (geth-compatible).
|
--rpc Equivalent to --jsonrpc (geth-compatible).
|
||||||
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST (geth-compatible).
|
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST (geth-compatible).
|
||||||
--rpcport PORT Equivalent to --jsonrpc-port PORT (geth-compatible).
|
--rpcport PORT Equivalent to --jsonrpc-port PORT (geth-compatible).
|
||||||
@ -111,7 +115,7 @@ API and Console Options:
|
|||||||
Sealing/Mining Options:
|
Sealing/Mining Options:
|
||||||
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
||||||
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
||||||
--extradata STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
--extra-data STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
||||||
|
|
||||||
Memory Footprint Options:
|
Memory Footprint Options:
|
||||||
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
||||||
@ -120,6 +124,21 @@ Memory Footprint Options:
|
|||||||
--cache MEGABYTES Set total amount of cache to use for the entire system, mutually exclusive with
|
--cache MEGABYTES Set total amount of cache to use for the entire system, mutually exclusive with
|
||||||
other cache options (geth-compatible).
|
other cache options (geth-compatible).
|
||||||
|
|
||||||
|
Geth-Compatibility Options
|
||||||
|
--datadir PATH Equivalent to --db-path PATH.
|
||||||
|
--testnet Equivalent to --chain testnet.
|
||||||
|
--networkid INDEX Override the network identifier from the chain we are on.
|
||||||
|
--rpc Equivalent to --jsonrpc.
|
||||||
|
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST.
|
||||||
|
--rpcport PORT Equivalent to --jsonrpc-port PORT.
|
||||||
|
--rpcapi APIS Equivalent to --jsonrpc-apis APIS.
|
||||||
|
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL.
|
||||||
|
--maxpeers COUNT Equivalent to --peers COUNT.
|
||||||
|
--nodekey KEY Equivalent to --node-key KEY.
|
||||||
|
--nodiscover Equivalent to --no-discovery.
|
||||||
|
--etherbase ADDRESS Equivalent to --author ADDRESS.
|
||||||
|
--extradata STRING Equivalent to --extra-data STRING.
|
||||||
|
|
||||||
Miscellaneous Options:
|
Miscellaneous Options:
|
||||||
-l --logging LOGGING Specify the logging level.
|
-l --logging LOGGING Specify the logging level.
|
||||||
-v --version Show information about version.
|
-v --version Show information about version.
|
||||||
@ -133,22 +152,18 @@ struct Args {
|
|||||||
cmd_new: bool,
|
cmd_new: bool,
|
||||||
cmd_list: bool,
|
cmd_list: bool,
|
||||||
arg_pid_file: String,
|
arg_pid_file: String,
|
||||||
arg_enode: Vec<String>,
|
|
||||||
flag_chain: String,
|
flag_chain: String,
|
||||||
flag_testnet: bool,
|
flag_db_path: String,
|
||||||
flag_datadir: String,
|
|
||||||
flag_networkid: Option<String>,
|
|
||||||
flag_identity: String,
|
flag_identity: String,
|
||||||
flag_cache: Option<usize>,
|
flag_cache: Option<usize>,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
flag_pruning: bool,
|
flag_bootnodes: Option<String>,
|
||||||
|
flag_pruning: String,
|
||||||
flag_no_bootstrap: bool,
|
flag_no_bootstrap: bool,
|
||||||
flag_listen_address: String,
|
flag_port: u16,
|
||||||
flag_public_address: Option<String>,
|
|
||||||
flag_address: Option<String>,
|
|
||||||
flag_peers: usize,
|
flag_peers: usize,
|
||||||
flag_no_discovery: bool,
|
flag_no_discovery: bool,
|
||||||
flag_no_upnp: bool,
|
flag_nat: String,
|
||||||
flag_node_key: Option<String>,
|
flag_node_key: Option<String>,
|
||||||
flag_cache_pref_size: usize,
|
flag_cache_pref_size: usize,
|
||||||
flag_cache_max_size: usize,
|
flag_cache_max_size: usize,
|
||||||
@ -158,15 +173,24 @@ struct Args {
|
|||||||
flag_jsonrpc_port: u16,
|
flag_jsonrpc_port: u16,
|
||||||
flag_jsonrpc_cors: String,
|
flag_jsonrpc_cors: String,
|
||||||
flag_jsonrpc_apis: String,
|
flag_jsonrpc_apis: String,
|
||||||
|
flag_logging: Option<String>,
|
||||||
|
flag_version: bool,
|
||||||
|
// geth-compatibility...
|
||||||
|
flag_nodekey: Option<String>,
|
||||||
|
flag_nodiscover: bool,
|
||||||
|
flag_maxpeers: Option<usize>,
|
||||||
|
flag_author: String,
|
||||||
|
flag_extra_data: Option<String>,
|
||||||
|
flag_datadir: Option<String>,
|
||||||
|
flag_extradata: Option<String>,
|
||||||
|
flag_etherbase: Option<String>,
|
||||||
flag_rpc: bool,
|
flag_rpc: bool,
|
||||||
flag_rpcaddr: Option<String>,
|
flag_rpcaddr: Option<String>,
|
||||||
flag_rpcport: Option<u16>,
|
flag_rpcport: Option<u16>,
|
||||||
flag_rpccorsdomain: Option<String>,
|
flag_rpccorsdomain: Option<String>,
|
||||||
flag_rpcapi: Option<String>,
|
flag_rpcapi: Option<String>,
|
||||||
flag_logging: Option<String>,
|
flag_testnet: bool,
|
||||||
flag_version: bool,
|
flag_networkid: Option<String>,
|
||||||
flag_author: String,
|
|
||||||
flag_extra_data: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_log(init: &Option<String>) {
|
fn setup_log(init: &Option<String>) {
|
||||||
@ -247,15 +271,17 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> String {
|
fn path(&self) -> String {
|
||||||
self.args.flag_datadir.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
let d = self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path);
|
||||||
|
d.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn author(&self) -> Address {
|
fn author(&self) -> Address {
|
||||||
Address::from_str(&self.args.flag_author).unwrap_or_else(|_| die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", self.args.flag_author))
|
let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author);
|
||||||
|
Address::from_str(d).unwrap_or_else(|_| die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", self.args.flag_author))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_data(&self) -> Bytes {
|
fn extra_data(&self) -> Bytes {
|
||||||
match self.args.flag_extra_data {
|
match self.args.flag_extradata.as_ref().or(self.args.flag_extra_data.as_ref()) {
|
||||||
Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(),
|
Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(),
|
||||||
None => version_data(),
|
None => version_data(),
|
||||||
Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); }
|
Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); }
|
||||||
@ -287,45 +313,36 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_nodes(&self, spec: &Spec) -> Vec<String> {
|
fn init_nodes(&self, spec: &Spec) -> Vec<String> {
|
||||||
if self.args.flag_no_bootstrap { Vec::new() } else {
|
let mut r = if self.args.flag_no_bootstrap { Vec::new() } else { spec.nodes().clone() };
|
||||||
match self.args.arg_enode.len() {
|
if let Some(ref x) = self.args.flag_bootnodes {
|
||||||
0 => spec.nodes().clone(),
|
r.extend(x.split(",").map(|s| Self::normalize_enode(s).unwrap_or_else(|| die!("{}: Invalid node address format given for a boot node.", s))));
|
||||||
_ => self.args.arg_enode.iter().map(|s| Self::normalize_enode(s).unwrap_or_else(||die!("{}: Invalid node address format given for a boot node.", s))).collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(useless_format))]
|
#[cfg_attr(feature="dev", allow(useless_format))]
|
||||||
fn net_addresses(&self) -> (Option<SocketAddr>, Option<SocketAddr>) {
|
fn net_addresses(&self) -> (Option<SocketAddr>, Option<SocketAddr>) {
|
||||||
let mut listen_address = None;
|
let listen_address = Some(SocketAddr::new(IpAddr::from_str("0.0.0.0").unwrap(), self.args.flag_port));
|
||||||
let mut public_address = None;
|
let public_address = if self.args.flag_nat.starts_with("extip:") {
|
||||||
|
let host = &self.args.flag_nat[6..];
|
||||||
if let Some(ref a) = self.args.flag_address {
|
let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host));
|
||||||
public_address = Some(SocketAddr::from_str(a.as_ref()).unwrap_or_else(|_| die!("{}: Invalid listen/public address given with --address", a)));
|
Some(SocketAddr::new(host, self.args.flag_port))
|
||||||
listen_address = public_address;
|
} else {
|
||||||
}
|
listen_address.clone()
|
||||||
if listen_address.is_none() {
|
};
|
||||||
listen_address = Some(SocketAddr::from_str(self.args.flag_listen_address.as_ref()).unwrap_or_else(|_| die!("{}: Invalid listen/public address given with --listen-address", self.args.flag_listen_address)));
|
|
||||||
}
|
|
||||||
if let Some(ref a) = self.args.flag_public_address {
|
|
||||||
if public_address.is_some() {
|
|
||||||
die!("Conflicting flags provided: --address and --public-address");
|
|
||||||
}
|
|
||||||
public_address = Some(SocketAddr::from_str(a.as_ref()).unwrap_or_else(|_| die!("{}: Invalid listen/public address given with --public-address", a)));
|
|
||||||
}
|
|
||||||
(listen_address, public_address)
|
(listen_address, public_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn net_settings(&self, spec: &Spec) -> NetworkConfiguration {
|
fn net_settings(&self, spec: &Spec) -> NetworkConfiguration {
|
||||||
let mut ret = NetworkConfiguration::new();
|
let mut ret = NetworkConfiguration::new();
|
||||||
ret.nat_enabled = !self.args.flag_no_upnp;
|
ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp";
|
||||||
ret.boot_nodes = self.init_nodes(spec);
|
ret.boot_nodes = self.init_nodes(spec);
|
||||||
let (listen, public) = self.net_addresses();
|
let (listen, public) = self.net_addresses();
|
||||||
ret.listen_address = listen;
|
ret.listen_address = listen;
|
||||||
ret.public_address = public;
|
ret.public_address = public;
|
||||||
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
|
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
|
||||||
ret.discovery_enabled = !self.args.flag_no_discovery;
|
ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover;
|
||||||
ret.ideal_peers = self.args.flag_peers as u32;
|
ret.ideal_peers = self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32;
|
||||||
let mut net_path = PathBuf::from(&self.path());
|
let mut net_path = PathBuf::from(&self.path());
|
||||||
net_path.push("network");
|
net_path.push("network");
|
||||||
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
||||||
@ -404,7 +421,14 @@ impl Configuration {
|
|||||||
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client_config.prefer_journal = self.args.flag_pruning;
|
client_config.pruning = match self.args.flag_pruning.as_str() {
|
||||||
|
"" => journaldb::Algorithm::Archive,
|
||||||
|
"archive" => journaldb::Algorithm::Archive,
|
||||||
|
"pruned" => journaldb::Algorithm::EarlyMerge,
|
||||||
|
// "fast" => journaldb::Algorithm::OverlayRecent, // TODO: @arkpar uncomment this once option 2 is merged.
|
||||||
|
// "slow" => journaldb::Algorithm::RefCounted, // TODO: @gavofyork uncomment this once ref-count algo is merged.
|
||||||
|
_ => { die!("Invalid pruning method given."); }
|
||||||
|
};
|
||||||
client_config.name = self.args.flag_identity.clone();
|
client_config.name = self.args.flag_identity.clone();
|
||||||
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
||||||
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
@ -425,7 +449,7 @@ impl Configuration {
|
|||||||
self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_addr),
|
self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_addr),
|
||||||
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
|
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
|
||||||
);
|
);
|
||||||
SocketAddr::from_str(&url).unwrap_or_else(|_|die!("{}: Invalid JSONRPC listen host/port given.", url));
|
SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||||
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||||
// TODO: use this as the API list.
|
// TODO: use this as the API list.
|
||||||
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||||
|
@ -27,6 +27,7 @@ use ethcore::block::{IsBlock};
|
|||||||
use ethcore::views::*;
|
use ethcore::views::*;
|
||||||
use ethcore::ethereum::Ethash;
|
use ethcore::ethereum::Ethash;
|
||||||
use ethcore::ethereum::denominations::shannon;
|
use ethcore::ethereum::denominations::shannon;
|
||||||
|
use ethcore::transaction::Transaction as EthTransaction;
|
||||||
use v1::traits::{Eth, EthFilter};
|
use v1::traits::{Eth, EthFilter};
|
||||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log};
|
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log};
|
||||||
use v1::helpers::{PollFilter, PollManager};
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
@ -274,7 +275,7 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
match accounts.account_secret(&transaction_request.from) {
|
match accounts.account_secret(&transaction_request.from) {
|
||||||
Ok(secret) => {
|
Ok(secret) => {
|
||||||
let sync = take_weak!(self.sync);
|
let sync = take_weak!(self.sync);
|
||||||
let (transaction, _) = transaction_request.to_eth();
|
let transaction: EthTransaction = transaction_request.into();
|
||||||
let signed_transaction = transaction.sign(&secret);
|
let signed_transaction = transaction.sign(&secret);
|
||||||
let hash = signed_transaction.hash();
|
let hash = signed_transaction.hash();
|
||||||
sync.insert_transaction(signed_transaction);
|
sync.insert_transaction(signed_transaction);
|
||||||
|
@ -20,7 +20,7 @@ use serde::de::Visitor;
|
|||||||
use util::common::FromHex;
|
use util::common::FromHex;
|
||||||
|
|
||||||
/// Wrapper structure around vector of bytes.
|
/// Wrapper structure around vector of bytes.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Bytes(Vec<u8>);
|
pub struct Bytes(Vec<u8>);
|
||||||
|
|
||||||
impl Bytes {
|
impl Bytes {
|
||||||
|
@ -23,6 +23,7 @@ mod log;
|
|||||||
mod optionals;
|
mod optionals;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
|
mod transaction_request;
|
||||||
|
|
||||||
pub use self::block::{Block, BlockTransactions};
|
pub use self::block::{Block, BlockTransactions};
|
||||||
pub use self::block_number::BlockNumber;
|
pub use self::block_number::BlockNumber;
|
||||||
@ -33,5 +34,5 @@ pub use self::log::Log;
|
|||||||
pub use self::optionals::OptionalValue;
|
pub use self::optionals::OptionalValue;
|
||||||
pub use self::sync::{SyncStatus, SyncInfo};
|
pub use self::sync::{SyncStatus, SyncInfo};
|
||||||
pub use self::transaction::Transaction;
|
pub use self::transaction::Transaction;
|
||||||
pub use self::transaction::TransactionRequest;
|
pub use self::transaction_request::TransactionRequest;
|
||||||
|
|
||||||
|
@ -17,8 +17,7 @@
|
|||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use ethcore::transaction::{LocalizedTransaction, Action};
|
use ethcore::transaction::{LocalizedTransaction, Action};
|
||||||
use v1::types::{Bytes, OptionalValue};
|
use v1::types::{Bytes, OptionalValue};
|
||||||
use serde::{Deserializer, Error};
|
use serde::Error;
|
||||||
use ethcore;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
@ -39,35 +38,6 @@ pub struct Transaction {
|
|||||||
pub input: Bytes
|
pub input: Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
|
||||||
pub struct TransactionRequest {
|
|
||||||
pub from: Address,
|
|
||||||
pub to: Option<Address>,
|
|
||||||
#[serde(rename="gasPrice")]
|
|
||||||
pub gas_price: Option<U256>,
|
|
||||||
pub gas: Option<U256>,
|
|
||||||
pub value: Option<U256>,
|
|
||||||
pub data: Bytes,
|
|
||||||
pub nonce: Option<U256>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransactionRequest {
|
|
||||||
/// maps transaction request to the transaction that can be signed and inserted
|
|
||||||
pub fn to_eth(self) -> (ethcore::transaction::Transaction, Address) {
|
|
||||||
(ethcore::transaction::Transaction {
|
|
||||||
nonce: self.nonce.unwrap_or(U256::zero()),
|
|
||||||
action: match self.to {
|
|
||||||
None => ethcore::transaction::Action::Create,
|
|
||||||
Some(addr) => ethcore::transaction::Action::Call(addr)
|
|
||||||
},
|
|
||||||
gas: self.gas.unwrap_or(U256::zero()),
|
|
||||||
gas_price: self.gas_price.unwrap_or(U256::zero()),
|
|
||||||
value: self.value.unwrap_or(U256::zero()),
|
|
||||||
data: self.data.to_vec()
|
|
||||||
}, self.from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LocalizedTransaction> for Transaction {
|
impl From<LocalizedTransaction> for Transaction {
|
||||||
fn from(t: LocalizedTransaction) -> Transaction {
|
fn from(t: LocalizedTransaction) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
|
139
rpc/src/v1/types/transaction_request.rs
Normal file
139
rpc/src/v1/types/transaction_request.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use util::hash::Address;
|
||||||
|
use util::numbers::{Uint, U256};
|
||||||
|
use ethcore::transaction::{Action, Transaction};
|
||||||
|
use v1::types::Bytes;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Deserialize)]
|
||||||
|
pub struct TransactionRequest {
|
||||||
|
pub from: Address,
|
||||||
|
pub to: Option<Address>,
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Option<U256>,
|
||||||
|
pub gas: Option<U256>,
|
||||||
|
pub value: Option<U256>,
|
||||||
|
pub data: Option<Bytes>,
|
||||||
|
pub nonce: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Transaction> for TransactionRequest {
|
||||||
|
fn into(self) -> Transaction {
|
||||||
|
Transaction {
|
||||||
|
nonce: self.nonce.unwrap_or_else(U256::zero),
|
||||||
|
action: self.to.map_or(Action::Create, Action::Call),
|
||||||
|
gas: self.gas.unwrap_or_else(U256::zero),
|
||||||
|
gas_price: self.gas_price.unwrap_or_else(U256::zero),
|
||||||
|
value: self.value.unwrap_or_else(U256::zero),
|
||||||
|
data: self.data.map_or_else(Vec::new, |d| d.to_vec()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use util::numbers::{Uint, U256};
|
||||||
|
use util::hash::Address;
|
||||||
|
use ethcore::transaction::{Transaction, Action};
|
||||||
|
use v1::types::Bytes;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_into_transaction() {
|
||||||
|
let tr = TransactionRequest {
|
||||||
|
from: Address::default(),
|
||||||
|
to: Some(Address::from(10)),
|
||||||
|
gas_price: Some(U256::from(20)),
|
||||||
|
gas: Some(U256::from(10_000)),
|
||||||
|
value: Some(U256::from(1)),
|
||||||
|
data: Some(Bytes::new(vec![10, 20])),
|
||||||
|
nonce: Some(U256::from(12)),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(Transaction {
|
||||||
|
nonce: U256::from(12),
|
||||||
|
action: Action::Call(Address::from(10)),
|
||||||
|
gas: U256::from(10_000),
|
||||||
|
gas_price: U256::from(20),
|
||||||
|
value: U256::from(1),
|
||||||
|
data: vec![10, 20],
|
||||||
|
}, tr.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_transaction_request_into_transaction() {
|
||||||
|
let tr = TransactionRequest {
|
||||||
|
from: Address::default(),
|
||||||
|
to: None,
|
||||||
|
gas_price: None,
|
||||||
|
gas: None,
|
||||||
|
value: None,
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(Transaction {
|
||||||
|
nonce: U256::zero(),
|
||||||
|
action: Action::Create,
|
||||||
|
gas: U256::zero(),
|
||||||
|
gas_price: U256::zero(),
|
||||||
|
value: U256::zero(),
|
||||||
|
data: vec![],
|
||||||
|
}, tr.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_deserialize() {
|
||||||
|
let s = r#"{
|
||||||
|
"from":"0x0000000000000000000000000000000000000001",
|
||||||
|
"to":"0x0000000000000000000000000000000000000002",
|
||||||
|
"gasPrice":"0x1",
|
||||||
|
"gas":"0x2",
|
||||||
|
"value":"0x3",
|
||||||
|
"data":"0x123456",
|
||||||
|
"nonce":"0x4"
|
||||||
|
}"#;
|
||||||
|
let deserialized: TransactionRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, TransactionRequest {
|
||||||
|
from: Address::from(1),
|
||||||
|
to: Some(Address::from(2)),
|
||||||
|
gas_price: Some(U256::from(1)),
|
||||||
|
gas: Some(U256::from(2)),
|
||||||
|
value: Some(U256::from(3)),
|
||||||
|
data: Some(Bytes::new(vec![0x12, 0x34, 0x56])),
|
||||||
|
nonce: Some(U256::from(4)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_deserialize_empty() {
|
||||||
|
let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#;
|
||||||
|
let deserialized: TransactionRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, TransactionRequest {
|
||||||
|
from: Address::from(1),
|
||||||
|
to: None,
|
||||||
|
gas_price: None,
|
||||||
|
gas: None,
|
||||||
|
value: None,
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1307,11 +1307,11 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add transaction to the transaction queue
|
/// Add transaction to the transaction queue
|
||||||
pub fn insert_transaction<T>(&self, transaction: ethcore::transaction::SignedTransaction, fetch_nonce: &T)
|
pub fn insert_transaction<T>(&self, transaction: ethcore::transaction::SignedTransaction, fetch_nonce: &T) -> Result<(), Error>
|
||||||
where T: Fn(&Address) -> U256
|
where T: Fn(&Address) -> U256
|
||||||
{
|
{
|
||||||
let mut queue = self.transaction_queue.lock().unwrap();
|
let mut queue = self.transaction_queue.lock().unwrap();
|
||||||
queue.add(transaction, fetch_nonce);
|
queue.add(transaction, fetch_nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +146,8 @@ impl SyncProvider for EthSync {
|
|||||||
|
|
||||||
let nonce_fn = |a: &Address| self.chain.state().nonce(a) + U256::one();
|
let nonce_fn = |a: &Address| self.chain.state().nonce(a) + U256::one();
|
||||||
let sync = self.sync.write().unwrap();
|
let sync = self.sync.write().unwrap();
|
||||||
sync.insert_transaction(transaction, &nonce_fn);
|
sync.insert_transaction(transaction, &nonce_fn).unwrap_or_else(
|
||||||
|
|e| warn!(target: "sync", "Error inserting transaction to queue: {:?}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,8 +684,8 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
|
|
||||||
let (tx, tx2) = new_txs(U256::from(1));
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
txq.add(tx.clone(), &prev_nonce);
|
txq.add(tx.clone(), &prev_nonce).unwrap();
|
||||||
txq.add(tx2.clone(), &prev_nonce);
|
txq.add(tx2.clone(), &prev_nonce).unwrap();
|
||||||
assert_eq!(txq.status().future, 2);
|
assert_eq!(txq.status().future, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -36,10 +36,12 @@
|
|||||||
//! The functions here are designed to be fast.
|
//! The functions here are designed to be fast.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(all(asm_available, target_arch="x86_64"))]
|
||||||
|
use std::mem;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use std::mem;
|
|
||||||
use std::str::{FromStr};
|
use std::str::{FromStr};
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@ -788,14 +790,11 @@ macro_rules! construct_uint {
|
|||||||
|
|
||||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: serde::Error {
|
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
// 0x + len
|
// 0x + len
|
||||||
if value.len() != 2 + $n_words / 8 {
|
if value.len() > 2 + $n_words * 16 {
|
||||||
return Err(serde::Error::custom("Invalid length."));
|
return Err(serde::Error::custom("Invalid length."));
|
||||||
}
|
}
|
||||||
|
|
||||||
match $name::from_str(&value[2..]) {
|
$name::from_str(&value[2..]).map_err(|_| serde::Error::custom("Invalid hex value."))
|
||||||
Ok(val) => Ok(val),
|
|
||||||
Err(_) => { return Err(serde::Error::custom("Invalid length.")); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
|
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
|
@ -257,7 +257,7 @@ macro_rules! impl_hash {
|
|||||||
return Err(serde::Error::custom("Invalid length."));
|
return Err(serde::Error::custom("Invalid length."));
|
||||||
}
|
}
|
||||||
|
|
||||||
value[2..].from_hex().map(|ref v| $from::from_slice(v)).map_err(|_| serde::Error::custom("Invalid valid hex."))
|
value[2..].from_hex().map(|ref v| $from::from_slice(v)).map_err(|_| serde::Error::custom("Invalid hex value."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
|
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
|
@ -20,7 +20,7 @@ use bytes::*;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Trait modelling datastore keyed by a 32-byte Keccak hash.
|
/// Trait modelling datastore keyed by a 32-byte Keccak hash.
|
||||||
pub trait HashDB {
|
pub trait HashDB : AsHashDB {
|
||||||
/// Get the keys in the database together with number of underlying references.
|
/// Get the keys in the database together with number of underlying references.
|
||||||
fn keys(&self) -> HashMap<H256, i32>;
|
fn keys(&self) -> HashMap<H256, i32>;
|
||||||
|
|
||||||
@ -111,3 +111,16 @@ pub trait HashDB {
|
|||||||
/// ```
|
/// ```
|
||||||
fn remove(&mut self, key: &H256) { self.kill(key) }
|
fn remove(&mut self, key: &H256) { self.kill(key) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Upcast trait.
|
||||||
|
pub trait AsHashDB {
|
||||||
|
/// Perform upcast to HashDB for anything that derives from HashDB.
|
||||||
|
fn as_hashdb(&self) -> &HashDB;
|
||||||
|
/// Perform mutable upcast to HashDB for anything that derives from HashDB.
|
||||||
|
fn as_hashdb_mut(&mut self) -> &mut HashDB;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HashDB> AsHashDB for T {
|
||||||
|
fn as_hashdb(&self) -> &HashDB { self }
|
||||||
|
fn as_hashdb_mut(&mut self) -> &mut HashDB { self }
|
||||||
|
}
|
||||||
|
388
util/src/journaldb/archivedb.rs
Normal file
388
util/src/journaldb/archivedb.rs
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Disk-backed HashDB implementation.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use rlp::*;
|
||||||
|
use hashdb::*;
|
||||||
|
use memorydb::*;
|
||||||
|
use super::traits::JournalDB;
|
||||||
|
use kvdb::{Database, DBTransaction, DatabaseConfig};
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay
|
||||||
|
/// and latent-removal semantics.
|
||||||
|
///
|
||||||
|
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
||||||
|
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
||||||
|
/// immediately. Rather some age (based on a linear but arbitrary metric) must pass before
|
||||||
|
/// the removals actually take effect.
|
||||||
|
pub struct ArchiveDB {
|
||||||
|
overlay: MemoryDB,
|
||||||
|
backing: Arc<Database>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// all keys must be at least 12 bytes
|
||||||
|
const LATEST_ERA_KEY : [u8; 12] = [ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
const VERSION_KEY : [u8; 12] = [ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
const DB_VERSION : u32 = 259;
|
||||||
|
|
||||||
|
impl ArchiveDB {
|
||||||
|
/// Create a new instance from file
|
||||||
|
pub fn new(path: &str) -> ArchiveDB {
|
||||||
|
let opts = DatabaseConfig {
|
||||||
|
prefix_size: Some(12) //use 12 bytes as prefix, this must match account_db prefix
|
||||||
|
};
|
||||||
|
let backing = Database::open(&opts, path).unwrap_or_else(|e| {
|
||||||
|
panic!("Error opening state db: {}", e);
|
||||||
|
});
|
||||||
|
if !backing.is_empty() {
|
||||||
|
match backing.get(&VERSION_KEY).map(|d| d.map(|v| decode::<u32>(&v))) {
|
||||||
|
Ok(Some(DB_VERSION)) => {},
|
||||||
|
v => panic!("Incompatible DB version, expected {}, got {:?}", DB_VERSION, v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveDB {
|
||||||
|
overlay: MemoryDB::new(),
|
||||||
|
backing: Arc::new(backing),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance with an anonymous temporary database.
|
||||||
|
#[cfg(test)]
|
||||||
|
fn new_temp() -> ArchiveDB {
|
||||||
|
let mut dir = env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
Self::new(dir.to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn payload(&self, key: &H256) -> Option<Bytes> {
|
||||||
|
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HashDB for ArchiveDB {
|
||||||
|
fn keys(&self) -> HashMap<H256, i32> {
|
||||||
|
let mut ret: HashMap<H256, i32> = HashMap::new();
|
||||||
|
for (key, _) in self.backing.iter() {
|
||||||
|
let h = H256::from_slice(key.deref());
|
||||||
|
ret.insert(h, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, refs) in self.overlay.keys().into_iter() {
|
||||||
|
let refs = *ret.get(&key).unwrap_or(&0) + refs;
|
||||||
|
ret.insert(key, refs);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, key: &H256) -> Option<&[u8]> {
|
||||||
|
let k = self.overlay.raw(key);
|
||||||
|
match k {
|
||||||
|
Some(&(ref d, rc)) if rc > 0 => Some(d),
|
||||||
|
_ => {
|
||||||
|
if let Some(x) = self.payload(key) {
|
||||||
|
Some(&self.overlay.denote(key, x).0)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&self, key: &H256) -> bool {
|
||||||
|
self.lookup(key).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 kill(&mut self, key: &H256) {
|
||||||
|
self.overlay.kill(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JournalDB for ArchiveDB {
|
||||||
|
fn spawn(&self) -> Box<JournalDB> {
|
||||||
|
Box::new(ArchiveDB {
|
||||||
|
overlay: MemoryDB::new(),
|
||||||
|
backing: self.backing.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_used(&self) -> usize {
|
||||||
|
self.overlay.mem_used()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.backing.get(&LATEST_ERA_KEY).expect("Low level database error").is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&mut self, _: u64, _: &H256, _: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
|
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 {
|
||||||
|
assert!(rc == 1);
|
||||||
|
batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
inserts += 1;
|
||||||
|
}
|
||||||
|
if rc < 0 {
|
||||||
|
assert!(rc == -1);
|
||||||
|
deletes += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try!(self.backing.write(batch));
|
||||||
|
Ok((inserts + deletes) as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use common::*;
|
||||||
|
use super::*;
|
||||||
|
use hashdb::*;
|
||||||
|
use journaldb::traits::JournalDB;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_same_in_fork() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
|
||||||
|
let x = jdb.insert(b"X");
|
||||||
|
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
||||||
|
jdb.commit(2, &b"2".sha3(), None).unwrap();
|
||||||
|
jdb.commit(3, &b"1002a".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
jdb.commit(4, &b"1003a".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&x);
|
||||||
|
jdb.commit(3, &b"1002b".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
let x = jdb.insert(b"X");
|
||||||
|
jdb.commit(4, &b"1003b".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.commit(5, &b"1004a".sha3(), Some((3, b"1002a".sha3()))).unwrap();
|
||||||
|
jdb.commit(6, &b"1005a".sha3(), Some((4, b"1003a".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&x));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn long_history() {
|
||||||
|
// history is 3
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
let h = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.remove(&h);
|
||||||
|
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.commit(2, &b"2".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.remove(&bar);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
assert!(jdb.exists(&baz));
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.remove(&baz);
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&baz));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((3, b"3".sha3()))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fork() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1a".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(1, &b"1b".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
assert!(jdb.exists(&baz));
|
||||||
|
|
||||||
|
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overwrite() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
jdb.insert(b"foo");
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
jdb.commit(3, &b"2".sha3(), Some((0, b"2".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fork_same_key() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(1, &b"1a".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.insert(b"foo");
|
||||||
|
jdb.commit(1, &b"1b".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
|
||||||
|
jdb.commit(2, &b"2a".sha3(), Some((1, b"1a".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reopen() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
let bar = H256::random();
|
||||||
|
|
||||||
|
let foo = {
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.emplace(bar.clone(), b"bar".to_vec());
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
foo
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reopen_remove() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
|
||||||
|
let foo = {
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
// foo is ancient history.
|
||||||
|
|
||||||
|
jdb.insert(b"foo");
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
foo
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((3, b"3".sha3()))).unwrap();
|
||||||
|
jdb.commit(5, &b"5".sha3(), Some((4, b"4".sha3()))).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn reopen_fork() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
let (foo, bar, baz) = {
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
jdb.remove(&foo);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1a".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(1, &b"1b".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
(foo, bar, baz)
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = ArchiveDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
util/src/journaldb/mod.rs
Normal file
78
util/src/journaldb/mod.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! JournalDB interface and implementation.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
|
||||||
|
/// Export the journaldb module.
|
||||||
|
pub mod traits;
|
||||||
|
mod archivedb;
|
||||||
|
mod optiononedb;
|
||||||
|
|
||||||
|
/// Export the JournalDB trait.
|
||||||
|
pub use self::traits::JournalDB;
|
||||||
|
|
||||||
|
/// A journal database algorithm.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Algorithm {
|
||||||
|
/// Keep all keys forever.
|
||||||
|
Archive,
|
||||||
|
|
||||||
|
/// Ancient and recent history maintained separately; recent history lasts for particular
|
||||||
|
/// number of blocks.
|
||||||
|
///
|
||||||
|
/// Inserts go into backing database, journal retains knowledge of whether backing DB key is
|
||||||
|
/// ancient or recent. Non-canon inserts get explicitly reverted and removed from backing DB.
|
||||||
|
EarlyMerge,
|
||||||
|
|
||||||
|
/// Ancient and recent history maintained separately; recent history lasts for particular
|
||||||
|
/// number of blocks.
|
||||||
|
///
|
||||||
|
/// Inserts go into memory overlay, which is tried for key fetches. Memory overlay gets
|
||||||
|
/// flushed in backing only at end of recent history.
|
||||||
|
OverlayRecent,
|
||||||
|
|
||||||
|
/// Ancient and recent history maintained separately; recent history lasts for particular
|
||||||
|
/// number of blocks.
|
||||||
|
///
|
||||||
|
/// References are counted in disk-backed DB.
|
||||||
|
RefCounted,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Algorithm {
|
||||||
|
fn default() -> Algorithm { Algorithm::Archive }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Algorithm {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", match self {
|
||||||
|
&Algorithm::Archive => "archive",
|
||||||
|
&Algorithm::EarlyMerge => "earlymerge",
|
||||||
|
&Algorithm::OverlayRecent => "overlayrecent",
|
||||||
|
&Algorithm::RefCounted => "refcounted",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new JournalDB trait object.
|
||||||
|
pub fn new(path: &str, algorithm: Algorithm) -> Box<JournalDB> {
|
||||||
|
match algorithm {
|
||||||
|
Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path)),
|
||||||
|
Algorithm::EarlyMerge => Box::new(optiononedb::OptionOneDB::new(path)),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ use common::*;
|
|||||||
use rlp::*;
|
use rlp::*;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
use memorydb::*;
|
use memorydb::*;
|
||||||
|
use super::traits::JournalDB;
|
||||||
use kvdb::{Database, DBTransaction, DatabaseConfig};
|
use kvdb::{Database, DBTransaction, DatabaseConfig};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::env;
|
use std::env;
|
||||||
@ -31,63 +32,38 @@ use std::env;
|
|||||||
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
||||||
/// immediately. Rather some age (based on a linear but arbitrary metric) must pass before
|
/// immediately. Rather some age (based on a linear but arbitrary metric) must pass before
|
||||||
/// the removals actually take effect.
|
/// the removals actually take effect.
|
||||||
pub struct JournalDB {
|
pub struct OptionOneDB {
|
||||||
overlay: MemoryDB,
|
overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
counters: Option<Arc<RwLock<HashMap<H256, i32>>>>,
|
counters: Option<Arc<RwLock<HashMap<H256, i32>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for JournalDB {
|
|
||||||
fn clone(&self) -> JournalDB {
|
|
||||||
JournalDB {
|
|
||||||
overlay: MemoryDB::new(),
|
|
||||||
backing: self.backing.clone(),
|
|
||||||
counters: self.counters.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// all keys must be at least 12 bytes
|
// all keys must be at least 12 bytes
|
||||||
const LATEST_ERA_KEY : [u8; 12] = [ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
const LATEST_ERA_KEY : [u8; 12] = [ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
const VERSION_KEY : [u8; 12] = [ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
const VERSION_KEY : [u8; 12] = [ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
|
||||||
const DB_VERSION : u32 = 3;
|
const DB_VERSION : u32 = 3;
|
||||||
const DB_VERSION_NO_JOURNAL : u32 = 3 + 256;
|
|
||||||
|
|
||||||
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
||||||
|
|
||||||
impl JournalDB {
|
impl OptionOneDB {
|
||||||
/// Create a new instance from file
|
/// Create a new instance from file
|
||||||
pub fn new(path: &str) -> JournalDB {
|
pub fn new(path: &str) -> OptionOneDB {
|
||||||
Self::from_prefs(path, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new instance from file
|
|
||||||
pub fn from_prefs(path: &str, prefer_journal: bool) -> JournalDB {
|
|
||||||
let opts = DatabaseConfig {
|
let opts = DatabaseConfig {
|
||||||
prefix_size: Some(12) //use 12 bytes as prefix, this must match account_db prefix
|
prefix_size: Some(12) //use 12 bytes as prefix, this must match account_db prefix
|
||||||
};
|
};
|
||||||
let backing = Database::open(&opts, path).unwrap_or_else(|e| {
|
let backing = Database::open(&opts, path).unwrap_or_else(|e| {
|
||||||
panic!("Error opening state db: {}", e);
|
panic!("Error opening state db: {}", e);
|
||||||
});
|
});
|
||||||
let with_journal;
|
|
||||||
if !backing.is_empty() {
|
if !backing.is_empty() {
|
||||||
match backing.get(&VERSION_KEY).map(|d| d.map(|v| decode::<u32>(&v))) {
|
match backing.get(&VERSION_KEY).map(|d| d.map(|v| decode::<u32>(&v))) {
|
||||||
Ok(Some(DB_VERSION)) => { with_journal = true; },
|
Ok(Some(DB_VERSION)) => {},
|
||||||
Ok(Some(DB_VERSION_NO_JOURNAL)) => { with_journal = false; },
|
|
||||||
v => panic!("Incompatible DB version, expected {}, got {:?}", DB_VERSION, v)
|
v => panic!("Incompatible DB version, expected {}, got {:?}", DB_VERSION, v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
backing.put(&VERSION_KEY, &encode(&(if prefer_journal { DB_VERSION } else { DB_VERSION_NO_JOURNAL }))).expect("Error writing version to database");
|
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
||||||
with_journal = prefer_journal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let counters = if with_journal {
|
let counters = Some(Arc::new(RwLock::new(OptionOneDB::read_counters(&backing))));
|
||||||
Some(Arc::new(RwLock::new(JournalDB::read_counters(&backing))))
|
OptionOneDB {
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
JournalDB {
|
|
||||||
overlay: MemoryDB::new(),
|
overlay: MemoryDB::new(),
|
||||||
backing: Arc::new(backing),
|
backing: Arc::new(backing),
|
||||||
counters: counters,
|
counters: counters,
|
||||||
@ -96,55 +72,12 @@ impl JournalDB {
|
|||||||
|
|
||||||
/// Create a new instance with an anonymous temporary database.
|
/// Create a new instance with an anonymous temporary database.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new_temp() -> JournalDB {
|
fn new_temp() -> OptionOneDB {
|
||||||
let mut dir = env::temp_dir();
|
let mut dir = env::temp_dir();
|
||||||
dir.push(H32::random().hex());
|
dir.push(H32::random().hex());
|
||||||
Self::new(dir.to_str().unwrap())
|
Self::new(dir.to_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this database has any commits
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.backing.get(&LATEST_ERA_KEY).expect("Low level database error").is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Commit all recent insert operations.
|
|
||||||
pub fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
|
||||||
let have_counters = self.counters.is_some();
|
|
||||||
if have_counters {
|
|
||||||
self.commit_with_counters(now, id, end)
|
|
||||||
} else {
|
|
||||||
self.commit_without_counters()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Drain the overlay and place it into a batch for the DB.
|
|
||||||
fn batch_overlay_insertions(overlay: &mut MemoryDB, batch: &DBTransaction) -> usize {
|
|
||||||
let mut inserts = 0usize;
|
|
||||||
let mut deletes = 0usize;
|
|
||||||
for i in overlay.drain().into_iter() {
|
|
||||||
let (key, (value, rc)) = i;
|
|
||||||
if rc > 0 {
|
|
||||||
assert!(rc == 1);
|
|
||||||
batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?");
|
|
||||||
inserts += 1;
|
|
||||||
}
|
|
||||||
if rc < 0 {
|
|
||||||
assert!(rc == -1);
|
|
||||||
deletes += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trace!("commit: Inserted {}, Deleted {} nodes", inserts, deletes);
|
|
||||||
inserts + deletes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Just commit the overlay into the backing DB.
|
|
||||||
fn commit_without_counters(&mut self) -> Result<u32, UtilError> {
|
|
||||||
let batch = DBTransaction::new();
|
|
||||||
let ret = Self::batch_overlay_insertions(&mut self.overlay, &batch);
|
|
||||||
try!(self.backing.write(batch));
|
|
||||||
Ok(ret as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn morph_key(key: &H256, index: u8) -> Bytes {
|
fn morph_key(key: &H256, index: u8) -> Bytes {
|
||||||
let mut ret = key.bytes().to_owned();
|
let mut ret = key.bytes().to_owned();
|
||||||
ret.push(index);
|
ret.push(index);
|
||||||
@ -226,9 +159,106 @@ impl JournalDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit all recent insert operations and historical removals from the old era
|
fn payload(&self, key: &H256) -> Option<Bytes> {
|
||||||
/// to the backing database.
|
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
|
||||||
fn commit_with_counters(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
}
|
||||||
|
|
||||||
|
fn read_counters(db: &Database) -> HashMap<H256, i32> {
|
||||||
|
let mut counters = HashMap::new();
|
||||||
|
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
||||||
|
let mut era = decode::<u64>(&val);
|
||||||
|
loop {
|
||||||
|
let mut index = 0usize;
|
||||||
|
while let Some(rlp_data) = db.get({
|
||||||
|
let mut r = RlpStream::new_list(3);
|
||||||
|
r.append(&era);
|
||||||
|
r.append(&index);
|
||||||
|
r.append(&&PADDING[..]);
|
||||||
|
&r.drain()
|
||||||
|
}).expect("Low-level database error.") {
|
||||||
|
trace!("read_counters: era={}, index={}", era, index);
|
||||||
|
let rlp = Rlp::new(&rlp_data);
|
||||||
|
let inserts: Vec<H256> = rlp.val_at(1);
|
||||||
|
Self::replay_keys(&inserts, db, &mut counters);
|
||||||
|
index += 1;
|
||||||
|
};
|
||||||
|
if index == 0 || era == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
era -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("Recovered {} counters", counters.len());
|
||||||
|
counters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HashDB for OptionOneDB {
|
||||||
|
fn keys(&self) -> HashMap<H256, i32> {
|
||||||
|
let mut ret: HashMap<H256, i32> = HashMap::new();
|
||||||
|
for (key, _) in self.backing.iter() {
|
||||||
|
let h = H256::from_slice(key.deref());
|
||||||
|
ret.insert(h, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, refs) in self.overlay.keys().into_iter() {
|
||||||
|
let refs = *ret.get(&key).unwrap_or(&0) + refs;
|
||||||
|
ret.insert(key, refs);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, key: &H256) -> Option<&[u8]> {
|
||||||
|
let k = self.overlay.raw(key);
|
||||||
|
match k {
|
||||||
|
Some(&(ref d, rc)) if rc > 0 => Some(d),
|
||||||
|
_ => {
|
||||||
|
if let Some(x) = self.payload(key) {
|
||||||
|
Some(&self.overlay.denote(key, x).0)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&self, key: &H256) -> bool {
|
||||||
|
self.lookup(key).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 kill(&mut self, key: &H256) {
|
||||||
|
self.overlay.kill(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JournalDB for OptionOneDB {
|
||||||
|
fn spawn(&self) -> Box<JournalDB> {
|
||||||
|
Box::new(OptionOneDB {
|
||||||
|
overlay: MemoryDB::new(),
|
||||||
|
backing: self.backing.clone(),
|
||||||
|
counters: self.counters.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_used(&self) -> usize {
|
||||||
|
self.overlay.mem_used() + match self.counters {
|
||||||
|
Some(ref c) => c.read().unwrap().heap_size_of_children(),
|
||||||
|
None => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.backing.get(&LATEST_ERA_KEY).expect("Low level database error").is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
// journal format:
|
// journal format:
|
||||||
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ]
|
// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
@ -339,99 +369,13 @@ impl JournalDB {
|
|||||||
try!(batch.delete(&last));
|
try!(batch.delete(&last));
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
trace!("JournalDB: delete journal for time #{}.{}, (canon was {})", end_era, index, canon_id);
|
trace!("OptionOneDB: delete journal for time #{}.{}, (canon was {})", end_era, index, canon_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(self.backing.write(batch));
|
try!(self.backing.write(batch));
|
||||||
// trace!("JournalDB::commit() deleted {} nodes", deletes);
|
// trace!("OptionOneDB::commit() deleted {} nodes", deletes);
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payload(&self, key: &H256) -> Option<Bytes> {
|
|
||||||
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_counters(db: &Database) -> HashMap<H256, i32> {
|
|
||||||
let mut counters = HashMap::new();
|
|
||||||
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
|
||||||
let mut era = decode::<u64>(&val);
|
|
||||||
loop {
|
|
||||||
let mut index = 0usize;
|
|
||||||
while let Some(rlp_data) = db.get({
|
|
||||||
let mut r = RlpStream::new_list(3);
|
|
||||||
r.append(&era);
|
|
||||||
r.append(&index);
|
|
||||||
r.append(&&PADDING[..]);
|
|
||||||
&r.drain()
|
|
||||||
}).expect("Low-level database error.") {
|
|
||||||
trace!("read_counters: era={}, index={}", era, index);
|
|
||||||
let rlp = Rlp::new(&rlp_data);
|
|
||||||
let inserts: Vec<H256> = rlp.val_at(1);
|
|
||||||
Self::replay_keys(&inserts, db, &mut counters);
|
|
||||||
index += 1;
|
|
||||||
};
|
|
||||||
if index == 0 || era == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
era -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trace!("Recovered {} counters", counters.len());
|
|
||||||
counters
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns heap memory size used
|
|
||||||
pub fn mem_used(&self) -> usize {
|
|
||||||
self.overlay.mem_used() + match self.counters {
|
|
||||||
Some(ref c) => c.read().unwrap().heap_size_of_children(),
|
|
||||||
None => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HashDB for JournalDB {
|
|
||||||
fn keys(&self) -> HashMap<H256, i32> {
|
|
||||||
let mut ret: HashMap<H256, i32> = HashMap::new();
|
|
||||||
for (key, _) in self.backing.iter() {
|
|
||||||
let h = H256::from_slice(key.deref());
|
|
||||||
ret.insert(h, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (key, refs) in self.overlay.keys().into_iter() {
|
|
||||||
let refs = *ret.get(&key).unwrap_or(&0) + refs;
|
|
||||||
ret.insert(key, refs);
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, key: &H256) -> Option<&[u8]> {
|
|
||||||
let k = self.overlay.raw(key);
|
|
||||||
match k {
|
|
||||||
Some(&(ref d, rc)) if rc > 0 => Some(d),
|
|
||||||
_ => {
|
|
||||||
if let Some(x) = self.payload(key) {
|
|
||||||
Some(&self.overlay.denote(key, x).0)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exists(&self, key: &H256) -> bool {
|
|
||||||
self.lookup(key).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 kill(&mut self, key: &H256) {
|
|
||||||
self.overlay.kill(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -439,11 +383,12 @@ mod tests {
|
|||||||
use common::*;
|
use common::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
|
use journaldb::traits::JournalDB;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_same_in_fork() {
|
fn insert_same_in_fork() {
|
||||||
// history is 1
|
// history is 1
|
||||||
let mut jdb = JournalDB::new_temp();
|
let mut jdb = OptionOneDB::new_temp();
|
||||||
|
|
||||||
let x = jdb.insert(b"X");
|
let x = jdb.insert(b"X");
|
||||||
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
||||||
@ -465,7 +410,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn long_history() {
|
fn long_history() {
|
||||||
// history is 3
|
// history is 3
|
||||||
let mut jdb = JournalDB::new_temp();
|
let mut jdb = OptionOneDB::new_temp();
|
||||||
let h = jdb.insert(b"foo");
|
let h = jdb.insert(b"foo");
|
||||||
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
assert!(jdb.exists(&h));
|
assert!(jdb.exists(&h));
|
||||||
@ -483,7 +428,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn complex() {
|
fn complex() {
|
||||||
// history is 1
|
// history is 1
|
||||||
let mut jdb = JournalDB::new_temp();
|
let mut jdb = OptionOneDB::new_temp();
|
||||||
|
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
let bar = jdb.insert(b"bar");
|
let bar = jdb.insert(b"bar");
|
||||||
@ -521,7 +466,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fork() {
|
fn fork() {
|
||||||
// history is 1
|
// history is 1
|
||||||
let mut jdb = JournalDB::new_temp();
|
let mut jdb = OptionOneDB::new_temp();
|
||||||
|
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
let bar = jdb.insert(b"bar");
|
let bar = jdb.insert(b"bar");
|
||||||
@ -549,7 +494,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn overwrite() {
|
fn overwrite() {
|
||||||
// history is 1
|
// history is 1
|
||||||
let mut jdb = JournalDB::new_temp();
|
let mut jdb = OptionOneDB::new_temp();
|
||||||
|
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
@ -568,7 +513,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fork_same_key() {
|
fn fork_same_key() {
|
||||||
// history is 1
|
// history is 1
|
||||||
let mut jdb = JournalDB::new_temp();
|
let mut jdb = OptionOneDB::new_temp();
|
||||||
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
@ -590,7 +535,7 @@ mod tests {
|
|||||||
let bar = H256::random();
|
let bar = H256::random();
|
||||||
|
|
||||||
let foo = {
|
let foo = {
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
// history is 1
|
// history is 1
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
jdb.emplace(bar.clone(), b"bar".to_vec());
|
jdb.emplace(bar.clone(), b"bar".to_vec());
|
||||||
@ -599,13 +544,13 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
jdb.remove(&foo);
|
jdb.remove(&foo);
|
||||||
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
assert!(jdb.exists(&bar));
|
assert!(jdb.exists(&bar));
|
||||||
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
@ -619,7 +564,7 @@ mod tests {
|
|||||||
dir.push(H32::random().hex());
|
dir.push(H32::random().hex());
|
||||||
|
|
||||||
let foo = {
|
let foo = {
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
// history is 1
|
// history is 1
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
@ -633,7 +578,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
jdb.remove(&foo);
|
jdb.remove(&foo);
|
||||||
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
@ -648,7 +593,7 @@ mod tests {
|
|||||||
let mut dir = ::std::env::temp_dir();
|
let mut dir = ::std::env::temp_dir();
|
||||||
dir.push(H32::random().hex());
|
dir.push(H32::random().hex());
|
||||||
let (foo, bar, baz) = {
|
let (foo, bar, baz) = {
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
// history is 1
|
// history is 1
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
let bar = jdb.insert(b"bar");
|
let bar = jdb.insert(b"bar");
|
||||||
@ -663,7 +608,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = OptionOneDB::new(dir.to_str().unwrap());
|
||||||
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
assert!(!jdb.exists(&baz));
|
assert!(!jdb.exists(&baz));
|
37
util/src/journaldb/traits.rs
Normal file
37
util/src/journaldb/traits.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Disk-backed HashDB implementation.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use hashdb::*;
|
||||||
|
|
||||||
|
/// A HashDB which can manage a short-term journal potentially containing many forks of mutually
|
||||||
|
/// exclusive actions.
|
||||||
|
pub trait JournalDB : HashDB + Send + Sync {
|
||||||
|
/// Return a copy of ourself, in a box.
|
||||||
|
fn spawn(&self) -> Box<JournalDB>;
|
||||||
|
|
||||||
|
/// Returns heap memory size used
|
||||||
|
fn mem_used(&self) -> usize;
|
||||||
|
|
||||||
|
/// Check if this database has any commits
|
||||||
|
fn is_empty(&self) -> bool;
|
||||||
|
|
||||||
|
/// Commit all recent insert operations and canonical historical commits' removals from the
|
||||||
|
/// old era to the backing database, reverting any non-canonical historical commit's inserts.
|
||||||
|
fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError>;
|
||||||
|
}
|
@ -154,7 +154,7 @@ pub use rlp::*;
|
|||||||
pub use hashdb::*;
|
pub use hashdb::*;
|
||||||
pub use memorydb::*;
|
pub use memorydb::*;
|
||||||
pub use overlaydb::*;
|
pub use overlaydb::*;
|
||||||
pub use journaldb::*;
|
pub use journaldb::JournalDB;
|
||||||
pub use math::*;
|
pub use math::*;
|
||||||
pub use crypto::*;
|
pub use crypto::*;
|
||||||
pub use triehash::*;
|
pub use triehash::*;
|
||||||
|
@ -36,6 +36,7 @@ use kvdb::{Database};
|
|||||||
///
|
///
|
||||||
/// `lookup()` and `contains()` maintain normal behaviour - all `insert()` and `remove()`
|
/// `lookup()` and `contains()` maintain normal behaviour - all `insert()` and `remove()`
|
||||||
/// queries have an immediate effect in terms of these functions.
|
/// queries have an immediate effect in terms of these functions.
|
||||||
|
//#[derive(Clone)]
|
||||||
pub struct OverlayDB {
|
pub struct OverlayDB {
|
||||||
overlay: MemoryDB,
|
overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
|
Loading…
Reference in New Issue
Block a user