def setUp(self):
        super().setUp()

        self.network = 'testnet'
        self.manager = self.create_peer(self.network, unlock_wallet=True)

        self.tmpdir = tempfile.mkdtemp()
        self.wallet = Wallet(directory=self.tmpdir)
        self.wallet.unlock(b'123')
Example #2
0
        def setUp(self, tx_storage, reactor=None):
            if not reactor:
                self.reactor = Clock()
            else:
                self.reactor = reactor
            self.reactor.advance(time.time())
            self.tx_storage = tx_storage
            assert tx_storage.first_timestamp > 0

            tx_storage._manually_initialize()

            self.genesis = self.tx_storage.get_all_genesis()
            self.genesis_blocks = [tx for tx in self.genesis if tx.is_block]
            self.genesis_txs = [tx for tx in self.genesis if not tx.is_block]

            from hathor.manager import HathorManager
            self.tmpdir = tempfile.mkdtemp()
            wallet = Wallet(directory=self.tmpdir)
            wallet.unlock(b'teste')
            self.manager = HathorManager(self.reactor, tx_storage=self.tx_storage, wallet=wallet)

            self.tx_storage.wallet_index = WalletIndex(self.manager.pubsub)
            self.tx_storage.tokens_index = TokensIndex()

            block_parents = [tx.hash for tx in chain(self.genesis_blocks, self.genesis_txs)]
            output = TxOutput(200, bytes.fromhex('1e393a5ce2ff1c98d4ff6892f2175100f2dad049'))
            self.block = Block(timestamp=MIN_TIMESTAMP, weight=12, outputs=[output], parents=block_parents,
                               nonce=100781, storage=tx_storage)
            self.block.resolve()
            self.block.verify()

            tx_parents = [tx.hash for tx in self.genesis_txs]
            tx_input = TxInput(
                tx_id=self.genesis_blocks[0].hash, index=0,
                data=bytes.fromhex('46304402203470cb9818c9eb842b0c433b7e2b8aded0a51f5903e971649e870763d0266a'
                                   'd2022049b48e09e718c4b66a0f3178ef92e4d60ee333d2d0e25af8868acf5acbb35aaa583'
                                   '056301006072a8648ce3d020106052b8104000a034200042ce7b94cba00b654d4308f8840'
                                   '7345cacb1f1032fb5ac80407b74d56ed82fb36467cb7048f79b90b1cf721de57e942c5748'
                                   '620e78362cf2d908e9057ac235a63'))

            self.tx = Transaction(
                timestamp=MIN_TIMESTAMP + 2, weight=10, nonce=932049, inputs=[tx_input], outputs=[output],
                tokens=[bytes.fromhex('0023be91834c973d6a6ddd1a0ae411807b7c8ef2a015afb5177ee64b666ce602')],
                parents=tx_parents, storage=tx_storage)
            self.tx.resolve()

            # Disable weakref to test the internal methods. Otherwise, most methods return objects from weakref.
            self.tx_storage._disable_weakref()

            self.tx_storage.enable_lock()
class BaseSignatureTest(unittest.TestCase):
    __test__ = False

    def setUp(self):
        super().setUp()

        self.network = 'testnet'
        self.manager = self.create_peer(self.network, unlock_wallet=True)

        self.tmpdir = tempfile.mkdtemp()
        self.wallet = Wallet(directory=self.tmpdir)
        self.wallet.unlock(b'123')

    def tearDown(self):
        super().tearDown()
        shutil.rmtree(self.tmpdir)

    def test_generate_signature(self):
        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = add_new_transactions(self.manager, 1, advance_clock=1)[0]

        address = self.wallet.get_unused_address()
        keypair = self.wallet.keys[address]
        private_key_hex = keypair.private_key_bytes.hex()

        private_key = keypair.get_private_key(b'123')
        public_key = private_key.public_key()

        parser = create_parser()

        # Generate signature to validate
        args = parser.parse_args([tx.get_struct().hex(), private_key_hex])
        f = StringIO()
        with capture_logs():
            with redirect_stdout(f):
                execute(args, '123')
        # Transforming prints str in array
        output = f.getvalue().strip().splitlines()

        signature = bytes.fromhex(output[0].split(':')[1].strip())

        # Now we validate that the signature is correct
        data_to_sign = tx.get_sighash_all()
        hashed_data = hashlib.sha256(data_to_sign).digest()
        self.assertIsNone(
            public_key.verify(signature, hashed_data,
                              ec.ECDSA(hashes.SHA256())))
Example #4
0
    def setUp(self):
        super().setUp()
        self.wallet = Wallet()
        self.tx_storage = TransactionMemoryStorage()
        self.genesis = self.tx_storage.get_all_genesis()
        self.genesis_blocks = [tx for tx in self.genesis if tx.is_block]
        self.genesis_txs = [tx for tx in self.genesis if not tx.is_block]

        # read genesis keys
        self.genesis_private_key = get_genesis_key()
        self.genesis_public_key = self.genesis_private_key.public_key()

        # this makes sure we can spend the genesis outputs
        self.manager = self.create_peer('testnet', tx_storage=self.tx_storage, unlock_wallet=True)
        blocks = add_blocks_unlock_reward(self.manager)
        self.last_block = blocks[-1]
