Пример #1
0
async def create_pool_args(pool_url: str) -> Dict:
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(f"{pool_url}/pool_info",
                                   ssl=ssl_context_for_root(
                                       get_mozilla_ca_crt())) as response:
                if response.ok:
                    json_dict = json.loads(await response.text())
                else:
                    raise ValueError(
                        f"Response from {pool_url} not OK: {response.status}")
    except Exception as e:
        raise ValueError(f"Error connecting to pool {pool_url}: {e}")

    if json_dict["relative_lock_height"] > 1000:
        raise ValueError(
            "Relative lock height too high for this pool, cannot join")
    if json_dict["protocol_version"] != POOL_PROTOCOL_VERSION:
        raise ValueError(
            f"Incorrect version: {json_dict['protocol_version']}, should be {POOL_PROTOCOL_VERSION}"
        )

    header_msg = f"\n---- Pool parameters fetched from {pool_url} ----"
    print(header_msg)
    pprint(json_dict)
    print("-" * len(header_msg))
    return json_dict
Пример #2
0
async def fetch(url: str):
    async with ClientSession() as session:
        try:
            mozzila_root = get_mozzila_ca_crt()
            ssl_context = ssl_context_for_root(mozzila_root)
            response = await session.get(url, ssl=ssl_context)
            if not response.ok:
                log.warning("Response not OK.")
                return None
            return await response.text()
        except Exception as e:
            log.error(f"Exception while fetching {url}, exception: {e}")
            return None
Пример #3
0
async def join_pool(args: dict, wallet_client: WalletRpcClient,
                    fingerprint: int) -> None:
    config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
    enforce_https = config["full_node"]["selected_network"] == "mainnet"
    pool_url: str = args["pool_url"]
    fee = Decimal(args.get("fee", 0))
    fee_mojos = uint64(int(fee * units["chia"]))

    if enforce_https and not pool_url.startswith("https://"):
        print(f"Pool URLs must be HTTPS on mainnet {pool_url}. Aborting.")
        return
    wallet_id = args.get("id", None)
    prompt = not args.get("yes", False)
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(f"{pool_url}/pool_info",
                                   ssl=ssl_context_for_root(
                                       get_mozilla_ca_crt())) as response:
                if response.ok:
                    json_dict = json.loads(await response.text())
                else:
                    print(f"Response not OK: {response.status}")
                    return
    except Exception as e:
        print(f"Error connecting to pool {pool_url}: {e}")
        return

    if json_dict["relative_lock_height"] > 1000:
        print("Relative lock height too high for this pool, cannot join")
        return
    if json_dict["protocol_version"] != POOL_PROTOCOL_VERSION:
        print(
            f"Incorrect version: {json_dict['protocol_version']}, should be {POOL_PROTOCOL_VERSION}"
        )
        return

    pprint(json_dict)
    msg = f"\nWill join pool: {pool_url} with Plot NFT {fingerprint}."
    func = functools.partial(
        wallet_client.pw_join_pool,
        wallet_id,
        hexstr_to_bytes(json_dict["target_puzzle_hash"]),
        pool_url,
        json_dict["relative_lock_height"],
        fee_mojos,
    )

    await submit_tx_with_confirmation(msg, prompt, func, wallet_client,
                                      fingerprint, wallet_id)
