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

fix: use sload for storage #19

Merged
merged 2 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions pyrevm.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,15 @@ class EVM:
:param balance: The balance.
"""

def storage(self: "EVM", address: str, index: int) -> Optional[int]:
def storage(self: "EVM", address: str, index: int) -> int:
"""
Returns the storage value of the given address at the given index.
:param address: The address.
:param index: The index.
:return: The storage value.
"""

def block_hash(self: "EVM", number: int) -> Optional[bytes]:
def block_hash(self: "EVM", number: int) -> bytes:
"""
Returns the block hash of the given number.
:param number: The number.
Expand Down
34 changes: 15 additions & 19 deletions src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,18 @@ impl EVM {
}

/// Get storage value of address at index.
fn storage(&mut self, address: &str, index: U256) -> PyResult<Option<U256>> {
let (account, _) = self.context.load_account(addr(address)?).map_err(pyerr)?;
Ok(account.storage.get(&index).map(|s| s.present_value))
fn storage(&mut self, address: &str, index: U256) -> PyResult<U256> {
let address = addr(address)?;
// `sload` expects the account to be already loaded.
let _ = self.context.load_account(address).map_err(pyerr)?;
let (value, _) = self.context.sload(address, index).map_err(pyerr)?;
Ok(value)
}

/// Get block hash by block number.
fn block_hash(&mut self, number: U256, py: Python<'_>) -> PyResult<Option<PyObject>> {
let bytes = self.context.block_hash(number).map_err(pyerr)?;
if bytes.is_empty() {
return Ok(None);
Copy link
Member Author

@DaniPopes DaniPopes May 5, 2024

Choose a reason for hiding this comment

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

this can't happen as bytes is B256 ([u8; 32]) which can never be empty

}
Ok(Some(PyBytes::new(py, bytes.as_ref()).into()))
fn block_hash(&mut self, number: U256, py: Python<'_>) -> PyResult<PyObject> {
let hash = self.context.block_hash(number).map_err(pyerr)?;
Ok(PyBytes::new(py, hash.as_ref()).into())
}

/// Inserts the provided account information in the database at the specified address.
Expand All @@ -153,14 +153,10 @@ impl EVM {

/// Set the balance of a given address.
fn set_balance(&mut self, address: &str, balance: U256) -> PyResult<()> {
let address_ = addr(address)?;
let account = {
let (account, _) = self.context.load_account(address_).map_err(pyerr)?;
account.info.balance = balance;
account.clone()
};
self.context.journaled_state.state.insert(address_, account);
self.context.journaled_state.touch(&address_);
let address = addr(address)?;
let (account, _) = self.context.load_account(address).map_err(pyerr)?;
account.info.balance = balance;
self.context.journaled_state.touch(&address);
Ok(())
}

Expand Down Expand Up @@ -342,10 +338,10 @@ impl EVM {
if let Output::Create(out, address) = output {
Ok((out, address.unwrap()))
} else {
Err(pyerr(output.clone()))
Err(pyerr(output))
}
} else {
Err(pyerr(result.clone()))
Err(pyerr(result))
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![warn(unreachable_pub)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![deny(unused_must_use, rust_2018_idioms)]
#![allow(non_local_definitions)]
Copy link
Contributor

Choose a reason for hiding this comment

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

We could eventually simplify (maybe even autogenerate) the getters and setters

#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![allow(clippy::too_many_arguments)]

Expand Down
6 changes: 3 additions & 3 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pyo3::types::PyBytes;
use pyo3::{exceptions::PyTypeError, prelude::*};
use revm::precompile::B256;
use revm::primitives::{fake_exponential as revm_fake_exponential, Address};
use std::fmt::Debug;
use std::fmt;

pub(crate) fn addr(s: &str) -> Result<Address, PyErr> {
s.parse::<Address>()
Expand All @@ -18,8 +18,8 @@ pub(crate) fn addr_or_zero(s: Option<&str>) -> Result<Address, PyErr> {
}

/// Convert a Rust error into a Python error.
pub(crate) fn pyerr<T: Debug>(err: T) -> PyErr {
PyRuntimeError::new_err(format!("{:?}", err))
pub(crate) fn pyerr<T: fmt::Debug>(err: T) -> PyErr {
PyRuntimeError::new_err(format!("{err:?}"))
}

pub(crate) fn from_pybytes(b: &PyBytes) -> PyResult<B256> {
Expand Down
41 changes: 29 additions & 12 deletions tests/test_evm.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import json
import os

from pyrevm import EVM, Env, BlockEnv, AccountInfo, TxEnv
from pyrevm import EVM, AccountInfo, BlockEnv, Env, TxEnv

from tests.utils import load_contract_bin, encode_uint, encode_address
import pytest

from tests.utils import encode_address, encode_uint, load_contract_bin

address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" # vitalik.eth
address2 = "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"

# use your own key during development to avoid rate limiting the CI job
fork_url = os.getenv("FORK_URL") or "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"
fork_url = (
os.getenv("FORK_URL")
or "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"
)

KWARG_CASES = [
{"fork_url": fork_url},
Expand Down Expand Up @@ -40,7 +42,7 @@ def test_revm_fork():
evm.message_call(
caller=address,
to=address2,
value=10000
value=10000,
# data
)

Expand All @@ -51,6 +53,13 @@ def test_revm_fork():
assert info.balance == 10000


def test_fork_storage():
weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
evm = EVM(fork_url=fork_url, fork_block="latest")
value = evm.storage(weth, 0)
assert value > 0


def test_deploy():
evm = EVM()

Expand All @@ -65,13 +74,13 @@ def test_deploy():

assert deployed_at == "0x3e4ea2156166390f880071d94458efb098473311"
deployed_code = evm.get_code(deployed_at)
assert deployed_code.hex().rstrip('0') in code.hex()
assert deployed_code.hex().rstrip("0") in code.hex()
assert evm.basic(deployed_at).code.hex() == deployed_code.hex()

result = evm.message_call(
address,
deployed_at,
calldata=b'\xc2\x98Ux' # ==method_id('foo()')
calldata=b"\xc2\x98Ux", # ==method_id('foo()')
)
assert int(result.hex(), 16) == 123

Expand All @@ -92,7 +101,10 @@ def test_balances():


def test_balances_fork():
evm = EVM(fork_url=fork_url, fork_block="0x3b01f793ed1923cd82df5fe345b3e12211aedd514c8546e69efd6386dc0c9a97")
evm = EVM(
fork_url=fork_url,
fork_block="0x3b01f793ed1923cd82df5fe345b3e12211aedd514c8546e69efd6386dc0c9a97",
)

vb_before = evm.basic(address)
assert vb_before.balance == 955628344913799071315
Expand Down Expand Up @@ -215,7 +227,7 @@ def test_blueprint():
# bytecode based on vyper `@external def foo() -> uint256: return 123`
bytecode = load_contract_bin("blueprint.bin")

bytecode = b"\xFE\x71\x00" + bytecode
bytecode = b"\xfe\x71\x00" + bytecode
bytecode_len = len(bytecode)
bytecode_len_hex = hex(bytecode_len)[2:].rjust(4, "0")

Expand All @@ -224,7 +236,7 @@ def test_blueprint():
deploy_bytecode = deploy_preamble + bytecode

deployer_address = evm.deploy(address, deploy_bytecode)
assert evm.basic(deployer_address).code.hex().rstrip('0') in deploy_bytecode.hex()
assert evm.basic(deployer_address).code.hex().rstrip("0") in deploy_bytecode.hex()


def test_block_setters():
Expand All @@ -251,7 +263,10 @@ def test_tx_setters():
assert evm.env.tx.blob_hashes == [b"1" * 32]


@pytest.mark.parametrize("excess_blob_gas,expected_fee", [(0, 1), (10**3, 1), (2**24, 152), (2**26, 537070730)])
@pytest.mark.parametrize(
"excess_blob_gas,expected_fee",
[(0, 1), (10**3, 1), (2**24, 152), (2**26, 537070730)],
)
def test_get_blobbasefee(excess_blob_gas, expected_fee):
evm = EVM()
evm.set_block_env(BlockEnv(excess_blob_gas=excess_blob_gas))
Expand Down Expand Up @@ -295,5 +310,7 @@ def test_call_reverting(kwargs):
value=10,
)

assert evm.get_code(deploy_address), "The code should still be deployed after revert"
assert evm.get_code(
deploy_address
), "The code should still be deployed after revert"
assert str(err.value).startswith("Transaction(LackOfFundForMaxFee")
Loading