Skip to content

Commit

Permalink
convert a few eip1153 json tests
Browse files Browse the repository at this point in the history
  • Loading branch information
winsvega committed Feb 26, 2024
1 parent bf35d79 commit c41f3bd
Show file tree
Hide file tree
Showing 4 changed files with 502 additions and 0 deletions.
141 changes: 141 additions & 0 deletions tests/cancun/eip1153_tstore/test_basic_tload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
Ethereum Transient Storage EIP Tests
https://eips.ethereum.org/EIPS/eip-1153
"""

from typing import Dict, Union

import pytest

from ethereum_test_tools import (
Account,
Address,
Environment,
StateTestFiller,
TestAddress,
Transaction,
)
from ethereum_test_tools.vm.opcode import Opcodes as Op

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-1153.md"
REFERENCE_SPEC_VERSION = "2f8299df31bb8173618901a03a8366a3183479b0"


@pytest.mark.valid_from("Cancun")
def test_basic_tload(
state_test: StateTestFiller,
):
"""
Covered .json vectors:
(01_tloadBeginningTxnFiller.yml)
load arbitrary value is 0 at beginning of transaction
(02_tloadAfterTstoreFiller.yml)
tload from same slot after tstore returns correct value
(03_tloadAfterStoreIs0Filler.yml)
Loading any other slot after storing to a slot returns 0.
(16_tloadGasFiller.yml)
tload costs 100 gas same as a warm sload
(18_tloadAfterStoreFiller.yml)
tload from same slot after store returns 0
"""

address_to = Address("A00000000000000000000000000000000000000A")
tload_at_transaction_begin_result = 1

tstore_value = 88
tload_after_tstore_result = 2
tload_after_tstore_result_second_time = 3
tload_wrong_after_tstore_result = 4

# N OPNAME GAS_COST TOTAL_GAS REMAINING_GAS STACK
# 28-1 MSTORE 2 20748 4958252 2:[4ba82f,0,]
# MSTORE [0] = 4958255
# 29-1 PUSH1 3 20754 4958246
# 30-1 TLOAD 100 20757 4958243 1:[10,]
# 31-1 GAS 2 20857 4958143 1:[2,]
# 32-1 PUSH1 3 20859 4958141 2:[2,4ba7bd,]
# 33-1 MSTORE 6 20862 4958138 3:[2,4ba7bd,20,]
# MSTORE [32] = 4958141
extra_opcode_gas = 11 # mstore(3), push1(3),gas(2),push1(3)

tload_nonzero_gas_price_result = 16
tload_zero_gas_price_result = 1601

tload_from_sstore_result = 18

pre = {
address_to: Account(
balance=1000000000000000000,
nonce=0,
code=Op.JUMPDEST()
# 01_tloadBeginningTxnFiller.yml
+ Op.SSTORE(tload_at_transaction_begin_result, Op.TLOAD(0))
# 02_tloadAfterTstoreFiller.yml
+ Op.TSTORE(2, tstore_value)
+ Op.SSTORE(tload_after_tstore_result, Op.TLOAD(2))
+ Op.SSTORE(tload_after_tstore_result_second_time, Op.TLOAD(2))
# 03_tloadAfterStoreIs0Filler.yml
+ Op.TSTORE(3, tstore_value) + Op.SSTORE(tload_wrong_after_tstore_result, Op.TLOAD(0))
# 16_tloadGasFiller.yml calculate tload gas
+ Op.TSTORE(16, 2)
+ Op.MSTORE(0, Op.GAS()) # hot load the memory
+ Op.MSTORE(0, Op.GAS())
+ Op.TLOAD(16)
+ Op.MSTORE(32, Op.GAS())
+ Op.SSTORE(tload_nonzero_gas_price_result, Op.SUB(Op.MLOAD(0), Op.MLOAD(32)))
+ Op.SSTORE(tload_nonzero_gas_price_result, Op.SUB(Op.SLOAD(16), extra_opcode_gas))
# from zero slot
+ Op.MSTORE(0, Op.GAS())
+ Op.TLOAD(5)
+ Op.MSTORE(32, Op.GAS())
+ Op.SSTORE(tload_zero_gas_price_result, Op.SUB(Op.MLOAD(0), Op.MLOAD(32)))
+ Op.SSTORE(tload_zero_gas_price_result, Op.SUB(Op.SLOAD(1601), extra_opcode_gas))
# 18_tloadAfterStoreFiller.yml
+ Op.SSTORE(18, 22) + Op.SSTORE(tload_from_sstore_result, Op.TLOAD(18)),
storage={
tload_at_transaction_begin_result: 0xFF,
tload_after_tstore_result: 0xFF,
tload_after_tstore_result_second_time: 0xFF,
tload_wrong_after_tstore_result: 0xFF,
tload_nonzero_gas_price_result: 0xFF,
tload_zero_gas_price_result: 0xFF,
tload_from_sstore_result: 0xFF,
},
),
TestAddress: Account(
balance=7000000000000000000,
nonce=0,
code="0x",
storage={},
),
}

post: Dict[Address, Union[Account, object]] = {}

post[address_to] = Account(
storage={
tload_at_transaction_begin_result: 0x00,
tload_after_tstore_result: tstore_value,
tload_after_tstore_result_second_time: tstore_value,
tload_wrong_after_tstore_result: 0x00,
tload_nonzero_gas_price_result: 100,
tload_zero_gas_price_result: 100,
tload_from_sstore_result: 0x00,
}
)

tx = Transaction(
nonce=0,
to=address_to,
gas_price=10,
data=b"",
gas_limit=5000000,
value=0,
)

state_test(env=Environment(), pre=pre, post=post, tx=tx)
108 changes: 108 additions & 0 deletions tests/cancun/eip1153_tstore/test_tload_calls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Ethereum Transient Storage EIP Tests
https://eips.ethereum.org/EIPS/eip-1153
"""

