예제 #1
0
파일: test_Wallet.py 프로젝트: grx7/QRL
    def test_encrypt_save_reload(self):
        with set_qrl_dir("no_data"):
            wallet = Wallet()
            wallet.add_new_address(height=4)
            wallet.save()

            wallet_b = Wallet()

            self.assertEqual(1, len(wallet_b.address_items))
            self.assertEqual(wallet.address_items[0],
                             wallet_b.address_items[0])

            TEST_KEY = 'mytestkey'

            wallet_b.encrypt(TEST_KEY)
            wallet_b.save()

            self.assertEqual(1, len(wallet_b.address_items))
            self.assertNotEqual(wallet.address_items[0],
                                wallet_b.address_items[0])

            wallet_c = Wallet()
            self.assertEqual(1, len(wallet_c.address_items))
            self.assertTrue(wallet_c.address_items[0].encrypted)

            wallet_c.decrypt(TEST_KEY)
            self.assertFalse(wallet_c.address_items[0].encrypted)
            self.assertEqual(wallet.address_items[0],
                             wallet_c.address_items[0])
예제 #2
0
    def test_create_load(self):
        with set_qrl_dir("no_data"):
            wallet = Wallet()
            wallet.add_new_address(height=4)

            wallet_b = Wallet()
            self.assertEqual(1, len(wallet_b.address_items))

            self.assertEqual(wallet.address_items[0],
                             wallet_b.address_items[0])
예제 #3
0
파일: cli.py 프로젝트: som-dev/QRL
def wallet_add(ctx, height):
    """
    Adds an address or generates a new wallet (working directory)
    """
    if ctx.obj.remote:
        click.echo('This command is unsupported for remote wallets')
        return

    wallet = Wallet(wallet_path=ctx.obj.wallet_path)
    wallet.add_new_address(height)
    _print_addresses(ctx, wallet.address_items, config.user.wallet_dir)
예제 #4
0
파일: test_Wallet.py 프로젝트: grx7/QRL
    def test_encrypt_wallet(self):
        # 2 unencrypted addresses. This should work.
        with set_qrl_dir("no_data"):
            TEST_KEY = 'mytestkey'
            wallet = Wallet()
            wallet.add_new_address(height=4)
            wallet.add_new_address(height=4)

            wallet.encrypt(TEST_KEY)

            self.assertTrue(wallet.encrypted)
            self.assertFalse(wallet.encrypted_partially)
예제 #5
0
파일: test_Wallet.py 프로젝트: grx7/QRL
    def test_decrypt_wallet(self):
        with set_qrl_dir("no_data"):
            wallet = Wallet()
            wallet.add_new_address(height=4)
            wallet.add_new_address(height=4)
            addresses = wallet.addresses

            TEST_KEY = 'mytestkey'
            wallet.encrypt(TEST_KEY)
            self.assertTrue(wallet.encrypted)

            wallet.decrypt(TEST_KEY)
            self.assertEqual(addresses, wallet.addresses)
            self.assertFalse(wallet.encrypted_partially)
예제 #6
0
파일: cli.py 프로젝트: som-dev/QRL
def wallet_gen(ctx, height):
    """
    Generates a new wallet with one address
    """
    if ctx.obj.remote:
        click.echo('This command is unsupported for remote wallets')
        return

    # FIXME: If the wallet is there, it should fail
    wallet = Wallet(wallet_path=ctx.obj.wallet_path)
    if len(wallet.address_items) == 0:
        wallet.add_new_address(height)
        _print_addresses(ctx, wallet.address_items, config.user.wallet_dir)
    else:
        # FIXME: !!!!!
        click.echo("Wallet already exists")
예제 #7
0
파일: cli.py 프로젝트: monsieurleberre/QRL
def wallet_gen(ctx, height, hash_function, encrypt):
    """
    Generates a new wallet with one address
    """
    wallet = Wallet(wallet_path=ctx.obj.wallet_path)
    if len(wallet.address_items) > 0:
        click.echo("Wallet already exists")
        return

    wallet.add_new_address(height, hash_function)

    _print_addresses(ctx, wallet.address_items, config.user.wallet_dir)

    if encrypt:
        secret = click.prompt('Enter password to encrypt wallet with', hide_input=True, confirmation_prompt=True)
        wallet.encrypt(secret)

    wallet.save()
