def worker(*, uid: int, endpoint: str): ctx = zmq.Context() dispatcher = RPCDispatcher() client = RPCClient( JSONRPCProtocol(), ZmqClientTransport.create(ctx, endpoint), ) proxy = client.get_proxy() @dispatcher.public def do_task(): from time import sleep for i in range(5): proxy.notify(f"{uid} y{i} ") sleep(0.1) return f"OK {uid}" @dispatcher.public def exit(): proxy.exit(uid) transport = ZmqServerTransport.create( ctx, endpoint.replace(".manager", f".{uid}")) rpc_server = RPCServer(transport, JSONRPCProtocol(), dispatcher) rpc_server.trace = partial(print, file=sys.stderr) rpc_server.serve_forever()
def __init__(self, host, port, privkey, nonce_update_interval=5.0, nonce_offset=0): endpoint = 'http://{}:{}'.format(host, port) session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_maxsize=50) session.mount(endpoint, adapter) self.transport = HttpPostClientTransport( endpoint, post_method=session.post, headers={'content-type': 'application/json'}, ) self.port = port self.privkey = privkey self.protocol = JSONRPCProtocol() self.sender = privatekey_to_address(privkey) # Needs to be initialized to None in the beginning since JSONRPCClient # gets constructed before the RaidenService Object. self.stop_event = None self.nonce_last_update = 0 self.nonce_current_value = None self.nonce_lock = Semaphore() self.nonce_update_interval = nonce_update_interval self.nonce_offset = nonce_offset
def __init__(self, host, port, privkey, nonce_update_interval=5.0, nonce_offset=0): endpoint = 'http://{}:{}'.format(host, port) session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_maxsize=50) session.mount(endpoint, adapter) self.transport = HttpPostClientTransport( endpoint, post_method=session.post, headers={'content-type': 'application/json'}, ) self.port = port self.privkey = privkey self.protocol = JSONRPCProtocol() self.sender = privatekey_to_address(privkey) self.nonce_last_update = 0 self.nonce_current_value = None self.nonce_lock = Semaphore() self.nonce_update_interval = nonce_update_interval self.nonce_offset = nonce_offset
def client(*, endpoint: str, callback_endpoint: str): ctx = zmq.Context() dispatcher = RPCDispatcher() @dispatcher.public def notify(s: str): print("** {s} **") callback_server = RPCServer( ZmqServerTransport.create(ctx, callback_endpoint), JSONRPCProtocol(), dispatcher) callback_server.trace = print threading.Thread(target=callback_server.serve_forever, daemon=True).start() rpc_client = RPCClient(JSONRPCProtocol(), ZmqClientTransport.create(ctx, endpoint)) remote_server = rpc_client.get_proxy() for i in range(7): result = remote_server.act(i, "Hello, World!") print("Server answered:", result) result = remote_server.shutdown() print("Send shutdown:", result)
def __init__(self, function_lists, is_id_valid): self._rpc_dispatcher = RPCDispatcher() self._rpc_protocol = JSONRPCProtocol() self._is_id_valid = is_id_valid for f in function_lists: self._rpc_dispatcher.add_method(f) logger.info("RPC service initialized")
def __init__( self, transport: Callable[[bytes, Optional[float]], Awaitable[bytes]], app: BaseApplication, exception_mapping_callback: Optional[Callable[ [Optional[int], Optional[str], Optional[Any]], None]] = None, ): self._app = app self._proto = JSONRPCProtocol() self._transport = transport self._exception_mapping_callback = exception_mapping_callback
def __init__(self, host: str, port: int, privkey: bytes, gasprice: int = None, nonce_update_interval: float = 5.0, nonce_offset: int = 0): if privkey is None or len(privkey) != 32: raise ValueError('Invalid private key') endpoint = 'http://{}:{}'.format(host, port) self.session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_maxsize=50) self.session.mount(endpoint, adapter) self.transport = HttpPostClientTransport( endpoint, post_method=self.session.post, headers={'content-type': 'application/json'}, ) self.port = port self.privkey = privkey self.protocol = JSONRPCProtocol() self.sender = privatekey_to_address(privkey) # Needs to be initialized to None in the beginning since JSONRPCClient # gets constructed before the RaidenService Object. self.stop_event = None self.nonce_last_update = 0 self.nonce_available_value = None self.nonce_lock = Semaphore() self.nonce_update_interval = nonce_update_interval self.nonce_offset = nonce_offset self.given_gas_price = gasprice cache = cachetools.TTLCache( maxsize=1, ttl=RPC_CACHE_TTL, ) cache_wrapper = cachetools.cached(cache=cache) self.gaslimit = cache_wrapper(self._gaslimit) cache = cachetools.TTLCache( maxsize=1, ttl=RPC_CACHE_TTL, ) cache_wrapper = cachetools.cached(cache=cache) self.gasprice = cache_wrapper(self._gasprice)
def __init__(self, ip_hostname='pulsestreamer'): print("Connect to Pulse Streamer via JSON-RPC.") print("IP / Hostname:", ip_hostname) url = 'http://' + ip_hostname + ':8050/json-rpc' try: self.INFINITE = -1 self.CONSTANT_ZERO = (0, 0, 0, 0) if sys.version_info.major > 2: client = RPCClient(url) else: client = RPCClient(JSONRPCProtocol(), HttpPostClientTransport(url)) self.proxy = client.get_proxy() try: self.proxy.getSerial() except: try: self.proxy.isRunning() print( "Pulse Streamer class not compatible with current firmware. Please update your firmware." ) sys.exit(1) except: print("No Pulse Streamer found at IP/Host-address: " + ip_hostname) sys.exit(1) except: print("No Pulse Streamer found at IP/Host-address: " + ip_hostname) sys.exit(1)
def nsZRPCBringupServer(ns, path, conn, maxconn): name = os.path.basename(path) dev_path = '/dev/zrpc/{}'.format(name) if nsRPCisServer(ns, path) is not True: nsError(ns, "ZRPC service {} misconfigured".format(name)) return False nsInfo(ns, "Configuring ZRPC server from {}".format(path)) nsMkdir(ns, dev_path) nsMkdir(ns, "{}/root".format(dev_path)) _to_root = nsGet(ns, "{}/jail".format(path), []) for j in _to_root: _n = os.path.basename(j) _dst = "{}/root/{}".format(dev_path, _n) nsInfo(ns, "ZRPC.JAIL({}): {}".format(name, j)) nsLn(ns, j, _dst) dispatcher = RPCDispatcher() nsSet(ns, "{}/dispatcher".format(dev_path), dispatcher) for h in nsLs(ns, "{}/handlers".format(path)): nsInfo(ns, "Registering {}->{} ".format(name, h)) _fun = nsGet(ns, "{}/handlers/{}".format(path, h)) dispatcher.add_method(partial(_fun, dev_path), h) transport = ZmqServerTransport.create(ctx, conn) nsSet(ns, "{}/transport".format(dev_path), transport) nsSet(ns, "{}/conn".format(dev_path), conn) rpc_server = RPCServerGreenlets(transport, JSONRPCProtocol(), dispatcher) nsSet(ns, "{}/rpc", rpc_server) nsDaemon(ns, "{}:ZRPC".format(name), rpc_server.serve_forever, _raw=True) nsInfo(ns, "ZRPC server {} is up".format(name)) return True
def authenticate(self): """ Performs authentication actions Patches `tinyrpc` to remove minor incompatibilities due to JSON-RPC protocol version differences. This is performed once. Initializes the RPCClient and RPCProxy instances that are used to make the requests to Lime. :return: None """ logger.info("Authenticating LimeAPI client") if not LimeAPI._rpc_protocol_patched: LimeAPI.patch_json_rpc_protocol() LimeAPI._rpc_protocol_patched = True self.rpc_client = RPCClient( JSONRPCProtocol(), HttpPostClientTransport(endpoint=self.remote_api_url, headers=self.headers), ) self.rpc_proxy = self.rpc_client.get_proxy() self.session_key = self.rpc_proxy.get_session_key( username=self.username, password=self.password) if not self._validate_session_key(): raise Exception(f"Failed to validate session key: url={self.url} " f"session_key={self.session_key}") self._authenticated = True logger.info(f"Acquired session key: {self.session_key}")
def test_argument_error(dispatch, invoke_with): method, args, kwargs, result = invoke_with protocol = JSONRPCProtocol() @dispatch.public def fn_a(a, b): return a - b @dispatch.public def fn_b(*a): return a[0] - a[1] @dispatch.public def fn_c(**a): return a['a'] - a['b'] mock_request = Mock(RPCRequest) mock_request.args = args mock_request.kwargs = kwargs mock_request.method = method dispatch._dispatch(mock_request) if inspect.isclass(result) and issubclass(result, Exception): assert type(mock_request.error_respond.call_args[0][0]) == result else: mock_request.respond.assert_called_with(result)
def __init__( self, host: str, port: int, privkey: bytes, nonce_update_interval: float = 5.0, nonce_offset: int = 0): endpoint = 'http://{}:{}'.format(host, port) session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_maxsize=50) session.mount(endpoint, adapter) self.transport = HttpPostClientTransport( endpoint, post_method=session.post, headers={'content-type': 'application/json'}, ) self.port = port self.privkey = privkey self.protocol = JSONRPCProtocol() self.sender = privatekey_to_address(privkey) # Needs to be initialized to None in the beginning since JSONRPCClient # gets constructed before the RaidenService Object. self.stop_event = None self.nonce_last_update = 0 self.nonce_current_value = None self.nonce_lock = Semaphore() self.nonce_update_interval = nonce_update_interval self.nonce_offset = nonce_offset
def __init__(self, ws): self.ws = ws self.queue = hub.Queue() super(WebSocketRPCClient, self).__init__( JSONRPCProtocol(), WebSocketClientTransport(ws, self.queue), )
class RPCSocketHandler(WebSocketHandler): ''' This is **not** a singleton. There is an instance for every client. ''' protocol = JSONRPCProtocol() # one instance is enough. # this needs to be implemented by a subclass. Dict: method_string --> # Procedure(method_callback) procedures = None def on_message(self, message): json_request = self.protocol.parse_request(message) # procedures (dict: sting --> method) must be implemented in a # subclass! callback = self.procedures.get(json_request.method) if callback is None: response = json_request.error_respond('Method %r not implemented' % json_request.method) self.write_message(response.serialize()) return try: result = callback(json_request) except Exception as exc: logger.error(exc, exc_info=True) response = json_request.error_respond(repr(exc)) self.write_message(response.serialize()) return return self.return_result(result, json_request) def return_result(self, result, json_request): response = json_request.respond(result) self.write_message(response.serialize())
def test_async_argument_error(async_dispatch, invoke_with, event_loop): method, args, kwargs, result = invoke_with protocol = JSONRPCProtocol() @async_dispatch.public async def fn_a(a, b): return a - b @async_dispatch.public async def fn_b(*a): return a[0] - a[1] @async_dispatch.public async def fn_c(**a): return a['a'] - a['b'] mock_request = Mock(RPCRequest) mock_request.args = args mock_request.kwargs = kwargs mock_request.method = method event_loop.run_until_complete(async_dispatch._dispatch(mock_request)) if inspect.isclass(result) and issubclass(result, Exception): assert type(mock_request.error_respond.call_args[0][0]) is result else: mock_request.respond.assert_called_with(result)
def test_static_method_argument_error(dispatch, invoke_with): method, args, kwargs, result = invoke_with protocol = JSONRPCProtocol() class Test: c = 0 @staticmethod @public def fn_a(a, b): return a - b @staticmethod @public def fn_b(*a): return a[0] - a[1] @staticmethod @public def fn_c(**a): return a['a'] - a['b'] test = Test() dispatch.register_instance(test) mock_request = Mock(RPCRequest) mock_request.args = args mock_request.kwargs = kwargs mock_request.method = method dispatch._dispatch(mock_request, getattr(protocol, '_caller', None)) if inspect.isclass(result) and issubclass(result, Exception): assert type(mock_request.error_respond.call_args[0][0]) == result else: mock_request.respond.assert_called_with(result)
def test_async_class_method_argument_error(async_dispatch, invoke_with, event_loop): method, args, kwargs, result = invoke_with protocol = JSONRPCProtocol() class Test: c = 0 @classmethod @public async def fn_a(cls, a, b): return a - b - cls.c @classmethod @public async def fn_b(cls, *a): return a[0] - a[1] - cls.c @classmethod @public async def fn_c(cls, **a): return a['a'] - a['b'] - cls.c test = Test() async_dispatch.register_instance(test) mock_request = Mock(RPCRequest) mock_request.args = args mock_request.kwargs = kwargs mock_request.method = method event_loop.run_until_complete(async_dispatch._dispatch(mock_request)) if inspect.isclass(result) and issubclass(result, Exception): assert type(mock_request.error_respond.call_args[0][0]) == result else: mock_request.respond.assert_called_with(result)
def login(self, username, password): self.rpc_client = RPCClient( JSONRPCProtocol(), HttpWebSocketClientTransport('ws://localhost:9001/api/ws')) self.loginManager = self.rpc_client.get_proxy("login.") hashed_password = hashlib.sha256(password).hexdigest() return self.loginManager.authenticate(username=username, password=hashed_password)
def __init__(self, ws, rpc_callback): dispatcher = RPCDispatcher() dispatcher.register_instance(rpc_callback) super(WebSocketRPCServer, self).__init__( WebSocketServerTransport(ws), JSONRPCProtocol(), dispatcher, )
def __init__(self, node_url=None): rpc_client = RPCClient( JSONRPCProtocol(), HttpPostClientTransport( node_url or os.getenv("RADIX_NODE_URL") or DEFAULT_NODE_URL ), ) self._rpc_proxy = rpc_client.get_proxy(prefix=RPC_METHOD_PREFIX)
def __init__(self, obj, one_way=False): url = obj.env['device_manager.settings']._get_param( 'mqtt_rpc_bridge_url') logger.debug('HTTP bridge url {}'.format(url)) rpc_client = RPCClient( JSONRPCProtocol(), HttpPostClientTransport(url) ) self.proxy = rpc_client.get_proxy(one_way=one_way)
def __init__(self, mqtt_url=None, client_uid=None, loop=None, config=None): if not loop: loop = asyncio.get_event_loop() self.loop = loop self.protocol = JSONRPCProtocol() self.dispatcher = dispatcher self.mqtt_url = mqtt_url if mqtt_url else MQTT_URL self.mqtt_reply_timeout = MQTT_REPLY_TIMEOUT self.client_uid = client_uid if client_uid else CLIENT_UID super(MQTTRPC, self).__init__(client_id=self.client_uid, loop=loop, config=config) for signame in ('SIGINT', 'SIGTERM'): self.loop.add_signal_handler( getattr(signal, signame), lambda: asyncio.ensure_future(self.stop())) logger.info('Client {} initialized'.format(self.client_uid))
def worker(): rpc_server = RPCServer( SubprocessServerTransport( input_port=sys.stdin.buffer, output_port=sys.stdout.buffer ), JSONRPCProtocol(), dispatcher, ) rpc_server.trace = partial(print, file=sys.stderr) rpc_server.serve_forever()
def WebSocketRPCHandler(trustlines: TrustlinesRelay): dispatcher = RPCDispatcher() dispatcher.add_method(partial(subscribe, trustlines), "subscribe") protocol = JSONRPCProtocol() def handle(ws): app = RPCWebSocketApplication(protocol, dispatcher, ws) app.handle() return handle
def __init__(self, rest_url=None): self.dispatcher = RESTDispatcher(rest_url) # TinyRPC WSGI App self.transport = WsgiServerTransport(queue_class=gevent.queue.Queue) self.wsgi_app = self.transport.handle # TinyRPC RPC Server self.rpc_server = RPCServerGreenlets(self.transport, JSONRPCProtocol(), self.dispatcher) gevent.spawn(self.rpc_server.serve_forever)
def MessagingWebSocketRPCHandler(trustlines: TrustlinesRelay): dispatcher = RPCDispatcher() dispatcher.add_method(partial(messaging_subscribe, trustlines), "listen") dispatcher.add_method(partial(get_missed_messages, trustlines), "getMissedMessages") protocol = JSONRPCProtocol() def handle(ws): app = RPCWebSocketApplication(protocol, dispatcher, ws) app.handle() return handle
def __init__(self, app): log.debug('initializing JSONRPCServer') BaseService.__init__(self, app) self.app = app self.dispatcher = RPCDispatcher() transport = WsgiServerTransport(queue_class=gevent.queue.Queue) # start wsgi server as a background-greenlet self.wsgi_server = gevent.wsgi.WSGIServer(('127.0.0.1', 5000), transport.handle) self.rpc_server = RPCServerGreenlets(transport, JSONRPCProtocol(), self.dispatcher)
def connectHandler(cmd, handler): dispatcher = RPCDispatcher() print("cmd: {}".format(" ".join(cmd))) # line buffered p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) transport = PipeTransport(p.stdout, p.stdin) rpc_server = RPCServer(transport, JSONRPCProtocol(), dispatcher) dispatcher.register_instance(handler, '') return (rpc_server, p)
def worker(): ctx = zmq.Context() rpc_client = RPCClient( JSONRPCProtocol(), ZmqClientTransport.create(ctx, "tcp://127.0.0.1:5002")) proxy = rpc_client.get_proxy() for message in iter(partial(recv, port=sys.stdin), ""): proxy.post(f"hello {message}") import time time.sleep(0.4) sys.stdout.write("") sys.stdout.close()
def gevent_main(wotabag, dispatcher, sdp_transport): """Secondary thread for gevent event loop. Needed so that it does not conflict with dbus/glib event loop. """ logger = logging.getLogger('wotabag') ble_rpc_server = WotabagRPCServerGreenlets( sdp_transport, JSONRPCProtocol(), dispatcher ) # Configure WSGI (HTTP) RPC server wsgi_transport = WsgiServerTransport(queue_class=gevent.queue.Queue) wsgi_server = WSGIServer((wotabag.rpc_host, wotabag.rpc_port), wsgi_transport.handle) gevent.spawn(wsgi_server.serve_forever) wsgi_rpc_server = WotabagRPCServerGreenlets( wsgi_transport, JSONRPCProtocol(), dispatcher ) try: greenlets = [] logger.info("Running RPC server at {}:{}".format(wotabag.rpc_host, wotabag.rpc_port)) greenlets.append(wsgi_rpc_server.start()) greenlets.append(ble_rpc_server.start()) gevent.sleep(0) server_done.wait() except Exception as e: logger.exception(e) raise e finally: gevent.joinall(greenlets) logger.info("RPC server finished")
def __init__(self, transport, publisher=None, ctx=None, protocol=None, dispatcher=None): self.ctx = ctx if ctx else zmq.Context().instance() self.protocol = protocol if protocol else JSONRPCProtocol() self.dispatcher = dispatcher if dispatcher else RPCDispatcher() self.publisher = publisher if publisher else NoOpPublisher() if isinstance(transport, dict): # dictionary: if 'receiver' in transport and 'replier' in transport: self.endpoints = transport else: msg = 'endpoint dictionary {} should contains receiver and replier' raise Exception(msg.format(transport)) self.endpoint = self.endpoints['receiver'] elif isinstance(transport, basestring): # only 1 endpoint is provided; create endpoint for replier by adding port by 10000 pattern = '(tcp://)?((?P<ip>[0-9.*]+):)?(?P<port>[0-9]+)' re_groups = re.match(pattern, transport.strip()) if not re_groups: raise Exception( 'Invalid transport format {}; ' 'expecting tcp://IP:PORT or IP:PORT'.format(transport)) replier_port = int(re_groups.group('port')) + 10000 ip = re_groups.group('ip') if re_groups.group('ip') else '*' receiver_endpoint = 'tcp://{}:{}'.format(ip, replier_port) replier_endpoint = 'tcp://{}:{}'.format(ip, replier_port) self.endpoints = { 'receiver': transport, 'replier': replier_endpoint } self.endpoint = self.endpoints['receiver'] else: # existing transport instance self.endpoint = transport logger_name = self.endpoint.replace('://', '') logger_name = logger_name.replace(':', '_') logger_name = logger_name.replace('tcp', '') self.logger = RPCLogger(logger_name) self.init_server(self.endpoints, self.logger) self.server_mode = 'normal'
class JSONRPCClient: """ Ethereum JSON RPC client. Args: host: Ethereum node host address. port: Ethereum node port number. privkey: Local user private key, used to sign transactions. nonce_update_interval: Update the account nonce every `nonce_update_interval` seconds. nonce_offset: Network's default base nonce number. """ def __init__( self, host: str, port: int, privkey: bytes, nonce_update_interval: float = 5.0, nonce_offset: int = 0): endpoint = 'http://{}:{}'.format(host, port) session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_maxsize=50) session.mount(endpoint, adapter) self.transport = HttpPostClientTransport( endpoint, post_method=session.post, headers={'content-type': 'application/json'}, ) self.port = port self.privkey = privkey self.protocol = JSONRPCProtocol() self.sender = privatekey_to_address(privkey) # Needs to be initialized to None in the beginning since JSONRPCClient # gets constructed before the RaidenService Object. self.stop_event = None self.nonce_last_update = 0 self.nonce_current_value = None self.nonce_lock = Semaphore() self.nonce_update_interval = nonce_update_interval self.nonce_offset = nonce_offset def __repr__(self): return '<JSONRPCClient @%d>' % self.port def block_number(self): """ Return the most recent block. """ return quantity_decoder(self.call('eth_blockNumber')) def nonce(self, address): if len(address) == 40: address = unhexlify(address) with self.nonce_lock: initialized = self.nonce_current_value is not None query_time = now() if self.nonce_last_update > query_time: # Python's 2.7 time is not monotonic and it's affected by clock # resets, force an update. self.nonce_update_interval = query_time - self.nonce_update_interval needs_update = True else: last_update_interval = query_time - self.nonce_last_update needs_update = last_update_interval > self.nonce_update_interval if initialized and not needs_update: self.nonce_current_value += 1 return self.nonce_current_value pending_transactions_hex = self.call( 'eth_getTransactionCount', address_encoder(address), 'pending', ) pending_transactions = quantity_decoder(pending_transactions_hex) nonce = pending_transactions + self.nonce_offset # we may have hammered the server and not all tx are # registered as `pending` yet if initialized: while nonce < self.nonce_current_value: log.debug( 'nonce on server too low; retrying', server=nonce, local=self.nonce_current_value, ) query_time = now() pending_transactions_hex = self.call( 'eth_getTransactionCount', address_encoder(address), 'pending', ) pending_transactions = quantity_decoder(pending_transactions_hex) nonce = pending_transactions + self.nonce_offset self.nonce_current_value = nonce self.nonce_last_update = query_time return self.nonce_current_value def inject_stop_event(self, event): self.stop_event = event def balance(self, account: address): """ Return the balance of the account of given address. """ res = self.call('eth_getBalance', address_encoder(account), 'pending') return quantity_decoder(res) def gaslimit(self) -> int: last_block = self.call('eth_getBlockByNumber', 'latest', True) gas_limit = quantity_decoder(last_block['gasLimit']) return gas_limit def new_contract_proxy(self, contract_interface, contract_address: address): """ Return a proxy for interacting with a smart contract. Args: contract_interface: The contract interface as defined by the json. address: The contract's address. """ return ContractProxy( self.sender, contract_interface, contract_address, self.eth_call, self.send_transaction, self.eth_estimateGas, ) def deploy_solidity_contract( self, # pylint: disable=too-many-locals sender, contract_name, all_contracts, libraries, constructor_parameters, contract_path=None, timeout=None, gasprice=GAS_PRICE): """ Deploy a solidity contract. Args: sender (address): the sender address contract_name (str): the name of the contract to compile all_contracts (dict): the json dictionary containing the result of compiling a file libraries (list): A list of libraries to use in deployment constructor_parameters (tuple): A tuple of arguments to pass to the constructor contract_path (str): If we are dealing with solc >= v0.4.9 then the path to the contract is a required argument to extract the contract data from the `all_contracts` dict. timeout (int): Amount of time to poll the chain to confirm deployment gasprice: The gasprice to provide for the transaction """ if contract_name in all_contracts: contract_key = contract_name elif contract_path is not None: _, filename = os.path.split(contract_path) contract_key = filename + ':' + contract_name if contract_key not in all_contracts: raise ValueError('Unknown contract {}'.format(contract_name)) else: raise ValueError( 'Unknown contract {} and no contract_path given'.format(contract_name) ) libraries = dict(libraries) contract = all_contracts[contract_key] contract_interface = contract['abi'] symbols = solidity_unresolved_symbols(contract['bin_hex']) if symbols: available_symbols = list(map(solidity_library_symbol, all_contracts.keys())) unknown_symbols = set(symbols) - set(available_symbols) if unknown_symbols: msg = 'Cannot deploy contract, known symbols {}, unresolved symbols {}.'.format( available_symbols, unknown_symbols, ) raise Exception(msg) dependencies = deploy_dependencies_symbols(all_contracts) deployment_order = dependencies_order_of_build(contract_key, dependencies) deployment_order.pop() # remove `contract_name` from the list log.debug('Deploying dependencies: {}'.format(str(deployment_order))) for deploy_contract in deployment_order: dependency_contract = all_contracts[deploy_contract] hex_bytecode = solidity_resolve_symbols(dependency_contract['bin_hex'], libraries) bytecode = unhexlify(hex_bytecode) dependency_contract['bin_hex'] = hex_bytecode dependency_contract['bin'] = bytecode transaction_hash_hex = self.send_transaction( sender, to=b'', data=bytecode, gasprice=gasprice, ) transaction_hash = unhexlify(transaction_hash_hex) self.poll(transaction_hash, timeout=timeout) receipt = self.eth_getTransactionReceipt(transaction_hash) contract_address = receipt['contractAddress'] # remove the hexadecimal prefix 0x from the address contract_address = contract_address[2:] libraries[deploy_contract] = contract_address deployed_code = self.eth_getCode(address_decoder(contract_address)) if len(deployed_code) == 0: raise RuntimeError('Contract address has no code, check gas usage.') hex_bytecode = solidity_resolve_symbols(contract['bin_hex'], libraries) bytecode = unhexlify(hex_bytecode) contract['bin_hex'] = hex_bytecode contract['bin'] = bytecode if constructor_parameters: translator = ContractTranslator(contract_interface) parameters = translator.encode_constructor_arguments(constructor_parameters) bytecode = contract['bin'] + parameters else: bytecode = contract['bin'] transaction_hash_hex = self.send_transaction( sender, to=b'', data=bytecode, gasprice=gasprice, ) transaction_hash = unhexlify(transaction_hash_hex) self.poll(transaction_hash, timeout=timeout) receipt = self.eth_getTransactionReceipt(transaction_hash) contract_address = receipt['contractAddress'] deployed_code = self.eth_getCode(address_decoder(contract_address)) if len(deployed_code) == 0: raise RuntimeError( 'Deployment of {} failed. Contract address has no code, check gas usage.'.format( contract_name, ) ) return self.new_contract_proxy( contract_interface, contract_address, ) def new_filter(self, fromBlock=None, toBlock=None, address=None, topics=None): """ Creates a filter object, based on filter options, to notify when the state changes (logs). To check if the state has changed, call eth_getFilterChanges. """ json_data = { 'fromBlock': block_tag_encoder(fromBlock or ''), 'toBlock': block_tag_encoder(toBlock or ''), } if address is not None: json_data['address'] = address_encoder(address) if topics is not None: if not isinstance(topics, list): raise ValueError('topics must be a list') json_data['topics'] = [topic_encoder(topic) for topic in topics] filter_id = self.call('eth_newFilter', json_data) return quantity_decoder(filter_id) def filter_changes(self, fid: int) -> List: changes = self.call('eth_getFilterChanges', quantity_encoder(fid)) if not changes: return list() assert isinstance(changes, list) decoders = { 'blockHash': data_decoder, 'transactionHash': data_decoder, 'data': data_decoder, 'address': address_decoder, 'topics': lambda x: [topic_decoder(t) for t in x], 'blockNumber': quantity_decoder, 'logIndex': quantity_decoder, 'transactionIndex': quantity_decoder } return [ {k: decoders[k](v) for k, v in c.items() if v is not None} for c in changes ] @check_node_connection def call(self, method: str, *args): """ Do the request and return the result. Args: method: The RPC method. args: The encoded arguments expected by the method. - Object arguments must be supplied as a dictionary. - Quantity arguments must be hex encoded starting with '0x' and without left zeros. - Data arguments must be hex encoded starting with '0x' """ request = self.protocol.create_request(method, args) reply = self.transport.send_message(request.serialize().encode()) jsonrpc_reply = self.protocol.parse_reply(reply) if isinstance(jsonrpc_reply, JSONRPCSuccessResponse): return jsonrpc_reply.result elif isinstance(jsonrpc_reply, JSONRPCErrorResponse): raise EthNodeCommunicationError(jsonrpc_reply.error, jsonrpc_reply._jsonrpc_error_code) else: raise EthNodeCommunicationError('Unknown type of JSONRPC reply') def send_transaction( self, sender: address, to: address, value: int = 0, data: bytes = b'', startgas: int = 0, gasprice: int = GAS_PRICE, nonce: Optional[int] = 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. """ if not self.privkey and not sender: raise ValueError('Either privkey or sender needs to be supplied.') if self.privkey: privkey_address = privatekey_to_address(self.privkey) sender = sender or privkey_address if sender != privkey_address: raise ValueError('sender for a different privkey.') if nonce is None: nonce = self.nonce(sender) else: if nonce is None: nonce = 0 if not startgas: startgas = self.gaslimit() - 1 tx = Transaction(nonce, gasprice, startgas, to=to, value=value, data=data) if self.privkey: tx.sign(self.privkey) result = self.call( 'eth_sendRawTransaction', data_encoder(rlp.encode(tx)), ) return result[2 if result.startswith('0x') else 0:] else: # rename the fields to match the eth_sendTransaction signature tx_dict = tx.to_dict() tx_dict.pop('hash') tx_dict['sender'] = sender tx_dict['gasPrice'] = tx_dict.pop('gasprice') tx_dict['gas'] = tx_dict.pop('startgas') res = self.eth_sendTransaction(**tx_dict) assert len(res) in (20, 32) return hexlify(res) def eth_sendTransaction( self, sender: address = b'', to: address = b'', value: int = 0, data: bytes = b'', gasPrice: int = GAS_PRICE, gas: int = GAS_PRICE, nonce: Optional[int] = None): """ Creates new message call transaction or a contract creation, if the data field contains code. Args: sender: The address the transaction is sent from. to: The address the transaction is directed to. (optional when creating new contract) gas: Gas provided for the transaction execution. It will return unused gas. gasPrice: gasPrice used for each unit of gas paid. value: Value sent with this transaction. data: The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. nonce: This allows to overwrite your own pending transactions that use the same nonce. """ if to == b'' and data.isalnum(): warnings.warn( 'Verify that the data parameter is _not_ hex encoded, if this is the case ' 'the data will be double encoded and result in unexpected ' 'behavior.' ) if to == b'0' * 40: warnings.warn('For contract creation the empty string must be used.') if sender is None: raise ValueError('sender needs to be provided.') json_data = format_data_for_call( sender, to, value, data, gas, gasPrice ) if nonce is not None: json_data['nonce'] = quantity_encoder(nonce) res = self.call('eth_sendTransaction', json_data) return data_decoder(res) def eth_call( self, sender: address = b'', to: address = b'', value: int = 0, data: bytes = b'', startgas: int = GAS_PRICE, gasprice: int = GAS_PRICE, block_number: Union[str, int] = 'latest'): """ Executes a new message call immediately without creating a transaction on the blockchain. Args: sender: The address the transaction is sent from. to: The address the transaction is directed to. gas: Gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions. gasPrice: gasPrice used for unit of gas paid. value: Integer of the value sent with this transaction. data: Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI. block_number: Determines the state of ethereum used in the call. """ json_data = format_data_for_call( sender, to, value, data, startgas, gasprice, ) res = self.call('eth_call', json_data, block_number) return data_decoder(res) def eth_estimateGas( self, sender: address = b'', to: address = b'', value: int = 0, data: bytes = b'', startgas: int = GAS_PRICE, gasprice: int = GAS_PRICE) -> int: """ Makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas. Args: sender: The address the transaction is sent from. to: The address the transaction is directed to. gas: Gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions. gasPrice: gasPrice used for unit of gas paid. value: Integer of the value sent with this transaction. data: Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI. block_number: Determines the state of ethereum used in the call. """ json_data = format_data_for_call( sender, to, value, data, startgas, gasprice, ) try: res = self.call('eth_estimateGas', json_data) except EthNodeCommunicationError as e: tx_would_fail = e.error_code and e.error_code in (-32015, -32000) if tx_would_fail: # -32015 is parity and -32000 is geth return None else: raise e return quantity_decoder(res) def eth_getTransactionReceipt(self, transaction_hash: bytes) -> Dict: """ Returns the receipt of a transaction by transaction hash. Args: transaction_hash: Hash of a transaction. Returns: A dict representing the transaction receipt object, or null when no receipt was found. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)' ) transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionReceipt', transaction_hash) def eth_getCode(self, code_address: address, block: Union[int, str] = 'latest') -> bytes: """ Returns code at a given address. Args: code_address: An address. block: Integer block number, or the string 'latest', 'earliest' or 'pending'. Default is 'latest'. """ if code_address.startswith(b'0x'): warnings.warn( 'address seems to be already encoded, this will result ' 'in unexpected behavior' ) if len(code_address) != 20: raise ValueError( 'address length must be 20 (it might be hex encoded)' ) result = self.call('eth_getCode', address_encoder(code_address), block) return data_decoder(result) def eth_getTransactionByHash(self, transaction_hash: bytes): """ Returns the information about a transaction requested by transaction hash. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)' ) transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionByHash', transaction_hash) def poll( self, transaction_hash: bytes, confirmations: Optional[int] = None, timeout: Optional[float] = None): """ Wait until the `transaction_hash` is applied or rejected. If timeout is None, this could wait indefinitely! Args: transaction_hash: Transaction hash that we are waiting for. confirmations: Number of block confirmations that we will wait for. timeout: Timeout in seconds, raise an Excpetion on timeout. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)' ) transaction_hash = data_encoder(transaction_hash) deadline = None if timeout: deadline = gevent.Timeout(timeout) deadline.start() try: # used to check if the transaction was removed, this could happen # if gas price is too low: # # > Transaction (acbca3d6) below gas price (tx=1 Wei ask=18 # > Shannon). All sequential txs from this address(7d0eae79) # > will be ignored # last_result = None while True: # Could return None for a short period of time, until the # transaction is added to the pool transaction = self.call('eth_getTransactionByHash', transaction_hash) # if the transaction was added to the pool and then removed if transaction is None and last_result is not None: raise Exception('invalid transaction, check gas price') # the transaction was added to the pool and mined if transaction and transaction['blockNumber'] is not None: break last_result = transaction gevent.sleep(.5) if confirmations: # this will wait for both APPLIED and REVERTED transactions transaction_block = quantity_decoder(transaction['blockNumber']) confirmation_block = transaction_block + confirmations block_number = self.block_number() while block_number < confirmation_block: gevent.sleep(.5) block_number = self.block_number() except gevent.Timeout: raise Exception('timeout when polling for transaction') finally: if deadline: deadline.cancel()
def provision_wap_conf(): json_data = JSONRPCProtocol().create_request('wap_conf') data = json_data.serialize() result = send_request("localhost", 5050, data, wait_for_eof = True) return result