Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
249 changes: 131 additions & 118 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"]
categories = ["cryptography::cryptocurrencies"]

[dependencies]
smplx-std = "0.0.8"
smplx-std = "0.0.9"

[dev-dependencies]
anyhow = { version = "1.0.101" }
Expand Down
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,44 @@ simf/lib
│ ├── Subtraction for `Fe`, `Scalar`, `Gej`.
│ ├── Equality predicates and their `assert_*` counterparts.
│ └── Safe Jacobian-to-affine normalization.
├── u1
│ └── convert.simf
├── u8
│ ├── convert.simf
│ └── math.simf
├── u16
│ ├── convert.simf
│ └── math.simf
├── u32
│ ├── convert.simf
│ └── math.simf
├── u64
│ ├── convert.simf
│ └── math.simf
├── u128
│ ├── bit.simf
│ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u128`.
│ ├── comparison.simf
│ │ └── Basic comparison operations that are available as jets for `u8`–`u64` but are missing for `u128`.
│ ├── convert.simf
│ │ └── Conversions between `u128` and other uint types.
│ └── math.simf
│ └── Overflow-checked arithmetic operations.
├── u256
│ ├── bit.simf
│ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u256`.
│ ├── comparison.simf
│ │ └── Basic comparison operations that are available as jets for `u8`–`u64` but are missing for `u256`.
│ ├── convert.simf
│ │ └── Conversions between `u256` and other uint types.
│ └── math.simf
│ └── Overflow-checked arithmetic operations.
├── asserts.simf
│ └── Generic assertion helpers with equality checks between uint values.
├── binary.simf
│ └── Basic binary logic operations: `and`, `or`, `not`, `xor`.
├── op_return.simf
│ └── Utilities for detecting and enforcing `OP_RETURN` (null data) outputs.
├── u8.simf
├── u16.simf
├── u32.simf
├── u64.simf
└── u128.simf
├── Overflow-checked arithmetic operations.
├── Comparison helpers.
└── Basic operations that are available as jets for `u8`-`u64` but are missing for `u128`.
└── Utilities for detecting and enforcing `OP_RETURN` (null data) outputs.
```

## Installation
Expand Down
71 changes: 71 additions & 0 deletions simf/lib/u256/bit.simf
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use crate::lib::u128::{and_128, or_128, left_shift_128, right_shift_128};

/// Bit logic

/// Bitwise AND of two 256-bit values
pub fn and_256(a: u256, b: u256) -> u256 {
let (a_high, a_low): (u128, u128) = <u256>::into(a);
let (b_high, b_low): (u128, u128) = <u256>::into(b);

<(u128, u128)>::into((and_128(a_high, b_high), and_128(a_low, b_low)))
}

/// Bitwise OR of two 256-bit values
pub fn or_256(a: u256, b: u256) -> u256 {
let (a_high, a_low): (u128, u128) = <u256>::into(a);
let (b_high, b_low): (u128, u128) = <u256>::into(b);

<(u128, u128)>::into((or_128(a_high, b_high), or_128(a_low, b_low)))
}

/// Left-shift a 256-bit value by the given amount. Bits are filled with zeroes
pub fn left_shift_256(shift: u8, a: u256) -> u256 {
match jet::is_zero_8(shift) {
true => a,
false => {
let (a_high, a_low): (u128, u128) = <u256>::into(a);

match jet::lt_8(shift, 128) {
true => {
let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128
let shifted_bits: u128 = right_shift_128(low_to_high_amount, a_low);

let res_high: u128 = or_128(left_shift_128(shift, a_high), shifted_bits);

<(u128, u128)>::into((res_high, left_shift_128(shift, a_low)))
},
false => {
let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128

<(u128, u128)>::into((left_shift_128(shift, a_low), 0))
}
}
}
}
}

/// Right-shift a 256-bit value by the given amount. Bits are filled with zeroes
pub fn right_shift_256(shift: u8, a: u256) -> u256 {
match jet::is_zero_8(shift) {
true => a,
false => {
let (a_high, a_low): (u128, u128) = <u256>::into(a);

match jet::lt_8(shift, 128) {
true => {
let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128
let shifted_bits: u128 = left_shift_128(high_to_low_amount, a_high);

let res_low: u128 = or_128(right_shift_128(shift, a_low), shifted_bits);

<(u128, u128)>::into((right_shift_128(shift, a_high), res_low))
},
false => {
let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128

<(u128, u128)>::into((0, right_shift_128(shift, a_high)))
}
}
}
}
}
49 changes: 49 additions & 0 deletions simf/lib/u256/comparison.simf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::lib::binary::and;
use crate::lib::u128::{eq_128, is_zero_128, lt_128, le_128};

/// Comparison operations

/// Checks if an integer is zero
pub fn is_zero_256(a: u256) -> bool {
let (a_high, a_low): (u128, u128) = <u256>::into(a);

and(is_zero_128(a_high), is_zero_128(a_low))
}

/// Checks if an integer is less than another integer
pub fn lt_256(a: u256, b: u256) -> bool {
let (a_high, a_low): (u128, u128) = <u256>::into(a);
let (b_high, b_low): (u128, u128) = <u256>::into(b);

match lt_128(a_high, b_high) {
true => true,
false => match eq_128(a_high, b_high) {
true => lt_128(a_low, b_low),
false => false,
},
}
}

/// Checks if an integer is less than or equal to another integer
pub fn le_256(a: u256, b: u256) -> bool {
let (a_high, a_low): (u128, u128) = <u256>::into(a);
let (b_high, b_low): (u128, u128) = <u256>::into(b);

match lt_128(a_high, b_high) {
true => true,
false => match eq_128(a_high, b_high) {
true => le_128(a_low, b_low),
false => false,
},
}
}

/// Check if an integer is greater than another integer
pub fn gt_256(a: u256, b: u256) -> bool {
lt_256(b, a)
}

/// Check if an integer is greater than or equal to another integer
pub fn ge_256(a: u256, b: u256) -> bool {
le_256(b, a)
}
138 changes: 138 additions & 0 deletions simf/lib/u256/convert.simf
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use crate::lib::u1::convert::u1_to_u256;
use crate::lib::u8::convert::u8_to_u256;
use crate::lib::u16::convert::u16_to_u256;
use crate::lib::u32::convert::u32_to_u256;
use crate::lib::u64::convert::u64_to_u256;
use crate::lib::u256::comparison::le_256;

/// Splitting uint conversions

/// Splits u256 into thirty two u8
pub fn split_u256_into_u8(
a: u256
) -> (
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8
) {
<u256>::into(a)
}

/// Splits u256 into sixteen u16
pub fn split_u256_into_u16(
a: u256
) -> (u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16) {
<u256>::into(a)
}

/// Splits u256 into eight u32
pub fn split_u256_into_u32(a: u256) -> (u32, u32, u32, u32, u32, u32, u32, u32) {
<u256>::into(a)
}

/// Splits u256 into four u64
pub fn split_u256_into_u64(a: u256) -> (u64, u64, u64, u64) {
<u256>::into(a)
}

/// Splits u256 into two u128
pub fn split_u256_into_u128(a: u256) -> (u128, u128) {
<u256>::into(a)
}

/// Narrowing uint conversions

/// Converts u256 into u1.
/// Panics if the value does not fit in u1
pub fn safe_u256_to_u1(a: u256) -> u1 {
let u1_max: u256 = u1_to_u256(jet::high_1());
let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a);

assert!(le_256(a, u1_max));

jet::rightmost_64_1(a_64)
}

/// Converts u256 into u8.
/// Panics if the value does not fit in u8
pub fn safe_u256_to_u8(a: u256) -> u8 {
let u8_max: u256 = u8_to_u256(jet::high_8());
let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a);

assert!(le_256(a, u8_max));

jet::rightmost_64_8(a_64)
}

/// Converts u256 into u16.
/// Panics if the value does not fit in u16
pub fn safe_u256_to_u16(a: u256) -> u16 {
let u16_max: u256 = u16_to_u256(jet::high_16());
let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a);

assert!(le_256(a, u16_max));

jet::rightmost_64_16(a_64)
}

/// Converts u256 into u32.
/// Panics if the value does not fit in u32
pub fn safe_u256_to_u32(a: u256) -> u32 {
let u32_max: u256 = u32_to_u256(jet::high_32());
let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a);

assert!(le_256(a, u32_max));

jet::rightmost_64_32(a_64)
}

/// Converts u256 into u64.
/// Panics if the value does not fit in u64
pub fn safe_u256_to_u64(a: u256) -> u64 {
let u64_max: u256 = u64_to_u256(jet::high_64());
let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a);

assert!(le_256(a, u64_max));

a_64
}

/// Converts u256 into u128.
/// Panics if the value does not fit in u128
pub fn safe_u256_to_u128(a: u256) -> u128 {
let u128_max: u256 = 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
let (_, a_128): (u128, u128) = split_u256_into_u128(a);

assert!(le_256(a, u128_max));

a_128
}
Loading