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 target_to_bits #16

Merged
merged 11 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod utils;

mod state;
mod validation;
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: u32) -> u256 {
if exp == 0 {
return 1.into();
}

let mut res: u256 = 1.into();
let mut base: u256 = base;
let mut exp: u32 = exp;

loop {
if exp % 2 == 1 {
res = res * base;
}
exp = exp / 2;
if exp == 0 {
break res;
}
base = base * base;
}
}
47 changes: 47 additions & 0 deletions src/validation.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::state::{Block, ChainState, UtreexoState};
use raito::utils::shl;
use raito::utils::shr;

#[generate_trait]
impl BlockValidatorImpl of BlockValidator {
Expand Down Expand Up @@ -70,6 +72,51 @@ fn validate_merkle_root(self: @ChainState, block: @Block) -> Result<(), ByteArra
Result::Ok(())
}


pub fn target_to_bits(target: u256) -> Result<u32, felt252> {
if target == 0 {
return Result::Err('Target is zero');
}

if target > MAX_TARGET {
return Result::Err('Exceeds max value');
}

// Find the most significant byte
let mut size: u32 = 32;
let mut compact = target;

// Count leading zero bytes by finding the first non-zero byte
while size > 1 && shr(compact, (size - 1) * 8) == 0 {
size -= 1;
};

// Extract mantissa (most significant 3 bytes)
let mut mantissa: u32 = shr(compact, (size - 3) * 8).try_into().unwrap();

// Normalize
if mantissa > 0x7fffff {
mantissa = (mantissa + 0x80) / 0x100;
size += 1;
}

// Ensure the mantissa is only 3 bytes
mantissa = mantissa & 0xffffff;

// Check size doesn't exceed maximum
if size > 34 {
return Result::Err('Overflow');
}

// Convert size to u256
let size_u256: u256 = size.into();

// Combine size and mantissa
let result: u32 = (shl(size_u256, 24) + mantissa.into()).try_into().unwrap();

Result::Ok(result)
}

#[cfg(test)]
mod tests {
use super::{validate_target, validate_timestamp};
Expand Down
66 changes: 66 additions & 0 deletions tests/tests.cairo
harsh-ps-2003 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use core::result::ResultTrait;
use core::option::OptionTrait;
use core::traits::Into;
use raito::validation::target_to_bits;

#[test]
fn test_target_to_bits_large_target() {
let target: u256 = 0x1bc330000000000000000000000000000000000000000000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x181bc330, "Incorrect bits for large target");
}

#[test]
fn test_target_to_bits_small_target() {
let target: u256 = 0x92340000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x05009234, "Incorrect bits for small target");
}

#[test]
fn test_target_to_bits_medium_target() {
let target: u256 = 0x12345600;
let result = target_to_bits(target).unwrap();
assert!(result == 0x04123456, "Incorrect bits for medium target");
}

#[test]
fn test_target_to_bits_max_target() {
let max_target: u256 = 0x00000000ffff0000000000000000000000000000000000000000000000000000;
let result = target_to_bits(max_target).unwrap();
assert!(result == 0x1d00ffff, "Incorrect bits for max target");
}

#[test]
fn test_target_to_bits_high_precision_target() {
let target: u256 = 0x000000000d314200000000000000000000000000000000000000000000000000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x1c0d3142, "Incorrect bits for high precision target");
}

#[test]
fn test_target_to_bits_low_precision_target() {
let target: u256 = 0x00000000000000000007a4290000000000000000000000000000000000000000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x1707a429, "Incorrect bits for low precision target");
}

#[test]
fn test_target_to_bits_full_mantissa() {
let target: u256 = 0xd86a528bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8b;
let result = target_to_bits(target).unwrap();
assert!(result == 0x1d00d86a, "Incorrect bits for full mantissa target");
}

#[test]
fn test_target_to_bits_zero_target() {
let result = target_to_bits(0.into());
assert!(result.is_err(), "Should error on zero target");
}

#[test]
fn test_target_to_bits_overflow_target() {
let target: u256 = 0x01000000000000000000000000000000000000000000000000000000000000000;
let result = target_to_bits(target);
assert!(result.is_err(), "Should error on overflow target");
}
Loading