Add helpers for setting work on fake chains

This commit is contained in:
Jane Lusby 2020-11-10 13:07:47 -08:00 committed by Deirdre Connolly
parent dc9081b738
commit c41a7303fa
3 changed files with 136 additions and 20 deletions

View File

@ -239,20 +239,60 @@ impl CompactDifficulty {
/// [Zcash Specification]: https://zips.z.cash/protocol/protocol.pdf#workdef /// [Zcash Specification]: https://zips.z.cash/protocol/protocol.pdf#workdef
pub fn to_work(&self) -> Option<Work> { pub fn to_work(&self) -> Option<Work> {
let expanded = self.to_expanded()?; let expanded = self.to_expanded()?;
Work::try_from(expanded).ok()
}
}
impl TryFrom<ExpandedDifficulty> for Work {
type Error = ();
fn try_from(expanded: ExpandedDifficulty) -> Result<Self, Self::Error> {
// We need to compute `2^256 / (expanded + 1)`, but we can't represent // We need to compute `2^256 / (expanded + 1)`, but we can't represent
// 2^256, as it's too large for a u256. However, as 2^256 is at least as // 2^256, as it's too large for a u256. However, as 2^256 is at least as
// large as `expanded + 1`, it is equal to // large as `expanded + 1`, it is equal to
// `((2^256 - expanded - 1) / (expanded + 1)) + 1`, or // `((2^256 - expanded - 1) / (expanded + 1)) + 1`, or
let result = (!expanded.0 / (expanded.0 + 1)) + 1; let result = (!expanded.0 / (expanded.0 + 1)) + 1;
if result <= u128::MAX.into() { if result <= u128::MAX.into() {
Some(Work(result.as_u128())) Ok(Work(result.as_u128()))
} else { } else {
None Err(())
} }
} }
} }
impl TryFrom<u128> for Work {
type Error = ();
fn try_from(value: u128) -> Result<Self, Self::Error> {
if value == 0 {
Err(())
} else {
Ok(Work(value))
}
}
}
impl From<ExpandedDifficulty> for CompactDifficulty {
fn from(value: ExpandedDifficulty) -> Self {
value.to_compact()
}
}
impl From<Work> for CompactDifficulty {
fn from(value: Work) -> Self {
let expanded = ExpandedDifficulty::from(value);
Self::from(expanded)
}
}
impl From<Work> for ExpandedDifficulty {
fn from(value: Work) -> Self {
let work: U256 = value.0.into();
let expanded = (!work + 1) / work;
ExpandedDifficulty(expanded)
}
}
impl ExpandedDifficulty { impl ExpandedDifficulty {
/// Returns the difficulty of the hash. /// Returns the difficulty of the hash.
/// ///

View File

@ -235,13 +235,10 @@ mod tests {
fn best_chain_wins() -> Result<()> { fn best_chain_wins() -> Result<()> {
zebra_test::init(); zebra_test::init();
let block2: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let block1: Arc<Block> = let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?; zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
// Create a random block which will have a much worse difficulty threshold let block2 = block1.make_fake_child().set_work(10);
// than an intentionally mined block from the mainnet let child = block1.make_fake_child().set_work(1);
let child = block1.make_fake_child();
let expected_hash = block2.hash(); let expected_hash = block2.hash();
@ -259,13 +256,10 @@ mod tests {
fn finalize_pops_from_best_chain() -> Result<()> { fn finalize_pops_from_best_chain() -> Result<()> {
zebra_test::init(); zebra_test::init();
let block2: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let block1: Arc<Block> = let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?; zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
// Create a random block which will have a much worse difficulty threshold let block2 = block1.make_fake_child().set_work(10);
// than an intentionally mined block from the mainnet let child = block1.make_fake_child().set_work(1);
let child = block1.make_fake_child();
let mut state = NonFinalizedState::default(); let mut state = NonFinalizedState::default();
state.commit_new_chain(block1.clone()); state.commit_new_chain(block1.clone());
@ -288,14 +282,11 @@ mod tests {
fn commit_block_extending_best_chain_doesnt_drop_worst_chains() -> Result<()> { fn commit_block_extending_best_chain_doesnt_drop_worst_chains() -> Result<()> {
zebra_test::init(); zebra_test::init();
let block2: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let block1: Arc<Block> = let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?; zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
// Create a random block which will have a much worse difficulty threshold let block2 = block1.make_fake_child().set_work(10);
// than an intentionally mined block from the mainnet let child1 = block1.make_fake_child().set_work(1);
let child1 = block1.make_fake_child(); let child2 = block2.make_fake_child().set_work(1);
let child2 = block2.make_fake_child();
let mut state = NonFinalizedState::default(); let mut state = NonFinalizedState::default();
assert_eq!(0, state.chain_set.len()); assert_eq!(0, state.chain_set.len());
@ -310,4 +301,79 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn shorter_chain_can_be_best_chain() -> Result<()> {
zebra_test::init();
let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
let long_chain_block1 = block1.make_fake_child().set_work(1);
let long_chain_block2 = long_chain_block1.make_fake_child().set_work(1);
let short_chain_block = block1.make_fake_child().set_work(3);
let mut state = NonFinalizedState::default();
state.commit_new_chain(block1);
state.commit_block(long_chain_block1);
state.commit_block(long_chain_block2);
state.commit_block(short_chain_block);
assert_eq!(2, state.chain_set.len());
assert_eq!(2, state.best_chain_len());
Ok(())
}
#[test]
fn longer_chain_with_more_work_wins() -> Result<()> {
zebra_test::init();
let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
let long_chain_block1 = block1.make_fake_child().set_work(1);
let long_chain_block2 = long_chain_block1.make_fake_child().set_work(1);
let long_chain_block3 = long_chain_block2.make_fake_child().set_work(1);
let long_chain_block4 = long_chain_block3.make_fake_child().set_work(1);
let short_chain_block = block1.make_fake_child().set_work(3);
let mut state = NonFinalizedState::default();
state.commit_new_chain(block1);
state.commit_block(long_chain_block1);
state.commit_block(long_chain_block2);
state.commit_block(long_chain_block3);
state.commit_block(long_chain_block4);
state.commit_block(short_chain_block);
assert_eq!(2, state.chain_set.len());
assert_eq!(5, state.best_chain_len());
Ok(())
}
#[test]
fn equal_length_goes_to_more_work() -> Result<()> {
zebra_test::init();
let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
let less_work_child = block1.make_fake_child().set_work(1);
let more_work_child = block1.make_fake_child().set_work(3);
let expected_hash = more_work_child.hash();
let mut state = NonFinalizedState::default();
state.commit_new_chain(block1);
state.commit_block(less_work_child);
state.commit_block(more_work_child);
assert_eq!(2, state.chain_set.len());
let tip_hash = state.tip().unwrap().1;
assert_eq!(expected_hash, tip_hash);
Ok(())
}
} }

View File

@ -1,9 +1,10 @@
use std::{mem, sync::Arc}; use std::{convert::TryFrom, mem, sync::Arc};
use zebra_chain::{ use zebra_chain::{
block::{self, Block}, block::{self, Block},
transaction::Transaction, transaction::Transaction,
transparent, transparent,
work::difficulty::Work,
}; };
use super::*; use super::*;
@ -11,9 +12,11 @@ use super::*;
/// Helper trait for constructing "valid" looking chains of blocks /// Helper trait for constructing "valid" looking chains of blocks
pub trait FakeChainHelper { pub trait FakeChainHelper {
fn make_fake_child(&self) -> Arc<Block>; fn make_fake_child(&self) -> Arc<Block>;
fn set_work(self, work: u128) -> Arc<Block>;
} }
impl FakeChainHelper for Block { impl FakeChainHelper for Arc<Block> {
fn make_fake_child(&self) -> Arc<Block> { fn make_fake_child(&self) -> Arc<Block> {
let parent_hash = self.hash(); let parent_hash = self.hash();
let mut child = Block::clone(self); let mut child = Block::clone(self);
@ -37,6 +40,13 @@ impl FakeChainHelper for Block {
Arc::new(child) Arc::new(child)
} }
fn set_work(mut self, work: u128) -> Arc<Block> {
let work = Work::try_from(work).expect("tests should only pass in valid work values");
let block = Arc::make_mut(&mut self);
block.header.difficulty_threshold = work.into();
self
}
} }
/// Block heights, and the expected minimum block locator height /// Block heights, and the expected minimum block locator height