async def test_did_recovery_with_empty_set(self, two_wallet_nodes): num_blocks = 5 full_nodes, wallets = two_wallet_nodes full_node_1 = full_nodes[0] server_1 = full_node_1.server wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await server_3.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, num_blocks): await full_node_1.farm_new_transaction_block( FarmNewBlockProtocol(ph)) funds = sum([ calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1) ]) await time_out_assert(15, wallet.get_confirmed_balance, funds) did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet( wallet_node.wallet_state_manager, wallet, uint64(101)) for i in range(1, num_blocks): await full_node_1.farm_new_transaction_block( FarmNewBlockProtocol(ph)) await time_out_assert(15, did_wallet.get_confirmed_balance, 101) await time_out_assert(15, did_wallet.get_unconfirmed_balance, 101) coins = await did_wallet.select_coins(1) coin = coins.pop() info = Program.to([]) pubkey = (await did_wallet.wallet_state_manager.get_unused_derivation_record( did_wallet.wallet_info.id)).pubkey spend_bundle = await did_wallet.recovery_spend( coin, ph, info, pubkey, SpendBundle([], AugSchemeMPL.aggregate([]))) additions = spend_bundle.additions() assert additions == []
async def new_signage_point(self, new_signage_point: farmer_protocol.NewSignagePoint): message = harvester_protocol.NewSignagePointHarvester( new_signage_point.challenge_hash, new_signage_point.difficulty, new_signage_point.sub_slot_iters, new_signage_point.signage_point_index, new_signage_point.challenge_chain_sp, ) msg = make_msg(ProtocolMessageTypes.new_signage_point_harvester, message) await self.farmer.server.send_to_all([msg], NodeType.HARVESTER) if new_signage_point.challenge_chain_sp not in self.farmer.sps: self.farmer.sps[new_signage_point.challenge_chain_sp] = [] self.farmer.sps[new_signage_point.challenge_chain_sp].append(new_signage_point) self.farmer.cache_add_time[new_signage_point.challenge_chain_sp] = uint64(int(time.time())) self.farmer.state_changed("new_signage_point", {"sp_hash": new_signage_point.challenge_chain_sp})
def get_future_reward_coins(block: FullBlock) -> Tuple[Coin, Coin]: pool_amount = calculate_pool_reward(block.height) farmer_amount = calculate_base_farmer_reward(block.height) if block.is_transaction_block(): assert block.transactions_info is not None farmer_amount = uint64(farmer_amount + block.transactions_info.fees) pool_coin: Coin = create_pool_coin( block.height, block.foliage.foliage_block_data.pool_target.puzzle_hash, pool_amount, constants.GENESIS_CHALLENGE) farmer_coin: Coin = create_farmer_coin( block.height, block.foliage.foliage_block_data.farmer_reward_puzzle_hash, farmer_amount, constants.GENESIS_CHALLENGE, ) return pool_coin, farmer_coin
def mempool_assert_relative_time_exceeds(condition: ConditionWithArgs, unspent: CoinRecord, timestamp: uint64) -> Optional[Err]: """ Check if the current time in seconds exceeds the time specified by condition """ try: expected_seconds = int_from_bytes(condition.vars[0]) except ValueError: return Err.INVALID_CONDITION if timestamp is None: timestamp = uint64(int(time.time())) if timestamp < expected_seconds + unspent.timestamp: return Err.ASSERT_SECONDS_RELATIVE_FAILED return None
async def get_farmer_points_and_payout_instructions(self) -> List[Tuple[uint64, bytes]]: cursor = await self.connection.execute(f"SELECT points, payout_instructions from farmer") rows = await cursor.fetchall() accumulated: Dict[bytes32, uint64] = {} for row in rows: points: uint64 = uint64(row[0]) ph: bytes32 = bytes32(bytes.fromhex(row[1])) if ph in accumulated: accumulated[ph] += points else: accumulated[ph] = points ret: List[Tuple[uint64, bytes32]] = [] for ph, total_points in accumulated.items(): ret.append((total_points, ph)) return ret
def mempool_assert_absolute_time_exceeds( condition: ConditionWithArgs, timestamp: Optional[uint64] = None) -> Optional[Err]: """ Check if the current time in millis exceeds the time specified by condition """ try: expected_mili_time = int_from_bytes(condition.vars[0]) except ValueError: return Err.INVALID_CONDITION if timestamp is None: timestamp = uint64(int(time.time() * 1000)) if timestamp < expected_mili_time: return Err.ASSERT_SECONDS_ABSOLUTE_FAILED return None
async def test_assert_time_exceeds_both_cases(self, two_nodes): reward_ph = WALLET_A.get_new_puzzlehash() full_node_1, full_node_2, server_1, server_2 = two_nodes blocks = await full_node_1.get_all_full_blocks() start_height = blocks[-1].height if len(blocks) > 0 else -1 blocks = bt.get_consecutive_blocks( 3, block_list_input=blocks, guarantee_transaction_block=True, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) peer = await connect_and_get_peer(server_1, server_2) for block in blocks: await full_node_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3) time_now = uint64(int(time() * 1000)) time_now_plus_3 = time_now + 3000 cvp = ConditionVarPair(ConditionOpcode.ASSERT_SECONDS_NOW_EXCEEDS, [time_now_plus_3.to_bytes(8, "big")]) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle( list(blocks[-1].get_included_reward_coins())[0], dic) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1) await full_node_1.respond_transaction(tx1, peer) # Sleep so that 3 sec passes await asyncio.sleep(3) tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1) await full_node_1.respond_transaction(tx2, peer) sb1 = full_node_1.full_node.mempool_manager.get_spendbundle( spend_bundle1.name()) assert sb1 is spend_bundle1
def _can_infuse_unfinished_block(self, block: timelord_protocol.NewUnfinishedBlockTimelord) -> Optional[uint64]: assert self.last_state is not None sub_slot_iters = self.last_state.get_sub_slot_iters() difficulty = self.last_state.get_difficulty() ip_iters = self.last_state.get_last_ip() rc_block = block.reward_chain_block try: block_sp_iters, block_ip_iters = iters_from_block( self.constants, rc_block, sub_slot_iters, difficulty, ) except Exception as e: log.warning(f"Received invalid unfinished block: {e}.") return None block_sp_total_iters = self.last_state.total_iters - ip_iters + block_sp_iters if is_overflow_block(self.constants, block.reward_chain_block.signage_point_index): block_sp_total_iters -= self.last_state.get_sub_slot_iters() found_index = -1 for index, (rc, total_iters) in enumerate(self.last_state.reward_challenge_cache): if rc == block.rc_prev: found_index = index break if found_index == -1: log.warning(f"Will not infuse {block.rc_prev} because its reward chain challenge is not in the chain") return None new_block_iters = uint64(block_ip_iters - ip_iters) if len(self.last_state.reward_challenge_cache) > found_index + 1: if self.last_state.reward_challenge_cache[found_index + 1][1] < block_sp_total_iters: log.warning( f"Will not infuse unfinished block {block.rc_prev} sp total iters {block_sp_total_iters}, " f"because there is another infusion before its SP" ) return None if self.last_state.reward_challenge_cache[found_index][1] > block_sp_total_iters: if not is_overflow_block(self.constants, block.reward_chain_block.signage_point_index): log.error( f"Will not infuse unfinished block {block.rc_prev}, sp total iters: {block_sp_total_iters}, " f"because its iters are too low" ) return None if new_block_iters > 0: return new_block_iters return None
def get_name_puzzle_conditions(block_program: SerializedProgram, safe_mode: bool): # TODO: allow generator mod to take something (future) # TODO: write more tests block_program_args = SerializedProgram.from_bytes(b"\x80") try: if safe_mode: cost, result = GENERATOR_MOD.run_safe_with_cost( block_program, block_program_args) else: cost, result = GENERATOR_MOD.run_with_cost(block_program, block_program_args) npc_list = [] opcodes = set(item.value for item in ConditionOpcode) for res in result.as_iter(): conditions_list = [] name = std_hash( bytes(res.first().first().as_atom() + res.first().rest().first().as_atom() + res.first().rest().rest().first().as_atom())) puzzle_hash = bytes32(res.first().rest().first().as_atom()) for cond in res.rest().first().as_iter(): if cond.first().as_atom() in opcodes: opcode = ConditionOpcode(cond.first().as_atom()) elif not safe_mode: opcode = ConditionOpcode.UNKNOWN else: return "Unknown operator in safe mode.", None, None if len(list(cond.as_iter())) > 1: cond_var_list = [] for cond_1 in cond.rest().as_iter(): cond_var_list.append(cond_1.as_atom()) cvl = ConditionVarPair(opcode, cond_var_list) else: cvl = ConditionVarPair(opcode, []) conditions_list.append(cvl) conditions_dict = conditions_by_opcode(conditions_list) if conditions_dict is None: conditions_dict = {} npc_list.append( NPC(name, puzzle_hash, [(a, b) for a, b in conditions_dict.items()])) return None, npc_list, uint64(cost) except Exception: tb = traceback.format_exc() return tb, None, None
def spend_coin_to_singleton( puzzle_db: PuzzleDB, launcher_puzzle: Program, coin_store: CoinStore, now: CoinTimestamp) -> Tuple[List[Coin], List[CoinSpend]]: farmed_coin_amount = 100000 metadata = [("foo", "bar")] now = CoinTimestamp(10012300, 1) farmed_coin = coin_store.farm_coin(ANYONE_CAN_SPEND_PUZZLE.get_tree_hash(), now, amount=farmed_coin_amount) now.seconds += 500 now.height += 1 launcher_amount: uint64 = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( puzzle_db, farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_spend = CoinSpend(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_spend], G2Element())]) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) launcher_coin = launcher_spend_bundle.coin_spends[0].coin assert_coin_spent(coin_store, launcher_coin) assert_coin_spent(coin_store, farmed_coin) singleton_expected_puzzle = singleton_puzzle(launcher_id, launcher_puzzle_hash, initial_singleton_puzzle) singleton_expected_puzzle_hash = singleton_expected_puzzle.get_tree_hash() expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert_coin_spent(coin_store, expected_singleton_coin, is_spent=False) return additions, removals
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 test_double_spend_with_higher_fee(self, two_nodes): reward_ph = WALLET_A.get_new_puzzlehash() full_node_1, full_node_2, server_1, server_2 = two_nodes blocks = await full_node_1.get_all_full_blocks() start_height = blocks[-1].height blocks = bt.get_consecutive_blocks( 3, block_list_input=blocks, guarantee_transaction_block=True, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) peer = await connect_and_get_peer(server_1, server_2) for block in blocks: await full_node_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3) spend_bundle1 = generate_test_spend_bundle( list(blocks[-1].get_included_reward_coins())[0]) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1) await full_node_1.respond_transaction(tx1, peer) spend_bundle2 = generate_test_spend_bundle(list( blocks[-1].get_included_reward_coins())[0], fee=uint64(1)) assert spend_bundle2 is not None tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle2) await full_node_1.respond_transaction(tx2, peer) sb1 = full_node_1.full_node.mempool_manager.get_spendbundle( spend_bundle1.name()) sb2 = full_node_1.full_node.mempool_manager.get_spendbundle( spend_bundle2.name()) assert sb1 is None assert sb2 == spend_bundle2
def tx(info): j = json.dumps(info) m: Dict = eval(j) inputs: List = m.get("inputs") outputs: List = m.get("outputs") primaries = [] for o in outputs: output: Dict = o address: str = output.get("address") value: float = output.get("value") primaries.append({ "puzzlehash": decode_puzzle_hash(address), "amount": value }) spends: List[CoinSolution] = [] pks: List[str] = [] first_spend = True for i in inputs: input: Dict = i pk: str = input.get("pk") pks.append(pk) txid: Dict = eval(input.get("txId")) parentCoinInfo = txid.get("parentCoinInfo") puzzleHash = txid.get("puzzleHash") amount = txid.get("amount") pa = bytes32(bytes.fromhex(parentCoinInfo[2:])) pu = bytes32(bytes.fromhex(puzzleHash[2:])) a = uint64(amount) coin: Coin = Coin(pa, pu, a) child_sk: PrivateKey = PrivateKey.from_bytes(bytes.fromhex(pk)) child_public_key = child_sk.get_g1() puzzle = puzzle_for_pk(child_public_key) if first_spend: solution: Program = Wallet().make_solution(primaries=primaries) first_spend = False else: solution = Wallet().make_solution() spends.append(CoinSolution(coin, puzzle, solution)) spend_bundle: SpendBundle = SpendBundle(spends, G2Element()) # return json.dumps(spend_bundle.to_json_dict()) return sign_tx(pks, spend_bundle)
async def setup_keys(self): self.all_root_sks: List[PrivateKey] = [ sk for sk, _ in await self.get_all_private_keys() ] self._private_keys = [ master_sk_to_farmer_sk(sk) for sk in self.all_root_sks ] + [master_sk_to_pool_sk(sk) for sk in self.all_root_sks] if len(self.get_public_keys()) == 0: error_str = "No keys exist. Please run 'chia keys generate' or open the UI." raise RuntimeError(error_str) # This is the farmer configuration self.farmer_target_encoded = self.config["xch_target_address"] self.farmer_target = decode_puzzle_hash(self.farmer_target_encoded) self.pool_public_keys = [ G1Element.from_bytes(bytes.fromhex(pk)) for pk in self.config["pool_public_keys"] ] # This is the self pooling configuration, which is only used for original self-pooled plots self.pool_target_encoded = self.pool_config["xch_target_address"] self.pool_target = decode_puzzle_hash(self.pool_target_encoded) self.pool_sks_map: Dict = {} for key in self.get_private_keys(): self.pool_sks_map[bytes(key.get_g1())] = key assert len(self.farmer_target) == 32 assert len(self.pool_target) == 32 if len(self.pool_sks_map) == 0: error_str = "No keys exist. Please run 'chia keys generate' or open the UI." raise RuntimeError(error_str) # The variables below are for use with an actual pool # From p2_singleton_puzzle_hash to pool state dict self.pool_state: Dict[bytes32, Dict] = {} # From public key bytes to PrivateKey self.authentication_keys: Dict[bytes, PrivateKey] = {} # Last time we updated pool_state based on the config file self.last_config_access_time: uint64 = uint64(0) self.harvester_cache: Dict[str, Dict[str, HarvesterCacheEntry]] = {}
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 cat_spend( self, wallet_id: str, amount: uint64, inner_address: str, fee: uint64 = uint64(0), memos: Optional[List[str]] = None, ) -> TransactionRecord: send_dict = { "wallet_id": wallet_id, "amount": amount, "inner_address": inner_address, "fee": fee, "memos": memos if memos else [], } res = await self.fetch("cat_spend", send_dict) return TransactionRecord.from_json_dict_convenience(res["transaction"])
def do_inspect_coin_spend_cmd(ctx, spends, print_results=True, **kwargs): if kwargs and all([kwargs['puzzle_reveal'], kwargs['solution']]): if (not kwargs['coin']) and all( [kwargs['parent_id'], kwargs['puzzle_hash'], kwargs['amount']]): coin_spend_objs = [ CoinSpend( Coin( bytes.fromhex(kwargs['parent_id']), bytes.fromhex(kwargs['puzzle_hash']), uint64(kwargs['amount']), ), parse_program(sanitize_bytes(kwargs['puzzle_reveal'])), parse_program(sanitize_bytes(kwargs['solution'])), ) ] elif kwargs['coin']: coin_spend_objs = [ CoinSpend( do_inspect_coin_cmd(ctx, [kwargs['coin']], print_results=False)[0], parse_program(sanitize_bytes(kwargs['puzzle_reveal'])), parse_program(sanitize_bytes(kwargs['solution'])), ) ] else: print("Invalid arguments specified.") elif not kwargs or not any([kwargs[key] for key in kwargs.keys()]): coin_spend_objs = [] try: if type(spends[0]) == str: coin_spend_objs = streamable_load(CoinSpend, spends) else: coin_spend_objs = spends except: print("One or more of the specified objects was not a coin spend") else: print("Invalid arguments specified.") return if print_results: inspect_callback(coin_spend_objs, ctx, id_calc=(lambda e: e.coin.name()), type='CoinSpend') else: return coin_spend_objs
async def send_transaction( self, wallet_id: str, amount: uint64, address: str, fee: uint64 = uint64(0)) -> TransactionRecord: res = await self.fetch( "send_transaction", { "wallet_id": wallet_id, "amount": amount, "address": address, "fee": fee }, ) return TransactionRecord.from_json_dict(res["transaction"])
async def add_block_record(self, header_block_record: HeaderBlockRecord, block_record: BlockRecord): """ Adds a block record to the database. This block record is assumed to be connected to the chain, but it may or may not be in the LCA path. """ cached = self.block_cache.get(header_block_record.header_hash) if cached is not None: # Since write to db can fail, we remove from cache here to avoid potential inconsistency # Adding to cache only from reading self.block_cache.put(header_block_record.header_hash, None) if header_block_record.header.foliage_transaction_block is not None: timestamp = header_block_record.header.foliage_transaction_block.timestamp else: timestamp = uint64(0) cursor = await self.db.execute( "INSERT OR REPLACE INTO header_blocks VALUES(?, ?, ?, ?)", ( header_block_record.header_hash.hex(), header_block_record.height, timestamp, bytes(header_block_record), ), ) await cursor.close() cursor_2 = await self.db.execute( "INSERT OR REPLACE INTO block_records VALUES(?, ?, ?, ?, ?, ?, ?,?)", ( header_block_record.header.header_hash.hex(), header_block_record.header.prev_header_hash.hex(), header_block_record.header.height, header_block_record.header.weight.to_bytes( 128 // 8, "big", signed=False).hex(), header_block_record.header.total_iters.to_bytes( 128 // 8, "big", signed=False).hex(), bytes(block_record), None if block_record.sub_epoch_summary_included is None else bytes(block_record.sub_epoch_summary_included), False, ), ) await cursor_2.close()
async def get_unconfirmed_balance(self, record_list=None) -> uint64: confirmed = await self.get_confirmed_balance(record_list) unconfirmed_tx: List[ TransactionRecord] = await self.wallet_state_manager.tx_store.get_unconfirmed_for_wallet( self.wallet_info.id) addition_amount = 0 removal_amount = 0 for record in unconfirmed_tx: if record.type == TransactionType.INCOMING_TX: addition_amount += record.amount else: removal_amount += record.amount result = confirmed - removal_amount + addition_amount self.log.info(f"Unconfirmed balance for did wallet is {result}") return uint64(result)
def calculate_cost_of_program(program: SerializedProgram, npc_result: NPCResult, cost_per_byte: int) -> uint64: """ This function calculates the total cost of either a block or a spendbundle """ total_cost = 0 total_cost += npc_result.clvm_cost npc_list = npc_result.npc_list # Add cost of conditions npc: NPC for npc in npc_list: total_cost += conditions_cost(npc.condition_dict) # Add raw size of the program total_cost += len(bytes(program)) * cost_per_byte return uint64(total_cost)
async def create_spend_bundle_relative_chia( self, chia_amount: int, exclude: List[Coin]) -> SpendBundle: list_of_solutions = [] utxos = None # If we're losing value then get coins with at least that much value # If we're gaining value then our amount doesn't matter if chia_amount < 0: utxos = await self.select_coins(abs(chia_amount), exclude) else: utxos = await self.select_coins(0, exclude) assert len(utxos) > 0 # Calculate output amount given sum of utxos spend_value = sum([coin.amount for coin in utxos]) chia_amount = spend_value + chia_amount # Create coin solutions for each utxo output_created = None for coin in utxos: puzzle = await self.puzzle_for_puzzle_hash(coin.puzzle_hash) if output_created is None: newpuzhash = await self.get_new_puzzlehash() primaries: List[AmountWithPuzzlehash] = [{ "puzzlehash": newpuzhash, "amount": uint64(chia_amount), "memos": [] }] solution = self.make_solution(primaries=primaries) output_created = coin list_of_solutions.append(CoinSpend(coin, puzzle, solution)) await self.hack_populate_secret_keys_for_coin_spends(list_of_solutions) spend_bundle = await sign_coin_spends( list_of_solutions, self.secret_key_store.secret_key_for_public_key, self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA, self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM, ) return spend_bundle
async def get_pending_change_balance(self) -> uint64: unconfirmed_tx = await self.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(self.id()) addition_amount = 0 for record in unconfirmed_tx: our_spend = False for coin in record.removals: if await self.wallet_state_manager.does_coin_belong_to_wallet(coin, self.id()): our_spend = True break if our_spend is not True: continue for coin in record.additions: if await self.wallet_state_manager.does_coin_belong_to_wallet(coin, self.id()): addition_amount += coin.amount return uint64(addition_amount)
async def join_pool(self, target_state: PoolState, fee: uint64) -> Tuple[uint64, TransactionRecord]: if target_state.state != FARMING_TO_POOL: raise ValueError(f"join_pool must be called with target_state={FARMING_TO_POOL} (FARMING_TO_POOL)") if self.target_state is not None: raise ValueError(f"Cannot join a pool while waiting for target state: {self.target_state}") if await self.have_unconfirmed_transaction(): raise ValueError( "Cannot join pool due to unconfirmed transaction. If this is stuck, delete the unconfirmed transaction." ) current_state: PoolWalletInfo = await self.get_current_state() total_fee = fee if current_state.current == target_state: self.target_state = None msg = f"Asked to change to current state. Target = {target_state}" self.log.info(msg) raise ValueError(msg) elif current_state.current.state in [SELF_POOLING, LEAVING_POOL]: total_fee = fee elif current_state.current.state == FARMING_TO_POOL: total_fee = uint64(fee * 2) if self.target_state is not None: raise ValueError( f"Cannot change to state {target_state} when already having target state: {self.target_state}" ) PoolWallet._verify_initial_target_state(target_state) if current_state.current.state == LEAVING_POOL: history: List[Tuple[uint32, CoinSpend]] = await self.get_spend_history() last_height: uint32 = history[-1][0] if self.wallet_state_manager.get_peak().height <= last_height + current_state.current.relative_lock_height: raise ValueError( f"Cannot join a pool until height {last_height + current_state.current.relative_lock_height}" ) self.target_state = target_state self.next_transaction_fee = fee tx_record: TransactionRecord = await self.generate_travel_transaction(fee) await self.wallet_state_manager.add_pending_transaction(tx_record) return total_fee, tx_record
def validate_spend_bundle( self, spend_bundle: SpendBundle, now: CoinTimestamp, max_cost: int, ) -> int: # this should use blockchain consensus code coin_announcements: Set[bytes32] = set() puzzle_announcements: Set[bytes32] = set() conditions_dicts = [] for coin_solution in spend_bundle.coin_solutions: err, conditions_dict, cost = conditions_dict_for_solution( coin_solution.puzzle_reveal, coin_solution.solution, max_cost) if conditions_dict is None: raise BadSpendBundleError(f"clvm validation failure {err}") conditions_dicts.append(conditions_dict) coin_announcements.update( coin_announcement_names_for_conditions_dict( conditions_dict, coin_solution.coin.name())) puzzle_announcements.update( puzzle_announcement_names_for_conditions_dict( conditions_dict, coin_solution.coin.puzzle_hash)) for coin_solution, conditions_dict in zip(spend_bundle.coin_solutions, conditions_dicts): prev_transaction_block_height = now.height timestamp = now.seconds coin_record = self._db[coin_solution.coin.name()] err = mempool_check_conditions_dict( coin_record, coin_announcements, puzzle_announcements, conditions_dict, uint32(prev_transaction_block_height), uint64(timestamp), ) if err is not None: raise BadSpendBundleError(f"condition validation failure {err}") return 0
async def create_offer_for_ids( self, offer_dict: Dict[uint32, int], fee=uint64(0), validate_only: bool = False ) -> Tuple[Optional[Offer], TradeRecord]: send_dict: Dict[str, int] = {} for key in offer_dict: send_dict[str(key)] = offer_dict[key] res = await self.fetch("create_offer_for_ids", { "offer": send_dict, "validate_only": validate_only, "fee": fee }) offer: Optional[Offer] = None if validate_only else Offer.from_bech32( res["offer"]) offer_str: str = "" if offer is None else bytes(offer).hex() return offer, TradeRecord.from_json_dict_convenience( res["trade_record"], offer_str)
def farm_coin( self, puzzle_hash: bytes32, birthday: CoinTimestamp, amount: int = 1024, prefix=bytes32.fromhex("ccd5bb71183532bff220ba46c268991a00000000000000000000000000000000"), # noqa ) -> Coin: parent = bytes32( [ a | b for a, b in zip( prefix, birthday.height.to_bytes(32, "big"), ) ], ) # parent = birthday.height.to_bytes(32, "big") coin = Coin(parent, puzzle_hash, uint64(amount)) self._add_coin_entry(coin, birthday) return coin
async def create( wallet_state_manager: Any, wallet: Wallet, launcher_coin_id: bytes32, block_spends: List[CoinSpend], block_height: uint32, in_transaction: bool, name: str = None, ): """ This creates a new PoolWallet with only one spend: the launcher spend. The DB MUST be committed after calling this method. """ self = PoolWallet() self.wallet_state_manager = wallet_state_manager self.wallet_info = await wallet_state_manager.user_store.create_wallet( "Pool wallet", WalletType.POOLING_WALLET.value, "", in_transaction=in_transaction ) self.wallet_id = self.wallet_info.id self.standard_wallet = wallet self.target_state = None self.next_transaction_fee = uint64(0) self.log = logging.getLogger(name if name else __name__) launcher_spend: Optional[CoinSpend] = None for spend in block_spends: if spend.coin.name() == launcher_coin_id: launcher_spend = spend assert launcher_spend is not None await self.wallet_state_manager.pool_store.add_spend(self.wallet_id, launcher_spend, block_height) await self.update_pool_config(True) p2_puzzle_hash: bytes32 = (await self.get_current_state()).p2_singleton_puzzle_hash await self.wallet_state_manager.interested_store.add_interested_puzzle_hash( p2_puzzle_hash, self.wallet_id, True ) await self.wallet_state_manager.add_new_wallet(self, self.wallet_info.id, create_puzzle_hashes=False) self.wallet_state_manager.set_new_peak_callback(self.wallet_id, self.new_peak) return self
async def new_signage_point(self, new_signage_point: farmer_protocol.NewSignagePoint): pool_difficulties: List[PoolDifficulty] = [] for p2_singleton_puzzle_hash, pool_dict in self.farmer.pool_state.items(): if pool_dict["pool_config"].pool_url == "": # Self pooling continue if pool_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 signage point, pool: " f"{pool_dict['pool_config'].pool_url} " ) continue pool_difficulties.append( PoolDifficulty( pool_dict["current_difficulty"], self.farmer.constants.POOL_SUB_SLOT_ITERS, p2_singleton_puzzle_hash, ) ) message = harvester_protocol.NewSignagePointHarvester( new_signage_point.challenge_hash, new_signage_point.difficulty, new_signage_point.sub_slot_iters, new_signage_point.signage_point_index, new_signage_point.challenge_chain_sp, pool_difficulties, ) msg = make_msg(ProtocolMessageTypes.new_signage_point_harvester, message) await self.farmer.server.send_to_all([msg], NodeType.HARVESTER) if new_signage_point.challenge_chain_sp not in self.farmer.sps: self.farmer.sps[new_signage_point.challenge_chain_sp] = [] if new_signage_point in self.farmer.sps[new_signage_point.challenge_chain_sp]: self.farmer.log.debug(f"Duplicate signage point {new_signage_point.signage_point_index}") return self.farmer.sps[new_signage_point.challenge_chain_sp].append(new_signage_point) self.farmer.cache_add_time[new_signage_point.challenge_chain_sp] = uint64(int(time.time())) self.farmer.state_changed("new_signage_point", {"sp_hash": new_signage_point.challenge_chain_sp})
async def create_wallet_backup(self, file_path: Path): all_wallets = await self.get_all_wallet_info_entries() for wallet in all_wallets: if wallet.id == 1: all_wallets.remove(wallet) break backup_pk = master_sk_to_backup_sk(self.private_key) now = uint64(int(time.time())) wallet_backup = WalletInfoBackup(all_wallets) backup: Dict[str, Any] = {} data = wallet_backup.to_json_dict() data["version"] = __version__ data["fingerprint"] = self.private_key.get_g1().get_fingerprint() data["timestamp"] = now data["start_height"] = await self.get_start_height() key_base_64 = base64.b64encode(bytes(backup_pk)) f = Fernet(key_base_64) data_bytes = json.dumps(data).encode() encrypted = f.encrypt(data_bytes) meta_data: Dict[str, Any] = { "timestamp": now, "pubkey": bytes(backup_pk.get_g1()).hex() } meta_data_bytes = json.dumps(meta_data).encode() signature = bytes( AugSchemeMPL.sign(backup_pk, std_hash(encrypted) + std_hash(meta_data_bytes))).hex() backup["data"] = encrypted.decode() backup["meta_data"] = meta_data backup["signature"] = signature backup_file_text = json.dumps(backup) file_path.write_text(backup_file_text)