iterate over all epochs
This commit is contained in:
parent
af868a7439
commit
4d3f137e1e
@ -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 {
|
||||
/// Create new instance of blockchain from given Genesis.
|
||||
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
|
||||
/// 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<_>>(), 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]);
|
||||
}
|
||||
}
|
||||
|
@ -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<EpochTransitions> 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<u8>, // "transition/epoch" proof from the engine.
|
||||
pub state_proof: Vec<DBValue>, // 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<Self, DecoderError> {
|
||||
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::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
|
@ -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(),
|
||||
}),
|
||||
|
Loading…
Reference in New Issue
Block a user