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

Key Verification in Big endian #4

Merged
merged 25 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b22cb11
feat: add tx and receipt proof logic
petscheit Apr 25, 2024
dfb453f
feat: add cairo eval + make command
petscheit Apr 25, 2024
443d065
chore: fmt
petscheit Apr 25, 2024
256e207
feat: add account profs
petscheit Apr 25, 2024
6d4de52
chore: update test fixtures
petscheit Apr 26, 2024
0f393ba
fix: remove key endian flip
petscheit Apr 29, 2024
4551585
uint128/256 reverse endian no padding
feltroidprime Apr 30, 2024
d9e5320
add count zeroes utils
feltroidprime May 2, 2024
985986a
add 2 edges cases to reverse_uint256 test
feltroidprime May 2, 2024
a0d04b3
Fix prove account test with new verify_mpt_proof
feltroidprime May 2, 2024
f4ae834
feat: Verify_mpt_proof with big endian key.
feltroidprime May 2, 2024
76d11f9
Fix Mpt Fuzz inputs processing
feltroidprime May 2, 2024
33fb040
fix: use assigned value in nibble cut
petscheit May 2, 2024
f88f1f7
chore: add content eval
petscheit May 2, 2024
f624e1c
chore: add special case for 0 bitlen
petscheit May 2, 2024
553ed66
chore: fmt
petscheit May 2, 2024
aea0a26
add failing test files
petscheit May 3, 2024
7c9835b
Fix comment in cairo_test script
feltroidprime May 6, 2024
64bc410
uncomment debug hints
feltroidprime May 6, 2024
cc4ced3
fix uint256_reverse and rework test
feltroidprime May 6, 2024
4b4def1
add py utils
feltroidprime May 6, 2024
f37ebf8
Count exact number of leading zeroes in assert_subset_in_key.
feltroidprime May 6, 2024
b674ebc
test: add small batch of MPT tests to CI
petscheit May 6, 2024
7f338f8
Merge pull request #3 from HerodotusDev/add-ci
petscheit May 8, 2024
4dcb024
Merge pull request #2 from HerodotusDev/fix/cut_assign
feltroidprime May 8, 2024
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
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ out/
.env
.encryptedKey
broadcast/

*.idea
tests/rust/target
node_modules
package-lock.json

.DS_Store
src/.DS_Store

solidity-verifier/lib/*
*.log

!tests/fuzzing/fixtures/*.json
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ test-full:

format-cairo:
@echo "Format all .cairo files"
./tools/make/format_cairo_files.sh
./tools/make/format_cairo_files.sh

fuzz-mpt:
./tools/make/fuzzer.sh tests/fuzzing/mpt.cairo
268 changes: 162 additions & 106 deletions lib/mpt.cairo

Large diffs are not rendered by default.

506 changes: 358 additions & 148 deletions lib/rlp_little.cairo

Large diffs are not rendered by default.

194 changes: 187 additions & 7 deletions lib/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,174 @@ from starkware.cairo.common.registers import get_fp_and_pc
const DIV_32 = 2 ** 32;
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 16.
func count_trailing_zeroes_128{bitwise_ptr: BitwiseBuiltin*}(x: felt, pow2_array: felt*) -> (
res: felt
) {
if (x == 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}Tr_Zerobytes")
%}
// Verify.
if (trailing_zeroes_bytes == 0) {
// Make sure the last byte is not zero.
let (_, last_byte) = bitwise_divmod(x, 2 ** 8);
if (last_byte == 0) {
assert 1 = 0; // Add unsatisfiability check.
return (res=0);
} else {
return (res=0);
}
} else {
// Make sure the last trailing_zeroes_bytes are zeroes.
let (q, r) = bitwise_divmod(x, pow2_array[8 * trailing_zeroes_bytes]);
assert r = 0;
// Make sure the byte just before the last trailing_zeroes_bytes is not zero.
let (_, first_non_zero_byte) = bitwise_divmod(q, 2 ** 8);
if (first_non_zero_byte == 0) {
assert 1 = 0; // Add unsatisfiability check.
return (res=0);
} else {
return (res=trailing_zeroes_bytes);
}
}
}

// Returns the number of bytes in a number with n_bits bits.
// Assumptions:
// - 0 <= n_bits < 8 * RC_BOUND
func n_bits_to_n_bytes{range_check_ptr: felt}(n_bits: felt) -> (res: felt) {
if (n_bits == 0) {
return (res=0);
}
let (q, r) = felt_divmod_8(n_bits);
if (q == 0) {
return (res=1);
}
if (r == 0) {
return (res=q);
}
return (res=q + 1);
}

// Returns the number of nibbles in a number with n_bits bits.
// Assumptions:
// - 0 <= n_bits < 4 * RC_BOUND
func n_bits_to_n_nibbles{range_check_ptr: felt}(n_bits: felt) -> (res: felt) {
if (n_bits == 0) {
return (res=0);
}
let (q, r) = felt_divmod(n_bits, 4);
if (q == 0) {
return (res=1);
}
if (r == 0) {
return (res=q);
}
return (res=q + 1);
}

// Returns the number of bytes in a 128 bits number.
// Assumptions:
// - 0 <= x < 2^128
func get_felt_n_bytes_128{range_check_ptr: felt}(x: felt, pow2_array: felt*) -> (n_bytes: felt) {
let n_bits = get_felt_bitlength_128{pow2_array=pow2_array}(x);
let (n_bytes) = n_bits_to_n_bytes(n_bits);
return (n_bytes,);
}

// Returns the number of nibbles in a 128 bits number.
func get_felt_n_nibbles{range_check_ptr: felt}(x: felt, pow2_array: felt*) -> (n_nibbles: felt) {
let n_bits = get_felt_bitlength_128{pow2_array=pow2_array}(x);
let (n_nibbles) = n_bits_to_n_nibbles(n_bits);
return (n_nibbles,);
}
// Returns the total number of bits in the uint256 number.
// Assumptions :
// - 0 <= x < 2^256
// Returns:
// - nbits: felt - Total number of bits in the uint256 number.
func get_uint256_bit_length{range_check_ptr}(x: Uint256, pow2_array: felt*) -> (nbits: felt) {
alloc_locals;
with pow2_array {
if (x.high != 0) {
let x_bit_high = get_felt_bitlength_128(x.high);
return (nbits=128 + x_bit_high);
} else {
if (x.low != 0) {
let x_bit_low = get_felt_bitlength_128(x.low);
return (nbits=x_bit_low);
} else {
return (nbits=0);
}
}
}
}

// Takes a uint128 number, reverse its byte endianness without adding right-padding
// Ex :
// Input = 0x123456
// Output = 0x563412
// Input = 0x123
// Output = 0x0312
func uint128_reverse_endian_no_padding{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(
x: felt, pow2_array: felt*
) -> (res: felt, n_bytes: felt) {
alloc_locals;
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"\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")
// %}
return (res=x_reversed, n_bytes=num_bytes_input);
}
return (res=x_reversed, n_bytes=num_bytes_input);
}

// Takes a uint256 number, reverse its byte endianness without adding right-padding and returns the number of bytes.
func uint256_reverse_endian_no_padding{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(
x: Uint256, pow2_array: felt*
) -> (res: Uint256, n_bytes: felt) {
alloc_locals;
if (x.high != 0) {
let (high_reversed, n_bytes_high) = uint128_reverse_endian_no_padding(x.high, pow2_array);
// %{ 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)}") %}
return (
res=Uint256(low=high_reversed + pow2_array[8 * n_bytes_high] * r, high=q),
n_bytes=16 + n_bytes_high,
);
} else {
let (low_reversed, n_bytes_low) = uint128_reverse_endian_no_padding(x.low, pow2_array);
return (res=Uint256(low=low_reversed, high=0), n_bytes=n_bytes_low);
}
}

// Adds two integers. Returns the result as a 256-bit integer and the (1-bit) carry.
// Strictly equivalent and faster version of common.uint256.uint256_add using the same whitelisted hint.
func uint256_add{range_check_ptr}(a: Uint256, b: Uint256) -> (res: Uint256, carry: felt) {
Expand Down Expand Up @@ -126,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 @@ -154,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 Expand Up @@ -191,12 +366,17 @@ func get_felt_bitlength_128{range_check_ptr, pow2_array: felt*}(x: felt) -> felt
// q: the quotient.
// r: the remainder.
func bitwise_divmod{bitwise_ptr: BitwiseBuiltin*}(x: felt, y: felt) -> (q: felt, r: felt) {
assert bitwise_ptr.x = x;
assert bitwise_ptr.y = y - 1;
let x_and_y = bitwise_ptr.x_and_y;
if (y == 1) {
let bitwise_ptr = bitwise_ptr;
return (q=x, r=0);
} else {
assert bitwise_ptr.x = x;
assert bitwise_ptr.y = y - 1;
let x_and_y = bitwise_ptr.x_and_y;

let bitwise_ptr = bitwise_ptr + BitwiseBuiltin.SIZE;
return (q=(x - x_and_y) / y, r=x_and_y);
let bitwise_ptr = bitwise_ptr + BitwiseBuiltin.SIZE;
return (q=(x - x_and_y) / y, r=x_and_y);
}
}

// Computes x//(2**32) and x%(2**32) using range checks operations.
Expand Down
37 changes: 20 additions & 17 deletions tests/cairo_programs/mpt_prove_EOA_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,10 @@ func main{
%}

let (pow2_array: felt*) = pow2alloc127();
let (keys_little: Uint256*) = alloc();
let (keys_be: Uint256*) = alloc();

hash_n_addresses(
addresses_64_little=addresses_64_little,
keys_little=keys_little,
n_addresses=n_proofs,
index=0,
addresses_64_little=addresses_64_little, keys_be=keys_be, n_addresses=n_proofs, index=0
);

let (values: felt**) = alloc();
Expand All @@ -91,7 +88,7 @@ func main{
mpt_proofs=account_proofs,
mpt_proofs_bytes_len=account_proofs_bytes_len,
mpt_proofs_len=account_proofs_len,
keys_little=keys_little,
keys_be=keys_be,
hashes_to_assert=state_roots,
n_proofs=n_proofs,
index=0,
Expand All @@ -104,19 +101,20 @@ func main{
}

func hash_n_addresses{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: KeccakBuiltin*}(
addresses_64_little: felt**, keys_little: Uint256*, n_addresses: felt, index: felt
addresses_64_little: felt**, keys_be: Uint256*, n_addresses: felt, index: felt
) {
alloc_locals;
if (index == n_addresses) {
return ();
} else {
let (hash: Uint256) = keccak(addresses_64_little[index], 20);
assert keys_little[index].low = hash.low;
assert keys_little[index].high = hash.high;
let (hash_le: Uint256) = keccak(addresses_64_little[index], 20);
let (hash: Uint256) = uint256_reverse_endian(hash_le);
assert keys_be[index].low = hash.low;
assert keys_be[index].high = hash.high;

return hash_n_addresses(
addresses_64_little=addresses_64_little,
keys_little=keys_little,
keys_be=keys_be,
n_addresses=n_addresses,
index=index + 1,
);
Expand All @@ -127,7 +125,7 @@ func verify_n_mpt_proofs{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_p
mpt_proofs: felt***,
mpt_proofs_bytes_len: felt**,
mpt_proofs_len: felt*,
keys_little: Uint256*,
keys_be: Uint256*,
hashes_to_assert: Uint256*,
n_proofs: felt,
index: felt,
Expand All @@ -139,14 +137,19 @@ func verify_n_mpt_proofs{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_p
if (index == n_proofs) {
return (values=values, values_lens=values_lens);
} else {
local key_be_leading_zeroes_nibbles;
let key_be: Uint256 = keys_be[index];
%{
from tools.py.utils import count_leading_zero_nibbles_from_hex
ids.key_be_leading_zeroes_nibbles = count_leading_zero_nibbles_from_hex(hex(ids.key_be.low+2**128*ids.key_be.high))
%}
let (value: felt*, value_len: felt) = verify_mpt_proof(
mpt_proof=mpt_proofs[index],
mpt_proof_bytes_len=mpt_proofs_bytes_len[index],
mpt_proof_len=mpt_proofs_len[index],
key_little=keys_little[index],
n_nibbles_already_checked=0,
node_index=0,
hash_to_assert=hashes_to_assert[index],
key_be=key_be,
key_be_leading_zeroes_nibbles=key_be_leading_zeroes_nibbles,
root=hashes_to_assert[index],
pow2_array=pow2_array,
);
assert values_lens[index] = value_len;
Expand All @@ -155,7 +158,7 @@ func verify_n_mpt_proofs{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_p
mpt_proofs=mpt_proofs,
mpt_proofs_bytes_len=mpt_proofs_bytes_len,
mpt_proofs_len=mpt_proofs_len,
keys_little=keys_little,
keys_be=keys_be,
hashes_to_assert=hashes_to_assert,
n_proofs=n_proofs,
index=index + 1,
Expand Down
Loading
Loading