Example #5
0
    def test_invalid_address(self):
        w = Wallet(directory=self.directory)
        w.unlock(PASSWORD)

        # creating valid address
        valid_address = '15d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'
        WalletOutputInfo(decode_address(valid_address), 100, None)

        # creating invalid address
        invalid_address = '5d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'
        with self.assertRaises(InvalidAddress):
            WalletOutputInfo(decode_address(invalid_address), 100, None)

        # invalid address (checksum invalid)
        invalid_address2 = '15d14K5jMqsN2uwUEFqiPG5SoD7Vr1Bfnq'
        with self.assertRaises(InvalidAddress):
            WalletOutputInfo(decode_address(invalid_address2), 100, None)
def execute(args: Namespace, wallet_passwd: str) -> None:
    from hathor.crypto.util import get_private_key_bytes, get_public_key_bytes_compressed
    from hathor.wallet import Wallet
    from hathor.wallet.util import generate_multisig_address, generate_multisig_redeem_script

    if (args.pubkey_count
            and args.pubkey_count > 16) or args.signatures_required > 16:
        print(
            'Error: maximum number of public keys or signatures required is 16'
        )
        return

    if not args.pubkey_count and not args.public_keys:
        print('Error: you must give at least pubkey_count or public_keys')
        return

    if args.dir:
        wallet = Wallet(directory=args.dir)
    else:
        wallet = Wallet()

    wallet.unlock(wallet_passwd.encode())

    if args.public_keys:
        public_keys_hex = args.public_keys.split(',')
        public_bytes = [bytes.fromhex(pkh) for pkh in public_keys_hex]
    else:
        # If not public keys as parameter, we need to create them
        public_bytes = []

        for i in range(args.pubkey_count):
            addr = wallet.get_unused_address()
            key = wallet.keys[addr]
            pk = key.get_private_key(wallet_passwd.encode())
            public_key_bytes = get_public_key_bytes_compressed(pk.public_key())
            public_bytes.append(public_key_bytes)
            print('------------------\n')
            print('Key {}\n'.format(i + 1))
            print('Private key: {}\n'.format(
                get_private_key_bytes(
                    pk,
                    encryption_algorithm=serialization.BestAvailableEncryption(
                        wallet_passwd.encode())).hex()))
            print('Public key: {}\n'.format(public_key_bytes.hex()))
            print('Address: {}\n'.format(addr))

    # Then we create the redeem script
    redeem_script = generate_multisig_redeem_script(args.signatures_required,
                                                    public_bytes)

    print('------------------\n')
    print('Redeem script:', redeem_script.hex())
    print('\n')

    # Then we created the multisig address
    address = generate_multisig_address(redeem_script)

    print('------------------\n')
    print('MultiSig address:', address)
    print('------------------\n\n')
Example #7
0
        def create_wallet():
            if args.wallet == 'hd':
                print('Using HDWallet')
                kwargs = {
                    'words': args.words,
                }

                if args.passphrase:
                    wallet_passphrase = getpass.getpass(
                        prompt='HD Wallet passphrase:')
                    kwargs['passphrase'] = wallet_passphrase.encode()

                if args.data:
                    kwargs['directory'] = args.data

                return HDWallet(**kwargs)
            elif args.wallet == 'keypair':
                print('Using KeyPairWallet')
                if args.data:
                    wallet = Wallet(directory=args.data)
                else:
                    wallet = Wallet()

                wallet.flush_to_disk_interval = 5  # seconds

                if args.unlock_wallet:
                    wallet_passwd = getpass.getpass(prompt='Wallet password:'******'Invalid type for wallet')
Example #8
0
    def test_manager_connections(self):
        tx_storage = TransactionMemoryStorage()
        tmpdir = tempfile.mkdtemp()
        wallet = Wallet(directory=tmpdir)
        wallet.unlock(b'teste')
        manager = HathorManager(self.clock, tx_storage=tx_storage, wallet=wallet)

        endpoint = 'tcp://127.0.0.1:8005'
        manager.connections.connect_to(endpoint, use_ssl=True)

        self.assertFalse(endpoint in manager.connections.connecting_peers)
        self.assertFalse(endpoint in manager.connections.handshaking_peers)
        self.assertFalse(endpoint in manager.connections.connected_peers)

        manager.stop()
        manager.stop()

        shutil.rmtree(tmpdir)
Example #9
0
    def _create_test_wallet(self):
        """ Generate a Wallet with a number of keypairs for testing
            :rtype: Wallet
        """
        tmpdir = tempfile.mkdtemp()
        self.tmpdirs.append(tmpdir)

        wallet = Wallet(directory=tmpdir)
        wallet.unlock(b'MYPASS')
        wallet.generate_keys(count=20)
        wallet.lock()
        return wallet
    def test_manager_connections(self):
        tx_storage = TransactionMemoryStorage()
        tmpdir = tempfile.mkdtemp()
        wallet = Wallet(directory=tmpdir)
        wallet.unlock(b'teste')
        manager = HathorManager(self.clock,
                                tx_storage=tx_storage,
                                wallet=wallet)

        endpoint = 'tcp://127.0.0.1:8005'
        manager.connections.connect_to(endpoint, use_ssl=True)

        self.assertNotIn(endpoint,
                         manager.connections.iter_not_ready_endpoints())
        self.assertNotIn(endpoint,
                         manager.connections.iter_ready_connections())
        self.assertNotIn(endpoint, manager.connections.iter_all_connections())

        shutil.rmtree(tmpdir)
