Esempio n. 1
0
    def prepare_transaction(self,
                            cls: ABCMeta,
                            inputs: List[WalletInputInfo],
                            outputs: List[WalletOutputInfo],
                            timestamp: Optional[int] = None) -> Transaction:
        """Prepares the tx inputs and outputs.

        Can be used to create blocks by passing empty list to inputs.

        :param cls: defines if we're creating a Transaction or Block
        :type cls: :py:class:`hathor.transaction.Block` or :py:class:`hathor.transaction.Transaction`

        :param inputs: the tx inputs
        :type inputs: List[WalletInputInfo]

        :param outputs: the tx outputs
        :type inputs: List[WalletOutputInfo]

        :param timestamp: timestamp to use for the transaction
        :type timestamp: int
        """
        tx_outputs = []
        token_dict: Dict[bytes, int] = {}  # Dict[token_uid, index]
        tokens = []  # List[bytes] = List[token_uid]
        for txout in outputs:
            token_uid = bytes.fromhex(txout.token_uid)
            if token_uid == settings.HATHOR_TOKEN_UID:
                token_index = 0
            elif token_uid in token_dict:
                token_index = token_dict[token_uid]
            else:
                tokens.append(token_uid)
                token_index = len(tokens)
                token_dict[token_uid] = token_index

            timelock = int_to_bytes(txout.timelock,
                                    4) if txout.timelock else None
            tx_outputs.append(
                TxOutput(txout.value,
                         create_output_script(txout.address, timelock),
                         token_index))

        tx_inputs = []
        private_keys = []
        for wtxin in inputs:
            private_keys.append(wtxin.private_key)
            tx_inputs.append(TxInput(wtxin.tx_id, wtxin.index, b''))

        tx = cls(inputs=tx_inputs,
                 outputs=tx_outputs,
                 tokens=tokens,
                 timestamp=timestamp)
        data_to_sign = tx.get_sighash_all(clear_input_data=True)

        for txin, privkey in zip(tx.inputs, private_keys):
            public_key_bytes, signature = self.get_input_aux_data(
                data_to_sign, privkey)
            txin.data = P2PKH.create_input_data(public_key_bytes, signature)

        return tx
Esempio n. 2
0
def from_raw_output(raw_output: Dict, tokens: List[bytes]) -> TxOutput:
    value = raw_output['value']
    token_uid = raw_output.get('token_uid')
    if token_uid is not None:
        if token_uid not in tokens:
            tokens.append(token_uid)
        token_data = tokens.index(token_uid) + 1
    else:
        token_data = 0
    raw_script = raw_output.get('script')
    if raw_script:
        script = base64.b64decode(raw_script)
    else:
        address = decode_address(raw_output['address'])
        script = create_output_script(address)
    return TxOutput(value, script, token_data)
Esempio n. 3
0
    def test_multisig_base_script(self):
        import base58

        addrs = [
            'hJBjKVU5HqfbNbikgfdhDmmBe9TMwx9Eft',
        ]

        for addr in addrs:
            script = create_base_script(addr)
            self.assertIsInstance(script, MultiSig)
            self.assertEqual(script.get_type(), 'MultiSig')
            self.assertEqual(script.get_address(), addr)
            self.assertEqual(script.get_timelock(), None)
            baddress = base58.b58decode(addr)
            script2 = create_output_script(baddress)
            self.assertEqual(script2, script.get_script())
