Ejemplo n.º 1
0
    def test_token_info_not_utf8(self):
        token_name = 'TestCoin'
        token_symbol = 'TST'

        # Token version 1; Name length; Name; Symbol length; Symbol
        bytes1 = (bytes([0x01]) + int_to_bytes(len(token_name), 1) +
                  token_name.encode('utf-8') +
                  int_to_bytes(len(token_symbol), 1) +
                  token_symbol.encode('utf-8'))

        name, symbol, _ = TokenCreationTransaction.deserialize_token_info(
            bytes1)

        self.assertEqual(name, token_name)
        self.assertEqual(symbol, token_symbol)

        encoded_name = token_name.encode('utf-16')
        bytes2 = (bytes([0x01]) + int_to_bytes(len(encoded_name), 1) +
                  encoded_name + int_to_bytes(len(token_symbol), 1) +
                  token_symbol.encode('utf-8'))

        with self.assertRaises(StructError):
            TokenCreationTransaction.deserialize_token_info(bytes2)

        encoded_symbol = token_symbol.encode('utf-16')
        bytes3 = (bytes([0x01]) + int_to_bytes(len(token_name), 1) +
                  token_name.encode('utf-8') +
                  int_to_bytes(len(encoded_symbol), 1) + encoded_symbol)

        with self.assertRaises(StructError):
            TokenCreationTransaction.deserialize_token_info(bytes3)
Ejemplo n.º 2
0
 def test_token_info_serialization(self):
     tx = create_tokens(self.manager, self.address_b58, mint_amount=500)
     info = tx.serialize_token_info()
     # try with version 2
     info2 = bytes([0x02]) + info[1:]
     with self.assertRaises(ValueError):
         TokenCreationTransaction.deserialize_token_info(info2)
Ejemplo n.º 3
0
def tx_or_block_from_proto(
        tx_proto: protos.BaseTransaction,
        storage: Optional['TransactionStorage'] = None) -> 'BaseTransaction':
    from hathor.transaction.block import Block
    from hathor.transaction.merge_mined_block import MergeMinedBlock
    from hathor.transaction.token_creation_tx import TokenCreationTransaction
    from hathor.transaction.transaction import Transaction
    if tx_proto.HasField('transaction'):
        return Transaction.create_from_proto(tx_proto, storage=storage)
    elif tx_proto.HasField('block'):
        if tx_proto.block.HasField('aux_pow'):
            return MergeMinedBlock.create_from_proto(tx_proto, storage=storage)
        else:
            return Block.create_from_proto(tx_proto, storage=storage)
    elif tx_proto.HasField('tokenCreationTransaction'):
        return TokenCreationTransaction.create_from_proto(tx_proto,
                                                          storage=storage)
    else:
        raise ValueError('invalid base_transaction_oneof')
Ejemplo n.º 4
0
def create_tokens(manager: 'HathorManager', address_b58: str = None, mint_amount: int = 300,
                  token_name: str = 'TestCoin', token_symbol: str = 'TTC', propagate: bool = True):
    """Creates a new token and propagates a tx with the following UTXOs:
    0. some tokens (already mint some tokens so they can be transferred);
    1. mint authority;
    2. melt authority;
    3. deposit change;

    :param manager: hathor manager
    :type manager: :class:`hathor.manager.HathorManager`

    :param address_b58: address where tokens will be transferred to
    :type address_b58: string

    :param token_name: the token name for the new token
    :type token_name: str

    :param token_symbol: the token symbol for the new token
    :type token_symbol: str

    :return: the propagated transaction so others can spend their outputs
    """
    genesis = manager.tx_storage.get_all_genesis()
    genesis_blocks = [tx for tx in genesis if tx.is_block]
    genesis_txs = [tx for tx in genesis if not tx.is_block]
    genesis_block = genesis_blocks[0]
    genesis_private_key = get_genesis_key()

    wallet = manager.wallet
    outputs = []

    if address_b58 is None:
        address_b58 = wallet.get_unused_address(mark_as_used=True)
    address = decode_address(address_b58)

    parents = [tx.hash for tx in genesis_txs]
    script = P2PKH.create_output_script(address)
    # deposit input
    deposit_amount = get_deposit_amount(mint_amount)
    deposit_input = TxInput(genesis_block.hash, 0, b'')
    # mint output
    if mint_amount > 0:
        outputs.append(TxOutput(mint_amount, script, 0b00000001))
    # authority outputs
    outputs.append(TxOutput(TxOutput.TOKEN_MINT_MASK, script, 0b10000001))
    outputs.append(TxOutput(TxOutput.TOKEN_MELT_MASK, script, 0b10000001))
    # deposit output
    outputs.append(TxOutput(genesis_block.outputs[0].value - deposit_amount, script, 0))

    tx = TokenCreationTransaction(
        weight=1,
        parents=parents,
        storage=manager.tx_storage,
        inputs=[deposit_input],
        outputs=outputs,
        token_name=token_name,
        token_symbol=token_symbol,
        timestamp=int(manager.reactor.seconds())
    )
    data_to_sign = tx.get_sighash_all(clear_input_data=True)
    public_bytes, signature = wallet.get_input_aux_data(data_to_sign, genesis_private_key)
    tx.inputs[0].data = P2PKH.create_input_data(public_bytes, signature)
    tx.resolve()
    if propagate:
        tx.verify()
        manager.propagate_tx(tx, fails_silently=False)
        manager.reactor.advance(8)
    return tx
Ejemplo n.º 5
0
def create_tokens(manager: 'HathorManager', address_b58: Optional[str] = None, mint_amount: int = 300,
                  token_name: str = 'TestCoin', token_symbol: str = 'TTC', propagate: bool = True,
                  use_genesis: bool = True, nft_data: Optional[str] = None) -> TokenCreationTransaction:
    """Creates a new token and propagates a tx with the following UTXOs:
    0. some tokens (already mint some tokens so they can be transferred);
    1. mint authority;
    2. melt authority;
    3. deposit change;

    :param manager: hathor manager
    :type manager: :class:`hathor.manager.HathorManager`

    :param address_b58: address where tokens will be transferred to
    :type address_b58: string

    :param token_name: the token name for the new token
    :type token_name: str

    :param token_symbol: the token symbol for the new token
    :type token_symbol: str

    :param use_genesis: If True will use genesis outputs to create token, otherwise will use manager wallet
    :type token_symbol: bool

    :param nft_data: If not None we create a first output as the NFT data script
    :type nft_data: str

    :return: the propagated transaction so others can spend their outputs
    """
    wallet = manager.wallet
    assert wallet is not None

    if address_b58 is None:
        address_b58 = wallet.get_unused_address(mark_as_used=True)
    address = decode_address(address_b58)
    script = P2PKH.create_output_script(address)

    deposit_amount = get_deposit_amount(mint_amount)
    if nft_data:
        # NFT creation needs 0.01 HTR of fee
        deposit_amount += 1
    genesis = manager.tx_storage.get_all_genesis()
    genesis_blocks = [tx for tx in genesis if tx.is_block]
    genesis_txs = [tx for tx in genesis if not tx.is_block]
    genesis_block = genesis_blocks[0]
    genesis_private_key = get_genesis_key()

    change_output: Optional[TxOutput]
    parents: List[bytes]
    if use_genesis:
        genesis_hash = genesis_block.hash
        assert genesis_hash is not None
        deposit_input = [TxInput(genesis_hash, 0, b'')]
        change_output = TxOutput(genesis_block.outputs[0].value - deposit_amount, script, 0)
        parents = [cast(bytes, tx.hash) for tx in genesis_txs]
        timestamp = int(manager.reactor.seconds())
    else:
        total_reward = 0
        deposit_input = []
        while total_reward < deposit_amount:
            block = add_new_block(manager, advance_clock=1, address=address)
            deposit_input.append(TxInput(block.hash, 0, b''))
            total_reward += block.outputs[0].value

        if total_reward > deposit_amount:
            change_output = TxOutput(total_reward - deposit_amount, script, 0)
        else:
            change_output = None

        add_blocks_unlock_reward(manager)
        timestamp = int(manager.reactor.seconds())
        parents = manager.get_new_tx_parents(timestamp)

    outputs = []
    if nft_data:
        script_data = DataScript.create_output_script(nft_data)
        output_data = TxOutput(1, script_data, 0)
        outputs.append(output_data)
    # mint output
    if mint_amount > 0:
        outputs.append(TxOutput(mint_amount, script, 0b00000001))
    # authority outputs
    outputs.append(TxOutput(TxOutput.TOKEN_MINT_MASK, script, 0b10000001))
    outputs.append(TxOutput(TxOutput.TOKEN_MELT_MASK, script, 0b10000001))
    # deposit output
    if change_output:
        outputs.append(change_output)

    tx = TokenCreationTransaction(
        weight=1,
        parents=parents,
        storage=manager.tx_storage,
        inputs=deposit_input,
        outputs=outputs,
        token_name=token_name,
        token_symbol=token_symbol,
        timestamp=timestamp
    )
    data_to_sign = tx.get_sighash_all()
    if use_genesis:
        public_bytes, signature = wallet.get_input_aux_data(data_to_sign, genesis_private_key)
    else:
        private_key = wallet.get_private_key(address_b58)
        public_bytes, signature = wallet.get_input_aux_data(data_to_sign, private_key)

    for input_ in tx.inputs:
        input_.data = P2PKH.create_input_data(public_bytes, signature)

    tx.resolve()
    if propagate:
        tx.verify()
        manager.propagate_tx(tx, fails_silently=False)
        manager.reactor.advance(8)
    return tx
