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 {
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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<_>, _>>()?,
|
||||||
})
|
})
|
||||||
|
@ -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(),
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user