# standard imports import copy import logging import json import datetime import time import socket from urllib.request import ( Request, urlopen, ) # third-party imports from hexathon import ( add_0x, strip_0x, ) # local imports from .error import ( DefaultErrorParser, RevertEthException, ) from .sign import ( sign_transaction, ) from chainlib.connection import ( ConnType, RPCConnection, JSONRPCHTTPConnection, JSONRPCUnixConnection, error_parser, ) from chainlib.jsonrpc import ( jsonrpc_template, jsonrpc_result, ) from chainlib.eth.tx import ( unpack, ) logg = logging.getLogger(__name__) class EthHTTPConnection(JSONRPCHTTPConnection): def wait(self, tx_hash_hex, delay=0.5, timeout=0.0, error_parser=error_parser): t = datetime.datetime.utcnow() i = 0 while True: o = jsonrpc_template() o['method'] ='eth_getTransactionReceipt' o['params'].append(add_0x(tx_hash_hex)) req = Request( self.location, method='POST', ) req.add_header('Content-Type', 'application/json') data = json.dumps(o) logg.debug('(HTTP) poll receipt attempt {} {}'.format(i, data)) res = urlopen(req, data=data.encode('utf-8')) r = json.load(res) e = jsonrpc_result(r, error_parser) if e != None: logg.debug('(HTTP) poll receipt completed {}'.format(r)) logg.debug('e {}'.format(strip_0x(e['status']))) if strip_0x(e['status']) == '00': raise RevertEthException(tx_hash_hex) return e if timeout > 0.0: delta = (datetime.datetime.utcnow() - t) + datetime.timedelta(seconds=delay) if delta.total_seconds() >= timeout: raise TimeoutError(tx_hash) time.sleep(delay) i += 1 class EthUnixConnection(JSONRPCUnixConnection): def wait(self, tx_hash_hex, delay=0.5, timeout=0.0, error_parser=error_parser): raise NotImplementedError('Not yet implemented for unix socket') def sign_transaction_to_rlp(chain_spec, doer, tx): txs = tx.serialize() logg.debug('serializing {}'.format(txs)) # TODO: because some rpc servers may fail when chainId is included, we are forced to spend cpu here on this chain_id = txs.get('chainId') or 1 if chain_spec != None: chain_id = chain_spec.chain_id() txs['chainId'] = add_0x(chain_id.to_bytes(2, 'big').hex()) txs['from'] = add_0x(tx.sender) o = sign_transaction(txs) r = doer(o) logg.debug('sig got {}'.format(r)) return bytes.fromhex(strip_0x(r)) def sign_message(doer, msg): o = sign_message(msg) return doer(o) class EthUnixSignerConnection(EthUnixConnection): def sign_transaction_to_rlp(self, tx): return sign_transaction_to_rlp(self.chain_spec, self.do, tx) def sign_message(self, tx): return sign_message(self.do, tx) class EthHTTPSignerConnection(EthHTTPConnection): def sign_transaction_to_rlp(self, tx): return sign_transaction_to_rlp(self.chain_spec, self.do, tx) def sign_message(self, tx): return sign_message(self.do, tx) RPCConnection.register_constructor(ConnType.HTTP, EthHTTPConnection, tag='eth_default') RPCConnection.register_constructor(ConnType.HTTP_SSL, EthHTTPConnection, tag='eth_default') RPCConnection.register_constructor(ConnType.UNIX, EthUnixConnection, tag='eth_default')