Skip to content

Commit

Permalink
[KGA-87] fix: high offset memory expansion computations
Browse files Browse the repository at this point in the history
  • Loading branch information
enitrat committed Nov 12, 2024
1 parent 474951c commit e703c48
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 47 deletions.
44 changes: 33 additions & 11 deletions cairo_zero/kakarot/gas.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ from starkware.cairo.common.bool import FALSE
from starkware.cairo.common.uint256 import Uint256, uint256_lt

from kakarot.model import model
from utils.uint256 import uint256_eq
from utils.uint256 import uint256_eq, uint256_add
from utils.utils import Helpers
from utils.maths import unsigned_div_rem

Expand Down Expand Up @@ -142,19 +142,41 @@ namespace Gas {
) -> model.MemoryExpansion {
alloc_locals;

let (is_zero_1) = uint256_eq([size_1], Uint256(0, 0));
let (is_zero_2) = uint256_eq([size_2], Uint256(0, 0));
tempvar both_zero = is_zero_1 * is_zero_2;
let (is_zero_size_1) = uint256_eq([size_1], Uint256(0, 0));
let (is_zero_size_2) = uint256_eq([size_2], Uint256(0, 0));
tempvar both_zero = is_zero_size_1 * is_zero_size_2;
jmp no_expansion if both_zero != 0;

tempvar is_not_saturated = Helpers.is_zero(offset_1.high) * Helpers.is_zero(size_1.high) *
Helpers.is_zero(offset_2.high) * Helpers.is_zero(size_2.high);
tempvar is_saturated = 1 - is_not_saturated;
tempvar range_check_ptr = range_check_ptr;
jmp expansion_cost_saturated if is_saturated != 0;
if (is_zero_size_1 == FALSE) {
let (max_offset_1_res, carry_1) = uint256_add([offset_1], [size_1]);
tempvar is_chunk_1_saturated = carry_1 + is_not_zero(max_offset_1_res.high);
tempvar max_offset_1 = max_offset_1_res.low;
tempvar range_check_ptr = range_check_ptr;
// Early jump if the chunk is saturated
jmp expansion_cost_saturated if is_chunk_1_saturated != 0;
} else {
tempvar max_offset_1 = 0;
tempvar range_check_ptr = range_check_ptr;

Check warning on line 160 in cairo_zero/kakarot/gas.cairo

View check run for this annotation

Codecov / codecov/patch

cairo_zero/kakarot/gas.cairo#L159-L160

Added lines #L159 - L160 were not covered by tests
}
let max_offset_1 = [ap - 2];
let range_check_ptr = [ap - 1];

Check warning on line 163 in cairo_zero/kakarot/gas.cairo

View check run for this annotation

Codecov / codecov/patch

cairo_zero/kakarot/gas.cairo#L163

Added line #L163 was not covered by tests

if (is_zero_size_2 == FALSE) {
let (max_offset_2_res, carry_2) = uint256_add([offset_2], [size_2]);
tempvar is_chunk_2_saturated = carry_2 + is_not_zero(max_offset_2_res.high);
tempvar max_offset_2 = max_offset_2_res.low;
tempvar range_check_ptr = range_check_ptr;
// Early jump if the chunk is saturated
jmp expansion_cost_saturated if is_chunk_2_saturated != 0;
} else {
tempvar max_offset_2 = 0;
tempvar range_check_ptr = range_check_ptr;

Check warning on line 175 in cairo_zero/kakarot/gas.cairo

View check run for this annotation

Codecov / codecov/patch

cairo_zero/kakarot/gas.cairo#L174-L175

Added lines #L174 - L175 were not covered by tests
}
let max_offset_2 = [ap - 2];
let range_check_ptr = [ap - 1];

let max_offset_1 = (1 - is_zero_1) * (offset_1.low + size_1.low);
let max_offset_2 = (1 - is_zero_2) * (offset_2.low + size_2.low);
let max_expansion_is_2 = is_le_felt(max_offset_1, max_offset_2);
let max_offset = max_offset_1 * (1 - max_expansion_is_2) + max_offset_2 *
max_expansion_is_2;
Expand Down
22 changes: 11 additions & 11 deletions cairo_zero/tests/src/kakarot/test_gas.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ func test__max_memory_expansion_cost{range_check_ptr}() -> felt {
local size_2: Uint256;
%{
ids.words_len = program_input["words_len"];
ids.offset_1.low = program_input["offset_1"];
ids.offset_1.high = 0;
ids.size_1.low = program_input["size_1"];
ids.size_1.high = 0;
ids.offset_2.low = program_input["offset_2"];
ids.offset_2.high = 0;
ids.size_2.low = program_input["size_2"];
ids.size_2.high = 0;
ids.offset_1.low = program_input["offset_1"][0]
ids.offset_1.high = program_input["offset_1"][1]
ids.size_1.low = program_input["size_1"][0]
ids.size_1.high = program_input["size_1"][1]
ids.offset_2.low = program_input["offset_2"][0]
ids.offset_2.high = program_input["offset_2"][1]
ids.size_2.low = program_input["size_2"][0]
ids.size_2.high = program_input["size_2"][1]
%}
let memory_expansion = Gas.max_memory_expansion_cost(
words_len, &offset_1, &size_1, &offset_2, &size_2
Expand Down Expand Up @@ -74,9 +74,9 @@ func test__compute_message_call_gas{range_check_ptr}() -> felt {
tempvar gas_param: Uint256;
tempvar gas_left: felt;
%{
ids.gas_param.low = program_input["gas_param"];
ids.gas_param.high = 0;
ids.gas_left = program_input["gas_left"];
ids.gas_param.low = program_input["gas_param"][0]
ids.gas_param.high = program_input["gas_param"][1]
ids.gas_left = program_input["gas_left"]
%}
let gas = Gas.compute_message_call_gas(gas_param, gas_left);

Expand Down
46 changes: 28 additions & 18 deletions cairo_zero/tests/src/kakarot/test_gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from hypothesis import given
from hypothesis.strategies import integers

from kakarot_scripts.utils.uint256 import int_to_uint256
from tests.utils.constants import FELT_252_PRIME


class TestGas:
class TestCost:
Expand Down Expand Up @@ -33,32 +36,37 @@ def test_should_return_correct_expansion_cost(
assert diff == output

@given(
offset_1=integers(min_value=0, max_value=0xFFFFF),
size_1=integers(min_value=0, max_value=0xFFFFF),
offset_2=integers(min_value=0, max_value=0xFFFFF),
size_2=integers(min_value=0, max_value=0xFFFFF),
offset_1=integers(min_value=0, max_value=FELT_252_PRIME - 1),
size_1=integers(min_value=0, max_value=FELT_252_PRIME - 1),
offset_2=integers(min_value=0, max_value=FELT_252_PRIME - 1),
size_2=integers(min_value=0, max_value=FELT_252_PRIME - 1),
)
def test_should_return_max_expansion_cost(
self, cairo_run, offset_1, size_1, offset_2, size_2
):
MEMORY_COST_U32 = 0x200018000000
output = cairo_run(
"test__max_memory_expansion_cost",
words_len=0,
offset_1=offset_1,
size_1=size_1,
offset_2=offset_2,
size_2=size_2,
offset_1=int_to_uint256(offset_1),
size_1=int_to_uint256(size_1),
offset_2=int_to_uint256(offset_2),
size_2=int_to_uint256(size_2),
)
expansion = calculate_gas_extend_memory(
b"",
[
(offset_1, size_1),
(offset_2, size_2),
],
)
assert (
output
== calculate_gas_extend_memory(
b"",
[
(offset_1, size_1),
(offset_2, size_2),
],
).cost

# If the memory expansion is greater than 2**27 words of 32 bytes
# We saturate it to the hardcoded value corresponding the the gas cost of a 2**32 memory size
expected_saturated = (
MEMORY_COST_U32 if expansion.expand_by > 2**32 else expansion.cost
)
assert output == expected_saturated

@given(
offset=integers(min_value=0, max_value=2**256 - 1),
Expand Down Expand Up @@ -94,6 +102,8 @@ def test_should_return_message_base_gas(
self, cairo_run, gas_param, gas_left, expected
):
output = cairo_run(
"test__compute_message_call_gas", gas_param=gas_param, gas_left=gas_left
"test__compute_message_call_gas",
gas_param=int_to_uint256(gas_param),
gas_left=gas_left,
)
assert output == expected
15 changes: 8 additions & 7 deletions cairo_zero/tests/src/utils/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
from hypothesis.strategies import integers

from kakarot_scripts.utils.uint256 import int_to_uint256
from tests.utils.constants import FELT_252_PRIME
from tests.utils.errors import cairo_error
from tests.utils.hints import patch_hint

PRIME = 0x800000000000011000000000000000000000000000000000000000000000001


class TestBytes:
class TestFeltToAscii:
Expand All @@ -29,7 +28,7 @@ def test_should_return_bytes(self, cairo_run, n):
)
assert expected == bytes(output)

@given(n=integers(min_value=2**248, max_value=PRIME - 1))
@given(n=integers(min_value=2**248, max_value=FELT_252_PRIME - 1))
def test_should_raise_when_value_sup_31_bytes(self, cairo_run, n):
with cairo_error(message="felt_to_bytes_little: value >= 2**248"):
cairo_run("test__felt_to_bytes_little", n=n)
Expand Down Expand Up @@ -100,14 +99,16 @@ def test_should_return_bytes(self, cairo_run, n):
assert bytes.fromhex(f"{n:x}".rjust(len(res) * 2, "0")) == res

class TestFeltToBytes20:
@pytest.mark.parametrize("n", [0, 10, 1234, 0xFFFFFF, 2**128, PRIME - 1])
@pytest.mark.parametrize(
"n", [0, 10, 1234, 0xFFFFFF, 2**128, FELT_252_PRIME - 1]
)
def test_should_return_bytes20(self, cairo_run, n):
output = cairo_run("test__felt_to_bytes20", n=n)
assert f"{n:064x}"[-40:] == bytes(output).hex()

class TestUint256ToBytesLittle:
@pytest.mark.parametrize(
"n", [0, 10, 1234, 0xFFFFFF, 2**128, PRIME - 1, 2**256 - 1]
"n", [0, 10, 1234, 0xFFFFFF, 2**128, FELT_252_PRIME - 1, 2**256 - 1]
)
def test_should_return_bytes(self, cairo_run, n):
output = cairo_run("test__uint256_to_bytes_little", n=int_to_uint256(n))
Expand All @@ -116,7 +117,7 @@ def test_should_return_bytes(self, cairo_run, n):

class TestUint256ToBytes:
@pytest.mark.parametrize(
"n", [0, 10, 1234, 0xFFFFFF, 2**128, PRIME - 1, 2**256 - 1]
"n", [0, 10, 1234, 0xFFFFFF, 2**128, FELT_252_PRIME - 1, 2**256 - 1]
)
def test_should_return_bytes(self, cairo_run, n):
output = cairo_run("test__uint256_to_bytes", n=int_to_uint256(n))
Expand All @@ -125,7 +126,7 @@ def test_should_return_bytes(self, cairo_run, n):

class TestUint256ToBytes32:
@pytest.mark.parametrize(
"n", [0, 10, 1234, 0xFFFFFF, 2**128, PRIME - 1, 2**256 - 1]
"n", [0, 10, 1234, 0xFFFFFF, 2**128, FELT_252_PRIME - 1, 2**256 - 1]
)
def test_should_return_bytes(self, cairo_run, n):
output = cairo_run("test__uint256_to_bytes32", n=int_to_uint256(n))
Expand Down
2 changes: 2 additions & 0 deletions tests/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
BLOCK_NUMBER = 0x42
BLOCK_TIMESTAMP = int(time())

FELT_252_PRIME = 0x800000000000011000000000000000000000000000000000000000000000001

# Taken from eth_account.account.Account.sign_transaction docstring
# https://eth-account.readthedocs.io/en/stable/eth_account.html?highlight=sign_transaction#eth_account.account.Account.sign_transaction
TRANSACTIONS = [
Expand Down

0 comments on commit e703c48

Please sign in to comment.