iterate over all epochs

This commit is contained in:
Robert Habermeier 2017-04-19 16:27:45 +02:00
parent af868a7439
commit 4d3f137e1e
3 changed files with 113 additions and 10 deletions

View File

@ -419,6 +419,45 @@ impl<'a> Iterator for AncestryIter<'a> {
} }
} }
/// An iterator which walks all epoch transitions.
/// Returns epoch transitions.
pub struct EpochTransitionIter<'a> {
chain: &'a BlockChain,
prefix_iter: Box<Iterator<Item=(Box<[u8]>, Box<[u8]>)> + 'a>,
}
impl<'a> Iterator for EpochTransitionIter<'a> {
type Item = (u64, EpochTransition);
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.prefix_iter.next() {
Some((key, val)) => {
// iterator may continue beyond values beginning with this
// prefix.
if !key.starts_with(&EPOCH_KEY_PREFIX[..]) { return None }
let transitions: EpochTransitions = ::rlp::decode(&val[..]);
// if there are multiple candidates, at most one will be on the
// canon chain.
for transition in transitions.candidates.into_iter() {
let is_in_canon_chain = self.chain.block_hash(transition.block_number)
.map_or(false, |hash| hash == transition.block_hash);
if is_in_canon_chain {
return Some((transitions.number, transition))
}
}
// some epochs never occurred on the main chain.
}
None => return None,
}
}
}
}
impl BlockChain { impl BlockChain {
/// Create new instance of blockchain from given Genesis. /// Create new instance of blockchain from given Genesis.
pub fn new(config: Config, genesis: &[u8], db: Arc<KeyValueDB>) -> BlockChain { pub fn new(config: Config, genesis: &[u8], db: Arc<KeyValueDB>) -> BlockChain {
@ -817,6 +856,15 @@ impl BlockChain {
} }
} }
/// Iterate over all epoch transitions.
pub fn epoch_transitions(&self) -> EpochTransitionIter {
let iter = self.db.iter_from_prefix(db::COL_EXTRA, &EPOCH_KEY_PREFIX[..]);
EpochTransitionIter {
chain: self,
prefix_iter: iter,
}
}
/// Add a child to a given block. Assumes that the block hash is in /// Add a child to a given block. Assumes that the block hash is in
/// the chain and the child's parent is this block. /// the chain and the child's parent is this block.
/// ///
@ -2130,4 +2178,58 @@ mod tests {
assert_eq!(bc.rewind(), Some(genesis_hash.clone())); assert_eq!(bc.rewind(), Some(genesis_hash.clone()));
assert_eq!(bc.rewind(), None); assert_eq!(bc.rewind(), None);
} }
#[test]
fn epoch_transitions_iter() {
use blockchain::extras::EpochTransition;
let mut canon_chain = ChainGenerator::default();
let mut finalizer = BlockFinalizer::default();
let genesis = canon_chain.generate(&mut finalizer).unwrap();
let db = new_db();
{
let bc = new_chain(&genesis, db.clone());
let uncle = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
let mut batch = db.transaction();
// create a longer fork
for i in 0..5 {
let canon_block = canon_chain.generate(&mut finalizer).unwrap();
let hash = BlockView::new(&canon_block).header_view().sha3();
bc.insert_block(&mut batch, &canon_block, vec![]);
bc.insert_epoch_transition(&mut batch, i, EpochTransition {
block_hash: hash,
block_number: i + 1,
proof: vec![],
state_proof: vec![],
});
bc.commit();
}
assert_eq!(bc.best_block_number(), 5);
let hash = BlockView::new(&uncle).header_view().sha3();
bc.insert_block(&mut batch, &uncle, vec![]);
bc.insert_epoch_transition(&mut batch, 999, EpochTransition {
block_hash: hash,
block_number: 1,
proof: vec![],
state_proof: vec![]
});
db.write(batch).unwrap();
bc.commit();
// epoch 999 not in canonical chain.
assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
}
// re-loading the blockchain should load the correct best block.
let bc = new_chain(&genesis, db);
assert_eq!(bc.best_block_number(), 5);
assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
}
} }

View File

@ -142,12 +142,9 @@ pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16;
/// epoch key prefix. /// epoch key prefix.
/// used to iterate over all epoch transitions in order from genesis. /// used to iterate over all epoch transitions in order from genesis.
pub fn epoch_key_prefix() -> [u8; DB_PREFIX_LEN] { pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[
let mut arr = [0u8; DB_PREFIX_LEN]; ExtrasIndex::EpochTransitions as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
arr[0] = ExtrasIndex::EpochTransitions as u8; ];
arr
}
pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]); pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]);
impl Deref for EpochTransitionsKey { impl Deref for EpochTransitionsKey {
@ -161,7 +158,7 @@ impl Key<EpochTransitions> for u64 {
fn key(&self) -> Self::Target { fn key(&self) -> Self::Target {
let mut arr = [0u8; EPOCH_KEY_LEN]; let mut arr = [0u8; EPOCH_KEY_LEN];
arr[..DB_PREFIX_LEN].copy_from_slice(&epoch_key_prefix()[..]); arr[..DB_PREFIX_LEN].copy_from_slice(&EPOCH_KEY_PREFIX[..]);
write!(&mut arr[DB_PREFIX_LEN..], "{:016x}", self) write!(&mut arr[DB_PREFIX_LEN..], "{:016x}", self)
.expect("format arg is valid; no more than 16 chars will be written; qed"); .expect("format arg is valid; no more than 16 chars will be written; qed");
@ -302,14 +299,16 @@ impl Decodable for EpochTransitions {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EpochTransition { pub struct EpochTransition {
pub block_hash: H256, // block hash at which the transition occurred. pub block_hash: H256, // block hash at which the transition occurred.
pub block_number: BlockNumber, // block number at which the tranition occurred.
pub proof: Vec<u8>, // "transition/epoch" proof from the engine. pub proof: Vec<u8>, // "transition/epoch" proof from the engine.
pub state_proof: Vec<DBValue>, // state items necessary to regenerate proof. pub state_proof: Vec<DBValue>, // state items necessary to regenerate proof.
} }
impl Encodable for EpochTransition { impl Encodable for EpochTransition {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3) s.begin_list(4)
.append(&self.block_hash) .append(&self.block_hash)
.append(&self.block_number)
.append(&self.proof) .append(&self.proof)
.begin_list(self.state_proof.len()); .begin_list(self.state_proof.len());
@ -323,8 +322,9 @@ impl Decodable for EpochTransition {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> { fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(EpochTransition { Ok(EpochTransition {
block_hash: rlp.val_at(0)?, block_hash: rlp.val_at(0)?,
proof: rlp.val_at(1)?, block_number: rlp.val_at(1)?,
state_proof: rlp.at(2)?.iter().map(|x| { proof: rlp.val_at(2)?,
state_proof: rlp.at(3)?.iter().map(|x| {
Ok(DBValue::from_slice(x.data()?)) Ok(DBValue::from_slice(x.data()?))
}).collect::<Result<Vec<_>, _>>()?, }).collect::<Result<Vec<_>, _>>()?,
}) })

View File

@ -667,6 +667,7 @@ impl Client {
Ok(proof) => Ok(proof) =>
chain.insert_epoch_transition(batch, epoch_number, EpochTransition { chain.insert_epoch_transition(batch, epoch_number, EpochTransition {
block_hash: hash.clone(), block_hash: hash.clone(),
block_number: header.number(),
proof: proof, proof: proof,
state_proof: read_values.into_inner().into_iter().collect(), state_proof: read_values.into_inner().into_iter().collect(),
}), }),