Remove panickers from trie iterators (#2209)

* port trie iterators to use error handling

* use new trie iterators in snapshot

allows proper recovery from a premature periodic snapshot
This commit is contained in:
Robert Habermeier 2016-09-21 12:56:13 +02:00 committed by Gav Wood
parent 8c111da70b
commit a100b9d09e
9 changed files with 62 additions and 37 deletions

View File

@ -144,7 +144,9 @@ pub struct Client {
factories: Factories, factories: Factories,
} }
const HISTORY: u64 = 1200; /// The pruning constant -- how old blocks must be before we
/// assume finality of a given candidate.
pub const HISTORY: u64 = 1200;
/// Append a path element to the given path and return the string. /// Append a path element to the given path and return the string.
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> { pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {

View File

@ -92,7 +92,8 @@ impl Account {
let mut pairs = Vec::new(); let mut pairs = Vec::new();
for (k, v) in db.iter() { for item in try!(db.iter()) {
let (k, v) = try!(item);
pairs.push((k, v)); pairs.push((k, v));
} }

View File

@ -358,7 +358,8 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
let mut used_code = HashSet::new(); let mut used_code = HashSet::new();
// account_key here is the address' hash. // account_key here is the address' hash.
for (account_key, account_data) in account_trie.iter() { for item in try!(account_trie.iter()) {
let (account_key, account_data) = try!(item);
let account = Account::from_thin_rlp(account_data); let account = Account::from_thin_rlp(account_data);
let account_key_hash = H256::from_slice(&account_key); let account_key_hash = H256::from_slice(&account_key);

View File

@ -27,7 +27,7 @@ use super::{ManifestData, StateRebuilder, BlockRebuilder, RestorationStatus, Sna
use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter};
use blockchain::BlockChain; use blockchain::BlockChain;
use client::Client; use client::{BlockChainClient, Client};
use engines::Engine; use engines::Engine;
use error::Error; use error::Error;
use ids::BlockID; use ids::BlockID;
@ -345,7 +345,17 @@ impl Service {
let res = client.take_snapshot(writer, BlockID::Number(num), &self.progress); let res = client.take_snapshot(writer, BlockID::Number(num), &self.progress);
self.taking_snapshot.store(false, Ordering::SeqCst); self.taking_snapshot.store(false, Ordering::SeqCst);
try!(res); if let Err(e) = res {
if client.chain_info().best_block_number >= num + ::client::HISTORY {
// "Cancelled" is mincing words a bit -- what really happened
// is that the state we were snapshotting got pruned out
// before we could finish.
info!("Cancelled prematurely-started periodic snapshot.");
return Ok(())
} else {
return Err(e);
}
}
info!("Finished taking snapshot at #{}", num); info!("Finished taking snapshot at #{}", num);

View File

@ -52,8 +52,9 @@ impl StateProducer {
// modify existing accounts. // modify existing accounts.
let mut accounts_to_modify: Vec<_> = { let mut accounts_to_modify: Vec<_> = {
let trie = TrieDB::new(&*db, &self.state_root).unwrap(); let trie = TrieDB::new(&*db, &self.state_root).unwrap();
let temp = trie.iter() // binding required due to complicated lifetime stuff let temp = trie.iter().unwrap() // binding required due to complicated lifetime stuff
.filter(|_| rng.gen::<f32>() < ACCOUNT_CHURN) .filter(|_| rng.gen::<f32>() < ACCOUNT_CHURN)
.map(Result::unwrap)
.map(|(k, v)| (H256::from_slice(&k), v.to_owned())) .map(|(k, v)| (H256::from_slice(&k), v.to_owned()))
.collect(); .collect();

View File

@ -46,8 +46,8 @@ impl<'db> FatDB<'db> {
} }
impl<'db> Trie for FatDB<'db> { impl<'db> Trie for FatDB<'db> {
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> { fn iter<'a>(&'a self) -> super::Result<Box<Iterator<Item = TrieItem> + 'a>> {
Box::new(FatDBIterator::new(&self.raw)) FatDBIterator::new(&self.raw).map(|iter| Box::new(iter) as Box<_>)
} }
fn root(&self) -> &H256 { fn root(&self) -> &H256 {
@ -73,22 +73,24 @@ pub struct FatDBIterator<'db> {
impl<'db> FatDBIterator<'db> { impl<'db> FatDBIterator<'db> {
/// Creates new iterator. /// Creates new iterator.
pub fn new(trie: &'db TrieDB) -> Self { pub fn new(trie: &'db TrieDB) -> super::Result<Self> {
FatDBIterator { Ok(FatDBIterator {
trie_iterator: TrieDBIterator::new(trie), trie_iterator: try!(TrieDBIterator::new(trie)),
trie: trie, trie: trie,
} })
} }
} }
impl<'db> Iterator for FatDBIterator<'db> { impl<'db> Iterator for FatDBIterator<'db> {
type Item = (Vec<u8>, &'db [u8]); type Item = TrieItem<'db>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.trie_iterator.next() self.trie_iterator.next()
.map(|(hash, value)| { .map(|res|
res.map(|(hash, value)| {
(self.trie.db().get_aux(&hash).expect("Missing fatdb hash"), value) (self.trie.db().get_aux(&hash).expect("Missing fatdb hash"), value)
}) })
)
} }
} }
@ -105,5 +107,5 @@ fn fatdb_to_trie() {
} }
let t = FatDB::new(&memdb, &root).unwrap(); let t = FatDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]); assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.iter().collect::<Vec<_>>(), vec![(vec![0x01u8, 0x23], &[0x01u8, 0x23] as &[u8])]); assert_eq!(t.iter().unwrap().map(Result::unwrap).collect::<Vec<_>>(), vec![(vec![0x01u8, 0x23], &[0x01u8, 0x23] as &[u8])]);
} }

View File

@ -72,12 +72,12 @@ impl fmt::Display for TrieError {
} }
} }
/// Trie-Item type.
pub type TrieItem<'a> = (Vec<u8>, &'a [u8]);
/// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries. /// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries.
pub type Result<T> = ::std::result::Result<T, Box<TrieError>>; pub type Result<T> = ::std::result::Result<T, Box<TrieError>>;
/// Trie-Item type.
pub type TrieItem<'a> = Result<(Vec<u8>, &'a [u8])>;
/// A key-value datastore implemented as a database-backed modified Merkle tree. /// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait Trie { pub trait Trie {
/// Return the root of the trie. /// Return the root of the trie.
@ -102,7 +102,7 @@ pub trait Trie {
where 'a: 'b, R: Recorder; where 'a: 'b, R: Recorder;
/// Returns an iterator over elements of trie. /// Returns an iterator over elements of trie.
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a>; fn iter<'a>(&'a self) -> Result<Box<Iterator<Item = TrieItem> + 'a>>;
} }
/// A key-value datastore implemented as a database-backed modified Merkle tree. /// A key-value datastore implemented as a database-backed modified Merkle tree.
@ -193,7 +193,7 @@ impl<'db> Trie for TrieKinds<'db> {
wrapper!(self, get_recorded, key, r) wrapper!(self, get_recorded, key, r)
} }
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> { fn iter<'a>(&'a self) -> Result<Box<Iterator<Item = TrieItem> + 'a>> {
wrapper!(self, iter,) wrapper!(self, iter,)
} }
} }

