From 75a2572f85a74a8d4c704a4430abfef919e0edaf Mon Sep 17 00:00:00 2001 From: 4gn3s Date: Wed, 17 Feb 2016 11:46:09 +0100 Subject: [PATCH 1/2] fixed #99, includes tests --- pyethapp/rpc_client.py | 24 ++++++++++++++++-------- pyethapp/tests/test_rpc_client.py | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/pyethapp/rpc_client.py b/pyethapp/rpc_client.py index 76b92be2..28b7ba9c 100644 --- a/pyethapp/rpc_client.py +++ b/pyethapp/rpc_client.py @@ -14,6 +14,8 @@ z_address = '\x00' * 20 +DEFAULT_TX_GAS = 3141591 # genesis block gasLimit - 1 + def address20(address): if address == '': @@ -56,13 +58,14 @@ class JSONRPCClientReplyError(Exception): class JSONRPCClient(object): protocol = JSONRPCProtocol() - def __init__(self, port=4000, print_communication=True, privkey=None, sender=None): + def __init__(self, port=4000, print_communication=True, privkey=None, sender=None, default_tx_gas=None): "specify privkey for local signing" self.transport = HttpPostClientTransport('http://127.0.0.1:{}'.format(port)) self.print_communication = print_communication self.privkey = privkey self._sender = sender self.port = port + self._default_tx_gas = default_tx_gas def __repr__(self): return '' % self.port @@ -75,6 +78,13 @@ def sender(self): self._sender = self.coinbase return self._sender + @property + def default_tx_gas(self): + if self._default_tx_gas: + return self._default_tx_gas + else: + return DEFAULT_TX_GAS + def call(self, method, *args): request = self.protocol.create_request(method, args) reply = self.transport.send_message(request.serialize()) @@ -175,7 +185,8 @@ def balance(self, account): return b def gaslimit(self): - return quantity_decoder(self.call('eth_gasLimit')) + latest_block_info = self.call('eth_getBlockByNumber', 'latest', False) + return quantity_decoder(latest_block_info['gasLimit']) def lastgasprice(self): return quantity_decoder(self.call('eth_lastGasPrice')) @@ -188,15 +199,12 @@ def send_transaction(self, sender, to, value=0, data='', startgas=0, _sender = sender sender = privtoaddr(self.privkey) assert sender == _sender - # fetch nonce - nonce = nonce if nonce is not None else self.nonce(sender) - if nonce is None: - nonce = 0 - + # fetch nonce + nonce = nonce if nonce is not None else self.nonce(sender) assert sender if not startgas: - startgas = quantity_decoder(self.call('eth_gasLimit')) - 1 + startgas = self.default_tx_gas # create transaction tx = Transaction(nonce, gasprice, startgas, to=to, value=value, data=data) diff --git a/pyethapp/tests/test_rpc_client.py b/pyethapp/tests/test_rpc_client.py index 4c2b5736..a68a094e 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -1,6 +1,21 @@ +from pyethapp.jsonrpc import quantity_decoder from pyethapp.rpc_client import JSONRPCClient + def test_find_block(): JSONRPCClient.call = lambda self, cmd, num, flag: num client = JSONRPCClient() client.find_block(lambda x: x == '0x5') + + +def test_default_tx_gas(): + client = JSONRPCClient() + genesis_block_info = client.call('eth_getBlockByNumber', 'earliest', False) + genesis_gas_limit = quantity_decoder(genesis_block_info['gasLimit']) + assert client.default_tx_gas == (genesis_gas_limit - 1) + + +def test_default_tx_gas_assigned(): + default_gas = 12345 + client = JSONRPCClient(default_tx_gas=default_gas) + assert client.default_tx_gas == default_gas From 02397d1aaf55fcb6671799810653a6f54713d7ad Mon Sep 17 00:00:00 2001 From: Agnieszka Date: Wed, 13 Jul 2016 15:20:22 +0200 Subject: [PATCH 2/2] tests fixed --- pyethapp/rpc_client.py | 45 +++------- pyethapp/tests/conftest.py | 131 ++++++++++++++++++++++++++++++ pyethapp/tests/test_jsonrpc.py | 128 ++--------------------------- pyethapp/tests/test_rpc_client.py | 5 +- requirements.txt | 1 + 5 files changed, 151 insertions(+), 159 deletions(-) diff --git a/pyethapp/rpc_client.py b/pyethapp/rpc_client.py index 78a50b7b..4fe9c813 100644 --- a/pyethapp/rpc_client.py +++ b/pyethapp/rpc_client.py @@ -9,7 +9,8 @@ from ethereum.keys import privtoaddr from ethereum.transactions import Transaction from ethereum.utils import denoms, int_to_big_endian, big_endian_to_int, normalize_address -from ethereum._solidity import solidity_unresolved_symbols, solidity_library_symbol, solidity_resolve_symbols +from ethereum._solidity import solidity_unresolved_symbols, solidity_library_symbol, \ + solidity_resolve_symbols from tinyrpc.protocols.jsonrpc import JSONRPCErrorResponse, JSONRPCSuccessResponse from tinyrpc.protocols.jsonrpc import JSONRPCProtocol from tinyrpc.transports.http import HttpPostClientTransport @@ -23,10 +24,8 @@ # pylint: disable=invalid-name,too-many-arguments,too-few-public-methods # The number of arguments an it's names are determined by the JSON-RPC spec - DEFAULT_TX_GAS = 3141591 # genesis block gasLimit - 1 - z_address = '\x00' * 20 log = logging.getLogger(__name__) @@ -120,7 +119,8 @@ class JSONRPCClientReplyError(Exception): class JSONRPCClient(object): protocol = JSONRPCProtocol() - def __init__(self, host='127.0.0.1', port=4000, print_communication=True, privkey=None, sender=None, default_tx_gas=None): + def __init__(self, host='127.0.0.1', port=4000, print_communication=True, privkey=None, + sender=None, default_tx_gas=None): "specify privkey for local signing" self.transport = HttpPostClientTransport('http://{}:{}'.format(host, port)) self.print_communication = print_communication @@ -142,6 +142,11 @@ def sender(self): return self._sender + @property + def coinbase(self): + """ Return the client coinbase address. """ + return address_decoder(self.call('eth_coinbase')) + @property def default_tx_gas(self): if self._default_tx_gas: @@ -149,17 +154,6 @@ def default_tx_gas(self): else: return DEFAULT_TX_GAS - def call(self, method, *args): - request = self.protocol.create_request(method, args) - reply = self.transport.send_message(request.serialize()) - if self.print_communication: - print json.dumps(json.loads(request.serialize()), indent=2) - print reply - - def coinbase(self): - """ Return the client coinbase address. """ - return address_decoder(self.call('eth_coinbase')) - def blocknumber(self): """ Return the most recent block. """ return quantity_decoder(self.call('eth_blockNumber')) @@ -185,7 +179,8 @@ def balance(self, account): return quantity_decoder(res) def gaslimit(self): - return quantity_decoder(self.call('eth_gasLimit')) + latest_block_info = self.call('eth_getBlockByNumber', 'latest', False) + return quantity_decoder(latest_block_info['gasLimit']) def lastgasprice(self): return quantity_decoder(self.call('eth_lastGasPrice')) @@ -375,28 +370,16 @@ def call(self, method, *args): def send_transaction(self, sender, to, value=0, data='', startgas=0, gasprice=10 * denoms.szabo, nonce=None): """ Helper to send signed messages. - -<<<<<<< HEAD - def gaslimit(self): - latest_block_info = self.call('eth_getBlockByNumber', 'latest', False) - return quantity_decoder(latest_block_info['gasLimit']) -======= This method will use the `privkey` provided in the constructor to locally sign the transaction. This requires an extended server implementation that accepts the variables v, r, and s. """ ->>>>>>> develop if not self.privkey and not sender: raise ValueError('Either privkey or sender needs to be supplied.') if self.privkey and not sender: sender = privtoaddr(self.privkey) -<<<<<<< HEAD - assert sender == _sender - # fetch nonce - nonce = nonce if nonce is not None else self.nonce(sender) -======= if nonce is None: nonce = self.nonce(sender) @@ -409,14 +392,9 @@ def gaslimit(self): else: if nonce is None: nonce = 0 ->>>>>>> develop if not startgas: -<<<<<<< HEAD startgas = self.default_tx_gas -======= - startgas = self.gaslimit() - 1 ->>>>>>> develop tx = Transaction(nonce, gasprice, startgas, to=to, value=value, data=data) @@ -652,7 +630,6 @@ def __call__(self, *args, **kargs): class ContractProxy(object): """ Exposes a smart contract as a python object. - Contract calls can be made directly in this object, all the functions will be exposed with the equivalent api and will perform the argument translation. diff --git a/pyethapp/tests/conftest.py b/pyethapp/tests/conftest.py index e69de29b..d26ca046 100644 --- a/pyethapp/tests/conftest.py +++ b/pyethapp/tests/conftest.py @@ -0,0 +1,131 @@ +from itertools import count +import gevent +import gc + +import pytest +import ethereum +import ethereum.config +import ethereum.keys +from ethereum.ethpow import mine +from ethereum import tester +from ethereum.slogging import get_logger +from devp2p.peermanager import PeerManager +import ethereum._solidity + +from pyethapp.accounts import Account, AccountsService, mk_random_privkey +from pyethapp.app import EthApp +from pyethapp.config import update_config_with_defaults, get_default_config +from pyethapp.db_service import DBService +from pyethapp.eth_service import ChainService +from pyethapp.jsonrpc import JSONRPCServer +from pyethapp.profiles import PROFILES +from pyethapp.pow_service import PoWService + + +log = get_logger('test.jsonrpc') + + +@pytest.fixture(params=[0, + PROFILES['testnet']['eth']['block']['ACCOUNT_INITIAL_NONCE']]) +def test_app(request, tmpdir): + + class TestApp(EthApp): + + def start(self): + super(TestApp, self).start() + log.debug('adding test accounts') + # high balance account + self.services.accounts.add_account(Account.new('', tester.keys[0]), store=False) + # low balance account + self.services.accounts.add_account(Account.new('', tester.keys[1]), store=False) + # locked account + locked_account = Account.new('', tester.keys[2]) + locked_account.lock() + self.services.accounts.add_account(locked_account, store=False) + assert set(acct.address for acct in self.services.accounts) == set(tester.accounts[:3]) + + def mine_next_block(self): + """Mine until a valid nonce is found. + + :returns: the new head + """ + log.debug('mining next block') + block = self.services.chain.chain.head_candidate + delta_nonce = 10 ** 6 + for start_nonce in count(0, delta_nonce): + bin_nonce, mixhash = mine(block.number, block.difficulty, block.mining_hash, + start_nonce=start_nonce, rounds=delta_nonce) + if bin_nonce: + break + self.services.pow.recv_found_nonce(bin_nonce, mixhash, block.mining_hash) + log.debug('block mined') + assert self.services.chain.chain.head.difficulty == 1 + return self.services.chain.chain.head + + def rpc_request(self, method, *args): + """Simulate an incoming JSON RPC request and return the result. + + Example:: + + >>> assert test_app.rpc_request('eth_getBalance', '0x' + 'ff' * 20) == '0x0' + + """ + log.debug('simulating rpc request', method=method) + method = self.services.jsonrpc.dispatcher.get_method(method) + res = method(*args) + log.debug('got response', response=res) + return res + + config = { + 'data_dir': str(tmpdir), + 'db': {'implementation': 'EphemDB'}, + 'pow': {'activated': False}, + 'p2p': { + 'min_peers': 0, + 'max_peers': 0, + 'listen_port': 29873 + }, + 'node': {'privkey_hex': mk_random_privkey().encode('hex')}, + 'discovery': { + 'boostrap_nodes': [], + 'listen_port': 29873 + }, + 'eth': { + 'block': { # reduced difficulty, increased gas limit, allocations to test accounts + 'ACCOUNT_INITIAL_NONCE': request.param, + 'GENESIS_DIFFICULTY': 1, + 'BLOCK_DIFF_FACTOR': 2, # greater than difficulty, thus difficulty is constant + 'GENESIS_GAS_LIMIT': 3141592, + 'GENESIS_INITIAL_ALLOC': { + tester.accounts[0].encode('hex'): {'balance': 10 ** 24}, + tester.accounts[1].encode('hex'): {'balance': 1}, + tester.accounts[2].encode('hex'): {'balance': 10 ** 24}, + } + } + }, + 'jsonrpc': {'listen_port': 4488, 'listen_host': '127.0.0.1'} + } + services = [DBService, AccountsService, PeerManager, ChainService, PoWService, JSONRPCServer] + update_config_with_defaults(config, get_default_config([TestApp] + services)) + update_config_with_defaults(config, {'eth': {'block': ethereum.config.default_config}}) + app = TestApp(config) + for service in services: + service.register_with_app(app) + + def fin(): + log.debug('stopping test app') + for service in app.services: + gevent.sleep(.1) + try: + app.services[service].stop() + except Exception as e: + log.DEV(str(e), exc_info=e) + pass + app.stop() + gevent.killall(task for task in gc.get_objects() if isinstance(task, gevent.Greenlet)) + + request.addfinalizer(fin) + + log.debug('starting test app') + app.start() + return app diff --git a/pyethapp/tests/test_jsonrpc.py b/pyethapp/tests/test_jsonrpc.py index 87f307e5..4a0b5b24 100644 --- a/pyethapp/tests/test_jsonrpc.py +++ b/pyethapp/tests/test_jsonrpc.py @@ -1,31 +1,19 @@ # -*- coding: utf8 -*- import os from os import path -from itertools import count -import gevent -import gc -import pytest -import rlp -import serpent import ethereum +import ethereum._solidity import ethereum.config import ethereum.keys -from ethereum.ethpow import mine +import pytest +import rlp +import serpent from ethereum import tester from ethereum.slogging import get_logger -from devp2p.peermanager import PeerManager -import ethereum._solidity -from pyethapp.accounts import Account, AccountsService, mk_random_privkey -from pyethapp.app import EthApp -from pyethapp.config import update_config_with_defaults, get_default_config -from pyethapp.db_service import DBService -from pyethapp.eth_service import ChainService -from pyethapp.jsonrpc import Compilers, JSONRPCServer, quantity_encoder, address_encoder, data_decoder, \ +from pyethapp.jsonrpc import Compilers, quantity_encoder, address_encoder, data_decoder, \ data_encoder, default_gasprice, default_startgas -from pyethapp.profiles import PROFILES -from pyethapp.pow_service import PoWService ethereum.keys.PBKDF2_CONSTANTS['c'] = 100 # faster key derivation log = get_logger('test.jsonrpc') # pylint: disable=invalid-name @@ -119,112 +107,6 @@ def test_compile_solidity(): assert compiler_info['abiDefinition'] == info['abiDefinition'] -@pytest.fixture(params=[0, - PROFILES['testnet']['eth']['block']['ACCOUNT_INITIAL_NONCE']]) -def test_app(request, tmpdir): - - class TestApp(EthApp): - - def start(self): - super(TestApp, self).start() - log.debug('adding test accounts') - # high balance account - self.services.accounts.add_account(Account.new('', tester.keys[0]), store=False) - # low balance account - self.services.accounts.add_account(Account.new('', tester.keys[1]), store=False) - # locked account - locked_account = Account.new('', tester.keys[2]) - locked_account.lock() - self.services.accounts.add_account(locked_account, store=False) - assert set(acct.address for acct in self.services.accounts) == set(tester.accounts[:3]) - - def mine_next_block(self): - """Mine until a valid nonce is found. - - :returns: the new head - """ - log.debug('mining next block') - block = self.services.chain.chain.head_candidate - delta_nonce = 10 ** 6 - for start_nonce in count(0, delta_nonce): - bin_nonce, mixhash = mine(block.number, block.difficulty, block.mining_hash, - start_nonce=start_nonce, rounds=delta_nonce) - if bin_nonce: - break - self.services.pow.recv_found_nonce(bin_nonce, mixhash, block.mining_hash) - log.debug('block mined') - assert self.services.chain.chain.head.difficulty == 1 - return self.services.chain.chain.head - - def rpc_request(self, method, *args): - """Simulate an incoming JSON RPC request and return the result. - - Example:: - - >>> assert test_app.rpc_request('eth_getBalance', '0x' + 'ff' * 20) == '0x0' - - """ - log.debug('simulating rpc request', method=method) - method = self.services.jsonrpc.dispatcher.get_method(method) - res = method(*args) - log.debug('got response', response=res) - return res - - config = { - 'data_dir': str(tmpdir), - 'db': {'implementation': 'EphemDB'}, - 'pow': {'activated': False}, - 'p2p': { - 'min_peers': 0, - 'max_peers': 0, - 'listen_port': 29873 - }, - 'node': {'privkey_hex': mk_random_privkey().encode('hex')}, - 'discovery': { - 'boostrap_nodes': [], - 'listen_port': 29873 - }, - 'eth': { - 'block': { # reduced difficulty, increased gas limit, allocations to test accounts - 'ACCOUNT_INITIAL_NONCE': request.param, - 'GENESIS_DIFFICULTY': 1, - 'BLOCK_DIFF_FACTOR': 2, # greater than difficulty, thus difficulty is constant - 'GENESIS_GAS_LIMIT': 3141592, - 'GENESIS_INITIAL_ALLOC': { - tester.accounts[0].encode('hex'): {'balance': 10 ** 24}, - tester.accounts[1].encode('hex'): {'balance': 1}, - tester.accounts[2].encode('hex'): {'balance': 10 ** 24}, - } - } - }, - 'jsonrpc': {'listen_port': 4488, 'listen_host': '127.0.0.1'} - } - services = [DBService, AccountsService, PeerManager, ChainService, PoWService, JSONRPCServer] - update_config_with_defaults(config, get_default_config([TestApp] + services)) - update_config_with_defaults(config, {'eth': {'block': ethereum.config.default_config}}) - app = TestApp(config) - for service in services: - service.register_with_app(app) - - def fin(): - log.debug('stopping test app') - for service in app.services: - gevent.sleep(.1) - try: - app.services[service].stop() - except Exception as e: - log.DEV(str(e), exc_info=e) - pass - app.stop() - gevent.killall(task for task in gc.get_objects() if isinstance(task, gevent.Greenlet)) - - request.addfinalizer(fin) - - log.debug('starting test app') - app.start() - return app - - def test_send_transaction(test_app): chain = test_app.services.chain.chain assert chain.head_candidate.get_balance('\xff' * 20) == 0 diff --git a/pyethapp/tests/test_rpc_client.py b/pyethapp/tests/test_rpc_client.py index 2059a936..ddda4b2b 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -8,8 +8,9 @@ def test_find_block(): client.find_block(lambda x: x == '0x5') -def test_default_tx_gas(): - client = JSONRPCClient() +def test_default_tx_gas(test_app): + JSONRPCClient.call = lambda self, cmd, block_id, flag: test_app.rpc_request(cmd, block_id, flag) + client = JSONRPCClient(port=test_app.config['jsonrpc']['listen_port']) genesis_block_info = client.call('eth_getBlockByNumber', 'earliest', False) genesis_gas_limit = quantity_decoder(genesis_block_info['gasLimit']) assert client.default_tx_gas == (genesis_gas_limit - 1) diff --git a/requirements.txt b/requirements.txt index 7d9d38f0..b7405aa2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,4 @@ ethereum>=1.3.5 pbkdf2 scrypt pexpect +pytest