def create_spend_for_auditor(parent_of_a, auditee): puzstring = f"(r (c (q 0x{auditee.name()}) (q ())))" puzzle = Program(binutils.assemble(puzstring)) coin = Coin(parent_of_a.name(), puzzle.get_tree_hash(), uint64(0)) solution = Program(binutils.assemble("()")) coinsol = CoinSolution(coin, Program.to([puzzle, solution])) return coinsol
async def get_unspent_coins_for_wallet(self, wallet_id: int) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet for a wallet. """ async with self.wallet_cache_lock: if wallet_id in self.coin_wallet_record_cache: wallet_coins: Dict[bytes32, WalletCoinRecord] = self.coin_wallet_record_cache[wallet_id] return set(wallet_coins.values()) coin_set = set() cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=0 and wallet_id=?", (wallet_id,), ) rows = await cursor.fetchall() await cursor.close() cache_dict = {} for row in rows: coin = Coin(bytes32(bytes.fromhex(row[8])), bytes32(bytes.fromhex(row[7])), row[9]) coin_record = WalletCoinRecord( coin, row[1], row[2], row[3], row[4], row[5], row[6], WalletType(row[10]), row[11] ) coin_set.add(coin_record) cache_dict[coin.name()] = coin_record self.coin_wallet_record_cache[wallet_id] = cache_dict return coin_set
async def get_spendable_for_index( self, index: uint32, wallet_id: int ) -> Set[WalletCoinRecord]: """ Returns set of unspent coin records that are not coinbases, or if they are coinbases, must have been confirmed at or before index. """ coins = set() cursor_coinbase_coins = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=? and confirmed_index<=? and wallet_id=? and coinbase=?", (0, int(index), wallet_id, 1), ) coinbase_rows = await cursor_coinbase_coins.fetchall() await cursor_coinbase_coins.close() cursor_regular_coins = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=? and wallet_id=? and coinbase=?", (0, wallet_id, 0,), ) regular_rows = await cursor_regular_coins.fetchall() await cursor_regular_coins.close() for row in coinbase_rows + regular_rows: coin = Coin( bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7] ) coins.add( WalletCoinRecord( coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9] ) ) return coins
def create_spend_for_ephemeral(parent_of_e, auditor_coin, spend_amount): puzstring = f"(r (r (c (q 0x{auditor_coin.name()}) (c (q {spend_amount}) (q ())))))" puzzle = Program(binutils.assemble(puzstring)) coin = Coin(parent_of_e.name(), puzzle.get_tree_hash(), uint64(0)) solution = Program(binutils.assemble("()")) coinsol = CoinSolution(coin, Program.to([puzzle, solution])) return coinsol
async def get_unspent_coins_at_height( self, height: Optional[uint32] = None ) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet. If a height is specified, We can also return coins that were unspent at this height (but maybe spent later). Finally, the coins must be confirmed at the height or less. """ coins = set() if height is not None: cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE (spent=? OR spent_index>?) AND confirmed_index<=?", (0, height, height), ) else: cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=?", (0,) ) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin( bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7] ) coins.add( WalletCoinRecord( coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9] ) ) return coins
async def get_coin_records_by_spent( self, spent: bool, spend_before_height: Optional[uint32] = None ) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet. """ coins = set() if spend_before_height: cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=? OR spent_index>=?", (int(spent), spend_before_height), ) else: cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=?", (int(spent),) ) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin( bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7] ) coins.add( WalletCoinRecord( coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9] ) ) return coins
def coin_solution_for_lock_coin( prev_coin: Coin, subtotal: int, coin: Coin, ) -> CoinSolution: puzzle_reveal = LOCK_INNER_PUZZLE.curry(prev_coin.as_list(), subtotal) coin = Coin(coin.name(), puzzle_reveal.get_tree_hash(), uint64(0)) coin_solution = CoinSolution(coin, Program.to([puzzle_reveal, 0])) return coin_solution
async def get_coin_records_by_puzzle_hash(self, puzzle_hash: bytes32) -> List[WalletCoinRecord]: """Returns a list of all coin records with the given puzzle hash""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),)) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return list(coins)
def generate_farmed_coin( block_index: int, puzzle_hash: bytes32, amount: int, ) -> Coin: """ Generate a (fake) coin which can be used as a starting point for a chain of coin tests. """ return Coin(int_as_bytes32(block_index), puzzle_hash, uint64(amount))
async def get_all_coins(self) -> Set[WalletCoinRecord]: """ Returns set of all CoinRecords.""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record") rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return coins
async def get_coin_record(self, coin_name: bytes32) -> Optional[WalletCoinRecord]: """ Returns CoinRecord with specified coin id. """ if coin_name in self.coin_record_cache: return self.coin_record_cache[coin_name] cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return None
async def get_unspent_coin_records(self) -> List[CoinRecord]: coins = set() cursor = await self.coin_record_db.execute( "SELECT * from coin_record WHERE spent=0") rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]) coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])) return list(coins)
async def get_coin_record_by_coin_id(self, coin_id: bytes32) -> Optional[WalletCoinRecord]: """Returns a coin records with the given name, if it exists""" cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_id.hex(),)) row = await cursor.fetchone() await cursor.close() if row is None: return None coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return coin_record
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]: if coin_name.hex() in self.coin_record_cache: return self.coin_record_cache[coin_name.hex()] cursor = await self.coin_record_db.execute( "SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(), )) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]) return CoinRecord(coin, row[1], row[2], row[3], row[4], row[8]) return None
async def get_coin_records_by_puzzle_hash( self, puzzle_hash: bytes32) -> List[CoinRecord]: coins = set() cursor = await self.coin_record_db.execute( "SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(), )) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]) coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])) return list(coins)
def created_outputs_for_conditions_dict( conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]], input_coin_name: bytes32, ) -> List[Coin]: output_coins = [] for cvp in conditions_dict.get(ConditionOpcode.CREATE_COIN, []): # TODO: check condition very carefully # (ensure there are the correct number and type of parameters) # maybe write a type-checking framework for conditions # and don't just fail with asserts puzzle_hash, amount_bin = cvp.var1, cvp.var2 amount = int_from_bytes(amount_bin) coin = Coin(input_coin_name, puzzle_hash, amount) output_coins.append(coin) return output_coins
async def get_unspent_coins_for_wallet( self, wallet_id: int) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet for a wallet. """ coins = set() cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=0 and wallet_id=?", (wallet_id, ), ) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]) coins.add( WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return coins
async def get_unspent_coin_records(self, header: Header = None ) -> List[CoinRecord]: coins = set() if header is not None and header.header_hash in self.head_diffs: diff_store = self.head_diffs[header.header_hash] for _, record in diff_store.diffs.items(): if not record.spent: coins.add(record) cursor = await self.coin_record_db.execute( "SELECT * from coin_record WHERE spent=0") rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]) coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4])) return list(coins)
async def get_coin_record(self, coin_name: bytes32, header: Header = None) -> Optional[CoinRecord]: if header is not None and header.header_hash in self.head_diffs: diff_store = self.head_diffs[header.header_hash] if coin_name.hex() in diff_store.diffs: return diff_store.diffs[coin_name.hex()] if coin_name.hex() in self.lca_coin_records: return self.lca_coin_records[coin_name.hex()] cursor = await self.coin_record_db.execute( "SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(), )) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]) return CoinRecord(coin, row[1], row[2], row[3], row[4]) return None
async def generate_new_coloured_coin(self, amount: uint64) -> Optional[SpendBundle]: coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() origin_id = origin.name() # self.add_parent(origin_id, origin_id) cc_core = cc_wallet_puzzles.cc_make_core(origin_id) parent_info = {} parent_info[origin_id] = ( origin.parent_coin_info, origin.puzzle_hash, origin.amount, ) cc_info: CCInfo = CCInfo(cc_core, [], origin_id.hex()) await self.save_info(cc_info) cc_inner = await self.get_new_inner_hash() cc_puzzle = cc_wallet_puzzles.cc_make_puzzle(cc_inner, cc_core) cc_puzzle_hash = cc_puzzle.get_tree_hash() tx_record: Optional[ TransactionRecord ] = await self.standard_wallet.generate_signed_transaction( amount, cc_puzzle_hash, uint64(0), origin_id, coins ) self.log.warning(f"cc_puzzle_hash is {cc_puzzle_hash}") eve_coin = Coin(origin_id, cc_puzzle_hash, amount) if tx_record is None or tx_record.spend_bundle is None: return None eve_spend = cc_generate_eve_spend(eve_coin, cc_puzzle) full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend]) return full_spend
async def generate_zero_val_coin(self, send=True, exclude: List[Coin] = None) -> SpendBundle: if self.cc_info.my_genesis_checker is None: raise ValueError("My genesis checker is None") if exclude is None: exclude = [] coins = await self.standard_wallet.select_coins(0, exclude) assert coins != set() origin = coins.copy().pop() origin_id = origin.name() cc_inner = await self.get_new_inner_hash() cc_puzzle_hash: Program = cc_puzzle_hash_for_inner_puzzle_hash( CC_MOD, self.cc_info.my_genesis_checker, cc_inner ) tx: TransactionRecord = await self.standard_wallet.generate_signed_transaction( uint64(0), cc_puzzle_hash, uint64(0), origin_id, coins ) assert tx.spend_bundle is not None full_spend: SpendBundle = tx.spend_bundle self.log.info(f"Generate zero val coin: cc_puzzle_hash is {cc_puzzle_hash}") # generate eve coin so we can add future lineage_proofs even if we don't eve spend eve_coin = Coin(origin_id, cc_puzzle_hash, uint64(0)) await self.add_lineage( eve_coin.name(), Program.to( ( 1, [eve_coin.parent_coin_info, cc_inner, eve_coin.amount], ) ), ) await self.add_lineage(eve_coin.parent_coin_info, Program.to((0, [origin.as_list(), 1]))) if send: regular_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=cc_puzzle_hash, amount=uint64(0), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=full_spend, additions=full_spend.additions(), removals=full_spend.removals(), wallet_id=uint32(1), sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=token_bytes(), ) cc_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=cc_puzzle_hash, amount=uint64(0), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=full_spend, additions=full_spend.additions(), removals=full_spend.removals(), wallet_id=self.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=full_spend.name(), ) await self.wallet_state_manager.add_transaction(regular_record) await self.wallet_state_manager.add_pending_transaction(cc_record) return full_spend
def _create_block( self, test_constants: Dict, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint128, timestamp: uint64, difficulty: uint64, min_iters: uint64, seed: bytes, genesis: bool = False, reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: BLSSignature = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ selected_prover = None selected_plot_sk = None selected_pool_sk = None selected_proof_index = 0 plots = list(self.plot_config["plots"].items()) selected_quality: Optional[bytes] = None best_quality = 0 if self.use_any_pos: for i in range(len(plots) * 3): # Allow passing in seed, to create reorgs and different chains random.seed(seed + i.to_bytes(4, "big")) seeded_pn = random.randint(0, len(plots) - 1) pool_sk = PrivateKey.from_bytes( bytes.fromhex(plots[seeded_pn][1]["pool_sk"]) ) plot_sk = PrivateKey.from_bytes( bytes.fromhex(plots[seeded_pn][1]["sk"]) ) prover = DiskProver(plots[seeded_pn][0]) qualities = prover.get_qualities_for_challenge(challenge_hash) if len(qualities) > 0: if self.use_any_pos: selected_quality = qualities[0] selected_prover = prover selected_pool_sk = pool_sk selected_plot_sk = plot_sk break else: for i in range(len(plots)): pool_sk = PrivateKey.from_bytes(bytes.fromhex(plots[i][1]["pool_sk"])) plot_sk = PrivateKey.from_bytes(bytes.fromhex(plots[i][1]["sk"])) prover = DiskProver(plots[i][0]) qualities = prover.get_qualities_for_challenge(challenge_hash) j = 0 for quality in qualities: qual_int = int.from_bytes(quality, "big", signed=False) if qual_int > best_quality: best_quality = qual_int selected_quality = quality selected_prover = prover selected_pool_sk = pool_sk selected_plot_sk = plot_sk selected_proof_index = j j += 1 assert selected_prover assert selected_pool_sk assert selected_plot_sk pool_pk = selected_pool_sk.get_public_key() plot_pk = selected_plot_sk.get_public_key() if selected_quality is None: raise RuntimeError("No proofs for this challenge") proof_xs: bytes = selected_prover.get_full_proof( challenge_hash, selected_proof_index ) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, pool_pk, plot_pk, selected_prover.get_size(), proof_xs ) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, min_iters ) # print("Doing iters", number_iters) int_size = (test_constants["DISCRIMINANT_SIZE_BITS"] + 16) >> 4 result = prove( challenge_hash, test_constants["DISCRIMINANT_SIZE_BITS"], number_iters ) output = ClassgroupElement( int512(int.from_bytes(result[0:int_size], "big", signed=True,)), int512( int.from_bytes(result[int_size : 2 * int_size], "big", signed=True,) ), ) proof_bytes = result[2 * int_size : 4 * int_size] proof_of_time = ProofOfTime( challenge_hash, number_iters, output, self.n_wesolowski, proof_bytes, ) if not reward_puzzlehash: reward_puzzlehash = self.fee_target # Use the extension data to create different blocks based on header hash extension_data: bytes32 = bytes32([random.randint(0, 255) for _ in range(32)]) cost = uint64(0) coinbase_reward = block_rewards.calculate_block_reward(height) fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees) coinbase_coin, coinbase_signature = create_coinbase_coin_and_signature( height, reward_puzzlehash, coinbase_reward, selected_pool_sk ) parent_coin_name = std_hash(std_hash(height)) fees_coin = Coin(parent_coin_name, reward_puzzlehash, uint64(fee_reward)) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] encoded = None if transactions: error, npc_list, _ = get_name_puzzle_conditions(transactions) additions: List[Coin] = additions_for_npc(npc_list) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for npc in npc_list: tx_removals.append(npc.coin_name) byte_array_tx.append(bytearray(npc.coin_name)) bip158: PyBIP158 = PyBIP158(byte_array_tx) encoded = bytes(bip158.GetEncoded()) removal_merkle_set = MerkleSet() addition_merkle_set = MerkleSet() # Create removal Merkle set for coin_name in tx_removals: removal_merkle_set.add_already_hashed(coin_name) # Create addition Merkle set puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {} for coin in tx_additions: if coin.puzzle_hash in puzzlehash_coin_map: puzzlehash_coin_map[coin.puzzle_hash].append(coin) else: puzzlehash_coin_map[coin.puzzle_hash] = [coin] # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash for puzzle, coins in puzzlehash_coin_map.items(): addition_merkle_set.add_already_hashed(puzzle) addition_merkle_set.add_already_hashed(hash_coin_list(coins)) additions_root = addition_merkle_set.get_root() removal_root = removal_merkle_set.get_root() generator_hash = ( transactions.get_tree_hash() if transactions is not None else bytes32([0] * 32) ) filter_hash = std_hash(encoded) if encoded is not None else bytes32([0] * 32) header_data: HeaderData = HeaderData( height, prev_header_hash, timestamp, filter_hash, proof_of_space.get_hash(), uint128(prev_weight + difficulty), uint64(prev_iters + number_iters), additions_root, removal_root, coinbase_coin, coinbase_signature, fees_coin, aggsig, cost, extension_data, generator_hash, ) header_hash_sig: PrependSignature = selected_plot_sk.sign_prepend( header_data.get_hash() ) header: Header = Header(header_data, header_hash_sig) full_block: FullBlock = FullBlock( proof_of_space, proof_of_time, header, transactions, encoded ) return full_block
def create_coinbase_coin(block_index: int, puzzle_hash: bytes32, reward: uint64): block_index_as_hash = bytes32(block_index.to_bytes(32, "big")) return Coin(block_index_as_hash, puzzle_hash, reward)
async def rl_generate_signed_aggregation_transaction( self, rl_info, consolidating_coin, rl_parent, rl_coin ): if ( rl_info.limit is None or rl_info.interval is None or rl_info.user_pubkey is None or rl_info.admin_pubkey is None ): raise ValueError("One or more of the elements of rl_info is None") if self.rl_coin_record is None: raise ValueError("Rl coin record is None") list_of_coinsolutions = [] self.rl_coin_record = await self._get_rl_coin_record() pubkey, secretkey = await self.get_keys(self.rl_coin_record.coin.puzzle_hash) # Spend wallet coin puzzle = rl_puzzle_for_pk( rl_info.user_pubkey, rl_info.limit, rl_info.interval, rl_info.rl_origin_id, rl_info.admin_pubkey, ) solution = rl_make_solution_mode_2( rl_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, rl_coin.parent_coin_info, rl_coin.amount, rl_parent.amount, rl_parent.parent_coin_info, ) signature = AugSchemeMPL.sign(secretkey, solution.get_tree_hash()) rl_spend = CoinSolution( self.rl_coin_record.coin, Program.to([puzzle, solution]) ) list_of_coinsolutions.append(rl_spend) # Spend consolidating coin puzzle = rl_make_aggregation_puzzle(self.rl_coin_record.coin.puzzle_hash) solution = rl_make_aggregation_solution( consolidating_coin.name(), self.rl_coin_record.coin.parent_coin_info, self.rl_coin_record.coin.amount, ) agg_spend = CoinSolution(consolidating_coin, Program.to([puzzle, solution])) list_of_coinsolutions.append(agg_spend) # Spend lock puzstring = f"(r (c (q 0x{consolidating_coin.name().hex()}) (q ())))" puzzle = Program(binutils.assemble(puzstring)) solution = Program(binutils.assemble("()")) ephemeral = CoinSolution( Coin(self.rl_coin_record.coin.name(), puzzle.get_tree_hash(), uint64(0)), Program.to([puzzle, solution]), ) list_of_coinsolutions.append(ephemeral) aggsig = AugSchemeMPL.aggregate([signature]) return SpendBundle(list_of_coinsolutions, aggsig)
async def set_user_info( self, interval: uint64, limit: uint64, origin_parent_id: str, origin_puzzle_hash: str, origin_amount: uint64, admin_pubkey: str, ) -> None: admin_pubkey_bytes = hexstr_to_bytes(admin_pubkey) assert self.rl_info.user_pubkey is not None origin = Coin( hexstr_to_bytes(origin_parent_id), hexstr_to_bytes(origin_puzzle_hash), origin_amount, ) rl_puzzle = rl_puzzle_for_pk( pubkey=self.rl_info.user_pubkey, rate_amount=limit, interval_time=interval, origin_id=origin.name(), clawback_pk=admin_pubkey_bytes, ) rl_puzzle_hash = rl_puzzle.get_tree_hash() new_rl_info = RLInfo( "user", admin_pubkey_bytes, self.rl_info.user_pubkey, limit, interval, origin, origin.name(), rl_puzzle_hash, True, ) rl_puzzle_hash = rl_puzzle.get_tree_hash() if await self.wallet_state_manager.puzzle_store.puzzle_hash_exists( rl_puzzle_hash ): raise ValueError( "Cannot create multiple Rate Limited wallets under the same keys. This will change in a future release." ) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( G1Element.from_bytes(self.rl_info.user_pubkey) ) assert index is not None record = DerivationRecord( index, rl_puzzle_hash, self.rl_info.user_pubkey, WalletType.RATE_LIMITED, self.id(), ) aggregation_puzzlehash = self.rl_get_aggregation_puzzlehash( new_rl_info.rl_puzzle_hash ) record2 = DerivationRecord( index + 1, aggregation_puzzlehash, self.rl_info.user_pubkey, WalletType.RATE_LIMITED, self.id(), ) await self.wallet_state_manager.puzzle_store.add_derivation_paths( [record, record2] ) self.wallet_state_manager.set_coin_with_puzzlehash_created_callback( aggregation_puzzlehash, self.aggregate_this_coin ) data_str = json.dumps(new_rl_info.to_json_dict()) new_wallet_info = WalletInfo( self.id(), self.wallet_info.name, self.type(), data_str ) await self.wallet_state_manager.user_store.update_wallet(new_wallet_info) await self.wallet_state_manager.add_new_wallet(self, self.id()) self.wallet_info = new_wallet_info self.rl_info = new_rl_info
async def test_store(self): db_filename = Path("blockchain_wallet_store_test.db") if db_filename.exists(): db_filename.unlink() db_connection = await aiosqlite.connect(db_filename) store = await WalletStore.create(db_connection) try: coin_1 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_2 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_3 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_4 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) record_replaced = WalletCoinRecord(coin_1, uint32(8), uint32(0), False, True, WalletType.STANDARD_WALLET, 0) record_1 = WalletCoinRecord(coin_1, uint32(4), uint32(0), False, True, WalletType.STANDARD_WALLET, 0) record_2 = WalletCoinRecord(coin_2, uint32(5), uint32(0), False, True, WalletType.STANDARD_WALLET, 0) record_3 = WalletCoinRecord( coin_3, uint32(5), uint32(10), True, False, WalletType.STANDARD_WALLET, 0, ) record_4 = WalletCoinRecord( coin_4, uint32(5), uint32(15), True, False, WalletType.STANDARD_WALLET, 0, ) # Test add (replace) and get assert await store.get_coin_record(coin_1.name()) is None await store.add_coin_record(record_replaced) await store.add_coin_record(record_1) await store.add_coin_record(record_2) await store.add_coin_record(record_3) await store.add_coin_record(record_4) assert await store.get_coin_record(coin_1.name()) == record_1 # Test persistance await db_connection.close() db_connection = await aiosqlite.connect(db_filename) store = await WalletStore.create(db_connection) assert await store.get_coin_record(coin_1.name()) == record_1 # Test set spent await store.set_spent(coin_1.name(), uint32(12)) assert (await store.get_coin_record(coin_1.name())).spent assert (await store.get_coin_record(coin_1.name() )).spent_block_index == 12 # No coins at height 3 assert len(await store.get_unspent_coins_at_height(3)) == 0 assert len(await store.get_unspent_coins_at_height(4)) == 1 assert len(await store.get_unspent_coins_at_height(5)) == 4 assert len(await store.get_unspent_coins_at_height(11)) == 3 assert len(await store.get_unspent_coins_at_height(12)) == 2 assert len(await store.get_unspent_coins_at_height(15)) == 1 assert len(await store.get_unspent_coins_at_height(16)) == 1 assert len(await store.get_unspent_coins_at_height()) == 1 assert len(await store.get_unspent_coins_for_wallet(0)) == 1 assert len(await store.get_unspent_coins_for_wallet(1)) == 0 coin_5 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) record_5 = WalletCoinRecord( coin_5, uint32(5), uint32(15), False, False, WalletType.STANDARD_WALLET, 1, ) await store.add_coin_record(record_5) assert len(await store.get_unspent_coins_for_wallet(1)) == 1 assert len(await store.get_spendable_for_index(100, 1)) == 1 assert len(await store.get_spendable_for_index(100, 0)) == 1 assert len(await store.get_spendable_for_index(0, 0)) == 0 coin_6 = Coin(token_bytes(32), coin_4.puzzle_hash, uint64(12312)) await store.add_coin_record(record_5) record_6 = WalletCoinRecord( coin_6, uint32(5), uint32(15), True, False, WalletType.STANDARD_WALLET, 2, ) await store.add_coin_record(record_6) assert (len(await store.get_coin_records_by_puzzle_hash( record_6.coin.puzzle_hash)) == 2) # 4 and 6 assert (len(await store.get_coin_records_by_puzzle_hash(token_bytes(32) )) == 0) assert await store.get_coin_record_by_coin_id(coin_6.name() ) == record_6 assert await store.get_coin_record_by_coin_id(token_bytes(32) ) is None # BLOCKS assert len(await store.get_lca_path()) == 0 # NOT lca block br_1 = BlockRecord( token_bytes(32), token_bytes(32), uint32(0), uint128(100), None, None, None, None, uint64(0), ) assert await store.get_block_record(br_1.header_hash) is None await store.add_block_record(br_1, False) assert len(await store.get_lca_path()) == 0 assert await store.get_block_record(br_1.header_hash) == br_1 # LCA genesis await store.add_block_record(br_1, True) assert await store.get_block_record(br_1.header_hash) == br_1 assert len(await store.get_lca_path()) == 1 assert (await store.get_lca_path())[br_1.header_hash] == br_1 br_2 = BlockRecord( token_bytes(32), token_bytes(32), uint32(1), uint128(100), None, None, None, None, uint64(0), ) await store.add_block_record(br_2, False) assert len(await store.get_lca_path()) == 1 await store.add_block_to_path(br_2.header_hash) assert len(await store.get_lca_path()) == 2 assert (await store.get_lca_path())[br_2.header_hash] == br_2 br_3 = BlockRecord( token_bytes(32), token_bytes(32), uint32(2), uint128(100), None, None, None, None, uint64(0), ) await store.add_block_record(br_3, True) assert len(await store.get_lca_path()) == 3 await store.remove_blocks_from_path(1) assert len(await store.get_lca_path()) == 2 await store.rollback_lca_to_block(0) assert len(await store.get_unspent_coins_at_height()) == 0 coin_7 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_8 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_9 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_10 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) record_7 = WalletCoinRecord(coin_7, uint32(0), uint32(1), True, False, WalletType.STANDARD_WALLET, 1) record_8 = WalletCoinRecord(coin_8, uint32(1), uint32(2), True, False, WalletType.STANDARD_WALLET, 1) record_9 = WalletCoinRecord(coin_9, uint32(2), uint32(3), True, False, WalletType.STANDARD_WALLET, 1) record_10 = WalletCoinRecord( coin_10, uint32(3), uint32(4), True, False, WalletType.STANDARD_WALLET, 1, ) await store.add_coin_record(record_7) await store.add_coin_record(record_8) await store.add_coin_record(record_9) await store.add_coin_record(record_10) assert len(await store.get_unspent_coins_at_height(0)) == 1 assert len(await store.get_unspent_coins_at_height(1)) == 1 assert len(await store.get_unspent_coins_at_height(2)) == 1 assert len(await store.get_unspent_coins_at_height(3)) == 1 assert len(await store.get_unspent_coins_at_height(4)) == 0 await store.add_block_record(br_2, True) await store.add_block_record(br_3, True) await store.rollback_lca_to_block(1) assert len(await store.get_unspent_coins_at_height(0)) == 1 assert len(await store.get_unspent_coins_at_height(1)) == 1 assert len(await store.get_unspent_coins_at_height(2)) == 1 assert len(await store.get_unspent_coins_at_height(3)) == 1 assert len(await store.get_unspent_coins_at_height(4)) == 1 except AssertionError: await db_connection.close() raise await db_connection.close()
def create_fees_coin(block_index: uint32, puzzle_hash: bytes32, reward: uint64): block_index_as_hash = std_hash(std_hash(block_index.to_bytes(4, "big"))) return Coin(block_index_as_hash, puzzle_hash, reward)
async def test_short_sync_with_transactions_wallet(self, wallet_node): full_node_1, wallet_node, server_1, server_2 = wallet_node wallet_a = wallet_node.wallet_state_manager.main_wallet wallet_a_dummy = WalletTool() wallet_b = WalletTool() coinbase_puzzlehash = await wallet_a.get_new_puzzlehash() coinbase_puzzlehash_rest = wallet_b.get_new_puzzlehash() puzzle_hashes = [ await wallet_a.get_new_puzzlehash() for _ in range(10) ] puzzle_hashes.append(wallet_b.get_new_puzzlehash()) blocks = bt.get_consecutive_blocks(test_constants, 3, [], 10, b"", coinbase_puzzlehash) for block in blocks: [ _ async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)) ] await asyncio.sleep(2) await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await asyncio.sleep(2) assert (wallet_node.wallet_state_manager.block_records[ wallet_node.wallet_state_manager.lca].height == 1) server_2.global_connections.close_all_connections() dic_h = {} prev_coin = blocks[1].header.data.coinbase for i in range(11): pk, sk = await wallet_a.wallet_state_manager.get_keys( prev_coin.puzzle_hash) transaction_unsigned = wallet_a_dummy.generate_unsigned_transaction( 1000, puzzle_hashes[i], prev_coin, {}, 0, secretkey=sk) spend_bundle = await wallet_a.sign_transaction(transaction_unsigned ) block_spendbundle = SpendBundle.aggregate([spend_bundle]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature prev_coin = Coin(prev_coin.name(), puzzle_hashes[i], uint64(1000)) dic_h[i + 4] = (program, aggsig) blocks = bt.get_consecutive_blocks(test_constants, 13, blocks, 10, b"", coinbase_puzzlehash_rest, dic_h) # Move chain to height 16, with consecutive transactions in blocks 4 to 14 for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Do a short sync from 0 to 14 await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) start = time.time() broke = False while time.time() - start < 60: if (wallet_node.wallet_state_manager.block_records[ wallet_node.wallet_state_manager.lca].height >= 14 # Tip at 16, LCA at 14 ): broke = True break await asyncio.sleep(0.1) if not broke: raise Exception( f"Took too long to process blocks, stopped at: {time.time() - start}" ) server_2.global_connections.close_all_connections() # 2 block rewards and 3 fees assert await wallet_a.get_confirmed_balance() == ( blocks[1].header.data.coinbase.amount * 2) + (blocks[1].header.data.fees_coin.amount * 3) # All of our coins are spent and puzzle hashes present for puzzle_hash in puzzle_hashes[:-1]: records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( puzzle_hash) assert len(records) == 1 assert records[0].spent and not records[0].coinbase # Then do the same but in a reorg chain dic_h = {} prev_coin = blocks[1].header.data.coinbase for i in range(11): pk, sk = await wallet_a.wallet_state_manager.get_keys( prev_coin.puzzle_hash) transaction_unsigned = wallet_a_dummy.generate_unsigned_transaction( 1000, puzzle_hashes[i], prev_coin, {}, 0, secretkey=sk) spend_bundle = await wallet_a.sign_transaction(transaction_unsigned ) block_spendbundle = SpendBundle.aggregate([spend_bundle]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature prev_coin = Coin(prev_coin.name(), puzzle_hashes[i], uint64(1000)) dic_h[i + 4] = (program, aggsig) blocks = bt.get_consecutive_blocks( test_constants, 31, blocks[:4], 10, b"this is a reorg", coinbase_puzzlehash_rest, dic_h, ) # Move chain to height 34, with consecutive transactions in blocks 4 to 14 for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Do a sync from 0 to 22 await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) broke = False while time.time() - start < 60: if (wallet_node.wallet_state_manager.block_records[ wallet_node.wallet_state_manager.lca].height >= 28 # Tip at 34, LCA at least 28 ): broke = True break await asyncio.sleep(0.1) if not broke: raise Exception( f"Took too long to process blocks, stopped at: {time.time() - start}" ) server_2.global_connections.close_all_connections() # 2 block rewards and 3 fees assert await wallet_a.get_confirmed_balance() == ( blocks[1].header.data.coinbase.amount * 2) + (blocks[1].header.data.fees_coin.amount * 3) # All of our coins are spent and puzzle hashes present for puzzle_hash in puzzle_hashes[:-1]: records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( puzzle_hash) assert len(records) == 1 assert records[0].spent and not records[0].coinbase # Test spending the rewards earned in reorg new_coinbase_puzzlehash = await wallet_a.get_new_puzzlehash() another_puzzlehash = await wallet_a.get_new_puzzlehash() dic_h = {} pk, sk = await wallet_a.wallet_state_manager.get_keys( new_coinbase_puzzlehash) coinbase_coin = create_coinbase_coin(25, new_coinbase_puzzlehash, uint64(14000000000000)) transaction_unsigned = wallet_a_dummy.generate_unsigned_transaction( 7000000000000, another_puzzlehash, coinbase_coin, {}, 0, secretkey=sk) spend_bundle = await wallet_a.sign_transaction(transaction_unsigned) block_spendbundle = SpendBundle.aggregate([spend_bundle]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature dic_h[26] = (program, aggsig) # Farm a block (25) to ourselves blocks = bt.get_consecutive_blocks( test_constants, 1, blocks[:25], 10, b"this is yet another reorg", new_coinbase_puzzlehash, ) # Brings height up to 40, with block 31 having half our reward spent to us blocks = bt.get_consecutive_blocks( test_constants, 15, blocks, 10, b"this is yet another reorg more blocks", coinbase_puzzlehash_rest, dic_h, ) for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) broke = False while time.time() - start < 60: if (wallet_node.wallet_state_manager.block_records[ wallet_node.wallet_state_manager.lca].height >= 38 # Tip at 40, LCA at 38 ): broke = True break await asyncio.sleep(0.1) if not broke: raise Exception( f"Took too long to process blocks, stopped at: {time.time() - start}" ) # 2 block rewards and 4 fees, plus 7000000000000 coins assert (await wallet_a.get_confirmed_balance() == (blocks[1].header.data.coinbase.amount * 2) + (blocks[1].header.data.fees_coin.amount * 4) + 7000000000000) records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( new_coinbase_puzzlehash) # Fee and coinbase assert len(records) == 2 assert records[0].spent != records[1].spent assert records[0].coinbase == records[1].coinbase records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( another_puzzlehash) assert len(records) == 1 assert not records[0].spent assert not records[0].coinbase
async def generate_zero_val_coin( self, send=True, exclude: List[Coin] = None) -> Optional[SpendBundle]: if self.cc_info.my_core is None: return None if exclude is None: exclude = [] coins = await self.standard_wallet.select_coins(0, exclude) if coins == set() or coins is None: return None origin = coins.copy().pop() origin_id = origin.name() parent_info = {} parent_info[origin_id] = ( origin.parent_coin_info, origin.puzzle_hash, origin.amount, ) cc_inner = await self.get_new_inner_hash() cc_puzzle = cc_wallet_puzzles.cc_make_puzzle(cc_inner, self.cc_info.my_core) cc_puzzle_hash = cc_puzzle.get_tree_hash() tx = await self.standard_wallet.generate_signed_transaction( uint64(0), cc_puzzle_hash, uint64(0), origin_id, coins) self.log.info( f"Generate zero val coin: cc_puzzle_hash is {cc_puzzle_hash}") eve_coin = Coin(origin_id, cc_puzzle_hash, uint64(0)) if tx is None or tx.spend_bundle is None: return None eve_spend = cc_generate_eve_spend(eve_coin, cc_puzzle) full_spend = SpendBundle.aggregate([tx.spend_bundle, eve_spend]) future_parent = CCParent(eve_coin.parent_coin_info, cc_inner, eve_coin.amount) eve_parent = CCParent(origin.parent_coin_info, origin.puzzle_hash, origin.amount) await self.add_parent(eve_coin.name(), future_parent) await self.add_parent(eve_coin.parent_coin_info, eve_parent) if send: regular_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=cc_puzzle_hash, amount=uint64(0), fee_amount=uint64(0), incoming=False, confirmed=False, sent=uint32(10), spend_bundle=full_spend, additions=full_spend.additions(), removals=full_spend.removals(), wallet_id=uint32(1), sent_to=[], trade_id=None, ) cc_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=cc_puzzle_hash, amount=uint64(0), fee_amount=uint64(0), incoming=True, confirmed=False, sent=uint32(0), spend_bundle=full_spend, additions=full_spend.additions(), removals=full_spend.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, ) await self.wallet_state_manager.add_transaction(regular_record) await self.wallet_state_manager.add_pending_transaction(cc_record) return full_spend
async def rl_generate_signed_aggregation_transaction( self, rl_info: RLInfo, consolidating_coin: Coin, rl_parent: Coin, rl_coin: Coin ): if ( rl_info.limit is None or rl_info.interval is None or rl_info.limit is None or rl_info.interval is None or rl_info.user_pubkey is None or rl_info.admin_pubkey is None ): raise Exception("One ore more of the elements of rl_info is None") list_of_coinsolutions = [] pubkey, secretkey = await self.get_keys(self.rl_coin_record.coin.puzzle_hash) # Spend wallet coin puzzle = rl_puzzle_for_pk( rl_info.user_pubkey, rl_info.limit, rl_info.interval, rl_info.rl_origin, rl_info.admin_pubkey, ) solution = rl_make_solution_mode_2( rl_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, rl_coin.parent_coin_info, rl_coin.amount, rl_parent.amount, rl_parent.parent_coin_info, ) signature = secretkey.sign(solution.get_hash()) list_of_coinsolutions.append( CoinSolution(self.rl_coin_record.coin, Program.to([puzzle, solution])) ) # Spend consolidating coin puzzle = rl_make_aggregation_puzzle(self.rl_coin_record.coin.puzzle_hash) solution = rl_make_aggregation_solution( consolidating_coin.name(), self.rl_coin_record.coin.parent_coin_info, self.rl_coin_record.coin.amount, ) list_of_coinsolutions.append( CoinSolution(consolidating_coin, Program.to([puzzle, solution])) ) # Spend lock puzstring = ( "(r (c (q 0x" + hexlify(consolidating_coin.name()).decode("ascii") + ") (q ())))" ) puzzle = Program(binutils.assemble(puzstring)) solution = Program(binutils.assemble("()")) list_of_coinsolutions.append( CoinSolution( Coin(self.rl_coin_record.coin, puzzle.get_hash(), uint64(0)), Program.to([puzzle, solution]), ) ) aggsig = AugSchemeMPL.aggregate([signature]) return SpendBundle(list_of_coinsolutions, aggsig)