State export
This commit is contained in:
parent
8b3cdc513b
commit
b0555fbe7c
@ -22,7 +22,7 @@ use std::time::{Instant};
|
|||||||
use time::precise_time_ns;
|
use time::precise_time_ns;
|
||||||
|
|
||||||
// util
|
// util
|
||||||
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock};
|
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock, Hashable};
|
||||||
use util::{journaldb, TrieFactory, Trie};
|
use util::{journaldb, TrieFactory, Trie};
|
||||||
use util::trie::TrieSpec;
|
use util::trie::TrieSpec;
|
||||||
use util::{U256, H256, Address, H2048, Uint, FixedHash};
|
use util::{U256, H256, Address, H2048, Uint, FixedHash};
|
||||||
@ -172,9 +172,11 @@ impl Client {
|
|||||||
false => TrieSpec::Secure,
|
false => TrieSpec::Secure,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let trie_factory = TrieFactory::new(trie_spec);
|
||||||
|
|
||||||
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
||||||
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
|
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);
|
let mut batch = DBTransaction::new(&db);
|
||||||
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
||||||
try!(db.write(batch).map_err(ClientError::Database));
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
@ -216,7 +218,7 @@ impl Client {
|
|||||||
|
|
||||||
let factories = Factories {
|
let factories = Factories {
|
||||||
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||||
trie: TrieFactory::new(trie_spec),
|
trie: trie_factory,
|
||||||
accountdb: Default::default(),
|
accountdb: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -952,6 +954,10 @@ impl BlockChainClient for Client {
|
|||||||
self.state_at(id).map(|s| s.nonce(address))
|
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> {
|
fn block_hash(&self, id: BlockID) -> Option<H256> {
|
||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
Self::block_hash(&chain, id)
|
Self::block_hash(&chain, id)
|
||||||
@ -969,7 +975,7 @@ impl BlockChainClient for Client {
|
|||||||
self.state_at(id).map(|s| s.storage_at(address, position))
|
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() {
|
if !self.factories.trie.is_fat() {
|
||||||
trace!(target: "fatdb", "list_accounts: Not a fat DB");
|
trace!(target: "fatdb", "list_accounts: Not a fat DB");
|
||||||
return None;
|
return None;
|
||||||
@ -989,18 +995,68 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let iter = match trie.iter() {
|
let mut iter = match trie.iter() {
|
||||||
Ok(iter) => iter,
|
Ok(iter) => iter,
|
||||||
_ => return None,
|
_ => 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| {
|
let accounts = iter.filter_map(|item| {
|
||||||
item.ok().map(|(addr, _)| Address::from_slice(&addr))
|
item.ok().map(|(addr, _)| Address::from_slice(&addr))
|
||||||
}).collect();
|
}).take(count as usize).collect();
|
||||||
|
|
||||||
Some(accounts)
|
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> {
|
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction> {
|
||||||
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
|
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 genesis_header = self.spec.genesis_header();
|
||||||
let mut db_result = get_temp_state_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
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 last_hashes = vec![genesis_header.hash()];
|
||||||
let mut open_block = OpenBlock::new(
|
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 {
|
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||||
self.nonce(address, BlockID::Latest).unwrap()
|
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
|
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> {
|
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
|
||||||
None // Simple default.
|
None // Simple default.
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,10 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// May not fail on BlockID::Latest.
|
/// May not fail on BlockID::Latest.
|
||||||
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>;
|
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.
|
/// Get address nonce at the latest block's state.
|
||||||
fn latest_nonce(&self, address: &Address) -> U256 {
|
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||||
self.nonce(address, BlockID::Latest)
|
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`.
|
/// 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.
|
/// Get transaction with given hash.
|
||||||
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
|
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
|
||||||
|
@ -64,13 +64,13 @@ impl PodAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Place additional data into given hash DB.
|
/// 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 {
|
match self.code {
|
||||||
Some(ref c) if !c.is_empty() => { db.insert(c); }
|
Some(ref c) if !c.is_empty() => { db.insert(c); }
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let mut r = H256::new();
|
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 {
|
for (k, v) in &self.storage {
|
||||||
if let Err(e) = t.insert(k, &rlp::encode(&U256::from(&**v))) {
|
if let Err(e) = t.insert(k, &rlp::encode(&U256::from(&**v))) {
|
||||||
warn!("Encountered potential DB corruption: {}", e);
|
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.
|
/// 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()) {
|
if !db.as_hashdb().contains(&self.state_root()) {
|
||||||
trace!(target: "spec", "ensure_db_good: Fresh database? Cannot find state root {}", 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 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() {
|
for (address, account) in self.genesis_state.get().iter() {
|
||||||
try!(t.insert(&**address, &account.rlp()));
|
try!(t.insert(&**address, &account.rlp()));
|
||||||
}
|
}
|
||||||
@ -258,7 +258,7 @@ impl Spec {
|
|||||||
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
|
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
|
||||||
for (address, account) in self.genesis_state.get().iter() {
|
for (address, account) in self.genesis_state.get().iter() {
|
||||||
db.note_non_null_account(address);
|
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()));
|
assert!(db.as_hashdb().contains(&self.state_root()));
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -314,11 +314,10 @@ impl Account {
|
|||||||
self.code_hash == SHA3_EMPTY
|
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} }
|
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 }
|
pub fn storage_changes(&self) -> &HashMap<H256, H256> { &self.storage_changes }
|
||||||
|
|
||||||
/// Increment the nonce of the account by one.
|
/// 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()))
|
|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`.
|
/// 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 {
|
||||||
// Storage key search and update works like this:
|
// Storage key search and update works like this:
|
||||||
|
@ -22,7 +22,7 @@ use std::thread::sleep;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
use io::{PanicHandler, ForwardPanic};
|
use io::{PanicHandler, ForwardPanic};
|
||||||
use util::{ToPretty, Uint};
|
use util::{ToPretty, Uint, U256, Address, Hashable};
|
||||||
use rlp::PayloadInfo;
|
use rlp::PayloadInfo;
|
||||||
use ethcore::service::ClientService;
|
use ethcore::service::ClientService;
|
||||||
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockID};
|
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockID};
|
||||||
@ -65,6 +65,7 @@ impl FromStr for DataFormat {
|
|||||||
pub enum BlockchainCmd {
|
pub enum BlockchainCmd {
|
||||||
Import(ImportBlockchain),
|
Import(ImportBlockchain),
|
||||||
Export(ExportBlockchain),
|
Export(ExportBlockchain),
|
||||||
|
ExportState(ExportState),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -103,10 +104,29 @@ pub struct ExportBlockchain {
|
|||||||
pub check_seal: bool,
|
pub check_seal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ExportState {
|
||||||
|
pub spec: SpecType,
|
||||||
|
pub cache_config: CacheConfig,
|
||||||
|
pub dirs: Directories,
|
||||||
|
pub file_path: Option<String>,
|
||||||
|
pub format: Option<DataFormat>,
|
||||||
|
pub pruning: Pruning,
|
||||||
|
pub pruning_history: u64,
|
||||||
|
pub compaction: DatabaseCompactionProfile,
|
||||||
|
pub wal: bool,
|
||||||
|
pub fat_db: Switch,
|
||||||
|
pub tracing: Switch,
|
||||||
|
pub at: BlockID,
|
||||||
|
pub storage: bool,
|
||||||
|
pub code: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(cmd: BlockchainCmd) -> Result<String, String> {
|
pub fn execute(cmd: BlockchainCmd) -> Result<String, String> {
|
||||||
match cmd {
|
match cmd {
|
||||||
BlockchainCmd::Import(import_cmd) => execute_import(import_cmd),
|
BlockchainCmd::Import(import_cmd) => execute_import(import_cmd),
|
||||||
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
|
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
|
||||||
|
BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,23 +281,28 @@ fn execute_import(cmd: ImportBlockchain) -> Result<String, String> {
|
|||||||
).into())
|
).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
|
fn start_client(
|
||||||
// Setup panic handler
|
dirs: Directories,
|
||||||
let panic_handler = PanicHandler::new_in_arc();
|
spec: SpecType,
|
||||||
|
pruning: Pruning,
|
||||||
|
pruning_history: u64,
|
||||||
|
tracing: Switch,
|
||||||
|
fat_db: Switch,
|
||||||
|
compaction: DatabaseCompactionProfile,
|
||||||
|
wal: bool,
|
||||||
|
cache_config: CacheConfig) -> Result<ClientService, String> {
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
try!(cmd.dirs.create_dirs(false, false));
|
try!(dirs.create_dirs(false, false));
|
||||||
|
|
||||||
let format = cmd.format.unwrap_or_default();
|
|
||||||
|
|
||||||
// load spec file
|
// load spec file
|
||||||
let spec = try!(cmd.spec.spec());
|
let spec = try!(spec.spec());
|
||||||
|
|
||||||
// load genesis hash
|
// load genesis hash
|
||||||
let genesis_hash = spec.genesis_header().hash();
|
let genesis_hash = spec.genesis_header().hash();
|
||||||
|
|
||||||
// database paths
|
// database paths
|
||||||
let db_dirs = cmd.dirs.database(genesis_hash, spec.fork_name.clone());
|
let db_dirs = dirs.database(genesis_hash, spec.fork_name.clone());
|
||||||
|
|
||||||
// user defaults path
|
// user defaults path
|
||||||
let user_defaults_path = db_dirs.user_defaults_path();
|
let user_defaults_path = db_dirs.user_defaults_path();
|
||||||
@ -288,34 +313,43 @@ fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
|
|||||||
fdlimit::raise_fd_limit();
|
fdlimit::raise_fd_limit();
|
||||||
|
|
||||||
// select pruning algorithm
|
// select pruning algorithm
|
||||||
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
|
let algorithm = pruning.to_algorithm(&user_defaults);
|
||||||
|
|
||||||
// check if tracing is on
|
// check if tracing is on
|
||||||
let tracing = try!(tracing_switch_to_bool(cmd.tracing, &user_defaults));
|
let tracing = try!(tracing_switch_to_bool(tracing, &user_defaults));
|
||||||
|
|
||||||
// check if fatdb is on
|
// check if fatdb is on
|
||||||
let fat_db = try!(fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm));
|
let fat_db = try!(fatdb_switch_to_bool(fat_db, &user_defaults, algorithm));
|
||||||
|
|
||||||
// prepare client and snapshot paths.
|
// prepare client and snapshot paths.
|
||||||
let client_path = db_dirs.client_path(algorithm);
|
let client_path = db_dirs.client_path(algorithm);
|
||||||
let snapshot_path = db_dirs.snapshot_path();
|
let snapshot_path = db_dirs.snapshot_path();
|
||||||
|
|
||||||
// execute upgrades
|
// execute upgrades
|
||||||
try!(execute_upgrades(&db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.fork_path().as_path())));
|
try!(execute_upgrades(&db_dirs, algorithm, compaction.compaction_profile(db_dirs.fork_path().as_path())));
|
||||||
|
|
||||||
// prepare client config
|
// prepare client config
|
||||||
let client_config = to_client_config(&cmd.cache_config, Mode::Active, tracing, fat_db, cmd.compaction, cmd.wal, VMType::default(), "".into(), algorithm, cmd.pruning_history, cmd.check_seal);
|
let client_config = to_client_config(&cache_config, Mode::Active, tracing, fat_db, compaction, wal, VMType::default(), "".into(), algorithm, pruning_history, true);
|
||||||
|
|
||||||
let service = try!(ClientService::start(
|
let service = try!(ClientService::start(
|
||||||
client_config,
|
client_config,
|
||||||
&spec,
|
&spec,
|
||||||
&client_path,
|
&client_path,
|
||||||
&snapshot_path,
|
&snapshot_path,
|
||||||
&cmd.dirs.ipc_path(),
|
&dirs.ipc_path(),
|
||||||
Arc::new(Miner::with_spec(&spec)),
|
Arc::new(Miner::with_spec(&spec)),
|
||||||
).map_err(|e| format!("Client service error: {:?}", e)));
|
).map_err(|e| format!("Client service error: {:?}", e)));
|
||||||
|
|
||||||
drop(spec);
|
drop(spec);
|
||||||
|
Ok(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
|
||||||
|
// Setup panic handler
|
||||||
|
let service = try!(start_client(cmd.dirs, cmd.spec, cmd.pruning, cmd.pruning_history, cmd.tracing, cmd.fat_db, cmd.compaction, cmd.wal, cmd.cache_config));
|
||||||
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
let format = cmd.format.unwrap_or_default();
|
||||||
|
|
||||||
panic_handler.forward_from(&service);
|
panic_handler.forward_from(&service);
|
||||||
let client = service.client();
|
let client = service.client();
|
||||||
@ -329,6 +363,9 @@ fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
|
|||||||
let to = try!(client.block_number(cmd.to_block).ok_or("To block could not be found"));
|
let to = try!(client.block_number(cmd.to_block).ok_or("To block could not be found"));
|
||||||
|
|
||||||
for i in from..(to + 1) {
|
for i in from..(to + 1) {
|
||||||
|
if i % 10000 == 0 {
|
||||||
|
info!("#{}", i);
|
||||||
|
}
|
||||||
let b = try!(client.block(BlockID::Number(i)).ok_or("Error exporting incomplete chain"));
|
let b = try!(client.block(BlockID::Number(i)).ok_or("Error exporting incomplete chain"));
|
||||||
match format {
|
match format {
|
||||||
DataFormat::Binary => { out.write(&b).expect("Couldn't write to stream."); }
|
DataFormat::Binary => { out.write(&b).expect("Couldn't write to stream."); }
|
||||||
@ -339,6 +376,61 @@ fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
|
|||||||
Ok("Export completed.".into())
|
Ok("Export completed.".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_export_state(cmd: ExportState) -> Result<String, String> {
|
||||||
|
// Setup panic handler
|
||||||
|
let service = try!(start_client(cmd.dirs, cmd.spec, cmd.pruning, cmd.pruning_history, cmd.tracing, cmd.fat_db, cmd.compaction, cmd.wal, cmd.cache_config));
|
||||||
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
|
||||||
|
panic_handler.forward_from(&service);
|
||||||
|
let client = service.client();
|
||||||
|
|
||||||
|
let mut out: Box<io::Write> = match cmd.file_path {
|
||||||
|
Some(f) => Box::new(try!(fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f)))),
|
||||||
|
None => Box::new(io::stdout()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut last: Option<Address> = None;
|
||||||
|
let at = cmd.at;
|
||||||
|
let mut i = 0usize;
|
||||||
|
|
||||||
|
out.write_fmt(format_args!("{{\n\"state\": [", )).expect("Couldn't write to stream.");
|
||||||
|
loop {
|
||||||
|
let accounts = try!(client.list_accounts(at, last.as_ref(), 1000).ok_or("Specified block not found"));
|
||||||
|
if accounts.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for account in accounts.into_iter() {
|
||||||
|
if i != 0 {
|
||||||
|
out.write(b",").expect("Write error");
|
||||||
|
}
|
||||||
|
out.write_fmt(format_args!("\n\"0x{}\": {{\"balance\": \"{:x}\", \"nonce\": \"{:x}\"", account.hex(), client.balance(&account, at).unwrap_or_else(U256::zero), client.nonce(&account, at).unwrap_or_else(U256::zero))).expect("Write error");
|
||||||
|
let code = client.code(&account, at).unwrap_or(None).unwrap_or_else(Vec::new);
|
||||||
|
if !code.is_empty() {
|
||||||
|
out.write_fmt(format_args!(", \"code_hash\": \"0x{}\"", code.sha3().hex())).expect("Write error");
|
||||||
|
if cmd.code {
|
||||||
|
out.write_fmt(format_args!(", \"code\": \"{}\"", code.to_hex())).expect("Write error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let storage_root = client.storage_root(&account, at).unwrap_or(::util::SHA3_NULL_RLP);
|
||||||
|
if storage_root != ::util::SHA3_NULL_RLP {
|
||||||
|
out.write_fmt(format_args!(", \"storage_root\": \"0x{}\"", storage_root.hex())).expect("Write error");
|
||||||
|
if cmd.storage {
|
||||||
|
//out.write_fmt(format_args!(", \"storage\": {\n", client.code(at, &account).hex()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.write(b"}").expect("Write error");
|
||||||
|
i += 1;
|
||||||
|
if i % 10000 == 0 {
|
||||||
|
info!("#{}", i);
|
||||||
|
}
|
||||||
|
last = Some(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.write_fmt(format_args!("\n]}}")).expect("Write error");
|
||||||
|
Ok("Export completed.".into())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::DataFormat;
|
use super::DataFormat;
|
||||||
|
@ -26,6 +26,8 @@ usage! {
|
|||||||
cmd_new: bool,
|
cmd_new: bool,
|
||||||
cmd_list: bool,
|
cmd_list: bool,
|
||||||
cmd_export: bool,
|
cmd_export: bool,
|
||||||
|
cmd_blocks: bool,
|
||||||
|
cmd_state: bool,
|
||||||
cmd_import: bool,
|
cmd_import: bool,
|
||||||
cmd_signer: bool,
|
cmd_signer: bool,
|
||||||
cmd_new_token: bool,
|
cmd_new_token: bool,
|
||||||
@ -246,6 +248,8 @@ usage! {
|
|||||||
flag_to: String = "latest", or |_| None,
|
flag_to: String = "latest", or |_| None,
|
||||||
flag_format: Option<String> = None, or |_| None,
|
flag_format: Option<String> = None, or |_| None,
|
||||||
flag_no_seal_check: bool = false, or |_| None,
|
flag_no_seal_check: bool = false, or |_| None,
|
||||||
|
flag_no_storage: bool = false, or |_| None,
|
||||||
|
flag_no_code: bool = false, or |_| None,
|
||||||
|
|
||||||
// -- Snapshot Optons
|
// -- Snapshot Optons
|
||||||
flag_at: String = "latest", or |_| None,
|
flag_at: String = "latest", or |_| None,
|
||||||
@ -484,6 +488,8 @@ mod tests {
|
|||||||
cmd_new: false,
|
cmd_new: false,
|
||||||
cmd_list: false,
|
cmd_list: false,
|
||||||
cmd_export: false,
|
cmd_export: false,
|
||||||
|
cmd_state: false,
|
||||||
|
cmd_blocks: false,
|
||||||
cmd_import: false,
|
cmd_import: false,
|
||||||
cmd_signer: false,
|
cmd_signer: false,
|
||||||
cmd_new_token: false,
|
cmd_new_token: false,
|
||||||
|
@ -10,7 +10,7 @@ Usage:
|
|||||||
parity account import <path>... [options]
|
parity account import <path>... [options]
|
||||||
parity wallet import <path> --password FILE [options]
|
parity wallet import <path> --password FILE [options]
|
||||||
parity import [ <file> ] [options]
|
parity import [ <file> ] [options]
|
||||||
parity export [ <file> ] [options]
|
parity export (blocks | state) [ <file> ] [options]
|
||||||
parity signer new-token [options]
|
parity signer new-token [options]
|
||||||
parity snapshot <file> [options]
|
parity snapshot <file> [options]
|
||||||
parity restore [ <file> ] [options]
|
parity restore [ <file> ] [options]
|
||||||
@ -271,6 +271,12 @@ Import/Export Options:
|
|||||||
one of 'hex' and 'binary'.
|
one of 'hex' and 'binary'.
|
||||||
(default: {flag_format:?} = Import: auto, Export: binary)
|
(default: {flag_format:?} = Import: auto, Export: binary)
|
||||||
--no-seal-check Skip block seal check. (default: {flag_no_seal_check})
|
--no-seal-check Skip block seal check. (default: {flag_no_seal_check})
|
||||||
|
--at BLOCK Export state at the given block, which may be an
|
||||||
|
index, hash, or 'latest'. Note that taking snapshots at
|
||||||
|
non-recent blocks will only work with --pruning archive
|
||||||
|
(default: {flag_at})
|
||||||
|
--no-storage Don't export account storge. (default: {flag_no_storage})
|
||||||
|
--no-code Don't export account code. (default: {flag_no_code})
|
||||||
|
|
||||||
Snapshot Options:
|
Snapshot Options:
|
||||||
--at BLOCK Take a snapshot at the given block, which may be an
|
--at BLOCK Take a snapshot at the given block, which may be an
|
||||||
|
@ -37,7 +37,7 @@ use dir::Directories;
|
|||||||
use dapps::Configuration as DappsConfiguration;
|
use dapps::Configuration as DappsConfiguration;
|
||||||
use signer::{Configuration as SignerConfiguration};
|
use signer::{Configuration as SignerConfiguration};
|
||||||
use run::RunCmd;
|
use run::RunCmd;
|
||||||
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat};
|
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, ExportState, DataFormat};
|
||||||
use presale::ImportWallet;
|
use presale::ImportWallet;
|
||||||
use account::{AccountCmd, NewAccount, ImportAccounts, ImportFromGethAccounts};
|
use account::{AccountCmd, NewAccount, ImportAccounts, ImportFromGethAccounts};
|
||||||
use snapshot::{self, SnapshotCommand};
|
use snapshot::{self, SnapshotCommand};
|
||||||
@ -161,23 +161,45 @@ impl Configuration {
|
|||||||
};
|
};
|
||||||
Cmd::Blockchain(BlockchainCmd::Import(import_cmd))
|
Cmd::Blockchain(BlockchainCmd::Import(import_cmd))
|
||||||
} else if self.args.cmd_export {
|
} else if self.args.cmd_export {
|
||||||
let export_cmd = ExportBlockchain {
|
if self.args.cmd_blocks {
|
||||||
spec: spec,
|
let export_cmd = ExportBlockchain {
|
||||||
cache_config: cache_config,
|
spec: spec,
|
||||||
dirs: dirs,
|
cache_config: cache_config,
|
||||||
file_path: self.args.arg_file.clone(),
|
dirs: dirs,
|
||||||
format: format,
|
file_path: self.args.arg_file.clone(),
|
||||||
pruning: pruning,
|
format: format,
|
||||||
pruning_history: pruning_history,
|
pruning: pruning,
|
||||||
compaction: compaction,
|
pruning_history: pruning_history,
|
||||||
wal: wal,
|
compaction: compaction,
|
||||||
tracing: tracing,
|
wal: wal,
|
||||||
fat_db: fat_db,
|
tracing: tracing,
|
||||||
from_block: try!(to_block_id(&self.args.flag_from)),
|
fat_db: fat_db,
|
||||||
to_block: try!(to_block_id(&self.args.flag_to)),
|
from_block: try!(to_block_id(&self.args.flag_from)),
|
||||||
check_seal: !self.args.flag_no_seal_check,
|
to_block: try!(to_block_id(&self.args.flag_to)),
|
||||||
};
|
check_seal: !self.args.flag_no_seal_check,
|
||||||
Cmd::Blockchain(BlockchainCmd::Export(export_cmd))
|
};
|
||||||
|
Cmd::Blockchain(BlockchainCmd::Export(export_cmd))
|
||||||
|
} else if self.args.cmd_state {
|
||||||
|
let export_cmd = ExportState {
|
||||||
|
spec: spec,
|
||||||
|
cache_config: cache_config,
|
||||||
|
dirs: dirs,
|
||||||
|
file_path: self.args.arg_file.clone(),
|
||||||
|
format: format,
|
||||||
|
pruning: pruning,
|
||||||
|
pruning_history: pruning_history,
|
||||||
|
compaction: compaction,
|
||||||
|
wal: wal,
|
||||||
|
tracing: tracing,
|
||||||
|
fat_db: fat_db,
|
||||||
|
at: try!(to_block_id(&self.args.flag_at)),
|
||||||
|
storage: !self.args.flag_no_storage,
|
||||||
|
code: !self.args.flag_no_code,
|
||||||
|
};
|
||||||
|
Cmd::Blockchain(BlockchainCmd::ExportState(export_cmd))
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
} else if self.args.cmd_snapshot {
|
} else if self.args.cmd_snapshot {
|
||||||
let snapshot_cmd = SnapshotCommand {
|
let snapshot_cmd = SnapshotCommand {
|
||||||
cache_config: cache_config,
|
cache_config: cache_config,
|
||||||
|
@ -265,7 +265,7 @@ pub fn fatdb_switch_to_bool(switch: Switch, user_defaults: &UserDefaults, algori
|
|||||||
(_, Switch::Auto, def) => Ok(def),
|
(_, Switch::Auto, def) => Ok(def),
|
||||||
};
|
};
|
||||||
|
|
||||||
if result.clone().unwrap_or(false) && algorithm != Algorithm::Archive {
|
if result.clone().unwrap_or(false) && algorithm != Algorithm::Archive && algorithm != Algorithm::OverlayRecent {
|
||||||
return Err("Fat DB is not supported with the chosen pruning option. Please rerun with `--pruning=archive`".into());
|
return Err("Fat DB is not supported with the chosen pruning option. Please rerun with `--pruning=archive`".into());
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
@ -219,7 +219,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
|
|||||||
// create client config
|
// create client config
|
||||||
let client_config = to_client_config(
|
let client_config = to_client_config(
|
||||||
&cmd.cache_config,
|
&cmd.cache_config,
|
||||||
mode,
|
mode.clone(),
|
||||||
tracing,
|
tracing,
|
||||||
fat_db,
|
fat_db,
|
||||||
cmd.compaction,
|
cmd.compaction,
|
||||||
@ -354,6 +354,8 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
|
|||||||
// save user defaults
|
// save user defaults
|
||||||
user_defaults.pruning = algorithm;
|
user_defaults.pruning = algorithm;
|
||||||
user_defaults.tracing = tracing;
|
user_defaults.tracing = tracing;
|
||||||
|
user_defaults.fat_db = fat_db;
|
||||||
|
user_defaults.mode = mode;
|
||||||
try!(user_defaults.save(&user_defaults_path));
|
try!(user_defaults.save(&user_defaults_path));
|
||||||
|
|
||||||
let on_mode_change = move |mode: &Mode| {
|
let on_mode_change = move |mode: &Mode| {
|
||||||
|
@ -234,19 +234,20 @@ impl<C, M, S: ?Sized> Parity for ParityClient<C, M, S> where
|
|||||||
Ok(Brain::new(phrase).generate().unwrap().address().into())
|
Ok(Brain::new(phrase).generate().unwrap().address().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_accounts(&self) -> Result<Option<Vec<H160>>, Error> {
|
fn list_accounts(&self, after: Option<H160>, count: u64) -> Result<Option<Vec<H160>>, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
|
||||||
Ok(take_weak!(self.client)
|
Ok(take_weak!(self.client)
|
||||||
.list_accounts(BlockID::Latest)
|
.list_accounts(BlockID::Latest, after.map(Into::into).as_ref(), count)
|
||||||
.map(|a| a.into_iter().map(Into::into).collect()))
|
.map(|a| a.into_iter().map(Into::into).collect()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_storage_keys(&self, _address: H160) -> Result<Option<Vec<H256>>, Error> {
|
fn list_storage_keys(&self, address: H160, after: Option<H256>, count: u64) -> Result<Option<Vec<H256>>, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
|
||||||
// TODO: implement this
|
Ok(take_weak!(self.client)
|
||||||
Ok(None)
|
.list_storage(BlockID::Latest, &address.into(), after.map(Into::into).as_ref(), count)
|
||||||
|
.map(|a| a.into_iter().map(Into::into).collect()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result<Bytes, Error> {
|
fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result<Bytes, Error> {
|
||||||
|
@ -103,12 +103,12 @@ build_rpc_trait! {
|
|||||||
|
|
||||||
/// Returns all addresses if Fat DB is enabled (`--fat-db`), or null if not.
|
/// Returns all addresses if Fat DB is enabled (`--fat-db`), or null if not.
|
||||||
#[rpc(name = "parity_listAccounts")]
|
#[rpc(name = "parity_listAccounts")]
|
||||||
fn list_accounts(&self) -> Result<Option<Vec<H160>>, Error>;
|
fn list_accounts(&self, Option<H160>, u64) -> Result<Option<Vec<H160>>, Error>;
|
||||||
|
|
||||||
/// Returns all storage keys of the given address (first parameter) if Fat DB is enabled (`--fat-db`),
|
/// Returns all storage keys of the given address (first parameter) if Fat DB is enabled (`--fat-db`),
|
||||||
/// or null if not.
|
/// or null if not.
|
||||||
#[rpc(name = "parity_listStorageKeys")]
|
#[rpc(name = "parity_listStorageKeys")]
|
||||||
fn list_storage_keys(&self, H160) -> Result<Option<Vec<H256>>, Error>;
|
fn list_storage_keys(&self, H160, Option<H256>, u64) -> Result<Option<Vec<H256>>, Error>;
|
||||||
|
|
||||||
/// Encrypt some data with a public key under ECIES.
|
/// Encrypt some data with a public key under ECIES.
|
||||||
/// First parameter is the 512-byte destination public key, second is the message.
|
/// First parameter is the 512-byte destination public key, second is the message.
|
||||||
|
@ -132,7 +132,7 @@ pub trait TrieMut {
|
|||||||
|
|
||||||
/// A trie iterator that also supports random access.
|
/// A trie iterator that also supports random access.
|
||||||
pub trait TrieIterator : Iterator {
|
pub trait TrieIterator : Iterator {
|
||||||
/// Position the iterator on the first element with key >= `key`
|
/// Position the iterator on the first element with key > `key`
|
||||||
fn seek(&mut self, key: &[u8]) -> Result<()>;
|
fn seek(&mut self, key: &[u8]) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,20 +295,23 @@ impl<'a> TrieDBIterator<'a> {
|
|||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no
|
fn seek_descend<'key> ( &mut self, node: &[u8], key: &NibbleSlice<'key>, d: u32) -> super::Result<()> {
|
||||||
/// value exists for the key.
|
|
||||||
///
|
|
||||||
/// Note: Not a public API; use Trie trait functions.
|
|
||||||
fn seek_recurse<'key> (
|
|
||||||
&mut self,
|
|
||||||
node: &[u8],
|
|
||||||
key: &NibbleSlice<'key>,
|
|
||||||
d: u32
|
|
||||||
) -> super::Result<()> {
|
|
||||||
let n = Node::decoded(node);
|
|
||||||
match Node::decoded(node) {
|
match Node::decoded(node) {
|
||||||
Node::Leaf(_, ref item) => {
|
Node::Leaf(ref slice, _) => {
|
||||||
self.descend(node);
|
let slice = &NibbleSlice::from_encoded(slice).0;
|
||||||
|
if slice == key {
|
||||||
|
self.trail.push(Crumb {
|
||||||
|
status: Status::At,
|
||||||
|
node: Node::decoded(node),
|
||||||
|
});
|
||||||
|
self.key_nibbles.extend(slice.iter());
|
||||||
|
} else {
|
||||||
|
self.trail.push(Crumb {
|
||||||
|
status: Status::Exiting,
|
||||||
|
node: Node::decoded(node),
|
||||||
|
});
|
||||||
|
self.key_nibbles.extend(slice.iter());
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Node::Extension(ref slice, ref item) => {
|
Node::Extension(ref slice, ref item) => {
|
||||||
@ -317,33 +320,34 @@ impl<'a> TrieDBIterator<'a> {
|
|||||||
let mut r = NoOp;
|
let mut r = NoOp;
|
||||||
self.trail.push(Crumb {
|
self.trail.push(Crumb {
|
||||||
status: Status::At,
|
status: Status::At,
|
||||||
node: n,
|
node: Node::decoded(node),
|
||||||
});
|
});
|
||||||
self.key_nibbles.extend(slice.iter());
|
self.key_nibbles.extend(slice.iter());
|
||||||
let data = try!(self.db.get_raw_or_lookup(&*item, &mut r, d));
|
let data = try!(self.db.get_raw_or_lookup(&*item, &mut r, d));
|
||||||
self.seek_recurse(&data, &key.mid(slice.len()), d + 1)
|
self.seek_descend(&data, &key.mid(slice.len()), d + 1)
|
||||||
} else {
|
} else {
|
||||||
self.descend(node);
|
try!(self.descend(node));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Node::Branch(ref nodes, _) => match key.is_empty() {
|
Node::Branch(ref nodes, _) => match key.is_empty() {
|
||||||
true => {
|
true => {
|
||||||
self.trail.push(Crumb {
|
self.trail.push(Crumb {
|
||||||
status: Status::Entering,
|
status: Status::At,
|
||||||
node: n,
|
node: Node::decoded(node),
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
false => {
|
false => {
|
||||||
let mut r = NoOp;
|
let mut r = NoOp;
|
||||||
let node = try!(self.db.get_raw_or_lookup(&*nodes[key.at(0) as usize], &mut r, d));
|
let i = key.at(0);
|
||||||
self.trail.push(Crumb {
|
self.trail.push(Crumb {
|
||||||
status: Status::AtChild(key.at(0) as usize),
|
status: Status::AtChild(i as usize),
|
||||||
node: n,
|
node: Node::decoded(node),
|
||||||
});
|
});
|
||||||
self.key_nibbles.push(key.at(0));
|
self.key_nibbles.push(i);
|
||||||
self.seek_recurse(&node, &key.mid(1), d + 1)
|
let child = try!(self.db.get_raw_or_lookup(&*nodes[i as usize], &mut r, d));
|
||||||
|
self.seek_descend(&child, &key.mid(1), d + 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => Ok(())
|
_ => Ok(())
|
||||||
@ -378,7 +382,7 @@ impl<'a> TrieIterator for TrieDBIterator<'a> {
|
|||||||
self.key_nibbles.clear();
|
self.key_nibbles.clear();
|
||||||
let mut r = NoOp;
|
let mut r = NoOp;
|
||||||
let root_rlp = try!(self.db.root_data(&mut r));
|
let root_rlp = try!(self.db.root_data(&mut r));
|
||||||
self.seek_recurse(&root_rlp, &NibbleSlice::new(key), 1)
|
self.seek_descend(&root_rlp, &NibbleSlice::new(key), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,10 +505,28 @@ fn iterator_seek() {
|
|||||||
|
|
||||||
let t = TrieDB::new(&memdb, &root).unwrap();
|
let t = TrieDB::new(&memdb, &root).unwrap();
|
||||||
let mut iter = t.iter().unwrap();
|
let mut iter = t.iter().unwrap();
|
||||||
//assert_eq!(iter.next(), Some(Ok((b"A".to_vec(), DBValue::from_slice(b"A")))));
|
assert_eq!(iter.next(), Some(Ok((b"A".to_vec(), DBValue::from_slice(b"A")))));
|
||||||
//iter.seek(b"!").unwrap();
|
iter.seek(b"!").unwrap();
|
||||||
//assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>());
|
assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>());
|
||||||
let mut iter = t.iter().unwrap();
|
let mut iter = t.iter().unwrap();
|
||||||
iter.seek(b"A").unwrap();
|
iter.seek(b"A").unwrap();
|
||||||
assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>());
|
assert_eq!(&d[1..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
|
let mut iter = t.iter().unwrap();
|
||||||
|
iter.seek(b"AA").unwrap();
|
||||||
|
assert_eq!(&d[2..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
|
let mut iter = t.iter().unwrap();
|
||||||
|
iter.seek(b"A!").unwrap();
|
||||||
|
assert_eq!(&d[1..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
|
let mut iter = t.iter().unwrap();
|
||||||
|
iter.seek(b"AB").unwrap();
|
||||||
|
assert_eq!(&d[3..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
|
let mut iter = t.iter().unwrap();
|
||||||
|
iter.seek(b"AB!").unwrap();
|
||||||
|
assert_eq!(&d[3..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
|
let mut iter = t.iter().unwrap();
|
||||||
|
iter.seek(b"B").unwrap();
|
||||||
|
assert_eq!(&d[4..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
|
let mut iter = t.iter().unwrap();
|
||||||
|
iter.seek(b"C").unwrap();
|
||||||
|
assert_eq!(&d[4..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user