async def generate_login_link(self, launcher_id: bytes32) -> Optional[str]: for pool_state in self.pool_state.values(): pool_config: PoolWalletConfig = pool_state["pool_config"] if pool_config.launcher_id == launcher_id: authentication_sk: Optional[PrivateKey] = await find_authentication_sk( self.all_root_sks, pool_config.authentication_public_key ) if authentication_sk is None: self.log.error(f"Could not find authentication sk for pk: {pool_config.authentication_public_key}") continue assert authentication_sk.get_g1() == pool_config.authentication_public_key authentication_token_timeout = pool_state["authentication_token_timeout"] authentication_token = get_current_authentication_token(authentication_token_timeout) message: bytes32 = std_hash( AuthenticationPayload( "get_login", pool_config.launcher_id, pool_config.target_puzzle_hash, authentication_token ) ) signature: G2Element = AugSchemeMPL.sign(authentication_sk, message) return ( pool_config.pool_url + f"/login?launcher_id={launcher_id.hex()}&authentication_token={authentication_token}" f"&signature={bytes(signature).hex()}" ) return None
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 get_farmer(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( 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: bytes32 = std_hash( AuthenticationPayload("get_farmer", 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()}.", ) 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 _pool_get_farmer( self, pool_config: PoolWalletConfig, authentication_token_timeout: uint8, authentication_sk: PrivateKey) -> Optional[Dict]: assert authentication_sk.get_g1( ) == pool_config.authentication_public_key authentication_token = get_current_authentication_token( authentication_token_timeout) message: bytes32 = std_hash( AuthenticationPayload("get_farmer", pool_config.launcher_id, pool_config.target_puzzle_hash, authentication_token)) signature: G2Element = AugSchemeMPL.sign(authentication_sk, message) get_farmer_params = { "launcher_id": pool_config.launcher_id.hex(), "authentication_token": authentication_token, "signature": bytes(signature).hex(), } try: async with aiohttp.ClientSession(trust_env=True) as session: async with session.get( f"{pool_config.pool_url}/farmer", params=get_farmer_params, ssl=ssl_context_for_root(get_mozilla_ca_crt(), log=self.log), ) as resp: if resp.ok: response: Dict = json.loads(await resp.text()) self.log.info(f"GET /farmer response: {response}") if "error_code" in response: self.pool_state[ pool_config.p2_singleton_puzzle_hash][ "pool_errors_24h"].append(response) return response else: self.handle_failed_pool_response( pool_config.p2_singleton_puzzle_hash, f"Error in GET /farmer {pool_config.pool_url}, {resp.status}", ) except Exception as e: self.handle_failed_pool_response( pool_config.p2_singleton_puzzle_hash, f"Exception in GET /farmer {pool_config.pool_url}, {e}") return None