def puzzle_for_pk(self, pubkey: bytes) -> Program: innerpuz = did_wallet_puzzles.create_innerpuz( pubkey, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed ) if self.did_info.origin_coin is not None: return did_wallet_puzzles.create_fullpuz(innerpuz, self.did_info.origin_coin.puzzle_hash) else: return did_wallet_puzzles.create_fullpuz(innerpuz, 0x00)
def puzzle_for_pk(self, pubkey: bytes) -> Program: innerpuz = did_wallet_puzzles.create_innerpuz( pubkey, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed ) if self.did_info.origin_coin is not None: return did_wallet_puzzles.create_fullpuz(innerpuz, self.did_info.origin_coin.name()) else: # TODO: address hint error and remove ignore # error: Argument 2 to "create_fullpuz" has incompatible type "int"; expected "bytes32" [arg-type] return did_wallet_puzzles.create_fullpuz(innerpuz, 0x00) # type: ignore[arg-type]
async def create_spend(self, puzhash: bytes32): assert self.did_info.current_inner is not None coins = await self.select_coins(1) assert coins is not None coin = coins.pop() # innerpuz solution is (mode amount new_puz identity my_puz) innersol: Program = Program.to([0, coin.amount, puzhash, coin.name(), coin.puzzle_hash]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.my_did, ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to( [ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ] ) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = bytes(puzhash) + bytes(coin.name()) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey) private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) return spend_bundle
async def generate_new_decentralised_id( self, amount: uint64) -> Optional[SpendBundle]: """ This must be called under the wallet state manager lock """ coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() did_inner: Program = await self.get_new_innerpuz() did_inner_hash = did_inner.get_tree_hash() did_puz = did_wallet_puzzles.create_fullpuz(did_inner, origin.puzzle_hash) did_puzzle_hash = did_puz.get_tree_hash() tx_record: Optional[ TransactionRecord] = await self.standard_wallet.generate_signed_transaction( amount, did_puzzle_hash, uint64(0), origin.name(), coins) eve_coin = Coin(origin.name(), did_puzzle_hash, amount) future_parent = CCParent( eve_coin.parent_coin_info, did_inner_hash, eve_coin.amount, ) eve_parent = CCParent( origin.parent_coin_info, origin.puzzle_hash, origin.amount, ) await self.add_parent(eve_coin.parent_coin_info, eve_parent, False) await self.add_parent(eve_coin.name(), future_parent, False) if tx_record is None or tx_record.spend_bundle is None: return None # Only want to save this information if the transaction is valid did_info: DIDInfo = DIDInfo( origin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, did_inner, None, None, None, ) await self.save_info(did_info, False) eve_spend = await self.generate_eve_spend(eve_coin, did_puz, did_inner) full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend]) return full_spend
async def recovery_spend( self, coin: Coin, puzhash: bytes, parent_innerpuzhash_amounts_for_recovery_ids: List[Tuple[bytes, bytes, int]], pubkey: G1Element, spend_bundle: SpendBundle, ) -> SpendBundle: assert self.did_info.origin_coin is not None # innerpuz solution is (mode amount new_puz identity my_puz parent_innerpuzhash_amounts_for_recovery_ids) innersol = Program.to([ 2, coin.amount, puzhash, coin.name(), coin.puzzle_hash, parent_innerpuzhash_amounts_for_recovery_ids, bytes(pubkey), self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, ]) # full solution is (parent_info my_amount solution) innerpuz = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.puzzle_hash, ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount ], [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) if index is None: raise ValueError("Unknown pubkey.") private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) message = bytes(puzhash) sigs = [AugSchemeMPL.sign(private, message)] for _ in spend_bundle.coin_solutions: sigs.append(AugSchemeMPL.sign(private, message)) aggsig = AugSchemeMPL.aggregate(sigs) # assert AugSchemeMPL.verify(pubkey, message, aggsig) if spend_bundle is None: spend_bundle = SpendBundle(list_of_solutions, aggsig) else: spend_bundle = spend_bundle.aggregate( [spend_bundle, SpendBundle(list_of_solutions, aggsig)]) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) return spend_bundle
async def create_attestment(self, recovering_coin_name: bytes32, newpuz: bytes32, pubkey: G1Element, filename=None) -> SpendBundle: assert self.did_info.current_inner is not None assert self.did_info.origin_coin is not None coins = await self.select_coins(1) assert coins is not None and coins != set() coin = coins.pop() message = did_wallet_puzzles.create_recovery_message_puzzle( recovering_coin_name, newpuz, pubkey) innermessage = message.get_tree_hash() # innerpuz solution is (mode amount new_puz identity my_puz) innersol = Program.to([ 1, coin.amount, innermessage, recovering_coin_name, coin.puzzle_hash ]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.puzzle_hash, ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount ], [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] message_spend = did_wallet_puzzles.create_spend_for_message( coin.name(), recovering_coin_name, newpuz, pubkey) message_spend_bundle = SpendBundle([message_spend], AugSchemeMPL.aggregate([])) # sign for AGG_SIG_ME to_sign = Program.to([coin.puzzle_hash, coin.amount, innermessage]).get_tree_hash() message = to_sign + coin.name( ) + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) spend_bundle = SpendBundle(list_of_solutions, signature) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=coin.puzzle_hash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) if filename is not None: f = open(filename, "w") f.write(self.get_my_DID()) f.write(":") f.write(bytes(message_spend_bundle).hex()) f.write(":") parent = coin.parent_coin_info.hex() innerpuzhash = self.did_info.current_inner.get_tree_hash().hex() amount = coin.amount f.write(parent) f.write(":") f.write(innerpuzhash) f.write(":") f.write(str(amount)) f.close() return message_spend_bundle
async def create_new_did_wallet( wallet_state_manager: Any, wallet: Wallet, amount: int, backups_ids: List = [], num_of_backup_ids_needed: uint64 = None, name: str = None, ): """ This must be called under the wallet state manager lock """ self = DIDWallet() self.base_puzzle_program = None self.base_inner_puzzle_hash = None self.standard_wallet = wallet if name: self.log = logging.getLogger(name) else: self.log = logging.getLogger(__name__) if amount & 1 == 0: raise ValueError("DID amount must be odd number") self.wallet_state_manager = wallet_state_manager if num_of_backup_ids_needed is None: num_of_backup_ids_needed = uint64(len(backups_ids)) if num_of_backup_ids_needed > len(backups_ids): raise ValueError("Cannot require more IDs than are known.") self.did_info = DIDInfo(None, backups_ids, num_of_backup_ids_needed, [], None, None, None, None) info_as_string = json.dumps(self.did_info.to_json_dict()) self.wallet_info = await wallet_state_manager.user_store.create_wallet( "DID Wallet", WalletType.DISTRIBUTED_ID.value, info_as_string) if self.wallet_info is None: raise ValueError("Internal Error") self.wallet_id = self.wallet_info.id bal = await self.standard_wallet.get_confirmed_balance() if amount > bal: raise ValueError("Not enough balance") spend_bundle = await self.generate_new_decentralised_id(uint64(amount)) if spend_bundle is None: raise ValueError("failed to generate ID for wallet") await self.wallet_state_manager.add_new_wallet(self, self.wallet_info.id) assert self.did_info.origin_coin is not None did_puzzle_hash = did_wallet_puzzles.create_fullpuz( self.did_info.current_inner, self.did_info.origin_coin.puzzle_hash).get_tree_hash() did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=did_puzzle_hash, amount=uint64(amount), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=None, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=token_bytes(), ) regular_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=did_puzzle_hash, amount=uint64(amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_state_manager.main_wallet.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(regular_record) await self.standard_wallet.push_transaction(did_record) return self
async def load_backup(self, filename: str): try: f = open(filename, "r") details = f.readline().split(":") f.close() origin = Coin(bytes.fromhex(details[0]), bytes.fromhex(details[1]), uint64(int(details[2]))) backup_ids = [] for d in details[3].split(","): backup_ids.append(bytes.fromhex(d)) num_of_backup_ids_needed = uint64(int(details[5])) if num_of_backup_ids_needed > len(backup_ids): raise Exception innerpuz = Program.from_bytes(bytes.fromhex(details[4])) did_info = DIDInfo( origin, backup_ids, num_of_backup_ids_needed, self.did_info.parent_info, innerpuz, None, None, None, ) await self.save_info(did_info, False) await self.wallet_state_manager.update_wallet_puzzle_hashes( self.wallet_info.id) full_puz = did_wallet_puzzles.create_fullpuz( innerpuz, origin.puzzle_hash) full_puzzle_hash = full_puz.get_tree_hash() ( sub_height, header_hash, ) = await self.wallet_state_manager.search_blockrecords_for_puzzlehash( full_puzzle_hash) assert sub_height is not None assert header_hash is not None full_nodes = self.wallet_state_manager.server.connection_by_type[ NodeType.FULL_NODE] additions: Union[RespondAdditions, RejectAdditionsRequest, None] = None for id, node in full_nodes.items(): request = wallet_protocol.RequestAdditions( sub_height, header_hash, None) additions = await node.request_additions(request) if additions is not None: break if isinstance(additions, RejectAdditionsRequest): continue assert additions is not None assert isinstance(additions, RespondAdditions) # All additions in this block here: new_puzhash = (await self.get_new_puzzle()).get_tree_hash() new_pubkey = bytes( (await self.wallet_state_manager.get_unused_derivation_record( self.wallet_info.id)).pubkey) all_parents: bytes32 = set() for puzzle_list_coin in additions.coins: puzzle_hash, coins = puzzle_list_coin for coin in coins: all_parents.add(coin.parent_coin_info) for puzzle_list_coin in additions.coins: puzzle_hash, coins = puzzle_list_coin if puzzle_hash == full_puzzle_hash: # our coin for coin in coins: future_parent = CCParent( coin.parent_coin_info, innerpuz.get_tree_hash(), coin.amount, ) await self.add_parent(coin.name(), future_parent, False) if coin.name() in all_parents: continue did_info = DIDInfo( origin, backup_ids, num_of_backup_ids_needed, self.did_info.parent_info, innerpuz, coin, new_puzhash, new_pubkey, ) await self.save_info(did_info, False) return None except Exception as e: raise e
async def generate_new_decentralised_id( self, amount: uint64) -> Optional[SpendBundle]: """ This must be called under the wallet state manager lock """ coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() genesis_launcher_puz = did_wallet_puzzles.SINGLETON_LAUNCHER launcher_coin = Coin(origin.name(), genesis_launcher_puz.get_tree_hash(), amount) did_inner: Program = await self.get_new_innerpuz() did_inner_hash = did_inner.get_tree_hash() did_full_puz = did_wallet_puzzles.create_fullpuz( did_inner, launcher_coin.name()) did_puzzle_hash = did_full_puz.get_tree_hash() announcement_set: Set[Announcement] = set() announcement_message = Program.to( [did_puzzle_hash, amount, bytes(0x80)]).get_tree_hash() announcement_set.add( Announcement(launcher_coin.name(), announcement_message).name()) tx_record: Optional[ TransactionRecord] = await self.standard_wallet.generate_signed_transaction( amount, genesis_launcher_puz.get_tree_hash(), uint64(0), origin.name(), coins, None, False, announcement_set) genesis_launcher_solution = Program.to( [did_puzzle_hash, amount, bytes(0x80)]) launcher_cs = CoinSolution(launcher_coin, genesis_launcher_puz, genesis_launcher_solution) launcher_sb = SpendBundle([launcher_cs], AugSchemeMPL.aggregate([])) eve_coin = Coin(launcher_coin.name(), did_puzzle_hash, amount) future_parent = LineageProof( eve_coin.parent_coin_info, did_inner_hash, eve_coin.amount, ) eve_parent = LineageProof( launcher_coin.parent_coin_info, launcher_coin.puzzle_hash, launcher_coin.amount, ) await self.add_parent(eve_coin.parent_coin_info, eve_parent, False) await self.add_parent(eve_coin.name(), future_parent, False) if tx_record is None or tx_record.spend_bundle is None: return None # Only want to save this information if the transaction is valid did_info: DIDInfo = DIDInfo( launcher_coin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, did_inner, None, None, None, ) await self.save_info(did_info, False) eve_spend = await self.generate_eve_spend(eve_coin, did_full_puz, did_inner) full_spend = SpendBundle.aggregate( [tx_record.spend_bundle, eve_spend, launcher_sb]) return full_spend
async def test_make_fake_coin(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] await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) wallet = wallet_node.wallet_state_manager.main_wallet wallet2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() 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)) ph2 = await wallet2.get_new_puzzlehash() for i in range(1, num_blocks): await full_node_1.farm_new_transaction_block( FarmNewBlockProtocol(ph2)) await time_out_assert(15, did_wallet.get_confirmed_balance, 101) await time_out_assert(15, did_wallet.get_unconfirmed_balance, 101) await time_out_assert(15, did_wallet.get_spendable_balance, 101) coins = await did_wallet.select_coins(1) coin = coins.pop() # copy info for later parent_info = await did_wallet.get_parent_for_coin(coin) id_puzhash = coin.puzzle_hash await did_wallet.create_spend(ph) 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, 0) await time_out_assert(15, did_wallet.get_unconfirmed_balance, 0) tx_record = await wallet.generate_signed_transaction(101, id_puzhash) await wallet.push_transaction(tx_record) for i in range(1, num_blocks): await full_node_1.farm_new_transaction_block( FarmNewBlockProtocol(ph)) await time_out_assert(15, wallet.get_confirmed_balance, 21999999999899) await time_out_assert(15, wallet.get_unconfirmed_balance, 21999999999899) coins = await did_wallet.select_coins(1) assert len(coins) >= 1 coin = coins.pop() # Write spend by hand # innerpuz solution is (mode amount new_puz identity my_puz) innersol = Program.to( [0, coin.amount, ph, coin.name(), coin.puzzle_hash]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz = did_wallet.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, did_wallet.did_info.my_did, ) fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = coin.puzzle_hash + coin.name( ) + did_wallet.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await did_wallet.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) private = master_sk_to_wallet_sk( did_wallet.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=ph, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=did_wallet.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await did_wallet.standard_wallet.push_transaction(did_record) await time_out_assert(15, wallet.get_confirmed_balance, 21999999999899) await time_out_assert(15, wallet.get_unconfirmed_balance, 21999999999899) ph2 = Program.to(binutils.assemble("()")).get_tree_hash() for i in range(1, num_blocks + 3): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph2)) # It ends in 900 so it's not gone through # Assert coin ID is failing await time_out_assert(15, wallet.get_confirmed_balance, 23999999999899) await time_out_assert(15, wallet.get_unconfirmed_balance, 23999999999899)
async def test_make_double_output(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 wallet2 = wallet_node_2.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)) ph2 = await wallet2.get_new_puzzlehash() for i in range(1, num_blocks): await full_node_1.farm_new_transaction_block( FarmNewBlockProtocol(ph2)) await time_out_assert(15, did_wallet.get_confirmed_balance, 101) await time_out_assert(15, did_wallet.get_unconfirmed_balance, 101) await time_out_assert(15, did_wallet.get_spendable_balance, 101) # Lock up with non DID innerpuz so that we can create two outputs # Innerpuz will output the innersol, so we just pass in ((51 0xMyPuz 49) (51 0xMyPuz 51)) innerpuz = Program.to(binutils.assemble("1")) innerpuzhash = innerpuz.get_tree_hash() puz = did_wallet_puzzles.create_fullpuz( innerpuzhash, did_wallet.did_info.my_did, ) # Add the hacked puzzle to the puzzle store so that it is recognised as "our" puzzle old_devrec = await did_wallet.wallet_state_manager.get_unused_derivation_record( did_wallet.wallet_info.id) devrec = DerivationRecord( old_devrec.index, puz.get_tree_hash(), old_devrec.pubkey, old_devrec.wallet_type, old_devrec.wallet_id, ) await did_wallet.wallet_state_manager.puzzle_store.add_derivation_paths( [devrec]) await did_wallet.create_spend(puz.get_tree_hash()) for i in range(1, num_blocks): await full_node_1.farm_new_transaction_block( FarmNewBlockProtocol(ph2)) await time_out_assert(15, did_wallet.get_confirmed_balance, 101) await time_out_assert(15, did_wallet.get_unconfirmed_balance, 101) await time_out_assert(15, did_wallet.get_spendable_balance, 101) # Create spend by hand so that we can use the weird innersol coins = await did_wallet.select_coins(1) coin = coins.pop() # innerpuz is our desired output innersol = Program.to([[51, coin.puzzle_hash, 45], [51, coin.puzzle_hash, 56]]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) parent_info = await did_wallet.get_parent_for_coin(coin) fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) try: cost, result = puz.run_with_cost(fullsol) except Exception as e: assert e.args == ("path into atom", ) else: assert False
async def create_message_spend(self, messages: List[Tuple[int, bytes]], new_innerpuzhash: Optional[bytes32] = None): assert self.did_info.current_inner is not None assert self.did_info.origin_coin is not None coins = await self.select_coins(1) assert coins is not None coin = coins.pop() innerpuz: Program = self.did_info.current_inner if new_innerpuzhash is None: new_innerpuzhash = innerpuz.get_tree_hash() # innerpuz solution is (mode amount messages new_puz) innersol: Program = Program.to([1, coin.amount, messages, new_innerpuzhash]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.name(), ) parent_info = self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to( [ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ] ) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME # new_inner_puzhash amount message message = ( Program.to([new_innerpuzhash, coin.amount, messages]).get_tree_hash() + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA ) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey) private = master_sk_to_wallet_sk_unhardened(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=new_innerpuzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=bytes32(token_bytes()), memos=list(compute_memos(spend_bundle).items()), ) await self.standard_wallet.push_transaction(did_record) return spend_bundle
def puzzle_for_pk(self, pubkey: bytes) -> Program: innerpuz = did_wallet_puzzles.create_innerpuz( pubkey, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed ) did = self.did_info.my_did return did_wallet_puzzles.create_fullpuz(innerpuz, did)
async def recovery_spend( self, coin: Coin, puzhash: bytes32, parent_innerpuzhash_amounts_for_recovery_ids: List[Tuple[bytes, bytes, int]], pubkey: G1Element, spend_bundle: SpendBundle, ) -> SpendBundle: assert self.did_info.origin_coin is not None # innersol is mode new_amount message new_inner_puzhash parent_innerpuzhash_amounts_for_recovery_ids pubkey recovery_list_reveal) # noqa innersol: Program = Program.to([ 2, coin.amount, puzhash, puzhash, parent_innerpuzhash_amounts_for_recovery_ids, bytes(pubkey), self.did_info.backup_ids, ]) # full solution is (parent_info my_amount solution) assert self.did_info.current_inner is not None innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.name(), ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) if index is None: raise ValueError("Unknown pubkey.") private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) message = bytes(puzhash) sigs = [AugSchemeMPL.sign(private, message)] for _ in spend_bundle.coin_spends: sigs.append(AugSchemeMPL.sign(private, message)) aggsig = AugSchemeMPL.aggregate(sigs) # assert AugSchemeMPL.verify(pubkey, message, aggsig) if spend_bundle is None: spend_bundle = SpendBundle(list_of_solutions, aggsig) else: spend_bundle = spend_bundle.aggregate( [spend_bundle, SpendBundle(list_of_solutions, aggsig)]) # TODO: address hint error and remove ignore # error: Argument "name" to "TransactionRecord" has incompatible type "bytes"; expected "bytes32" # [arg-type] did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), # type: ignore[arg-type] ) await self.standard_wallet.push_transaction(did_record) new_did_info = DIDInfo( self.did_info.origin_coin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, self.did_info.current_inner, self.did_info.temp_coin, self.did_info.temp_puzhash, self.did_info.temp_pubkey, True, ) await self.save_info(new_did_info, True) return spend_bundle
async def create_exit_spend(self, puzhash: bytes32): assert self.did_info.current_inner is not None assert self.did_info.origin_coin is not None coins = await self.select_coins(1) assert coins is not None coin = coins.pop() amount = coin.amount - 1 # innerpuz solution is (mode amount new_puzhash) innersol: Program = Program.to([0, amount, puzhash]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.name(), ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = ( Program.to([amount, puzhash]).get_tree_hash() + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) # TODO: address hint error and remove ignore # error: Argument "name" to "TransactionRecord" has incompatible type "bytes"; expected "bytes32" # [arg-type] did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), # type: ignore[arg-type] ) await self.standard_wallet.push_transaction(did_record) return spend_bundle
async def create_new_did_wallet( wallet_state_manager: Any, wallet: Wallet, amount: uint64, backups_ids: List = [], num_of_backup_ids_needed: uint64 = None, name: str = None, ): """ This must be called under the wallet state manager lock """ self = DIDWallet() self.base_puzzle_program = None self.base_inner_puzzle_hash = None self.standard_wallet = wallet self.log = logging.getLogger(name if name else __name__) std_wallet_id = self.standard_wallet.wallet_id bal = await wallet_state_manager.get_confirmed_balance_for_wallet_already_locked( std_wallet_id) if amount > bal: raise ValueError("Not enough balance") if amount & 1 == 0: raise ValueError("DID amount must be odd number") self.wallet_state_manager = wallet_state_manager if num_of_backup_ids_needed is None: num_of_backup_ids_needed = uint64(len(backups_ids)) if num_of_backup_ids_needed > len(backups_ids): raise ValueError("Cannot require more IDs than are known.") self.did_info = DIDInfo(None, backups_ids, num_of_backup_ids_needed, [], None, None, None, None, False) info_as_string = json.dumps(self.did_info.to_json_dict()) self.wallet_info = await wallet_state_manager.user_store.create_wallet( "DID Wallet", WalletType.DISTRIBUTED_ID.value, info_as_string) if self.wallet_info is None: raise ValueError("Internal Error") self.wallet_id = self.wallet_info.id std_wallet_id = self.standard_wallet.wallet_id bal = await wallet_state_manager.get_confirmed_balance_for_wallet_already_locked( std_wallet_id) if amount > bal: raise ValueError("Not enough balance") try: spend_bundle = await self.generate_new_decentralised_id( uint64(amount)) except Exception: await wallet_state_manager.user_store.delete_wallet( self.id(), False) raise if spend_bundle is None: await wallet_state_manager.user_store.delete_wallet( self.id(), False) raise ValueError("Failed to create spend.") await self.wallet_state_manager.add_new_wallet(self, self.wallet_info.id) assert self.did_info.origin_coin is not None assert self.did_info.current_inner is not None did_puzzle_hash = did_wallet_puzzles.create_fullpuz( self.did_info.current_inner, self.did_info.origin_coin.name()).get_tree_hash() # TODO: address hint error and remove ignore # error: Argument "name" to "TransactionRecord" has incompatible type "bytes"; expected "bytes32" # [arg-type] did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=did_puzzle_hash, amount=uint64(amount), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=None, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=token_bytes(), # type: ignore[arg-type] ) # TODO: address hint error and remove ignore # error: Argument "name" to "TransactionRecord" has incompatible type "bytes"; expected "bytes32" # [arg-type] regular_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=did_puzzle_hash, amount=uint64(amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_state_manager.main_wallet.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), # type: ignore[arg-type] ) await self.standard_wallet.push_transaction(regular_record) await self.standard_wallet.push_transaction(did_record) return self