Skip to content

Commit

Permalink
Merge branch 'develop' into evm-tool
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Xie committed Aug 22, 2017
2 parents b533a24 + 0b12134 commit c387cfb
Show file tree
Hide file tree
Showing 72 changed files with 13,161 additions and 12,998 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ pyethereum/todo.txt
pyethereum/monkeypatch.py
.eggs
.cache
.env
29 changes: 18 additions & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
language: python
python: 3.5
python: 2.7
sudo: required
dist: trusty
env:
matrix:
- TOX_ENV=py27
- TOX_ENV=py34
- TOX_ENV=py35
#matrix:
#- TOX_ENV=py27
#- TOX_ENV=py34
#- TOX_ENV=py35
global:
- COVERAGE_APPEND="--append"
- secure: cKbIgpTJ1yjKLBxpCEiT6IH7NShDWZUE+BvnrAfc+ujCsR6LyLJcKxFQmKnWryJCqg7fp82Ep2bF2oDKzanAROar2xDY1SFGbai42seYMaFCw53YPGJ6u3VNCcfT0rN9BWgE7el/m4fjcD6CRsZYKArNNJbMX8csRt3uXXCFLso=
Expand All @@ -23,15 +23,22 @@ before_install:
- if [ -n "$SOLC_VERSION" ]; then sudo apt-get install -y tree unzip; fi
install:
- if [ -n "$SOLC_VERSION" ]; then /.$TRAVIS_BUILD_DIR/.travis/install_solc.sh; fi
- travis_retry pip install setuptools --upgrade
- travis_retry pip install pip setuptools --upgrade
- travis_retry pip install tox
- travis_retry pip install coverage
- travis_retry pip install flake8
- travis_retry python setup.py install
- travis_retry pip install -r dev_requirements.txt
script:
- if [ -d .tox/$TOX_ENV/ ]; then cd .tox/$TOX_ENV && coverage erase; fi;
- tox -e $TOX_ENV -- --ignore ethereum/tests/test_vm.py --ignore ethereum/tests/test_state.py
- tox -e $TOX_ENV -- ethereum/tests/test_vm.py
- tox -e $TOX_ENV -- ethereum/tests/test_state.py
- coverage report --show-missing
# XXX: For now we're only performing minimal CI checks as most tests are
# broken. Tests will be individually added here as they're fixed.
- make lint-minimal
- make test-passing
#- if [ -d .tox/$TOX_ENV/ ]; then cd .tox/$TOX_ENV && coverage erase; fi;
#- tox -e $TOX_ENV -- --ignore ethereum/tests/test_vm.py --ignore ethereum/tests/test_state.py
#- tox -e $TOX_ENV -- ethereum/tests/test_vm.py
#- tox -e $TOX_ENV -- ethereum/tests/test_state.py
#- coverage report --show-missing
after_success:
- travis_retry pip install coveralls
- cd .tox/$TOX_ENV && coveralls
Expand Down
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,18 @@ clean-test:
lint:
flake8 ethereum tests --ignore=E501

lint-minimal:
python -m flake8 --ignore=F401,F841,F811 --select=F --exclude=todo,experimental,ethash.py,ethash_utils.py ethereum

test:
py.test --tb=no ethereum/tests/

test-passing:
py.test ethereum/tests/test_abi.py ethereum/tests/test_bloom.py ethereum/tests/test_chain.py ethereum/tests/test_compress.py ethereum/tests/test_db.py ethereum/tests/test_difficulty.py ethereum/tests/test_opcodes.py ethereum/tests/test_trie_next_prev.py ethereum/tests/test_genesis.py ethereum/tests/test_serialization.py ethereum/tests/test_trie.py

test-failing:
py.test ethereum/tests/test_blockstransactions.py ethereum/tests/test_transactions.py ethereum/tests/test_keys.py ethereum/tests/test_state.py ethereum/tests/test_contracts.py ethereum/tests/test_tester.py

testnovm:
py.test --tb=no ethereum/tests/ --ignore=ethereum/tests/test_vm.py

Expand Down
251 changes: 251 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
This is the Python core library of the Ethereum project.

For the python based command line client see:
https://github.com/ethereum/pyethapp

## Installation:

```bash
sudo apt-get install libssl-dev build-essential automake pkg-config libtool libffi-dev libgmp-dev
git clone https://github.com/ethereum/pyethereum/
cd pyethereum
python setup.py install
```

## Components

### ethereum.pow.chain

Contains the Chain class, which can be used to manage a blockchain. Main methods are:

* `__init__(genesis=None, env=None, new_head_cb=None, reset_genesis=False, localtime=None)` - initializes with the given genesis. `env` specifies the _environment_ (including chain config and database), `new_head_cb` is a callback called when a new head is added, and `localtime` is what the chain assumes is the current timestamp. The genesis can be:
* None - in which case it assumes `env` is given, and creates a Chain object with the data saved in `env.db`. If `reset_genesis` is set, it re-initializes the chain.
* A `State` object
* A genesis declaration
* A state snapshot (`State.snapshot()`)
* An allocation (ie. dict `{address: {balance: 1, nonce: 2, code: b'\x03\x04\x05', storage: {"0x06": "0x07"}}}`)
* `add_block(block)` - adds a block to the chain
* `process_time_queue(timestamp)` - tells the chain that the current time has increased to the new timestamp. The chain will then process any blocks that were unprocessed because they appeared too "early"
* `get_blockhash_by_number(num)` - get the block hash of a block at the given block number
* `get_block(hash)` - gets the block with the given blockhash
* `get_block_by_number(num)` - equivalent to `get_block(get_blockhash_by_number(num))`
* `get_parent(block)` - gets the parent of a block
* `get_children(block)` - gets the children of a block
* `head` (property) - gets the block at the head of the chain
* `state` (property) - gets the state at the head of the chain
* `mk_poststate_of_blockhash(hash)` - creates a state object after a given block
* `has_block(block)` - is that block in the chain? Returns True/False
* `get_chain(from, to)` - roughly equivalent to `[get_block_by_number(i) for i in range(from, to)]`, though automatically stops if it reaches the head. `from` can be elided to start from genesis, `to` can be elided to go up to the head.
* `get_tx_position(tx)` - if the transaction is in the chain, returns `(blknum, index)` where `blknum` is the block number of the block that contains the transaction and `index` is its position in the block

### ethereum.state

Contains the State class, which is used to manage a state. Main methods are:

* `__init__(root_hash, env, **kwargs)` - initializes a state with the given root hash, the given env (which includes a config and database) and the given auxiliary arguments. These include:
* `txindex` - the transaction index
* `gas_used` - amount of gas used
* `gas_limit` - block gas limit
* `block_number` - block number
* `block_coinbase` - block coinbase address
* `block_difficulty` - block difficulty
* `timestamp` - timestamp
* `logs` - logs created so far
* `receipts` - receipts created so far (from previous transactions in the current block)
* `bloom` - the bloom filter
* `suicides` - suicides (or selfdestructs, the newer more politically correct synonym)
* `recent_uncles` - recent uncle blocks in the chain
* `prev_headers` - previous block headers
* `refunds` - suicide/selfdestruct refund counter

Pyethereum follows a **maximally state-centric model**; the ONLY information needed to process a transaction or a block is located within the state itself, allowing the actual state transition logic to be a very clean `apply_transaction(state, tx)` and `apply_block(state, block)`.

* `get_balance`- gets the balance of an account
* `get_code` - gets the code of an account
* `get_storage_data(addr, k)` - gets the storage at the given key of the given address. Expects a key in **numerical** form (eg. b"cow" or "0x636f77" is represented as 6516599).
* `to_snapshot(root_only=False, no_prevblocks=False)` - creates a snapshot for the current state. If `root_only` is set, only adds the state root, not the entire state. If `no_prevblocks` is set, does not add previous headers and uncles. Setting either of those flags means that the same database would be required to recover from the snapshot.
* `from_snapshot(snapshot, env)` (classmethod) - creates a state from the given snapshot with the given `env`.
* `ephemeral_clone()` - creates a clone of the state that you can work with without affecting the original

