diff --git a/etheno/jsonrpc.py b/etheno/jsonrpc.py index 05f36b3..c315353 100644 --- a/etheno/jsonrpc.py +++ b/etheno/jsonrpc.py @@ -4,6 +4,60 @@ from .etheno import EthenoPlugin from .utils import format_hex_address + +# source: https://ethereum.stackexchange.com/a/83855 +import rlp +from eth_typing import HexStr +from eth_utils import keccak, to_bytes +from rlp.sedes import Binary, big_endian_int, binary +from web3 import Web3 +from web3.auto import w3 + + +class Transaction(rlp.Serializable): + fields = [ + ("nonce", big_endian_int), + ("gas_price", big_endian_int), + ("gas", big_endian_int), + ("to", Binary.fixed_length(20, allow_empty=True)), + ("value", big_endian_int), + ("data", binary), + ("v", big_endian_int), + ("r", big_endian_int), + ("s", big_endian_int), + ] + + +def hex_to_bytes(data: str) -> bytes: + return to_bytes(hexstr=HexStr(data)) + + +def decode_raw_tx(raw_tx: str): + tx_bytes = hex_to_bytes(raw_tx) + tx = rlp.decode(tx_bytes, Transaction) + hash_tx = Web3.toHex(keccak(tx_bytes)) + from_ = w3.eth.account.recover_transaction(raw_tx) + to = w3.toChecksumAddress(tx.to) if tx.to else None + data = w3.toHex(tx.data) + r = hex(tx.r) + s = hex(tx.s) + chain_id = (tx.v - 35) // 2 if tx.v % 2 else (tx.v - 36) // 2 + return { + 'txHash': hash_tx, + 'from': from_, + 'to': to, + 'nonce': tx.nonce, + 'gas': tx.gas, + 'gasPrice': tx.gas_price, + 'value': tx.value, + 'data': data, + 'chainId': chain_id, + 'r': r, + 's': s, + 'v': tx.v + } + + class JSONExporter: def __init__(self, out_stream: Union[str, TextIO]): self._was_path = isinstance(out_stream, str) @@ -65,12 +119,15 @@ def after_post(self, post_data, result): result = result[0] if 'method' not in post_data: return - elif post_data['method'] == 'eth_sendTransaction' and 'result' in result: + elif (post_data['method'] == 'eth_sendTransaction' or post_data['method'] == 'eth_sendRawTransaction') and 'result' in result: try: transaction_hash = int(result['result'], 16) except ValueError: return - self._transactions[transaction_hash] = post_data + if post_data['method'] == 'eth_sendRawTransaction': + self._transactions[transaction_hash] = decode_raw_tx(post_data['params'][0]) + else: + self._transactions[transaction_hash] = post_data['params'][0] elif post_data['method'] == 'evm_mine': self.handle_increase_block_number() elif post_data['method'] == 'evm_increaseTime': @@ -80,7 +137,7 @@ def after_post(self, post_data, result): if transaction_hash not in self._transactions: self.logger.error(f'Received transaction receipt {result} for unknown transaction hash {post_data["params"][0]}') return - original_transaction = self._transactions[transaction_hash]['params'][0] + original_transaction = self._transactions[transaction_hash] if 'value' not in original_transaction or original_transaction['value'] is None: value = '0x0' else: