async def get_farmer(self, request_obj) -> web.Response: # TODO(pool): add rate limiting launcher_id = hexstr_to_bytes(request_obj.rel_url.query["launcher_id"]) authentication_token = uint64(request_obj.rel_url.query["authentication_token"]) authentication_token_error: Optional[web.Response] = check_authentication_token( launcher_id, authentication_token, self.pool.authentication_token_timeout ) if authentication_token_error is not None: return authentication_token_error farmer_record: Optional[FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id) if farmer_record is None: return error_response( PoolErrorCode.FARMER_NOT_KNOWN, f"Farmer with launcher_id {launcher_id.hex()} unknown." ) # Validate provided signature signature: G2Element = G2Element.from_bytes(hexstr_to_bytes(request_obj.rel_url.query["signature"])) message = std_hash(launcher_id + bytes(authentication_token)) if not AugSchemeMPL.verify(farmer_record.authentication_public_key, message, signature): return error_response( PoolErrorCode.INVALID_SIGNATURE, f"Failed to verify signature {signature} for launcher_id {launcher_id.hex()}.", ) response: GetFarmerResponse = GetFarmerResponse( farmer_record.authentication_public_key, farmer_record.payout_instructions, farmer_record.difficulty, farmer_record.points, ) self.pool.log.info(f"get_farmer response {response.to_json_dict()}, " f"launcher_id: {launcher_id.hex()}") return obj_to_response(response)
async def post_partial(self, request_obj) -> web.Response: # TODO(pool): add rate limiting start_time = time.time() request = await request_obj.json() partial: PostPartialRequest = PostPartialRequest.from_json_dict( request) authentication_token_error = check_authentication_token( partial.payload.launcher_id, partial.payload.authentication_token, self.pool.authentication_token_timeout, ) if authentication_token_error is not None: return authentication_token_error farmer_record: Optional[ FarmerRecord] = await self.pool.store.get_farmer_record( partial.payload.launcher_id) if farmer_record is None: return error_response( PoolErrorCode.FARMER_NOT_KNOWN, f"Farmer with launcher_id {partial.payload.launcher_id.hex()} not known.", ) post_partial_response = await self.pool.process_partial( partial, farmer_record, uint64(int(start_time))) self.pool.log.info( f"post_partial response {post_partial_response}, time: {time.time() - start_time} " f"launcher_id: {request['payload']['launcher_id']}") return obj_to_response(post_partial_response)
async def get_login(self, request_obj) -> web.Response: # TODO(pool): add rate limiting launcher_id = request_obj.rel_url.query["launcher_id"] authentication_token = request_obj.rel_url.query["authentication_token"] authentication_token_error = check_authentication_token( launcher_id, authentication_token, self.pool.authentication_token_timeout ) if authentication_token_error is not None: return authentication_token_error farmer_record: Optional[FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id) if farmer_record is None: return error_response(PoolErrorCode.FARMER_NOT_KNOWN, f"Farmer with launcher_id {launcher_id} unknown.") # Validate provided signature signature = request_obj.rel_url.query["signature"] message = std_hash(launcher_id + bytes(authentication_token)) if not AugSchemeMPL.verify(farmer_record.authentication_public_key, message, signature): return error_response( PoolErrorCode.INVALID_SIGNATURE, f"Failed to verify signature {signature} for launcher_id {launcher_id}.", ) self.pool.log.info(f"Login successful for launcher_id: {launcher_id}") # TODO(pool) Do what ever you like with the successful login return obj_to_response({"login_data", "Put server side login information here?"})
async def login_response(self, launcher_id): record: Optional[ FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id ) response = {} if record is not None: response["farmer_record"] = record recent_partials = await self.pool.store.get_recent_partials( launcher_id, 20) response["recent_partials"] = recent_partials return obj_to_response(response)
async def get_pool_info(self, _) -> web.Response: res: PoolInfo = PoolInfo( "The Reference Pool", "https://www.chia.net/img/chia_logo.svg", uint64(self.pool.min_difficulty), uint32(self.pool.relative_lock_height), "1.0.0", str(self.pool.pool_fee), "(example) The Reference Pool allows you to pool with low fees, paying out daily using Chia.", self.pool.default_pool_puzzle_hash, ) return obj_to_response(res)
async def get_pool_info(self, _) -> web.Response: res: GetPoolInfoResponse = GetPoolInfoResponse( self.pool.info_name, self.pool.info_logo_url, uint64(self.pool.min_difficulty), uint32(self.pool.relative_lock_height), POOL_PROTOCOL_VERSION, str(self.pool.pool_fee), self.pool.info_description, self.pool.default_target_puzzle_hash, self.pool.authentication_token_timeout, ) return obj_to_response(res)
async def get_login(self, request_obj) -> web.Response: # TODO(pool): add rate limiting launcher_id: bytes32 = hexstr_to_bytes( request_obj.rel_url.query["launcher_id"]) authentication_token: uint64 = uint64( request_obj.rel_url.query["authentication_token"]) authentication_token_error = check_authentication_token( launcher_id, authentication_token, self.pool.authentication_token_timeout) if authentication_token_error is not None: return authentication_token_error farmer_record: Optional[ FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id ) if farmer_record is None: return error_response( PoolErrorCode.FARMER_NOT_KNOWN, f"Farmer with launcher_id {launcher_id.hex()} unknown.") # Validate provided signature signature: G2Element = G2Element.from_bytes( hexstr_to_bytes(request_obj.rel_url.query["signature"])) message: bytes32 = std_hash( AuthenticationPayload("get_login", launcher_id, self.pool.default_target_puzzle_hash, authentication_token)) if not AugSchemeMPL.verify(farmer_record.authentication_public_key, message, signature): return error_response( PoolErrorCode.INVALID_SIGNATURE, f"Failed to verify signature {signature} for launcher_id {launcher_id.hex()}.", ) self.pool.log.info( f"Login successful for launcher_id: {launcher_id.hex()}") record: Optional[ FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id ) response = {} if record is not None: response["farmer_record"] = record recent_partials = await self.pool.store.get_recent_partials( launcher_id, 20) response["recent_partials"] = recent_partials # TODO(pool) Do what ever you like with the successful login return obj_to_response(response)
async def inner(request) -> aiohttp.web.Response: request_data = await request.json() try: res_object = await f(request_data) if res_object is None: res_object = {} if "success" not in res_object: res_object["success"] = True except Exception as e: tb = traceback.format_exc() self.log.warning(f"Error while handling message: {tb}") if len(e.args) > 0: res_object = {"success": False, "error": f"{e.args[0]}"} else: res_object = {"success": False, "error": f"{e}"} return obj_to_response(res_object)
async def submit_partial(self, request_obj) -> web.Response: start_time = time.time() request = await request_obj.json() # TODO(pool): add rate limiting partial: SubmitPartial = SubmitPartial.from_json_dict(request) time_received_partial = uint64(int(time.time())) # It's important that on the first request from this farmer, the default difficulty is used. Changing the # difficulty requires a few minutes, otherwise farmers can abuse by setting the difficulty right under the # proof that they found. farmer_record: Optional[ FarmerRecord] = await self.pool.store.get_farmer_record( partial.payload.singleton_genesis) if farmer_record is not None: curr_difficulty: uint64 = farmer_record.difficulty balance = farmer_record.points else: curr_difficulty = self.pool.default_difficulty balance = uint64(0) async def await_and_call(cor, *args): # 10 seconds gives our node some time to get the signage point, in case we are slightly slowed down await asyncio.sleep(10) res = await cor(args) self.pool.log.info(f"Delayed response: {res}") res_dict = await self.pool.process_partial( partial, time_received_partial, balance, curr_difficulty, ) if "error_code" in res_dict and "error_code" == PoolErr.NOT_FOUND.value: asyncio.create_task( await_and_call(self.pool.process_partial, partial, time_received_partial, balance, curr_difficulty)) self.pool.log.info( f"Returning {res_dict}, time: {time.time() - start_time} " f"singleton: {request['payload']['singleton_genesis']}") return obj_to_response(res_dict)
async def post_farmer(self, request_obj) -> web.Response: # TODO(pool): add rate limiting post_farmer_request: PostFarmerRequest = PostFarmerRequest.from_json_dict( await request_obj.json()) authentication_token_error = check_authentication_token( post_farmer_request.payload.launcher_id, post_farmer_request.payload.authentication_token, self.pool.authentication_token_timeout, ) if authentication_token_error is not None: return authentication_token_error post_farmer_response = await self.pool.add_farmer(post_farmer_request) self.pool.log.info( f"post_farmer response {post_farmer_response}, " f"launcher_id: {post_farmer_request.payload.launcher_id.hex()}", ) return obj_to_response(post_farmer_response)
async def inner(request) -> aiohttp.web.Response: request_data = await request.json() try: res_object = await f(request_data) if res_object is None: res_object = {} except Exception as e: tb = traceback.format_exc() self.log.warning(f"Error while handling message: {tb}") if len(e.args) > 0: res_object = { "error_code": PoolErr.SERVER_EXCEPTION, "error_message": f"{e.args[0]}" } else: res_object = { "error_code": PoolErr.SERVER_EXCEPTION, "error_message": f"{e}" } return obj_to_response(res_object)
def error_response(code: PoolErrorCode, message: str): error: ErrorResponse = ErrorResponse(uint16(code.value), message) return obj_to_response(error)