Add unit tests for
This commit is contained in:
parent
95fce3ad68
commit
74af22e5ca
|
|
@ -404,11 +404,11 @@ chain and updates all side chains to match.
|
||||||
3. Remove the lowest height block from the best chain with
|
3. Remove the lowest height block from the best chain with
|
||||||
`let finalized_block = best_chain.pop_root();`
|
`let finalized_block = best_chain.pop_root();`
|
||||||
|
|
||||||
4. Add `best_chain` back to `self.chain_set`
|
4. Add `best_chain` back to `self.chain_set` if it is not empty
|
||||||
|
|
||||||
5. For each remaining `chain` in `side_chains`
|
5. For each remaining `chain` in `side_chains`
|
||||||
- remove the lowest height block from `chain`
|
- remove the lowest height block from `chain`
|
||||||
- If that block is equal to `finalized_block` add `chain` back to `self.chain_set`
|
- If that block is equal to `finalized_block` and `chain` is not empty add `chain` back to `self.chain_set`
|
||||||
- Else, drop `chain`
|
- Else, drop `chain`
|
||||||
|
|
||||||
6. Return `finalized_block`
|
6. Return `finalized_block`
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,10 @@ impl Chain {
|
||||||
.next_back()
|
.next_back()
|
||||||
.expect("only called while blocks is populated")
|
.expect("only called while blocks is populated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.blocks.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper trait to organize inverse operations done on the `Chain` type. Used to
|
/// Helper trait to organize inverse operations done on the `Chain` type. Used to
|
||||||
|
|
@ -414,9 +418,7 @@ impl Ord for Chain {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use transaction::Transaction;
|
use std::{env, fmt};
|
||||||
|
|
||||||
use std::{env, fmt, mem};
|
|
||||||
|
|
||||||
use zebra_chain::serialization::ZcashDeserializeInto;
|
use zebra_chain::serialization::ZcashDeserializeInto;
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
|
|
@ -425,6 +427,8 @@ mod tests {
|
||||||
};
|
};
|
||||||
use zebra_test::prelude::*;
|
use zebra_test::prelude::*;
|
||||||
|
|
||||||
|
use crate::tests::FakeChainHelper;
|
||||||
|
|
||||||
use self::assert_eq;
|
use self::assert_eq;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
@ -436,37 +440,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper trait for constructing "valid" looking chains of blocks
|
|
||||||
trait FakeChainHelper {
|
|
||||||
fn make_fake_child(&self) -> Arc<Block>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FakeChainHelper for Block {
|
|
||||||
fn make_fake_child(&self) -> Arc<Block> {
|
|
||||||
let parent_hash = self.hash();
|
|
||||||
let mut child = Block::clone(self);
|
|
||||||
let mut transactions = mem::take(&mut child.transactions);
|
|
||||||
let mut tx = transactions.remove(0);
|
|
||||||
|
|
||||||
let input = match Arc::make_mut(&mut tx) {
|
|
||||||
Transaction::V1 { inputs, .. } => &mut inputs[0],
|
|
||||||
Transaction::V2 { inputs, .. } => &mut inputs[0],
|
|
||||||
Transaction::V3 { inputs, .. } => &mut inputs[0],
|
|
||||||
Transaction::V4 { inputs, .. } => &mut inputs[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
match input {
|
|
||||||
transparent::Input::Coinbase { height, .. } => height.0 += 1,
|
|
||||||
_ => panic!("block must have a coinbase height to create a child"),
|
|
||||||
}
|
|
||||||
|
|
||||||
child.transactions.push(tx);
|
|
||||||
child.header.previous_block_hash = parent_hash;
|
|
||||||
|
|
||||||
Arc::new(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn construct_empty() {
|
fn construct_empty() {
|
||||||
zebra_test::init();
|
zebra_test::init();
|
||||||
|
|
@ -511,6 +484,25 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bigger_is_greater() -> Result<()> {
|
||||||
|
zebra_test::init();
|
||||||
|
let block: Arc<Block> =
|
||||||
|
zebra_test::vectors::BLOCK_MAINNET_434873_BYTES.zcash_deserialize_into()?;
|
||||||
|
|
||||||
|
let mut lesser_chain = Chain::default();
|
||||||
|
lesser_chain.push(block.clone());
|
||||||
|
|
||||||
|
let fake_child = block.make_fake_child();
|
||||||
|
let mut bigger_chain = Chain::default();
|
||||||
|
bigger_chain.push(block);
|
||||||
|
bigger_chain.push(fake_child);
|
||||||
|
|
||||||
|
assert!(bigger_chain > lesser_chain);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn arbitrary_chain(tip_height: block::Height) -> BoxedStrategy<Vec<Arc<Block>>> {
|
fn arbitrary_chain(tip_height: block::Height) -> BoxedStrategy<Vec<Arc<Block>>> {
|
||||||
Block::partial_chain_strategy(LedgerState::new(tip_height, Network::Mainnet), 100)
|
Block::partial_chain_strategy(LedgerState::new(tip_height, Network::Mainnet), 100)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,18 @@ impl NonFinalizedState {
|
||||||
|
|
||||||
// remove the lowest height block from the best_chain as finalized_block
|
// remove the lowest height block from the best_chain as finalized_block
|
||||||
let finalized_block = best_chain.pop_root();
|
let finalized_block = best_chain.pop_root();
|
||||||
|
|
||||||
// add best_chain back to `self.chain_set`
|
// add best_chain back to `self.chain_set`
|
||||||
|
if !best_chain.is_empty() {
|
||||||
self.chain_set.insert(best_chain);
|
self.chain_set.insert(best_chain);
|
||||||
|
}
|
||||||
|
|
||||||
// for each remaining chain in side_chains
|
// for each remaining chain in side_chains
|
||||||
for mut chain in side_chains {
|
for mut chain in side_chains {
|
||||||
// remove the first block from `chain`
|
// remove the first block from `chain`
|
||||||
let chain_start = chain.pop_root();
|
let chain_start = chain.pop_root();
|
||||||
// if block equals finalized_block
|
// if block equals finalized_block
|
||||||
if chain_start == finalized_block {
|
if !chain.is_empty() && chain_start == finalized_block {
|
||||||
// add the chain back to `self.chain_set`
|
// add the chain back to `self.chain_set`
|
||||||
self.chain_set.insert(chain);
|
self.chain_set.insert(chain);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -217,3 +220,66 @@ impl NonFinalizedState {
|
||||||
metrics::gauge!("state.memory.best.chain.length", self.best_chain_len() as _);
|
metrics::gauge!("state.memory.best.chain.length", self.best_chain_len() as _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use zebra_chain::serialization::ZcashDeserializeInto;
|
||||||
|
use zebra_test::prelude::*;
|
||||||
|
|
||||||
|
use crate::tests::FakeChainHelper;
|
||||||
|
|
||||||
|
use self::assert_eq;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn best_chain_wins() -> Result<()> {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
let block2: Arc<Block> =
|
||||||
|
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
|
||||||
|
let block1: Arc<Block> =
|
||||||
|
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
|
||||||
|
// Create a random block which will have a much worse difficulty hash
|
||||||
|
// than an intentionally mined block from the mainnet
|
||||||
|
let child = block1.make_fake_child();
|
||||||
|
|
||||||
|
let expected_hash = block2.hash();
|
||||||
|
|
||||||
|
let mut state = NonFinalizedState::default();
|
||||||
|
state.commit_new_chain(block2);
|
||||||
|
state.commit_new_chain(child);
|
||||||
|
|
||||||
|
let best_chain = state.best_chain().unwrap();
|
||||||
|
assert!(best_chain.height_by_hash.contains_key(&expected_hash));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finalize_pops_from_best_chain() -> Result<()> {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
let block2: Arc<Block> =
|
||||||
|
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
|
||||||
|
let block1: Arc<Block> =
|
||||||
|
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
|
||||||
|
// Create a random block which will have a much worse difficulty hash
|
||||||
|
// than an intentionally mined block from the mainnet
|
||||||
|
let child = block1.make_fake_child();
|
||||||
|
|
||||||
|
let mut state = NonFinalizedState::default();
|
||||||
|
state.commit_new_chain(block1.clone());
|
||||||
|
state.commit_block(block2.clone());
|
||||||
|
state.commit_block(child);
|
||||||
|
|
||||||
|
let finalized = state.finalize();
|
||||||
|
assert_eq!(block1, finalized);
|
||||||
|
|
||||||
|
let finalized = state.finalize();
|
||||||
|
assert_eq!(block2, finalized);
|
||||||
|
|
||||||
|
assert!(state.best_chain().is_none());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,44 @@
|
||||||
use zebra_chain::block;
|
use std::{mem, sync::Arc};
|
||||||
|
|
||||||
|
use zebra_chain::{
|
||||||
|
block::{self, Block},
|
||||||
|
transaction::Transaction,
|
||||||
|
transparent,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// Helper trait for constructing "valid" looking chains of blocks
|
||||||
|
pub trait FakeChainHelper {
|
||||||
|
fn make_fake_child(&self) -> Arc<Block>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FakeChainHelper for Block {
|
||||||
|
fn make_fake_child(&self) -> Arc<Block> {
|
||||||
|
let parent_hash = self.hash();
|
||||||
|
let mut child = Block::clone(self);
|
||||||
|
let mut transactions = mem::take(&mut child.transactions);
|
||||||
|
let mut tx = transactions.remove(0);
|
||||||
|
|
||||||
|
let input = match Arc::make_mut(&mut tx) {
|
||||||
|
Transaction::V1 { inputs, .. } => &mut inputs[0],
|
||||||
|
Transaction::V2 { inputs, .. } => &mut inputs[0],
|
||||||
|
Transaction::V3 { inputs, .. } => &mut inputs[0],
|
||||||
|
Transaction::V4 { inputs, .. } => &mut inputs[0],
|
||||||
|
};
|
||||||
|
|
||||||
|
match input {
|
||||||
|
transparent::Input::Coinbase { height, .. } => height.0 += 1,
|
||||||
|
_ => panic!("block must have a coinbase height to create a child"),
|
||||||
|
}
|
||||||
|
|
||||||
|
child.transactions.push(tx);
|
||||||
|
child.header.previous_block_hash = parent_hash;
|
||||||
|
|
||||||
|
Arc::new(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Block heights, and the expected minimum block locator height
|
/// Block heights, and the expected minimum block locator height
|
||||||
static BLOCK_LOCATOR_CASES: &[(u32, u32)] = &[
|
static BLOCK_LOCATOR_CASES: &[(u32, u32)] = &[
|
||||||
(0, 0),
|
(0, 0),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue