From 4d3f137e1e12d3788040c51260d2dcf03d6715b3 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 19 Apr 2017 16:27:45 +0200 Subject: [PATCH] iterate over all epochs --- ethcore/src/blockchain/blockchain.rs | 102 +++++++++++++++++++++++++++ ethcore/src/blockchain/extras.rs | 20 +++--- ethcore/src/client/client.rs | 1 + 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 79169fd82..0824bd351 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -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, Box<[u8]>)> + 'a>, +} + +impl<'a> Iterator for EpochTransitionIter<'a> { + type Item = (u64, EpochTransition); + + fn next(&mut self) -> Option { + 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 { /// Create new instance of blockchain from given Genesis. pub fn new(config: Config, genesis: &[u8], db: Arc) -> 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 /// 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(), 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![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![0, 1, 2, 3, 4]); + } } diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index b9fd5d3bd..7de49f3bf 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -142,12 +142,9 @@ pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16; /// epoch key prefix. /// used to iterate over all epoch transitions in order from genesis. -pub fn epoch_key_prefix() -> [u8; DB_PREFIX_LEN] { - let mut arr = [0u8; DB_PREFIX_LEN]; - arr[0] = ExtrasIndex::EpochTransitions as u8; - - arr -} +pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[ + ExtrasIndex::EpochTransitions as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]); impl Deref for EpochTransitionsKey { @@ -161,7 +158,7 @@ impl Key for u64 { fn key(&self) -> Self::Target { 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) .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)] pub struct EpochTransition { 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, // "transition/epoch" proof from the engine. pub state_proof: Vec, // state items necessary to regenerate proof. } impl Encodable for EpochTransition { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) + s.begin_list(4) .append(&self.block_hash) + .append(&self.block_number) .append(&self.proof) .begin_list(self.state_proof.len()); @@ -323,8 +322,9 @@ impl Decodable for EpochTransition { fn decode(rlp: &UntrustedRlp) -> Result { Ok(EpochTransition { block_hash: rlp.val_at(0)?, - proof: rlp.val_at(1)?, - state_proof: rlp.at(2)?.iter().map(|x| { + block_number: rlp.val_at(1)?, + proof: rlp.val_at(2)?, + state_proof: rlp.at(3)?.iter().map(|x| { Ok(DBValue::from_slice(x.data()?)) }).collect::, _>>()?, }) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 26d823dce..88fd181bb 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -667,6 +667,7 @@ impl Client { Ok(proof) => chain.insert_epoch_transition(batch, epoch_number, EpochTransition { block_hash: hash.clone(), + block_number: header.number(), proof: proof, state_proof: read_values.into_inner().into_iter().collect(), }),