Example #11
0
    def test_insuficient_funds(self):
        w = Wallet(directory=self.directory)
        w.unlock(PASSWORD)

        # create transaction spending some value
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 100, timelock=None)
        with self.assertRaises(InsufficientFunds):
            w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
Example #12
0
def execute(args: Namespace, password: str) -> None:
    from hathor.wallet import Wallet

    passwd: bytes = password.encode('utf-8')

    count = args.count
    directory = args.directory or './'
    print('Generating {} keys at {}'.format(count, directory))

    wallet = Wallet(directory=directory)
    wallet.unlock(passwd)
    wallet.generate_keys(count=count)
    wallet._write_keys_to_file()
Example #13
0
 def test_block_increase_balance(self):
     # generate a new block and check if we increase balance
     w = Wallet(directory=self.directory)
     w.unlock(PASSWORD)
     new_address = w.get_unused_address()
     key = w.keys[new_address]
     out = WalletOutputInfo(decode_address(key.address),
                            BLOCK_REWARD,
                            timelock=None)
     tx = w.prepare_transaction(Transaction, inputs=[], outputs=[out])
     tx.update_hash()
     w.on_new_tx(tx)
     utxo = w.unspent_txs[settings.HATHOR_TOKEN_UID].get((tx.hash, 0))
     self.assertIsNotNone(utxo)
     self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                      WalletBalance(0, BLOCK_REWARD))