from typing import Dict, Union

import pytest

from ethereum_test_tools import (
Account,
Address,
Environment,
StateTestFiller,
TestAddress,
Transaction,
)
from ethereum_test_tools.vm.opcode import Opcodes as Op

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-1153.md"
REFERENCE_SPEC_VERSION = "2f8299df31bb8173618901a03a8366a3183479b0"


@pytest.mark.valid_from("Cancun")
@pytest.mark.parametrize("call_type", [Op.CALL, Op.CALLCODE, Op.DELEGATECALL])
def test_tload_calls(state_test: StateTestFiller, call_type: Op):
"""
Covered .json vectors:
(04_tloadAfterCallFiller.yml)
Loading a slot after a call to another contract is 0.
(12_tloadDelegateCallFiller.yml)
delegatecall reads transient storage in the context of the current address
"""

address_to = Address("A00000000000000000000000000000000000000A")
address_call = Address("B00000000000000000000000000000000000000B")

# Storages
str_a_tload_after_subcall_result = 0
str_a_subcall_result = 1
str_b_subcall_tload_result = 2
str_b_subcall_updated_tload_result = 3

pre = {
address_to: Account(
balance=1000000000000000000,
nonce=0,
code=Op.JUMPDEST()
+ Op.TSTORE(0, 10)
+ Op.SSTORE(str_a_subcall_result, call_type(Op.GAS(), address_call, 0, 0, 32, 0, 0))
+ Op.SSTORE(str_a_tload_after_subcall_result, Op.TLOAD(0)),
storage={
str_a_subcall_result: 0xFF,
str_a_tload_after_subcall_result: 0xFF,
},
),
address_call: Account(
balance=7000000000000000000,
nonce=0,
code=Op.JUMPDEST()
+ Op.SSTORE(str_b_subcall_tload_result, Op.TLOAD(0))
+ Op.TSTORE(0, 20)
+ Op.SSTORE(str_b_subcall_updated_tload_result, Op.TLOAD(0)),
storage={
str_b_subcall_tload_result: 0xFF,
str_b_subcall_updated_tload_result: 0xFF,
},
),
TestAddress: Account(
balance=7000000000000000000,
nonce=0,
code="0x",
storage={},
),
}

post: Dict[Address, Union[Account, object]] = {}

