Skip to content

Commit

Permalink
Merge pull request #2412 from AleoHQ/unify-target-calculations
Browse files Browse the repository at this point in the history
[HackerOne-2332566] Unify target calculations
  • Loading branch information
howardwu authored Apr 2, 2024
2 parents abc7b50 + 2c95bb6 commit 925b3ca
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 83 deletions.
177 changes: 176 additions & 1 deletion ledger/block/src/helpers/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use console::prelude::{ensure, Result};
use console::prelude::{ensure, Network, Result};

/// A safety bound (sanity-check) for the coinbase reward.
pub const MAX_COINBASE_REWARD: u64 = 190_258_739; // Coinbase reward at block 1.
Expand Down Expand Up @@ -226,6 +226,74 @@ fn retarget(
Ok(u64::try_from(candidate_target)?)
}

/// This function calculates the next targets for the given attributes:
/// `latest_cumulative_proof_target`: The latest cumulative proof target.
/// `combined_proof_target`: The combined proof target of solutions in the block.
/// `latest_coinbase_target`: The latest coinbase target.
/// `last_coinbase_target`: The coinbase target for the last coinbase.
/// `last_coinbase_timestamp`: The timestamp for the last coinbase.
/// `next_timestamp`: The timestamp for the next block.
///
/// Returns the following as a tuple:
/// `next_coinbase_target` - The next coinbase target.
/// `next_proof_target` - The next proof target.
/// `next_cumulative_proof_target` - The next cumulative proof target.
/// `next_cumulative_weight` - The next cumulative weight.
/// `next_last_coinbase_target` - The next last coinbase target.
/// `next_last_coinbase_timestamp` - The next last coinbase timestamp.
pub fn to_next_targets<N: Network>(
latest_cumulative_proof_target: u128,
combined_proof_target: u128,
latest_coinbase_target: u64,
latest_cumulative_weight: u128,
last_coinbase_target: u64,
last_coinbase_timestamp: i64,
next_timestamp: i64,
) -> Result<(u64, u64, u128, u128, u64, i64)> {
// Compute the coinbase target threshold.
let latest_coinbase_threshold = latest_coinbase_target.saturating_div(2) as u128;
// Compute the next cumulative proof target.
let next_cumulative_proof_target = latest_cumulative_proof_target.saturating_add(combined_proof_target);
// Determine if the coinbase target threshold is reached.
let is_coinbase_threshold_reached = next_cumulative_proof_target >= latest_coinbase_threshold;
// Construct the next coinbase target.
let next_coinbase_target = coinbase_target(
last_coinbase_target,
last_coinbase_timestamp,
next_timestamp,
N::ANCHOR_TIME,
N::NUM_BLOCKS_PER_EPOCH,
N::GENESIS_COINBASE_TARGET,
)?;
// Construct the next proof target.
let next_proof_target =
proof_target(next_coinbase_target, N::GENESIS_PROOF_TARGET, N::MAX_SOLUTIONS_AS_POWER_OF_TWO);

// Update the next cumulative proof target, if necessary.
let next_cumulative_proof_target = match is_coinbase_threshold_reached {
true => 0,
false => next_cumulative_proof_target,
};

// Compute the next cumulative weight.
let next_cumulative_weight = latest_cumulative_weight.saturating_add(combined_proof_target);

// Construct the next last coinbase target and next last coinbase timestamp.
let (next_last_coinbase_target, next_last_coinbase_timestamp) = match is_coinbase_threshold_reached {
true => (next_coinbase_target, next_timestamp),
false => (last_coinbase_target, last_coinbase_timestamp),
};

Ok((
next_coinbase_target,
next_proof_target,
next_cumulative_proof_target,
next_cumulative_weight,
next_last_coinbase_target,
next_last_coinbase_timestamp,
))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -722,4 +790,111 @@ mod tests {

assert_eq!(EXPECTED_NUM_BLOCKS_TO_DOUBLE, num_blocks);
}

#[test]
fn test_to_next_targets_meets_threshold() {
let mut rng = TestRng::default();

let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;

for _ in 0..ITERATIONS {
// Sample the initial values.
let latest_coinbase_target = rng.gen_range(minimum_coinbase_target..u64::MAX / 2);
let threshold = latest_coinbase_target as u128 / 2;
let last_coinbase_target = rng.gen_range(minimum_coinbase_target..latest_coinbase_target);
let last_coinbase_timestamp = rng.gen_range(0..i64::MAX / 2);
let next_timestamp = last_coinbase_timestamp + 100;
let latest_cumulative_weight = rng.gen_range(0..u128::MAX / 2);

// Sample a cumulative proof target and combined proof target pair that meets the threshold.
let latest_cumulative_proof_target = rng.gen_range(0..threshold);
let combined_proof_target =
rng.gen_range(threshold.saturating_sub(latest_cumulative_proof_target)..u128::MAX);

assert!(latest_cumulative_proof_target.saturating_add(combined_proof_target) >= threshold);

// Calculate the next targets.
let (
_,
_,
next_cumulative_proof_target,
next_cumulative_weight,
next_last_coinbase_target,
next_last_coinbase_timestamp,
) = to_next_targets::<CurrentNetwork>(
latest_cumulative_proof_target,
combined_proof_target,
latest_coinbase_target,
latest_cumulative_weight,
last_coinbase_target,
last_coinbase_timestamp,
next_timestamp,
)
.unwrap();

// Check that meeting the target threshold does the following:
// 1. Resets the next_cumulative_proof_target.
// 2. Updates the last_coinbase_target.
// 3. Updates the last_coinbase_timestamp.
assert_eq!(next_cumulative_proof_target, 0);
assert_ne!(next_last_coinbase_target, last_coinbase_target);
assert_eq!(next_last_coinbase_timestamp, next_timestamp);

// Check that the cumulative_weight is updated correctly.
assert_eq!(next_cumulative_weight, latest_cumulative_weight.saturating_add(combined_proof_target));
}
}

#[test]
fn test_to_next_targets_does_not_meet_threshold() {
let mut rng = TestRng::default();

let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;

for _ in 0..ITERATIONS {
// Sample the initial values.
let latest_coinbase_target = rng.gen_range(minimum_coinbase_target..u64::MAX / 2);
let threshold = latest_coinbase_target as u128 / 2;
let last_coinbase_target = rng.gen_range(minimum_coinbase_target..latest_coinbase_target);
let last_coinbase_timestamp = rng.gen_range(0..i64::MAX / 2);
let next_timestamp = last_coinbase_timestamp + 100;
let latest_cumulative_weight = rng.gen_range(0..u128::MAX / 2);

// Sample a cumulative proof target and combined proof target pair that meets the threshold.
let latest_cumulative_proof_target = rng.gen_range(0..threshold);
let combined_proof_target = rng.gen_range(0..threshold.saturating_sub(latest_cumulative_proof_target));

assert!(latest_cumulative_proof_target.saturating_add(combined_proof_target) < threshold);

// Calculate the next targets.
let (
_,
_,
next_cumulative_proof_target,
next_cumulative_weight,
next_last_coinbase_target,
next_last_coinbase_timestamp,
) = to_next_targets::<CurrentNetwork>(
latest_cumulative_proof_target,
combined_proof_target,
latest_coinbase_target,
latest_cumulative_weight,
last_coinbase_target,
last_coinbase_timestamp,
next_timestamp,
)
.unwrap();

// Check that missing the target threshold does the following:
// 1. Does not reset the next_cumulative_proof_target.
// 2. Does not update the last_coinbase_target.
// 3. Does not update the last_coinbase_timestamp.
assert_eq!(next_cumulative_proof_target, latest_cumulative_proof_target + combined_proof_target);
assert_eq!(next_last_coinbase_target, last_coinbase_target);
assert_eq!(next_last_coinbase_timestamp, last_coinbase_timestamp);

// Check that the cumulative_weight is updated correctly.
assert_eq!(next_cumulative_weight, latest_cumulative_weight.saturating_add(combined_proof_target));
}
}
}
78 changes: 25 additions & 53 deletions ledger/block/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,67 +348,39 @@ impl<N: Network> Block<N> {
None => 0u128,
};

