def replace_str_to_bytes(self, **changes) -> "ConsensusConstants": """ Overrides str (hex) values with bytes. """ for k, v in changes.items(): if isinstance(v, str): changes[k] = hexstr_to_bytes(v) return dataclasses.replace(self, **changes)
async def get_mempool_item_by_tx_id(self, request: Dict) -> Optional[Dict]: if "tx_id" not in request: raise ValueError("No tx_id in request") tx_id: bytes32 = hexstr_to_bytes(request["tx_id"]) item = self.service.mempool_manager.get_mempool_item(tx_id) if item is None: raise ValueError(f"Tx id 0x{tx_id.hex()} not in the mempool") return {"mempool_item": item}
async def get_transaction(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None: wallet_id = args["id"] transaction_id = hexstr_to_bytes(args["tx_id"]) config = load_config(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME) name = config["network_overrides"]["config"][ config["selected_network"]]["address_prefix"] tx: TransactionRecord = await wallet_client.get_transaction( wallet_id, transaction_id=transaction_id) print_transaction(tx, verbose=(args["verbose"] > 0), name=name)
async def get_block(self, request: Dict) -> Optional[Dict]: if "header_hash" not in request: raise ValueError("No header_hash in request") header_hash = hexstr_to_bytes(request["header_hash"]) block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash) if block is None: raise ValueError(f"Block {header_hash.hex()} not found") return {"block": block}
async def did_create_attest(self, request): wallet_id = int(request["wallet_id"]) wallet: DIDWallet = self.service.wallet_state_manager.wallets[ wallet_id] async with self.service.wallet_state_manager.lock: info = await wallet.get_info_for_recovery() coin = hexstr_to_bytes(request["coin_name"]) pubkey = G1Element.from_bytes(hexstr_to_bytes(request["pubkey"])) spend_bundle = await wallet.create_attestment( coin, hexstr_to_bytes(request["puzhash"]), pubkey, request["filename"]) if spend_bundle is not None: return { "success": True, "message_spend_bundle": bytes(spend_bundle).hex(), "info": [info[0].hex(), info[1].hex(), info[2]], } else: return {"success": False}
async def get_connections(self, node_type: Optional[NodeType] = None ) -> List[Dict]: request = {} if node_type is not None: request["node_type"] = node_type.value response = await self.fetch("get_connections", request) for connection in response["connections"]: connection["node_id"] = hexstr_to_bytes(connection["node_id"]) return response["connections"]
async def get_transaction(self, request: Dict) -> Dict: assert self.service.wallet_state_manager is not None transaction_id: bytes32 = bytes32(hexstr_to_bytes(request["transaction_id"])) tr: Optional[TransactionRecord] = await self.service.wallet_state_manager.get_transaction(transaction_id) if tr is None: raise ValueError(f"Transaction 0x{transaction_id.hex()} not found") return { "transaction": tr, "transaction_id": tr.name, }
async def cancel_trade(self, request: Dict): assert self.service.wallet_state_manager is not None wsm = self.service.wallet_state_manager secure = request["secure"] trade_id = hexstr_to_bytes(request["trade_id"]) if secure: await wsm.trade_manager.cancel_pending_offer_safely(trade_id) else: await wsm.trade_manager.cancel_pending_offer(trade_id) return {}
async def create(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None: state = args["state"] prompt = not args.get("yes", False) # Could use initial_pool_state_from_dict to simplify if state == "SELF_POOLING": pool_url: Optional[str] = None relative_lock_height = uint32(0) target_puzzle_hash = None # wallet will fill this in elif state == "FARMING_TO_POOL": pool_url = str(args["pool_url"]) json_dict = await create_pool_args(pool_url) relative_lock_height = json_dict["relative_lock_height"] target_puzzle_hash = hexstr_to_bytes(json_dict["target_puzzle_hash"]) else: raise ValueError( "Plot NFT must be created in SELF_POOLING or FARMING_TO_POOL state." ) pool_msg = f" and join pool: {pool_url}" if pool_url else "" print(f"Will create a plot NFT{pool_msg}.") if prompt: user_input: str = input("Confirm [n]/y: ") else: user_input = "yes" if user_input.lower() == "y" or user_input.lower() == "yes": try: tx_record: TransactionRecord = await wallet_client.create_new_pool_wallet( target_puzzle_hash, pool_url, relative_lock_height, "localhost:5000", "new", state, ) start = time.time() while time.time() - start < 10: await asyncio.sleep(0.1) tx = await wallet_client.get_transaction( str(1), tx_record.name) if len(tx.sent_to) > 0: print(f"Transaction submitted to nodes: {tx.sent_to}") print( f"Do chia wallet get_transaction -f {fingerprint} -tx 0x{tx_record.name} to get status" ) return None except Exception as e: print(f"Error creating plot NFT: {e}") return print("Aborting.")
async def get_coin_record_by_name(self, request: Dict) -> Optional[Dict]: """ Retrieves a coin record by it's name. """ if "name" not in request: raise ValueError("Name not in request") name = hexstr_to_bytes(request["name"]) coin_record: Optional[CoinRecord] = await self.service.blockchain.coin_store.get_coin_record(name) if coin_record is None: raise ValueError(f"Coin record 0x{name.hex()} not found") return {"coin_record": coin_record}
async def get_block_record(self, request: Dict): if "header_hash" not in request: raise ValueError("header_hash not in request") header_hash_str = request["header_hash"] header_hash = hexstr_to_bytes(header_hash_str) record: Optional[BlockRecord] = self.service.blockchain.try_block_record(header_hash) if record is None: # Fetch from DB record = await self.service.blockchain.block_store.get_block_record(header_hash) if record is None: raise ValueError(f"Block {header_hash.hex()} does not exist") return {"block_record": record}
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()}") return await self.login_response(launcher_id)
async def get_object(self, key: str, type: Any) -> Any: """ Return bytes representation of stored object """ cursor = await self.db_connection.execute( "SELECT * from key_val_store WHERE key=?", (key, )) row = await cursor.fetchone() await cursor.close() if row is None: return None return type.from_bytes(hexstr_to_bytes(row[1]))
async def close_connection(self, request: Dict): node_id = hexstr_to_bytes(request["node_id"]) if self.rpc_api.service.server is None: raise aiohttp.web.HTTPInternalServerError() connections_to_close = [ c for c in self.rpc_api.service.server.get_connections() if c.peer_node_id == node_id ] if len(connections_to_close) == 0: raise ValueError( f"Connection with node_id {node_id.hex()} does not exist") for connection in connections_to_close: await connection.close() return {}
def dataclass_from_dict(klass, d): """ Converts a dictionary based on a dataclass, into an instance of that dataclass. Recursively goes through lists, optionals, and dictionaries. """ if is_type_SpecificOptional(klass): # Type is optional, data is either None, or Any if not d: return None return dataclass_from_dict(get_args(klass)[0], d) elif is_type_Tuple(klass): # Type is tuple, can have multiple different types inside i = 0 klass_properties = [] for item in d: klass_properties.append( dataclass_from_dict(klass.__args__[i], item)) i = i + 1 return tuple(klass_properties) elif dataclasses.is_dataclass(klass): # Type is a dataclass, data is a dictionary fieldtypes = {f.name: f.type for f in dataclasses.fields(klass)} return klass( **{f: dataclass_from_dict(fieldtypes[f], d[f]) for f in d}) elif is_type_List(klass): # Type is a list, data is a list return [dataclass_from_dict(get_args(klass)[0], item) for item in d] elif issubclass(klass, bytes): # Type is bytes, data is a hex string return klass(hexstr_to_bytes(d)) elif klass in unhashable_types: # Type is unhashable (bls type), so cast from hex string return klass.from_bytes(hexstr_to_bytes(d)) else: # Type is a primitive, cast with correct class return klass(d)
async def get_network_space(self, request: Dict) -> Optional[Dict]: """ Retrieves an estimate of total space validating the chain between two block header hashes. """ if "newer_block_header_hash" not in request or "older_block_header_hash" not in request: raise ValueError( "Invalid request. newer_block_header_hash and older_block_header_hash required" ) newer_block_hex = request["newer_block_header_hash"] older_block_hex = request["older_block_header_hash"] if newer_block_hex == older_block_hex: raise ValueError("New and old must not be the same") newer_block_bytes = hexstr_to_bytes(newer_block_hex) older_block_bytes = hexstr_to_bytes(older_block_hex) newer_block = await self.service.block_store.get_block_record( newer_block_bytes) if newer_block is None: raise ValueError("Newer block not found") older_block = await self.service.block_store.get_block_record( older_block_bytes) if older_block is None: raise ValueError("Newer block not found") delta_weight = newer_block.weight - older_block.weight delta_iters = newer_block.total_iters - older_block.total_iters weight_div_iters = delta_weight / delta_iters additional_difficulty_constant = self.service.constants.DIFFICULTY_CONSTANT_FACTOR eligible_plots_filter_multiplier = 2**self.service.constants.NUMBER_ZERO_BITS_PLOT_FILTER network_space_bytes_estimate = (UI_ACTUAL_SPACE_CONSTANT_FACTOR * weight_div_iters * additional_difficulty_constant * eligible_plots_filter_multiplier) return {"space": uint128(int(network_space_bytes_estimate))}
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)
def load_pool_config(root_path: Path) -> List[PoolWalletConfig]: config = load_config(root_path, "config.yaml") ret_list: List[PoolWalletConfig] = [] if "pool_list" in config["pool"]: for pool_config_dict in config["pool"]["pool_list"]: try: pool_config = PoolWalletConfig( hexstr_to_bytes(pool_config_dict["launcher_id"]), pool_config_dict["pool_url"], pool_config_dict["payout_instructions"], hexstr_to_bytes(pool_config_dict["target_puzzle_hash"]), hexstr_to_bytes( pool_config_dict["p2_singleton_puzzle_hash"]), G1Element.from_bytes( hexstr_to_bytes(pool_config_dict["owner_public_key"])), G1Element.from_bytes( hexstr_to_bytes( pool_config_dict["authentication_public_key"])), ) ret_list.append(pool_config) except Exception as e: log.error(f"Exception loading config: {pool_config_dict} {e}") return ret_list
async def did_recovery_spend(self, request): wallet_id = int(request["wallet_id"]) wallet: DIDWallet = self.service.wallet_state_manager.wallets[ wallet_id] if len(request["attest_filenames"] ) < wallet.did_info.num_of_backup_ids_needed: return {"success": False, "reason": "insufficient messages"} async with self.service.wallet_state_manager.lock: ( info_list, message_spend_bundle, ) = await wallet.load_attest_files_for_recovery_spend( request["attest_filenames"]) if "pubkey" in request: pubkey = G1Element.from_bytes( hexstr_to_bytes(request["pubkey"])) else: assert wallet.did_info.temp_pubkey is not None pubkey = wallet.did_info.temp_pubkey if "puzhash" in request: puzhash = hexstr_to_bytes(request["puzhash"]) else: assert wallet.did_info.temp_puzhash is not None puzhash = wallet.did_info.temp_puzhash success = await wallet.recovery_spend( wallet.did_info.temp_coin, puzhash, info_list, pubkey, message_spend_bundle, ) return {"success": success}
async def get_additions_and_removals(self, request: Dict) -> Optional[Dict]: if "header_hash" not in request: raise ValueError("No header_hash in request") header_hash = hexstr_to_bytes(request["header_hash"]) block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash) if block is None: raise ValueError(f"Block {header_hash.hex()} not found") async with self.service.blockchain.lock: if self.service.blockchain.height_to_hash(block.height) != header_hash: raise ValueError(f"Block at {header_hash.hex()} is no longer in the blockchain (it's in a fork)") additions: List[CoinRecord] = await self.service.coin_store.get_coins_added_at_height(block.height) removals: List[CoinRecord] = await self.service.coin_store.get_coins_removed_at_height(block.height) return {"additions": additions, "removals": removals}
def replace_str_to_bytes(self, **changes) -> "ConsensusConstants": """ Overrides str (hex) values with bytes. """ filtered_changes = {} for k, v in changes.items(): if not hasattr(self, k): log.warn(f'invalid key in network configuration (config.yaml) "{k}". Ignoring') continue if isinstance(v, str): filtered_changes[k] = hexstr_to_bytes(v) else: filtered_changes[k] = v return dataclasses.replace(self, **filtered_changes)
async def create( wallet_state_manager: Any, wallet: Wallet, wallet_info: WalletInfo, ) -> CATWallet: self = CATWallet() self.log = logging.getLogger(__name__) self.cost_of_single_tx = None self.wallet_state_manager = wallet_state_manager self.wallet_info = wallet_info self.standard_wallet = wallet self.cat_info = CATInfo.from_bytes( hexstr_to_bytes(self.wallet_info.data)) return self
async def did_update_recovery_ids(self, request): wallet_id = int(request["wallet_id"]) wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id] recovery_list = [] for _ in request["new_list"]: recovery_list.append(hexstr_to_bytes(_)) if "num_verifications_required" in request: new_amount_verifications_required = uint64(request["num_verifications_required"]) else: new_amount_verifications_required = len(recovery_list) success = await wallet.update_recovery_list(recovery_list, new_amount_verifications_required) # Update coin with new ID info updated_puz = await wallet.get_new_puzzle() spend_bundle = await wallet.create_spend(updated_puz.get_tree_hash()) if spend_bundle is not None and success: return {"success": True} return {"success": False}
async def create( wallet_state_manager: Any, wallet: Wallet, wallet_info: WalletInfo, ) -> CCWallet: self = CCWallet() self.log = logging.getLogger(__name__) self.cost_of_single_tx = None self.wallet_state_manager = wallet_state_manager self.wallet_info = wallet_info self.standard_wallet = wallet self.cc_info = CCInfo.from_bytes(hexstr_to_bytes(self.wallet_info.data)) self.base_puzzle_program = None self.base_inner_puzzle_hash = None return self
async def puzzle_solution_received(self, response: RespondPuzzleSolution): unwrapped: PuzzleSolutionResponse = response.response actions: List[WalletAction] = await self.action_store.get_all_pending_actions() for action in actions: data = json.loads(action.data) action_data = data["data"]["action_data"] if action.name == "request_puzzle_solution": stored_coin_name = bytes32(hexstr_to_bytes(action_data["coin_name"])) height = uint32(action_data["height"]) if stored_coin_name == unwrapped.coin_name and height == unwrapped.height: if action.done: return None wallet = self.wallets[uint32(action.wallet_id)] callback_str = action.wallet_callback if callback_str is not None: callback = getattr(wallet, callback_str) await callback(unwrapped, action.id)
async def generator_received(self, height: uint32, header_hash: uint32, program: Program): actions: List[WalletAction] = await self.action_store.get_all_pending_actions() for action in actions: data = json.loads(action.data) action_data = data["data"]["action_data"] if action.name == "request_generator": stored_header_hash = bytes32(hexstr_to_bytes(action_data["header_hash"])) stored_height = uint32(action_data["height"]) if stored_header_hash == header_hash and stored_height == height: if action.done: return None wallet = self.wallets[uint32(action.wallet_id)] callback_str = action.wallet_callback if callback_str is not None: callback = getattr(wallet, callback_str) await callback(height, header_hash, program, action.id)
async def set_payout_instructions(self, launcher_id: bytes32, payout_instructions: str): for p2_singleton_puzzle_hash, pool_state_dict in self.pool_state.items(): if launcher_id == pool_state_dict["pool_config"].launcher_id: config = load_config(self._root_path, "config.yaml") new_list = [] for list_element in config["pool"]["pool_list"]: if hexstr_to_bytes(list_element["launcher_id"]) == bytes(launcher_id): list_element["payout_instructions"] = payout_instructions new_list.append(list_element) config["pool"]["pool_list"] = new_list save_config(self._root_path, "config.yaml", config) # Force a GET /farmer which triggers the PUT /farmer if it detects the changed instructions pool_state_dict["next_farmer_update"] = 0 return self.log.warning(f"Launcher id: {launcher_id} not found")
async def _action_messages(self) -> List[Message]: if self.wallet_state_manager is None or self.backup_initialized is False: return [] actions: List[WalletAction] = await self.wallet_state_manager.action_store.get_all_pending_actions() result: List[Message] = [] for action in actions: data = json.loads(action.data) action_data = data["data"]["action_data"] if action.name == "request_puzzle_solution": coin_name = bytes32(hexstr_to_bytes(action_data["coin_name"])) height = uint32(action_data["height"]) msg = make_msg( ProtocolMessageTypes.request_puzzle_solution, wallet_protocol.RequestPuzzleSolution(coin_name, height), ) result.append(msg) return result
async def get_coin_records_by_puzzle_hash(self, request: Dict) -> Optional[Dict]: """ Retrieves the coins for a given puzzlehash, by default returns unspent coins. """ if "puzzle_hash" not in request: raise ValueError("Puzzle hash not in request") kwargs: Dict[str, Any] = {"include_spent_coins": False, "puzzle_hash": hexstr_to_bytes(request["puzzle_hash"])} if "start_height" in request: kwargs["start_height"] = uint32(request["start_height"]) if "end_height" in request: kwargs["end_height"] = uint32(request["end_height"]) if "include_spent_coins" in request: kwargs["include_spent_coins"] = request["include_spent_coins"] coin_records = await self.service.blockchain.coin_store.get_coin_records_by_puzzle_hash(**kwargs) return {"coin_records": coin_records}
async def get_signage_point(self, request: Dict) -> Dict: sp_hash = hexstr_to_bytes(request["sp_hash"]) for _, sps in self.service.sps.items(): for sp in sps: if sp.challenge_chain_sp == sp_hash: pospaces = self.service.proofs_of_space.get(sp.challenge_chain_sp, []) return { "signage_point": { "challenge_hash": sp.challenge_hash, "challenge_chain_sp": sp.challenge_chain_sp, "reward_chain_sp": sp.reward_chain_sp, "difficulty": sp.difficulty, "sub_slot_iters": sp.sub_slot_iters, "signage_point_index": sp.signage_point_index, }, "proofs": pospaces, } raise ValueError(f"Signage point {sp_hash.hex()} not found")