Ejemplo n.º 6
0
 def test_token_struct(self):
     tx = create_tokens(self.manager, self.address_b58, mint_amount=500)
     tx2 = TokenCreationTransaction.create_from_struct(tx.get_struct())
     self.assertEqual(tx.hash, tx2.hash)
Ejemplo n.º 7
0
    def test_token_info(self):
        def update_tx(tx):
            """ sighash_all data changes with token name or symbol, so we have to compute signature again
            """
            data_to_sign = tx.get_sighash_all()
            public_bytes, signature = self.manager.wallet.get_input_aux_data(
                data_to_sign, self.genesis_private_key)
            tx.inputs[0].data = P2PKH.create_input_data(
                public_bytes, signature)
            tx.resolve()

        # test token name and symbol
        tx = create_tokens(self.manager, self.address_b58)

        # max token name length
        tx.token_name = 'a' * settings.MAX_LENGTH_TOKEN_NAME
        update_tx(tx)
        tx.verify()

        # max token symbol length
        tx.token_symbol = 'a' * settings.MAX_LENGTH_TOKEN_SYMBOL
        update_tx(tx)
        tx.verify()

        # long token name
        tx.token_name = 'a' * (settings.MAX_LENGTH_TOKEN_NAME + 1)
        update_tx(tx)
        with self.assertRaises(TransactionDataError):
            tx.verify()

        # long token symbol
        tx.token_name = 'ValidName'
        tx.token_symbol = 'a' * (settings.MAX_LENGTH_TOKEN_SYMBOL + 1)
        update_tx(tx)
        with self.assertRaises(TransactionDataError):
            tx.verify()

        # Hathor token name
        tx.token_name = settings.HATHOR_TOKEN_NAME
        tx.token_symbol = 'TST'
        update_tx(tx)
        with self.assertRaises(TransactionDataError):
            tx.verify()

        # Hathor token symbol
        tx.token_name = 'Test'
        tx.token_symbol = settings.HATHOR_TOKEN_SYMBOL
        update_tx(tx)
        with self.assertRaises(TransactionDataError):
            tx.verify()

        # Token name unicode
        tx.token_name = 'Test ∞'
        tx.token_symbol = 'TST'
        token_info = tx.serialize_token_info()
        TokenCreationTransaction.deserialize_token_info(token_info)
        update_tx(tx)
        tx.verify()

        # Token symbol unicode
        tx.token_name = 'Test Token'
        tx.token_symbol = 'TST∞'
        token_info = tx.serialize_token_info()
        TokenCreationTransaction.deserialize_token_info(token_info)
        update_tx(tx)
        tx.verify()
