Exemple #1
0
def wallet_rm(ctx, wallet_idx, skip_confirmation):
    """
    Removes an address from the wallet using the given address index.

    Warning! Use with caution. Removing an address from the wallet
    will result in loss of access to the address and is not
    reversible unless you have address recovery information.
    Use the wallet_secret command for obtaining the recovery Mnemonic/Hexseed and
    the wallet_recover command for restoring an address.
    """
    wallet = Wallet(wallet_path=ctx.obj.wallet_path)

    address_item = get_item_from_wallet(wallet, wallet_idx)

    if address_item:
        if not skip_confirmation:
            click.echo(
                'You are about to remove address [{0}]: {1} from the wallet.'.
                format(wallet_idx, address_item.qaddress))
            click.echo(
                'Warning! By continuing, you risk complete loss of access to this address if you do not have a '
                'recovery Mnemonic/Hexseed.')
            click.confirm('Do you want to continue?', abort=True)
        wallet.remove(address_item.qaddress)

        _print_addresses(ctx, wallet.address_items, config.user.wallet_dir)
Exemple #2
0
def wallet_rm(ctx, wallet_idx, skip_confirmation):
    """
    Removes an address from the wallet using the given address index.

    Warning! Use with caution. Removing an address from the wallet
    will result in loss of access to the address and is not
    reversible unless you have address recovery information.
    Use the wallet_secret command for obtaining the recovery Mnemonic/Hexseed and
    the wallet_recover command for restoring an address.
    """
    if ctx.obj.remote:
        click.echo('This command is unsupported for remote wallets')
        return

    wallet = Wallet(wallet_path=ctx.obj.wallet_path)

    if 0 <= wallet_idx < len(wallet.address_items):
        addr_item = wallet.address_items[wallet_idx]
        if not skip_confirmation:
            click.echo(
                'You are about to remove address [{0}]: {1} from the wallet.'.
                format(wallet_idx, addr_item.qaddress))
            click.echo(
                'Warning! By continuing, you risk complete loss of access to this address if you do not have a recovery Mnemonic/Hexseed.'
            )
            click.confirm('Do you want to continue?', abort=True)
        wallet.remove(addr_item.qaddress)

        _print_addresses(ctx, wallet.address_items, config.user.wallet_dir)
    else:
        click.echo('Wallet index not found', color='yellow')
Exemple #3
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))))