Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
29 changes: 20 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
language: python
python:
- "3.5"
env:
matrix:
matrix:
include:
# lint
- python: "3.5"
env: TOX_POSARGS="-e flake8"
# core
- TOX_POSARGS="-e py27-core -e py34-core -e py35-core"
# web3
- TOX_POSARGS="-e py27-web3v313 -e py34-web3v313 -e py35-web3v313"
- python: "2.7"
env: TOX_POSARGS="-e py27-core"
- python: "3.4"
env: TOX_POSARGS="-e py34-core"
- python: "3.5"
env: TOX_POSARGS="-e py35-core"
# pyethereum 1.6.x
- TOX_POSARGS="-e py27-pyethereum16 -e py34-pyethereum16 -e py35-pyethereum16"
# pyethereum 2.0.x
#- TOX_POSARGS="-e py27-pyethereum20 -e py34-pyethereum20 -e py35-pyethereum20"
- TOX_POSARGS="-e flake8"
- python: "2.7"
env: TOX_POSARGS="-e py27-pyethereum16"
- python: "3.4"
env: TOX_POSARGS="-e py34-pyethereum16"
- python: "3.5"
env: TOX_POSARGS="-e py35-pyethereum16"
# pyevm
- python: "3.5"
env: TOX_POSARGS="-e py35-pyevm"
cache:
pip: true
install:
Expand Down
70 changes: 47 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -640,9 +640,15 @@ various backends by default. You can however install ethereum tester with the
necessary dependencies using the following method.

```bash
$ pip install ethereum-tester[pyethereum16]
$ pip install ethereum-tester[<backend-name>]
```

You should replace `<backend-name>` with the name of the desired testing
backend. Available backends are:

