"Invalid opcode 0x5e" when emitting an event
fjarri opened this issue · 2 comments
Macos 14.3.1, Python 3.10.10, py-evm 0.10.0b2
Trying to call a method of a compiled contract, getting an "invalid opcode" error from the VM. Solidity compiler version is set to 0.8.25 in solcx
.
Full reproducing example:
import tempfile
import rlp
import solcx
from eth_account import Account
from eth_keys import KeyAPI
from eth_utils import keccak, to_canonical_address
from eth import constants
from eth.chains.base import MiningChain
from eth.db.atomic import AtomicDB
from eth.vm.forks.shanghai import ShanghaiVM
from eth.vm.spoof import SpoofTransaction
def compile_contract():
src = """
pragma solidity ^0.8;
contract Test {
event MyEvent(
bytes x
) anonymous;
function emitEvent() public {
bytes memory bytestring = "0123456789";
emit MyEvent(bytestring);
}
}
"""
with tempfile.NamedTemporaryFile(mode="w") as f:
f.file.write(src)
f.file.close()
compiled = solcx.compile_files([f.name], output_values=["bin", "asm"], solc_version="0.8.25")
return bytes.fromhex(compiled[f.name + ":Test"]["bin"])
def make_chain():
klass = MiningChain.configure(vm_configuration=((0, ShanghaiVM),))
genesis_params = {
"coinbase": constants.ZERO_ADDRESS,
"transaction_root": constants.BLANK_ROOT_HASH,
"receipt_root": constants.BLANK_ROOT_HASH,
"difficulty": 0,
"gas_limit": 30029122,
"timestamp": 0,
"extra_data": constants.GENESIS_EXTRA_DATA,
"nonce": b"\x00" * 8,
}
# This seems to be hardcoded in PyEVM somehow.
root_private_key = KeyAPI().PrivateKey(b"\x00" * 31 + b"\x01")
account = Account.from_key(root_private_key)
genesis_state = {
to_canonical_address(account.address): {
"balance": 100 * 10**18,
"storage": {},
"code": b"",
"nonce": 0,
}
}
chain = klass.from_genesis(AtomicDB(), genesis_params, genesis_state)
return account, chain
def main():
account, chain = make_chain()
contract_data = compile_contract()
# Deploy the contract
block = chain.get_block()
header = block.header
evm_transaction = chain.create_unsigned_transaction(
gas_price=0, gas=30029122, nonce=0, value=0, data=contract_data, to=b""
)
spoofed_transaction = SpoofTransaction(
evm_transaction, from_=to_canonical_address(account.address)
)
gas = chain.estimate_gas(spoofed_transaction, header)
gas_price = header.base_fee_per_gas + 10**9
tx = {
"chainId": "0x1",
"value": "0x0",
"gas": hex(gas),
"maxFeePerGas": hex(gas_price),
"maxPriorityFeePerGas": hex(10**9),
"nonce": "0x0",
"data": "0x" + contract_data.hex(),
"type": "0x2",
}
signed_tx = account.sign_transaction(tx).rawTransaction
vm = chain.get_vm(at_header=header)
tx = vm.get_transaction_builder().decode(signed_tx)
chain.apply_transaction(tx)
chain.mine_block()
contract_address = keccak(rlp.encode([to_canonical_address(account.address), 0]))[-20:]
# Transact
header = chain.get_block().header
evm_transaction = chain.create_unsigned_transaction(
gas_price=0,
gas=30029122,
nonce=1,
value=0,
data=keccak(b"emitEvent()")[:4],
to=contract_address,
)
spoofed_transaction = SpoofTransaction(
evm_transaction, from_=to_canonical_address(account.address)
)
chain.estimate_gas(spoofed_transaction, header)
if __name__ == "__main__":
main()
Output:
Traceback (most recent call last):
File "/Users/bogdan/wb/repos/py-evm/test.py", line 128, in <module>
main()
File "/Users/bogdan/wb/repos/py-evm/test.py", line 124, in main
chain.estimate_gas(spoofed_transaction, header)
File "/Users/bogdan/wb/repos/py-evm/eth/chains/base.py", line 491, in estimate_gas
return self.gas_estimator(state, transaction)
File "cytoolz/functoolz.pyx", line 263, in cytoolz.functoolz.curry.__call__
File "/Users/bogdan/wb/repos/py-evm/eth/estimators/gas.py", line 89, in binary_gas_search
raise error
File "/Users/bogdan/wb/repos/py-evm/eth/vm/computation.py", line 409, in apply_computation
opcode_fn(computation=computation)
File "/Users/bogdan/wb/repos/py-evm/eth/vm/logic/invalid.py", line 21, in __call__
raise InvalidInstruction(
eth.exceptions.InvalidInstruction: Invalid opcode 0x5e @ 167
According to the information provided on https://www.evm.codes/?fork=cancun, the opcode MCOPY(0X5E) was introduced in the Cancun fork. However, in your code snippet:
klass = MiningChain.configure(vm_configuration=((0, ShanghaiVM),))
you are using the ShanghaiVM, where 0x5e is not defined. To address the issue, you may consider either using an older version of the compiler to escape using new opcode or a newer version of the VM that supports the MCOPY opcode.
@Alleysira has the right idea. You'll need to use the CancunVM
from the latest available version of py-evm
in order to support MCOPY
.