From fa72dff74e68d5a3a6a93345cd43d60b5e1dd0b5 Mon Sep 17 00:00:00 2001 From: 4gn3s Date: Wed, 17 Feb 2016 11:46:09 +0100 Subject: [PATCH 01/11] fixed #99, includes tests --- pyethapp/rpc_client.py | 27 ++++++++++++++++++++++----- pyethapp/tests/test_rpc_client.py | 15 +++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/pyethapp/rpc_client.py b/pyethapp/rpc_client.py index de0552e0..fe1f25f5 100644 --- a/pyethapp/rpc_client.py +++ b/pyethapp/rpc_client.py @@ -23,6 +23,9 @@ # 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__) @@ -116,13 +119,14 @@ 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): + 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 self.privkey = privkey self._sender = sender self.port = port + self._default_tx_gas = default_tx_gas def __repr__(self): return '' % self.port @@ -141,6 +145,20 @@ def sender(self): 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: + 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()) + if self.print_communication: + print json.dumps(json.loads(request.serialize()), indent=2) + print reply def blocknumber(self): """ Return the most recent block. """ @@ -362,15 +380,14 @@ def send_transaction(self, sender, to, value=0, data='', startgas=0, locally sign the transaction. This requires an extended server implementation that accepts the variables v, r, and s. """ - 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) - if nonce is None: - nonce = self.nonce(sender) + if nonce is None: + nonce = self.nonce(sender) elif self.privkey: if sender != privtoaddr(self.privkey): raise ValueError('sender for a different privkey.') @@ -382,7 +399,7 @@ def send_transaction(self, sender, to, value=0, data='', startgas=0, nonce = 0 if not startgas: - startgas = self.gaslimit() - 1 + startgas = self.default_tx_gas 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 104874a8..720dc4b3 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -1,5 +1,7 @@ +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() @@ -18,3 +20,16 @@ def test_set_host(): client = JSONRPCClient(host) assert client.transport.endpoint == 'http://{}:{}'.format(host, client.port) assert client.transport.endpoint != 'http://{}:{}'.format(default_host, client.port) + +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 cb9702ff24d9f0e9e001d8cb1fcf027c12583cfb Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Wed, 13 Jul 2016 11:53:04 +0300 Subject: [PATCH 02/11] Added the tests for the rpc client --- pyethapp/config.py | 1 + pyethapp/jsonrpc.py | 5 +- pyethapp/tests/test_rpc_client.py | 113 +++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/pyethapp/config.py b/pyethapp/config.py index 2928bbd1..42c3dbf8 100644 --- a/pyethapp/config.py +++ b/pyethapp/config.py @@ -143,6 +143,7 @@ def set_config_param(config, s, strict=True): raise ValueError('Invalid config parameter') d = config for key in keys[:-1]: + key = key.strip() if strict and key not in d: raise KeyError('Unknown config option %s' % param) d = d.setdefault(key, {}) diff --git a/pyethapp/jsonrpc.py b/pyethapp/jsonrpc.py index e21770cd..45e62ede 100644 --- a/pyethapp/jsonrpc.py +++ b/pyethapp/jsonrpc.py @@ -33,6 +33,7 @@ import ethereum.bloom as bloom from accounts import Account from ipc_rpc import bind_unix_listener, serve +from ethereum.exceptions import InvalidTransaction from ethereum.utils import int32 @@ -399,6 +400,7 @@ def address_decoder(data): """Decode an address from hex with 0x prefix to 20 bytes.""" addr = data_decoder(data) if len(addr) not in (20, 0): + import pdb; pdb.set_trace() raise BadRequestError('Addresses must be 20 or 0 bytes long') return addr @@ -1189,8 +1191,9 @@ def call(self, data, block_id='pending'): tx.sender = sender try: + import pdb; pdb.set_trace() success, output = processblock.apply_transaction(test_block, tx) - except processblock.InvalidTransaction: + except InvalidTransaction as e: success = False # make sure we didn't change the real state snapshot_after = block.snapshot() diff --git a/pyethapp/tests/test_rpc_client.py b/pyethapp/tests/test_rpc_client.py index 720dc4b3..ad8d2169 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -1,11 +1,57 @@ from pyethapp.jsonrpc import quantity_decoder from pyethapp.rpc_client import JSONRPCClient +import pytest +from subprocess import Popen +import time +from pyethapp.jsonrpc import address_encoder +from ethereum.utils import zpad + +def prepare_rpc_tests(tmpdir): + rpc_tests = tmpdir.mkdir('testdata') + + assert Popen(['git', 'clone', 'https://github.com/ethereum/rpc-tests'], cwd=str(rpc_tests)).wait() == 0 + tests_dir = rpc_tests.join('rpc-tests') + assert Popen(['git', 'submodule', 'update', '--init', '--recursive'], cwd=str(tests_dir)).wait() == 0 + return tests_dir + + +@pytest.fixture() +def test_setup(request, tmpdir): + """ + start the test_app with `subprocess.Popen`, so we can kill it properly. + :param request: + :param tmpdir: + :return: + """ + rpc_tests_dir = prepare_rpc_tests(tmpdir) + + test_data = rpc_tests_dir.join('lib/tests/BlockchainTests/bcRPC_API_Test.json') + test_app = Popen([ + 'pyethapp', + '-d', str(tmpdir), + '-l:info,eth.chainservice:debug,jsonrpc:debug', + '-c jsonrpc.listen_port=8081', + '-c p2p.max_peers=0', + '-c p2p.min_peers=0', + 'blocktest', + str(test_data), + 'RPC_API_Test' + ]) + def fin(): + test_app.terminate() + request.addfinalizer(fin) + + time.sleep(60) + return (test_app, rpc_tests_dir) + def test_find_block(): + restore = JSONRPCClient.call JSONRPCClient.call = lambda self, cmd, num, flag: num client = JSONRPCClient() client.find_block(lambda x: x == '0x5') + JSONRPCClient.call = restore def test_default_host(): @@ -21,12 +67,75 @@ def test_set_host(): assert client.transport.endpoint == 'http://{}:{}'.format(host, client.port) assert client.transport.endpoint != 'http://{}:{}'.format(default_host, client.port) -def test_default_tx_gas(): - client = JSONRPCClient() +# The fixture takes much time to initialize, so the tests are grouped into one method +def test_client(test_setup): + (test_app, rpc_tests_dir) = test_setup + + client = JSONRPCClient(port=8081) + 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) + sender = client.sender + assert sender == '\xde\x0b)Vi\xa9\xfd\x93\xd5\xf2\x8d\x9e\xc8^@\xf4\xcbi{\xae' + + coinbase = client.coinbase + assert coinbase == '\xde\x0b)Vi\xa9\xfd\x93\xd5\xf2\x8d\x9e\xc8^@\xf4\xcbi{\xae' + + blocknumber = client.blocknumber() + assert blocknumber == 32 + + nonce = client.nonce(sender) + assert nonce == 0 + + balance1 = client.balance(sender) + assert balance1 == 5156250000000000000 + + gaslimit = client.gaslimit() + assert gaslimit == 3141592 + + lastgasprice = client.lastgasprice() + assert lastgasprice == 1 + + balance2 = client.balance('\xff' * 20) + assert balance2 == 0 + + #sender = address_encoder("06295ee1b4f6dd65047762f924ecd367c17eabf8f") + import pdb; pdb.set_trace() + # sender = sender.decode("hex") + #sender = zpad(sender, 20) + fid = client.new_filter('pending', 'pending') + # assert fid == 0 + + # changes = client.filter_changes(fid) + # assert len(changes) + + # accs = client.call('eth_accounts') + # sender = accs[0] + # res_est = client.eth_sendTransaction(nonce, sender, address_encoder('\xff' * 20), 1) + # assert 'result' in res_est.keys() + + res_call = client.eth_call(sender, '\xff' * 20, 1) + assert 'result' in res_call.keys() + + res_st = client.send_transaction(sender, address_encoder('\xff' * 20), 1) + assert 'result' in res_st.keys() + + solidity_code = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }" + + import ethereum._solidity + s = ethereum._solidity.get_solidity() + if s is None: + pytest.xfail("solidity not installed, not tested") + else: + abi = s.mk_full_signature(solidity_code) + abic = client.new_abi_contract(abi, sender) + mult = abic.multiply(11111111) + assert mult == 77777777 + pass + + def test_default_tx_gas_assigned(): default_gas = 12345 From 74e9cc4965a31326d2ff63ca1a82d225ac24cbed Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Mon, 18 Jul 2016 12:35:24 +0300 Subject: [PATCH 03/11] Updated the rpc_client tests --- pyethapp/tests/test_rpc_client.py | 48 +++++++++++++------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/pyethapp/tests/test_rpc_client.py b/pyethapp/tests/test_rpc_client.py index ad8d2169..13501713 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -4,7 +4,10 @@ from subprocess import Popen import time from pyethapp.jsonrpc import address_encoder -from ethereum.utils import zpad +#from ethereum.utils import zpad +from ethereum.tester import a0, a1, a2, a3, a4, a5, a6,a7, a8, a9, k0 +from pyethapp.accounts import Account +from ethereum import utils def prepare_rpc_tests(tmpdir): rpc_tests = tmpdir.mkdir('testdata') @@ -45,7 +48,6 @@ def fin(): return (test_app, rpc_tests_dir) - def test_find_block(): restore = JSONRPCClient.call JSONRPCClient.call = lambda self, cmd, num, flag: num @@ -70,7 +72,6 @@ def test_set_host(): # The fixture takes much time to initialize, so the tests are grouped into one method def test_client(test_setup): (test_app, rpc_tests_dir) = test_setup - client = JSONRPCClient(port=8081) genesis_block_info = client.call('eth_getBlockByNumber', 'earliest', False) @@ -100,41 +101,32 @@ def test_client(test_setup): balance2 = client.balance('\xff' * 20) assert balance2 == 0 - - #sender = address_encoder("06295ee1b4f6dd65047762f924ecd367c17eabf8f") - import pdb; pdb.set_trace() - # sender = sender.decode("hex") - #sender = zpad(sender, 20) fid = client.new_filter('pending', 'pending') - # assert fid == 0 - - # changes = client.filter_changes(fid) - # assert len(changes) + assert fid == 0 + # The following tests require an account with a positive balance # accs = client.call('eth_accounts') # sender = accs[0] # res_est = client.eth_sendTransaction(nonce, sender, address_encoder('\xff' * 20), 1) # assert 'result' in res_est.keys() - res_call = client.eth_call(sender, '\xff' * 20, 1) - assert 'result' in res_call.keys() - - res_st = client.send_transaction(sender, address_encoder('\xff' * 20), 1) - assert 'result' in res_st.keys() + # res_call = client.eth_call(utils.encode_hex(a0), '\xff' * 20, 0) + # assert 'result' in res_call.keys() - solidity_code = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }" + # res_st = client.send_transaction(sender, address_encoder('\xff' * 20), 1) + # assert 'result' in res_st.keys() - import ethereum._solidity - s = ethereum._solidity.get_solidity() - if s is None: - pytest.xfail("solidity not installed, not tested") - else: - abi = s.mk_full_signature(solidity_code) - abic = client.new_abi_contract(abi, sender) - mult = abic.multiply(11111111) - assert mult == 77777777 - pass + # solidity_code = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }" + # import ethereum._solidity + # s = ethereum._solidity.get_solidity() + # if s is None: + # pytest.xfail("solidity not installed, not tested") + # else: + # abi = s.mk_full_signature(solidity_code) + # abic = client.new_abi_contract(abi, sender) + # mult = abic.multiply(11111111) + # assert mult == 77777777 def test_default_tx_gas_assigned(): From 500988223929b8ab34ebae5d5f1821fc2aa1d310 Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Mon, 18 Jul 2016 14:23:13 +0300 Subject: [PATCH 04/11] Minor fix --- pyethapp/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyethapp/config.py b/pyethapp/config.py index 42c3dbf8..2928bbd1 100644 --- a/pyethapp/config.py +++ b/pyethapp/config.py @@ -143,7 +143,6 @@ def set_config_param(config, s, strict=True): raise ValueError('Invalid config parameter') d = config for key in keys[:-1]: - key = key.strip() if strict and key not in d: raise KeyError('Unknown config option %s' % param) d = d.setdefault(key, {}) From 07950860ce85e384a954c80a5333eaa81c2920ba Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Mon, 18 Jul 2016 17:47:29 +0300 Subject: [PATCH 05/11] Revert "Minor fix" This reverts commit 500988223929b8ab34ebae5d5f1821fc2aa1d310. --- pyethapp/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyethapp/config.py b/pyethapp/config.py index 2928bbd1..42c3dbf8 100644 --- a/pyethapp/config.py +++ b/pyethapp/config.py @@ -143,6 +143,7 @@ def set_config_param(config, s, strict=True): raise ValueError('Invalid config parameter') d = config for key in keys[:-1]: + key = key.strip() if strict and key not in d: raise KeyError('Unknown config option %s' % param) d = d.setdefault(key, {}) From 339de86b5d016cea578419fcfcda4d471225aaf2 Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Mon, 18 Jul 2016 17:49:05 +0300 Subject: [PATCH 06/11] Added path checks --- pyethapp/tests/test_rpc_client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyethapp/tests/test_rpc_client.py b/pyethapp/tests/test_rpc_client.py index 13501713..91d9486b 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -4,9 +4,6 @@ from subprocess import Popen import time from pyethapp.jsonrpc import address_encoder -#from ethereum.utils import zpad -from ethereum.tester import a0, a1, a2, a3, a4, a5, a6,a7, a8, a9, k0 -from pyethapp.accounts import Account from ethereum import utils def prepare_rpc_tests(tmpdir): @@ -14,7 +11,11 @@ def prepare_rpc_tests(tmpdir): assert Popen(['git', 'clone', 'https://github.com/ethereum/rpc-tests'], cwd=str(rpc_tests)).wait() == 0 tests_dir = rpc_tests.join('rpc-tests') + import os.path + fpath = str(tests_dir.join('lib/config.js')) + assert os.path.isfile(fpath) assert Popen(['git', 'submodule', 'update', '--init', '--recursive'], cwd=str(tests_dir)).wait() == 0 + assert os.path.isfile(str(tests_dir.join('lib/tests/BlockchainTests/bcRPC_API_Test.json')).decode('unicode-escape')) return tests_dir @@ -102,7 +103,6 @@ def test_client(test_setup): balance2 = client.balance('\xff' * 20) assert balance2 == 0 fid = client.new_filter('pending', 'pending') - assert fid == 0 # The following tests require an account with a positive balance # accs = client.call('eth_accounts') From 297d75280ea1ad25036498799686b5709ddbd498 Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Mon, 18 Jul 2016 18:55:26 +0300 Subject: [PATCH 07/11] Added executable check --- pyethapp/tests/test_rpc_client.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyethapp/tests/test_rpc_client.py b/pyethapp/tests/test_rpc_client.py index 91d9486b..d4b2cb63 100644 --- a/pyethapp/tests/test_rpc_client.py +++ b/pyethapp/tests/test_rpc_client.py @@ -6,6 +6,19 @@ from pyethapp.jsonrpc import address_encoder from ethereum import utils +def executable_installed(program): + import os + + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + def prepare_rpc_tests(tmpdir): rpc_tests = tmpdir.mkdir('testdata') @@ -30,6 +43,7 @@ def test_setup(request, tmpdir): rpc_tests_dir = prepare_rpc_tests(tmpdir) test_data = rpc_tests_dir.join('lib/tests/BlockchainTests/bcRPC_API_Test.json') + assert executable_installed('pyethapp') test_app = Popen([ 'pyethapp', '-d', str(tmpdir), From 9124a4136ae57510f5ec7f1b1911a8b96604a283 Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Wed, 20 Jul 2016 12:13:36 +0300 Subject: [PATCH 08/11] Updated the setup mode to install so that the pyethapp executable can be executed for testing the rpc client --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 65c89fc1..bfab1839 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ install: - pip install coveralls readme_renderer script: - coverage run --source pyethapp setup.py test -- python setup.py check --restructuredtext --strict +- python setup.py install --restructuredtext --strict after_success: - coveralls env: From 82a62741365327a0c48f35882295609379686cea Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Wed, 20 Jul 2016 12:49:32 +0300 Subject: [PATCH 09/11] Revert "Updated the setup mode to install so that the pyethapp executable can be executed for testing the rpc client" This reverts commit 9124a4136ae57510f5ec7f1b1911a8b96604a283. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bfab1839..65c89fc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ install: - pip install coveralls readme_renderer script: - coverage run --source pyethapp setup.py test -- python setup.py install --restructuredtext --strict +- python setup.py check --restructuredtext --strict after_success: - coveralls env: From e2a028adb9fef3d9d53f6185bba70585413448bf Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Wed, 20 Jul 2016 12:52:10 +0300 Subject: [PATCH 10/11] Added the "setup.py install" command so that the pyethapp executable can be executed for testing the rpc client --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 65c89fc1..bd3c82c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ install: - pip install -r requirements.txt - pip install coveralls readme_renderer script: +- python setup.py install - coverage run --source pyethapp setup.py test - python setup.py check --restructuredtext --strict after_success: From 5616f1f4eab5ce7188895abeb6e13fe89e238032 Mon Sep 17 00:00:00 2001 From: RomanZacharia Date: Fri, 26 Aug 2016 10:41:42 +0300 Subject: [PATCH 11/11] Cleanup --- pyethapp/jsonrpc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyethapp/jsonrpc.py b/pyethapp/jsonrpc.py index 45e62ede..95a4c69a 100644 --- a/pyethapp/jsonrpc.py +++ b/pyethapp/jsonrpc.py @@ -400,7 +400,6 @@ def address_decoder(data): """Decode an address from hex with 0x prefix to 20 bytes.""" addr = data_decoder(data) if len(addr) not in (20, 0): - import pdb; pdb.set_trace() raise BadRequestError('Addresses must be 20 or 0 bytes long') return addr @@ -1191,7 +1190,6 @@ def call(self, data, block_id='pending'): tx.sender = sender try: - import pdb; pdb.set_trace() success, output = processblock.apply_transaction(test_block, tx) except InvalidTransaction as e: success = False