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

Feat/move ether #476

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
26 changes: 24 additions & 2 deletions cairo/ethereum/cancun/state.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ from starkware.cairo.common.cairo_builtins import PoseidonBuiltin
from starkware.cairo.common.dict_access import DictAccess
from starkware.cairo.common.registers import get_fp_and_pc
from starkware.cairo.common.math import assert_not_zero

from starkware.cairo.common.uint256 import Uint256
from src.utils.uint256 import uint256_add, uint256_sub
from ethereum.cancun.fork_types import (
Address,
Account,
Expand Down Expand Up @@ -32,7 +33,7 @@ from ethereum.cancun.trie import (
)
from ethereum_types.bytes import Bytes, Bytes32
from ethereum_types.numeric import U256, U256Struct, Bool, bool, Uint
from ethereum.utils.numeric import is_zero
from ethereum.utils.numeric import is_zero, U256_le, U256_sub, U256_add

from src.utils.dict import hashdict_read, hashdict_write, hashdict_get, dict_new_empty

Expand Down Expand Up @@ -184,6 +185,27 @@ func set_account{poseidon_ptr: PoseidonBuiltin*, state: State}(
return ();
}

func move_ether{range_check_ptr, poseidon_ptr: PoseidonBuiltin*, state: State}(
sender_address: Address, recipient_address: Address, amount: U256
) {
alloc_locals;
let sender_account = get_account(sender_address);
let recipient_account = get_account(recipient_address);
let sender_balance = sender_account.value.balance;

let is_sender_balance_sufficient = U256_le(amount, sender_balance);
if (is_sender_balance_sufficient.value == 0) {
assert 0 = 1;
}

let new_sender_account_balance = U256_sub(sender_balance, amount);
let new_recipient_account_balance = U256_add(recipient_account.value.balance, amount);

set_account_balance(sender_address, new_sender_account_balance);
set_account_balance(recipient_address, new_recipient_account_balance);
return ();
}

func get_storage{poseidon_ptr: PoseidonBuiltin*, state: State}(
address: Address, key: Bytes32
) -> U256 {
Expand Down
35 changes: 35 additions & 0 deletions cairo/ethereum/utils/numeric.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ from starkware.cairo.common.uint256 import uint256_reverse_endian
from ethereum_types.numeric import Uint, U256, U256Struct, bool
from ethereum_types.bytes import Bytes32, Bytes32Struct
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
from src.utils.uint256 import uint256_add, uint256_sub
from starkware.cairo.common.uint256 import Uint256, uint256_le

func min{range_check_ptr}(a: felt, b: felt) -> felt {
alloc_locals;
Expand Down Expand Up @@ -148,3 +150,36 @@ func U256__eq__(a: U256, b: U256) -> bool {
tempvar res = bool(0);
return res;
}

// @dev Panics if overflow
func U256_add{range_check_ptr}(a: U256, b: U256) -> U256 {
alloc_locals;
let (res, carry) = uint256_add(cast([a.value], Uint256), cast([b.value], Uint256));
if (carry != 0) {
with_attr error_message("OverflowError") {
assert 0 = 1;
}
}
tempvar result = U256(new U256Struct(res.low, res.high));
return result;
}

// @dev Panics if underflow with OverflowError
func U256_sub{range_check_ptr}(a: U256, b: U256) -> U256 {
alloc_locals;
let is_within_bounds = U256_le(b, a);
if (is_within_bounds.value == 0) {
with_attr error_message("OverflowError") {
assert 0 = 1;
}
}
let (result) = uint256_sub(cast([a.value], Uint256), cast([b.value], Uint256));
tempvar res = U256(new U256Struct(result.low, result.high));
return res;
}

func U256_le{range_check_ptr}(a: U256, b: U256) -> bool {
let (result) = uint256_le(cast([a.value], Uint256), cast([b.value], Uint256));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let (result) = uint256_le(cast([a.value], Uint256), cast([b.value], Uint256));
let (result) = uint256_le([a.value], [b.value]);

You should not have to cast: a.value is a Uint256*

(to apply in all functions above)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it'd be clearer that we're using U256Struct as a Uint256 here by casting it, even though U256Struct is just a type alias for Uint256

tempvar res = bool(result);
return res;
}
22 changes: 21 additions & 1 deletion cairo/tests/ethereum/cancun/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from hypothesis import strategies as st
from hypothesis.strategies import composite

from ethereum.cancun.fork_types import Account
from ethereum.cancun.fork_types import Account, Address
from ethereum.cancun.state import (
account_exists,
account_exists_and_is_empty,
Expand All @@ -23,13 +23,15 @@
is_account_alive,
is_account_empty,
mark_account_created,
move_ether,
set_account,
set_account_balance,
set_code,
set_storage,
set_transient_storage,
)
from tests.utils.args_gen import State, TransientStorage
from tests.utils.errors import strict_raises
from tests.utils.strategies import address, bytes32, code, state, transient_storage


Expand Down Expand Up @@ -117,6 +119,24 @@ def test_set_account(self, cairo_run, data, account: Optional[Account]):
set_account(state, address, account)
assert state_cairo == state

@given(
data=state_and_address_and_optional_key(), recipient_address=address, amount=...
)
def test_move_ether(
self, cairo_run, data, recipient_address: Address, amount: U256
):
state, sender_address = data
try:
state_cairo = cairo_run(
"move_ether", state, sender_address, recipient_address, amount
)
except Exception as cairo_error:
with strict_raises(type(cairo_error)):
move_ether(state, sender_address, recipient_address, amount)
return
move_ether(state, sender_address, recipient_address, amount)
assert state_cairo == state

@given(data=state_and_address_and_optional_key())
def test_destroy_account(self, cairo_run, data):
state, address = data
Expand Down
28 changes: 28 additions & 0 deletions cairo/tests/ethereum/utils/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from ethereum.cancun.vm.gas import BLOB_GASPRICE_UPDATE_FRACTION, MIN_BLOB_GASPRICE
from ethereum.utils.numeric import ceil32, taylor_exponential
from tests.utils.errors import cairo_error
from tests.utils.strategies import felt, uint128

pytestmark = pytest.mark.python_vm
Expand Down Expand Up @@ -76,3 +77,30 @@ def test_U256_to_le_bytes(self, cairo_run, value: U256):
@given(a=..., b=...)
def test_U256__eq__(self, cairo_run, a: U256, b: U256):
assert (a == b) == cairo_run("U256__eq__", a, b)

@given(a=..., b=...)
def test_U256_le(self, cairo_run, a: U256, b: U256):
assert (a <= b) == cairo_run("U256_le", a, b)

@given(a=..., b=...)
def test_U256_add(self, cairo_run, a: U256, b: U256):
try:
a + b
except OverflowError:
with cairo_error("OverflowError"):
cairo_result = cairo_run("U256_add", a, b)
return
cairo_result = cairo_run("U256_add", a, b)
assert cairo_result == a + b

@given(a=..., b=...)
def test_U256_sub(self, cairo_run, a: U256, b: U256):
try:
a - b
except OverflowError:
with cairo_error("OverflowError"):
cairo_result = cairo_run("U256_sub", a, b)
return

cairo_result = cairo_run("U256_sub", a, b)
assert cairo_result == a - b
Loading