Skip to content

Commit

Permalink
feat: adaptable min difficulty check (#5896)
Browse files Browse the repository at this point in the history
Description
---
This includes an adaptable min difficulty check for incoming blocks. 

Motivation and Context
---
See issue: #5620 for the reasoning behind why this needs to include an
adaptable min difficulty check.

---------

Co-authored-by: Brian Pearce <brianp@users.noreply.github.com>
  • Loading branch information
SWvheerden and brianp committed Nov 7, 2023
1 parent aa2ae10 commit 76f323c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 5 deletions.
34 changes: 29 additions & 5 deletions base_layer/core/src/base_node/comms_interface/inbound_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::{
cmp::max,
collections::HashSet,
convert::{TryFrom, TryInto},
sync::Arc,
Expand Down Expand Up @@ -448,10 +449,11 @@ where B: BlockchainBackend + 'static
return Ok(());
}

// lets check that the difficulty at least matches the min required difficulty
// We cannot check the target difficulty as orphan blocks dont have a target difficulty.
// All we care here is that bad blocks are not free to make, and that they are more expensive to make then they
// are to validate. As soon as a block can be linked to the main chain, a proper full proof of work check will
// lets check that the difficulty at least matches 50% of the tip header. The max difficulty drop is 16%, thus
// 50% is way more than that and in order to attack the node, you need 50% of the mining power. We cannot check
// the target difficulty as orphan blocks dont have a target difficulty. All we care here is that bad
// blocks are not free to make, and that they are more expensive to make then they are to validate. As
// soon as a block can be linked to the main chain, a proper full proof of work check will
// be done before any other validation.
self.check_min_block_difficulty(&new_block).await?;

Expand Down Expand Up @@ -504,7 +506,29 @@ where B: BlockchainBackend + 'static

async fn check_min_block_difficulty(&self, new_block: &NewBlock) -> Result<(), CommsInterfaceError> {
let constants = self.consensus_manager.consensus_constants(new_block.header.height);
let min_difficulty = constants.min_pow_difficulty(new_block.header.pow.pow_algo);
let mut min_difficulty = constants.min_pow_difficulty(new_block.header.pow.pow_algo);
let mut header = self.blockchain_db.fetch_last_chain_header().await?;
loop {
if new_block.header.pow_algo() == header.header().pow_algo() {
min_difficulty = max(
header
.accumulated_data()
.target_difficulty
.checked_div_u64(2)
.unwrap_or(min_difficulty),
min_difficulty,
);
break;
}
if header.height() == 0 {
break;
}
// we have not reached gen block, and the pow algo does not match, so lets go further back
header = self
.blockchain_db
.fetch_chain_header(header.height().saturating_sub(1))
.await?;
}
let achieved = match new_block.header.pow_algo() {
PowAlgorithm::RandomX => randomx_difficulty(&new_block.header, &self.randomx_factory)?,
PowAlgorithm::Sha3x => sha3x_difficulty(&new_block.header)?,
Expand Down
13 changes: 13 additions & 0 deletions base_layer/core/src/proof_of_work/difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ impl Difficulty {
let result = result.min(u64::MAX.into());
Difficulty::from_u64(result.low_u64())
}

pub fn checked_div_u64(&self, other: u64) -> Option<Difficulty> {
match self.0.checked_div(other) {
None => None,
Some(n) => {
if n < MIN_DIFFICULTY {
None
} else {
Some(Difficulty(n))
}
},
}
}
}

/// These traits should not be implemented for `Difficulty`:
Expand Down

0 comments on commit 76f323c

Please sign in to comment.