Пример #4
0
    async def _pool_post_farmer(self, pool_config: PoolWalletConfig,
                                authentication_token_timeout: uint8,
                                owner_sk: PrivateKey) -> Optional[Dict]:
        post_farmer_payload: PostFarmerPayload = PostFarmerPayload(
            pool_config.launcher_id,
            get_current_authentication_token(authentication_token_timeout),
            pool_config.authentication_public_key,
            pool_config.payout_instructions,
            None,
        )
        assert owner_sk.get_g1() == pool_config.owner_public_key
        signature: G2Element = AugSchemeMPL.sign(
            owner_sk, post_farmer_payload.get_hash())
        post_farmer_request = PostFarmerRequest(post_farmer_payload, signature)
        post_farmer_body = json.dumps(post_farmer_request.to_json_dict())

        headers = {
            "content-type": "application/json;",
        }
        try:
            async with aiohttp.ClientSession() as session:
                async with session.post(
                        f"{pool_config.pool_url}/farmer",
                        data=post_farmer_body,
                        headers=headers,
                        ssl=ssl_context_for_root(get_mozilla_ca_crt()),
                ) as resp:
                    if resp.ok:
                        response: Dict = json.loads(await resp.text())
                        self.log.info(f"POST /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 POST /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 POST /farmer {pool_config.pool_url}, {e}")
        return None
Пример #5
0
 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
