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
39 changes: 37 additions & 2 deletions src/engine.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use core::result::Result;
use core::option::OptionTrait;
use core::traits::Into;
use core::byte_array::ByteArray;
use raito::utils::shl;
use raito::utils::shr;

// Constants
const BLOCK_HEADER_SIZE: u32 = 80;
Expand Down Expand Up @@ -129,6 +131,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 +170,37 @@ 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);
target = shr(target, shift);
} else {
let shift = 8 * (exponent - 3);
target = shl(target, shift);
}

// 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 Down
1 change: 1 addition & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod engine;
pub mod utils;

mod main;
35 changes: 35 additions & 0 deletions src/utils.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use core::traits::Into;
use core::traits::TryInto;

// Bitwise shift left for u256
pub fn shl(value: u256, shift: u32) -> u256 {
value * fast_pow(2.into(), shift.into())
}

// Bitwise shift right for u256
pub fn shr(value: u256, shift: u32) -> u256 {
value / fast_pow(2.into(), shift.into())
}

// Fast exponentiation using the square-and-multiply algorithm
// Reference: https://github.com/keep-starknet-strange/alexandria/blob/bcdca70afdf59c9976148e95cebad5cf63d75a7f/packages/math/src/fast_power.cairo#L12
pub fn fast_pow(base: u256, exp: u256) -> u256 {
if exp == 0.into() {
return 1.into();
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved
}

let mut res: u256 = 1.into();
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved
let mut base: u256 = base;
let mut exp: u256 = exp;

loop {
if exp % 2.into() == 1.into() {
res = res * base;
}
exp = exp / 2.into();
if exp == 0.into() {
break res;
}
base = base * base;
}
}
76 changes: 76 additions & 0 deletions tests/tests.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use raito::engine::bits_to_target;
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved

#[test]
fn test_bits_to_target_01003456() {
let result = bits_to_target(0x01003456);
assert!(result.is_ok(), "Should be valid");
assert!(result.unwrap() == 0x00_u256, "Incorrect target for 0x01003456");
}

#[test]
fn test_bits_to_target_01123456() {
let result = bits_to_target(0x01123456);
assert!(result.is_ok(), "Should be valid");
assert!(result.unwrap() == 0x12_u256, "Incorrect target for 0x01123456");
}

#[test]
fn test_bits_to_target_02008000() {
let result = bits_to_target(0x02008000);
assert!(result.is_ok(), "Should be valid");
assert!(result.unwrap() == 0x80_u256, "Incorrect target for 0x02008000");
}

#[test]
fn test_bits_to_target_181bc330() {
let result = bits_to_target(0x181bc330);
assert!(result.is_ok(), "Should be valid");
assert!(
result.unwrap() == 0x1bc330000000000000000000000000000000000000000000_u256,
"Incorrect target for 0x181bc330"
);
}

#[test]
fn test_bits_to_target_05009234() {
let result = bits_to_target(0x05009234);
assert!(result.is_ok(), "Should be valid");
assert!(result.unwrap() == 0x92340000_u256, "Incorrect target for 0x05009234");
}

#[test]
fn test_bits_to_target_04123456() {
let result = bits_to_target(0x04123456);
assert!(result.is_ok(), "Should be valid");
assert!(result.unwrap() == 0x12345600_u256, "Incorrect target for 0x04123456");
}

#[test]
fn test_bits_to_target_1d00ffff() {
let result = bits_to_target(0x1d00ffff);
assert!(result.is_ok(), "Should be valid");
assert!(
result.unwrap() == 0x00000000ffff0000000000000000000000000000000000000000000000000000_u256,
"Incorrect target for 0x1d00ffff"
);
}

#[test]
fn test_bits_to_target_1c0d3142() {
let result = bits_to_target(0x1c0d3142);
assert!(result.is_ok(), "Should be valid");
assert!(
result.unwrap() == 0x000000000d314200000000000000000000000000000000000000000000000000_u256,
"Incorrect target for 0x1c0d3142"
);
}

#[test]
fn test_bits_to_target_1707a429() {
let result = bits_to_target(0x1707a429);
assert!(result.is_ok(), "Should be valid");
assert!(
result.unwrap() == 0x00000000000000000007a4290000000000000000000000000000000000000000_u256,
"Incorrect target for 0x1707a429"
);
}