Esempio n. 4
0
    def test_p2pkh_base_script(self):
        import base58

        addrs = [
            'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A',
            'HGov979VaeyMQ92ubYcnVooP6qPzUJU8Ro',
        ]

        for addr in addrs:
            script = create_base_script(addr)
            self.assertIsInstance(script, P2PKH)
            self.assertEqual(script.get_type(), 'P2PKH')
            self.assertEqual(script.get_address(), addr)
            self.assertEqual(script.get_timelock(), None)
            baddress = base58.b58decode(addr)
            script2 = create_output_script(baddress)
            self.assertEqual(script2, script.get_script())
    def generate_mining_block(
        self,
        rng: Random,
        merge_mined: bool = False,
        address: Optional[bytes] = None,
        timestamp: Optional[int] = None,
        data: Optional[bytes] = None,
        storage: Optional[TransactionStorage] = None,
        include_metadata: bool = False,
    ) -> Union[Block, MergeMinedBlock]:
        """ Generates a block by filling the template with the given options and random parents (if multiple choices).

        Note that if a timestamp is given it will be coerced into the [timestamp_min, timestamp_max] range.
        """
        # XXX: importing these here to try to contain hathor dependencies as much as possible
        from hathor.transaction import TransactionMetadata, TxOutput
        from hathor.transaction.scripts import create_output_script

        parents = list(self.get_random_parents(rng))
        output_script = create_output_script(
            address) if address is not None else b''
        base_timestamp = timestamp if timestamp is not None else self.timestamp_now
        block_timestamp = min(max(base_timestamp, self.timestamp_min),
                              self.timestamp_max)
        tx_outputs = [TxOutput(self.reward, output_script)]
        cls: Union[
            Type['Block'],
            Type['MergeMinedBlock']] = MergeMinedBlock if merge_mined else Block
        block = cls(outputs=tx_outputs,
                    parents=parents,
                    timestamp=block_timestamp,
                    data=data or b'',
                    storage=storage,
                    weight=self.weight)
        if include_metadata:
            block._metadata = TransactionMetadata(height=self.height,
                                                  score=self.score)
        block.get_metadata(use_storage=False)
        return block
Esempio n. 6
0
    def test_spend_multisig(self):
        # Adding funds to the wallet
        # XXX: note further down the test, 20.00 HTR will be used, block_count must yield at least that amount
        block_count = 3  # 3 * 8.00 -> 24.00 HTR is enough
        blocks = add_new_blocks(self.manager, block_count, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        blocks_tokens = [sum(txout.value for txout in blk.outputs) for blk in blocks]
        available_tokens = sum(blocks_tokens)
        self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, available_tokens))

        # First we send tokens to a multisig address
        block_reward = blocks_tokens[0]
        outputs = [WalletOutputInfo(address=self.multisig_address, value=block_reward, timelock=None)]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(Transaction, outputs, self.manager.tx_storage)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.manager.propagate_tx(tx1)
        self.clock.advance(10)

        wallet_balance = WalletBalance(0, available_tokens - block_reward)
        self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], wallet_balance)

        # Then we create a new tx that spends this tokens from multisig wallet
        tx = Transaction.create_from_struct(tx1.get_struct())
        tx.weight = 10
        tx.parents = self.manager.get_new_tx_parents()
        tx.timestamp = int(self.clock.seconds())

        multisig_script = create_output_script(self.multisig_address)

        multisig_output = TxOutput(200, multisig_script)
        wallet_output = TxOutput(300, create_output_script(self.address))
        outside_output = TxOutput(block_reward - 200 - 300, create_output_script(self.outside_address))

        tx.outputs = [multisig_output, wallet_output, outside_output]

        tx_input = TxInput(tx1.hash, 0, b'')
        tx.inputs = [tx_input]

        signatures = []
        for private_key_hex in self.private_keys:
            signature = generate_signature(tx, bytes.fromhex(private_key_hex), password=b'1234')
            signatures.append(signature)

        parser = create_parser()
        # Generate spend tx
        args = parser.parse_args([
            tx.get_struct().hex(), '{},{}'.format(signatures[0].hex(), signatures[1].hex()),
            self.redeem_script.hex()
        ])
        f = StringIO()
        with capture_logs():
            with redirect_stdout(f):
                execute(args)
        # Transforming prints str in array
        output = f.getvalue().strip().splitlines()

        tx_raw = output[0].split(':')[1].strip()

        tx = Transaction.create_from_struct(bytes.fromhex(tx_raw))
        self.assertTrue(self.manager.propagate_tx(tx, False))
