Skip to content
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

add bits_to_target #15

Merged
merged 11 commits into from
Aug 8, 2024
86 changes: 84 additions & 2 deletions src/engine.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ pub impl BlockHeaderEngineImpl of BlockHeaderEngineTrait {
}

fn validate_target(ref self: BlockHeaderEngine) -> Result<(), felt252> {
let target = bits_to_target(self.context.block_header.bits)?;
if self.context.target != target {
return Result::Err('Invalid target');
}
Result::Ok(())
}

Expand Down Expand Up @@ -164,8 +168,54 @@ pub impl BlockHeaderEngineImpl of BlockHeaderEngineTrait {
}

// Helper functions
fn bits_to_target(bits: u32) -> u256 {
0
pub fn bits_to_target(bits: u32) -> Result<u256, felt252> {
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved
// Extract exponent and mantissa
let exponent: u32 = (bits / 0x1000000);
let mantissa: u32 = bits & 0x00FFFFFF;

// Check if mantissa is valid (should be less than 0x1000000)
if mantissa > 0x7FFFFF && exponent != 0 {
return Result::Err('Invalid mantissa');
}

// Calculate the full target value
let mut target: u256 = mantissa.into();

if exponent == 0 {
// Special case: exponent 0 means we use the mantissa as-is
return Result::Ok(target);
} else if exponent <= 3 {
// For exponents 1, 2, and 3, divide by 256^(3 - exponent) i.e right shift
let shift = 8 * (3 - exponent);
let mut multiplier: u256 = 1.into();
let mut i = 0;
while i < shift {
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved
multiplier = checked_mul(multiplier, 256.into()).expect('u256_mul Overflow');
i += 1;
};
target = checked_mul(target, multiplier).expect('u256_mul Overflow');
} else {
// Check for potential overflow
let shift = exponent - 3;
if shift >= 29 {
return Result::Err('Exponent too large');
}
// left shift
let mut multiplier: u256 = 256.into();
let mut i: u32 = 3;
while i < exponent {
multiplier = checked_mul(multiplier, 256.into()).expect('u256_mul Overflow');
i += 1;
};
target = checked_mul(target, multiplier).expect('u256_mul Overflow');
}

// Ensure the target doesn't exceed the maximum allowed value
if target > MAX_TARGET {
return Result::Err('Target exceeds maximum');
}

Result::Ok(target)
}

fn target_to_bits(target: u256) -> u32 {
Expand All @@ -179,3 +229,35 @@ fn compute_work_from_target(target: u256) -> u256 {
fn compute_timestamps_median(timestamps: Span<u32>) -> u32 {
0
}

// Helper function to calculate power of 256
fn u256_pow(base: u256, exp: u32) -> u256 {
if exp == 0 {
return 1.into();
}
let mut result: u256 = 1.into();
let mut i = 0;
while i < exp {
result = checked_mul(result, base).expect('u256_mul Overflow');
i += 1;
};
result
}

fn checked_mul(a: u256, b: u256) -> Option<u256> {
// If either number is zero, return zero immediately
if a == 0.into() || b == 0.into() {
return Option::Some(0.into());
}

// Perform multiplication and check for overflow
let product = a * b;

// Check if the product divided by either of the operands does not yield the other operand,
// which would indicate an overflow.
if product / a != b || product / b != a {
return Option::None;
}

Option::Some(product)
}
72 changes: 72 additions & 0 deletions tests/tests.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use raito::engine::bits_to_target;
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved

#[test]
fn test_bits_to_target_mantissa_zero() {
let result = bits_to_target(0x04000000);
assert!(result.is_ok(), "Should be valid");
assert!(result.unwrap() == 0_u256, "Target should be zero");
}

#[test]
fn test_bits_to_target_invalid_mantissa() {
let result = bits_to_target(0x04800000);
assert!(result.is_err(), "Should be invalid due to mantissa");
}

#[test]
fn test_bits_to_target_exponent_equal_3() {
let result = bits_to_target(0x03123456);
assert!(result.is_ok(), "Should be valid");
assert!(
result.unwrap() == 0x0000000000000000000000000000000000000000000000000000000000123456_u256,
"Incorrect target for exp = 3"
);
}

#[test]
fn test_bits_to_target_exponent_greater_than_3() {
let result = bits_to_target(0x05012345);
assert!(result.is_ok(), "Should be valid");
assert!(
result.unwrap() == 0x0000000000000000000000000000000000000000000000000000012345000000_u256,
"Incorrect target for exp = 5"
);
}

#[test]
fn test_bits_to_target_exponent_too_large() {
let result = bits_to_target(0x20123456);
assert!(result.is_err(), "Should be invalid due to large exponent");
}

#[test]
fn test_bits_to_target_max_target() {
let result = bits_to_target(0x1d00ffff);
assert!(result.is_err(), "Should be invalid due to exceeding MAX_TARGET");
}

#[test]
fn test_bits_to_target_overflow_target() {
let result = bits_to_target(0x1e00ffff);
assert!(result.is_err(), "Should be invalid due to overflow");
}

#[test]
fn test_bits_to_target_exponent_zero() {
let result = bits_to_target(0x00012345);
assert!(result.is_ok(), "Should be valid for exponent zero");
assert!(
result.unwrap() == 0x0000000000000000000000000000000000000000000000000000000000012345_u256,
"Incorrect target for exp = 0"
);
}

#[test]
fn test_bits_to_target_large_mantissa() {
let result = bits_to_target(0x050FFFFF);
assert!(result.is_ok(), "Should be valid for large mantissa");
assert!(
result.unwrap() == 0x00000000000000000000000000000000000000000000000000000FFFFF000000_u256,
"Incorrect target for large mantissa"
);
}