-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
571 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,367 @@ | ||
import operator | ||
import random | ||
import sys | ||
|
||
from eth_tester import ( | ||
EthereumTester, | ||
) | ||
from eth_tester.exceptions import ( | ||
BlockNotFound, | ||
FilterNotFound, | ||
TransactionNotFound, | ||
ValidationError, | ||
) | ||
from eth_utils import ( | ||
decode_hex, | ||
encode_hex, | ||
is_null, | ||
keccak, | ||
) | ||
|
||
from .utils.formatters import ( | ||
apply_formatter_if, | ||
) | ||
from .utils.toolz import ( | ||
compose, | ||
curry, | ||
excepts, | ||
) | ||
|
||
|
||
def not_implemented(*args, **kwargs): | ||
raise NotImplementedError("RPC method not implemented") | ||
|
||
|
||
@curry | ||
def call_eth_tester(fn_name, eth_tester, fn_args, fn_kwargs=None): | ||
if fn_kwargs is None: | ||
fn_kwargs = {} | ||
return getattr(eth_tester, fn_name)(*fn_args, **fn_kwargs) | ||
|
||
|
||
def without_eth_tester(fn): | ||
# workaround for: https://github.com/pytoolz/cytoolz/issues/103 | ||
# @functools.wraps(fn) | ||
def inner(eth_tester, params): | ||
return fn(params) | ||
return inner | ||
|
||
|
||
def without_params(fn): | ||
# workaround for: https://github.com/pytoolz/cytoolz/issues/103 | ||
# @functools.wraps(fn) | ||
def inner(eth_tester, params): | ||
return fn(eth_tester) | ||
return inner | ||
|
||
|
||
@curry | ||
def preprocess_params(eth_tester, params, preprocessor_fn): | ||
return eth_tester, preprocessor_fn(params) | ||
|
||
|
||
def static_return(value): | ||
def inner(*args, **kwargs): | ||
return value | ||
return inner | ||
|
||
|
||
def client_version(eth_tester, params): | ||
# TODO: account for the backend that is in use. | ||
from eth_tester import __version__ | ||
return "EthereumTester/{version}/{platform}/python{v.major}.{v.minor}.{v.micro}".format( | ||
version=__version__, | ||
v=sys.version_info, | ||
platform=sys.platform, | ||
) | ||
|
||
|
||
@curry | ||
def null_if_excepts(exc_type, fn): | ||
return excepts( | ||
exc_type, | ||
fn, | ||
static_return(None), | ||
) | ||
|
||
|
||
null_if_block_not_found = null_if_excepts(BlockNotFound) | ||
null_if_transaction_not_found = null_if_excepts(TransactionNotFound) | ||
null_if_filter_not_found = null_if_excepts(FilterNotFound) | ||
null_if_indexerror = null_if_excepts(IndexError) | ||
|
||
|
||
@null_if_indexerror | ||
@null_if_block_not_found | ||
def get_transaction_by_block_hash_and_index(eth_tester, params): | ||
block_hash, transaction_index = params | ||
block = eth_tester.get_block_by_hash(block_hash, full_transactions=True) | ||
transaction = block['transactions'][transaction_index] | ||
return transaction | ||
|
||
|
||
@null_if_indexerror | ||
@null_if_block_not_found | ||
def get_transaction_by_block_number_and_index(eth_tester, params): | ||
block_number, transaction_index = params | ||
block = eth_tester.get_block_by_number(block_number, full_transactions=True) | ||
transaction = block['transactions'][transaction_index] | ||
return transaction | ||
|
||
|
||
def create_log_filter(eth_tester, params): | ||
filter_params = params[0] | ||
filter_id = eth_tester.create_log_filter(**filter_params) | ||
return filter_id | ||
|
||
|
||
def get_logs(eth_tester, params): | ||
filter_params = params[0] | ||
logs = eth_tester.get_logs(**filter_params) | ||
return logs | ||
|
||
|
||
def _generate_random_private_key(): | ||
""" | ||
WARNING: This is not a secure way to generate private keys and should only | ||
be used for testing purposes. | ||
""" | ||
return encode_hex(bytes(bytearray(( | ||
random.randint(0, 255) | ||
for _ in range(32) | ||
)))) | ||
|
||
|
||
@without_params | ||
def create_new_account(eth_tester): | ||
return eth_tester.add_account(_generate_random_private_key()) | ||
|
||
|
||
def personal_send_transaction(eth_tester, params): | ||
transaction, password = params | ||
|
||
try: | ||
eth_tester.unlock_account(transaction['from'], password) | ||
transaction_hash = eth_tester.send_transaction(transaction) | ||
finally: | ||
eth_tester.lock_account(transaction['from']) | ||
|
||
return transaction_hash | ||
|
||
|
||
API_ENDPOINTS = { | ||
'web3': { | ||
'clientVersion': client_version, | ||
'sha3': compose( | ||
encode_hex, | ||
keccak, | ||
decode_hex, | ||
without_eth_tester(operator.itemgetter(0)), | ||
), | ||
}, | ||
'net': { | ||
'version': not_implemented, | ||
'peerCount': not_implemented, | ||
'listening': not_implemented, | ||
}, | ||
'eth': { | ||
'protocolVersion': not_implemented, | ||
'syncing': not_implemented, | ||
'coinbase': compose( | ||
operator.itemgetter(0), | ||
call_eth_tester('get_accounts'), | ||
), | ||
'mining': not_implemented, | ||
'hashrate': not_implemented, | ||
'gasPrice': not_implemented, | ||
'accounts': call_eth_tester('get_accounts'), | ||
'blockNumber': compose( | ||
operator.itemgetter('number'), | ||
call_eth_tester('get_block_by_number', fn_kwargs={'block_number': 'latest'}), | ||
), | ||
'getBalance': call_eth_tester('get_balance'), | ||
'getStorageAt': not_implemented, | ||
'getTransactionCount': call_eth_tester('get_nonce'), | ||
'getBlockTransactionCountByHash': null_if_block_not_found(compose( | ||
len, | ||
operator.itemgetter('transactions'), | ||
call_eth_tester('get_block_by_hash'), | ||
)), | ||
'getBlockTransactionCountByNumber': null_if_block_not_found(compose( | ||
len, | ||
operator.itemgetter('transactions'), | ||
call_eth_tester('get_block_by_number'), | ||
)), | ||
'getUncleCountByBlockHash': null_if_block_not_found(compose( | ||
len, | ||
operator.itemgetter('uncles'), | ||
call_eth_tester('get_block_by_hash'), | ||
)), | ||
'getUncleCountByBlockNumber': null_if_block_not_found(compose( | ||
len, | ||
operator.itemgetter('uncles'), | ||
call_eth_tester('get_block_by_number'), | ||
)), | ||
'getCode': call_eth_tester('get_code'), | ||
'sign': not_implemented, | ||
'sendTransaction': call_eth_tester('send_transaction'), | ||
'sendRawTransaction': call_eth_tester('send_raw_transaction'), | ||
'call': call_eth_tester('call'), # TODO: untested | ||
'estimateGas': call_eth_tester('estimate_gas'), # TODO: untested | ||
'getBlockByHash': null_if_block_not_found(call_eth_tester('get_block_by_hash')), | ||
'getBlockByNumber': null_if_block_not_found(call_eth_tester('get_block_by_number')), | ||
'getTransactionByHash': null_if_transaction_not_found( | ||
call_eth_tester('get_transaction_by_hash') | ||
), | ||
'getTransactionByBlockHashAndIndex': get_transaction_by_block_hash_and_index, | ||
'getTransactionByBlockNumberAndIndex': get_transaction_by_block_number_and_index, | ||
'getTransactionReceipt': null_if_transaction_not_found(compose( | ||
apply_formatter_if( | ||
compose(is_null, operator.itemgetter('block_number')), | ||
static_return(None), | ||
), | ||
call_eth_tester('get_transaction_receipt'), | ||
)), | ||
'getUncleByBlockHashAndIndex': not_implemented, | ||
'getUncleByBlockNumberAndIndex': not_implemented, | ||
'getCompilers': not_implemented, | ||
'compileLLL': not_implemented, | ||
'compileSolidity': not_implemented, | ||
'compileSerpent': not_implemented, | ||
'newFilter': create_log_filter, | ||
'newBlockFilter': call_eth_tester('create_block_filter'), | ||
'newPendingTransactionFilter': call_eth_tester('create_pending_transaction_filter'), | ||
'uninstallFilter': excepts( | ||
FilterNotFound, | ||
compose( | ||
is_null, | ||
call_eth_tester('delete_filter'), | ||
), | ||
static_return(False), | ||
), | ||
'getFilterChanges': null_if_filter_not_found(call_eth_tester('get_only_filter_changes')), | ||
'getFilterLogs': null_if_filter_not_found(call_eth_tester('get_all_filter_logs')), | ||
'getLogs': get_logs, | ||
'getWork': not_implemented, | ||
'submitWork': not_implemented, | ||
'submitHashrate': not_implemented, | ||
}, | ||
'db': { | ||
'putString': not_implemented, | ||
'getString': not_implemented, | ||
'putHex': not_implemented, | ||
'getHex': not_implemented, | ||
}, | ||
'shh': { | ||
'post': not_implemented, | ||
'version': not_implemented, | ||
'newIdentity': not_implemented, | ||
'hasIdentity': not_implemented, | ||
'newGroup': not_implemented, | ||
'addToGroup': not_implemented, | ||
'newFilter': not_implemented, | ||
'uninstallFilter': not_implemented, | ||
'getFilterChanges': not_implemented, | ||
'getMessages': not_implemented, | ||
}, | ||
'admin': { | ||
'addPeer': not_implemented, | ||
'datadir': not_implemented, | ||
'nodeInfo': not_implemented, | ||
'peers': not_implemented, | ||
'setSolc': not_implemented, | ||
'startRPC': not_implemented, | ||
'startWS': not_implemented, | ||
'stopRPC': not_implemented, | ||
'stopWS': not_implemented, | ||
}, | ||
'debug': { | ||
'backtraceAt': not_implemented, | ||
'blockProfile': not_implemented, | ||
'cpuProfile': not_implemented, | ||
'dumpBlock': not_implemented, | ||
'gtStats': not_implemented, | ||
'getBlockRLP': not_implemented, | ||
'goTrace': not_implemented, | ||
'memStats': not_implemented, | ||
'seedHashSign': not_implemented, | ||
'setBlockProfileRate': not_implemented, | ||
'setHead': not_implemented, | ||
'stacks': not_implemented, | ||
'startCPUProfile': not_implemented, | ||
'startGoTrace': not_implemented, | ||
'stopCPUProfile': not_implemented, | ||
'stopGoTrace': not_implemented, | ||
'traceBlock': not_implemented, | ||
'traceBlockByNumber': not_implemented, | ||
'traceBlockByHash': not_implemented, | ||
'traceBlockFromFile': not_implemented, | ||
'traceTransaction': not_implemented, | ||
'verbosity': not_implemented, | ||
'vmodule': not_implemented, | ||
'writeBlockProfile': not_implemented, | ||
'writeMemProfile': not_implemented, | ||
}, | ||
'miner': { | ||
'makeDAG': not_implemented, | ||
'setExtra': not_implemented, | ||
'setGasPrice': not_implemented, | ||
'start': not_implemented, | ||
'startAutoDAG': not_implemented, | ||
'stop': not_implemented, | ||
'stopAutoDAG': not_implemented, | ||
}, | ||
'personal': { | ||
'ecRecover': not_implemented, | ||
'importRawKey': call_eth_tester('add_account'), | ||
'listAccounts': call_eth_tester('get_accounts'), | ||
'lockAccount': excepts( | ||
ValidationError, | ||
compose(static_return(True), call_eth_tester('lock_account')), | ||
static_return(False), | ||
), | ||
'newAccount': create_new_account, | ||
'unlockAccount': excepts( | ||
ValidationError, | ||
compose(static_return(True), call_eth_tester('unlock_account')), | ||
static_return(False), | ||
), | ||
'sendTransaction': personal_send_transaction, | ||
'sign': not_implemented, | ||
}, | ||
'testing': { | ||
'timeTravel': call_eth_tester('time_travel'), | ||
}, | ||
'txpool': { | ||
'content': not_implemented, | ||
'inspect': not_implemented, | ||
'status': not_implemented, | ||
}, | ||
'evm': { | ||
'mine': call_eth_tester('mine_blocks'), | ||
'revert': call_eth_tester('revert_to_snapshot'), | ||
'snapshot': call_eth_tester('take_snapshot'), | ||
}, | ||
} | ||
|
||
|
||
class RPCMethods: | ||
|
||
def __init__(self, eth_tester=None, api_endpoints=None): | ||
if eth_tester is None: | ||
self.client = EthereumTester() | ||
else: | ||
self.client = eth_tester | ||
|
||
if api_endpoints is None: | ||
self.api_endpoints = API_ENDPOINTS | ||
else: | ||
self.api_endpoints = api_endpoints | ||
|
||
def __getattr__(self, item): | ||
namespace, _, endpoint = item.partition('_') | ||
try: | ||
delegator = self.api_endpoints[namespace][endpoint] | ||
return curry(delegator)(self.client) | ||
except KeyError: | ||
return super().__getattribute__(item) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from jsonrpc import JSONRPCResponseManager, dispatcher | ||
|
||
|
||
def get_application(): | ||
pass |
Empty file.
Oops, something went wrong.