Skip to content

Commit

Permalink
feat: optimize compute_target_threshold
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Oct 18, 2024
1 parent c338561 commit ab208ee
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 54 deletions.
74 changes: 51 additions & 23 deletions src/bitcoin/block.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::utils::{
pow2::{pow2_u128, pow2_u256}, numeric::u32_byte_reverse, hash::Digest,
double_sha256::double_sha256_u32_array
pow2::pow2_u128, numeric::u32_byte_reverse, hash::Digest, double_sha256::double_sha256_u32_array
};
use core::traits::DivRem;

Expand Down Expand Up @@ -96,32 +95,61 @@ pub impl PowVerificationImpl of PowVerificationTrait {
fn compute_target_threshold(self: @BlockHeader) -> u256 {
let (exponent, mantissa) = DivRem::div_rem(*self.bits, 0x1000000);

if exponent == 0 {
// Special case: exponent 0 means we use the mantissa as-is
return mantissa.into();
}

// Check if mantissa is valid (most significant byte has to be < 0x80)
// https://bitcoin.stackexchange.com/questions/113535/why-1d00ffff-and-not-1cffffff-as-target-in-genesis-block
if mantissa > 0x7FFFFF {
panic!("Target cannot have most significant bit set");
};

// Calculate the full target value
if exponent <= 3 {
let shift = 8 * (3 - exponent);
// MAX_TARGET > 2^128 so we can return early
(mantissa.into() / pow2_u128(shift)).into()
} else if exponent <= 32 {
let shift = 8 * (exponent - 3);
let target = (mantissa.into() * pow2_u256(shift));
// Ensure the target doesn't exceed the maximum allowed value
if target > MAX_TARGET {
panic!("Target exceeds maximum value");
}
target
let target = match exponent {
0 => { return u256 { low: (mantissa / 0x1000000).into(), high: 0 }; },
1 => { return u256 { low: (mantissa / 0x10000).into(), high: 0 }; },
2 => { return u256 { low: (mantissa / 0x100).into(), high: 0 }; },
3 => { return u256 { low: mantissa.into(), high: 0 }; },
// because mantissa is on 3 bytes, if we shift by less than 2^(128-24), it's a low
4 => { return u256 { low: (mantissa.into() * 0x100), high: 0 }; },
5 => { return u256 { low: (mantissa.into() * 0x10000), high: 0 }; },
6 => { return u256 { low: (mantissa.into() * 0x1000000), high: 0 }; },
7 => { return u256 { low: (mantissa.into() * 0x100000000), high: 0 }; },
8 => { return u256 { low: (mantissa.into() * 0x10000000000), high: 0 }; },
9 => { return u256 { low: (mantissa.into() * 0x1000000000000), high: 0 }; },
10 => { return u256 { low: (mantissa.into() * 0x100000000000000), high: 0 }; },
11 => { return u256 { low: (mantissa.into() * 0x10000000000000000), high: 0 }; },
12 => { return u256 { low: (mantissa.into() * 0x1000000000000000000), high: 0 }; },
13 => { return u256 { low: (mantissa.into() * 0x100000000000000000000), high: 0 }; },
14 => { return u256 { low: (mantissa.into() * 0x10000000000000000000000), high: 0 }; },
15 => {
return u256 { low: (mantissa.into() * 0x1000000000000000000000000), high: 0 };
},
16 => {
return u256 { low: (mantissa.into() * 0x100000000000000000000000000), high: 0 };
},
// here we don't know
17 => { return mantissa.into() * 0x10000000000000000000000000000; },
18 => { return mantissa.into() * 0x1000000000000000000000000000000; },
19 => { return mantissa.into() * 0x100000000000000000000000000000000; },
// here it's only a high
20 => { return u256 { low: 0, high: mantissa.into() * 0x100 }; },
21 => { return u256 { low: 0, high: mantissa.into() * 0x10000 }; },
22 => { return u256 { low: 0, high: mantissa.into() * 0x1000000 }; },
23 => { return u256 { low: 0, high: mantissa.into() * 0x100000000 }; },
24 => { return u256 { low: 0, high: mantissa.into() * 0x10000000000 }; },
25 => { return u256 { low: 0, high: mantissa.into() * 0x1000000000000 }; },
26 => { return u256 { low: 0, high: mantissa.into() * 0x100000000000000 }; },
27 => { return u256 { low: 0, high: mantissa.into() * 0x10000000000000000 }; },
28 => { return u256 { low: 0, high: mantissa.into() * 0x1000000000000000000 }; },
// because 0x7FFFFF * 2**(8 * (28 - 3)) < MAX_TARGET, for these two elements we have to
// check the target
29 => u256 { low: 0, high: mantissa.into() * 0x100000000000000000000 },
30 => u256 { low: 0, high: mantissa.into() * 0x10000000000000000000000 },
// because 2^(8 * (31 - 3)) > MAX_TARGET
31 => { return panic!("Target exceeds maximum value"); },
32 => { return panic!("Target exceeds maximum value"); },
_ => { return panic!("Target size cannot exceed 32 bytes"); },
};

if target > MAX_TARGET {
panic!("Target exceeds maximum value")
} else {
panic!("Target size cannot exceed 32 bytes")
target
}
}
}
Expand Down
32 changes: 1 addition & 31 deletions src/utils/pow2.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -140,44 +140,14 @@ pub fn pow2_u128(exponent: u32) -> u128 {
*results.span()[exponent]
}

/// Calculate 2 raised to the power of the given exponent
/// using a pre-computed lookup table
/// # Arguments
/// * `exponent` - The exponent to raise 2 to
/// # Returns
/// * `u256` - The result of 2^exponent
/// # Panics
/// * If `exponent` is greater than 255 (out of the supported range)
pub fn pow2_u256(exponent: u32) -> u256 {
if exponent < 128 {
pow2_u128(exponent).into()
} else {
u256 { low: 0, high: pow2_u128(exponent - 128), }
}
}

#[cfg(test)]
mod tests {
use super::{pow2_u128, pow2_u256};
use super::pow2_u128;

#[test]
fn test_fast_pow2_u128() {
assert(pow2_u128(0) == 1, '2^0 should be 1');
assert(pow2_u128(18) == 262144, '2^18 should be 262144');
assert(pow2_u128(127) == 170141183460469231731687303715884105728, '2^127 correct');
}

#[test]
#[available_gas(1000000000)]
fn test_fast_pow2_u256() {
assert(pow2_u256(0) == 1, '2^0 should be 1');
assert(pow2_u256(1) == 2, '2^1 should be 2');
assert(pow2_u256(128) == 340282366920938463463374607431768211456, '2^128 correct');
assert(
pow2_u256(
255
) == 57896044618658097711785492504343953926634992332820282019728792003956564819968,
'2^255 correct'
);
}
}

0 comments on commit ab208ee

Please sign in to comment.