Ejemplo n.º 8
0
    def test_get_one_known_tx_with_authority(self):
        # Tx tesnet 00005f234469407614bf0abedec8f722bb5e534949ad37650f6077c899741ed7
        # We had a bug with this endpoint in this tx because the token_data from inputs
        # was not considering authority mask

        # First add needed data on storage
        tx_hex = (
            '0001010202000023b318c91dcfd4b967b205dc938f9f5e2fd5114256caacfb8f6dd13db330000023b318c91dcfd4b967b20'
            '5dc938f9f5e2fd5114256caacfb8f6dd13db33000006946304402200f7de9e866fbc2d600d6a46eb620fa2d72c9bf032250'
            'f1bb4d241b988182ecfe022002e3010a01ecc539f1f095759642549ca3c626d5603b8efa9499acba0ea13c3621038f962b5'
            '6731fdb26740e04830b63ae5a39e392fd821beef0e99f6d9ae401f201000023b318c91dcfd4b967b205dc938f9f5e2fd511'
            '4256caacfb8f6dd13db33002006946304402200f7de9e866fbc2d600d6a46eb620fa2d72c9bf032250f1bb4d241b988182e'
            'cfe022002e3010a01ecc539f1f095759642549ca3c626d5603b8efa9499acba0ea13c3621038f962b56731fdb26740e0483'
            '0b63ae5a39e392fd821beef0e99f6d9ae401f2010000000100001976a914ee216186a0fad459df6f067f9bfa51ce913e1b0'
            '588ac0000000281001976a914ee216186a0fad459df6f067f9bfa51ce913e1b0588ac4030c398b4620e3161087c07020000'
            '7851af043c11e19f28675b010e8cf4d8da3278f126d2429490a804a7fb2c000023b318c91dcfd4b967b205dc938f9f5e2fd'
            '5114256caacfb8f6dd13db33000020393')
        tx = Transaction.create_from_struct(bytes.fromhex(tx_hex),
                                            self.manager.tx_storage)
        self.manager.tx_storage.save_transaction(tx)

        tx_parent1_hex = (
            '0001010203000023b318c91dcfd4b967b205dc938f9f5e2fd5114256caacfb8f6dd13db330000023b318c91dcfd'
            '4b967b205dc938f9f5e2fd5114256caacfb8f6dd13db33003006a473045022100b1a0293277469636ae5af69703'
            '5c2cba5f15f625814f27938e29ffab8d609ce2022047bce945a30dd498e429b8e73cbce51ef6413c4f4cba5de83'
            '7559dafb754ed45210234490c2447ce61a54cd242b8c24e76fb7e1c6f5313792b33ef75bbc85b3f4302000023b3'
            '18c91dcfd4b967b205dc938f9f5e2fd5114256caacfb8f6dd13db330010069463044022056588e67e0971ab42d6'
            '432d0b758e28247393e0dcfddec6bdb07805655d9948f0220141cc506e7e0c95d672e476498cd0eacd7f62737fe'
            '16f475791eaaa372094e9e21038f962b56731fdb26740e04830b63ae5a39e392fd821beef0e99f6d9ae401f2010'
            '000006401001976a914d937d2c33f04ee680c996ebbc80af79330c4071288ac0000000181001976a914d937d2c3'
            '3f04ee680c996ebbc80af79330c4071288ac0000000800001976a914ed9c36b495444302885969447f0fae5e256'
            '08ef288ac40311513e4fef9d161087be202000023b318c91dcfd4b967b205dc938f9f5e2fd5114256caacfb8f6d'
            'd13db3300038c3d3b69ce90bb88c0c4d6a87b9f0c349e5b10c9b7ce6714f996e512ac16400021261'
        )
        tx_parent1 = Transaction.create_from_struct(
            bytes.fromhex(tx_parent1_hex), self.manager.tx_storage)
        self.manager.tx_storage.save_transaction(tx_parent1)

        tx_parent2_hex = (
            '000201040000476810205cb3625d62897fcdad620e01d66649869329640f5504d77e960d01006a473045022100c'
            'e2ce57330c77b5599e2d044686338a1d55faca50d3436359a60be81654db2d00220574e78eebf7c97f57cde9468'
            '323aacc2f0abeadc84f69ca6fa2485b99eac3ac62102b4efd2d336030d430b37c3b287ae6de6c2bd5aced0d5e03'
            '653de6e33c18e4ebe0000006401001976a91481ec322ae3282046d833013529da8d5dcbfb30bf88ac0000000181'
            '001976a91481ec322ae3282046d833013529da8d5dcbfb30bf88ac0000000281001976a91481ec322ae3282046d'
            '833013529da8d5dcbfb30bf88ac0000000900001976a914fe96dc8cd6ed2e8fda3ce6fe12e4714195c215b888ac'
            '010757617420776174035741544030e34594da5bdd6108740d020038c3d3b69ce90bb88c0c4d6a87b9f0c349e5b'
            '10c9b7ce6714f996e512ac1640000476810205cb3625d62897fcdad620e01d66649869329640f5504d77e960d00'
            '00d810')
        tx_parent2_bytes = bytes.fromhex(tx_parent2_hex)
        tx_parent2 = TokenCreationTransaction.create_from_struct(
            tx_parent2_bytes, self.manager.tx_storage)
        self.manager.tx_storage.save_transaction(tx_parent2)

        # Both inputs are the same as the last parent, so no need to manually add them

        # XXX: this is completely dependant on MemoryTokensIndex implementation
        token_bytes1 = bytes.fromhex(
            '000023b318c91dcfd4b967b205dc938f9f5e2fd5114256caacfb8f6dd13db330')
        status = self.manager.tx_storage.indexes.tokens._tokens[token_bytes1]
        status.name = 'Wat wat'
        status.symbol = 'WAT'

        response = yield self.web.get(
            "transaction", {
                b'id':
                b'00005f234469407614bf0abedec8f722bb5e534949ad37650f6077c899741ed7'
            })
        data = response.json_value()

        self.assertEqual(len(data['tx']['inputs']), 2)
        self.assertEqual(len(data['tx']['outputs']), 2)
        self.assertEqual(len(data['tx']['tokens']), 1)

        # Inputs token data
        self.assertEqual(data['tx']['inputs'][0]['token_data'], 1)
        self.assertEqual(data['tx']['inputs'][0]['decoded']['token_data'], 1)
        self.assertEqual(data['tx']['inputs'][1]['token_data'], 129)
        self.assertEqual(data['tx']['inputs'][1]['decoded']['token_data'], 129)

        # Outputs token data
        self.assertEqual(data['tx']['outputs'][0]['token_data'], 0)
        self.assertEqual(data['tx']['outputs'][0]['decoded']['token_data'], 0)
        self.assertEqual(data['tx']['outputs'][1]['token_data'], 129)
        self.assertEqual(data['tx']['outputs'][1]['decoded']['token_data'],
                         129)