View File

@ -49,8 +49,8 @@ impl<'db> SecTrieDB<'db> {
} }
impl<'db> Trie for SecTrieDB<'db> { impl<'db> Trie for SecTrieDB<'db> {
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> { fn iter<'a>(&'a self) -> super::Result<Box<Iterator<Item = TrieItem> + 'a>> {
Box::new(TrieDB::iter(&self.raw)) TrieDB::iter(&self.raw)
} }
fn root(&self) -> &H256 { self.raw.root() } fn root(&self) -> &H256 { self.raw.root() }

View File

@ -279,30 +279,38 @@ pub struct TrieDBIterator<'a> {
impl<'a> TrieDBIterator<'a> { impl<'a> TrieDBIterator<'a> {
/// Create a new iterator. /// Create a new iterator.
pub fn new(db: &'a TrieDB) -> TrieDBIterator<'a> { pub fn new(db: &'a TrieDB) -> super::Result<TrieDBIterator<'a>> {
let mut r = TrieDBIterator { let mut r = TrieDBIterator {
db: db, db: db,
trail: vec![], trail: vec![],
key_nibbles: Vec::new(), key_nibbles: Vec::new(),
}; };
r.descend(db.root_data(&mut NoOp).unwrap());
r try!(db.root_data(&mut NoOp).and_then(|root| r.descend(root)));
Ok(r)
} }
/// Descend into a payload. /// Descend into a payload.
fn descend(&mut self, d: &'a [u8]) { fn descend(&mut self, d: &'a [u8]) -> super::Result<()> {
self.trail.push(Crumb { self.trail.push(Crumb {
status: Status::Entering, status: Status::Entering,
node: self.db.get_node(d, &mut NoOp, 0).unwrap(), node: try!(self.db.get_node(d, &mut NoOp, 0)),
}); });
match self.trail.last().unwrap().node { match self.trail.last().unwrap().node {
Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); }, Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); },
_ => {} _ => {}
} }
Ok(())
} }
/// Descend into a payload and get the next item. /// Descend into a payload and get the next item.
fn descend_next(&mut self, d: &'a [u8]) -> Option<(Bytes, &'a [u8])> { self.descend(d); self.next() } fn descend_next(&mut self, d: &'a [u8]) -> Option<TrieItem<'a>> {
match self.descend(d) {
Ok(()) => self.next(),
Err(e) => Some(Err(e)),
}
}
/// The present key. /// The present key.
fn key(&self) -> Bytes { fn key(&self) -> Bytes {
@ -312,12 +320,12 @@ impl<'a> TrieDBIterator<'a> {
} }
impl<'a> Iterator for TrieDBIterator<'a> { impl<'a> Iterator for TrieDBIterator<'a> {
type Item = (Bytes, &'a [u8]); type Item = TrieItem<'a>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let b = match self.trail.last_mut() { let b = match self.trail.last_mut() {
Some(mut b) => { b.increment(); b.clone() }, Some(mut b) => { b.increment(); b.clone() },
None => return None None => return None,
}; };
match (b.status, b.node) { match (b.status, b.node) {
(Status::Exiting, n) => { (Status::Exiting, n) => {
@ -332,7 +340,7 @@ impl<'a> Iterator for TrieDBIterator<'a> {
self.trail.pop(); self.trail.pop();
self.next() self.next()
}, },
(Status::At, Node::Leaf(_, v)) | (Status::At, Node::Branch(_, Some(v))) => Some((self.key(), v)), (Status::At, Node::Leaf(_, v)) | (Status::At, Node::Branch(_, Some(v))) => Some(Ok((self.key(), v))),
(Status::At, Node::Extension(_, d)) => self.descend_next(d), (Status::At, Node::Extension(_, d)) => self.descend_next(d),
(Status::At, Node::Branch(_, _)) => self.next(), (Status::At, Node::Branch(_, _)) => self.next(),
(Status::AtChild(i), Node::Branch(children, _)) if children[i].len() > 0 => { (Status::AtChild(i), Node::Branch(children, _)) if children[i].len() > 0 => {
@ -352,8 +360,8 @@ impl<'a> Iterator for TrieDBIterator<'a> {
} }
impl<'db> Trie for TrieDB<'db> { impl<'db> Trie for TrieDB<'db> {
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> { fn iter<'a>(&'a self) -> super::Result<Box<Iterator<Item = TrieItem> + 'a>> {
Box::new(TrieDBIterator::new(self)) TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>)
} }
fn root(&self) -> &H256 { self.root } fn root(&self) -> &H256 { self.root }
@ -392,6 +400,6 @@ fn iterator() {
} }
let t = TrieDB::new(&memdb, &root).unwrap(); let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(d.iter().map(|i|i.to_vec()).collect::<Vec<_>>(), t.iter().map(|x|x.0).collect::<Vec<_>>()); assert_eq!(d.iter().map(|i|i.to_vec()).collect::<Vec<_>>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::<Vec<_>>());
assert_eq!(d, t.iter().map(|x|x.1).collect::<Vec<_>>()); assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::<Vec<_>>());
} }