State export
This commit is contained in:
@@ -22,7 +22,7 @@ use std::time::{Instant};
|
||||
use time::precise_time_ns;
|
||||
|
||||
// util
|
||||
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock};
|
||||
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock, Hashable};
|
||||
use util::{journaldb, TrieFactory, Trie};
|
||||
use util::trie::TrieSpec;
|
||||
use util::{U256, H256, Address, H2048, Uint, FixedHash};
|
||||
@@ -172,9 +172,11 @@ impl Client {
|
||||
false => TrieSpec::Secure,
|
||||
};
|
||||
|
||||
let trie_factory = TrieFactory::new(trie_spec);
|
||||
|
||||
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
||||
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
|
||||
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
||||
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db, &trie_factory)) {
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
@@ -216,7 +218,7 @@ impl Client {
|
||||
|
||||
let factories = Factories {
|
||||
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||
trie: TrieFactory::new(trie_spec),
|
||||
trie: trie_factory,
|
||||
accountdb: Default::default(),
|
||||
};
|
||||
|
||||
@@ -952,6 +954,10 @@ impl BlockChainClient for Client {
|
||||
self.state_at(id).map(|s| s.nonce(address))
|
||||
}
|
||||
|
||||
fn storage_root(&self, address: &Address, id: BlockID) -> Option<H256> {
|
||||
self.state_at(id).and_then(|s| s.storage_root(address))
|
||||
}
|
||||
|
||||
fn block_hash(&self, id: BlockID) -> Option<H256> {
|
||||
let chain = self.chain.read();
|
||||
Self::block_hash(&chain, id)
|
||||
@@ -969,7 +975,7 @@ impl BlockChainClient for Client {
|
||||
self.state_at(id).map(|s| s.storage_at(address, position))
|
||||
}
|
||||
|
||||
fn list_accounts(&self, id: BlockID) -> Option<Vec<Address>> {
|
||||
fn list_accounts(&self, id: BlockID, after: Option<&Address>, count: u64) -> Option<Vec<Address>> {
|
||||
if !self.factories.trie.is_fat() {
|
||||
trace!(target: "fatdb", "list_accounts: Not a fat DB");
|
||||
return None;
|
||||
@@ -989,18 +995,68 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
};
|
||||
|
||||
let iter = match trie.iter() {
|
||||
let mut iter = match trie.iter() {
|
||||
Ok(iter) => iter,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if let Some(after) = after {
|
||||
if let Err(e) = iter.seek(after) {
|
||||
trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
let accounts = iter.filter_map(|item| {
|
||||
item.ok().map(|(addr, _)| Address::from_slice(&addr))
|
||||
}).collect();
|
||||
}).take(count as usize).collect();
|
||||
|
||||
Some(accounts)
|
||||
}
|
||||
|
||||
fn list_storage(&self, id: BlockID, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> {
|
||||
if !self.factories.trie.is_fat() {
|
||||
trace!(target: "fatdb", "list_stroage: Not a fat DB");
|
||||
return None;
|
||||
}
|
||||
|
||||
let state = match self.state_at(id) {
|
||||
Some(state) => state,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let root = match state.storage_root(account) {
|
||||
Some(root) => root,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (_, db) = state.drop();
|
||||
let account_db = self.factories.accountdb.readonly(db.as_hashdb(), account.sha3());
|
||||
let trie = match self.factories.trie.readonly(account_db.as_hashdb(), &root) {
|
||||
Ok(trie) => trie,
|
||||
_ => {
|
||||
trace!(target: "fatdb", "list_storage: Couldn't open the DB");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut iter = match trie.iter() {
|
||||
Ok(iter) => iter,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if let Some(after) = after {
|
||||
if let Err(e) = iter.seek(after) {
|
||||
trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
let keys = iter.filter_map(|item| {
|
||||
item.ok().map(|(key, _)| H256::from_slice(&key))
|
||||
}).take(count as usize).collect();
|
||||
|
||||
Some(keys)
|
||||
}
|
||||
|
||||
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction> {
|
||||
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
let genesis_header = self.spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
self.spec.ensure_db_good(&mut db).unwrap();
|
||||
self.spec.ensure_db_good(&mut db, &TrieFactory::default()).unwrap();
|
||||
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let mut open_block = OpenBlock::new(
|
||||
@@ -385,6 +385,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn storage_root(&self, _address: &Address, _id: BlockID) -> Option<H256> {
|
||||
None
|
||||
}
|
||||
|
||||
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||
self.nonce(address, BlockID::Latest).unwrap()
|
||||
}
|
||||
@@ -416,10 +420,13 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn list_accounts(&self, _id: BlockID) -> Option<Vec<Address>> {
|
||||
fn list_accounts(&self, _id: BlockID, _after: Option<&Address>, _count: u64) -> Option<Vec<Address>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn list_storage(&self, _id: BlockID, _account: &Address, _after: Option<&H256>, _count: u64) -> Option<Vec<H256>> {
|
||||
None
|
||||
}
|
||||
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
|
||||
None // Simple default.
|
||||
}
|
||||
|
||||
@@ -68,6 +68,10 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// May not fail on BlockID::Latest.
|
||||
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Attempt to get address storage root at given block.
|
||||
/// May not fail on BlockID::Latest.
|
||||
fn storage_root(&self, address: &Address, id: BlockID) -> Option<H256>;
|
||||
|
||||
/// Get address nonce at the latest block's state.
|
||||
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||
self.nonce(address, BlockID::Latest)
|
||||
@@ -114,7 +118,12 @@ pub trait BlockChainClient : Sync + Send {
|
||||
}
|
||||
|
||||
/// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`.
|
||||
fn list_accounts(&self, id: BlockID) -> Option<Vec<Address>>;
|
||||
/// If `after` is set the list starts with the following item.
|
||||
fn list_accounts(&self, id: BlockID, after: Option<&Address>, count: u64) -> Option<Vec<Address>>;
|
||||
|
||||
/// Get a list of all storage keys in the block `id`, if fat DB is in operation, otherwise `None`.
|
||||
/// If `after` is set the list starts with the following item.
|
||||
fn list_storage(&self, id: BlockID, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>>;
|
||||
|
||||
/// Get transaction with given hash.
|
||||
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
|
||||
|
||||
@@ -64,13 +64,13 @@ impl PodAccount {
|
||||
}
|
||||
|
||||
/// Place additional data into given hash DB.
|
||||
pub fn insert_additional(&self, db: &mut AccountDBMut) {
|
||||
pub fn insert_additional(&self, db: &mut AccountDBMut, factory: &TrieFactory) {
|
||||
match self.code {
|
||||
Some(ref c) if !c.is_empty() => { db.insert(c); }
|
||||
_ => {}
|
||||
}
|
||||
let mut r = H256::new();
|
||||
let mut t = SecTrieDBMut::new(db, &mut r);
|
||||
let mut t = factory.create(db, &mut r);
|
||||
for (k, v) in &self.storage {
|
||||
if let Err(e) = t.insert(k, &rlp::encode(&U256::from(&**v))) {
|
||||
warn!("Encountered potential DB corruption: {}", e);
|
||||
|
||||
@@ -244,13 +244,13 @@ impl Spec {
|
||||
}
|
||||
|
||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||
pub fn ensure_db_good(&self, db: &mut StateDB) -> Result<bool, Box<TrieError>> {
|
||||
pub fn ensure_db_good(&self, db: &mut StateDB, factory: &TrieFactory) -> Result<bool, Box<TrieError>> {
|
||||
if !db.as_hashdb().contains(&self.state_root()) {
|
||||
trace!(target: "spec", "ensure_db_good: Fresh database? Cannot find state root {}", self.state_root());
|
||||
let mut root = H256::new();
|
||||
|
||||
{
|
||||
let mut t = SecTrieDBMut::new(db.as_hashdb_mut(), &mut root);
|
||||
let mut t = factory.create(db.as_hashdb_mut(), &mut root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
try!(t.insert(&**address, &account.rlp()));
|
||||
}
|
||||
@@ -258,7 +258,7 @@ impl Spec {
|
||||
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
db.note_non_null_account(address);
|
||||
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address));
|
||||
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address), factory);
|
||||
}
|
||||
assert!(db.as_hashdb().contains(&self.state_root()));
|
||||
Ok(true)
|
||||
|
||||
@@ -314,11 +314,10 @@ impl Account {
|
||||
self.code_hash == SHA3_EMPTY
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// return the storage root associated with this account or None if it has been altered via the overlay.
|
||||
/// Return the storage root associated with this account or None if it has been altered via the overlay.
|
||||
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
|
||||
|
||||
/// return the storage overlay.
|
||||
/// Return the storage overlay.
|
||||
pub fn storage_changes(&self) -> &HashMap<H256, H256> { &self.storage_changes }
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
|
||||
@@ -369,6 +369,12 @@ impl State {
|
||||
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
|
||||
}
|
||||
|
||||
/// Get the storage root of account `a`.
|
||||
pub fn storage_root(&self, a: &Address) -> Option<H256> {
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().and_then(|account| account.storage_root().cloned()))
|
||||
}
|
||||
|
||||
/// Mutate storage of account `address` so that it is `value` for `key`.
|
||||
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
|
||||
// Storage key search and update works like this:
|
||||
|
||||
Reference in New Issue
Block a user