post[address_to] = Account(
storage={
# other calls don't change context, there for tload updated in this account
str_a_tload_after_subcall_result: 10 if call_type == Op.CALL else 20,
str_a_subcall_result: 1,
# since context unchanged the subcall works as if continued execution
str_b_subcall_tload_result: 0 if call_type == Op.CALL else 10,
str_b_subcall_updated_tload_result: 0 if call_type == Op.CALL else 20,
}
)

post[address_call] = Account(
storage={
str_b_subcall_tload_result: 0 if call_type == Op.CALL else 0xFF,
str_b_subcall_updated_tload_result: 20 if call_type == Op.CALL else 0xFF,
}
)

tx = Transaction(
nonce=0,
to=address_to,
gas_price=10,
data=b"",
gas_limit=5000000,
value=0,
)

state_test(env=Environment(), pre=pre, post=post, tx=tx)
112 changes: 112 additions & 0 deletions tests/cancun/eip1153_tstore/test_tload_reentrancy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""
Ethereum Transient Storage EIP Tests
https://eips.ethereum.org/EIPS/eip-1153
"""

from typing import Dict, Union

import pytest

from ethereum_test_tools import (
Account,
Address,
Case,
Environment,
Hash,
StateTestFiller,
Switch,
TestAddress,
Transaction,
)
from ethereum_test_tools.vm.opcode import Opcodes as Op

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-1153.md"
REFERENCE_SPEC_VERSION = "2f8299df31bb8173618901a03a8366a3183479b0"


@pytest.mark.valid_from("Cancun")
@pytest.mark.parametrize("call_type", [Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL])
@pytest.mark.parametrize("call_return", [Op.RETURN, Op.REVERT, Op.OOG])
def test_tload_reentrancy(state_test: StateTestFiller, call_type: Op, call_return: Op):
"""
Covered .json vectors:
(05_tloadReentrancyFiller.yml)
Reentrant calls access the same transient storage
"""

address_to = Address("A00000000000000000000000000000000000000A")

# Storages
str_tload_in_reentrancy_result = 1
str_tload_after_reentrancy_result = 2
str_reentrancy_call_worked = 3

# Function names
do_load = 1
do_reenter = 2

def make_call(call_type: Op) -> bytes:
if call_type == Op.DELEGATECALL or call_type == Op.STATICCALL:
return call_type(Op.GAS(), Op.ADDRESS(), 0, 32, 32, 32)
else:
return call_type(Op.GAS(), Op.ADDRESS(), 0, 0, 32, 32, 32)

pre = {
address_to: Account(
balance=1000000000000000000,
nonce=0,
code=Switch(
cases=[
Case(
condition=Op.EQ(Op.CALLDATALOAD(0), do_load),
action=Op.MSTORE(0, Op.TLOAD(0)) + call_return(0, 32),
),
Case(
condition=Op.EQ(Op.CALLDATALOAD(0), do_reenter),
action=Op.TSTORE(0, 44)
+ Op.MSTORE(0, do_load) + Op.MSTORE(32, 0xFF)
+ Op.SSTORE(str_reentrancy_call_worked, make_call(call_type))
+ Op.SSTORE(str_tload_in_reentrancy_result, Op.MLOAD(32))
+ Op.SSTORE(str_tload_after_reentrancy_result, Op.TLOAD(0)),
),
],
default_action=b"",
),
storage={
str_tload_in_reentrancy_result: 0xFF,
str_tload_after_reentrancy_result: 0xFF,
str_reentrancy_call_worked: 0xFF,
},
),
TestAddress: Account(
balance=7000000000000000000,
nonce=0,
code="0x",
storage={},
),
}

post: Dict[Address, Union[Account, object]] = {}

post[address_to] = Account(
storage={
# if call OOG, we fail to obtain the result
str_tload_in_reentrancy_result: 0xFF if call_return == Op.OOG else 44,
str_tload_after_reentrancy_result: 44,
str_reentrancy_call_worked: (
0 if call_return == Op.REVERT or call_return == Op.OOG else 1
),
}
)

tx = Transaction(
nonce=0,
to=address_to,
gas_price=10,
data=Hash(do_reenter),
gas_limit=5000000,
value=0,
)

state_test(env=Environment(), pre=pre, post=post, tx=tx)
Loading

0 comments on commit c41f3bd

Please sign in to comment.