// Compute the cumulative proof target.
let cumulative_proof_target = match self.solutions.deref() {
Some(coinbase) => {
// Ensure the puzzle proof is valid.
if let Err(e) =
current_puzzle.check_solutions(coinbase, current_epoch_hash, previous_block.proof_target())
{
bail!("Block {height} contains an invalid puzzle proof - {e}");
}

// Ensure that the block cumulative proof target is less than the previous block's coinbase target.
// Note: This is a sanity check, as the cumulative proof target resets to 0 if the
// coinbase target was reached in this block.
if self.cumulative_proof_target() >= previous_block.coinbase_target() as u128 {
bail!(
"The cumulative proof target in block {height} must be less than the previous coinbase target"
)
}

// Compute the actual cumulative proof target (which can exceed the coinbase target).
previous_block.cumulative_proof_target().saturating_add(combined_proof_target)
// Verify the solutions.
if let Some(coinbase) = self.solutions.deref() {
// Ensure the puzzle proof is valid.
if let Err(e) = current_puzzle.check_solutions(coinbase, current_epoch_hash, previous_block.proof_target())
{
bail!("Block {height} contains an invalid puzzle proof - {e}");
}
None => previous_block.cumulative_proof_target(),
};

// Compute the coinbase target threshold.
let coinbase_threshold = previous_block.coinbase_target().saturating_div(2) as u128;
// Determine if the coinbase target threshold is reached.
let is_coinbase_threshold_reached = cumulative_proof_target >= coinbase_threshold;
// Compute the block cumulative proof target (which cannot exceed the coinbase target).
let expected_cumulative_proof_target = match is_coinbase_threshold_reached {
true => 0u128,
false => cumulative_proof_target,
// Ensure that the block cumulative proof target is less than the previous block's coinbase target.
// Note: This is a sanity check, as the cumulative proof target resets to 0 if the
// coinbase target was reached in this block.
if self.cumulative_proof_target() >= previous_block.coinbase_target() as u128 {
bail!("The cumulative proof target in block {height} must be less than the previous coinbase target")
}
};

// Compute the expected cumulative weight.
let expected_cumulative_weight = previous_block.cumulative_weight().saturating_add(combined_proof_target);

// Construct the next coinbase target.
let expected_coinbase_target = coinbase_target(
// Calculate the next coinbase targets and timestamps.
let (
expected_coinbase_target,
expected_proof_target,
expected_cumulative_proof_target,
expected_cumulative_weight,
expected_last_coinbase_target,
expected_last_coinbase_timestamp,
) = to_next_targets::<N>(
self.cumulative_proof_target(),
combined_proof_target,
previous_block.coinbase_target(),
previous_block.cumulative_weight(),
previous_block.last_coinbase_target(),
previous_block.last_coinbase_timestamp(),
timestamp,
N::ANCHOR_TIME,
N::NUM_BLOCKS_PER_EPOCH,
N::GENESIS_COINBASE_TARGET,
)?;
// Ensure the proof target is correct.
let expected_proof_target =
proof_target(expected_coinbase_target, N::GENESIS_PROOF_TARGET, N::MAX_SOLUTIONS_AS_POWER_OF_TWO);

// Determine the expected last coinbase target.
let expected_last_coinbase_target = match is_coinbase_threshold_reached {
true => expected_coinbase_target,
false => previous_block.last_coinbase_target(),
};
// Determine the expected last coinbase timestamp.
let expected_last_coinbase_timestamp = match is_coinbase_threshold_reached {
true => timestamp,
false => previous_block.last_coinbase_timestamp(),
};

// Calculate the expected coinbase reward.
let expected_coinbase_reward = coinbase_reward(
Expand Down
51 changes: 22 additions & 29 deletions ledger/src/advance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
let latest_cumulative_proof_target = previous_block.cumulative_proof_target();
// Retrieve the latest coinbase target.
let latest_coinbase_target = previous_block.coinbase_target();
// Retrieve the latest cumulative weight.
let latest_cumulative_weight = previous_block.cumulative_weight();
// Retrieve the last coinbase target.
let last_coinbase_target = previous_block.last_coinbase_target();
// Retrieve the last coinbase timestamp.
let last_coinbase_timestamp = previous_block.last_coinbase_timestamp();

// Compute the next round number.
let next_round = match subdag {
Expand Down Expand Up @@ -257,37 +263,24 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
}
None => OffsetDateTime::now_utc().unix_timestamp(),
};
// Compute the next cumulative weight.
let next_cumulative_weight = previous_block.cumulative_weight().saturating_add(combined_proof_target);
// Compute the next cumulative proof target.
let next_cumulative_proof_target = latest_cumulative_proof_target.saturating_add(combined_proof_target);
// Compute the coinbase target threshold.
let latest_coinbase_threshold = latest_coinbase_target.saturating_div(2) as u128;
// Determine if the coinbase target threshold is reached.
let is_coinbase_threshold_reached = next_cumulative_proof_target >= latest_coinbase_threshold;
// Update the next cumulative proof target, if necessary.
let next_cumulative_proof_target = match is_coinbase_threshold_reached {
true => 0,
false => next_cumulative_proof_target,
};
// Construct the next coinbase target.
let next_coinbase_target = coinbase_target(
previous_block.last_coinbase_target(),
previous_block.last_coinbase_timestamp(),

// Calculate the next coinbase targets and timestamps.
let (
next_coinbase_target,
next_proof_target,
next_cumulative_proof_target,
next_cumulative_weight,
next_last_coinbase_target,
next_last_coinbase_timestamp,
) = to_next_targets::<N>(
latest_cumulative_proof_target,
combined_proof_target,
latest_coinbase_target,
latest_cumulative_weight,
last_coinbase_target,
last_coinbase_timestamp,
next_timestamp,
N::ANCHOR_TIME,
N::NUM_BLOCKS_PER_EPOCH,
N::GENESIS_COINBASE_TARGET,
)?;
// Construct the next proof target.
let next_proof_target =
proof_target(next_coinbase_target, N::GENESIS_PROOF_TARGET, N::MAX_SOLUTIONS_AS_POWER_OF_TWO);

// Construct the next last coinbase target and next last coinbase timestamp.
let (next_last_coinbase_target, next_last_coinbase_timestamp) = match is_coinbase_threshold_reached {
true => (next_coinbase_target, next_timestamp),
false => (previous_block.last_coinbase_target(), previous_block.last_coinbase_timestamp()),
};

// Calculate the coinbase reward.
let coinbase_reward = coinbase_reward(
Expand Down

0 comments on commit 925b3ca

Please sign in to comment.