Skip to content

Commit

Permalink
fix: ensure values < RC_BOUND for charge gas (#1390)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

<!-- Give an estimate of the time you spent on this PR in terms of work
days.
Did you spend 0.5 days on this PR or rather 2 days?  -->

Time spent on this PR:

## Pull request type

<!-- Please try to limit your pull request to one type,
submit multiple pull requests if needed. -->

Please check the type of change your PR introduces:

- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying,
or link to a relevant issue. -->

Resolves #1388

## What is the new behavior?
Ensures values < RC_BOUND for charge gas

<!-- Reviewable:start -->
- - -
This change is [<img src="https://reviewable.io/review_button.svg"
height="34" align="absmiddle"
alt="Reviewable"/>](https://reviewable.io/reviews/kkrt-labs/kakarot/1390)
<!-- Reviewable:end -->
  • Loading branch information
obatirou authored Sep 5, 2024
1 parent 1046bdd commit 25a58e8
Show file tree
Hide file tree
Showing 14 changed files with 213 additions and 20 deletions.
1 change: 0 additions & 1 deletion kakarot_scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
logger.setLevel(logging.INFO)
load_dotenv()

# Hardcode block gas limit to 7M
BLOCK_GAS_LIMIT = 7_000_000
DEFAULT_GAS_PRICE = int(1e9)
BEACON_ROOT_ADDRESS = "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
Expand Down
14 changes: 14 additions & 0 deletions src/kakarot/evm.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ namespace EVM {
);
}

func out_of_gas{range_check_ptr}(self: model.EVM*, amount: felt) -> model.EVM* {
let (revert_reason_len, revert_reason) = Errors.outOfGas(self.gas_left, amount);
return new model.EVM(
message=self.message,
return_data_len=revert_reason_len,
return_data=revert_reason,
program_counter=self.program_counter,
stopped=TRUE,
gas_left=0,
gas_refund=self.gas_refund,
reverted=Errors.EXCEPTIONAL_HALT,
);
}

// @notice Update the return data of the current execution context.
// @param self The pointer to the execution context.
// @param return_data_len The length of the return_data.
Expand Down
21 changes: 13 additions & 8 deletions src/kakarot/gas.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ namespace Gas {
const COLD_ACCOUNT_ACCESS = 2600;
const WARM_ACCESS = 100;
const INIT_CODE_WORD_COST = 2;
const MEMORY_COST_U128 = 0x200000000000000000000000000018000000000000000000000000000000;
const TX_BASE_COST = 21000;
const TX_ACCESS_LIST_ADDRESS_COST = 2400;
const TX_ACCESS_LIST_STORAGE_KEY_COST = 1900;
const BLOBHASH = 3;
const MEMORY_COST_U32 = 0x200018000000;
// @notice Compute the cost of the memory for a given words length.
// @dev To avoid range_check overflow, we compute words_len / 512
Expand Down Expand Up @@ -115,13 +115,13 @@ namespace Gas {
return expansion;
}

let is_low_part_overflowing = is_le_felt(2 ** 128, offset.low + size.low);
if (offset.high == 0 and size.high == 0 and is_low_part_overflowing == 0) {
let (q, _) = unsigned_div_rem(offset.low + size.low, 2 ** 32);
if (offset.high == 0 and size.high == 0 and q == 0) {
return calculate_gas_extend_memory(words_len, offset.low + size.low);
}
// Hardcoded value of cost(2**128) and size of 2**128 bytes = 2**123 words of 32 bytes
// Hardcoded value of cost(2**32) and size of 2**32 bytes = 2**27 words of 32 bytes
// This offset would produce an OOG error in any case
let expansion = model.MemoryExpansion(cost=MEMORY_COST_U128, new_words_len=2 ** 123);
let expansion = model.MemoryExpansion(cost=MEMORY_COST_U32, new_words_len=2 ** 27);
return expansion;
}

Expand Down Expand Up @@ -150,13 +150,18 @@ namespace Gas {
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;

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;
let (q, _) = unsigned_div_rem(max_offset, 2 ** 32);
tempvar range_check_ptr = range_check_ptr;
jmp expansion_cost_saturated if q != 0;

let expansion = calculate_gas_extend_memory(words_len, max_offset);
let expansion = model.MemoryExpansion(
cost=expansion.cost, new_words_len=expansion.new_words_len
Expand All @@ -169,10 +174,10 @@ namespace Gas {
return expansion;

expansion_cost_saturated:
let range_check_ptr = [fp - 8];
// Hardcoded value of cost(2**128) and size of 2**128 bytes = 2**123 words of 32 bytes
let range_check_ptr = [ap - 1];
// Hardcoded value of cost(2**32) and size of 2**32 bytes = 2**27 words of 32 bytes
// This offset would produce an OOG error in any case
let expansion = model.MemoryExpansion(cost=Gas.MEMORY_COST_U128, new_words_len=2 ** 123);
let expansion = model.MemoryExpansion(cost=MEMORY_COST_U32, new_words_len=2 ** 27);
return expansion;
}

Expand Down
15 changes: 15 additions & 0 deletions src/kakarot/instructions/environmental_information.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ namespace EnvironmentalInformation {
memory.words_len, memory_offset, size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Any size upper than 2**128 will cause an OOG error, considering the maximum gas for a transaction.
// here with size.low = 2**128 - 1, copy_gas_cost is 0x18000000000000000000000000000000, ie is between 2**124 and 2**125
let upper_bytes_bound = size.low + 31;
Expand Down Expand Up @@ -253,6 +258,11 @@ namespace EnvironmentalInformation {
memory.words_len, dest_offset, size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Any size upper than 2**128 will cause an OOG error, considering the maximum gas for a transaction.
// here with size.low = 2**128 - 1, copy_gas_cost is 0x18000000000000000000000000000000, ie is between 2**124 and 2**125
let upper_bytes_bound = size.low + 31;
Expand Down Expand Up @@ -398,6 +408,11 @@ namespace EnvironmentalInformation {
memory.words_len, dest_offset, size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

let evm = EVM.charge_gas(evm, access_gas_cost + copy_gas_cost + memory_expansion.cost);
if (evm.reverted != FALSE) {
return evm;
Expand Down
5 changes: 5 additions & 0 deletions src/kakarot/instructions/logging_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ namespace LoggingOperations {

let memory_expansion = Gas.memory_expansion_cost_saturated(memory.words_len, offset, size);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

let size_cost_low = Gas.LOG_DATA * size.low;
tempvar size_cost_high = is_not_zero(size.high) * 2 ** 128;
let topics_cost = Gas.LOG_TOPIC * topics_len;
Expand Down
20 changes: 20 additions & 0 deletions src/kakarot/instructions/memory_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ namespace MemoryOperations {
let memory_expansion = Gas.memory_expansion_cost_saturated(
memory.words_len, [offset], Uint256(32, 0)
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

let evm = EVM.charge_gas(evm, memory_expansion.cost);
if (evm.reverted != FALSE) {
return evm;
Expand Down Expand Up @@ -72,6 +78,11 @@ namespace MemoryOperations {
let memory_expansion = Gas.memory_expansion_cost_saturated(
memory.words_len, offset, Uint256(32, 0)
);
if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

let evm = EVM.charge_gas(evm, memory_expansion.cost);
if (evm.reverted != FALSE) {
return evm;
Expand Down Expand Up @@ -107,6 +118,11 @@ namespace MemoryOperations {
memory.words_len, src, size, dst, size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Any size upper than 2**128 will cause an OOG error, considering the maximum gas for a transaction.
// here with size.low = 2**128 - 1, copy_gas_cost is 0x18000000000000000000000000000000, ie is between 2**124 and 2**125
let upper_bytes_bound = size.low + 31;
Expand Down Expand Up @@ -258,6 +274,10 @@ namespace MemoryOperations {
let memory_expansion = Gas.memory_expansion_cost_saturated(
memory.words_len, offset, Uint256(1, 0)
);
if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}
let evm = EVM.charge_gas(evm, memory_expansion.cost);
if (evm.reverted != FALSE) {
return evm;
Expand Down
4 changes: 4 additions & 0 deletions src/kakarot/instructions/sha3.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ namespace Sha3 {

// GAS
let memory_expansion = Gas.memory_expansion_cost_saturated(memory.words_len, offset, size);
if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}
let (words, _) = unsigned_div_rem(size.low + 31, 32);
let words_gas_cost_low = Gas.KECCAK256_WORD * words;
tempvar words_gas_cost_high = is_not_zero(size.high) * 2 ** 128;
Expand Down
32 changes: 32 additions & 0 deletions src/kakarot/instructions/system_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ namespace SystemOperations {
// + init_code_gas
// + is_create2 * GAS_KECCAK256_WORD * call_data_words
let memory_expansion = Gas.memory_expansion_cost_saturated(memory.words_len, offset, size);
if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}
let (calldata_words, _) = unsigned_div_rem(size.low + 31, 32);
let init_code_gas_low = Gas.INIT_CODE_WORD_COST * calldata_words;
tempvar init_code_gas_high = is_not_zero(size.high) * 2 ** 128;
Expand Down Expand Up @@ -253,6 +257,10 @@ namespace SystemOperations {
let size = popped[1];

let memory_expansion = Gas.memory_expansion_cost_saturated(memory.words_len, offset, size);
if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}
let evm = EVM.charge_gas(evm, memory_expansion.cost);
if (evm.reverted != FALSE) {
return evm;
Expand Down Expand Up @@ -295,6 +303,10 @@ namespace SystemOperations {
let size = popped[1];

let memory_expansion = Gas.memory_expansion_cost_saturated(memory.words_len, offset, size);
if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}
let evm = EVM.charge_gas(evm, memory_expansion.cost);
if (evm.reverted != FALSE) {
return evm;
Expand Down Expand Up @@ -354,6 +366,11 @@ namespace SystemOperations {
memory.words_len, args_offset, args_size, ret_offset, ret_size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Access gas cost. The account is marked as warm in the `generic_call` function,
// which performs a `get_account`.
let is_account_warm = State.is_account_warm(to);
Expand Down Expand Up @@ -483,6 +500,11 @@ namespace SystemOperations {
memory.words_len, args_offset, args_size, ret_offset, ret_size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Access gas cost. The account is marked as warm in the `is_account_alive` instruction,
// which performs a `get_account`.
let is_account_warm = State.is_account_warm(to);
Expand Down Expand Up @@ -581,6 +603,11 @@ namespace SystemOperations {
memory.words_len, args_offset, args_size, ret_offset, ret_size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Access gas cost. The account is marked as warm in the `is_account_alive` instruction,
// which performs a `get_account`.
let is_account_warm = State.is_account_warm(code_address);
Expand Down Expand Up @@ -685,6 +712,11 @@ namespace SystemOperations {
memory.words_len, args_offset, args_size, ret_offset, ret_size
);

if (memory_expansion.cost == Gas.MEMORY_COST_U32) {
let evm = EVM.out_of_gas(evm, memory_expansion.cost);
return evm;
}

// Access gas cost. The account is marked as warm in the `generic_call` function,
// which performs a `get_account`.
let is_account_warm = State.is_account_warm(code_address);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ func test__exec_extcodecopy_zellic_issue_1258{
return memory;
}

func test__exec_codecopy{
func test__exec_copy{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*
}() -> model.Memory* {
}() -> (model.EVM*, model.Memory*) {
// Given
alloc_locals;
local size: felt;
Expand Down Expand Up @@ -194,10 +194,10 @@ func test__exec_codecopy{

// Then
assert stack.size = 0;
return memory;
return (evm, memory);
}

func test__exec_codecopy_offset_high_zellic_issue_1258{
func test__exec_copy_offset_high_zellic_issue_1258{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*
}() -> model.Memory* {
// Given
Expand Down
36 changes: 31 additions & 5 deletions tests/src/kakarot/instructions/test_environmental_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import pytest
from Crypto.Hash import keccak
from hypothesis import example, given
from hypothesis import strategies as st

from kakarot_scripts.utils.uint256 import int_to_uint256
from tests.utils.syscall_handler import SyscallHandler
Expand Down Expand Up @@ -145,12 +147,12 @@ class TestCopy:
"offset_is_bytecodelen",
],
)
def test_exec_codecopy_should_copy_code(
def test_exec_copy_should_copy_code(
self, cairo_run, size, offset, dest_offset, opcode_number, bytecode
):
bytecode.insert(0, opcode_number) # random bytecode that can be mutated
memory = cairo_run(
"test__exec_codecopy",
(_, memory) = cairo_run(
"test__exec_copy",
size=size,
offset=offset,
dest_offset=dest_offset,
Expand All @@ -167,6 +169,30 @@ def test_exec_codecopy_should_copy_code(
== copied_bytecode
)

@given(
opcode_number=st.sampled_from([0x39, 0x37]),
offset=st.integers(0, 2**128 - 1),
dest_offset=st.integers(0, 2**128 - 1),
)
@example(opcode_number=0x39, offset=2**128 - 1, dest_offset=0)
@example(opcode_number=0x39, offset=0, dest_offset=2**128 - 1)
@example(opcode_number=0x37, offset=2**128 - 1, dest_offset=0)
@example(opcode_number=0x37, offset=0, dest_offset=2**128 - 1)
def test_exec_copy_fail_oog(
self, cairo_run, opcode_number, bytecode, offset, dest_offset
):
bytecode.insert(0, opcode_number) # random bytecode that can be mutated
(evm, _) = cairo_run(
"test__exec_copy",
size=2**128 - 1,
offset=offset,
dest_offset=dest_offset,
bytecode=bytecode,
opcode_number=opcode_number,
)
assert evm["reverted"] == 2
assert b"Kakarot: outOfGas left" in bytes(evm["return_data"])

@pytest.mark.parametrize("opcode_number", [0x39, 0x37])
@pytest.mark.parametrize(
"size",
Expand All @@ -178,13 +204,13 @@ def test_exec_codecopy_should_copy_code(
"size_is_0",
],
)
def test_exec_codecopy_offset_high_zellic_issue_1258(
def test_exec_copy_offset_high_zellic_issue_1258(
self, cairo_run, size, opcode_number, bytecode
):
bytecode.insert(0, opcode_number) # random bytecode that can be mutated
offset_high = 1
memory = cairo_run(
"test__exec_codecopy_offset_high_zellic_issue_1258",
"test__exec_copy_offset_high_zellic_issue_1258",
size=size,
offset_high=offset_high,
dest_offset=0,
Expand Down
Loading

0 comments on commit 25a58e8

Please sign in to comment.