Skip to content
This repository has been archived by the owner on Aug 8, 2018. It is now read-only.

closes #99 eth_gasLimit missing #103

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions pyethapp/rpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,6 +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__)

Expand Down Expand Up @@ -116,13 +119,15 @@ 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 '<JSONRPCClient @%d>' % self.port
Expand All @@ -142,6 +147,13 @@ 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 blocknumber(self):
""" Return the most recent block. """
return quantity_decoder(self.call('eth_blockNumber'))
Expand All @@ -167,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'))
Expand Down Expand Up @@ -357,7 +370,6 @@ 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.

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.
Expand All @@ -382,7 +394,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)

Expand Down Expand Up @@ -618,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.
Expand Down
131 changes: 131 additions & 0 deletions pyethapp/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -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
128 changes: 5 additions & 123 deletions pyethapp/tests/test_jsonrpc.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions pyethapp/tests/test_rpc_client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
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(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)


def test_default_tx_gas_assigned():
default_gas = 12345
client = JSONRPCClient(default_tx_gas=default_gas)
assert client.default_tx_gas == default_gas


def test_default_host():
default_host = '127.0.0.1'
client = JSONRPCClient()
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ ethereum>=1.3.5
pbkdf2
scrypt
pexpect
pytest