Skip to content

Commit

Permalink
Add fixtures for debugging EF tests, and sha3_d0g0v0_Shanghai test (#702
Browse files Browse the repository at this point in the history
)

Time spent on this PR: 0.3

## Pull request type

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] 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?

No fixture available for deploying raw bytecode (not from compilation
artifacts) or setting a bytecode (without running it as in deploy).

## What is the new behavior?

Added fixtures:

- `deploy_eoa(private_key)` to deploy an EOA
- `deploy_bytecode(bytecode)` to deploy a given raw bytecode
- `create_account_with_bytecode(bytecode)` to create a CA with the given
bytecode

## Other information

Also added a first EF test (sha3_d0g0v0_Shanghai), currently failing due
to the `return_data` shorted than the `retSize`.
  • Loading branch information
ClementWalter authored Aug 28, 2023
1 parent 47cdfdf commit 0c6e7a6
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 63 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ markers = [
"UniswapV2Factory",
"Utils",
"Safe",
"EF_TEST",
]

[tool.isort]
Expand Down
6 changes: 0 additions & 6 deletions src/kakarot/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,6 @@ namespace Kakarot {
assert success = TRUE;
}

// TODO: add check that target contract does not exist or has empty bytecode
if (data_len == 0) {
let (return_data) = alloc();
return (0, return_data);
}