Esempio n. 7
0
    def test_spend_multisig(self):
        # Adding funds to the wallet
        blocks = add_new_blocks(self.manager, 2, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, sum(blk.outputs[0].value for blk in blocks)))

        first_block_amount = blocks[0].outputs[0].value

        # First we send tokens to a multisig address
        outputs = [
            WalletOutputInfo(address=self.multisig_address,
                             value=first_block_amount,
                             timelock=int(self.clock.seconds()) + 15)
        ]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.manager.propagate_tx(tx1)
        self.clock.advance(10)

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, first_block_amount))

        # Then we create a new tx that spends this tokens from multisig wallet
        tx = Transaction.create_from_struct(tx1.get_struct())
        tx.weight = 10
        tx.parents = self.manager.get_new_tx_parents()
        tx.timestamp = int(self.clock.seconds())

        multisig_script = create_output_script(self.multisig_address)

        multisig_output = TxOutput(200, multisig_script)
        wallet_output = TxOutput(300, create_output_script(self.address))
        outside_output = TxOutput(first_block_amount - 200 - 300,
                                  create_output_script(self.outside_address))

        tx.outputs = [multisig_output, wallet_output, outside_output]

        tx_input = TxInput(tx1.hash, 0, b'')
        tx.inputs = [tx_input]

        signatures = []
        for private_key_hex in self.private_keys:
            signature = generate_signature(tx,
                                           bytes.fromhex(private_key_hex),
                                           password=b'1234')
            signatures.append(signature)

        input_data = MultiSig.create_input_data(self.redeem_script, signatures)
        tx.inputs[0].data = input_data

        tx.resolve()
        # Transaction is still locked
        self.assertFalse(self.manager.propagate_tx(tx))

        self.clock.advance(6)
        tx.timestamp = int(self.clock.seconds())
        tx.resolve()

        # First we try to propagate with a P2PKH input
        private_key_obj = get_private_key_from_bytes(bytes.fromhex(
            self.private_keys[0]),
                                                     password=b'1234')
        pubkey_obj = private_key_obj.public_key()
        public_key_compressed = get_public_key_bytes_compressed(pubkey_obj)
        p2pkh_input_data = P2PKH.create_input_data(public_key_compressed,
                                                   signatures[0])
        tx2 = Transaction.create_from_struct(tx.get_struct())
        tx2.inputs[0].data = p2pkh_input_data
        tx2.resolve()
        self.assertFalse(self.manager.propagate_tx(tx2))

        # Now we propagate the correct
        self.assertTrue(self.manager.propagate_tx(tx))

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, first_block_amount + 300))

        # Testing the MultiSig class methods
        cls_script = parse_address_script(multisig_script)
        self.assertTrue(isinstance(cls_script, MultiSig))
        self.assertEqual(cls_script.address, self.multisig_address_b58)

        expected_dict = {
            'type': 'MultiSig',
            'address': self.multisig_address_b58,
            'timelock': None
        }
        self.assertEqual(cls_script.to_human_readable(), expected_dict)

        script_eval(tx, tx_input, tx1)

        # Script error
        with self.assertRaises(ScriptError):
            create_output_script(
                base58.b58decode('55d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'))