예제 #8
0
파일: cli.py 프로젝트: monsieurleberre/QRL
def wallet_add(ctx, height, hash_function):
    """
    Adds an address or generates a new wallet (working directory)
    """
    secret = None
    wallet = Wallet(wallet_path=ctx.obj.wallet_path)
    wallet_was_encrypted = wallet.encrypted
    if wallet.encrypted:
        secret = click.prompt('The wallet is encrypted. Enter password', hide_input=True)
        wallet.decrypt(secret)

    wallet.add_new_address(height, hash_function)

    _print_addresses(ctx, wallet.address_items, config.user.wallet_dir)

    if wallet_was_encrypted:
        wallet.encrypt(secret)

    wallet.save()
예제 #9
0
    def test_create_custom_hash_function_load(self):
        with set_qrl_dir("no_data"):
            wallet = Wallet()
            xmss = wallet.add_new_address(height=4, hash_function="sha2_256")
            wallet.save()

            self.assertEqual("sha2_256", xmss.hash_function)

            wallet_reloaded = Wallet()
            self.assertEqual("sha2_256", wallet_reloaded.address_items[0].hashFunction)
예제 #10
0
파일: test_Wallet.py 프로젝트: grx7/QRL
    def test_create(self):
        with set_qrl_dir("no_data"):
            wallet = Wallet()
            self.assertEqual(0, len(wallet.address_items))

            xmss1 = wallet.add_new_address(4)
            self.assertEqual(1, len(wallet.address_items))

            xmss2 = wallet.get_xmss_by_index(0)

            self.assertEqual(xmss1.address, xmss2.address)
            self.assertEqual(xmss1.mnemonic, xmss2.mnemonic)
예제 #11
0
파일: test_Wallet.py 프로젝트: grx7/QRL
    def test_integrity_behaviour(self):
        with set_qrl_dir("no_data"):
            TEST_KEY = 'mytestkey'
            wallet = Wallet()
            wallet.add_new_address(4)
            wallet.add_new_address(4)
            wallet.encrypt(TEST_KEY)

            # An encrypted wallet is not editable.
            with self.assertRaises(WalletEncryptionError):
                wallet.add_new_address(4)
            # You may not re-encrypt it.
            with self.assertRaises(WalletEncryptionError):
                wallet.encrypt(TEST_KEY)
            # You can save it.
            wallet.save()

            wallet.decrypt_item(1, TEST_KEY)
            # A partially encrypted wallet is not editable.
            with self.assertRaises(WalletEncryptionError):
                wallet.add_new_address(4)
            # You may not re-encrypt it.
            with self.assertRaises(WalletEncryptionError):
                wallet.encrypt(TEST_KEY)
            # You may not re-decrypt it.
            with self.assertRaises(WalletEncryptionError):
                wallet.decrypt(TEST_KEY)
            # You can't even save it.
            with self.assertRaises(WalletEncryptionError):
                wallet.save()

            wallet.decrypt_item(0, TEST_KEY)
            # A fully decrypted wallet is editable.
            wallet.add_new_address(4)
            # You may not re-decrypt it.
            with self.assertRaises(WalletEncryptionError):
                wallet.decrypt(TEST_KEY)
            # You can save it.
            wallet.save()
예제 #12
0
파일: test_Wallet.py 프로젝트: grx7/QRL
 def test_create_custom_hash_function(self):
     with set_qrl_dir("no_data"):
         wallet = Wallet()
         xmss = wallet.add_new_address(height=4, hash_function="sha2_256")
         self.assertEqual("sha2_256", xmss.hash_function)
