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

Final Fixs #2

Merged
merged 12 commits into from
May 8, 2024
4 changes: 3 additions & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ jobs:
- name: Run Cairo tests
env:
RPC_URL_MAINNET: ${{ secrets.RPC_URL_MAINNET }}
run: source ./tools/make/cairo_tests.sh
run: source ./tools/make/cairo_tests.sh
- name: Run MPT tests
run: source ./tools/make/fuzzer.sh tests/fuzzing/mpt.cairo --ci
102 changes: 51 additions & 51 deletions lib/mpt.cairo

Large diffs are not rendered by default.

243 changes: 186 additions & 57 deletions lib/rlp_little.cairo

Large diffs are not rendered by default.

43 changes: 24 additions & 19 deletions lib/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ const DIV_32_MINUS_1 = DIV_32 - 1;

// Takes the hex representation and count the number of zeroes.
// Ie: returns the number of trailing zeroes bytes.
// If x is 0, returns 0.
// If x is 0, returns 16.
func count_trailing_zeroes_128{bitwise_ptr: BitwiseBuiltin*}(x: felt, pow2_array: felt*) -> (
res: felt
) {
if (x == 0) {
return (res=0);
return (res=16);
}
alloc_locals;
local trailing_zeroes_bytes;
%{
from tools.py.utils import count_trailing_zero_bytes_from_int
ids.trailing_zeroes_bytes = count_trailing_zero_bytes_from_int(ids.x)
print(f"Input: {hex(ids.x)}_{ids.trailing_zeroes_bytes}bytes")
#print(f"Input: {hex(ids.x)}_{ids.trailing_zeroes_bytes}Tr_Zerobytes")
%}
// Verify.
if (trailing_zeroes_bytes == 0) {
Expand All @@ -55,6 +55,7 @@ func count_trailing_zeroes_128{bitwise_ptr: BitwiseBuiltin*}(x: felt, pow2_array
}
}
}

// Returns the number of bytes in a number with n_bits bits.
// Assumptions:
// - 0 <= n_bits < 8 * RC_BOUND
Expand Down Expand Up @@ -136,24 +137,23 @@ func uint128_reverse_endian_no_padding{range_check_ptr, bitwise_ptr: BitwiseBuil
x: felt, pow2_array: felt*
) -> (res: felt, n_bytes: felt) {
alloc_locals;
// %{ import math %}
let (num_bytes_input) = get_felt_n_bytes_128(x, pow2_array);
let (x_reversed) = word_reverse_endian(x);
let (num_bytes_reversed) = get_felt_n_bytes_128(x_reversed, pow2_array);
let (trailing_zeroes_input) = count_trailing_zeroes_128(x, pow2_array);

if (num_bytes_input != num_bytes_reversed) {
%{ print(f"\tinput: {hex(ids.x)}_{ids.num_bytes_input}bytes") %}
%{ print(f"\treversed: {hex(ids.x_reversed)}_{ids.num_bytes_reversed}bytes") %}
// %{ print(f"\tinput128: {hex(ids.x)}_{ids.num_bytes_input}bytes") %}
// %{ print(f"\treversed: {hex(ids.x_reversed)}_{ids.num_bytes_reversed}bytes") %}
let (x_reversed, r) = bitwise_divmod(
x_reversed,
pow2_array[8 * (num_bytes_reversed - num_bytes_input + trailing_zeroes_input)],
);
assert r = 0; // Sanity check.
%{
import math
print(f"\treversed_fixed: {hex(ids.x_reversed)}_{math.ceil(ids.x_reversed.bit_length() / 8)}bytes")
%}
// %{
// import math
// print(f"\treversed_fixed: {hex(ids.x_reversed)}_{math.ceil(ids.x_reversed.bit_length() / 8)}bytes")
// %}
return (res=x_reversed, n_bytes=num_bytes_input);
}
return (res=x_reversed, n_bytes=num_bytes_input);
Expand All @@ -166,15 +166,13 @@ func uint256_reverse_endian_no_padding{range_check_ptr, bitwise_ptr: BitwiseBuil
alloc_locals;
if (x.high != 0) {
let (high_reversed, n_bytes_high) = uint128_reverse_endian_no_padding(x.high, pow2_array);
%{ print(f"High: {hex(ids.high_reversed)}_{ids.high_reversed.bit_length()}b {ids.n_bytes_high}bytes") %}
let (low_reversed, n_bytes_low) = uint128_reverse_endian_no_padding(x.low, pow2_array);
%{ print(f"Low: {hex(ids.low_reversed)}_{ids.low_reversed.bit_length()}b {ids.n_bytes_low}bytes") %}
let low_reversed = low_reversed * pow2_array[8 * (16 - n_bytes_low)]; // Righ pad with 0 to make it 128 bits.
%{ print(f"Low: {hex(ids.low_reversed)}_{ids.low_reversed.bit_length()}b") %}
// %{ print(f"High_Rev: {hex(ids.high_reversed)}_{ids.high_reversed.bit_length()}b {ids.n_bytes_high}bytes") %}
let (low_reversed) = word_reverse_endian(x.low);
// %{ print(f"Low_rev: {hex(ids.low_reversed)}_{ids.low_reversed.bit_length()}b") %}

let (q, r) = bitwise_divmod(low_reversed, pow2_array[8 * (16 - n_bytes_high)]);
%{ print(f"Q: {hex(ids.q)}") %}
%{ print(f"R: {hex(ids.r)}") %}
// %{ print(f"Q: {hex(ids.q)}") %}
// %{ print(f"R: {hex(ids.r)}") %}
return (
res=Uint256(low=high_reversed + pow2_array[8 * n_bytes_high] * r, high=q),
n_bytes=16 + n_bytes_high,
Expand Down Expand Up @@ -296,10 +294,13 @@ func write_felt_array_to_dict_keys{dict_end: DictAccess*}(array: felt*, index: f
// Params:
// - x: felt - Input value.
// Assumptions for the caller:
// - 1 <= x < 2^127
// - 0 <= x < 2^127
// Returns:
// - bit_length: felt - Number of bits in x.
func get_felt_bitlength{range_check_ptr, pow2_array: felt*}(x: felt) -> felt {
if (x == 0) {
return 0;
}
alloc_locals;
local bit_length;
%{
Expand All @@ -324,12 +325,16 @@ func get_felt_bitlength{range_check_ptr, pow2_array: felt*}(x: felt) -> felt {
// Params:
// - x: felt - Input value.
// Assumptions for the caller:
// - 1 <= x < 2^128
// - 0 <= x < 2^128
// Returns:
// - bit_length: felt - Number of bits in x.
func get_felt_bitlength_128{range_check_ptr, pow2_array: felt*}(x: felt) -> felt {
if (x == 0) {
return 0;
}
alloc_locals;
local bit_length;

%{
x = ids.x
ids.bit_length = x.bit_length()
Expand Down
124 changes: 59 additions & 65 deletions tests/cairo_programs/reverse_uint256.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,76 @@ from lib.utils import pow2alloc128, uint256_reverse_endian_no_padding, Uint256
func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
alloc_locals;
let (pow2_array: felt*) = pow2alloc128();
// test_reverse(n_bits_index=1, pow2_array=pow2_array);
// %{ print("End tests!") %}
local x: Uint256;

// Some edge cases.
local x1: Uint256;
local x2: Uint256;
local x3: Uint256;
local x4: Uint256;
local x5: Uint256;
local x6: Uint256;
local x7: Uint256;
%{
import random
random.seed(0)
from tools.py.utils import split_128
# Used for the sanity check
def parse_int_to_bytes(x:int)-> bytes:
hex_str = hex(x)[2:]
if len(hex_str)%2==1:
hex_str = '0'+hex_str
return bytes.fromhex(hex_str)
from tools.py.utils import split_128, parse_int_to_bytes, count_leading_zero_nibbles_from_hex

x = 0x5553adf1a587bc5465b321660008c4c2e825bd3df2d2ccf001a009f3
input_bytes = parse_int_to_bytes(x)
print(f"input: {input_bytes}")
ids.x.low, ids.x.high = split_128(x)
print(f"input : {hex(x)}_{x.bit_length()}b")
print(f"input: {hex(ids.x.high)}_{ids.x.high.bit_length()}b {hex(ids.x.low)}_{ids.x.low.bit_length()}b")
%}
let (res, n_bytes) = uint256_reverse_endian_no_padding(x, pow2_array);
%{
# Test.
res_bytes = parse_int_to_bytes(ids.res.low + 2**128 * ids.res.high)
print(f"output : {hex(ids.res.high)}_{ids.res.high.bit_length()}b {hex(ids.res.low)}_{ids.res.low.bit_length()}b")
print(f"output: {res_bytes}")
# The input and output bytes should be the same, reversed.
assert input_bytes[::-1] == res_bytes, f"{input_bytes[::-1]} != {res_bytes}"
# The number of bytes returned should be the same as the number of bytes in the input.
assert ids.n_bytes == len(input_bytes)==len(res_bytes), f"{ids.n_bytes} != {len(input_bytes)} != {len(res_bytes)}"
%}
local x: Uint256;
%{
print("Second edge case")
x = 0xb847b60dcb5d984fc2a1ca0040a550cd3c4ac0adef268ded1249e1ae
input_bytes = parse_int_to_bytes(x)
print(f"input: {input_bytes}")
ids.x.low, ids.x.high = split_128(x)
print(f"input : {hex(x)}_{x.bit_length()}b")
print(f"input: {hex(ids.x.high)}_{ids.x.high.bit_length()}b {hex(ids.x.low)}_{ids.x.low.bit_length()}b")
x1 = 0x5553adf1a587bc5465b321660008c4c2e825bd3df2d2ccf001a009f3
x2 = 0xb847b60dcb5d984fc2a1ca0040a550cd3c4ac0adef268ded1249e1ae
x3 = 0xd4ae28bf208da8ad396463110021760d8278befbac8b4ecacb7c6e00
x4 = 0x12345600
x5 = 0x12401
x6 = 0x1240100
x7 = 0x124010

ids.x1.low, ids.x1.high = split_128(x1)
ids.x2.low, ids.x2.high = split_128(x2)
ids.x3.low, ids.x3.high = split_128(x3)
ids.x4.low, ids.x4.high = split_128(x4)
ids.x5.low, ids.x5.high = split_128(x5)
ids.x6.low, ids.x6.high = split_128(x6)
ids.x7.low, ids.x7.high = split_128(x7)

def test_function(test_input:int, test_output:int):
input_bytes = parse_int_to_bytes(test_input)
res_bytes = parse_int_to_bytes(test_output)

print(f"test_input: {input_bytes}")
print(f"test_output: {res_bytes}")

expected_output = input_bytes[::-1] # Reverse the input bytes.
# Trim leading zeroes from expected output :
expected_output = expected_output.lstrip(b'\x00')
# The input and output bytes should be the same, reversed.
assert expected_output == res_bytes, f"{expected_output} != {res_bytes}"
%}

test_reverse_single(x=x1, pow2_array=pow2_array);
test_reverse_single(x=x2, pow2_array=pow2_array);
test_reverse_single(x=x3, pow2_array=pow2_array);
test_reverse_single(x=x4, pow2_array=pow2_array);
test_reverse_single(x=x5, pow2_array=pow2_array);
test_reverse_single(x=x6, pow2_array=pow2_array);
test_reverse_single(x=x7, pow2_array=pow2_array);

test_reverse(n_bits_index=1, pow2_array=pow2_array);

%{ print("End tests!") %}

return ();
}

func test_reverse_single{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(
x: Uint256, pow2_array: felt*
) {
alloc_locals;
let (res, n_bytes) = uint256_reverse_endian_no_padding(x, pow2_array);
%{
# Test.
res_bytes = parse_int_to_bytes(ids.res.low + 2**128 * ids.res.high)
print(f"output : {hex(ids.res.high)}_{ids.res.high.bit_length()}b {hex(ids.res.low)}_{ids.res.low.bit_length()}b")
print(f"output: {res_bytes}")
# The input and output bytes should be the same, reversed.
assert input_bytes[::-1] == res_bytes, f"{input_bytes[::-1]} != {res_bytes}"
# The number of bytes returned should be the same as the number of bytes in the input.
assert ids.n_bytes == len(input_bytes)==len(res_bytes), f"{ids.n_bytes} != {len(input_bytes)} != {len(res_bytes)}"
test_function(test_input=ids.x.low + 2**128*ids.x.high, test_output=ids.res.low+2**128*ids.res.high)
%}
return ();
}

func test_reverse_inner{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(
n_bits_in_input: felt, pow2_array: felt*
) {
Expand All @@ -73,32 +86,13 @@ func test_reverse_inner{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(
%{
import random
random.seed(0)
from tools.py.utils import split_128
# Used for the sanity check
def parse_int_to_bytes(x:int)-> bytes:
hex_str = hex(x)[2:]
if len(hex_str)%2==1:
hex_str = '0'+hex_str
return bytes.fromhex(hex_str)


x = random.randint(2**(ids.n_bits_in_input - 1), 2**ids.n_bits_in_input - 1)
input_bytes = parse_int_to_bytes(x)

print(f"N bits in input: {ids.n_bits_in_input}")
print(f"input: {input_bytes}")
ids.x.low, ids.x.high = split_128(x)
print(f"input: {hex(ids.x.low + 2**128 * ids.x.high)}")
%}
let (res, n_bytes) = uint256_reverse_endian_no_padding(x, pow2_array);
%{
# Test.
res_bytes = parse_int_to_bytes(ids.res.low + 2**128 * ids.res.high)
print(f"output: {res_bytes}")
# The input and output bytes should be the same, reversed.
assert input_bytes[::-1] == res_bytes, f"{input_bytes[::-1]} != {res_bytes}"
# The number of bytes returned should be the same as the number of bytes in the input.
assert ids.n_bytes == len(input_bytes)==len(res_bytes), f"{ids.n_bytes} != {len(input_bytes)} != {len(res_bytes)}"
test_function(test_input=ids.x.low + 2**128*ids.x.high, test_output=ids.res.low+2**128*ids.res.high)
%}
return ();
}
Expand Down
Loading
Loading