Esempio n. 8
0
    def generate_mining_block(
            self,
            timestamp: Optional[float] = None,
            parent_block_hash: Optional[bytes] = None,
            data: bytes = b'',
            address: Optional[bytes] = None,
            merge_mined: bool = False) -> Union[Block, MergeMinedBlock]:
        """ Generates a block ready to be mined. The block includes new issued tokens,
        parents, and the weight.

        :return: A block ready to be mined
        :rtype: :py:class:`hathor.transaction.Block`
        """
        from hathor.transaction.scripts import create_output_script

        if not timestamp:
            timestamp = max(self.tx_storage.latest_timestamp,
                            self.reactor.seconds())

        if parent_block_hash is None:
            tip_blocks = self.tx_storage.get_best_block_tips(timestamp)
        else:
            tip_blocks = [parent_block_hash]

        parent_block = self.tx_storage.get_transaction(
            random.choice(tip_blocks))
        if not parent_block.is_genesis and timestamp - parent_block.timestamp > settings.MAX_DISTANCE_BETWEEN_BLOCKS:
            timestamp = parent_block.timestamp + settings.MAX_DISTANCE_BETWEEN_BLOCKS

        assert timestamp is not None
        tip_txs = self.get_new_tx_parents(timestamp - 1)

        assert len(tip_blocks) >= 1
        assert len(tip_txs) == 2

        parents = [parent_block.hash] + tip_txs

        parents_tx = [self.tx_storage.get_transaction(x) for x in parents]

        timestamp1 = int(timestamp)
        timestamp2 = max(x.timestamp for x in parents_tx) + 1

        if address is None:
            if self.wallet is None:
                raise ValueError(
                    'No wallet available and no mining address given')
            address = self.wallet.get_unused_address_bytes(mark_as_used=False)
        height = parent_block.get_metadata().height + 1
        amount = self.get_tokens_issued_per_block(height)
        output_script = create_output_script(address)
        tx_outputs = [TxOutput(amount, output_script)]

        cls: Union[Type['Block'], Type['MergeMinedBlock']]
        if merge_mined:
            cls = MergeMinedBlock
        else:
            cls = Block
        blk = cls(outputs=tx_outputs,
                  parents=parents,
                  storage=self.tx_storage,
                  data=data)
        blk.timestamp = max(timestamp1, timestamp2)
        blk.weight = self.calculate_block_difficulty(blk)
        return blk