Example #14
0
class BasicTransaction(unittest.TestCase):
    def setUp(self):
        super().setUp()
        self.wallet = Wallet()
        self.tx_storage = TransactionMemoryStorage()
        self.genesis = self.tx_storage.get_all_genesis()
        self.genesis_blocks = [tx for tx in self.genesis if tx.is_block]
        self.genesis_txs = [tx for tx in self.genesis if not tx.is_block]

        # read genesis keys
        self.genesis_private_key = get_genesis_key()
        self.genesis_public_key = self.genesis_private_key.public_key()

        # this makes sure we can spend the genesis outputs
        self.manager = self.create_peer('testnet',
                                        tx_storage=self.tx_storage,
                                        unlock_wallet=True,
                                        wallet_index=True)
        blocks = add_blocks_unlock_reward(self.manager)
        self.last_block = blocks[-1]

    def test_input_output_match(self):
        genesis_block = self.genesis_blocks[0]

        _input = TxInput(genesis_block.hash, 0, b'')

        # spend less than what was generated
        value = genesis_block.outputs[0].value - 1
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)
        tx = Transaction(inputs=[_input],
                         outputs=[output],
                         storage=self.tx_storage)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        with self.assertRaises(InputOutputMismatch):
            tx.verify_sum()

    def test_script(self):
        genesis_block = self.genesis_blocks[0]

        # random keys to be used
        random_priv = 'MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgMnAHVIyj7Hym2yI' \
                      'w+JcKEfdCHByIp+FHfPoIkcnjqGyhRANCAATX76SGshGeoacUcZDhXEzERt' \
                      'AHbd30CVpUg8RRnAIhaFcuMY3G+YFr/mReAPRuiLKCnolWz3kCltTtNj36rJyd'
        private_key_random = get_private_key_from_bytes(
            base64.b64decode(random_priv))

        # create input data with incorrect private key
        _input = TxInput(genesis_block.hash, 0, b'')
        value = genesis_block.outputs[0].value

        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        tx = Transaction(inputs=[_input],
                         outputs=[output],
                         storage=self.tx_storage,
                         timestamp=self.last_block.timestamp + 1)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, private_key_random)
        data_wrong = P2PKH.create_input_data(public_bytes, signature)
        _input.data = data_wrong

        with self.assertRaises(InvalidInputData):
            tx.verify_inputs()

    def test_too_many_inputs(self):
        random_bytes = bytes.fromhex(
            '0000184e64683b966b4268f387c269915cc61f6af5329823a93e3696cb0fe902')

        _input = TxInput(random_bytes, 0, random_bytes)
        inputs = [_input] * (MAX_NUM_INPUTS + 1)

        tx = Transaction(inputs=inputs, storage=self.tx_storage)

        with self.assertRaises(TooManyInputs):
            tx.verify_number_of_inputs()

    def test_no_inputs(self):
        tx = Transaction(inputs=[], storage=self.tx_storage)

        with self.assertRaises(NoInputError):
            tx.verify_number_of_inputs()

    def test_too_many_outputs(self):
        random_bytes = bytes.fromhex(
            '0000184e64683b966b4268f387c269915cc61f6af5329823a93e3696cb0fe902')

        output = TxOutput(1, random_bytes)
        outputs = [output] * (MAX_NUM_OUTPUTS + 1)

        tx = Transaction(outputs=outputs, storage=self.tx_storage)

        with self.assertRaises(TooManyOutputs):
            tx.verify_number_of_outputs()

    def _gen_tx_spending_genesis_block(self):
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        _input = TxInput(genesis_block.hash, 0, b'')

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        tx = Transaction(nonce=100,
                         inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        tx.inputs[0].data = P2PKH.create_input_data(public_bytes, signature)

        tx.update_hash()
        return tx

    def test_struct(self):
        tx = self._gen_tx_spending_genesis_block()
        data = tx.get_struct()
        tx_read = Transaction.create_from_struct(data)

        self.assertEqual(tx, tx_read)

    def test_children_update(self):
        tx = self._gen_tx_spending_genesis_block()

        # get info before update
        children_len = []
        for parent in tx.get_parents():
            metadata = parent.get_metadata()
            children_len.append(len(metadata.children))

        # update metadata
        tx.update_initial_metadata()

        # genesis transactions should have only this tx in their children set
        for old_len, parent in zip(children_len, tx.get_parents()):
            metadata = parent.get_metadata()
            self.assertEqual(len(metadata.children) - old_len, 1)
            self.assertEqual(metadata.children.pop(), tx.hash)

    def test_block_inputs(self):
        # a block with inputs should be invalid
        parents = [tx.hash for tx in self.genesis]
        genesis_block = self.genesis_blocks[0]

        tx_inputs = [TxInput(genesis_block.hash, 0, b'')]

        address = get_address_from_public_key(self.genesis_public_key)
        output_script = P2PKH.create_output_script(address)
        tx_outputs = [TxOutput(100, output_script)]

        block = Block(
            nonce=100,
            outputs=tx_outputs,
            parents=parents,
            weight=1,  # low weight so we don't waste time with PoW
            storage=self.tx_storage)

        block.inputs = tx_inputs

        block.resolve()

        with self.assertRaises(BlockWithInputs):
            block.verify()

    def test_block_outputs(self):
        from hathor.transaction import MAX_NUM_OUTPUTS
        from hathor.transaction.exceptions import TooManyOutputs

        # a block should have no more than MAX_NUM_OUTPUTS outputs
        parents = [tx.hash for tx in self.genesis]

        address = get_address_from_public_key(self.genesis_public_key)
        output_script = P2PKH.create_output_script(address)
        tx_outputs = [TxOutput(100, output_script)] * (MAX_NUM_OUTPUTS + 1)

        block = Block(
            nonce=100,
            outputs=tx_outputs,
            parents=parents,
            weight=1,  # low weight so we don't waste time with PoW
            storage=self.tx_storage)

        with self.assertRaises(TooManyOutputs):
            block.verify_outputs()

    def test_tx_number_parents(self):
        genesis_block = self.genesis_blocks[0]

        _input = TxInput(genesis_block.hash, 0, b'')

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        parents = [self.genesis_txs[0].hash]
        tx = Transaction(weight=1,
                         inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage,
                         timestamp=self.last_block.timestamp + 1)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        tx.inputs[0].data = P2PKH.create_input_data(public_bytes, signature)

        # in first test, only with 1 parent
        tx.resolve()
        with self.assertRaises(IncorrectParents):
            tx.verify()

        # test with 3 parents
        parents = [tx.hash for tx in self.genesis]
        tx.parents = parents
        tx.resolve()
        with self.assertRaises(IncorrectParents):
            tx.verify()

        # 2 parents, 1 tx and 1 block
        parents = [self.genesis_txs[0].hash, self.genesis_blocks[0].hash]
        tx.parents = parents
        tx.resolve()
        with self.assertRaises(IncorrectParents):
            tx.verify()

    def test_block_unknown_parent(self):
        address = get_address_from_public_key(self.genesis_public_key)
        output_script = P2PKH.create_output_script(address)
        tx_outputs = [TxOutput(100, output_script)]

        # Random unknown parent
        parents = [hashlib.sha256().digest()]

        block = Block(
            nonce=100,
            outputs=tx_outputs,
            parents=parents,
            weight=1,  # low weight so we don't waste time with PoW
            storage=self.tx_storage)

        block.resolve()
        with self.assertRaises(ParentDoesNotExist):
            block.verify()

    def test_block_number_parents(self):
        address = get_address_from_public_key(self.genesis_public_key)
        output_script = P2PKH.create_output_script(address)
        tx_outputs = [TxOutput(100, output_script)]

        parents = [tx.hash for tx in self.genesis_txs]

        block = Block(
            nonce=100,
            outputs=tx_outputs,
            parents=parents,
            weight=1,  # low weight so we don't waste time with PoW
            storage=self.tx_storage)

        block.resolve()
        with self.assertRaises(IncorrectParents):
            block.verify()

    def test_tx_inputs_out_of_range(self):
        # we'll try to spend output 3 from genesis transaction, which does not exist
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        _input = TxInput(genesis_block.hash,
                         len(genesis_block.outputs) + 1, b'')
        tx = Transaction(weight=1,
                         inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        data = P2PKH.create_input_data(public_bytes, signature)
        tx.inputs[0].data = data

        # test with an inexistent index
        tx.resolve()
        with self.assertRaises(InexistentInput):
            tx.verify()

        # now with index equals of len of outputs
        _input = [
            TxInput(genesis_block.hash, len(genesis_block.outputs), data)
        ]
        tx.inputs = _input
        # test with an inexistent index
        tx.resolve()
        with self.assertRaises(InexistentInput):
            tx.verify()

        # now with inexistent tx hash
        random_bytes = bytes.fromhex(
            '0000184e64683b966b4268f387c269915cc61f6af5329823a93e3696cb0fe902')
        _input = [TxInput(random_bytes, 3, data)]
        tx.inputs = _input
        tx.resolve()
        with self.assertRaises(InexistentInput):
            tx.verify()

    def test_tx_inputs_conflict(self):
        # the new tx inputs will try to spend the same output
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        # We can't only duplicate the value because genesis is using the max value possible
        outputs = [TxOutput(value, script), TxOutput(value, script)]

        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(weight=1,
                         inputs=[_input, _input],
                         outputs=outputs,
                         parents=parents,
                         storage=self.tx_storage,
                         timestamp=self.last_block.timestamp + 1)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        tx.resolve()
        with self.assertRaises(ConflictingInputs):
            tx.verify()

    def test_regular_tx(self):
        # this should succeed
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(weight=1,
                         inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage,
                         timestamp=self.last_block.timestamp + 1)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        tx.resolve()
        tx.verify()

    def test_weight_nan(self):
        # this should succeed
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage)
        tx.weight = float('NaN')

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        tx.update_hash()
        self.assertTrue(isnan(tx.weight))
        with self.assertRaises(WeightError):
            tx.verify()

    def test_weight_inf(self):
        # this should succeed
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage)
        tx.weight = float('inf')

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        tx.update_hash()
        self.assertTrue(isinf(tx.weight))
        with self.assertRaises(WeightError):
            tx.verify()

    def test_tx_duplicated_parents(self):
        # the new tx will confirm the same tx twice
        parents = [self.genesis_txs[0].hash, self.genesis_txs[0].hash]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(weight=1,
                         inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage,
                         timestamp=self.last_block.timestamp + 1)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        tx.resolve()
        with self.assertRaises(DuplicatedParents):
            tx.verify()

    def test_update_timestamp(self):
        parents = [tx for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        # update based on input
        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(weight=1,
                         inputs=[_input],
                         outputs=[output],
                         parents=[p.hash for p in parents],
                         storage=self.tx_storage)

        input_timestamp = genesis_block.timestamp

        max_ts = max(input_timestamp, parents[0].timestamp,
                     parents[1].timestamp)
        tx.update_timestamp(0)
        self.assertEquals(tx.timestamp, max_ts + 1)

        ts = max_ts + 20
        tx.update_timestamp(ts)
        self.assertEquals(tx.timestamp, ts)

    def test_propagation_error(self):
        manager = self.create_peer('testnet', unlock_wallet=True)
        manager.test_mode = TestMode.DISABLED

        # 1. propagate genesis
        genesis_block = self.genesis_blocks[0]
        genesis_block.storage = manager.tx_storage
        self.assertFalse(manager.propagate_tx(genesis_block))

        # 2. propagate block with weight 1
        block = manager.generate_mining_block()
        block.weight = 1
        block.resolve()
        self.assertFalse(manager.propagate_tx(block))

        # 3. propagate block with wrong amount of tokens
        block = manager.generate_mining_block()
        output = TxOutput(1, block.outputs[0].script)
        block.outputs = [output]
        block.resolve()
        self.assertFalse(manager.propagate_tx(block))

        # 4. propagate block from the future
        block = manager.generate_mining_block()
        block.timestamp = int(
            self.clock.seconds()) + settings.MAX_FUTURE_TIMESTAMP_ALLOWED + 100
        block.resolve(update_time=False)
        self.assertFalse(manager.propagate_tx(block))

    def test_tx_methods(self):
        blocks = add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        txs = add_new_transactions(self.manager, 2, advance_clock=1)

        # Validate __str__, __bytes__, __eq__
        tx = txs[0]
        tx2 = txs[1]
        str_tx = str(tx)
        self.assertTrue(isinstance(str_tx, str))
        self.assertEqual(bytes(tx), tx.get_struct())

        tx_equal = Transaction.create_from_struct(tx.get_struct())
        self.assertTrue(tx == tx_equal)
        self.assertFalse(tx == tx2)

        tx2_hash = tx2.hash
        tx2.hash = None
        self.assertFalse(tx == tx2)
        tx2.hash = tx2_hash

        # Validate is_genesis without storage
        tx_equal.storage = None
        self.assertFalse(tx_equal.is_genesis)

        # Pow error
        tx2.verify_pow()
        tx2.weight = 100
        with self.assertRaises(PowError):
            tx2.verify_pow()

        # Verify parent timestamps
        tx2.verify_parents()
        tx2_timestamp = tx2.timestamp
        tx2.timestamp = 2
        with self.assertRaises(TimestampError):
            tx2.verify_parents()
        tx2.timestamp = tx2_timestamp

        # Verify inputs timestamps
        tx2.verify_inputs()
        tx2.timestamp = 2
        with self.assertRaises(TimestampError):
            tx2.verify_inputs()
        tx2.timestamp = tx2_timestamp

        # Validate maximum distance between blocks
        block = blocks[0]
        block2 = blocks[1]
        block2.timestamp = block.timestamp + settings.MAX_DISTANCE_BETWEEN_BLOCKS
        block2.verify_parents()
        block2.timestamp += 1
        with self.assertRaises(TimestampError):
            block2.verify_parents()

    def test_block_big_nonce(self):
        block = self.genesis_blocks[0]

        # Integer with more than 4 bytes of representation
        start = 1 << (8 * 12)
        end = start + 1 << (8 * 4)

        hash = block.start_mining(start, end)
        assert hash is not None

        block.hash = hash
        cloned_block = block.clone()

        assert cloned_block == block

    def test_block_data(self):
        def add_block_with_data(data: bytes = b'') -> None:
            add_new_blocks(self.manager, 1, advance_clock=1,
                           block_data=data)[0]

        add_block_with_data()
        add_block_with_data(b'Testing, testing 1, 2, 3...')
        add_block_with_data(100 * b'a')
        with self.assertRaises(TransactionDataError):
            add_block_with_data(101 * b'a')

    def test_output_serialization(self):
        from hathor.transaction.base_transaction import (
            _MAX_OUTPUT_VALUE_32,
            MAX_OUTPUT_VALUE,
            bytes_to_output_value,
            output_value_to_bytes,
        )
        max_32 = output_value_to_bytes(_MAX_OUTPUT_VALUE_32)
        self.assertEqual(len(max_32), 4)
        value, buf = bytes_to_output_value(max_32)
        self.assertEqual(value, _MAX_OUTPUT_VALUE_32)

        over_32 = output_value_to_bytes(_MAX_OUTPUT_VALUE_32 + 1)
        self.assertEqual(len(over_32), 8)
        value, buf = bytes_to_output_value(over_32)
        self.assertEqual(value, _MAX_OUTPUT_VALUE_32 + 1)

        max_64 = output_value_to_bytes(MAX_OUTPUT_VALUE)
        self.assertEqual(len(max_64), 8)
        value, buf = bytes_to_output_value(max_64)
        self.assertEqual(value, MAX_OUTPUT_VALUE)

    def test_output_value(self):
        from hathor.transaction.base_transaction import bytes_to_output_value

        # first test using a small output value with 8 bytes. It should fail
        parents = [tx.hash for tx in self.genesis_txs]
        outputs = [TxOutput(1, b'')]
        tx = Transaction(outputs=outputs, parents=parents)
        original_struct = tx.get_struct()
        struct_bytes = tx.get_funds_struct()

        # we'll get the struct without the last output bytes and add it ourselves
        struct_bytes = struct_bytes[:-7]
        # add small value using 8 bytes and expect failure when trying to deserialize
        struct_bytes += (-1).to_bytes(8, byteorder='big', signed=True)
        struct_bytes += int_to_bytes(0, 1)
        struct_bytes += int_to_bytes(0, 2)
        struct_bytes += tx.get_graph_struct()
        struct_bytes += int_to_bytes(tx.nonce, tx.SERIALIZATION_NONCE_SIZE)

        len_difference = len(struct_bytes) - len(original_struct)
        assert len_difference == 4, 'new struct is incorrect, len difference={}'.format(
            len_difference)

        with self.assertRaises(ValueError):
            Transaction.create_from_struct(struct_bytes)

        # now use 8 bytes and make sure it's working
        outputs = [TxOutput(MAX_OUTPUT_VALUE, b'')]
        tx = Transaction(outputs=outputs, parents=parents)
        tx.update_hash()
        original_struct = tx.get_struct()
        tx2 = Transaction.create_from_struct(original_struct)
        tx2.update_hash()
        assert tx == tx2

        # Validating that all output values must be positive
        value = 1
        address = decode_address('WUDtnw3GYjvUnZmiHAmus6hhs9GoSUSJMG')
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)
        output.value = -1
        random_bytes = bytes.fromhex(
            '0000184e64683b966b4268f387c269915cc61f6af5329823a93e3696cb0fe902')
        _input = TxInput(random_bytes, 0, random_bytes)
        tx = Transaction(inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage)
        with self.assertRaises(InvalidOutputValue):
            tx.resolve()

        # 'Manually resolving', to validate verify method
        tx.hash = bytes.fromhex(
            '012cba011be3c29f1c406f9015e42698b97169dbc6652d1f5e4d5c5e83138858')
        with self.assertRaises(InvalidOutputValue):
            tx.verify()

        # Invalid output value
        invalid_output = bytes.fromhex('ffffffff')
        with self.assertRaises(InvalidOutputValue):
            bytes_to_output_value(invalid_output)

        # Can't instantiate an output with negative value
        with self.assertRaises(AssertionError):
            TxOutput(-1, script)

    def test_tx_version(self):
        from hathor.transaction.base_transaction import TxVersion

        # test the 1st byte of version field is ignored
        version = TxVersion(0xFF00)
        self.assertEqual(version.get_cls(), Block)
        version = TxVersion(0xFF01)
        self.assertEqual(version.get_cls(), Transaction)

        # test serialization doesn't mess up with version
        block = Block(version=0xFF00, nonce=100, weight=1)
        block2 = block.clone()
        self.assertEqual(block.version, block2.version)

    def test_output_sum_ignore_authority(self):
        # sum of tx outputs should ignore authority outputs
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output1 = TxOutput(5, script)  # regular utxo
        output2 = TxOutput(30, script, 0b10000001)  # authority utxo
        output3 = TxOutput(3, script)  # regular utxo
        tx = Transaction(outputs=[output1, output2, output3],
                         storage=self.tx_storage)

        self.assertEqual(8, tx.sum_outputs)

    def _spend_reward_tx(self, manager, reward_block):
        value = reward_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        input_ = TxInput(reward_block.hash, 0, b'')
        output = TxOutput(value, script)
        tx = Transaction(
            weight=1,
            timestamp=int(manager.reactor.seconds()) + 1,
            inputs=[input_],
            outputs=[output],
            parents=manager.get_new_tx_parents(),
            storage=manager.tx_storage,
        )
        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        input_.data = P2PKH.create_input_data(public_bytes, signature)
        tx.resolve()
        return tx

    def test_reward_lock(self):
        from hathor.transaction.exceptions import RewardLocked

        # add block with a reward we can spend
        reward_block = self.manager.generate_mining_block(
            address=get_address_from_public_key(self.genesis_public_key))
        reward_block.resolve()
        self.assertTrue(self.manager.propagate_tx(reward_block))
        # reward cannot be spent while not enough blocks are added
        for _ in range(settings.REWARD_SPEND_MIN_BLOCKS):
            tx = self._spend_reward_tx(self.manager, reward_block)
            with self.assertRaises(RewardLocked):
                tx.verify()
            add_new_blocks(self.manager, 1, advance_clock=1)
        # now it should be spendable
        tx = self._spend_reward_tx(self.manager, reward_block)
        self.assertTrue(self.manager.propagate_tx(tx, fails_silently=False))

    def test_reward_lock_timestamp(self):
        from hathor.transaction.exceptions import RewardLocked

        # add block with a reward we can spend
        reward_block = self.manager.generate_mining_block(
            address=get_address_from_public_key(self.genesis_public_key))
        reward_block.resolve()
        self.assertTrue(self.manager.propagate_tx(reward_block))

        # we add enough blocks that this output could be spent based on block height
        blocks = add_blocks_unlock_reward(self.manager)

        # tx timestamp is equal to the block that unlock the spent rewards. It should
        # be greater, so it'll fail
        tx = self._spend_reward_tx(self.manager, reward_block)
        tx.timestamp = blocks[-1].timestamp
        tx.resolve()
        with self.assertRaises(RewardLocked):
            tx.verify()

        # we can fix it be incrementing the timestamp
        tx._height_cache = None
        tx.timestamp = blocks[-1].timestamp + 1
        tx.resolve()
        tx.verify()

    def test_wallet_index(self):
        # First transaction: send tokens to output with address=address_b58
        parents = [tx.hash for tx in self.genesis_txs]
        genesis_block = self.genesis_blocks[0]

        value = genesis_block.outputs[0].value
        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(value, script)

        address_b58 = parse_address_script(script).address
        # Get how many transactions wallet index already has for this address
        wallet_index_count = len(
            self.tx_storage.wallet_index.index[address_b58])

        _input = TxInput(genesis_block.hash, 0, b'')
        tx = Transaction(weight=1,
                         inputs=[_input],
                         outputs=[output],
                         parents=parents,
                         storage=self.tx_storage,
                         timestamp=self.last_block.timestamp + 1)

        data_to_sign = tx.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        _input.data = P2PKH.create_input_data(public_bytes, signature)

        tx.resolve()
        self.manager.propagate_tx(tx)

        # This transaction has an output to address_b58, so we need one more element on the index
        self.assertEqual(len(self.tx_storage.wallet_index.index[address_b58]),
                         wallet_index_count + 1)

        # Second transaction: spend tokens from output with address=address_b58 and
        # send tokens to 2 outputs, one with address=address_b58 and another one
        # with address=new_address_b58, which is an address of a random wallet
        new_address_b58 = self.get_address(0)
        new_address = decode_address(new_address_b58)

        output1 = TxOutput(value - 100, script)
        script2 = P2PKH.create_output_script(new_address)
        output2 = TxOutput(100, script2)

        input1 = TxInput(tx.hash, 0, b'')
        tx2 = Transaction(weight=1,
                          inputs=[input1],
                          outputs=[output1, output2],
                          parents=parents,
                          storage=self.tx_storage,
                          timestamp=self.last_block.timestamp + 2)

        data_to_sign = tx2.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        input1.data = P2PKH.create_input_data(public_bytes, signature)

        tx2.resolve()
        self.manager.propagate_tx(tx2)

        # tx2 has two outputs, for address_b58 and new_address_b58
        # So we must have one more element on address_b58 index and only one on new_address_b58
        self.assertEqual(len(self.tx_storage.wallet_index.index[address_b58]),
                         wallet_index_count + 2)
        self.assertEqual(
            len(self.tx_storage.wallet_index.index[new_address_b58]), 1)

        # Third transaction: spend tokens from output with address=address_b58 and send
        # tokens to a new address = output3_address_b58, which is from a random wallet
        output3_address_b58 = self.get_address(1)
        output3_address = decode_address(output3_address_b58)
        script3 = P2PKH.create_output_script(output3_address)
        output3 = TxOutput(value - 100, script3)

        input2 = TxInput(tx2.hash, 0, b'')
        tx3 = Transaction(weight=1,
                          inputs=[input2],
                          outputs=[output3],
                          parents=parents,
                          storage=self.tx_storage,
                          timestamp=self.last_block.timestamp + 3)

        data_to_sign = tx3.get_sighash_all()
        public_bytes, signature = self.wallet.get_input_aux_data(
            data_to_sign, self.genesis_private_key)
        input2.data = P2PKH.create_input_data(public_bytes, signature)

        tx3.resolve()
        self.manager.propagate_tx(tx3)

        # tx3 has one output, for another new address (output3_address_b58) and it's spending an output of address_b58
        # So address_b58 index must have one more element and output3_address_b58 should have one element also
        # new_address_b58 was not spent neither received tokens, so didn't change
        self.assertEqual(len(self.tx_storage.wallet_index.index[address_b58]),
                         wallet_index_count + 3)
        self.assertEqual(
            len(self.tx_storage.wallet_index.index[output3_address_b58]), 1)
        self.assertEqual(
            len(self.tx_storage.wallet_index.index[new_address_b58]), 1)

    def test_sighash_cache(self):
        from unittest import mock

        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(5, script)
        tx = Transaction(outputs=[output], storage=self.tx_storage)

        with mock.patch('hathor.transaction.transaction.bytearray') as mocked:
            for _ in range(10):
                tx.get_sighash_all()

            mocked.assert_called_once()

    def test_sighash_data_cache(self):
        from unittest import mock

        address = get_address_from_public_key(self.genesis_public_key)
        script = P2PKH.create_output_script(address)
        output = TxOutput(5, script)
        tx = Transaction(outputs=[output], storage=self.tx_storage)

        with mock.patch('hathor.transaction.transaction.hashlib') as mocked:
            for _ in range(10):
                tx.get_sighash_all_data()

            mocked.sha256.assert_called_once()
