From d62595db7410f31fbe7ceeb38d8c56f65afca057 Mon Sep 17 00:00:00 2001 From: enitrat Date: Fri, 3 Jan 2025 11:53:49 +0100 Subject: [PATCH 1/4] feat: arithmetic opcodes --- cairo/ethereum/cancun/vm.cairo | 1 + cairo/ethereum/cancun/vm/gas.cairo | 51 ++ .../cancun/vm/instructions/arithmetic.cairo | 513 ++++++++++++++++++ .../cancun/vm/instructions/test_arithmetic.py | 153 ++++++ cairo/tests/utils/strategies.py | 48 +- 5 files changed, 765 insertions(+), 1 deletion(-) create mode 100644 cairo/ethereum/cancun/vm/instructions/arithmetic.cairo create mode 100644 cairo/tests/ethereum/cancun/vm/instructions/test_arithmetic.py diff --git a/cairo/ethereum/cancun/vm.cairo b/cairo/ethereum/cancun/vm.cairo index 36e4d036..01d075bf 100644 --- a/cairo/ethereum/cancun/vm.cairo +++ b/cairo/ethereum/cancun/vm.cairo @@ -124,6 +124,7 @@ namespace EvmImpl { code=evm.value.code, gas_left=evm.value.gas_left, env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, logs=evm.value.logs, refund_counter=evm.value.refund_counter, running=evm.value.running, diff --git a/cairo/ethereum/cancun/vm/gas.cairo b/cairo/ethereum/cancun/vm/gas.cairo index a10bb462..2ad23863 100644 --- a/cairo/ethereum/cancun/vm/gas.cairo +++ b/cairo/ethereum/cancun/vm/gas.cairo @@ -164,3 +164,54 @@ func calculate_data_fee{range_check_ptr}(excess_blob_gas: U64, tx: Transaction) let data_fee = Uint(total_blob_gas.value * blob_gas_price.value); return data_fee; } + +namespace GasConstants { + const GAS_JUMPDEST = 1; + const GAS_BASE = 2; + const GAS_VERY_LOW = 3; + const GAS_STORAGE_SET = 20000; + const GAS_STORAGE_UPDATE = 5000; + const GAS_STORAGE_CLEAR_REFUND = 4800; + const GAS_LOW = 5; + const GAS_MID = 8; + const GAS_HIGH = 10; + const GAS_EXPONENTIATION = 10; + const GAS_EXPONENTIATION_PER_BYTE = 50; + const GAS_MEMORY = 3; + const GAS_KECCAK256 = 30; + const GAS_KECCAK256_WORD = 6; + const GAS_COPY = 3; + const GAS_BLOCK_HASH = 20; + const GAS_LOG = 375; + const GAS_LOG_DATA = 8; + const GAS_LOG_TOPIC = 375; + const GAS_CREATE = 32000; + const GAS_CODE_DEPOSIT = 200; + const GAS_ZERO = 0; + const GAS_NEW_ACCOUNT = 25000; + const GAS_CALL_VALUE = 9000; + const GAS_CALL_STIPEND = 2300; + const GAS_SELF_DESTRUCT = 5000; + const GAS_SELF_DESTRUCT_NEW_ACCOUNT = 25000; + const GAS_ECRECOVER = 3000; + const GAS_SHA256 = 60; + const GAS_SHA256_WORD = 12; + const GAS_RIPEMD160 = 600; + const GAS_RIPEMD160_WORD = 120; + const GAS_IDENTITY = 15; + const GAS_IDENTITY_WORD = 3; + const GAS_RETURN_DATA_COPY = 3; + const GAS_FAST_STEP = 5; + const GAS_BLAKE2_PER_ROUND = 1; + const GAS_COLD_SLOAD = 2100; + const GAS_COLD_ACCOUNT_ACCESS = 2600; + const GAS_WARM_ACCESS = 100; + const GAS_INIT_CODE_WORD_COST = 2; + const GAS_BLOBHASH_OPCODE = 3; + const GAS_POINT_EVALUATION = 50000; + + const TARGET_BLOB_GAS_PER_BLOCK = 393216; + const GAS_PER_BLOB = 2 ** 17; + const MIN_BLOB_GASPRICE = 1; + const BLOB_GASPRICE_UPDATE_FRACTION = 3338477; +} diff --git a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo new file mode 100644 index 00000000..d2f4f755 --- /dev/null +++ b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo @@ -0,0 +1,513 @@ +from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin, KeccakBuiltin +from starkware.cairo.common.bool import FALSE +from starkware.cairo.common.uint256 import ( + uint256_add, + uint256_sub, + uint256_mul, + uint256_unsigned_div_rem, + uint256_signed_div_rem, +) + +from src.utils.uint256 import uint256_eq, uint256_fast_exp, uint256_signextend +from src.utils.utils import Helpers +from ethereum.cancun.vm.stack import Stack, pop, push +from ethereum.cancun.vm import Evm, EvmImpl +from ethereum.cancun.vm.exceptions import ExceptionalHalt +from ethereum_types.numeric import U256, U256Struct, Uint +from ethereum.cancun.vm.gas import charge_gas, GasConstants + +// @title Arithmetic operations for the EVM +// @notice Implements arithmetic operations like add, sub, mul, div, etc. + +// @notice Adds the top two elements of the stack together +func add{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_VERY_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + let (result, _) = uint256_add([x.value], [y.value]); + with stack { + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + } + + // PROGRAM COUNTER + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + EvmImpl.set_stack(stack); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Subtracts the top two elements of the stack +func sub{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_VERY_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + let (result) = uint256_sub([x.value], [y.value]); + with stack { + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Multiplies the top two elements of the stack +func mul{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + let (result, _) = uint256_mul([x.value], [y.value]); + with stack { + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Integer division of the top two elements of the stack +func div{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + with stack { + let (is_zero) = uint256_eq([y.value], U256Struct(0, 0)); + if (is_zero != 0) { + let err4 = push(U256(new U256Struct(0, 0))); + if (cast(err4, felt) != 0) { + return err4; + } + tempvar range_check_ptr = range_check_ptr; + } else { + let (result, _) = uint256_unsigned_div_rem([x.value], [y.value]); + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + tempvar range_check_ptr = range_check_ptr; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Signed integer division of the top two elements of the stack +func sdiv{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + let (result, _) = uint256_signed_div_rem([x.value], [y.value]); + with stack { + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Modulo remainder of the top two elements of the stack +func mod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + with stack { + let (is_zero) = uint256_eq([y.value], U256Struct(0, 0)); + if (is_zero != 0) { + let err4 = push(U256(new U256Struct(0, 0))); + if (cast(err4, felt) != 0) { + return err4; + } + tempvar range_check_ptr = range_check_ptr; + } else { + let (_, remainder) = uint256_unsigned_div_rem([x.value], [y.value]); + let err4 = push(U256(new U256Struct(remainder.low, remainder.high))); + if (cast(err4, felt) != 0) { + return err4; + } + tempvar range_check_ptr = range_check_ptr; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Signed modulo remainder of the top two elements of the stack +func smod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + with stack { + let (is_zero) = uint256_eq([y.value], U256Struct(0, 0)); + if (is_zero != 0) { + let err4 = push(U256(new U256Struct(0, 0))); + if (cast(err4, felt) != 0) { + return err4; + } + tempvar range_check_ptr = range_check_ptr; + } else { + let (_, remainder) = uint256_signed_div_rem([x.value], [y.value]); + let err4 = push(U256(new U256Struct(remainder.low, remainder.high))); + if (cast(err4, felt) != 0) { + return err4; + } + tempvar range_check_ptr = range_check_ptr; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Addition modulo of three elements on the stack +func addmod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + let (n, err3) = pop(); + if (cast(err3, felt) != 0) { + return err3; + } + } + + // GAS + let err4 = charge_gas(Uint(GasConstants.GAS_MID)); + if (cast(err4, felt) != 0) { + return err4; + } + + // OPERATION + with stack { + let (is_zero) = uint256_eq([n.value], U256Struct(0, 0)); + if (is_zero != 0) { + let err5 = push(U256(new U256Struct(0, 0))); + if (cast(err5, felt) != 0) { + return err5; + } + tempvar range_check_ptr = range_check_ptr; + } else { + let (sum, _) = uint256_add([x.value], [y.value]); + let (_, remainder) = uint256_unsigned_div_rem(sum, [n.value]); + let err5 = push(U256(new U256Struct(remainder.low, remainder.high))); + if (cast(err5, felt) != 0) { + return err5; + } + tempvar range_check_ptr = range_check_ptr; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Multiplication modulo of three elements on the stack +func mulmod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (x, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (y, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + let (n, err3) = pop(); + if (cast(err3, felt) != 0) { + return err3; + } + } + + // GAS + let err4 = charge_gas(Uint(GasConstants.GAS_MID)); + if (cast(err4, felt) != 0) { + return err4; + } + + // OPERATION + with stack { + let (is_zero) = uint256_eq([n.value], U256Struct(0, 0)); + if (is_zero != 0) { + let err5 = push(U256(new U256Struct(0, 0))); + if (cast(err5, felt) != 0) { + return err5; + } + tempvar range_check_ptr = range_check_ptr; + } else { + let (product, _) = uint256_mul([x.value], [y.value]); + let (_, remainder) = uint256_unsigned_div_rem(product, [n.value]); + let err5 = push(U256(new U256Struct(remainder.low, remainder.high))); + if (cast(err5, felt) != 0) { + return err5; + } + tempvar range_check_ptr = range_check_ptr; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Exponential operation of the top 2 elements +func exp{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (base, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (exponent, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + // Calculate bytes used for gas cost + local bytes_used: felt; + if (exponent.value.high == 0) { + let bytes_used_low = Helpers.bytes_used_128(exponent.value.low); + assert bytes_used = bytes_used_low; + tempvar range_check_ptr = range_check_ptr; + } else { + let bytes_used_high = Helpers.bytes_used_128(exponent.value.high); + assert bytes_used = bytes_used_high + 16; + tempvar range_check_ptr = range_check_ptr; + } + + let gas_cost = Uint( + GasConstants.GAS_EXPONENTIATION + GasConstants.GAS_EXPONENTIATION_PER_BYTE * bytes_used + ); + let err3 = charge_gas(gas_cost); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + let result = uint256_fast_exp([base.value], [exponent.value]); + with stack { + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} + +// @notice Sign extend operation +func signextend{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { + alloc_locals; + // STACK + let stack = evm.value.stack; + with stack { + let (byte_num, err1) = pop(); + if (cast(err1, felt) != 0) { + return err1; + } + let (value, err2) = pop(); + if (cast(err2, felt) != 0) { + return err2; + } + } + + // GAS + let err3 = charge_gas(Uint(GasConstants.GAS_LOW)); + if (cast(err3, felt) != 0) { + return err3; + } + + // OPERATION + let result = uint256_signextend([value.value], [byte_num.value]); + with stack { + let err4 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err4, felt) != 0) { + return err4; + } + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; +} diff --git a/cairo/tests/ethereum/cancun/vm/instructions/test_arithmetic.py b/cairo/tests/ethereum/cancun/vm/instructions/test_arithmetic.py new file mode 100644 index 00000000..a62ffe87 --- /dev/null +++ b/cairo/tests/ethereum/cancun/vm/instructions/test_arithmetic.py @@ -0,0 +1,153 @@ +import pytest +from hypothesis import given + +from ethereum.cancun.vm.instructions.arithmetic import ( + add, + addmod, + div, + exp, + mod, + mul, + mulmod, + sdiv, + signextend, + smod, + sub, +) +from tests.utils.args_gen import Evm +from tests.utils.strategies import evm_lite + + +class TestArithmetic: + @given(evm=evm_lite) + def test_add(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("add", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + add(evm) + return + + add(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_sub(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("sub", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + sub(evm) + return + + sub(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_mul(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("mul", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + mul(evm) + return + + mul(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_div(self, cairo_run, evm: Evm): + """Test the DIV operation""" + try: + cairo_result = cairo_run("div", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + div(evm) + return + + div(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_sdiv(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("sdiv", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + sdiv(evm) + return + + sdiv(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_mod(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("mod", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + mod(evm) + return + + mod(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_smod(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("smod", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + smod(evm) + return + + smod(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_addmod(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("addmod", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + addmod(evm) + return + + addmod(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_mulmod(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("mulmod", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + mulmod(evm) + return + + mulmod(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_exp(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("exp", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + exp(evm) + return + + exp(evm) + assert evm == cairo_result + + @given(evm=evm_lite) + def test_signextend(self, cairo_run, evm: Evm): + try: + cairo_result = cairo_run("signextend", evm) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + signextend(evm) + return + + signextend(evm) + assert evm == cairo_result diff --git a/cairo/tests/utils/strategies.py b/cairo/tests/utils/strategies.py index 3d2430b2..fb4796da 100644 --- a/cairo/tests/utils/strategies.py +++ b/cairo/tests/utils/strategies.py @@ -97,7 +97,6 @@ def stack_strategy(thing): # 2**32 bytes would be the value at which the memory expansion would trigger an OOG memory = st.binary(min_size=0, max_size=2**13).map(Memory) - evm = st.fixed_dictionaries( { "pc": st.from_type(Uint), @@ -143,6 +142,53 @@ def stack_strategy(thing): } ).map(lambda x: Message(**x)) +# Versions strategies with less data in collections + +memory_lite = st.binary(min_size=0, max_size=128).map(Memory) + +message_lite = st.fixed_dictionaries( + { + "caller": address, + "target": st.one_of(bytes0, address), + "current_target": address, + "gas": uint, + "value": uint256, + "data": st.just(b""), + "code_address": st.none() | address, + "code": st.just(b""), + "depth": uint, + "should_transfer_value": st.booleans(), + "is_static": st.booleans(), + "accessed_addresses": st.just(set()), + "accessed_storage_keys": st.just(set()), + "parent_evm": st.none(), + } +).map(lambda x: Message(**x)) + + +evm_lite = st.fixed_dictionaries( + { + "pc": uint, + "stack": stack_strategy(Stack[U256]), + "memory": memory_lite, + "code": st.just(b""), + "gas_left": uint, + "env": st.from_type(Environment), + "valid_jump_destinations": st.just(set()), + "logs": st.just(()), + "refund_counter": st.just(0), + "running": st.booleans(), + "message": message_lite, + "output": st.just(b""), + "accounts_to_delete": st.just(set()), + "touched_accounts": st.just(set()), + "return_data": st.just(b""), + "error": st.none() | st.from_type(EthereumException), + "accessed_addresses": st.just(set()), + "accessed_storage_keys": st.just(set()), + } +).map(lambda x: Evm(**x)) + # Fork state = st.lists(bytes20).flatmap( From 763eda520a325f78b7fbab146fac210bace6f4d1 Mon Sep 17 00:00:00 2001 From: enitrat Date: Fri, 3 Jan 2025 13:44:22 +0100 Subject: [PATCH 2/4] fix addmod --- .../cancun/vm/instructions/arithmetic.cairo | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo index d2f4f755..56b318a1 100644 --- a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo +++ b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo @@ -1,14 +1,16 @@ from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin, KeccakBuiltin from starkware.cairo.common.bool import FALSE -from starkware.cairo.common.uint256 import ( - uint256_add, +from starkware.cairo.common.uint256 import uint256_mul, ALL_ONES, uint256_lt + +from src.utils.uint256 import ( + uint256_eq, + uint256_fast_exp, + uint256_signextend, uint256_sub, - uint256_mul, + uint256_add, uint256_unsigned_div_rem, uint256_signed_div_rem, ) - -from src.utils.uint256 import uint256_eq, uint256_fast_exp, uint256_signextend from src.utils.utils import Helpers from ethereum.cancun.vm.stack import Stack, pop, push from ethereum.cancun.vm import Evm, EvmImpl @@ -321,11 +323,11 @@ func addmod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { // STACK let stack = evm.value.stack; with stack { - let (x, err1) = pop(); + let (a, err1) = pop(); if (cast(err1, felt) != 0) { return err1; } - let (y, err2) = pop(); + let (b, err2) = pop(); if (cast(err2, felt) != 0) { return err2; } @@ -342,26 +344,69 @@ func addmod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { } // OPERATION - with stack { - let (is_zero) = uint256_eq([n.value], U256Struct(0, 0)); - if (is_zero != 0) { + let (is_zero) = uint256_eq([n.value], U256Struct(0, 0)); + if (is_zero != 0) { + with stack { let err5 = push(U256(new U256Struct(0, 0))); if (cast(err5, felt) != 0) { return err5; } - tempvar range_check_ptr = range_check_ptr; - } else { - let (sum, _) = uint256_add([x.value], [y.value]); - let (_, remainder) = uint256_unsigned_div_rem(sum, [n.value]); - let err5 = push(U256(new U256Struct(remainder.low, remainder.high))); - if (cast(err5, felt) != 0) { - return err5; + } + // early return if n is zero + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; + } + + // (a + b) mod n = (a mod n + b mod n) mod n + let (_, x) = uint256_unsigned_div_rem([a.value], [n.value]); + let (_, y) = uint256_unsigned_div_rem([b.value], [n.value]); + // x, y in range [0, n-1] thus: + // if x + y < n then x + y mod n = x + y + // if x + y >= n then x + y mod n = x + y - n + let (sum, carry) = uint256_add(x, y); + if (carry != 0) { + // result = (2**256) - (n - overflown_sum) + // <=> result = (2**256 - 1) - (n - overflown_sum - 1) + // as n > overflown_sum we can't have an underflow + tempvar max_u256 = U256Struct(ALL_ONES, ALL_ONES); + let (overflown_part) = uint256_sub([n.value], sum); + let (to_remove) = uint256_sub(overflown_part, U256Struct(1, 0)); + let (result) = uint256_sub(max_u256, to_remove); + with stack { + let err6 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err6, felt) != 0) { + return err6; } - tempvar range_check_ptr = range_check_ptr; } + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; } - // PROGRAM COUNTER + let (is_sum_lt_n) = uint256_lt(sum, [n.value]); + if (is_sum_lt_n != 0) { + with stack { + let err7 = push(U256(new U256Struct(sum.low, sum.high))); + if (cast(err7, felt) != 0) { + return err7; + } + } + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; + } + + let (result) = uint256_sub(sum, [n.value]); + with stack { + let err8 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err8, felt) != 0) { + return err8; + } + } EvmImpl.set_stack(stack); EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); let ok = cast(0, ExceptionalHalt*); From 7cf52d1904ffe310ede63192f6b2523e2bd56246 Mon Sep 17 00:00:00 2001 From: enitrat Date: Fri, 3 Jan 2025 13:51:46 +0100 Subject: [PATCH 3/4] fix mulmod --- .../cancun/vm/instructions/arithmetic.cairo | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo index 56b318a1..ce3d773e 100644 --- a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo +++ b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo @@ -10,6 +10,7 @@ from src.utils.uint256 import ( uint256_add, uint256_unsigned_div_rem, uint256_signed_div_rem, + uint256_mul_div_mod, ) from src.utils.utils import Helpers from ethereum.cancun.vm.stack import Stack, pop, push @@ -419,11 +420,11 @@ func mulmod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { // STACK let stack = evm.value.stack; with stack { - let (x, err1) = pop(); + let (a, err1) = pop(); if (cast(err1, felt) != 0) { return err1; } - let (y, err2) = pop(); + let (b, err2) = pop(); if (cast(err2, felt) != 0) { return err2; } @@ -440,22 +441,27 @@ func mulmod{range_check_ptr, evm: Evm}() -> ExceptionalHalt* { } // OPERATION - with stack { - let (is_zero) = uint256_eq([n.value], U256Struct(0, 0)); - if (is_zero != 0) { + let (is_zero) = uint256_eq([n.value], U256Struct(0, 0)); + if (is_zero != 0) { + with stack { let err5 = push(U256(new U256Struct(0, 0))); if (cast(err5, felt) != 0) { return err5; } - tempvar range_check_ptr = range_check_ptr; - } else { - let (product, _) = uint256_mul([x.value], [y.value]); - let (_, remainder) = uint256_unsigned_div_rem(product, [n.value]); - let err5 = push(U256(new U256Struct(remainder.low, remainder.high))); - if (cast(err5, felt) != 0) { - return err5; - } - tempvar range_check_ptr = range_check_ptr; + } + + // PROGRAM COUNTER + EvmImpl.set_stack(stack); + EvmImpl.set_pc(Uint(evm.value.pc.value + 1)); + let ok = cast(0, ExceptionalHalt*); + return ok; + } + + let (_, _, result) = uint256_mul_div_mod([a.value], [b.value], [n.value]); + with stack { + let err6 = push(U256(new U256Struct(result.low, result.high))); + if (cast(err6, felt) != 0) { + return err6; } } From a4943e341e97b68e195c96a291b3c5fa1aa89f9d Mon Sep 17 00:00:00 2001 From: enitrat Date: Fri, 3 Jan 2025 13:53:19 +0100 Subject: [PATCH 4/4] remove unused import --- cairo/ethereum/cancun/vm/instructions/arithmetic.cairo | 2 -- 1 file changed, 2 deletions(-) diff --git a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo index ce3d773e..7665d300 100644 --- a/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo +++ b/cairo/ethereum/cancun/vm/instructions/arithmetic.cairo @@ -1,5 +1,3 @@ -from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin, KeccakBuiltin -from starkware.cairo.common.bool import FALSE from starkware.cairo.common.uint256 import uint256_mul, ALL_ONES, uint256_lt from src.utils.uint256 import (