Esempio n. 9
0
    def test_get(self):
        # Mining new block
        response_mining = yield self.web_mining.get('mining')
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(
            block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post(
            'mining',
            {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})

        # Unlocking wallet
        self.manager.wallet.unlock(b'MYPASS')

        # Creating a valid transaction to be pushed to the network
        blocks = add_new_blocks(self.manager, 3, advance_clock=2)
        add_blocks_unlock_reward(self.manager)
        tx_id = blocks[0].hash
        output = blocks[0].outputs[0]
        script_type_out = parse_address_script(output.script)
        address = script_type_out.address
        private_key = self.manager.wallet.get_private_key(address)

        output_address = decode_address(self.get_address(0))
        value = self.manager.get_tokens_issued_per_block(1)
        o = TxOutput(value, create_output_script(output_address, None))
        i = TxInput(tx_id, 0, b'')
        tx = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)
        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs = [i]
        tx.timestamp = int(self.clock.seconds())
        tx.weight = self.manager.minimum_tx_weight(tx)
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)
        tx.resolve()

        response = yield self.web.get(
            'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Sending token to random address without input
        data_json = {
            'outputs': [{
                'address': self.get_address(0),
                'value': 5
            }],
            'inputs': []
        }
        yield self.web_tokens.post('wallet/send_tokens', {'data': data_json})

        # modify tx so it will be a double spending, then rejected
        tx.weight += 0.1
        tx.resolve()
        response_success = yield self.web.get(
            'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')})
        data_success = response_success.json_value()
        self.assertFalse(data_success['success'])

        # Invalid tx (don't have inputs)
        genesis_tx = get_genesis_transactions(self.manager.tx_storage)[1]
        response_genesis = yield self.web.get(
            'push_tx',
            {b'hex_tx': bytes(genesis_tx.get_struct().hex(), 'utf-8')})
        data_genesis = response_genesis.json_value()
        self.assertFalse(data_genesis['success'])

        # Invalid hex
        response_error1 = yield self.web.get('push_tx', {b'hex_tx': b'XXXX'})
        data_error1 = response_error1.json_value()

        self.assertFalse(data_error1['success'])

        # Invalid tx hex
        response_error2 = yield self.web.get('push_tx', {b'hex_tx': b'a12c'})
        data_error2 = response_error2.json_value()

        self.assertFalse(data_error2['success'])

        # Token creation tx
        tx2 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=False)
        response = yield self.web.get(
            'push_tx', {b'hex_tx': bytes(tx2.get_struct().hex(), 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])
Esempio n. 10
0
    def _run_push_tx_test(self, is_post: bool) -> Generator:
        # Mining new block
        response_mining = yield self.web_mining.get('mining')
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post('mining', {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})

        # Unlocking wallet
        self.manager.wallet.unlock(b'MYPASS')

        # Creating a valid transaction to be pushed to the network
        blocks = add_new_blocks(self.manager, 3, advance_clock=2)
        add_blocks_unlock_reward(self.manager)
        tx_id = blocks[0].hash
        output = blocks[0].outputs[0]
        script_type_out = parse_address_script(output.script)
        assert script_type_out is not None
        address = script_type_out.address
        private_key = self.manager.wallet.get_private_key(address)

        script_out_addr = self.get_address(0)
        assert script_out_addr is not None
        output_address = decode_address(script_out_addr)
        value = self.manager.get_tokens_issued_per_block(1)
        o = TxOutput(value, create_output_script(output_address, None))
        i = TxInput(tx_id, 0, b'')
        tx = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key)
        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs = [i]
        tx.timestamp = int(self.clock.seconds())
        tx.weight = self.manager.minimum_tx_weight(tx)
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)
        tx.resolve()

        push_tx_fn = self.web.post if is_post else self.web.get
        hex_param = 'hex_tx' if is_post else b'hex_tx'
        force_param = 'force' if is_post else b'force'

        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')

        response = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Sending token to random address without input
        data_json = {'outputs': [{'address': self.get_address(0), 'value': 5}], 'inputs': []}
        yield self.web_tokens.post('wallet/send_tokens', {'data': data_json})

        # modify tx so it will be a double spending, then rejected
        tx.weight += 0.1
        tx.resolve()

        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')
        response_success = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data_success = response_success.json_value()
        self.assertFalse(data_success['success'])

        # invalid transaction, without forcing
        tx.timestamp = 5
        tx.inputs = [TxInput(blocks[1].hash, 0, b'')]
        script_type_out = parse_address_script(blocks[1].outputs[0].script)
        assert script_type_out is not None
        private_key = self.manager.wallet.get_private_key(script_type_out.address)
        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key)
        tx.inputs[0].data = P2PKH.create_input_data(public_key_bytes, signature_bytes)

        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')
        response = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data = response.json_value()
        self.assertFalse(data['success'])

        # force
        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')
        response = yield push_tx_fn('push_tx', {hex_param: hex_data, force_param: True if is_post else b'true'})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Invalid tx (don't have inputs)
        genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis() if x.is_transaction)
        genesis_hex = genesis_tx.get_struct().hex()
        hex_data = genesis_hex if is_post else bytes(genesis_hex, 'utf-8')
        response_genesis = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data_genesis = response_genesis.json_value()
        self.assertFalse(data_genesis['success'])

        # Invalid tx hex
        invalid_hex_data = 'a12c' if is_post else b'a12c'
        response_error2 = yield push_tx_fn('push_tx', {hex_param: invalid_hex_data})
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Token creation tx
        tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=False)
        tx2_hex = tx2.get_struct().hex()
        hex_data = tx2_hex if is_post else bytes(tx2_hex, 'utf-8')
        response = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data = response.json_value()
        self.assertTrue(data['success'])