Example #15
0
    def test_wallet_create_transaction(self):
        genesis_private_key_bytes = get_private_key_bytes(
            self.genesis_private_key,
            encryption_algorithm=serialization.BestAvailableEncryption(
                PASSWORD))
        genesis_address = get_address_b58_from_public_key(
            self.genesis_public_key)
        # create wallet with genesis block key
        key_pair = KeyPair(private_key_bytes=genesis_private_key_bytes,
                           address=genesis_address,
                           used=True)
        keys = {}
        keys[key_pair.address] = key_pair
        w = Wallet(keys=keys, directory=self.directory)
        w.unlock(PASSWORD)
        genesis_blocks = [
            tx for tx in get_genesis_transactions(None) if tx.is_block
        ]
        genesis_block = genesis_blocks[0]
        genesis_value = sum([output.value for output in genesis_block.outputs])

        # wallet will receive genesis block and store in unspent_tx
        w.on_new_tx(genesis_block)
        for index in range(len(genesis_block.outputs)):
            utxo = w.unspent_txs[settings.HATHOR_TOKEN_UID].get(
                (genesis_block.hash, index))
            self.assertIsNotNone(utxo)
        self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, genesis_value))

        # create transaction spending this value, but sending to same wallet
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 100, timelock=None)
        tx1 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx1.storage = self.storage
        tx1.update_hash()
        self.storage.save_transaction(tx1)
        w.on_new_tx(tx1)
        self.assertEqual(len(w.spent_txs), 1)
        self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, genesis_value))

        # pass inputs and outputs to prepare_transaction, but not the input keys
        # spend output last transaction
        input_info = WalletInputInfo(tx1.hash, 1, None)
        new_address = w.get_unused_address()
        key2 = w.keys[new_address]
        out = WalletOutputInfo(decode_address(key2.address),
                               100,
                               timelock=None)
        tx2 = w.prepare_transaction_incomplete_inputs(Transaction,
                                                      inputs=[input_info],
                                                      outputs=[out],
                                                      tx_storage=self.storage)
        tx2.storage = self.storage
        tx2.update_hash()
        self.storage.save_transaction(tx2)
        w.on_new_tx(tx2)
        self.assertEqual(len(w.spent_txs), 2)
        self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, genesis_value))

        # test keypair exception
        with self.assertRaises(WalletLocked):
            key_pair.get_private_key(None)
Example #16
0
 def test_wallet_keys_storage(self):
     w = Wallet(directory=self.directory)
     # Testing password error not in bytes
     with self.assertRaises(ValueError):
         w.unlock('testpass')
     w.unlock(b'testpass')
     w.generate_keys()
     # Using one address to save used/unused addresses in the file
     w.get_unused_address()
     w._write_keys_to_file()
     # wallet 2 will read from saved file
     w2 = Wallet(directory=self.directory)
     w2._manually_initialize()
     for address, key in w.keys.items():
         key2 = w2.keys.pop(address)
         self.assertEqual(key, key2)
Example #17
0
    def test_locked(self):
        # generate a new block and check if we increase balance
        w = Wallet(directory=self.directory)
        with self.assertRaises(OutOfUnusedAddresses):
            w.get_unused_address()

        # now it should work
        w.unlock(PASSWORD)
        w.get_unused_address()

        # lock wallet and fake that there are no more unused keys
        w.unused_keys = set()
        w.lock()
        with self.assertRaises(OutOfUnusedAddresses):
            w.get_unused_address()

        with self.assertRaises(WalletLocked):
            w.generate_keys()