コード例 #1
0
    async def get_derivation_record(
            self, index: uint32,
            wallet_id: uint32) -> Optional[DerivationRecord]:
        """
        Returns the derivation record by index and wallet id.
        """
        cursor = await self.db_connection.execute(
            "SELECT * FROM derivation_paths WHERE derivation_index=? and wallet_id=?;",
            (
                index,
                wallet_id,
            ),
        )
        row = await cursor.fetchone()
        await cursor.close()

        if row is not None and row[0] is not None:
            return DerivationRecord(
                row[0],
                bytes.fromhex(row[2]),
                G1Element.from_bytes(bytes.fromhex(row[1])),
                row[3],
                row[4],
            )

        return None
コード例 #2
0
    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
コード例 #3
0
    async def create_rl_user(
        config: Dict,
        key_config: Dict,
        wallet_state_manager: Any,
        wallet: Wallet,
        name: str = None,
    ):
        async with wallet_state_manager.puzzle_store.lock:
            unused: Optional[
                uint32
            ] = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
            if unused is None:
                await wallet_state_manager.create_more_puzzle_hashes()
            unused = (
                await wallet_state_manager.puzzle_store.get_unused_derivation_path()
            )
            assert unused is not None

            sk_hex = key_config["wallet_sk"]
            private_key = PrivateKey.from_bytes(bytes.fromhex(sk_hex))
            pubkey_bytes: bytes = bytes(
                master_sk_to_wallet_sk(private_key, unused).get_g1()
            )

            rl_info = RLInfo("user", None, pubkey_bytes, None, None, None, None, None)
            info_as_string = json.dumps(rl_info.to_json_dict())
            await wallet_state_manager.user_store.create_wallet(
                "RL User", WalletType.RATE_LIMITED, info_as_string
            )
            wallet_info = await wallet_state_manager.user_store.get_last_wallet()
            if wallet_info is None:
                raise Exception("wallet_info is None")

            self = await RLWallet.create(
                config, key_config, wallet_state_manager, wallet_info, wallet, name
            )

            await wallet_state_manager.puzzle_store.add_derivation_paths(
                [
                    DerivationRecord(
                        unused,
                        token_bytes(),
                        pubkey_bytes,
                        WalletType.RATE_LIMITED,
                        wallet_info.id,
                    )
                ]
            )
            await wallet_state_manager.puzzle_store.set_used_up_to(unused)

        return self
コード例 #4
0
    async def create_rl_user(
        wallet_state_manager: Any,
    ):
        async with wallet_state_manager.puzzle_store.lock:
            unused: Optional[
                uint32
            ] = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
            if unused is None:
                await wallet_state_manager.create_more_puzzle_hashes()
            unused = (
                await wallet_state_manager.puzzle_store.get_unused_derivation_path()
            )
            assert unused is not None

            private_key = wallet_state_manager.private_key

            pubkey_bytes: bytes = bytes(
                master_sk_to_wallet_sk(private_key, unused).get_g1()
            )

            rl_info = RLInfo(
                "user", None, pubkey_bytes, None, None, None, None, None, False
            )
            info_as_string = json.dumps(rl_info.to_json_dict())
            await wallet_state_manager.user_store.create_wallet(
                "RL User", WalletType.RATE_LIMITED, info_as_string
            )
            wallet_info = await wallet_state_manager.user_store.get_last_wallet()
            if wallet_info is None:
                raise Exception("wallet_info is None")

            self = await RLWallet.create(wallet_state_manager, wallet_info)

            await wallet_state_manager.puzzle_store.add_derivation_paths(
                [
                    DerivationRecord(
                        unused,
                        token_bytes(),
                        pubkey_bytes,
                        WalletType.RATE_LIMITED,
                        wallet_info.id,
                    )
                ]
            )
            await wallet_state_manager.puzzle_store.set_used_up_to(unused)

            await wallet_state_manager.add_new_wallet(self, self.id())
            return self
コード例 #5
0
    async def create_more_puzzle_hashes(self, from_zero: bool = False):
        """
        For all wallets in the user store, generates the first few puzzle hashes so
        that we can restore the wallet from only the private keys.
        """
        for wallet_id in self.wallets.keys():
            target_wallet = self.wallets[wallet_id]
            unused: Optional[
                uint32] = await self.puzzle_store.get_unused_derivation_path()
            last: Optional[
                uint32] = await self.puzzle_store.get_last_derivation_path()

            to_generate = 500
            start_index = 0
            derivation_paths: List[DerivationRecord] = []

            if last is None:
                assert unused is None
            if unused is not None:
                assert last is not None
                start_index = last + 1
                to_generate -= last - unused

            # If the key was replaced (from_zero=True), we should generate the puzzle hashes for the new key
            end = start_index + to_generate
            if from_zero:
                start_index = 0

            for index in range(start_index, end):
                pubkey: PublicKey = target_wallet.get_public_key(index)
                puzzle: Program = target_wallet.puzzle_for_pk(bytes(pubkey))
                puzzlehash: bytes32 = puzzle.get_hash()
                self.log.info(
                    f"Generating public key at index {index} puzzle hash {puzzlehash.hex()}"
                )
                derivation_paths.append(
                    DerivationRecord(
                        uint32(index),
                        puzzlehash,
                        pubkey,
                        WalletType.STANDARD_WALLET,
                        uint32(target_wallet.wallet_info.id),
                    ))

            await self.puzzle_store.add_derivation_paths(derivation_paths)
            if from_zero and unused is not None and unused > 0:
                await self.puzzle_store.set_used_up_to(uint32(unused - 1))
コード例 #6
0
ファイル: rl_wallet.py プロジェクト: starlure/chia-blockchain
    async def create_rl_admin(
        config: Dict,
        key_config: Dict,
        wallet_state_manager: Any,
        wallet: Wallet,
        name: str = None,
    ):
        unused: Optional[
            uint32] = await wallet_state_manager.puzzle_store.get_unused_derivation_path(
            )
        if unused is None:
            await wallet_state_manager.create_more_puzzle_hashes()
        unused = await wallet_state_manager.puzzle_store.get_unused_derivation_path(
        )
        assert unused is not None

        sk_hex = key_config["wallet_sk"]
        private_key = ExtendedPrivateKey.from_bytes(bytes.fromhex(sk_hex))
        pubkey_bytes: bytes = bytes(
            private_key.public_child(unused).get_public_key())

        rl_info = RLInfo("admin", pubkey_bytes, None, None, None, None, None,
                         None)
        info_as_string = json.dumps(rl_info.to_json_dict())
        await wallet_state_manager.user_store.create_wallet(
            "RL Admin", WalletType.RATE_LIMITED, info_as_string)
        wallet_info = await wallet_state_manager.user_store.get_last_wallet()
        if wallet_info is None:
            raise

        await wallet_state_manager.puzzle_store.add_derivation_paths([
            DerivationRecord(
                unused,
                token_bytes(),
                pubkey_bytes,
                WalletType.RATE_LIMITED,
                wallet_info.id,
            )
        ])
        await wallet_state_manager.puzzle_store.set_used_up_to(unused)

        self = await RLWallet.create(config, key_config, wallet_state_manager,
                                     wallet_info, wallet, name)
        return self
コード例 #7
0
    async def create_rl_admin(
        wallet_state_manager: Any,
    ):
        unused: Optional[
            uint32
        ] = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
        if unused is None:
            await wallet_state_manager.create_more_puzzle_hashes()
        unused = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
        assert unused is not None

        private_key = master_sk_to_wallet_sk(wallet_state_manager.private_key, unused)
        pubkey_bytes: bytes = bytes(private_key.get_g1())

        rl_info = RLInfo(
            "admin", pubkey_bytes, None, None, None, None, None, None, False
        )
        info_as_string = json.dumps(rl_info.to_json_dict())
        wallet_info: Optional[
            WalletInfo
        ] = await wallet_state_manager.user_store.create_wallet(
            "RL Admin", WalletType.RATE_LIMITED.value, info_as_string
        )
        if wallet_info is None:
            raise Exception("wallet_info is None")

        await wallet_state_manager.puzzle_store.add_derivation_paths(
            [
                DerivationRecord(
                    unused,
                    token_bytes(),
                    pubkey_bytes,
                    WalletType.RATE_LIMITED,
                    wallet_info.id,
                )
            ]
        )
        await wallet_state_manager.puzzle_store.set_used_up_to(unused)

        self = await RLWallet.create(wallet_state_manager, wallet_info)
        await wallet_state_manager.add_new_wallet(self, self.wallet_info.id)
        return self
コード例 #8
0
    async def get_derivation_record_for_puzzle_hash(self, puzzle_hash: str) -> Optional[DerivationRecord]:
        """
        Returns the derivation record by index and wallet id.
        """
        cursor = await self.db_connection.execute(
            "SELECT * FROM derivation_paths WHERE puzzle_hash=?;",
            (puzzle_hash,),
        )
        row = await cursor.fetchone()
        await cursor.close()

        if row is not None and row[0] is not None:
            return DerivationRecord(
                row[0],
                bytes.fromhex(row[2]),
                G1Element.from_bytes(bytes.fromhex(row[1])),
                row[3],
                row[4],
            )

        return None