Esempio n. 11
0
    def test_post(self):
        # Unlocking wallet
        self.manager.wallet.unlock(b'MYPASS')

        blocks = add_new_blocks(self.manager, 3, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        blocks_tokens = [
            sum(txout.value for txout in blk.outputs) for blk in blocks
        ]

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID].available,
            sum(blocks_tokens))

        # Options
        yield self.web.options('thin_wallet/send_tokens')

        tx_id = blocks[0].hash
        output = blocks[0].outputs[0]
        script_type_out = parse_address_script(output.script)
        address = script_type_out.address
        private_key = self.manager.wallet.get_private_key(address)

        output_address = decode_address(self.get_address(0))
        value = blocks_tokens[0]
        o = TxOutput(value, create_output_script(output_address, None))
        o_invalid_amount = TxOutput(value - 1,
                                    create_output_script(output_address, None))
        i = TxInput(tx_id, 0, b'')

        # wrong weight
        tx = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)

        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs = [i]
        tx.timestamp = int(self.clock.seconds())
        tx.weight = 0

        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx.get_struct().hex()})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Error wrong amount
        tx2 = Transaction(inputs=[i], outputs=[o_invalid_amount])

        data_to_sign = tx2.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)

        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx2.inputs = [i]
        tx2.timestamp = int(self.clock.seconds())
        tx2.weight = self.manager.minimum_tx_weight(tx2)

        response_wrong_amount = yield self.web.post(
            'thin_wallet/send_tokens', {'tx_hex': tx2.get_struct().hex()})
        data_wrong_amount = response_wrong_amount.json_value()
        self.assertFalse(data_wrong_amount['success'])

        # successful tx
        tx3 = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx3.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)

        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx3.inputs = [i]
        tx3.timestamp = int(self.clock.seconds())
        tx3.weight = self.manager.minimum_tx_weight(tx3)

        # Then send tokens
        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx3.get_struct().hex()})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Trying to send a double spending will not have success
        self.clock.advance(5)
        tx3.timestamp = int(self.clock.seconds())
        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx3.get_struct().hex()})
        data_error = response.json_value()
        self.assertFalse(data_error['success'])
        self.clock.advance(5)

        # Check if tokens were really sent
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID].available,
            sum(blocks_tokens[:-1]))

        response_history = yield self.web_address_history.get(
            'thin_wallet/address_history', {
                b'addresses[]': address.encode(),
            })

        response_data = response_history.json_value()['history']
        self.assertIn(data['tx']['hash'], [x['tx_id'] for x in response_data])

        # Create token tx
        tx4 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=False)
        tx4.nonce = 0
        tx4.timestamp = int(self.clock.seconds())
        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx4.get_struct().hex()})
        data = response.json_value()
        self.assertTrue(data['success'])
Esempio n. 12
0
    def test_token_history(self):
        self.manager.wallet.unlock(b'MYPASS')
        resource = StubSite(TokenHistoryResource(self.manager))

        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = create_tokens(self.manager,
                           mint_amount=100,
                           token_name='Teste',
                           token_symbol='TST')
        token_uid = tx.tokens[0]

        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': 3
        })
        data = response.json_value()
        # Success returning the token creation tx
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get('thin_wallet/token_history', {
            b'id': b'123',
            b'count': 3
        })
        data = response.json_value()
        # Fail because token is unknown
        self.assertFalse(data['success'])

        # Create a tx with this token, so we can have more tx in the history
        output = tx.outputs[0]
        script_type_out = parse_address_script(output.script)
        address = script_type_out.address
        private_key = self.manager.wallet.get_private_key(address)

        output_address = decode_address(self.get_address(0))
        o = TxOutput(100, create_output_script(output_address, None), 1)
        i = TxInput(tx.hash, 0, b'')

        tx2 = Transaction(inputs=[i], outputs=[o], tokens=[token_uid])
        data_to_sign = tx2.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)
        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx2.inputs = [i]
        tx2.timestamp = int(self.clock.seconds())
        tx2.weight = self.manager.minimum_tx_weight(tx2)
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.resolve()
        self.manager.propagate_tx(tx2)

        # Now we have 2 txs with this token
        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': 3
        })
        data = response.json_value()
        # Success returning the token creation tx and newly created tx
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(2, len(data['transactions']))
        self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id'])
        self.assertEqual(tx.hash.hex(), data['transactions'][1]['tx_id'])

        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': 1
        })
        data = response.json_value()
        # Testing has_more
        self.assertTrue(data['success'])
        self.assertTrue(data['has_more'])
        self.assertEqual(1, len(data['transactions']))

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': 10,
                b'page': b'next',
                b'hash': tx2.hash.hex().encode(),
                b'timestamp': str(tx2.timestamp).encode(),
            })
        data = response.json_value()
        # Testing next
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': 10,
                b'page': b'previous',
                b'hash': tx.hash.hex().encode(),
                b'timestamp': str(tx.timestamp).encode(),
            })
        data = response.json_value()
        # Testing previous
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': 10,
                b'page': b'previous',
                b'hash': tx2.hash.hex().encode(),
                b'timestamp': str(tx2.timestamp).encode(),
            })
        data = response.json_value()
        # Testing previous from first
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(0, len(data['transactions']))