From f1430a8c3ec40f75967b0b32eca8166f0f5fc585 Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Sun, 18 Mar 2018 22:29:16 +0100 Subject: [PATCH] CODE: CLI Refactoring Submission 522596 by nowind_lee to the challenge http://www.topcoder.com/challenges/30063470 --- Dockerfile | 4 +- myth | 3 +- mythril/ipc/client.py | 668 +++---------------------------------- mythril/ipc/utils.py | 48 +++ mythril/rpc/base_client.py | 644 +++++++++++++++++++++++++++++++++++ mythril/rpc/client.py | 635 +---------------------------------- requirements.txt | 9 +- setup.py | 1 - 8 files changed, 759 insertions(+), 1253 deletions(-) create mode 100644 mythril/rpc/base_client.py diff --git a/Dockerfile b/Dockerfile index 8a6dcf53..98730cd9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ COPY . . RUN apt-get update \ && apt-get install -y software-properties-common python-software-properties \ - && add-apt-repository ppa:ethereum/ethereum \ + && add-apt-repository -y ppa:ethereum/ethereum \ && apt-get update \ && apt-get install -y solc \ && apt-get install -y libssl-dev \ @@ -13,7 +13,7 @@ RUN apt-get update \ && pip3 install --upgrade pip \ && apt-get install -y pandoc \ && apt-get install -y git \ - && pip3 install laser-ethereum \ + && pip3 install -r requirements.txt \ && python setup.py install CMD [] diff --git a/myth b/myth index 3016bab6..6471ddbe 100755 --- a/myth +++ b/myth @@ -20,7 +20,6 @@ from mythril.analysis.callgraph import generate_graph from mythril.analysis.security import fire_lasers from mythril.analysis.report import Report from laser.ethereum import helper -from web3 import Web3 from ethereum import utils from pathlib import Path from json.decoder import JSONDecodeError @@ -340,7 +339,7 @@ if args.storage: if array: position_formated = str(position).zfill(64) - position = int(Web3.sha3(position_formated), 16) + position = int(utils.sha3(position_formated), 16) try: if length == 1: diff --git a/mythril/ipc/client.py b/mythril/ipc/client.py index a6377048..844471b0 100644 --- a/mythril/ipc/client.py +++ b/mythril/ipc/client.py @@ -1,633 +1,71 @@ import json -import warnings +import socket -from ethereum import utils -from ethereum.abi import encode_abi, decode_abi -from web3 import Web3, IPCProvider -#HTTPProvider, +from mythril.rpc.base_client import BaseClient +from .utils import (get_default_ipc_path, to_text, to_bytes) -from .constants import BLOCK_TAGS, BLOCK_TAG_LATEST -from .utils import hex_to_dec, clean_hex, validate_block -from .exceptions import (ConnectionError, BadStatusCodeError, - BadJsonError, BadResponseError) -IPC_PATH = None +try: + from json import JSONDecodeError +except ImportError: + JSONDecodeError = ValueError +IPC_PATH = None ''' -This code is adapted from: https://github.com/ConsenSys/ethjsonrpc +This code is mostly adapted from: +- https://github.com/ConsenSys/ethjsonrpc +- https://github.com/pipermerriam/ethereum-ipc-client +- https://github.com/ethereum/web3.py ''' -class EthIpc(object): - ''' - Ethereum IPC client class using web3.py - ''' - web3 = Web3(IPCProvider()) - - DEFAULT_GAS_PER_TX = 90000 - DEFAULT_GAS_PRICE = 50 * 10**9 # 50 gwei - - def __init__(self, ipc_path=IPC_PATH): - #not used so far - self.ipc_path = ipc_path - - def _encode_function(self, signature, param_values): - - prefix = utils.big_endian_to_int(utils.sha3(signature)[:4]) - - if signature.find('(') == -1: - raise RuntimeError('Invalid function signature. Missing "(" and/or ")"...') - - if signature.find(')') - signature.find('(') == 1: - return utils.encode_int(prefix) - - types = signature[signature.find('(') + 1: signature.find(')')].split(',') - encoded_params = encode_abi(types, param_values) - return utils.zpad(utils.encode_int(prefix), 4) + encoded_params - -################################################################################ -# high-level methods -################################################################################ - - def transfer(self, from_, to, amount): - ''' - Send wei from one address to another - ''' - return self.eth_sendTransaction(from_address=from_, to_address=to, value=amount) - - def create_contract(self, from_, code, gas, sig=None, args=None): - ''' - Create a contract on the blockchain from compiled EVM code. Returns the - transaction hash. - ''' - from_ = from_ or self.eth_coinbase() - if sig is not None and args is not None: - types = sig[sig.find('(') + 1: sig.find(')')].split(',') - encoded_params = encode_abi(types, args) - code += encoded_params.hex() - - return self.eth_sendTransaction(from_address=from_, gas=gas, data=code) - - def get_contract_address(self, tx): - ''' - Get the address for a contract from the transaction that created it - ''' - receipt = self.eth_getTransactionReceipt(tx) - return receipt['contractAddress'] - - def call(self, address, sig, args, result_types): - ''' - Call a contract function on the IPC server, without sending a - transaction (useful for reading data) - ''' - data = self._encode_function(sig, args) - data_hex = data.encode('hex') - # could be made to use web3py directly, but instead uses eth_call which is adapted - response = self.eth_call(to_address=address, data=data_hex) - return decode_abi(result_types, response[2:].decode('hex')) - - def call_with_transaction(self, from_, address, sig, args, gas=None, gas_price=None, value=None): - ''' - Call a contract function by sending a transaction (useful for storing - data) - ''' - 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') - return self.eth_sendTransaction(from_address=from_, to_address=address, data=data_hex, gas=gas, - gas_price=gas_price, value=value) - -################################################################################ -# JSON-RPC methods -################################################################################ - - def web3_clientVersion(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_clientversion - http://web3py.readthedocs.io/en/latest/web3.version.html#web3.version.Version.node - - ''' - return self.web3.version.node - - def web3_sha3(self, data): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_sha3 - http://web3py.readthedocs.io/en/latest/overview.html#Web3.sha3 - Encoding probably not necessary - - ''' - data = str(data).encode('hex') - return self.web3.sha3(data) - - def net_version(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version - http://web3py.readthedocs.io/en/latest/web3.version.html#web3.version.Version.network - - - ''' - return self.web3.version.network - - def net_listening(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#net_listening - Only indirectly available - - ''' - return self.web3net.listening() - - def net_peerCount(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#net_peercount - ONLY indirectly available - - ''' - return self.web3.net.peerCount() - - def eth_protocolVersion(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_protocolversion - http://web3py.readthedocs.io/en/latest/web3.version.html?highlight=net#web3.version.Version.ethereum - - ''' - return self.web3.version.ethereum - - def eth_syncing(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_syncing - - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.syncing - ''' - return self.web3.eth.syncing - - def eth_coinbase(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_coinbase - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.coinbase - - ''' - return self.web3.eth.coinbase - - def eth_mining(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_mining - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.mining - - ''' - return self.web3.Eth.mining - - def eth_hashrate(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_hashrate - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.hashrate - - ''' - return web3.eth.hashrate - - def eth_gasPrice(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gasprice - - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.gasPrice - ''' - return web3.eth.gasPrice - - def eth_accounts(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_accounts - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.accounts - - ''' - return self.web3.eth.accounts - - def eth_blockNumber(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_blocknumber - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.blockNumber - - ''' - return self.web3.eth.blockNumber - - def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getbalance - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBalance - - ''' - address = address or self.eth_coinbase() - block = validate_block(block) - return self.web3.eth.getBalance(address, block_identifier=block) - - def eth_getStorageAt(self, address=None, position=0, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getStorageAt - - ''' - block = validate_block(block) - return self.web3.eth.getStorageAt(address, position, block_identifier=block) - - def eth_getTransactionCount(self, address, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactioncount - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getTransactionCount - - ''' - block = validate_block(block) - return self.web3.eth.getTransactionCount(address, block_identifier=block) - - def eth_getBlockTransactionCountByHash(self, block_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblocktransactioncountbyhash - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBlockTransactionCount - - ''' - return self.web3.eth.getBlockTransactionCount(block_hash) - - def eth_getBlockTransactionCountByNumber(self, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblocktransactioncountbynumber - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBlockTransactionCount - - ''' - block = validate_block(block) - return self.web3.eth.getBlockTransactionCount(block) - - def eth_getUncleCountByBlockHash(self, block_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblockhash - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getUncle - (not implemented with convenience functions) - ''' - return self.web3.manager.request_blocking('eth_getUncleCountByBlockHash', [block_hash]) - return self.web3.eth.getUncleCount(block_hash) - - def eth_getUncleCountByBlockNumber(self, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblocknumber - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getUncle - - ''' - block = validate_block(block) - self.web3.manager.request_blocking('debug_getBlockRlp', [number]) - return self.web3.eth.getUncleCount(block) - def eth_getCode(self, address, default_block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcode - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getCode - ''' - if isinstance(default_block, str): - if default_block not in BLOCK_TAGS: - raise ValueError - return self.web3.eth.getCode(address, block_identifier=default_block) - def eth_sign(self, address, data): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.sign - either data= hexstr= or text= probably not needed now but if used should be differentiated - ''' - return self.web3.eth.sign(address, data) +class EthIpc(BaseClient): - def eth_sendTransaction(self, to_address=None, from_address=None, gas=None, gas_price=None, value=None, data=None, - nonce=None): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendtransaction - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.sendTransaction - ''' - params = {} - params['from'] = from_address or self.eth_coinbase() - if to_address is not None: - params['to'] = to_address - if gas is not None: - params['gas'] = hex(gas) - if gas_price is not None: - params['gasPrice'] = clean_hex(gas_price) - if value is not None: - params['value'] = clean_hex(value) - if data is not None: - params['data'] = data - if nonce is not None: - params['nonce'] = hex(nonce) - return self.web3.eth.sendTransaction(params) - - def eth_sendRawTransaction(self, data): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendrawtransaction - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.sendRawTransaction - ''' - return self.web3.eth.sendRawTransaction(data) - - def eth_call(self, to_address, from_address=None, gas=None, gas_price=None, value=None, data=None, - default_block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_call - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.call - - ''' - if isinstance(default_block, str): - if default_block not in BLOCK_TAGS: - raise ValueError - obj = {} - obj['to'] = to_address - if from_address is not None: - obj['from'] = from_address - if gas is not None: - obj['gas'] = hex(gas) - if gas_price is not None: - obj['gasPrice'] = clean_hex(gas_price) - if value is not None: - obj['value'] = value - if data is not None: - obj['data'] = data - return self.web3.eth.call(obj, block_identifier=default_block) - - def eth_estimateGas(self, to_address=None, from_address=None, gas=None, gas_price=None, value=None, data=None, - default_block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.estimateGas - ''' - if isinstance(default_block, str): - if default_block not in BLOCK_TAGS: - raise ValueError - obj = {} - if to_address is not None: - obj['to'] = to_address - if from_address is not None: - obj['from'] = from_address - if gas is not None: - obj['gas'] = hex(gas) - if gas_price is not None: - obj['gasPrice'] = clean_hex(gas_price) - if value is not None: - obj['value'] = value - if data is not None: - obj['data'] = data - return self.web3.eth.estimateGas(obj) - - def eth_getBlockByHash(self, block_hash, tx_objects=True): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbyhash - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBlock - ''' - return self.web3.eth.getBlock(block_identifier=block_hash, full_transactions=tx_objects) - - def eth_getBlockByNumber(self, block=BLOCK_TAG_LATEST, tx_objects=True): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBlock - ''' - block = validate_block(block) - return self.web3.eth.getBlock(block_identifier=block, full_transactions=tx_objects) - - def eth_getTransactionByHash(self, tx_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyhash - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getTransaction - ''' - return self.web3.eth.getTransactionByHash(tx_hash) - - def eth_getTransactionByBlockHashAndIndex(self, block_hash, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblockhashandindex - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getTransactionFromBlock - ''' - return self.web3.eth.getTransactionFromBlock(block_identifier=block_hash, transaction_index=index) - - def eth_getTransactionByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblocknumberandindex - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getTransactionFromBlock - - ''' - block = validate_block(block) - return self.web3.eth.getTransactionFromBlock(block_identifier=block, transaction_index=index) - - def eth_getTransactionReceipt(self, tx_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getTransactionReceipt - ''' - return self.web3.eth.getTransactionReceipt(tx_hash) - - def eth_getUncleByBlockHashAndIndex(self, block_hash, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblockhashandindex - Indirectly accessible - self.web3.manager.request_blocking('rpc/ipc function', [params]) - - ''' - return self.web3.manager.request_blocking('eth_getUncleByBlockHashAndIndex', [block_hash, web3.toHex(index)]) - - def eth_getUncleByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblocknumberandindex - Indirectly accessible - ''' - block = validate_block(block) - return self.web3.manager.request_blocking('eth_getUncleByBlockNumberAndIndex', [block, web3.toHex(index)]) - - def eth_getCompilers(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcompilers - Indirectly implemented - ''' - return self.web3.manager.request_blocking('eth_getCompilers') - - def eth_compileSolidity(self, code): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compilesolidity - Indirectly implemented - ''' - return self.web3.manager.request_blocking('eth_compileSolidity', [code]) - - - def eth_compileLLL(self, code): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compilelll - Indirectly accessible - ''' - return self.web3.manager.request_blocking('eth_compileLLL', [code]) - - def eth_compileSerpent(self, code): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compileserpent - Indirectly implemented - ''' - return self.web3.manager.request_blocking('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 - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.filter - ''' - filter_params = { - 'fromBlock': from_block, - 'toBlock': to_block, - 'address': address, - 'topics': topics, - } - return self.web3.eth.newFilter(filter_params) - - def eth_newBlockFilter(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.filter - ''' - return self.web3.eth.newFilter('latest') - - def eth_newPendingTransactionFilter(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.filter - ''' - return self.web3.eth.newFilter('pending') - - def eth_uninstallFilter(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.uninstallFilter - ''' - return self.web3.eth.uninstallFilter(filter_id) - - def eth_getFilterChanges(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getFilterChanges - ''' - return self.web3.eth.getFilterChanges(filter_id) - - def eth_getFilterLogs(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs - http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getFilterLogs - ''' - return self.web3.eth.getFilterLogs(filter_id) - - def eth_getLogs(self, filter_object): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs - http://web3py.readthedocs.io/en/latest/filters.html?highlight=getLogs#web3.utils.filters.LogFilter.get - ''' - return self.filter_object.get() - - def eth_getWork(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getwork - http://web3py.readthedocs.io/en/latest/releases.html?highlight=getWork#id15 - ''' - return self.web3.eth.getWork() - - def eth_submitWork(self, nonce, header, mix_digest): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submitwork - Implemented indirectly - ''' - return self.web3.manager.request_blocking('eth_submitWork', [nonce, header, mix_digest]) - - def eth_submitHashrate(self, hash_rate, client_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submithashrate - Implemented indirectly - ''' - return self.web3.manager.request_blocking('eth_submitHashrate', [hash_rate, client_id]) - - def shh_version(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_version - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.version - ''' - return self.web3.shh.version() - - def shh_post(self, topics, payload, priority, ttl, from_=None, to=None): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_post - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.post - # only topics and payload are necessary according to web3.py - ''' - whisper_object = { - 'from': from_, - 'to': to, - 'topics': web3.toHex(topics), - 'payload': web3.toHex(payload), - 'priority': web3.toHex(priority), - 'ttl': web3.toHex(ttl) - } - return self.web3.shh.post(whisper_object) - - def shh_newIdentity(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newidentity - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.newIdentity - ''' - return self.web3.shh.newIdentity() - - def shh_hasIdentity(self, address): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_hasidentity - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.hasIdentity - ''' - return self.web3.shh.hasIdentity(address) - - def shh_newGroup(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newgroup - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.newGroup - ''' - return self.web3.shh.newGroup() - - def shh_addToGroup(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_addtogroup - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.addToGroup - ''' - return self.web3.shh.addToGroup() - - def shh_newFilter(self, to, topics): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newfilter - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.filter - # to is optional - ''' - _filter = { - 'topics': topics, - 'to': to + def __init__(self, ipc_path=IPC_PATH, testnet=False): + if ipc_path is None: + ipc_path = get_default_ipc_path(testnet) + self.ipc_path = ipc_path + self._socket = self.get_socket() + + def get_socket(self): + _socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + _socket.connect(self.ipc_path) + # Tell the socket not to block on reads. + _socket.settimeout(2) + return _socket + + def _call(self, method, params=None, _id=1): + params = params or [] + data = { + 'jsonrpc': '2.0', + 'method': method, + 'params': params, + 'id': _id, } - return self.web3.shh.newFilter(_filter) - - def shh_uninstallFilter(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_uninstallfilter - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.uninstallFilter - ''' - return self.web3.shh.uninstallFilter(filter_id) + request = to_bytes(json.dumps(data)) - def shh_getFilterChanges(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getfilterchanges - http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.getFilterChanges - ''' - filt = self.web3.eth.filter() - return self.web3.shh.getFilterChanges(filter_id) + for _ in range(3): + self._socket.sendall(request) + response_raw = "" - def shh_getMessages(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getmessages - http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.getMessages - ''' - return self.web3.shh.getMessages(filter_id) + while True: + try: + response_raw += to_text(self._socket.recv(4096)) + except socket.timeout: + break - def getBlockRlp(self, number=0): + if response_raw == "": + self._socket.close() + self._socket = self.get_socket() + continue - #not accessible with convenience functions + break + else: + raise ValueError("No JSON returned by socket") - return self.web3.manager.request_blocking('debug_getBlockRlp', [number]) + response = json.loads(response_raw) - def traceTransaction(self, txHash): - #atm not directly accessible, so something like this is needed - #https://github.com/pipermerriam/web3.py/issues/308 - - return self.web3.manager.request_blocking('debug_traceTransaction', [txHash]) + if "error" in response: + raise ValueError(response["error"]["message"]) + return response["result"] diff --git a/mythril/ipc/utils.py b/mythril/ipc/utils.py index e87b7dc6..d6b9cee1 100644 --- a/mythril/ipc/utils.py +++ b/mythril/ipc/utils.py @@ -1,3 +1,6 @@ +import os +import sys + from .constants import BLOCK_TAGS @@ -7,6 +10,11 @@ def hex_to_dec(x): ''' return int(x, 16) +def to_bytes(text): + return text.encode('utf-8') + +def to_text(byte_array): + return byte_array.decode('utf-8') def clean_hex(d): ''' @@ -36,3 +44,43 @@ def ether_to_wei(ether): Convert ether to wei ''' return ether * 10**18 + + +def get_default_ipc_path(testnet=False): + if testnet: + testnet = "testnet" + else: + testnet = "" + + if sys.platform == 'darwin': + ipc_path = os.path.expanduser(os.path.join("~", "Library", "Ethereum", testnet, "geth.ipc")) + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.expanduser(os.path.join("~", "Library", "Application Support", "io.parity.ethereum", "jsonrpc.ipc")) + if os.path.exists(ipc_path): + return ipc_path + + elif sys.platform.startswith('linux'): + ipc_path = os.path.expanduser(os.path.join("~", ".ethereum", testnet, "geth.ipc")) + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.expanduser(os.path.join("~", ".local", "share", "io.parity.ethereum", "jsonrpc.ipc")) + if os.path.exists(ipc_path): + return ipc_path + + elif sys.platform == 'win32': + ipc_path = os.path.join("\\\\", ".", "pipe", "geth.ipc") + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.join("\\\\", ".", "pipe", "jsonrpc.ipc") + if os.path.exists(ipc_path): + return ipc_path + + else: + raise ValueError( + "Unsupported platform '{0}'. Only darwin/linux2/win32 are " + "supported. You must specify the ipc_path".format(sys.platform) + ) \ No newline at end of file diff --git a/mythril/rpc/base_client.py b/mythril/rpc/base_client.py new file mode 100644 index 00000000..8ec62ec7 --- /dev/null +++ b/mythril/rpc/base_client.py @@ -0,0 +1,644 @@ +from abc import (abstractmethod) + +from ethereum.abi import encode_abi, decode_abi +from ethereum import utils + +from .constants import BLOCK_TAGS, BLOCK_TAG_LATEST +from .utils import hex_to_dec, clean_hex, validate_block + +GETH_DEFAULT_RPC_PORT = 8545 +ETH_DEFAULT_RPC_PORT = 8545 +PARITY_DEFAULT_RPC_PORT = 8545 +PYETHAPP_DEFAULT_RPC_PORT = 4000 +MAX_RETRIES = 3 +JSON_MEDIA_TYPE = 'application/json' + +''' +This code is adapted from: https://github.com/ConsenSys/ethjsonrpc +''' +class BaseClient(object): + + @abstractmethod + def _call(self, method, params=None, _id=1): + pass + + def _encode_function(self, signature, param_values): + + prefix = utils.big_endian_to_int(utils.sha3(signature)[:4]) + + if signature.find('(') == -1: + raise RuntimeError('Invalid function signature. Missing "(" and/or ")"...') + + if signature.find(')') - signature.find('(') == 1: + return utils.encode_int(prefix) + + types = signature[signature.find('(') + 1: signature.find(')')].split(',') + encoded_params = encode_abi(types, param_values) + return utils.zpad(utils.encode_int(prefix), 4) + encoded_params + +################################################################################ +# high-level methods +################################################################################ + + def transfer(self, from_, to, amount): + ''' + Send wei from one address to another + ''' + return self.eth_sendTransaction(from_address=from_, to_address=to, value=amount) + + def create_contract(self, from_, code, gas, sig=None, args=None): + ''' + Create a contract on the blockchain from compiled EVM code. Returns the + transaction hash. + ''' + from_ = from_ or self.eth_coinbase() + if sig is not None and args is not None: + types = sig[sig.find('(') + 1: sig.find(')')].split(',') + encoded_params = encode_abi(types, args) + code += encoded_params.hex() + + return self.eth_sendTransaction(from_address=from_, gas=gas, data=code) + + def get_contract_address(self, tx): + ''' + Get the address for a contract from the transaction that created it + ''' + receipt = self.eth_getTransactionReceipt(tx) + return receipt['contractAddress'] + + def call(self, address, sig, args, result_types): + ''' + Call a contract function on the RPC server, without sending a + transaction (useful for reading data) + ''' + data = self._encode_function(sig, args) + data_hex = data.encode('hex') + response = self.eth_call(to_address=address, data=data_hex) + return decode_abi(result_types, response[2:].decode('hex')) + + def call_with_transaction(self, from_, address, sig, args, gas=None, gas_price=None, value=None): + ''' + Call a contract function by sending a transaction (useful for storing + data) + ''' + 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') + return self.eth_sendTransaction(from_address=from_, to_address=address, data=data_hex, gas=gas, + gas_price=gas_price, value=value) + +################################################################################ +# JSON-RPC methods +################################################################################ + + def web3_clientVersion(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_clientversion + + TESTED + ''' + return self._call('web3_clientVersion') + + def web3_sha3(self, data): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_sha3 + + TESTED + ''' + data = str(data).encode('hex') + return self._call('web3_sha3', [data]) + + def net_version(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version + + TESTED + ''' + return self._call('net_version') + + def net_listening(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#net_listening + + TESTED + ''' + return self._call('net_listening') + + def net_peerCount(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#net_peercount + + TESTED + ''' + return hex_to_dec(self._call('net_peerCount')) + + def eth_protocolVersion(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_protocolversion + + TESTED + ''' + return self._call('eth_protocolVersion') + + def eth_syncing(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_syncing + + TESTED + ''' + return self._call('eth_syncing') + + def eth_coinbase(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_coinbase + + TESTED + ''' + return self._call('eth_coinbase') + + def eth_mining(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_mining + + TESTED + ''' + return self._call('eth_mining') + + def eth_hashrate(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_hashrate + + TESTED + ''' + return hex_to_dec(self._call('eth_hashrate')) + + def eth_gasPrice(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gasprice + + TESTED + ''' + return hex_to_dec(self._call('eth_gasPrice')) + + def eth_accounts(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_accounts + + TESTED + ''' + return self._call('eth_accounts') + + def eth_blockNumber(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_blocknumber + + TESTED + ''' + return hex_to_dec(self._call('eth_blockNumber')) + + def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getbalance + + TESTED + ''' + address = address or self.eth_coinbase() + block = validate_block(block) + return hex_to_dec(self._call('eth_getBalance', [address, block])) + + def eth_getStorageAt(self, address=None, position=0, block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat + + TESTED + ''' + block = validate_block(block) + return self._call('eth_getStorageAt', [address, hex(position), block]) + + def eth_getTransactionCount(self, address, block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactioncount + + TESTED + ''' + block = validate_block(block) + return hex_to_dec(self._call('eth_getTransactionCount', [address, block])) + + def eth_getBlockTransactionCountByHash(self, block_hash): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblocktransactioncountbyhash + + TESTED + ''' + return hex_to_dec(self._call('eth_getBlockTransactionCountByHash', [block_hash])) + + def eth_getBlockTransactionCountByNumber(self, block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblocktransactioncountbynumber + + TESTED + ''' + block = validate_block(block) + return hex_to_dec(self._call('eth_getBlockTransactionCountByNumber', [block])) + + def eth_getUncleCountByBlockHash(self, block_hash): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblockhash + + TESTED + ''' + return hex_to_dec(self._call('eth_getUncleCountByBlockHash', [block_hash])) + + def eth_getUncleCountByBlockNumber(self, block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblocknumber + + TESTED + ''' + block = validate_block(block) + return hex_to_dec(self._call('eth_getUncleCountByBlockNumber', [block])) + + def eth_getCode(self, address, default_block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcode + + NEEDS TESTING + ''' + if isinstance(default_block, str): + if default_block not in BLOCK_TAGS: + raise ValueError + return self._call('eth_getCode', [address, default_block]) + + def eth_sign(self, address, data): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign + + NEEDS TESTING + ''' + return self._call('eth_sign', [address, data]) + + def eth_sendTransaction(self, to_address=None, from_address=None, gas=None, gas_price=None, value=None, data=None, + nonce=None): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendtransaction + + NEEDS TESTING + ''' + params = {} + params['from'] = from_address or self.eth_coinbase() + if to_address is not None: + params['to'] = to_address + if gas is not None: + params['gas'] = hex(gas) + if gas_price is not None: + params['gasPrice'] = clean_hex(gas_price) + if value is not None: + params['value'] = clean_hex(value) + if data is not None: + params['data'] = data + if nonce is not None: + params['nonce'] = hex(nonce) + return self._call('eth_sendTransaction', [params]) + + def eth_sendRawTransaction(self, data): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendrawtransaction + + NEEDS TESTING + ''' + return self._call('eth_sendRawTransaction', [data]) + + def eth_call(self, to_address, from_address=None, gas=None, gas_price=None, value=None, data=None, + default_block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_call + + NEEDS TESTING + ''' + if isinstance(default_block, str): + if default_block not in BLOCK_TAGS: + raise ValueError + obj = {} + obj['to'] = to_address + if from_address is not None: + obj['from'] = from_address + if gas is not None: + obj['gas'] = hex(gas) + if gas_price is not None: + obj['gasPrice'] = clean_hex(gas_price) + if value is not None: + obj['value'] = value + if data is not None: + obj['data'] = data + return self._call('eth_call', [obj, default_block]) + + def eth_estimateGas(self, to_address=None, from_address=None, gas=None, gas_price=None, value=None, data=None, + default_block=BLOCK_TAG_LATEST): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas + + NEEDS TESTING + ''' + if isinstance(default_block, str): + if default_block not in BLOCK_TAGS: + raise ValueError + obj = {} + if to_address is not None: + obj['to'] = to_address + if from_address is not None: + obj['from'] = from_address + if gas is not None: + obj['gas'] = hex(gas) + if gas_price is not None: + obj['gasPrice'] = clean_hex(gas_price) + if value is not None: + obj['value'] = value + if data is not None: + obj['data'] = data + return hex_to_dec(self._call('eth_estimateGas', [obj, default_block])) + + def eth_getBlockByHash(self, block_hash, tx_objects=True): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbyhash + + TESTED + ''' + return self._call('eth_getBlockByHash', [block_hash, tx_objects]) + + def eth_getBlockByNumber(self, block=BLOCK_TAG_LATEST, tx_objects=True): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber + + TESTED + ''' + block = validate_block(block) + return self._call('eth_getBlockByNumber', [block, tx_objects]) + + def eth_getTransactionByHash(self, tx_hash): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyhash + + TESTED + ''' + return self._call('eth_getTransactionByHash', [tx_hash]) + + def eth_getTransactionByBlockHashAndIndex(self, block_hash, index=0): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblockhashandindex + + TESTED + ''' + return self._call('eth_getTransactionByBlockHashAndIndex', [block_hash, hex(index)]) + + def eth_getTransactionByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblocknumberandindex + + TESTED + ''' + block = validate_block(block) + return self._call('eth_getTransactionByBlockNumberAndIndex', [block, hex(index)]) + + def eth_getTransactionReceipt(self, tx_hash): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt + + TESTED + ''' + return self._call('eth_getTransactionReceipt', [tx_hash]) + + def eth_getUncleByBlockHashAndIndex(self, block_hash, index=0): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblockhashandindex + + TESTED + ''' + return self._call('eth_getUncleByBlockHashAndIndex', [block_hash, hex(index)]) + + def eth_getUncleByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblocknumberandindex + + TESTED + ''' + 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 + + NEEDS TESTING + ''' + _filter = { + 'fromBlock': from_block, + 'toBlock': to_block, + 'address': address, + 'topics': topics, + } + return self._call('eth_newFilter', [_filter]) + + def eth_newBlockFilter(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter + + TESTED + ''' + return self._call('eth_newBlockFilter') + + def eth_newPendingTransactionFilter(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter + + TESTED + ''' + return hex_to_dec(self._call('eth_newPendingTransactionFilter')) + + def eth_uninstallFilter(self, filter_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter + + NEEDS TESTING + ''' + return self._call('eth_uninstallFilter', [filter_id]) + + def eth_getFilterChanges(self, filter_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges + + NEEDS TESTING + ''' + return self._call('eth_getFilterChanges', [filter_id]) + + def eth_getFilterLogs(self, filter_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs + + NEEDS TESTING + ''' + return self._call('eth_getFilterLogs', [filter_id]) + + def eth_getLogs(self, filter_object): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs + + NEEDS TESTING + ''' + return self._call('eth_getLogs', [filter_object]) + + def eth_getWork(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getwork + + TESTED + ''' + return self._call('eth_getWork') + + def eth_submitWork(self, nonce, header, mix_digest): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submitwork + + NEEDS TESTING + ''' + return self._call('eth_submitWork', [nonce, header, mix_digest]) + + def eth_submitHashrate(self, hash_rate, client_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submithashrate + + TESTED + ''' + return self._call('eth_submitHashrate', [hex(hash_rate), client_id]) + + def shh_version(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_version + + N/A + ''' + return self._call('shh_version') + + def shh_post(self, topics, payload, priority, ttl, from_=None, to=None): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_post + + NEEDS TESTING + ''' + whisper_object = { + 'from': from_, + 'to': to, + 'topics': topics, + 'payload': payload, + 'priority': hex(priority), + 'ttl': hex(ttl), + } + return self._call('shh_post', [whisper_object]) + + def shh_newIdentity(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newidentity + + N/A + ''' + return self._call('shh_newIdentity') + + def shh_hasIdentity(self, address): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_hasidentity + + NEEDS TESTING + ''' + return self._call('shh_hasIdentity', [address]) + + def shh_newGroup(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newgroup + + N/A + ''' + return self._call('shh_newGroup') + + def shh_addToGroup(self): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_addtogroup + + NEEDS TESTING + ''' + return self._call('shh_addToGroup') + + def shh_newFilter(self, to, topics): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newfilter + + NEEDS TESTING + ''' + _filter = { + 'to': to, + 'topics': topics, + } + return self._call('shh_newFilter', [_filter]) + + def shh_uninstallFilter(self, filter_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_uninstallfilter + + NEEDS TESTING + ''' + return self._call('shh_uninstallFilter', [filter_id]) + + def shh_getFilterChanges(self, filter_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getfilterchanges + + NEEDS TESTING + ''' + return self._call('shh_getFilterChanges', [filter_id]) + + def shh_getMessages(self, filter_id): + ''' + https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getmessages + + NEEDS TESTING + ''' + return self._call('shh_getMessages', [filter_id]) + + def getBlockRlp(self, number=0): + + return self._call('debug_getBlockRlp', [number]) + + def traceTransaction(self, txHash): + + return self._call('debug_traceTransaction', [txHash]) diff --git a/mythril/rpc/client.py b/mythril/rpc/client.py index f5bd4aa2..e1100778 100644 --- a/mythril/rpc/client.py +++ b/mythril/rpc/client.py @@ -3,13 +3,8 @@ import json import requests from requests.adapters import HTTPAdapter from requests.exceptions import ConnectionError as RequestsConnectionError -from ethereum import utils -from ethereum.abi import encode_abi, decode_abi - -from .constants import BLOCK_TAGS, BLOCK_TAG_LATEST -from .utils import hex_to_dec, clean_hex, validate_block -from .exceptions import (ConnectionError, BadStatusCodeError, - BadJsonError, BadResponseError) +from .exceptions import (ConnectionError, BadStatusCodeError, BadJsonError, BadResponseError) +from .base_client import BaseClient GETH_DEFAULT_RPC_PORT = 8545 ETH_DEFAULT_RPC_PORT = 8545 @@ -21,14 +16,11 @@ JSON_MEDIA_TYPE = 'application/json' ''' This code is adapted from: https://github.com/ConsenSys/ethjsonrpc ''' -class EthJsonRpc(object): +class EthJsonRpc(BaseClient): ''' Ethereum JSON-RPC client class ''' - DEFAULT_GAS_PER_TX = 90000 - DEFAULT_GAS_PRICE = 50 * 10**9 # 50 gwei - def __init__(self, host='localhost', port=GETH_DEFAULT_RPC_PORT, tls=False): self.host = host self.port = port @@ -65,624 +57,3 @@ class EthJsonRpc(object): except KeyError: raise BadResponseError(response) - def _encode_function(self, signature, param_values): - - prefix = utils.big_endian_to_int(utils.sha3(signature)[:4]) - - if signature.find('(') == -1: - raise RuntimeError('Invalid function signature. Missing "(" and/or ")"...') - - if signature.find(')') - signature.find('(') == 1: - return utils.encode_int(prefix) - - types = signature[signature.find('(') + 1: signature.find(')')].split(',') - encoded_params = encode_abi(types, param_values) - return utils.zpad(utils.encode_int(prefix), 4) + encoded_params - -################################################################################ -# high-level methods -################################################################################ - - def transfer(self, from_, to, amount): - ''' - Send wei from one address to another - ''' - return self.eth_sendTransaction(from_address=from_, to_address=to, value=amount) - - def create_contract(self, from_, code, gas, sig=None, args=None): - ''' - Create a contract on the blockchain from compiled EVM code. Returns the - transaction hash. - ''' - from_ = from_ or self.eth_coinbase() - if sig is not None and args is not None: - types = sig[sig.find('(') + 1: sig.find(')')].split(',') - encoded_params = encode_abi(types, args) - code += encoded_params.hex() - - return self.eth_sendTransaction(from_address=from_, gas=gas, data=code) - - def get_contract_address(self, tx): - ''' - Get the address for a contract from the transaction that created it - ''' - receipt = self.eth_getTransactionReceipt(tx) - return receipt['contractAddress'] - - def call(self, address, sig, args, result_types): - ''' - Call a contract function on the RPC server, without sending a - transaction (useful for reading data) - ''' - data = self._encode_function(sig, args) - data_hex = data.encode('hex') - response = self.eth_call(to_address=address, data=data_hex) - return decode_abi(result_types, response[2:].decode('hex')) - - def call_with_transaction(self, from_, address, sig, args, gas=None, gas_price=None, value=None): - ''' - Call a contract function by sending a transaction (useful for storing - data) - ''' - 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') - return self.eth_sendTransaction(from_address=from_, to_address=address, data=data_hex, gas=gas, - gas_price=gas_price, value=value) - -################################################################################ -# JSON-RPC methods -################################################################################ - - def web3_clientVersion(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_clientversion - - TESTED - ''' - return self._call('web3_clientVersion') - - def web3_sha3(self, data): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_sha3 - - TESTED - ''' - data = str(data).encode('hex') - return self._call('web3_sha3', [data]) - - def net_version(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version - - TESTED - ''' - return self._call('net_version') - - def net_listening(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#net_listening - - TESTED - ''' - return self._call('net_listening') - - def net_peerCount(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#net_peercount - - TESTED - ''' - return hex_to_dec(self._call('net_peerCount')) - - def eth_protocolVersion(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_protocolversion - - TESTED - ''' - return self._call('eth_protocolVersion') - - def eth_syncing(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_syncing - - TESTED - ''' - return self._call('eth_syncing') - - def eth_coinbase(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_coinbase - - TESTED - ''' - return self._call('eth_coinbase') - - def eth_mining(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_mining - - TESTED - ''' - return self._call('eth_mining') - - def eth_hashrate(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_hashrate - - TESTED - ''' - return hex_to_dec(self._call('eth_hashrate')) - - def eth_gasPrice(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gasprice - - TESTED - ''' - return hex_to_dec(self._call('eth_gasPrice')) - - def eth_accounts(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_accounts - - TESTED - ''' - return self._call('eth_accounts') - - def eth_blockNumber(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_blocknumber - - TESTED - ''' - return hex_to_dec(self._call('eth_blockNumber')) - - def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getbalance - - TESTED - ''' - address = address or self.eth_coinbase() - block = validate_block(block) - return hex_to_dec(self._call('eth_getBalance', [address, block])) - - def eth_getStorageAt(self, address=None, position=0, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat - - TESTED - ''' - block = validate_block(block) - return self._call('eth_getStorageAt', [address, hex(position), block]) - - def eth_getTransactionCount(self, address, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactioncount - - TESTED - ''' - block = validate_block(block) - return hex_to_dec(self._call('eth_getTransactionCount', [address, block])) - - def eth_getBlockTransactionCountByHash(self, block_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblocktransactioncountbyhash - - TESTED - ''' - return hex_to_dec(self._call('eth_getBlockTransactionCountByHash', [block_hash])) - - def eth_getBlockTransactionCountByNumber(self, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblocktransactioncountbynumber - - TESTED - ''' - block = validate_block(block) - return hex_to_dec(self._call('eth_getBlockTransactionCountByNumber', [block])) - - def eth_getUncleCountByBlockHash(self, block_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblockhash - - TESTED - ''' - return hex_to_dec(self._call('eth_getUncleCountByBlockHash', [block_hash])) - - def eth_getUncleCountByBlockNumber(self, block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblocknumber - - TESTED - ''' - block = validate_block(block) - return hex_to_dec(self._call('eth_getUncleCountByBlockNumber', [block])) - - def eth_getCode(self, address, default_block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcode - - NEEDS TESTING - ''' - if isinstance(default_block, str): - if default_block not in BLOCK_TAGS: - raise ValueError - return self._call('eth_getCode', [address, default_block]) - - def eth_sign(self, address, data): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign - - NEEDS TESTING - ''' - return self._call('eth_sign', [address, data]) - - def eth_sendTransaction(self, to_address=None, from_address=None, gas=None, gas_price=None, value=None, data=None, - nonce=None): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendtransaction - - NEEDS TESTING - ''' - params = {} - params['from'] = from_address or self.eth_coinbase() - if to_address is not None: - params['to'] = to_address - if gas is not None: - params['gas'] = hex(gas) - if gas_price is not None: - params['gasPrice'] = clean_hex(gas_price) - if value is not None: - params['value'] = clean_hex(value) - if data is not None: - params['data'] = data - if nonce is not None: - params['nonce'] = hex(nonce) - return self._call('eth_sendTransaction', [params]) - - def eth_sendRawTransaction(self, data): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendrawtransaction - - NEEDS TESTING - ''' - return self._call('eth_sendRawTransaction', [data]) - - def eth_call(self, to_address, from_address=None, gas=None, gas_price=None, value=None, data=None, - default_block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_call - - NEEDS TESTING - ''' - if isinstance(default_block, str): - if default_block not in BLOCK_TAGS: - raise ValueError - obj = {} - obj['to'] = to_address - if from_address is not None: - obj['from'] = from_address - if gas is not None: - obj['gas'] = hex(gas) - if gas_price is not None: - obj['gasPrice'] = clean_hex(gas_price) - if value is not None: - obj['value'] = value - if data is not None: - obj['data'] = data - return self._call('eth_call', [obj, default_block]) - - def eth_estimateGas(self, to_address=None, from_address=None, gas=None, gas_price=None, value=None, data=None, - default_block=BLOCK_TAG_LATEST): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas - - NEEDS TESTING - ''' - if isinstance(default_block, str): - if default_block not in BLOCK_TAGS: - raise ValueError - obj = {} - if to_address is not None: - obj['to'] = to_address - if from_address is not None: - obj['from'] = from_address - if gas is not None: - obj['gas'] = hex(gas) - if gas_price is not None: - obj['gasPrice'] = clean_hex(gas_price) - if value is not None: - obj['value'] = value - if data is not None: - obj['data'] = data - return hex_to_dec(self._call('eth_estimateGas', [obj, default_block])) - - def eth_getBlockByHash(self, block_hash, tx_objects=True): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbyhash - - TESTED - ''' - return self._call('eth_getBlockByHash', [block_hash, tx_objects]) - - def eth_getBlockByNumber(self, block=BLOCK_TAG_LATEST, tx_objects=True): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber - - TESTED - ''' - block = validate_block(block) - return self._call('eth_getBlockByNumber', [block, tx_objects]) - - def eth_getTransactionByHash(self, tx_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyhash - - TESTED - ''' - return self._call('eth_getTransactionByHash', [tx_hash]) - - def eth_getTransactionByBlockHashAndIndex(self, block_hash, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblockhashandindex - - TESTED - ''' - return self._call('eth_getTransactionByBlockHashAndIndex', [block_hash, hex(index)]) - - def eth_getTransactionByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblocknumberandindex - - TESTED - ''' - block = validate_block(block) - return self._call('eth_getTransactionByBlockNumberAndIndex', [block, hex(index)]) - - def eth_getTransactionReceipt(self, tx_hash): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt - - TESTED - ''' - return self._call('eth_getTransactionReceipt', [tx_hash]) - - def eth_getUncleByBlockHashAndIndex(self, block_hash, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblockhashandindex - - TESTED - ''' - return self._call('eth_getUncleByBlockHashAndIndex', [block_hash, hex(index)]) - - def eth_getUncleByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblocknumberandindex - - TESTED - ''' - 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 - - NEEDS TESTING - ''' - _filter = { - 'fromBlock': from_block, - 'toBlock': to_block, - 'address': address, - 'topics': topics, - } - return self._call('eth_newFilter', [_filter]) - - def eth_newBlockFilter(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter - - TESTED - ''' - return self._call('eth_newBlockFilter') - - def eth_newPendingTransactionFilter(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter - - TESTED - ''' - return hex_to_dec(self._call('eth_newPendingTransactionFilter')) - - def eth_uninstallFilter(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter - - NEEDS TESTING - ''' - return self._call('eth_uninstallFilter', [filter_id]) - - def eth_getFilterChanges(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges - - NEEDS TESTING - ''' - return self._call('eth_getFilterChanges', [filter_id]) - - def eth_getFilterLogs(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs - - NEEDS TESTING - ''' - return self._call('eth_getFilterLogs', [filter_id]) - - def eth_getLogs(self, filter_object): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs - - NEEDS TESTING - ''' - return self._call('eth_getLogs', [filter_object]) - - def eth_getWork(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getwork - - TESTED - ''' - return self._call('eth_getWork') - - def eth_submitWork(self, nonce, header, mix_digest): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submitwork - - NEEDS TESTING - ''' - return self._call('eth_submitWork', [nonce, header, mix_digest]) - - def eth_submitHashrate(self, hash_rate, client_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submithashrate - - TESTED - ''' - return self._call('eth_submitHashrate', [hex(hash_rate), client_id]) - - def shh_version(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_version - - N/A - ''' - return self._call('shh_version') - - def shh_post(self, topics, payload, priority, ttl, from_=None, to=None): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_post - - NEEDS TESTING - ''' - whisper_object = { - 'from': from_, - 'to': to, - 'topics': topics, - 'payload': payload, - 'priority': hex(priority), - 'ttl': hex(ttl), - } - return self._call('shh_post', [whisper_object]) - - def shh_newIdentity(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newidentity - - N/A - ''' - return self._call('shh_newIdentity') - - def shh_hasIdentity(self, address): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_hasidentity - - NEEDS TESTING - ''' - return self._call('shh_hasIdentity', [address]) - - def shh_newGroup(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newgroup - - N/A - ''' - return self._call('shh_newGroup') - - def shh_addToGroup(self): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_addtogroup - - NEEDS TESTING - ''' - return self._call('shh_addToGroup') - - def shh_newFilter(self, to, topics): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newfilter - - NEEDS TESTING - ''' - _filter = { - 'to': to, - 'topics': topics, - } - return self._call('shh_newFilter', [_filter]) - - def shh_uninstallFilter(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_uninstallfilter - - NEEDS TESTING - ''' - return self._call('shh_uninstallFilter', [filter_id]) - - def shh_getFilterChanges(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getfilterchanges - - NEEDS TESTING - ''' - return self._call('shh_getFilterChanges', [filter_id]) - - def shh_getMessages(self, filter_id): - ''' - https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getmessages - - NEEDS TESTING - ''' - return self._call('shh_getMessages', [filter_id]) - - def getBlockRlp(self, number=0): - - return self._call('debug_getBlockRlp', [number]) - - def traceTransaction(self, txHash): - - return self._call('debug_traceTransaction', [txHash]) - diff --git a/requirements.txt b/requirements.txt index 61630a50..fd95aaba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,15 @@ ethereum>=2.0.4 ZODB>=5.3.0 z3-solver>=4.5 -web3 laser-ethereum==0.5.9 requests BTrees py-solc +eth_abi>=1.0.0 +eth-utils>=1.0.1 +eth-account>=0.1.0a2 +eth-hash>=0.1.0 +eth-keyfile>=0.5.1 +eth-keys>=0.2.0b3 +eth-rlp>=0.1.0 +eth-tester>=0.1.0b21 \ No newline at end of file diff --git a/setup.py b/setup.py index 7d3e2d07..c4cdabf6 100755 --- a/setup.py +++ b/setup.py @@ -288,7 +288,6 @@ setup( install_requires=[ 'ethereum>=2.0.4', - 'web3', 'ZODB>=5.3.0', 'z3-solver>=4.5', 'laser-ethereum==0.5.9',