There are also many methods that modify the state, eg. `set_code`, `set_storage_data`, but it is generally recommended to avoid using these, and instead modify the state ONLY through `apply_transaction` and `apply_block`.

### ethereum.meta

This file contains two functions:

* `apply_block(state, block)` - takes a state and processes a block onto that state
* `make_head_candidate(chain, txqueue=None, parent=None, timestamp, coinbase, extra_data, min_gasprice=0)` - creates a candidate block for the chain on top of the given parent block (default: head of the chain). Gets transactions from the given `txqueue` object with the given `mingasprice` (otherwise does not add transactions). `timestamp`, `coinbase` and `extra_data` can be used to specify those parameters in the block; otherwise defaults are used

### ethereum.messages

The main function that should be called from here is `apply_transaction(state, tx)`.

### ethereum.utils

Contains a bunch of utility functions, including:

#### Numerical and hex conversions

* `encode_int(i)` - converts an integer into big-endian binary representation
* `zpad(data, length)` - pads the data up to the desired length by adding zero bytes on the left
* `encode_int32(i)` - equivalent to `zpad(encode_int(i), 32)` but faster
* `big_endian_to_int(d)` - converts binary data into an integer
* `encode_hex(b)` - converts bytes to hex
* `decode_hex(h)` - converts hex to bytes
* `int_to_addr(i)` - converts integer to address
* `is_numeric(i)` - returns True if the value is int or long, otherwise False

#### Cryptography

* `sha3(data)` - computes the SHA3 (or more precisely, keccak256) hash
* `ecrecover_to_pub(hash, v, r, s)` - recovers the public key that made the signature as a 64-byte binary blob of `encode_int32(x) + encode_int32(y)`. Hashing this and taking the last 20 bytes gives the _address_ that signed a message.
* `ecsign(hash, key)` - returns the v, r, s values of a signature
* `normalize_key(key)` - converts a key from many formats into 32-byte binary
* `privtoaddr(key)` - converts a key to an address

#### Addresses

* `normalize_address(addr)` - converts an address into 20-byte binary form
* `check_checksum(addr)` - returns True if the address checksum passes, otherwise False
* `checksum_encode(addr)` - converts an address into hex form with a checksum
* `mk_contract_address(addr, nonce)` - creates the address of a contract created by the given address with the given nonce

#### Miscellaneous

* `denoms` - contains the denominations of ether, eg. `denoms.finney = 10**15`, `denoms.shannon = 10**9`, `denoms.gwei = 10**9`

### ethereum.block

Contains the `Block` and `BlockHeader` classes. Generally recommended to avoid creating blocks and block headers directly, instead using `mk_head_candidate`. The member variables are straightforward:

* `block.transactions` - transactions in a block
* `block.uncles` - uncles in a block
* `block.header` - header of a block

And in the header:

* `header.hash` - the hash (also the block hash)
* `header.mining_hash` - the hash used for proof of work mining
* `header.to_dict()` - serializes into a human-readable dict
* `header.prevhash` - previous block hash
* `header.uncles_hash` - hash of the uncle list
* `header.coinbase` - coinbase (miner) address
* `header.state_root` - root hash of the post-state
* `header.tx_list_root` - hash of the transactions in the block
* `header.receipts_root` - hash of the receipt trie
* `header.bloom` - bloom filter
* `header.difficulty` - block difficulty
* `header.number` - block number
* `header.gas_limit` - gas limit
* `header.gas_used` - gas used
* `header.timestamp` - timestamp
* `header.extra_data` - block extra data
* `header.mixhash` and `header.nonce` - Ethash proof of work values

### ethereum.transactions

Contains the Transaction class, with the following methods and values:

* `__init__(nonce, gasprice, startgas, to, value, data, (v, r, s optional))` - constructor
* `sign(key, network_id=None)` - signs the transaction with the given key, and with the given EIP155 chain ID (leaving as None will create a pre-EIP155 tx, be warned of replay attacks if you do this!)
* `sender` - the sender address of the transaction
* `network_id` - the EIP155 chain ID of the transaction
* `hash` - the hash of the transaction
* `to_dict()` - serializes into a human-readable dict
* `intrinsic_gas_used` - the amount of gas consumed by the transaction, including the cost of the tx data
* `creates` - if the transaction creates a contract, returns the contract address
* `nonce`, `gasprice`, `startgas`, `to`, `value`, `data`, `v`, `r`, `s` - parameters in the transaction

### ethereum.tools.keys

Creates encrypted private key storages

* `decode_keystore_json(jsondata, password)` - returns the private key from an encrypted keystore object. NOTE: if you are loading from a file, the most convenient way to do this is `import json; key = decode_keystore_json(json.load(open('filename.json')), 'password')`
* `make_keystore_json(key, pw, kdf='pbkdf2', cipher='aes-128-ctr')` - creates an encrypted keystore object for the key. Keeping `kdf` and `cipher` at their default values is recommended.

### ethereum.abi

Most compilers for HLLs (solidity, serpent, viper, etc) on top of Ethereum have the option to output an ABI declaration for a program. This is a json object that looks something like this:

```json
[{"name": "ecrecover(uint256,uint256,uint256,uint256)", "type": "function", "constant": false,
"inputs": [{"name": "h", "type": "uint256"}, {"name": "v", "type": "uint256"}, {"name": "r", "type": "uint256"}, {"name": "s", "type": "uint256"}],
"outputs": [{"name": "out", "type": "int256[]"}]},
{"name": "PubkeyTripleLogEvent(uint256,uint256,uint256)", "type": "event",
"inputs": [{"name": "x", "type": "uint256", "indexed": false}, {"name": "y", "type": "uint256", "indexed": false}, {"name": "z", "type": "uint256", "indexed": false}]}]
```

You can initialize an `abi.ContractTranslator` object to encode and decode data for contracts as follows:

```python
true, false = True, False
ct = abi.ContractTranslator(<json here>)
txdata = ct.encode('function_name', [arg1, arg2, arg3])
```

You can also call `ct.decode_event([topic1, topic2...], logdata)` to decode a log.

### RLP encoding and decoding

For any transaction or block, you can simply do:

```python
import rlp
bindata = rlp.encode(<tx or block>)
```

To decode:

```python
import rlp
from ethereum.transactions import Transaction
rlp.decode(blob, Transaction)
```

Or:

```python
import rlp
from ethereum.blocks import Block
rlp.decode(blob, Block)
```

### Consensus abstraction

The pyethereum codebase is designed to be maximally friendly for use across many different consensus algorithms. If you want to add a new consensus algo, you'll need to take the following steps:

* Add a directory alongside `pow`, and in it create a `chain.py` class that implements a `Chain` module. This may have a totally different fork choice rule for proof of work (GHOST, signature counting, Casper, etc).
* Add an entry to `consensus_strategy.py`. You will need to implement:
* `check_seal` - check that a block is correctly "sealed" (mined, signed, etc)
* `validate_uncles(state, block)` - check that uncles are valid
* `initialize(state, block)` - called in `apply_block` before transactions are processed
* `finalize(state, block)` - called in `apply_block` after transactions are processed
* `get_uncle_candidates(chain, state)` - called in `mk_head_candidate` to include uncles in a block
* Create a chain config with the `CONSENSUS_STRATEGY` set to whatever you named your new consensus strategy

## Tester module

See https://github.com/ethereum/pyethereum/wiki/Using-pyethereum.tester

## Tests

Run `python3.6 -m pytest ethereum/tests/<filename>` for any .py file in that directory. Currently all tests are passing except for a few Metropolis-specific state tests and block tests.

To make your own state tests, use the tester module as follows:

```python
from ethereum.tools import tester as t
import json
c = t.Chain()
x = c.contract(<code>, language=<language>)
pre = t.mk_state_test_prefill(c)
x.foo(<args>)
post = t.mk_state_test_postfill(c, pre)
open('output.json', 'w').write(json.dumps(post, indent=4))
```

To make a test filler file instead, do `post = t.mk_state_test_postfill(c, pre, True)`.

## License

See [LICENSE](LICENSE)
Loading

0 comments on commit c387cfb

Please sign in to comment.