def generate_unsigned_clawback_transaction( self, clawback_coin: Coin, clawback_puzzle_hash: bytes32, fee ): if ( self.rl_info.limit is None or self.rl_info.interval is None or self.rl_info.user_pubkey is None or self.rl_info.admin_pubkey is None ): raise ValueError("One ore more of the elements of rl_info is None") spends = [] coin = clawback_coin if self.rl_info.rl_origin is None: raise ValueError("Origin not initialized") puzzle = rl_puzzle_for_pk( self.rl_info.user_pubkey, self.rl_info.limit, self.rl_info.interval, self.rl_info.rl_origin.name(), self.rl_info.admin_pubkey, ) solution = make_clawback_solution( clawback_puzzle_hash, clawback_coin.amount, fee ) spends.append((puzzle, CoinSolution(coin, solution))) return spends
async def rl_generate_unsigned_transaction(self, to_puzzlehash, amount, fee): spends = [] coin = self.rl_coin_record.coin puzzle_hash = coin.puzzle_hash pubkey = self.rl_info.user_pubkey rl_parent: Optional[Coin] = await self._get_rl_parent() if rl_parent is None: raise ValueError("No RL parent coin") puzzle = rl_puzzle_for_pk( bytes(pubkey), self.rl_info.limit, self.rl_info.interval, self.rl_info.rl_origin_id, self.rl_info.admin_pubkey, ) solution = solution_for_rl( coin.parent_coin_info, puzzle_hash, coin.amount, to_puzzlehash, amount, rl_parent.parent_coin_info, rl_parent.amount, self.rl_info.interval, self.rl_info.limit, fee, ) spends.append((puzzle, CoinSolution(coin, solution))) return spends
async def rl_generate_unsigned_transaction(self, to_puzzlehash, amount): spends = [] coin = self.rl_coin_record.coin puzzle_hash = coin.puzzle_hash pubkey, secretkey = await self.get_keys(puzzle_hash) rl_parent: Coin = await self.get_rl_parent() puzzle = rl_puzzle_for_pk( bytes(pubkey), self.rl_info.interval, self.rl_info.limit, self.rl_info.rl_origin.name(), self.rl_info.rl_clawback_pk, ) solution = solution_for_rl( coin.parent_coin_info, puzzle_hash, coin.amount, to_puzzlehash, amount, rl_parent.parent_coin_info, rl_parent.amount, None, None, ) spends.append((puzzle, CoinSolution(coin, solution))) return spends
def get_new_puzzle(self): return rl_puzzle_for_pk( pubkey=self.rl_info.user_pubkey, rate_amount=self.rl_info.limit, interval_time=self.rl_info.interval, origin_id=self.rl_info.rl_origin_id, clawback_pk=self.rl_info.admin_pubkey, )
def puzzle_for_pk(self, pk): if self.rl_info.initialized is False: return None return rl_puzzle_for_pk( pubkey=self.rl_info.user_pubkey, rate_amount=self.rl_info.limit, interval_time=self.rl_info.interval, origin_id=self.rl_info.rl_origin_id, clawback_pk=self.rl_info.admin_pubkey, )
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) aggsig = AugSchemeMPL.aggregate([signature]) return SpendBundle(list_of_coinsolutions, aggsig)
def get_new_puzzle(self) -> Program: if (self.rl_info.limit is None or self.rl_info.interval is None or self.rl_info.user_pubkey is None or self.rl_info.admin_pubkey is None or self.rl_info.rl_origin_id is None): raise ValueError("One or more of the RL info fields is None") return rl_puzzle_for_pk( pubkey=self.rl_info.user_pubkey, rate_amount=self.rl_info.limit, interval_time=self.rl_info.interval, origin_id=self.rl_info.rl_origin_id, clawback_pk=self.rl_info.admin_pubkey, )
async def set_user_info( self, interval: uint64, limit: uint64, origin_id: str, admin_pubkey: str ): if admin_pubkey.startswith("0x"): admin_pubkey = admin_pubkey[2:] admin_pubkey_bytes = bytes.fromhex(admin_pubkey) assert self.rl_info.user_pubkey is not None rl_puzzle = rl_puzzle_for_pk( pubkey=self.rl_info.user_pubkey, rate_amount=limit, interval_time=interval, origin_id=bytes.fromhex(origin_id), clawback_pk=admin_pubkey_bytes, ) rl_puzzle_hash = rl_puzzle.get_hash() new_rl_info = RLInfo( "admin", admin_pubkey_bytes, self.rl_info.user_pubkey, limit, interval, None, origin_id, rl_puzzle_hash, ) rl_puzzle_hash = rl_puzzle.get_hash() index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( self.rl_info.user_pubkey.hex() ) assert index is not None record = DerivationRecord( index, rl_puzzle_hash, self.rl_info.user_pubkey, WalletType.RATE_LIMITED, self.wallet_info.id, ) await self.wallet_state_manager.puzzle_store.add_derivation_paths([record]) data_str = json.dumps(new_rl_info.to_json_dict()) new_wallet_info = WalletInfo( self.wallet_info.id, self.wallet_info.name, self.wallet_info.type, data_str ) await self.wallet_state_manager.user_store.update_wallet(new_wallet_info) self.wallet_info = new_wallet_info self.rl_info = new_rl_info
def puzzle_for_pk(self, pk) -> Optional[Program]: if self.rl_info.initialized is False: return None if (self.rl_info.limit is None or self.rl_info.interval is None or self.rl_info.user_pubkey is None or self.rl_info.admin_pubkey is None or self.rl_info.rl_origin_id is None): return None return rl_puzzle_for_pk( pubkey=self.rl_info.user_pubkey, rate_amount=self.rl_info.limit, interval_time=self.rl_info.interval, origin_id=self.rl_info.rl_origin_id, clawback_pk=self.rl_info.admin_pubkey, )
def generate_unsigned_clawback_transaction(self, clawback_coin: Coin, clawback_puzzle_hash: bytes32): if (self.rl_info.limit is None or self.rl_info.interval is None or self.rl_info.user_pubkey is None or self.rl_info.admin_pubkey is None): raise spends = [] coin = clawback_coin puzzle = rl_puzzle_for_pk( self.rl_info.user_pubkey, self.rl_info.limit, self.rl_info.interval, self.rl_info.rl_origin, self.rl_info.admin_pubkey, ) solution = make_clawback_solution(clawback_puzzle_hash, clawback_coin.amount) spends.append((puzzle, CoinSolution(coin, solution))) return spends
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 admin_create_coin( self, interval: uint64, limit: uint64, user_pubkey: str, amount: uint64, fee: uint64, ) -> bool: coins = await self.wallet_state_manager.main_wallet.select_coins(amount) if coins is None: return False origin = coins.copy().pop() origin_id = origin.name() user_pubkey_bytes = hexstr_to_bytes(user_pubkey) assert self.rl_info.admin_pubkey is not None rl_puzzle = rl_puzzle_for_pk( pubkey=user_pubkey_bytes, rate_amount=limit, interval_time=interval, origin_id=origin_id, clawback_pk=self.rl_info.admin_pubkey, ) rl_puzzle_hash = rl_puzzle.get_tree_hash() index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( G1Element.from_bytes(self.rl_info.admin_pubkey) ) assert index is not None record = DerivationRecord( index, rl_puzzle_hash, self.rl_info.admin_pubkey, WalletType.RATE_LIMITED, self.id(), ) await self.wallet_state_manager.puzzle_store.add_derivation_paths([record]) spend_bundle = await self.main_wallet.generate_signed_transaction(amount, rl_puzzle_hash, fee, origin_id, coins) if spend_bundle is None: return False await self.main_wallet.push_transaction(spend_bundle) new_rl_info = RLInfo( "admin", self.rl_info.admin_pubkey, user_pubkey_bytes, limit, interval, origin, origin.name(), rl_puzzle_hash, True, ) 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 return True
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)