* `pyethereum16`: [PyEthereum v1.6.x](https://pypi.python.org/pypi/ethereum/1.6.1)
* `py-evm`: [PyEVM (alpha)](https://pypi.python.org/pypi/py-evm) **(experimental)**

### Selecting a Backend

You can select which backend in a few different ways.
Expand All @@ -665,36 +671,64 @@ backend class you wish to use.
Ethereum tester can be used with the following backends.

* PyEthereum 1.6.x (default)
* PyEVM (experimental)
* MockBackend

The following backends on the roadmap to be developed.

* PyEthereum 2.0.x (under development)
* PyEVM (experimental)

#### PyEthereum 1.6.x
#### MockBackend

TODO
This backend has limited functionality. It cannot perform any VM computations.
It mocks out all of the objects and interactions.

#### PyEthereum 2.0.x (under development)
```python
>>> from eth_tester import MockBackend
>>> t = EthereumTester(MockBackend())
```

> Under development
#### PyEthereum 1.6.x

Uses the PyEthereum library at version `v1.6.x`

```python
>>> from eth_tester import PyEthereum16Backend
>>> t = EthereumTester(PyEthereum16Backend())
```

#### PyEVM (experimental)

> **WARNING** Py-EVM is experimental and should not be relied on for mission critical testing at this stage.

Uses the experimental Py-EVM library.

```python
>>> from eth_tester import PyEVMBackend
>>> t = EthereumTester(PyEVMBackend())
```

#### PyEthereum 2.0.x (under development)

> Under development

### Implementing Custom Backends

The base class `eth_tester.backends.base.BaseChainBackend` is the recommended
base class to begin with if you wish to write your own backend. In order for
ethereum tester to operate correctly, your backend **must** be able to do all
of the following.
base class to begin with if you wish to write your own backend.

TODO
Details on implementation are beyond the scope of this document.


## Data Formats

Ethereum tester uses two formats for data.

* The *normal* format is the data format the is expected as input arguments to all `EthereumTester` methods as well as the return types from all method calls.
* The *canonical* format is the data format that is used internally by the backend class.

Ethereum tester enforces strict validation rules on these formats.

### Canonical Formats

The canonical format is intended for low level handling by backends.
Expand Down Expand Up @@ -756,16 +790,6 @@ The specifics of this object are beyong the scope of this document.

# Use with Web3.py

While the `ethereum-tester` library can be used on its own it can also be used
with the [`web3.py`](https://github.com/pipermerriam/web3.py) library. The
`ethereum-tester` library comes with the provider class
`eth_tester.web3.EthereumTesterProvider`. You can use it like this:

```python
>>> from eth_tester import EthereumTester
>>> from eth_tester.web3 import EthereumTesterProvider
>>> from web3 import Web3
>>> eth_tester = EthereumTester()
>>> provider = EthereumTesterProvider(eth_tester)
>>> web3 = Web3(provider)
```
See the [web3.py documentation](http://web3py.readthedocs.io/en/latest/) for
information on the `EthereumTester` provider which integrates with this
library.
2 changes: 1 addition & 1 deletion eth_tester/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .backends import ( # noqa: F401
MockBackend,
PyEthereum16Backend,
PyEthereum20Backend,
PyEVMBackend,
)


Expand Down
36 changes: 27 additions & 9 deletions eth_tester/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import os
import sys

import warnings

from eth_tester.utils.module_loading import (
get_import_path,
Expand All @@ -10,21 +13,36 @@
)
from .pyethereum.v16 import (
PyEthereum16Backend,
is_pyethereum16_available,
)
from .pyethereum.v20 import ( # noqa: F401
PyEthereum20Backend,
from .pyevm import ( # noqa: F401
PyEVMBackend,
is_pyevm_available,
)


DEFAULT_CHAIN_BACKEND_CLASS = get_import_path(PyEthereum16Backend)


def get_chain_backend_class(backend_import_path=None):
warnings.simplefilter('default')

if backend_import_path is None:
backend_import_path = os.environ.get(
'ETHEREUM_TESTER_CHAIN_BACKEND',
DEFAULT_CHAIN_BACKEND_CLASS,
)
if 'ETHEREUM_TESTER_CHAIN_BACKEND' in os.environ:
backend_import_path = os.environ['ETHEREUM_TESTER_CHAIN_BACKEND']
elif is_pyevm_available():
vi = sys.version_info
if vi.major != 3 or vi.minor < 5:
warnings.warn(UserWarning("Py-EVM does not support python < 3.5"))
backend_import_path = get_import_path(PyEVMBackend)
elif is_pyethereum16_available():
backend_import_path = get_import_path(PyEthereum16Backend)
else:
warnings.warn(UserWarning(
"Ethereum Tester: No backend was explicitely set, and no *full* "
"backends were available. Falling back to the `MockBackend` "
"which does not support all EVM functionality. Please refer to "
"the `ethereum-tester` documentation for information on what "
"backends are available and how to set them."
))
backend_import_path = get_import_path(MockBackend)
return import_string(backend_import_path)


Expand Down
11 changes: 7 additions & 4 deletions eth_tester/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def get_fork_block(self, fork_name):
#
# Meta
#
def time_travel(self, timestamp):
def time_travel(self, to_timestamp):
raise NotImplementedError("Must be implemented by subclasses")

#
Expand All @@ -38,13 +38,16 @@ def mine_blocks(self, num_blocks=1, coinbase=None):
def get_accounts(self):
raise NotImplementedError("Must be implemented by subclasses")

def add_account(self, private_key):
raise NotImplementedError("Must be implemented by subclasses")

#
# Chain data
#
def get_block_by_number(self, block_number):
def get_block_by_number(self, block_number, full_transaction=True):
raise NotImplementedError("Must be implemented by subclasses")

def get_block_by_hash(self, block_hash):
def get_block_by_hash(self, block_hash, full_transaction=True):
raise NotImplementedError("Must be implemented by subclasses")

def get_transaction_by_hash(self, transaction_hash):
Expand Down Expand Up @@ -74,5 +77,5 @@ def send_transaction(self, transaction):
def estimate_gas(self, transaction):
raise NotImplementedError("Must be implemented by subclasses")

def call(self, transaction):
def call(self, transaction, block_number="latest"):
raise NotImplementedError("Must be implemented by subclasses")
28 changes: 18 additions & 10 deletions eth_tester/backends/mock/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ def _get_default_account_data():
}


@to_tuple
def get_default_alloc(num_accounts=10):
return {
_generate_dummy_address(idx): _get_default_account_data()
for idx
in range(num_accounts)
}
for idx in range(num_accounts):
yield (
_generate_dummy_address(idx),
_get_default_account_data(),
)


class MockBackend(BaseChainBackend):
Expand Down Expand Up @@ -115,6 +116,11 @@ def reset_to_genesis(self):
self.block = self.genesis_block
self.receipts = {}
self.fork_blocks = {}
self.mine_blocks()

@property
def account_state_lookup(self):
return dict(self.alloc)

#
# Fork block numbers
Expand Down Expand Up @@ -160,11 +166,13 @@ def mine_blocks(self, num_blocks=1, coinbase=None):
# Accounts
#
def get_accounts(self):
return tuple(self.alloc.keys())
return tuple(account for account, _ in self.alloc)

def add_account(self, private_key):
account = private_key_to_address(private_key)
self.alloc[account] = _get_default_account_data()
self.alloc = self.alloc + (
(account, _get_default_account_data()),
)

#
# Chain data
Expand Down Expand Up @@ -263,19 +271,19 @@ def get_transaction_receipt(self, transaction_hash):
#
def get_nonce(self, account, block_number=None):
try:
return self.alloc[account]['nonce']
return self.account_state_lookup[account]['nonce']
except KeyError:
return 0

def get_balance(self, account, block_number=None):
try:
return self.alloc[account]['balance']
return self.account_state_lookup[account]['balance']
except KeyError:
return 0

def get_code(self, account, block_number=None):
try:
return self.alloc[account]['code']
return self.account_state_lookup[account]['code']
except KeyError:
return 0

Expand Down
5 changes: 5 additions & 0 deletions eth_tester/backends/pyethereum/v16/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from __future__ import absolute_import

from ..utils import ( # noqa: F401
is_pyethereum16_available,
)
from .main import ( # noqa: F401
PyEthereum16Backend,
)
1 change: 1 addition & 0 deletions eth_tester/backends/pyethereum/v16/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ def reset_to_genesis(self):
from ethereum import tester
self.evm = tester.state()
self.evm.extra_accounts = {}
self.mine_blocks()

#
# Meta
Expand Down
8 changes: 8 additions & 0 deletions eth_tester/backends/pyevm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import absolute_import

from .utils import ( # noqa: F401
is_pyevm_available,
)
from .main import ( # noqa: F401
PyEVMBackend,
)
Loading