ethereum / web3.py

A python interface for interacting with the Ethereum blockchain and ecosystem.
http://web3py.readthedocs.io
MIT License
4.96k stars 1.69k forks source link

web3.eth.getBlock is broken with PyEVMBackend #1213

Closed njgheorghita closed 5 years ago

njgheorghita commented 5 years ago
appnope (0.1.0)
asn1crypto (0.24.0)
attrdict (2.0.0)
backcall (0.1.0)
certifi (2018.11.29)
cffi (1.11.5)
chardet (3.0.4)
cryptography (2.4.2)
cytoolz (0.9.0.1)
decorator (4.3.0)
eth-abi (2.0.0b4)
eth-account (0.3.0)
eth-bloom (1.0.3)
eth-hash (0.2.0)
eth-keyfile (0.5.1)
eth-keys (0.2.1)
eth-rlp (0.1.2)
eth-tester (0.1.0b36)
eth-typing (2.0.0)
eth-utils (1.4.1)
hexbytes (0.1.0)
idna (2.8)
ipython (7.2.0)
ipython-genutils (0.2.0)
jedi (0.13.2)
lru-dict (1.1.6)
mypy-extensions (0.4.1)
parsimonious (0.8.1)
parso (0.3.1)
pexpect (4.6.0)
pickleshare (0.7.5)
pip (9.0.1)
prompt-toolkit (2.0.7)
ptyprocess (0.6.0)
py-ecc (1.4.7)
py-evm (0.2.0a39)
py-geth (2.0.1)
pycparser (2.19)
pycryptodome (3.7.2)
pyethash (0.1.27)
Pygments (2.3.1)
requests (2.21.0)
rlp (1.1.0)
semantic-version (2.6.0)
setuptools (28.8.0)
six (1.12.0)
toolz (0.9.0)
traitlets (4.3.2)
trie (1.3.8)
urllib3 (1.24.1)
wcwidth (0.1.7)
web3 (5.0.0a3)
websockets (7.0)

What was wrong?

Using web3.eth.getBlock() method is broken in v5 alpha - when using a PyEVMBackend - the same code works fine when using MockBackend.

from web3 import Web3
from eth_tester import PyEVMBackend

w3 = Web3(Web3.EthereumTesterProvider(PyEVMBackend()))
w3.testing.mine()
>>> '0x123....'
w3.eth.getBlock('0x123....')

The final line breaks with the following error

~/ethereum/py-ethpm/trash-venv/lib/python3.6/site-packages/eth/validation.py in validate_word(value, title)
    162             "{title} is not a valid word. Must be of bytes type: Got: {0}".format(
    163                 type(value),
--> 164                 title=title,
    165             )
    166         )

ValidationError: Block Hash is not a valid word. Must be of bytes type: Got: <class 'str'>

How can it be fixed?

Not sure, I spent some time digging around eth-tester/py-evm to try and find a solution, but no luck. Though, this is most likely related to update in those dependencies in v5.

pipermerriam commented 5 years ago

This is almost certainly related to #1212

Likely candidate for when/how this got introduced is to look at https://github.com/ethereum/web3.py/pull/1188 since it touched the middlewares in web3.py that handled some of the normalization steps.

njgheorghita commented 5 years ago

@pipermerriam The problem was with how I was instantiating my class using the PyEVMBackend.

This is what was broken...

from web3 import Web3
from eth_tester import PyEVMBackend

w3 = Web3(Web3.EthereumTesterProvider(PyEVMBackend()))

This is what I should have been doing / What works fine.

from web3 import Web3
from eth_tester import PyEVMBackend, EthereumTester

w3 = Web3(Web3.EthereumTesterProvider(EthereumTester(PyEVMBackend())))

Basically, the inbound tx normalization happens in the EthereumTester class and not in PyEVMBackend - but they share the same methods, so web3's delegator was calling the PyEVMBackend.estimate_gas() directly instead of EthereumTester.estimate_gas() which would normalize and then subsequently pass on the normalized tx to PyEVMBackend.estimate_gas()

I'm unsure as to exactly where I saw the first (aka broken) pattern, but it seems to me that maybe the best fix is something in the docs explaining how to instantiate w/ PyEVMBackend directly - as I think this'll be common now that you can adjust genesis-params in eth-tester.

pipermerriam commented 5 years ago

When @kclowes and I looked over this today I had the thought that we could add a check to EthereumTesterProvider.__init__ which checked if the provided value was an instance of EthereumTester and errors out if it isn't. Additionally, if the value is a backend it could lazily wrap it in the EthereumTester class. Either way we can prevent this mistake with some simple value checks.