예제 #13
0
class WalletD:
    def __init__(self):
        self._wallet_path = os.path.join(config.user.wallet_dir,
                                         'walletd.json')
        self._public_stub = qrl_pb2_grpc.PublicAPIStub(
            grpc.insecure_channel(config.user.public_api_server))
        self._wallet = None
        self._passphrase = None
        self.load_wallet()

    def to_plain_blocks(self, block):
        pheader = qrlwallet_pb2.PlainBlockHeader()
        pheader.hash_header = bin2hstr(block.header.hash_header)
        pheader.block_number = block.header.block_number
        pheader.timestamp_seconds = block.header.timestamp_seconds
        pheader.hash_header_prev = bin2hstr(block.header.hash_header_prev)
        pheader.reward_block = block.header.reward_block
        pheader.reward_fee = block.header.reward_fee
        pheader.merkle_root = bin2hstr(block.header.merkle_root)

        pheader.mining_nonce = block.header.mining_nonce
        pheader.extra_nonce = block.header.extra_nonce

        pblock = qrlwallet_pb2.PlainBlock()
        pblock.header.MergeFrom(pheader)

        for tx in block.transactions:
            pblock.transactions.extend([self.to_plain_transaction(tx)])

        for genesis_balance in block.genesis_balance:
            pgb = qrlwallet_pb2.PlainGenesisBalance()
            pgb.address = self.address_to_qaddress(genesis_balance.address)
            pgb.balance = genesis_balance.balance
            pblock.genesis_balance.extend([pgb])

        return pblock

    @staticmethod
    def to_plain_address_amount(address_amount):
        am = qrlwallet_pb2.PlainAddressAmount()
        am.address = bin2hstr(address_amount.address)
        am.amount = address_amount.amount
        return am

    def to_plain_transaction(self, tx):
        ptx = qrlwallet_pb2.PlainTransaction()
        if not tx.WhichOneof('transactionType'):
            return ptx
        if tx.master_addr:
            ptx.master_addr = self.address_to_qaddress(tx.master_addr)
        ptx.fee = tx.fee
        ptx.public_key = bin2hstr(tx.public_key)
        ptx.signature = bin2hstr(tx.signature)
        ptx.nonce = tx.nonce
        ptx.transaction_hash = bin2hstr(tx.transaction_hash)
        if tx.WhichOneof('transactionType') != 'coinbase':
            ptx.signer_addr = self.get_address_from_pk(ptx.public_key)

        if tx.WhichOneof('transactionType') == "transfer":
            ptx.transfer.amounts.extend(tx.transfer.amounts)
            for addr in tx.transfer.addrs_to:
                ptx.transfer.addrs_to.extend([self.address_to_qaddress(addr)])

        elif tx.WhichOneof('transactionType') == 'coinbase':
            ptx.coinbase.addr_to = self.address_to_qaddress(
                tx.coinbase.addr_to)
            ptx.coinbase.amount = tx.coinbase.amount

        elif tx.WhichOneof('transactionType') == 'lattice_public_key':
            ptx.lattice_public_key.MergeFrom(ptx.lattice_public_key())
            ptx.lattice_public_key.kyber_pk = bin2hstr(
                tx.lattice_public_key.kyber_pk)
            ptx.lattice_public_key.dilithium_pk = bin2hstr(
                tx.lattice_public_key.dilithium_pk)

        elif tx.WhichOneof('transactionType') == 'message':
            ptx.message.message_hash = tx.message.message_hash

        elif tx.WhichOneof('transactionType') == 'token':
            ptx.token.symbol = tx.token.symbol
            ptx.token.name = tx.token.name
            ptx.token.owner = self.address_to_qaddress(tx.token.owner)
            ptx.token.decimals = tx.token.decimals
            for initial_balance in tx.token.initial_balances:
                ptx.token.initial_balances.extend(
                    [self.to_plain_address_amount(initial_balance)])

        elif tx.WhichOneof('transactionType') == 'transfer_token':
            ptx.transfer_token.token_txhash = bin2hstr(
                tx.transfer_token.token_txhash)
            ptx.transfer_token.addrs_to.extend(
                self.addresses_to_qaddress(tx.transfer_token.addrs_to))
            ptx.transfer_token.amounts.extend(tx.transfer_token.amounts)

        elif tx.WhichOneof('transactionType') == 'slave':
            for slave_pk in tx.slave.slave_pks:
                ptx.slave.slave_pks.extend([bin2hstr(slave_pk)])
            ptx.slave.access_types.extend(tx.slave.access_types)

        return ptx

    def load_wallet(self):
        self._wallet = Wallet(self._wallet_path)

    @staticmethod
    def address_to_qaddress(address: bytes):
        return 'Q' + bin2hstr(address)

    @staticmethod
    def addresses_to_qaddress(addresses: list):
        qaddresses = []
        for address in addresses:
            qaddresses.append(WalletD.address_to_qaddress(address))
        return qaddresses

    @staticmethod
    def qaddress_to_address(qaddress: str) -> bytes:
        if not qaddress:
            return qaddress
        return bytes(hstr2bin(qaddress[1:]))

    @staticmethod
    def qaddresses_to_address(qaddresses: list) -> list:
        if not qaddresses:
            return qaddresses
        addresses = []
        for qaddress in qaddresses:
            addresses.append(WalletD.qaddress_to_address(qaddress))
        return addresses

    def authenticate(self):
        if not self._passphrase:
            if self._wallet.is_encrypted():
                raise ValueError('Failed: Passphrase Missing')

    def _encrypt_last_item(self):
        if not self._passphrase:
            return
        self._wallet.encrypt_item(
            len(self._wallet.address_items) - 1, self._passphrase)

    def _get_wallet_index_xmss(self, signer_address: str, ots_index: int):
        index, _ = self._wallet.get_address_item(signer_address)
        xmss = self._wallet.get_xmss_by_index(index, self._passphrase)
        if ots_index > 0:
            xmss.set_ots_index(ots_index)
        return index, xmss

    def add_new_address(self, height=10, hash_function='shake128') -> str:
        self.authenticate()

        if not hash_function:
            hash_function = 'shake128'

        if not height:
            height = 10

        self._wallet.add_new_address(height, hash_function, True)
        self._encrypt_last_item()
        self._wallet.save()
        return self._wallet.address_items[-1].qaddress

    def add_address_from_seed(self, seed=None) -> str:
        self.authenticate()

        words = seed.split()
        if len(words) == 34:
            bin_seed = mnemonic2bin(seed)
        elif len(seed) == 102:
            bin_seed = hstr2bin(seed)
        else:
            raise ValueError("Invalid Seed")

        address_from_seed = XMSS.from_extended_seed(bin_seed)
        if self._wallet.get_xmss_by_qaddress(address_from_seed.qaddress,
                                             self._passphrase):
            raise Exception("Address is already in the wallet")
        self._wallet.append_xmss(address_from_seed)
        self._encrypt_last_item()
        self._wallet.save()

        return address_from_seed.qaddress

    def list_address(self) -> list:
        self.authenticate()

        addresses = []
        for item in self._wallet.address_items:
            addresses.append(item.qaddress)

        return addresses

    def remove_address(self, qaddress: str) -> bool:
        self.authenticate()

        return self._wallet.remove(qaddress)

    def get_recovery_seeds(self, qaddress: str):
        self.authenticate()

        xmss = self._wallet.get_xmss_by_qaddress(qaddress, self._passphrase)
        if xmss:
            return xmss.hexseed, xmss.mnemonic

        raise ValueError("No such address found in wallet")

    def get_wallet_info(self):
        self.authenticate()

        return self._wallet.wallet_info()

    def _push_transaction(self, tx, xmss):
        tx.sign(xmss)
        if not tx.validate(True):
            return None

        push_transaction_req = qrl_pb2.PushTransactionReq(
            transaction_signed=tx.pbdata)
        push_transaction_resp = self._public_stub.PushTransaction(
            push_transaction_req, timeout=CONNECTION_TIMEOUT)
        if push_transaction_resp.error_code != qrl_pb2.PushTransactionResp.SUBMITTED:
            raise Exception(push_transaction_resp.error_description)

    def relay_transfer_txn(self, qaddresses_to: list, amounts: list, fee: int,
                           master_qaddress, signer_address: str,
                           ots_index: int):
        self.authenticate()
        index, xmss = self._get_wallet_index_xmss(signer_address, ots_index)

        tx = TransferTransaction.create(
            addrs_to=self.qaddresses_to_address(qaddresses_to),
            amounts=amounts,
            fee=fee,
            xmss_pk=xmss.pk,
            master_addr=self.qaddress_to_address(master_qaddress))

        self._push_transaction(tx, xmss)
        self._wallet.set_ots_index(index, xmss.ots_index)

        return self.to_plain_transaction(tx.pbdata)

    def relay_message_txn(self, message: str, fee: int, master_qaddress,
                          signer_address: str, ots_index: int):
        self.authenticate()
        index, xmss = self._get_wallet_index_xmss(signer_address, ots_index)

        tx = MessageTransaction.create(
            message_hash=message.encode(),
            fee=fee,
            xmss_pk=xmss.pk,
            master_addr=self.qaddress_to_address(master_qaddress))

        self._push_transaction(tx, xmss)
        self._wallet.set_ots_index(index, xmss.ots_index)

        return self.to_plain_transaction(tx.pbdata)

    def relay_token_txn(self, symbol: str, name: str, owner_qaddress: str,
                        decimals: int, qaddresses: list, amounts: list,
                        fee: int, master_qaddress, signer_address: str,
                        ots_index: int):
        self.authenticate()

        if len(qaddresses) != len(amounts):
            raise Exception("Number of Addresses & Amounts Mismatch")

        index, xmss = self._get_wallet_index_xmss(signer_address, ots_index)

        initial_balances = []
        for idx, qaddress in enumerate(qaddresses):
            initial_balances.append(
                qrl_pb2.AddressAmount(
                    address=self.qaddress_to_address(qaddress),
                    amount=amounts[idx]))
        tx = TokenTransaction.create(
            symbol=symbol.encode(),
            name=name.encode(),
            owner=self.qaddress_to_address(owner_qaddress),
            decimals=decimals,
            initial_balances=initial_balances,
            fee=fee,
            xmss_pk=xmss.pk,
            master_addr=self.qaddress_to_address(master_qaddress))

        self._push_transaction(tx, xmss)
        self._wallet.set_ots_index(index, xmss.ots_index)

        return self.to_plain_transaction(tx.pbdata)

    def relay_transfer_token_txn(self, qaddresses_to: list, amounts: list,
                                 token_txhash: str, fee: int, master_qaddress,
                                 signer_address: str, ots_index: int):
        self.authenticate()
        index, xmss = self._get_wallet_index_xmss(signer_address, ots_index)

        tx = TransferTokenTransaction.create(
            token_txhash=bytes(hstr2bin(token_txhash)),
            addrs_to=self.qaddresses_to_address(qaddresses_to),
            amounts=amounts,
            fee=fee,
            xmss_pk=xmss.pk,
            master_addr=self.qaddress_to_address(master_qaddress))

        self._push_transaction(tx, xmss)
        self._wallet.set_ots_index(index, xmss.ots_index)

        return self.to_plain_transaction(tx.pbdata)

    def relay_slave_txn(self, slave_pks: list, access_types: list, fee: int,
                        master_qaddress, signer_address: str, ots_index: int):
        self.authenticate()
        index, xmss = self._get_wallet_index_xmss(signer_address, ots_index)

        tx = SlaveTransaction.create(
            slave_pks=slave_pks,
            access_types=access_types,
            fee=fee,
            xmss_pk=xmss.pk,
            master_addr=self.qaddress_to_address(master_qaddress))

        self._push_transaction(tx, xmss)
        self._wallet.set_ots_index(index, xmss.ots_index)

        return self.to_plain_transaction(tx.pbdata)

    def encrypt_wallet(self, passphrase: str):
        if self._wallet.is_encrypted():
            raise Exception('Wallet Already Encrypted')
        if not passphrase:
            raise Exception("Missing Passphrase")
        if len(self._wallet.address_items) == 0:
            raise ValueError(
                'Cannot be encrypted as wallet does not have any address.')
        self._wallet.encrypt(passphrase)
        self._wallet.save()

    def lock_wallet(self):
        self._passphrase = None

    def unlock_wallet(self, passphrase: str):
        self._passphrase = passphrase
        self._wallet.decrypt(passphrase, first_address_only=True)
        self.load_wallet()

    def change_passphrase(self, old_passphrase: str, new_passphrase: str):
        if len(old_passphrase) == 0:
            raise Exception('Missing Old Passphrase')

        if len(new_passphrase) == 0:
            raise Exception('Missing New Passphrase')

        if old_passphrase == new_passphrase:
            raise Exception('Old Passphrase and New Passphrase cannot be same')

        self._passphrase = old_passphrase

        if not self._wallet:
            self.unlock_wallet(old_passphrase)
        try:
            self._wallet.decrypt(old_passphrase)
        except WalletDecryptionError:
            raise ValueError('Invalid Old Passphrase')

        self._wallet.encrypt(new_passphrase)
        self._wallet.save()
        self.lock_wallet()

    def get_transactions_by_address(self, qaddress: str) -> tuple:
        address = self.qaddress_to_address(qaddress)
        response = self._public_stub.GetTransactionsByAddress(
            qrl_pb2.GetTransactionsByAddressReq(address=address))
        return response.mini_transactions, response.balance

    def get_transaction(self, tx_hash: str):
        txhash = bytes(hstr2bin(tx_hash))
        response = self._public_stub.GetTransaction(
            qrl_pb2.GetTransactionReq(tx_hash=txhash))
        return self.to_plain_transaction(response.tx), response.confirmations

    def get_balance(self, qaddress: str) -> int:
        address = self.qaddress_to_address(qaddress)
        response = self._public_stub.GetBalance(
            qrl_pb2.GetBalanceReq(address=address))
        return response.balance

    def get_ots(self, qaddress: str):
        address = self.qaddress_to_address(qaddress)
        response = self._public_stub.GetOTS(qrl_pb2.GetOTSReq(address=address))
        return response.ots_bitfield, response.next_unused_ots_index

    def get_height(self) -> int:
        response = self._public_stub.GetHeight(qrl_pb2.GetHeightReq())
        return response.height

    def get_block(self, header_hash: str):
        headerhash = bytes(hstr2bin(header_hash))
        response = self._public_stub.GetBlock(
            qrl_pb2.GetBlockReq(header_hash=headerhash))
        return self.to_plain_blocks(response.block)

    def get_block_by_number(self, block_number: int):
        response = self._public_stub.GetBlockByNumber(
            qrl_pb2.GetBlockByNumberReq(block_number=block_number))
        return self.to_plain_blocks(response.block)

    def get_address_from_pk(self, pk: str) -> str:
        return self.address_to_qaddress(
            QRLHelper.getAddress(bytes(hstr2bin(pk))))