def open( self, config_path: str, fee: bool = True, audit: bool = True, deployer_whitelist: bool = False, score_package_validator: bool = False, builtin_score_owner: str = "", ): conf = IconConfig("", default_icon_config) if config_path != "": conf.load(config_path) conf.update_conf({ "builtinScoreOwner": builtin_score_owner, "service": { "fee": fee, "audit": audit, "scorePackageValidator": score_package_validator, "deployerWhiteList": deployer_whitelist, }, }) Logger.load_config(conf) self._engine.open(conf)
def _stop_message_queue(self): Logger.info(tag=_TAG, msg="_stop_message_queue() start") request = NoneRequest() self._message_queue.put(request) Logger.info(tag=_TAG, msg="_stop_message_queue() end")
def claim_iscore(self, address: 'Address', block_height: int, block_hash: bytes, tx_index: int, tx_hash: bytes) -> Tuple[int, int]: """Claim IScore of a given address It is called on invoke thread :param address: the address to claim :param block_height: the height of block which contains this claim tx :param block_hash: the hash of block which contains this claim tx :param tx_index: the index of claimIScore transaction which is contained in a block :param tx_hash: the hash of claimIScore transaction :return: [i-score(int), block_height(int)] :exception TimeoutException: The operation has timed-out """ Logger.debug( tag=_TAG, msg=f"claim_iscore() start: " f"address({address}) block_height({block_height}) block_hash({block_hash.hex()})" ) future: concurrent.futures.Future = asyncio.run_coroutine_threadsafe( self._claim_iscore(address, block_height, block_hash, tx_index, tx_hash), self._loop) try: response: 'ClaimResponse' = future.result(self._ipc_timeout) except asyncio.TimeoutError: future.cancel() raise TimeoutException("claim_iscore message to RewardCalculator has timed-out") Logger.debug(tag=_TAG, msg=f"claim_iscore() end: iscore({response.iscore})") return response.iscore, response.block_height
def start_as_rest_server(args): peer_port = args.port channel = conf.LOOPCHAIN_DEFAULT_CHANNEL amqp_key = args.amqp_key or conf.AMQP_KEY api_port = int(peer_port) + conf.PORT_DIFF_REST_SERVICE_CONTAINER from iconrpcserver.default_conf.icon_rpcserver_config import default_rpcserver_config from iconrpcserver.icon_rpcserver_cli import start_process, find_procs_by_params from iconcommons.icon_config import IconConfig from iconcommons.logger import Logger additional_conf = { "log": { "logger": "iconrpcserver", "colorLog": True, "level": "info", "filePath": "./log/iconrpcserver.log", "outputType": "console|file" }, "channel": channel, "port": api_port, "amqpKey": amqp_key, "gunicornWorkerCount": 1, "tbearsMode": False } rpcserver_conf = IconConfig("", default_rpcserver_config) rpcserver_conf.load() rpcserver_conf.update_conf(additional_conf) Logger.load_config(rpcserver_conf) if not find_procs_by_params(api_port): start_process(conf=rpcserver_conf) Logger.info("start_command done!, IconRpcServerCli")
def stop(self): Logger.debug(tag=_TAG, msg="stop() start") self._stop_message_queue() self._ipc_server.stop() Logger.debug(tag=_TAG, msg="stop() end")
def rollback(self, block_height: int, block_hash: bytes) -> Tuple[bool, int, bytes]: """Request reward calculator to rollback the DB of the reward calculator to the specific block height. Reward calculator DOES NOT process other messages while processing ROLLBACK message :param block_height: :param block_hash: :return: """ Logger.debug( tag=_TAG, msg=f"rollback() start: block_height={block_height}, block_hash={bytes_to_hex(block_hash)}" ) future: concurrent.futures.Future = asyncio.run_coroutine_threadsafe( self._rollback(block_height, block_hash), self._loop) try: response: 'RollbackResponse' = future.result(self._ipc_timeout) except asyncio.TimeoutError: future.cancel() raise TimeoutException("rollback message to RewardCalculator has timed-out") Logger.debug(tag=_TAG, msg=f"rollback() end. response: {response}") return response.success, response.block_height, response.block_hash
def write_server_conf(conf: dict): write_conf = { "hostAddress": conf['hostAddress'], "port": conf['port'], "scoreRootPath": conf['scoreRootPath'], "stateDbRootPath": conf['stateDbRootPath'], ConfigKey.CHANNEL: conf.get(ConfigKey.CHANNEL, None), # to stop iconservice ConfigKey.AMQP_TARGET: conf.get(ConfigKey.AMQP_TARGET, None), # to stop iconservice ConfigKey.AMQP_KEY: conf.get(ConfigKey.AMQP_KEY, None) # to stop iconservice } Logger.debug(f"Write server Info.({conf}) to {TBEARS_CLI_ENV}", TBEARS_CLI_TAG) file_path = TBEARS_CLI_ENV file_name = file_path[file_path.rfind('/') + 1:] parent_directory = file_path[:file_path.rfind('/')] try: write_file(parent_directory=parent_directory, file_name=file_name, contents=json.dumps(write_conf), overwrite=True) except Exception as e: print(f"Can't write conf to file. {e}") except TBearsWriteFileException as e: print(f"{e}")
def _query(self, request: dict): response = None try: method = request['method'] if method == 'debug_estimateStep': converted_request = TypeConverter.convert(request, ParamType.INVOKE_TRANSACTION) value = self._icon_service_engine.estimate_step(converted_request) else: converted_request = TypeConverter.convert(request, ParamType.QUERY) value = self._icon_service_engine.query(method, converted_request['params']) if isinstance(value, Address): value = str(value) response = MakeResponse.make_response(value) except FatalException as e: self._log_exception(e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response(ExceptionCode.SYSTEM_ERROR, str(e)) except IconServiceBaseException as icon_e: self._log_exception(icon_e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response(icon_e.code, icon_e.message) except Exception as e: self._log_exception(e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response(ExceptionCode.SYSTEM_ERROR, str(e)) finally: Logger.debug(f'query response with {response}', ICON_INNER_LOG_TAG) self._icon_service_engine.clear_context_stack() return response
def main(): # Response server name as loopchain, not gunicorn. gunicorn.SERVER_SOFTWARE = 'loopchain' # Parse arguments. parser = argparse.ArgumentParser() parser.add_argument("-p", type=str, dest=ConfigKey.PORT, default=None, help="rest_proxy port") parser.add_argument("-c", type=str, dest=ConfigKey.CONFIG, default=None, help="json configure file path") args = parser.parse_args() conf_path = args.config if conf_path is not None: if not IconConfig.valid_conf_path(conf_path): print(f'invalid config file : {conf_path}') sys.exit(ExitCode.COMMAND_IS_WRONG.value) if conf_path is None: conf_path = str() conf = IconConfig(conf_path, default_rpcserver_config) conf.load() conf.update_conf(dict(vars(args))) Logger.load_config(conf) _run_async(_run(conf))
def _close(self): Logger.info("icon_score_service close", ICON_INNER_LOG_TAG) if self._icon_service_engine: self._icon_service_engine.close() self._icon_service_engine = None MessageQueueService.loop.stop()
def _invoke(self, request: dict): """Process transactions in a block :param request: :return: """ response = None try: params = TypeConverter.convert(request, ParamType.INVOKE) converted_block_params = params['block'] block = Block.from_dict(converted_block_params) converted_tx_requests = params['transactions'] tx_results, state_root_hash = self._icon_service_engine.invoke( block=block, tx_requests=converted_tx_requests) convert_tx_results = \ {bytes.hex(tx_result.tx_hash): tx_result.to_dict(to_camel_case) for tx_result in tx_results} results = { 'txResults': convert_tx_results, 'stateRootHash': bytes.hex(state_root_hash) } response = MakeResponse.make_response(results) except IconServiceBaseException as icon_e: self._log_exception(icon_e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response( icon_e.code, icon_e.message) except Exception as e: self._log_exception(e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response( ExceptionCode.SERVER_ERROR, str(e)) finally: Logger.info(f'invoke response with {response}', ICON_INNER_LOG_TAG) return response
def _remove_precommit_state(self, request: dict): response = None try: converted_block_params = TypeConverter.convert( request, ParamType.WRITE_PRECOMMIT) block_height, instant_block_hash, _ = \ self._get_block_info_for_precommit_state(converted_block_params) self._icon_service_engine.rollback(block_height, instant_block_hash) response = MakeResponse.make_response(ExceptionCode.OK) except FatalException as e: self._log_exception(e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response( ExceptionCode.SYSTEM_ERROR, str(e)) self._close() except IconServiceBaseException as icon_e: self._log_exception(icon_e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response( icon_e.code, icon_e.message) except Exception as e: self._log_exception(e, ICON_SERVICE_LOG_TAG) response = MakeResponse.make_error_response( ExceptionCode.SYSTEM_ERROR, str(e)) finally: Logger.info(f'remove_precommit_state response with {response}', ICON_INNER_LOG_TAG) return response
async def stop_process(conf: 'IconConfig'): icon_score_queue_name = _make_icon_score_queue_name( conf[ConfigKey.CHANNEL], conf[ConfigKey.AMQP_KEY]) stub = await _create_icon_score_stub(conf[ConfigKey.AMQP_TARGET], icon_score_queue_name) await stub.async_task().close() Logger.info(f'stop_process_icon_service!', _TAG)
def _put_preps_to_rc_db(cls, context: 'IconScoreContext', revision: int, term: Optional['Term'] = None): # If term is not None, it is the term which has been changed in term assert context.is_decentralized() if term is None: block_height: int = context.block.height - 1 term: 'Term' = context.engine.prep.term else: block_height: int = context.block.height if revision < Revision.FIX_TOTAL_ELECTED_PREP_DELEGATED.value: total_elected_prep_delegated: int = term.total_elected_prep_delegated_snapshot else: total_elected_prep_delegated: int = term.total_elected_prep_delegated Logger.info( tag=cls.TAG, msg=f"_put_preps_for_rc_db() " f"block_height={block_height} " f"total_elected_prep_delegated={term.total_elected_prep_delegated} " f"total_elected_prep_delegated_snapshot={term.total_elected_prep_delegated_snapshot}") data: 'PRepsData' = RewardCalcDataCreator.create_prep_data(block_height, total_elected_prep_delegated, term.preps) context.storage.rc.put(context.rc_block_batch, data)
def query_iscore(self, address: 'Address') -> Tuple[int, int]: """Returns the I-Score of a given address It should be called on query thread :param address: the address to query :return: [i-score(int), block_height(int)] :exception TimeoutException: The operation has timed-out """ assert isinstance(address, Address) Logger.debug(tag=_TAG, msg="query_iscore() start") future: concurrent.futures.Future = asyncio.run_coroutine_threadsafe( self._query_iscore(address), self._loop) try: response: 'QueryResponse' = future.result(self._ipc_timeout) except asyncio.TimeoutError: future.cancel() raise TimeoutException("query_iscore message to RewardCalculator has timed-out") Logger.debug(tag=_TAG, msg="query_iscore() end") return response.iscore, response.block_height
async def _run(conf: 'IconConfig'): Logger.print_config(conf, ICON_RPCSERVER_CLI) ServerComponents().conf = conf ServerComponents().set_resource() Logger.debug( f"Run gunicorn webserver for HA. Port = {conf[ConfigKey.PORT]}") # Configure SSL. ssl_context = ServerComponents().component.ssl_context certfile = '' keyfile = '' if ssl_context is not None: certfile = ssl_context[0] keyfile = ssl_context[1] options = conf.get(ConfigKey.GUNICORN_CONFIG, {}) options.update({ 'bind': f'{conf[ConfigKey.HOST]}:{conf[ConfigKey.PORT]}', 'certfile': certfile, 'keyfile': keyfile, 'SERVER_SOFTWARE': gunicorn.SERVER_SOFTWARE, 'capture_output': False }) # Launch gunicorn web server. ServerComponents().conf = conf # ServerComponents().ready() StandaloneApplication(ServerComponents().component.app, options).run()
def commit_block(self, success: bool, block_height: int, block_hash: bytes) -> tuple: """Notify reward calculator of block confirmation It is called on invoke thread :param success: true for success, false for failure :param block_height: the height of block :param block_hash: the hash of block :return: [success(bool), block_height(int), block_hash(bytes)] :exception TimeoutException: The operation has timed-out """ Logger.debug( tag=_TAG, msg=f"commit_block() start: success={success}, " f"block_height={block_height}, " f"block_hash={bytes_to_hex(block_hash)}" ) future: concurrent.futures.Future = asyncio.run_coroutine_threadsafe( self._commit_block(success, block_height, block_hash), self._loop) try: response: 'CommitBlockResponse' = future.result(self._ipc_timeout) except asyncio.TimeoutError: future.cancel() raise TimeoutException("commit_block message to RewardCalculator has timed-out") Logger.debug(tag=_TAG, msg=f"commit_block() end. response: {response}") return response.success, response.block_height, response.block_hash
def commit_claim(self, success: bool, address: 'Address', block_height: int, block_hash: bytes, tx_index: int, tx_hash: bytes): Logger.debug( tag=_TAG, msg=f"commit_claim() start: " f"success={success} " f"address={address} " f"block_height={block_height} " f"block_hash={bytes_to_hex(block_hash)} " f"tx_index={tx_index} " f"tx_hash={bytes_to_hex(tx_hash)}" ) future: concurrent.futures.Future = asyncio.run_coroutine_threadsafe( self._commit_claim(success, address, block_height, block_hash, tx_index, tx_hash), self._loop ) try: future.result(self._ipc_timeout) except asyncio.TimeoutError: future.cancel() raise TimeoutException("COMMIT_CLAIM message to RewardCalculator has timed-out") Logger.debug(tag=_TAG, msg="commit_claim() end")
async def icx_getTransactionResult(**kwargs): channel = kwargs['context']['channel'] request = convert_params(kwargs, RequestParamType.get_tx_result) channel_stub = StubCollection().channel_stubs[channel] verify_result = dict() tx_hash = request["txHash"] response_code, result = await channel_stub.async_task( ).get_invoke_result(tx_hash) if response_code == message_code.Response.fail_tx_not_invoked: raise GenericJsonRpcServerError( code=JsonError.INVALID_PARAMS, message=message_code.responseCodeMap[response_code][1], http_status=status.HTTP_BAD_REQUEST) elif response_code == message_code.Response.fail_invalid_key_error or \ response_code == message_code.Response.fail: raise GenericJsonRpcServerError( code=JsonError.INVALID_PARAMS, message='Invalid params txHash', http_status=status.HTTP_BAD_REQUEST) if result: try: result_dict = json.loads(result) verify_result = result_dict except json.JSONDecodeError as e: Logger.warning( f"your result is not json, result({result}), {e}") response = convert_params(verify_result, ResponseParamType.get_tx_result) return response
def _start_process(conf: 'IconConfig'): Logger.info('start_server() start') python_module_string = 'iconservice.icon_service' converted_params = { '-sc': conf[ConfigKey.SCORE_ROOT_PATH], '-st': conf[ConfigKey.STATE_DB_ROOT_PATH], '-ch': conf[ConfigKey.CHANNEL], '-ak': conf[ConfigKey.AMQP_KEY], '-at': conf[ConfigKey.AMQP_TARGET], '-c': conf.get(ConfigKey.CONFIG) } custom_argv = [] for k, v in converted_params.items(): if v is None: continue custom_argv.append(k) custom_argv.append(str(v)) if conf[ConfigKey.TBEARS_MODE]: custom_argv.append('-tbears') if conf[ConfigKey.STEP_TRACE_FLAG]: custom_argv.append('-steptrace') is_foreground = conf.get('foreground', False) if is_foreground: from iconservice.icon_service import run_in_foreground del conf['foreground'] run_in_foreground(conf) else: subprocess.Popen( [sys.executable, '-m', python_module_string, *custom_argv], close_fds=True) Logger.info('start_process() end')
def start_as_rest_server(args): from iconcommons.icon_config import IconConfig from iconcommons.logger import Logger from iconrpcserver.default_conf.icon_rpcserver_config import default_rpcserver_config from iconrpcserver import icon_rpcserver_app amqp_key = args.amqp_key or conf.AMQP_KEY api_port = int(args.port) + conf.PORT_DIFF_REST_SERVICE_CONTAINER conf_path = conf.CONF_PATH_ICONRPCSERVER_DEV if args.radio_station_target == conf.URL_CITIZEN_TESTNET: conf_path = conf.CONF_PATH_ICONRPCSERVER_TESTNET elif args.radio_station_target == conf.URL_CITIZEN_MAINNET: conf_path = conf.CONF_PATH_ICONRPCSERVER_MAINNET additional_conf = { "port": api_port, "amqpTarget": conf.AMQP_TARGET, "amqpKey": amqp_key, "channel": conf.LOOPCHAIN_DEFAULT_CHANNEL } rpcserver_conf: IconConfig = IconConfig(conf_path, default_rpcserver_config) rpcserver_conf.load() rpcserver_conf.update_conf(additional_conf) Logger.load_config(rpcserver_conf) icon_rpcserver_app.run_in_foreground(rpcserver_conf)
def _create_rc_result(context: "IconScoreContext", start_block: int, end_block: int) -> dict: rc_result = dict() if start_block < 0 or end_block < 0: return rc_result iscore, request_block_height, rc_state_hash = \ context.storage.rc.get_calc_response_from_rc() if iscore == -1: return rc_result if request_block_height != end_block: Logger.warning( tag="ISE", msg=f"Response block height is not matched to the request: " f"response block height:{request_block_height} " f"request block height:{end_block}", ) return rc_result rc_result["iscore"] = iscore rc_result["estimatedICX"] = iscore // ISCORE_EXCHANGE_RATE rc_result["startBlockHeight"] = start_block rc_result["endBlockHeight"] = end_block rc_result["stateHash"] = rc_state_hash return rc_result
def run(self, last_block_height: int, rollback_block_height: int, term_start_block_height: int): """Rollback to the previous block state :param last_block_height: the last confirmed block height :param rollback_block_height: the height of block to rollback to :param term_start_block_height: the start block height of the current term """ Logger.info(tag=TAG, msg=f"run() start: " f"last_block_height={last_block_height} " f"rollback_block_height={rollback_block_height} " f"term_start_block_height={term_start_block_height}") self._validate_block_heights(last_block_height, rollback_block_height, term_start_block_height) term_change_exists = \ self._term_change_exists(last_block_height, rollback_block_height, term_start_block_height) calc_end_block_height = term_start_block_height - 1 reader = WriteAheadLogReader() state_db_batch = {} iiss_db_batch = {} for block_height in range(last_block_height - 1, rollback_block_height - 1, -1): # Make backup file with a given block_height path: str = self._get_backup_file_path(block_height) if not os.path.isfile(path): raise InternalServiceErrorException( f"Backup file not found: {path}") reader.open(path) # Merge backup data into state_db_batch self._write_batch(reader.get_iterator(WALDBType.STATE.value), state_db_batch) # Merge backup data into iiss_db_batch if not (term_change_exists and block_height > calc_end_block_height): self._write_batch(reader.get_iterator(WALDBType.RC.value), iiss_db_batch) reader.close() # If a term change is detected during rollback, handle the exceptions below if term_change_exists: self._remove_block_produce_info(iiss_db_batch, calc_end_block_height) self._rename_iiss_db_to_current_db(calc_end_block_height) # Commit write_batch to db self._commit_batch(state_db_batch, self._state_db) iiss_db = RewardCalcStorage.create_current_db(self._rc_data_path) self._commit_batch(iiss_db_batch, iiss_db) iiss_db.close() Logger.info(tag=TAG, msg="run() end")
async def invoke(self, request: dict): Logger.info(f'invoke request with {request}', ICON_INNER_LOG_TAG) if self._is_thread_flag_on(EnableThreadFlag.INVOKE): loop = get_event_loop() return await loop.run_in_executor(self._thread_pool[THREAD_INVOKE], self._invoke, request) else: return self._invoke(request)
async def query(self, request: dict): Logger.info(f'query request with {request}', ICON_INNER_LOG_TAG) if self._is_thread_flag_on(EnableThreadFlag.QUERY): loop = get_event_loop() return await loop.run_in_executor(self._thread_pool[THREAD_QUERY], self._query, request) else: return self._query(request)
def stop_process(conf: 'IconConfig'): Logger.info(f'stop_process!', ICON_RPCSERVER_CLI) pids = _get_process_list_by_port(conf[ConfigKey.PORT]) for p in pids: try: os.kill(int(p), signal.SIGKILL) except ValueError: continue
async def channel_register(ws, channel_name: str, peer_id: str): channel_stub = get_channel_stub_by_channel_name(channel_name) approved = await channel_stub.async_task().register_subscriber(peer_id=peer_id) if not approved: raise RuntimeError("This peer can no longer take more subscribe requests.") Logger.debug(f"register subscriber: {peer_id}")
async def validate_transaction(self, request: dict): Logger.info(f'pre_validate_check request with {request}', ICON_INNER_LOG_TAG) if self._is_thread_flag_on(EnableThreadFlag.Validate): loop = get_event_loop() return await loop.run_in_executor(self._thread_pool[THREAD_VALIDATE], self._validate_transaction, request) else: return self._validate_transaction(request)
async def remove_precommit_state(self, request: dict): Logger.info(tag=_TAG, msg=f'remove_precommit_state() start') self._check_icon_service_ready() """ Unused API """ return {}
def ready_handler(self, response: 'ReadyNotification'): Logger.debug(tag=_TAG, msg=f"ready_handler() start {response}") if self._ready_callback is not None: self._ready_callback(response) self._ready_future.set_result(RCStatus.READY) self._rc_block = RewardCalcBlock(response.block_height, response.block_height)