Skip to content

Latest commit

 

History

History
371 lines (293 loc) · 13.7 KB

README.rst

File metadata and controls

371 lines (293 loc) · 13.7 KB

This is the Python core library of the Ethereum project.

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

Installation:

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 storaes

  • 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:

[{"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:

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:

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

To decode:

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

Or:

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:

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