Пример #6
0
    async def _pool_get_pool_info(self, pool_config: PoolWalletConfig) -> Optional[Dict]:
        try:
            async with aiohttp.ClientSession(trust_env=True) as session:
                async with session.get(
                    f"{pool_config.pool_url}/pool_info", 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 /pool_info response: {response}")
                        return response
                    else:
                        self.handle_failed_pool_response(
                            pool_config.p2_singleton_puzzle_hash,
                            f"Error in GET /pool_info {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 /pool_info {pool_config.pool_url}, {e}"
            )

        return None
Пример #7
0
async def post(session: aiohttp.ClientSession, url: str, data: Any):
    mozilla_root = get_mozilla_ca_crt()
    ssl_context = ssl_context_for_root(mozilla_root)
    response = await session.post(url, json=data, ssl=ssl_context)
    return await response.json()
Пример #8
0
    async def new_proof_of_space(
            self, new_proof_of_space: harvester_protocol.NewProofOfSpace,
            peer: ws.WSChiaConnection):
        """
        This is a response from the harvester, for a NewChallenge. Here we check if the proof
        of space is sufficiently good, and if so, we ask for the whole proof.
        """
        if new_proof_of_space.sp_hash not in self.farmer.number_of_responses:
            self.farmer.number_of_responses[new_proof_of_space.sp_hash] = 0
            self.farmer.cache_add_time[new_proof_of_space.sp_hash] = uint64(
                int(time.time()))

        max_pos_per_sp = 5

        if self.farmer.constants.NETWORK_TYPE != NetworkType.MAINNET:
            # This is meant to make testnets more stable, when difficulty is very low
            if self.farmer.number_of_responses[
                    new_proof_of_space.sp_hash] > max_pos_per_sp:
                self.farmer.log.info(
                    f"Surpassed {max_pos_per_sp} PoSpace for one SP, no longer submitting PoSpace for signage point "
                    f"{new_proof_of_space.sp_hash}")
                return None

        if new_proof_of_space.sp_hash not in self.farmer.sps:
            self.farmer.log.warning(
                f"Received response for a signage point that we do not have {new_proof_of_space.sp_hash}"
            )
            return None

        sps = self.farmer.sps[new_proof_of_space.sp_hash]
        for sp in sps:
            computed_quality_string = new_proof_of_space.proof.verify_and_get_quality_string(
                self.farmer.constants,
                new_proof_of_space.challenge_hash,
                new_proof_of_space.sp_hash,
            )
            if computed_quality_string is None:
                self.farmer.log.error(
                    f"Invalid proof of space {new_proof_of_space.proof}")
                return None

            self.farmer.number_of_responses[new_proof_of_space.sp_hash] += 1

            required_iters: uint64 = calculate_iterations_quality(
                self.farmer.constants.DIFFICULTY_CONSTANT_FACTOR,
                computed_quality_string,
                new_proof_of_space.proof.size,
                sp.difficulty,
                new_proof_of_space.sp_hash,
            )

            # If the iters are good enough to make a block, proceed with the block making flow
            if required_iters < calculate_sp_interval_iters(
                    self.farmer.constants, sp.sub_slot_iters):
                # Proceed at getting the signatures for this PoSpace
                request = harvester_protocol.RequestSignatures(
                    new_proof_of_space.plot_identifier,
                    new_proof_of_space.challenge_hash,
                    new_proof_of_space.sp_hash,
                    [sp.challenge_chain_sp, sp.reward_chain_sp],
                )

                if new_proof_of_space.sp_hash not in self.farmer.proofs_of_space:
                    self.farmer.proofs_of_space[
                        new_proof_of_space.sp_hash] = []
                self.farmer.proofs_of_space[new_proof_of_space.sp_hash].append(
                    (
                        new_proof_of_space.plot_identifier,
                        new_proof_of_space.proof,
                    ))
                self.farmer.cache_add_time[
                    new_proof_of_space.sp_hash] = uint64(int(time.time()))
                self.farmer.quality_str_to_identifiers[
                    computed_quality_string] = (
                        new_proof_of_space.plot_identifier,
                        new_proof_of_space.challenge_hash,
                        new_proof_of_space.sp_hash,
                        peer.peer_node_id,
                    )
                self.farmer.cache_add_time[computed_quality_string] = uint64(
                    int(time.time()))

                await peer.send_message(
                    make_msg(ProtocolMessageTypes.request_signatures, request))

            p2_singleton_puzzle_hash = new_proof_of_space.proof.pool_contract_puzzle_hash
            if p2_singleton_puzzle_hash is not None:
                # Otherwise, send the proof of space to the pool
                # When we win a block, we also send the partial to the pool
                if p2_singleton_puzzle_hash not in self.farmer.pool_state:
                    self.farmer.log.info(
                        f"Did not find pool info for {p2_singleton_puzzle_hash}"
                    )
                    return
                pool_state_dict: Dict = self.farmer.pool_state[
                    p2_singleton_puzzle_hash]
                pool_url = pool_state_dict["pool_config"].pool_url
                if pool_url == "":
                    return

                if pool_state_dict["current_difficulty"] is None:
                    self.farmer.log.warning(
                        f"No pool specific difficulty has been set for {p2_singleton_puzzle_hash}, "
                        f"check communication with the pool, skipping this partial to {pool_url}."
                    )
                    return

                required_iters = calculate_iterations_quality(
                    self.farmer.constants.DIFFICULTY_CONSTANT_FACTOR,
                    computed_quality_string,
                    new_proof_of_space.proof.size,
                    pool_state_dict["current_difficulty"],
                    new_proof_of_space.sp_hash,
                )
                if required_iters >= calculate_sp_interval_iters(
                        self.farmer.constants,
                        self.farmer.constants.POOL_SUB_SLOT_ITERS):
                    self.farmer.log.info(
                        f"Proof of space not good enough for pool {pool_url}: {pool_state_dict['current_difficulty']}"
                    )
                    return

                authentication_token_timeout = pool_state_dict[
                    "authentication_token_timeout"]
                if authentication_token_timeout is None:
                    self.farmer.log.warning(
                        f"No pool specific authentication_token_timeout has been set for {p2_singleton_puzzle_hash}"
                        f", check communication with the pool.")
                    return

                # Submit partial to pool
                is_eos = new_proof_of_space.signage_point_index == 0

                payload = PostPartialPayload(
                    pool_state_dict["pool_config"].launcher_id,
                    get_current_authentication_token(
                        authentication_token_timeout),
                    new_proof_of_space.proof,
                    new_proof_of_space.sp_hash,
                    is_eos,
                    peer.peer_node_id,
                )

                # The plot key is 2/2 so we need the harvester's half of the signature
                m_to_sign = payload.get_hash()
                request = harvester_protocol.RequestSignatures(
                    new_proof_of_space.plot_identifier,
                    new_proof_of_space.challenge_hash,
                    new_proof_of_space.sp_hash,
                    [m_to_sign],
                )
                response: Any = await peer.request_signatures(request)
                if not isinstance(response,
                                  harvester_protocol.RespondSignatures):
                    self.farmer.log.error(
                        f"Invalid response from harvester: {response}")
                    return

                assert len(response.message_signatures) == 1

                plot_signature: Optional[G2Element] = None
                for sk in self.farmer.get_private_keys():
                    pk = sk.get_g1()
                    if pk == response.farmer_pk:
                        agg_pk = ProofOfSpace.generate_plot_public_key(
                            response.local_pk, pk, True)
                        assert agg_pk == new_proof_of_space.proof.plot_public_key
                        sig_farmer = AugSchemeMPL.sign(sk, m_to_sign, agg_pk)
                        taproot_sk: PrivateKey = ProofOfSpace.generate_taproot_sk(
                            response.local_pk, pk)
                        taproot_sig: G2Element = AugSchemeMPL.sign(
                            taproot_sk, m_to_sign, agg_pk)

                        plot_signature = AugSchemeMPL.aggregate([
                            sig_farmer, response.message_signatures[0][1],
                            taproot_sig
                        ])
                        assert AugSchemeMPL.verify(agg_pk, m_to_sign,
                                                   plot_signature)
                authentication_pk = pool_state_dict[
                    "pool_config"].authentication_public_key
                if bytes(authentication_pk) is None:
                    self.farmer.log.error(
                        f"No authentication sk for {authentication_pk}")
                    return
                authentication_sk: PrivateKey = self.farmer.authentication_keys[
                    bytes(authentication_pk)]
                authentication_signature = AugSchemeMPL.sign(
                    authentication_sk, m_to_sign)

                assert plot_signature is not None

                agg_sig: G2Element = AugSchemeMPL.aggregate(
                    [plot_signature, authentication_signature])

                post_partial_request: PostPartialRequest = PostPartialRequest(
                    payload, agg_sig)
                self.farmer.log.info(
                    f"Submitting partial for {post_partial_request.payload.launcher_id.hex()} to {pool_url}"
                )
                pool_state_dict["points_found_since_start"] += pool_state_dict[
                    "current_difficulty"]
                pool_state_dict["points_found_24h"].append(
                    (time.time(), pool_state_dict["current_difficulty"]))

                try:
                    async with aiohttp.ClientSession() as session:
                        async with session.post(
                                f"{pool_url}/partial",
                                json=post_partial_request.to_json_dict(),
                                ssl=ssl_context_for_root(get_mozilla_ca_crt(),
                                                         log=self.farmer.log),
                        ) as resp:
                            if resp.ok:
                                pool_response: Dict = json.loads(await
                                                                 resp.text())
                                self.farmer.log.info(
                                    f"Pool response: {pool_response}")
                                if "error_code" in pool_response:
                                    self.farmer.log.error(
                                        f"Error in pooling: "
                                        f"{pool_response['error_code'], pool_response['error_message']}"
                                    )
                                    pool_state_dict["pool_errors_24h"].append(
                                        pool_response)
                                    if pool_response[
                                            "error_code"] == PoolErrorCode.PROOF_NOT_GOOD_ENOUGH.value:
                                        self.farmer.log.error(
                                            "Partial not good enough, forcing pool farmer update to "
                                            "get our current difficulty.")
                                        pool_state_dict[
                                            "next_farmer_update"] = 0
                                        await self.farmer.update_pool_state()
                                else:
                                    new_difficulty = pool_response[
                                        "new_difficulty"]
                                    pool_state_dict[
                                        "points_acknowledged_since_start"] += new_difficulty
                                    pool_state_dict[
                                        "points_acknowledged_24h"].append(
                                            (time.time(), new_difficulty))
                                    pool_state_dict[
                                        "current_difficulty"] = new_difficulty
                            else:
                                self.farmer.log.error(
                                    f"Error sending partial to {pool_url}, {resp.status}"
                                )
                except Exception as e:
                    self.farmer.log.error(f"Error connecting to pool: {e}")
                    return

                return