diff --git a/.gitignore b/.gitignore index 539da74..6ef1d14 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ *.py[co] +*.bin +.cache/ +.tern-port +.tox/ +ethjsonrpc.egg-info/ +geth.log +**/geth +**/history +**/keystore diff --git a/README.rst b/README.rst index 5db894e..0245054 100644 --- a/README.rst +++ b/README.rst @@ -177,7 +177,6 @@ Implemented JSON-RPC methods * eth_getTransactionReceipt * eth_getUncleByBlockHashAndIndex * eth_getUncleByBlockNumberAndIndex -* eth_getCompilers * eth_compileSolidity * eth_compileLLL * eth_compileSerpent @@ -191,10 +190,6 @@ Implemented JSON-RPC methods * eth_getWork * eth_submitWork * eth_submitHashrate -* db_putString -* db_getString -* db_putHex -* db_getHex * shh_version * shh_post * shh_newIdentity diff --git a/ethjsonrpc/client.py b/ethjsonrpc/client.py index 89a9edb..81381d1 100644 --- a/ethjsonrpc/client.py +++ b/ethjsonrpc/client.py @@ -113,7 +113,7 @@ def call(self, address, sig, args, result_types): transaction (useful for reading data) ''' data = self._encode_function(sig, args) - data_hex = data.encode('hex') + data_hex = '0x'+data.encode('hex') response = self.eth_call(to_address=address, data=data_hex) return decode_abi(result_types, response[2:].decode('hex')) @@ -125,7 +125,7 @@ def call_with_transaction(self, from_, address, sig, args, gas=None, gas_price=N gas = gas or self.DEFAULT_GAS_PER_TX gas_price = gas_price or self.DEFAULT_GAS_PRICE data = self._encode_function(sig, args) - data_hex = data.encode('hex') + data_hex = '0x'+data.encode('hex') return self.eth_sendTransaction(from_address=from_, to_address=address, data=data_hex, gas=gas, gas_price=gas_price, value=value) @@ -246,7 +246,10 @@ def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST): ''' address = address or self.eth_coinbase() block = validate_block(block) - return hex_to_dec(self._call('eth_getBalance', [address, block])) + bal = self._call('eth_getBalance', [address, block]) + if bal and bal != '': + return hex_to_dec(bal) + return 0 def eth_getStorageAt(self, address=None, position=0, block=BLOCK_TAG_LATEST): ''' @@ -264,7 +267,10 @@ def eth_getTransactionCount(self, address, block=BLOCK_TAG_LATEST): TESTED ''' block = validate_block(block) - return hex_to_dec(self._call('eth_getTransactionCount', [address, block])) + hex = self._call('eth_getTransactionCount', [address, block]) + if hex != None: + return hex_to_dec(hex) + return 0 def eth_getBlockTransactionCountByHash(self, block_hash): ''' @@ -466,38 +472,6 @@ def eth_getUncleByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): block = validate_block(block) return self._call('eth_getUncleByBlockNumberAndIndex', [block, hex(index)]) - def eth_getCompilers(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcompilers - - TESTED - ''' - return self._call('eth_getCompilers') - - def eth_compileSolidity(self, code): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compilesolidity - - TESTED - ''' - return self._call('eth_compileSolidity', [code]) - - def eth_compileLLL(self, code): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compilelll - - N/A - ''' - return self._call('eth_compileLLL', [code]) - - def eth_compileSerpent(self, code): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compileserpent - - N/A - ''' - return self._call('eth_compileSerpent', [code]) - def eth_newFilter(self, from_block=BLOCK_TAG_LATEST, to_block=BLOCK_TAG_LATEST, address=None, topics=None): ''' https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter @@ -584,44 +558,6 @@ def eth_submitHashrate(self, hash_rate, client_id): ''' return self._call('eth_submitHashrate', [hex(hash_rate), client_id]) - def db_putString(self, db_name, key, value): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#db_putstring - - TESTED - ''' - warnings.warn('deprecated', DeprecationWarning) - return self._call('db_putString', [db_name, key, value]) - - def db_getString(self, db_name, key): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#db_getstring - - TESTED - ''' - warnings.warn('deprecated', DeprecationWarning) - return self._call('db_getString', [db_name, key]) - - def db_putHex(self, db_name, key, value): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#db_puthex - - TESTED - ''' - if not value.startswith('0x'): - value = '0x{}'.format(value) - warnings.warn('deprecated', DeprecationWarning) - return self._call('db_putHex', [db_name, key, value]) - - def db_getHex(self, db_name, key): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#db_gethex - - TESTED - ''' - warnings.warn('deprecated', DeprecationWarning) - return self._call('db_getHex', [db_name, key]) - def shh_version(self): ''' https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_version diff --git a/ethjsonrpc/utils.py b/ethjsonrpc/utils.py index e4defc2..57d9146 100644 --- a/ethjsonrpc/utils.py +++ b/ethjsonrpc/utils.py @@ -5,7 +5,9 @@ def hex_to_dec(x): ''' Convert hex to decimal ''' - return int(x, 16) + if x: + return int(x, 16) + return 0 def clean_hex(d): diff --git a/setup.py b/setup.py index e98903f..b883c6d 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='ethjsonrpc', - version='0.3.0', + version='0.4.0', description='Ethereum JSON-RPC client', long_description=open('README.rst').read(), author='ConsenSys', diff --git a/test.py b/test.py deleted file mode 100644 index 3518e88..0000000 --- a/test.py +++ /dev/null @@ -1,140 +0,0 @@ -from ethjsonrpc import EthJsonRpc - -methods = [ - 'web3_clientVersion', - 'net_version', - 'net_peerCount', - 'net_listening', - 'eth_protocolVersion', - 'eth_syncing', - 'eth_coinbase', - 'eth_mining', - 'eth_hashrate', - 'eth_gasPrice', - 'eth_accounts', - 'eth_blockNumber', - 'eth_getCompilers', - 'eth_newPendingTransactionFilter', - 'eth_getWork', -# 'shh_version', -# 'shh_newIdentity', -# 'shh_newGroup', -] - -c = EthJsonRpc() -print len(methods) -for m in methods: - meth = getattr(c, m) - result = meth() - print '%s: %s (%s)' % (m, result, type(result)) - -################################################################################ -print '*' * 80 - -addr = '0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe' -for x in ['earliest', 'latest', 'pending', 150000]: - result = c.eth_getTransactionCount(addr, x) - print 'eth_getTransactionCount: %s (%s)' % (result, type(result)) - -b = (231301, '0x9476018748ba1dae5bdf5e3725f8966df1fa127d49f58e66f621bf6868a23c85') -result = c.eth_getBlockTransactionCountByHash(b[1]) -print 'eth_getBlockTransactionCountByHash: %s (%s)' % (result, type(result)) - -for x in ['earliest', 'latest', 'pending', b[0]]: - result = c.eth_getBlockTransactionCountByNumber(x) - print 'eth_getBlockTransactionCountByNumber: %s (%s)' % (result, type(result)) - - -b = (199583, '0x19d761c6f944eefe91ad70b9aff3d2d76c972e5bb68c443eea7c0eaa144cef9f') -result = c.eth_getUncleCountByBlockHash(b[1]) -print 'eth_getUncleCountByBlockHash: %s (%s)' % (result, type(result)) - -for x in ['earliest', 'latest', 'pending', b[0]]: - result = c.eth_getUncleCountByBlockNumber(x) - print 'eth_getUncleCountByBlockNumber: %s (%s)' % (result, type(result)) - -################################################################################ -print '*' * 80 - -db_name = 'db_name' -k = 'my_key' -v = 'my_value' -print c.db_putString(db_name, k, v) -x = c.db_getString(db_name, k) -print x -assert v == x - -db_name = 'db_name' -k = 'my_key' -v = '0xabcdef' -print c.db_putHex(db_name, k, v) -x = c.db_getHex(db_name, k) -print x -assert v == x - -################################################################################ -print '*' * 80 - -b = (199583, '0x19d761c6f944eefe91ad70b9aff3d2d76c972e5bb68c443eea7c0eaa144cef9f') -print c.eth_getBlockByHash(b[1], tx_objects=False) - -for x in ['earliest', 'latest', 'pending', b[0]]: - print c.eth_getBlockByNumber(x, tx_objects=False) - -tx = '0x12cd5d9a82049154c8990214a551479853d1bfe45852688833bc4ef86a29b1a3' -print c.eth_getTransactionByHash(tx) - -################################################################################ -print '*' * 80 - -code = 'contract Test {}' -print c.eth_compileSolidity(code) - -#code = '' -#print c.eth_compileSerpent(code) - -#code = '' -#print c.eth_compileLLL(code) - -################################################################################ -print '*' * 80 - -b = (246236, '0xcd43703a1ead33ffa1f317636c7b67453c5cc03a3350cd71dbbdd70fcbe0987a') -index = 2 -print c.eth_getTransactionByBlockHashAndIndex(b[1], index) - -for x in ['earliest', 'latest', 'pending', b[0]]: - print c.eth_getTransactionByBlockNumberAndIndex(b[0], index) - -tx = '0x27191ea9e8228c98bc4418fa60843540937b0c615b2db5e828756800f533f8cd' -print c.eth_getTransactionReceipt(tx) - -b = (246294, '0x3d596ca3c7b344419567957b41b2132bb339d365b6b6b3b6a7645e5444914a16') -index = 0 -print c.eth_getUncleByBlockHashAndIndex(b[1], index) - -for x in ['earliest', 'latest', 'pending', b[0]]: - print c.eth_getUncleByBlockNumberAndIndex(b[0], index) - -################################################################################ -print '*' * 80 - -addr = '0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe' -for x in ['earliest', 'latest', 'pending', 150000]: - print c.eth_getBalance(addr, x) - -addr = '0x407d73d8a49eeb85d32cf465507dd71d507100c1' -for x in ['earliest', 'latest', 'pending', 2]: - print c.eth_getStorageAt(addr, 0, x) - -################################################################################ -print '*' * 80 - -hash_rate = 1000000 -client_id = '0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c' -print c.eth_submitHashrate(hash_rate, client_id) - -digest = c.web3_sha3('') -print digest -# keccak-256, not sha3-256 -assert digest == '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' diff --git a/tests/Example.sol b/tests/Example.sol new file mode 100644 index 0000000..3337b7b --- /dev/null +++ b/tests/Example.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.4.0; + +contract Example { + + string s; + + function set_s(string new_s) { + s = new_s; + } + + function get_s() returns (string) { + return s; + } +} diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..122924a --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,21 @@ +ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +GETH := $(ROOT_DIR)/bin/geth.sh +SOURCES = $(wildcard *.sol) +BINS = $(SOURCES:.sol=.bin) + +.PHONY: start +start: + @sh $(GETH) start + +%.bin: %.sol + @solc --bin --output-dir ${ROOT_DIR} $< + +compile: $(BINS) + +.PHONY: test +test: start compile + @pytest -v -s && sh $(GETH) stop || sh $(GETH) stop + +.PHONY: clean +clean: + @rm -f ${BINS} && sh $(GETH) clean diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/bin/geth.sh b/tests/bin/geth.sh new file mode 100644 index 0000000..4dd65c9 --- /dev/null +++ b/tests/bin/geth.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +LOG="$DIR"/geth.log +PID="$DIR"/geth.pid +IPC="$DIR"/geth.ipc + +start() { + if [ ! -e "$PID" ]; then + geth --dev --rpc --datadir "$DIR" "$IPC" 2> "$LOG" & + echo $! > "$PID" && \ + until $(curl --output /dev/null --silent --head --fail http://127.0.0.1:8545); do + sleep 1 + done + geth --datadir "$DIR" --exec 'loadScript("'"$DIR"'/setup.js"); console.log(miner)' attach "$IPC" + fi +} + +stop() { + if [ -e "$PID" ]; then + kill $(cat "$PID") + rm "$PID" + fi +} + +if [ "$1" = "start" ]; then + start +elif [ "$1" = "stop" ]; then + stop +elif [ "$1" = "clean" ]; then + rm -f "$PID" "$LOG" "$IPC" +fi + + diff --git a/tests/bin/setup.js b/tests/bin/setup.js new file mode 100644 index 0000000..20159c7 --- /dev/null +++ b/tests/bin/setup.js @@ -0,0 +1,7 @@ +if( !web3.eth.accounts ) { + personal.newAccount( "" ); +} +var main = web3.eth.accounts[0]; +miner.setEtherbase( main ); +miner.start(); +personal.unlockAccount( main, "" ); diff --git a/tests/test_ethjsonrpc.py b/tests/test_ethjsonrpc.py new file mode 100644 index 0000000..9b50d41 --- /dev/null +++ b/tests/test_ethjsonrpc.py @@ -0,0 +1,150 @@ +from ethjsonrpc.client import EthJsonRpc +from ethjsonrpc.utils import hex_to_dec +import sys +import inspect +import pytest +import json +from ethjsonrpc.exceptions import (ConnectionError, BadStatusCodeError, + BadJsonError, BadResponseError) +c = EthJsonRpc() + +def isHash ( hash ): + assert str(hash)[:2] == "0x" + +def test_web3_clientVersion (): + assert len( c.web3_clientVersion() ) > 1 + +def test_net_version (): + assert c.net_version() == "1" + +def test_net_peerCount (): + assert c.net_peerCount() == 0 + +def test_net_listening (): + assert c.net_listening() + +def test_eth_protocolVersion (): + hex_to_dec(c.eth_protocolVersion()) > 60 + +def test_eth_syncing (): + assert not c.eth_syncing() + +def test_eth_coinbase (): + isHash( c.eth_coinbase() ) + +def test_eth_mining (): + assert c.eth_mining() + +def test_eth_hashrate (): + assert c.eth_hashrate() >= 0 + +def test_eth_gasPrice (): + assert c.eth_gasPrice() >= 0 + +def test_eth_accounts (): + l = c.eth_accounts(); + assert isinstance(l,list) + isHash( l[0] ) + assert len(l) > 0 + +def test_eth_blockNumber (): + assert c.eth_blockNumber() > 0 + +def test_eth_newPendingTransactionFilter (): + assert c.eth_newPendingTransactionFilter() > 0 + +def test_eth_getWork (): + assert isinstance( c.eth_getWork(), list) + +@pytest.mark.parametrize("addr,expected", [('0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe',0)] ) +@pytest.mark.parametrize("tag", ['earliest', 'latest', 'pending'] ) +def test_eth_getTransactionCount(addr, tag, expected): + assert c.eth_getTransactionCount( addr, tag ) == expected + +@pytest.mark.parametrize("addr,expected", [('0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe',0)] ) +@pytest.mark.parametrize("quantity", [150000] ) +def test_eth_getTransactionCount(addr, quantity, expected): + assert c.eth_getTransactionCount( addr, quantity ) == expected + +@pytest.mark.parametrize("data,expected", [ + ('0x9476018748ba1dae5bdf5e3725f8966df1fa127d49f58e66f621bf6868a23c85',0)]) +def test_eth_getBlockTransactionCountByHash(data,expected): + assert c.eth_getBlockTransactionCountByHash( data ) == expected + +@pytest.mark.parametrize("tag,expected", [(231301,0), + ("earliest",0),("latest",0),("pending",0)]) +def test_eth_getBlockTransactionCountByNumber(tag,expected): + assert c.eth_getBlockTransactionCountByNumber(tag) == expected + +@pytest.mark.parametrize("hash", ['0x19d761c6f944eefe91ad70b9aff3d2d76c972e5bb68c443eea7c0eaa144cef9f']) +def test_eth_getUncleCountByBlockHash (hash): + assert c.eth_getUncleCountByBlockHash(hash) == 0 + +@pytest.mark.parametrize("tag",['earliest', 'latest', 'pending', 199583]) +def test_eth_getUncleCountByBlockNumber (tag): + assert c.eth_getUncleCountByBlockNumber(tag) == 0 + +@pytest.mark.parametrize("hash",['0x19d761c6f944eefe91ad70b9aff3d2d76c972e5bb68c443eea7c0eaa144cef9f']) +def test_eth_getBlockByHash (hash): + assert c.eth_getBlockByHash(hash, tx_objects=False) == None + +@pytest.mark.parametrize("tag",['earliest', 'latest']) +def test_eth_getBlockByNumber (tag): + isHash( c.eth_getBlockByNumber(tag, tx_objects=False)["hash"]) + +@pytest.mark.parametrize("tx",['0x12cd5d9a82049154c8990214a551479853d1bfe45852688833bc4ef86a29b1a3']) +def test_eth_getTransactionByHash (tx): + assert c.eth_getTransactionByHash(tx) == None + +@pytest.mark.parametrize("hash,index",[('0xcd43703a1ead33ffa1f317636c7b67453c5cc03a3350cd71dbbdd70fcbe0987a',2)]) +def test_eth_getTransactionByBlockHashAndIndex (hash,index): + assert c.eth_getTransactionByBlockHashAndIndex(hash, index) == None + +@pytest.mark.parametrize("tag",[246236,'earliest', 'latest', 'pending']) +@pytest.mark.parametrize("index",[2]) +def test_eth_getTransactionByBlockNumberAndIndex (tag,index): + assert c.eth_getTransactionByBlockNumberAndIndex(tag, index) == None + +@pytest.mark.parametrize("tx",['0x27191ea9e8228c98bc4418fa60843540937b0c615b2db5e828756800f533f8cd']) +def test_eth_getTransactionReceipt (tx) : + assert c.eth_getTransactionReceipt(tx) == None + +@pytest.mark.parametrize("tag",['0x3d596ca3c7b344419567957b41b2132bb339d365b6b6b3b6a7645e5444914a16']) +@pytest.mark.parametrize("index",[0]) +def test_eth_getUncleByBlockHashAndIndex(tag,index): + assert c.eth_getUncleByBlockHashAndIndex(tag, index) == None + +@pytest.mark.parametrize("tag",['earliest', 'latest', 'pending', 246294]) +@pytest.mark.parametrize("index",[0]) +def test_eth_getUncleByBlockNumberAndIndex(tag,index): + assert c.eth_getUncleByBlockNumberAndIndex(tag, index) == None + +@pytest.mark.parametrize("addr",['0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe']) +@pytest.mark.parametrize("tag",['earliest', 'latest', 'pending', 150000]) +def test_eth_getBalance (addr,tag): + assert c.eth_getBalance(addr, tag) == 0 + + +@pytest.mark.parametrize("addr",["0x407d73d8a49eeb85d32cf465507dd71d507100c1"]) +@pytest.mark.parametrize("pos",[0]) +@pytest.mark.parametrize("tag", ["earliest","latest","pending",2]) +def test_eth_getStorageAt(addr,pos,tag): + assert hex_to_dec( c.eth_getStorageAt(addr, pos, tag )) == 0 + +@pytest.mark.parametrize("rate,client",[(1000000, + "0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")]) +def test_eth_submitHashrate(rate,client): + print c.eth_submitHashrate(rate, client) + +def test_web3_sha3(): + # keccak-256, not sha3-256 + assert c.web3_sha3('') == '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' + +def test_create_contract (): + with pytest.raises(BadResponseError) as err: + hash = c.create_contract(None, '0x%s' % open('tests/Example.bin', 'r').read(), None ) + # in case "Intrinsic gas too low" wasn't an issue, check return hash + isHash( hash ) + #request was good, but we still need to throw + raise BadResponseError() + diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..faaea91 --- /dev/null +++ b/tox.ini @@ -0,0 +1,7 @@ +[tox] +envlist = py27 +[testenv] +deps = pytest +whitelist_externals = make +commands = + make -f tests/Makefile test