コード例 #9
0
    async def create_more_puzzle_hashes(self, from_zero: bool = False):
        """
        For all wallets in the user store, generates the first few puzzle hashes so
        that we can restore the wallet from only the private keys.
        """
        targets = list(self.wallets.keys())

        unused: Optional[uint32] = await self.puzzle_store.get_unused_derivation_path()
        if unused is None:
            # This handles the case where the database has entries but they have all been used
            unused = await self.puzzle_store.get_last_derivation_path()
            if unused is None:
                # This handles the case where the database is empty
                unused = uint32(0)

        if self.new_wallet:
            to_generate = self.config["initial_num_public_keys_new_wallet"]
        else:
            to_generate = self.config["initial_num_public_keys"]

        for wallet_id in targets:
            target_wallet = self.wallets[wallet_id]

            last: Optional[uint32] = await self.puzzle_store.get_last_derivation_path_for_wallet(wallet_id)

            start_index = 0
            derivation_paths: List[DerivationRecord] = []

            if last is not None:
                start_index = last + 1

            # If the key was replaced (from_zero=True), we should generate the puzzle hashes for the new key
            if from_zero:
                start_index = 0

            for index in range(start_index, unused + to_generate):
                if WalletType(target_wallet.type()) == WalletType.RATE_LIMITED:
                    if target_wallet.rl_info.initialized is False:
                        break
                    wallet_type = target_wallet.rl_info.type
                    if wallet_type == "user":
                        rl_pubkey = G1Element.from_bytes(target_wallet.rl_info.user_pubkey)
                    else:
                        rl_pubkey = G1Element.from_bytes(target_wallet.rl_info.admin_pubkey)
                    rl_puzzle: Program = target_wallet.puzzle_for_pk(rl_pubkey)
                    puzzle_hash: bytes32 = rl_puzzle.get_tree_hash()

                    rl_index = self.get_derivation_index(rl_pubkey)
                    if rl_index == -1:
                        break

                    derivation_paths.append(
                        DerivationRecord(
                            uint32(rl_index),
                            puzzle_hash,
                            rl_pubkey,
                            target_wallet.type(),
                            uint32(target_wallet.id()),
                        )
                    )
                    break

                pubkey: G1Element = self.get_public_key(uint32(index))
                puzzle: Program = target_wallet.puzzle_for_pk(bytes(pubkey))
                if puzzle is None:
                    self.log.warning(f"Unable to create puzzles with wallet {target_wallet}")
                    break
                puzzlehash: bytes32 = puzzle.get_tree_hash()
                self.log.info(f"Puzzle at index {index} wallet ID {wallet_id} puzzle hash {puzzlehash.hex()}")
                derivation_paths.append(
                    DerivationRecord(
                        uint32(index),
                        puzzlehash,
                        pubkey,
                        target_wallet.type(),
                        uint32(target_wallet.id()),
                    )
                )

            await self.puzzle_store.add_derivation_paths(derivation_paths)
        if unused > 0:
            await self.puzzle_store.set_used_up_to(uint32(unused - 1))
コード例 #10
0
    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
コード例 #11
0
    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
コード例 #12
0
    async def test_puzzle_store(self):
        db_filename = Path("puzzle_store_test.db")

        if db_filename.exists():
            db_filename.unlink()

        con = await aiosqlite.connect(db_filename)
        db = await WalletPuzzleStore.create(con)
        try:
            derivation_recs = []
            # wallet_types = [t for t in WalletType]
            [t for t in WalletType]

            for i in range(1000):
                derivation_recs.append(
                    DerivationRecord(
                        uint32(i),
                        token_bytes(32),
                        PrivateKey.from_seed(token_bytes(32)).get_g1(),
                        WalletType.STANDARD_WALLET,
                        uint32(1),
                    )
                )
                derivation_recs.append(
                    DerivationRecord(
                        uint32(i),
                        token_bytes(32),
                        PrivateKey.from_seed(token_bytes(32)).get_g1(),
                        WalletType.RATE_LIMITED,
                        uint32(2),
                    )
                )
            assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is False
            assert await db.index_for_pubkey(derivation_recs[0].pubkey) is None
            assert (
                await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
            )
            assert (
                await db.wallet_info_for_puzzle_hash(derivation_recs[2].puzzle_hash)
                is None
            )
            assert len((await db.get_all_puzzle_hashes())) == 0
            assert await db.get_last_derivation_path() is None
            assert await db.get_unused_derivation_path() is None
            assert await db.get_derivation_record(0, 2) is None

            await db.add_derivation_paths(derivation_recs)

            assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is True
            assert await db.index_for_pubkey(derivation_recs[4].pubkey) == 2
            assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) == 1
            assert await db.wallet_info_for_puzzle_hash(
                derivation_recs[2].puzzle_hash
            ) == (derivation_recs[2].wallet_id, derivation_recs[2].wallet_type)
            assert len((await db.get_all_puzzle_hashes())) == 2000
            assert await db.get_last_derivation_path() == 999
            assert await db.get_unused_derivation_path() == 0
            assert await db.get_derivation_record(0, 2) == derivation_recs[1]

            # Indeces up to 250
            await db.set_used_up_to(249)

            assert await db.get_unused_derivation_path() == 250

        except Exception as e:
            print(e, type(e))
            await db._clear_database()
            await db.close()
            db_filename.unlink()
            raise e

        await db._clear_database()
        await db.close()
        db_filename.unlink()