if (to == 0) {
with_attr error_message("Kakarot: value should be 0 when deploying a contract") {
assert value = 0;
Expand Down
61 changes: 35 additions & 26 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,45 @@ async def kakarot(
return kakarot


@pytest_asyncio.fixture(scope="session")
async def addresses(
@pytest.fixture(scope="session")
def deploy_eoa(
starknet,
kakarot,
externally_owned_account_class,
fund_evm_address,
deployer_address,
):
async def _factory(private_key=None):
if private_key is None:
private_key = generate_random_private_key()

evm_address = private_key.public_key.to_checksum_address()

# pre fund account so that fees can be paid back to deployer
await fund_evm_address(int(evm_address, 16))

eoa_deploy_tx = await kakarot.deploy_externally_owned_account(
int(evm_address, 16)
).execute(caller_address=deployer_address)

return Wallet(
address=evm_address,
private_key=private_key,
starknet_contract=StarknetContract(
starknet.state,
externally_owned_account_class.abi,
eoa_deploy_tx.call_info.internal_calls[0].contract_address,
eoa_deploy_tx,
),
starknet_address=eoa_deploy_tx.call_info.internal_calls[0].contract_address,
)

return _factory


@pytest_asyncio.fixture(scope="session")
async def addresses(
deploy_eoa,
) -> List[Wallet]:
"""
Returns a list of addresses to be used in tests.
Expand All @@ -73,30 +105,7 @@ async def addresses(

wallets = []
for private_key in private_keys:
evm_address = private_key.public_key.to_checksum_address()

# pre fund account so that fees can be paid back to deployer
await fund_evm_address(int(evm_address, 16))

eoa_deploy_tx = await kakarot.deploy_externally_owned_account(
int(evm_address, 16)
).execute(caller_address=deployer_address)

wallets.append(
Wallet(
address=evm_address,
private_key=private_key,
starknet_contract=StarknetContract(
starknet.state,
externally_owned_account_class.abi,
eoa_deploy_tx.call_info.internal_calls[0].contract_address,
eoa_deploy_tx,
),
starknet_address=eoa_deploy_tx.call_info.internal_calls[
0
].contract_address,
)
)
wallets.append(await deploy_eoa(private_key))
return wallets


Expand Down
42 changes: 42 additions & 0 deletions tests/integration/solidity_contracts/EFTests/test_sha3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import logging

import pytest
from starkware.starknet.testing.contract import StarknetContract

from tests.utils.helpers import hex_string_to_bytes_array

logger = logging.getLogger()


@pytest.mark.asyncio
@pytest.mark.EF_TEST
class TestSha3:
@pytest.mark.skip(
"TODO: need to fix when return_data is shorter than retSize in CallHelper.finalize_calling_context"
)
async def test_sha3_d0g0v0_Shanghai(
self,
owner,
create_account_with_bytecode,
kakarot: StarknetContract,
):
called_contract = await create_account_with_bytecode("0x600060002060005500")
caller_contract = await create_account_with_bytecode(
"0x604060206010600f6000600435610100016001600003f100"
)

res = await kakarot.eth_send_transaction(
origin=int(owner.address, 16),
to=int(caller_contract.evm_contract_address, 16),
gas_limit=1_000_000,
gas_price=0,
value=0,
data=hex_string_to_bytes_array(
# In the original EF test, the called contract is supposed to be set in genesis
# at address 0x000000000000000000000000000000000000010{i} while the payload of
# the tx uses {i}, hence we sub 0x100 to the real deployed called_address
f"0x693c6139{int(called_contract.evm_contract_address, 16) - 0x100:064x}"
),
).execute(caller_address=owner.starknet_address)
sha3 = called_contract.storage(0).call()
assert res == sha3
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ async def test_should_create_counters(
evm_addresses = await plain_opcodes.create(
bytecode=counter.constructor().data_in_transaction,
count=count,
caller_address=plain_opcodes_deployer.starknet_address,
caller_address=plain_opcodes_deployer,
)
assert len(evm_addresses) == count
for evm_address in evm_addresses:
Expand Down
122 changes: 92 additions & 30 deletions tests/integration/solidity_contracts/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
calculate_contract_address_from_hash,
)
from starkware.starknet.testing.contract import StarknetContract
from starkware.starknet.testing.contract_utils import gather_deprecated_compiled_class
from web3 import Web3

from tests.utils.contracts import get_contract, use_kakarot_backend
from tests.utils.helpers import hex_string_to_bytes_array
from tests.utils.reporting import traceit
from tests.utils.helpers import generate_random_private_key, hex_string_to_bytes_array

logger = logging.getLogger()

Expand Down Expand Up @@ -64,7 +64,48 @@ def _factory(


@pytest.fixture(scope="package")
def deploy_solidity_contract(kakarot, get_solidity_contract):
def deploy_bytecode(kakarot, deploy_eoa):
"""
Fixture to deploy a bytecode in kakarot. Returns the EVM address of the deployed contract,
the corresponding starknet address and the starknet transaction.
"""

async def _factory(bytecode: str, caller_eoa=None):
"""
This factory is what is actually returned by pytest when requesting the `deploy_bytecode`
fixture.
"""
if caller_eoa is None:
caller_eoa = await deploy_eoa(
generate_random_private_key(int(bytecode, 16))
)

tx = await kakarot.eth_send_transaction(
origin=int(caller_eoa.address, 16),
to=0,
gas_limit=1_000_000,
gas_price=0,
value=0,
data=hex_string_to_bytes_array(bytecode),
).execute(caller_address=caller_eoa.starknet_address)

deploy_event = [
e
for e in tx.main_call_events
if type(e).__name__ == "evm_contract_deployed"
][0]

starknet_contract_address = deploy_event.starknet_contract_address
evm_contract_address = Web3.to_checksum_address(
f"{deploy_event.evm_contract_address:040x}"
)
return evm_contract_address, starknet_contract_address, tx

return _factory


@pytest.fixture(scope="package")
def deploy_solidity_contract(deploy_bytecode, get_solidity_contract):
"""
Fixture to deploy a solidity contract in kakarot. The returned contract is a modified
web3.contract instance with an added `contract_account` attribute that return the actual
Expand All @@ -83,33 +124,10 @@ async def _factory(contract_app, contract_name, *args, **kwargs):
is required and filtered out before calling the constructor.
"""
contract = get_contract(contract_app, contract_name)
if "caller_eoa" not in kwargs:
raise ValueError(
"caller_eoa needs to be given in kwargs for deploying the contract"
)
caller_eoa = kwargs["caller_eoa"]
del kwargs["caller_eoa"]
deploy_bytecode = hex_string_to_bytes_array(
contract.constructor(*args, **kwargs).data_in_transaction
)
with traceit.context(contract_name):
tx = await kakarot.eth_send_transaction(
origin=int(caller_eoa.address, 16),
to=0,
gas_limit=1_000_000,
gas_price=0,
value=0,
data=deploy_bytecode,
).execute(caller_address=caller_eoa.starknet_address)

deploy_event = [
e
for e in tx.main_call_events
if type(e).__name__ == "evm_contract_deployed"
][0]
starknet_contract_address = deploy_event.starknet_contract_address
evm_contract_address = Web3.to_checksum_address(
f"{deploy_event.evm_contract_address:040x}"
caller_eoa = kwargs.pop("caller_eoa", None)
evm_contract_address, starknet_contract_address, tx = await deploy_bytecode(
contract.constructor(*args, **kwargs).data_in_transaction,
caller_eoa,
)
return get_solidity_contract(
contract_app,
Expand All @@ -120,3 +138,47 @@ async def _factory(contract_app, contract_name, *args, **kwargs):
)

return _factory


@pytest.fixture(scope="package")
def create_account_with_bytecode(starknet, kakarot, deploy_bytecode, deploy_eoa):
"""
Fixture to create a solidity contract in kakarot without running the bytecode.
The given bytecode is directly stored into the account, similarly to what is done
in a genesis config.
Returns the corresponding starknet contract with the extra evm_contract_address attribute.
"""

async def _factory(bytecode: str, caller_eoa=None):
"""
This factory is what is actually returned by pytest when requesting the `create_account_with_bytecode`
fixture.
"""
if caller_eoa is None:
caller_eoa = await deploy_eoa(
generate_random_private_key(int(bytecode, 16))
)

evm_contract_address, starknet_contract_address, _ = await deploy_bytecode(
"",
caller_eoa,
)
contract_class = gather_deprecated_compiled_class(
source="./src/kakarot/accounts/contract/contract_account.cairo",
cairo_path=["src"],
disable_hint_validation=True,
)
contract = StarknetContract(
starknet.state,
contract_class.abi,
starknet_contract_address,
None,
)
await contract.write_bytecode(hex_string_to_bytes_array(bytecode)).execute(
caller_address=kakarot.contract_address
)
setattr(contract, "evm_contract_address", evm_contract_address)
return contract

return _factory

0 comments on commit 0c6e7a6

Please sign in to comment.