-
Notifications
You must be signed in to change notification settings - Fork 35
proof of work based on bitcoin. #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
30e15e5
0a0d284
5d62b30
502c88d
1457783
71c37ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||
| use crate::chain::TxOutput; | ||||||
| use crate::primitives::Compact; | ||||||
| use parity_scale_codec::{Decode, Encode}; | ||||||
|
|
||||||
| #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] | ||||||
| pub enum ConsensusData { | ||||||
| #[codec(index = 0)] | ||||||
| None, | ||||||
| #[codec(index = 1)] | ||||||
| PoW(PoWData), | ||||||
| } | ||||||
|
|
||||||
| #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] | ||||||
| pub struct PoWData { | ||||||
| bits: Compact, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this would deviate from Bitcoin but the difficulty is determined by the time difference of the last two blocks that are at a height that is a multiple of 2016, so this field is not needed. Even if the field is included, we need to check it has the correct value if it's relied upon for the difficulty check. Have you thought about getting rid of it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bits field is being used in the ff. ways:
Let's say the current block height is 8067, and it's not due for retarget (since retarget has happened at 8064). As for checking whether its value is correct, Did you mean the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What I mean is that the value of the assert_eq!(calculate_difficulty_from_the_scratch(...).try_into(), Just(this_block.bits))Blocks that do not satisfy that condition must be rejected, since the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The But I removed it in this PR, because it's meant for the block production PR (which is currently closed, until Anton's or this is pushed). Did you mean because the bits is a u32 representation, while the difficulty is 256, that's why the bits field can't be trusted? Or because the previous block can't be trusted, since the bits field can easily be just any number and not from a calculated value during the generation of the 2016th multiple block? I haven't checked in bitcoin where that is, but we can actually add that kind of validation. I somehow do agree with you; removing the bits field is not so bad, since we can always calculate for the difficulty. Your For now I've been following how Bitcoin does it. But also I don't have problems diverging from Bitcoin. |
||||||
| nonce: u128, | ||||||
| /// contains the block reward | ||||||
| outputs: Vec<TxOutput>, | ||||||
| } | ||||||
|
|
||||||
| impl PoWData { | ||||||
| pub fn new(bits: Compact, nonce: u128, outputs: Vec<TxOutput>) -> Self { | ||||||
| PoWData { | ||||||
| bits, | ||||||
| nonce, | ||||||
| outputs, | ||||||
| } | ||||||
| } | ||||||
| pub fn bits(&self) -> Compact { | ||||||
| self.bits | ||||||
| } | ||||||
|
|
||||||
| pub fn nonce(&self) -> u128 { | ||||||
| self.nonce | ||||||
| } | ||||||
|
|
||||||
| pub fn outputs(&self) -> &[TxOutput] { | ||||||
| &self.outputs | ||||||
| } | ||||||
|
|
||||||
| pub fn update_nonce(&mut self, nonce: u128) { | ||||||
| self.nonce = nonce; | ||||||
| } | ||||||
| } | ||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| use crate::pow::temp::BlockIndex; | ||
| use crate::pow::Error; | ||
| use common::primitives::{BlockHeight, Compact}; | ||
| use common::Uint256; | ||
|
|
||
| /// checks if retargeting is due for the provided block_height | ||
| pub fn due_for_retarget(difficulty_adjustment_interval: u64, block_height: BlockHeight) -> bool { | ||
| block_height.inner() % difficulty_adjustment_interval == 0 | ||
| } | ||
|
|
||
| /// The block time of the first block, based on the difficulty adjustment interval, | ||
| /// where first block = height of given block - difficulty adjustment interval - 1 (off by one) | ||
| pub fn get_starting_block_time( | ||
| difficulty_adjustment_interval: u64, | ||
| block_index: &BlockIndex, | ||
| ) -> u32 { | ||
| let retarget_height = { | ||
| // Go back by what we want to be 14 days worth of blocks (the last 2015 blocks) | ||
| let old_block_height = block_index.height.inner() - (difficulty_adjustment_interval - 1); | ||
| BlockHeight::new(old_block_height) | ||
| }; | ||
|
|
||
| let retarget_block_index = block_index.get_ancestor(retarget_height); | ||
|
|
||
| retarget_block_index.get_block_time() | ||
| } | ||
|
|
||
| /// Returns a calculated new target as Compact datatype. | ||
| /// See Bitcoin's Protocol rules of [Difficulty change](https://en.bitcoin.it/wiki/Protocol_rules) | ||
| /// # Arguments | ||
| /// `actual_timespan_of_last_interval` - the actual timespan or the difference between the current block | ||
| /// and the 2016th block before it. This should be in seconds. | ||
| /// `target_timespan` - found in the `PoWChainConfig`. This should be in seconds. | ||
| /// `old_target` - Coming from the last block, this is the `bits` of the PoWData. | ||
| /// `difficulty_limit` - found in the PoWChainConfig, as `limit` | ||
| pub fn calculate_new_target( | ||
| actual_timespan_of_last_interval: u64, | ||
| target_timespan: u64, | ||
| old_target: Compact, | ||
| difficulty_limit: Uint256, | ||
| ) -> Result<Compact, Error> { | ||
| let actual_timespan = Uint256::from_u64(actual_timespan_of_last_interval).ok_or_else(|| { | ||
| Error::ConversionError(format!( | ||
| "conversion of actual timespan {:?} to Uint256 type failed.", | ||
| actual_timespan_of_last_interval | ||
| )) | ||
| })?; | ||
|
|
||
| let target_timespan = Uint256::from_u64(target_timespan).ok_or_else(|| { | ||
| Error::ConversionError(format!( | ||
| "conversion of target timespan {:?} to Uint256 type failed.", | ||
| target_timespan | ||
| )) | ||
| })?; | ||
|
|
||
| let old_target = Uint256::try_from(old_target).map_err(|e| { | ||
| Error::ConversionError(format!( | ||
| "conversion of bits {:?} to Uint256 type: {:?}", | ||
| old_target, e | ||
| )) | ||
| })?; | ||
|
|
||
| // new target is computed by multiplying the old target by ratio of the actual timespan / target timespan. | ||
| // see Bitcoin's Protocol rules of Difficulty change: https://en.bitcoin.it/wiki/Protocol_rules | ||
| let mut new_target = old_target * actual_timespan; | ||
| new_target = new_target / target_timespan; | ||
|
|
||
| new_target = if new_target > difficulty_limit { | ||
| difficulty_limit | ||
| } else { | ||
| new_target | ||
| }; | ||
|
|
||
| Ok(Compact::from(new_target)) | ||
| } | ||
|
|
||
| pub mod special_rules { | ||
| use super::*; | ||
|
|
||
| /// Checks if it took > 20 minutes to find a block | ||
| pub fn block_production_stalled( | ||
| target_spacing_in_secs: u64, | ||
| new_block_time: u32, | ||
| prev_block_time: u32, | ||
| ) -> bool { | ||
| new_block_time as u64 > (prev_block_time as u64 + (target_spacing_in_secs * 2)) | ||
| } | ||
|
|
||
| pub fn last_non_special_min_difficulty(_block_index: &BlockIndex) -> Compact { | ||
| // Return the last non-special-min-difficulty-rules-block | ||
| // let mut ctr_index = block_index.clone(); | ||
| // loop { | ||
| // let block_bits = ctr_index.data.bits(); | ||
| // if ctr_index.height == BlockHeight::zero() { | ||
| // return block_bits; | ||
| // } | ||
| // | ||
| // if due_for_retarget(pow_cfg, ctr_index.height) && block_bits == pow_cfg.limit() { | ||
| // match ctr_index.prev() { | ||
| // None => { return block_bits; } | ||
| // Some(id) => { } | ||
| // } | ||
| // } | ||
| // } | ||
| todo!() | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use crate::pow::helpers::due_for_retarget; | ||
| use common::primitives::BlockHeight; | ||
|
|
||
| #[test] | ||
| fn due_for_retarget_test() { | ||
| let interval = 2016; | ||
| let test = |h: BlockHeight| due_for_retarget(interval, h); | ||
|
|
||
| assert!(test(BlockHeight::zero())); | ||
| assert!(!test(BlockHeight::one())); | ||
| assert!(test(BlockHeight::new(interval))); | ||
| assert!(!test(BlockHeight::new(interval + 1))); | ||
| assert!(test(BlockHeight::new(interval * 2))); | ||
| assert!(test(BlockHeight::new(interval * 5))); | ||
| assert!(test(BlockHeight::new(interval * 10))); | ||
| assert!(!test(BlockHeight::new((interval * 10) + 1))); | ||
| assert!(!test(BlockHeight::new((interval * 10) - 1))); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| use common::chain::PoWChainConfig; | ||
| use common::Uint256; | ||
| use std::time::Duration; | ||
|
|
||
| mod helpers; | ||
| mod temp; | ||
| pub mod work; | ||
|
|
||
| #[derive(Debug)] | ||
| pub enum Error { | ||
| BlockToMineError(String), | ||
| ConversionError(String), | ||
| OutofBounds(String), | ||
| } | ||
|
|
||
| pub struct PoW(PoWChainConfig); | ||
|
|
||
| impl PoW { | ||
| pub fn difficulty_limit(&self) -> Uint256 { | ||
| self.0.limit() | ||
| } | ||
|
|
||
| pub fn no_retargeting(&self) -> bool { | ||
| self.0.no_retargeting() | ||
| } | ||
|
|
||
| pub fn allow_min_difficulty_blocks(&self) -> bool { | ||
| self.0.allow_min_difficulty_blocks() | ||
| } | ||
|
|
||
| pub fn target_spacing(&self) -> Duration { | ||
| self.0.target_spacing() | ||
| } | ||
|
|
||
| pub fn max_retarget_factor(&self) -> u64 { | ||
| self.0.max_retarget_factor() | ||
| } | ||
|
|
||
| pub fn target_timespan_in_secs(&self) -> u64 { | ||
| self.0.target_timespan().as_secs() | ||
| } | ||
|
|
||
| /// Follows the upper bound of the target timespan (2 weeks * 4) of Bitcoin. | ||
| /// See Bitcoin's Protocol rules on [Difficulty change](https://en.bitcoin.it/wiki/Protocol_rules) | ||
| pub fn max_target_timespan_in_secs(&self) -> u64 { | ||
| self.target_timespan_in_secs() * self.max_retarget_factor() | ||
| } | ||
|
|
||
| /// Follows the lower bound of the target timespan (2 weeks / 4) of Bitcoin. | ||
| /// See Bitcoin's Protocol rules on [Difficulty change](https://en.bitcoin.it/wiki/Protocol_rules) | ||
| pub fn min_target_timespan_in_secs(&self) -> u64 { | ||
| self.target_timespan_in_secs() / self.max_retarget_factor() | ||
| } | ||
|
|
||
| pub fn difficulty_adjustment_interval(&self) -> u64 { | ||
| // or a total of 2016 blocks | ||
| self.target_timespan_in_secs() / self.target_spacing().as_secs() | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.