Пример #1
0
    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()
Пример #2
0
    def test_checkdatasig_raise_on_uncompressed_pubkey(self):
        block = self.genesis_blocks[0]
        data = b'some_random_data'

        from hathor.transaction import Transaction, TxInput, TxOutput
        txin = TxInput(tx_id=block.hash, index=0, data=b'')
        txout = TxOutput(value=block.outputs[0].value, script=b'')
        tx = Transaction(inputs=[txin], outputs=[txout])

        import hashlib
        data_to_sign = tx.get_sighash_all()
        hashed_data = hashlib.sha256(data_to_sign).digest()
        signature = self.genesis_private_key.sign(hashed_data,
                                                  ec.ECDSA(hashes.SHA256()))

        from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
        pubkey_uncompressed = self.genesis_public_key.public_bytes(
            Encoding.X962, PublicFormat.UncompressedPoint)
        # ScriptError if pubkey is not a valid compressed public key
        # with wrong signature
        stack = [data, b'123', pubkey_uncompressed]
        with self.assertRaises(ScriptError):
            op_checkdatasig(stack, log=[], extras=None)
        # or with rigth one
        # this will make sure the signature is not made when parameters are wrong
        stack = [data, signature, pubkey_uncompressed]
        with self.assertRaises(ScriptError):
            op_checkdatasig(stack, log=[], extras=None)
    def test_match_values(self):
        pubkey_hash = '6o6ul2c+sqAariBVW+CwNaSJb9w='
        pubkey = 'Awmloohhey8WhajdDURgvbk1z3JHX2vxDSBjz9uG9wEp'
        # ./hathor-cli oracle-encode-data str:some_id int:1543974403 int:100
        oracle_data = 'B3NvbWVfaWQEXAcuAwFk'
        oracle_signature = 'MEYCIQC5cyg1tOY4oyPZ5KY7ugWJGRShrsSPxr8AxxyuvO5PYwIhAOxHBDMid7aRXe' \
                           '+85rIaDPI2ussIcw54avaFWfT9svSp'

        address = base58.b58decode(self.get_address(0))

        # they should be the same
        nc = NanoContractMatchValues(base64.b64decode(pubkey_hash), 1543970403,
                                     'some_id'.encode('utf-8'), {address: 100})
        script = nc.create_output_script()
        nc2 = NanoContractMatchValues.parse_script(script)
        self.assertIsNotNone(nc2)
        self.assertEqual(json.dumps(nc.to_human_readable()),
                         json.dumps(nc2.to_human_readable()))

        # if we add some more bytes, parsing should not match
        script2 = script + b'00'
        nc3 = NanoContractMatchValues.parse_script(script2)
        self.assertIsNone(nc3)

        # test script eval is true
        input_data = NanoContractMatchValues.create_input_data(
            base64.b64decode(oracle_data), base64.b64decode(oracle_signature),
            base64.b64decode(pubkey))
        txin = TxInput(b'aa', 0, input_data)
        spent_tx = Transaction(outputs=[TxOutput(20, script)])
        tx = Transaction(
            outputs=[TxOutput(20, P2PKH.create_output_script(address))])
        script_eval(tx, txin, spent_tx)
Пример #4
0
    def test_checksig(self):
        with self.assertRaises(MissingStackItems):
            op_checksig([1], log=[], extras=None)

        block = self.genesis_blocks[0]

        from hathor.transaction import Transaction, TxInput, TxOutput
        txin = TxInput(tx_id=block.hash, index=0, data=b'')
        txout = TxOutput(value=block.outputs[0].value, script=b'')
        tx = Transaction(inputs=[txin], outputs=[txout])

        import hashlib
        data_to_sign = tx.get_sighash_all()
        hashed_data = hashlib.sha256(data_to_sign).digest()
        signature = self.genesis_private_key.sign(hashed_data,
                                                  ec.ECDSA(hashes.SHA256()))
        pubkey_bytes = get_public_key_bytes_compressed(self.genesis_public_key)

        extras = ScriptExtras(tx=tx, txin=None, spent_tx=None)

        # wrong signature puts False (0) on stack
        stack = [b'aaaaaaaaa', pubkey_bytes]
        op_checksig(stack, log=[], extras=extras)
        self.assertEqual(0, stack.pop())

        stack = [signature, pubkey_bytes]
        op_checksig(stack, log=[], extras=extras)
        self.assertEqual(1, stack.pop())
Пример #5
0
    def update_voided_info(self, tx: Transaction) -> None:
        """ This method should be called only once when the transactions is added to the DAG.
        """
        assert tx.hash is not None
        assert tx.storage is not None

        voided_by: Set[bytes] = set()

        # Union of voided_by of parents
        for parent in tx.get_parents():
            parent_meta = parent.get_metadata()
            if parent_meta.voided_by:
                voided_by.update(parent_meta.voided_by)

        # Union of voided_by of inputs
        for txin in tx.inputs:
            spent_tx = tx.storage.get_transaction(txin.tx_id)
            spent_meta = spent_tx.get_metadata()
            if spent_meta.voided_by:
                voided_by.update(spent_meta.voided_by)

        # Update accumulated weight of the transactions voiding us.
        assert tx.hash not in voided_by
        for h in voided_by:
            tx2 = tx.storage.get_transaction(h)
            tx2_meta = tx2.get_metadata()
            tx2_meta.accumulated_weight = sum_weights(
                tx2_meta.accumulated_weight, tx.weight)
            assert tx2.storage is not None
            tx2.storage.save_transaction(tx2, only_metadata=True)

        # Then, we add ourselves.
        meta = tx.get_metadata()
        assert not meta.voided_by or meta.voided_by == {tx.hash}
        assert meta.accumulated_weight == tx.weight
        if meta.conflict_with:
            voided_by.add(tx.hash)

        if voided_by:
            meta.voided_by = voided_by.copy()
            tx.storage.save_transaction(tx, only_metadata=True)
            tx.storage._del_from_cache(tx)  # XXX: accessing private method

        # Check conflicts of the transactions voiding us.
        for h in voided_by:
            if h == tx.hash:
                continue
            conflict_tx = tx.storage.get_transaction(h)
            if not conflict_tx.is_block:
                assert isinstance(conflict_tx, Transaction)
                self.check_conflicts(conflict_tx)

        # Finally, check our conflicts.
        meta = tx.get_metadata()
        if meta.voided_by == {tx.hash}:
            self.check_conflicts(tx)
Пример #6
0
    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()
Пример #7
0
    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()
Пример #8
0
    def _test_txout_script_limit(self, offset):
        genesis_block = self.genesis_blocks[0]
        _input = TxInput(genesis_block.hash, 0, b'')

        value = genesis_block.outputs[0].value
        script = b'*' * (settings.MAX_OUTPUT_SCRIPT_SIZE + offset)
        _output = TxOutput(value, script)

        tx = Transaction(inputs=[_input],
                         outputs=[_output],
                         storage=self.tx_storage)
        tx.verify_outputs()
Пример #9
0
    def render_POST(self, request):
        """ Creates a nano contract tx and returns it in hexadecimal format.

        Post data should be a json with the following items:
        values: List[{'address', 'value'}], with bet address and value
        fallback_address: if none of the addresses above is the winner, this address
                          can execute the contract
        oracle_pubkey_hash: oracle's public key hashed
        oracle_data_id: oracle's id about this nano contract
        total_value: nano contract total value
        input_value: amount this wallet should stake in the nano contract

        :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'POST')

        try:
            data = json.loads(request.content.read().decode('utf-8'))
        except json.JSONDecodeError:
            return json.dumps({'success': False, 'message': 'Invalid format for post data'}).encode('utf-8')

        for param in PARAMS_POST:
            if param not in data:
                return get_missing_params_msg(param)

        try:
            decoded_params = self.decode_post_params(data)
        except ValueError as e:
            return json.dumps({'success': False, 'message': e.message}).encode('utf-8')

        nano_contract = NanoContractMatchValues(
            decoded_params.oracle_pubkey_hash, decoded_params.min_timestamp, decoded_params.oracle_data_id,
            decoded_params.value_dict, decoded_params.fallback_address
        )

        tx_outputs = []
        tx_outputs.append(TxOutput(decoded_params.total_value, nano_contract.create_output_script()))

        inputs, total_inputs_amount = self.manager.wallet.get_inputs_from_amount(
            decoded_params.input_value,
            self.manager.tx_storage
        )
        change_tx = self.manager.wallet.handle_change_tx(total_inputs_amount, decoded_params.input_value)
        if change_tx:
            tx_outputs.append(TxOutput(change_tx.value, P2PKH.create_output_script(change_tx.address)))
        tx_inputs = [TxInput(txin.tx_id, txin.index, b'') for txin in inputs]

        tx = Transaction(inputs=tx_inputs, outputs=tx_outputs)

        ret = {'success': True, 'hex_tx': tx.get_struct().hex()}
        return json.dumps(ret).encode('utf-8')
Пример #10
0
    def _test_txin_data_limit(self, offset):
        genesis_block = self.genesis_blocks[0]
        data = b'*' * (settings.MAX_INPUT_DATA_SIZE + offset)
        _input = TxInput(genesis_block.hash, 0, data)

        value = genesis_block.outputs[0].value
        _output = TxOutput(value, b'')

        tx = Transaction(timestamp=int(self.manager.reactor.seconds()) + 1,
                         inputs=[_input],
                         outputs=[_output],
                         storage=self.tx_storage)
        tx.verify_inputs(skip_script=True)
Пример #11
0
    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()
Пример #12
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()
Пример #13
0
    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()
Пример #14
0
    def test_find_p2pkh(self):
        with self.assertRaises(MissingStackItems):
            op_find_p2pkh([], log=[], extras=None)

        addr1 = '15d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'
        addr2 = '1K35zJQeYrVzQAW7X3s7vbPKmngj5JXTBc'
        addr3 = '1MnHN3D41yaMN5WLLKPARRdF77USvPLDfy'

        import base58
        out1 = P2PKH.create_output_script(base58.b58decode(addr1))
        out2 = P2PKH.create_output_script(base58.b58decode(addr2))
        out3 = P2PKH.create_output_script(base58.b58decode(addr3))

        # read genesis keys
        genesis_address = get_address_from_public_key(self.genesis_public_key)
        out_genesis = P2PKH.create_output_script(genesis_address)

        from hathor.transaction import Transaction, TxOutput, TxInput
        spent_tx = Transaction(outputs=[TxOutput(1, b'nano_contract_code')])
        txin = TxInput(b'dont_care', 0, b'data')

        # try with just 1 output
        stack = [genesis_address]
        tx = Transaction(outputs=[TxOutput(1, out_genesis)])
        extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx)
        op_find_p2pkh(stack, log=[], extras=extras)
        self.assertEqual(stack.pop(), 1)

        # several outputs and correct output among them
        stack = [genesis_address]
        tx = Transaction(outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(1, out_genesis), TxOutput(1, out3)])
        extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx)
        op_find_p2pkh(stack, log=[], extras=extras)
        self.assertEqual(stack.pop(), 1)

        # several outputs without correct amount output
        stack = [genesis_address]
        tx = Transaction(outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(2, out_genesis), TxOutput(1, out3)])
        extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx)
        with self.assertRaises(VerifyFailed):
            op_find_p2pkh(stack, log=[], extras=extras)

        # several outputs without correct address output
        stack = [genesis_address]
        tx = Transaction(outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(1, out3)])
        extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx)
        with self.assertRaises(VerifyFailed):
            op_find_p2pkh(stack, log=[], extras=extras)
Пример #15
0
    def test_twin(self):
        # Normal twin
        params = ['--raw_tx', self.tx.get_struct().hex()]
        args = self.parser.parse_args(params)

        f = StringIO()
        with capture_logs():
            with redirect_stdout(f):
                execute(args)

        # Transforming prints str in array
        output = f.getvalue().strip().splitlines()

        twin_tx = Transaction.create_from_struct(bytes.fromhex(output[0]))
        # Parents are the same but in different order
        self.assertEqual(twin_tx.parents[0], self.tx.parents[1])
        self.assertEqual(twin_tx.parents[1], self.tx.parents[0])

        # Testing metadata creation from json
        meta_before_conflict = self.tx.get_metadata()
        meta_before_conflict_json = meta_before_conflict.to_json()
        del meta_before_conflict_json['conflict_with']
        del meta_before_conflict_json['voided_by']
        del meta_before_conflict_json['twins']
        new_meta = TransactionMetadata.create_from_json(meta_before_conflict_json)
        self.assertEqual(meta_before_conflict, new_meta)

        self.manager.propagate_tx(twin_tx)

        # Validate they are twins
        meta = self.tx.get_metadata(force_reload=True)
        self.assertEqual(meta.twins, [twin_tx.hash])

        meta2 = twin_tx.get_metadata()
        self.assertFalse(meta == meta2)
Пример #16
0
    def test_balance_update3(self):
        # Tx2 is twin with tx1 with higher acc weight, so tx1 will get voided

        # Start balance
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        # Change of parents only, so it's a twin.
        # With higher weight, so the balance will continue because tx2 will be the winner
        tx2 = Transaction.create_from_struct(self.tx1.get_struct())
        tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]]
        tx2.weight = 13
        tx2.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)
        self.run_to_completion()

        meta1 = self.tx1.get_metadata(force_reload=True)
        self.assertEqual(meta1.twins, [tx2.hash])
        self.assertEqual(meta1.voided_by, {self.tx1.hash})

        meta2 = tx2.get_metadata()
        self.assertEqual(meta2.voided_by, None)

        # Balance is the same
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))
Пример #17
0
    def test_balance_update2(self):
        # Tx2 is twin with tx1 with equal acc weight, so both will get voided

        # Start balance
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        # Change of parents only, so it's a twin.
        # Same weight, so both will be voided then the balance increases
        tx2 = Transaction.create_from_struct(self.tx1.get_struct())
        tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]]
        tx2.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)
        self.run_to_completion()

        meta1 = self.tx1.get_metadata(force_reload=True)
        self.assertEqual(meta1.twins, [tx2.hash])
        self.assertEqual(meta1.voided_by, {self.tx1.hash})

        meta2 = tx2.get_metadata()
        self.assertEqual(meta2.voided_by, {tx2.hash})

        # Balance changed
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, sum(self.blocks_tokens[:3])))
Пример #18
0
 def test_spend_tx_by_script(self):
     src_tx = self.unspent_tx
     address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
     script = create_base_script(address).get_script()
     script_str = base64.b64encode(script).decode('utf-8')
     resp = (yield self.web.post(
         'create_tx', {
             'inputs': [{
                 'tx_id': src_tx.hash_hex,
                 'index': 1,
             }],
             'outputs': [{
                 'script': script_str,
                 'value': 100,
             }]
         })).json_value()
     self.assertEqual(resp['success'], True)
     data = resp['data']
     hex_data = resp['hex_data']
     struct_bytes = bytes.fromhex(hex_data)
     tx = Transaction.create_from_struct(struct_bytes)
     tx_data = tx.to_json()
     del tx_data['hash']
     del tx_data['nonce']
     self.assertEqual(data, tx_data)
     self.assertEqual(len(tx.inputs), 1)
     self.assertEqual(tx.inputs[0].tx_id, src_tx.hash)
     self.assertEqual(tx.inputs[0].index, 1)
     self.assertEqual(tx.inputs[0].data, b'')
     self.assertEqual(len(tx.outputs), 1)
     self.assertEqual(tx.outputs[0].value, 100)
     self.assertEqual(tx.outputs[0].token_data, 0)
     self.assertEqual(tx.outputs[0].script, script)
        def setUp(self):
            super().setUp()

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

            data = b'This is a test block.'
            self.blocks = add_new_blocks(self.manager, 3, advance_clock=15, block_data=data)

            address = self.get_address(0)
            value = 100

            outputs = [
                WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None)
            ]

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

            # Change of parents only, so it's a twin.
            # With less weight, so the balance will continue because tx1 will be the winner
            self.tx2 = Transaction.create_from_struct(self.tx1.get_struct())
            self.tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]]
            self.tx2.weight = 9
            self.tx2.resolve()

            # Propagate a conflicting twin transaction
            self.manager.propagate_tx(self.tx2)
Пример #20
0
 def test_tx_propagate(self):
     _set_test_mode(
         TestMode.DISABLED)  # disable test_mode so the weight is not 1
     src_tx = self.unspent_tx
     output_address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
     resp = (yield self.web.post(
         'create_tx', {
             'inputs': [{
                 'tx_id': src_tx.hash_hex,
                 'index': 1,
             }],
             'outputs': [{
                 'address': output_address,
                 'value': 100,
             }]
         })).json_value()
     self.assertEqual(resp['success'], True)
     data = resp['data']
     hex_data = resp['hex_data']
     struct_bytes = bytes.fromhex(hex_data)
     orig_tx = Transaction.create_from_struct(struct_bytes)
     tx = orig_tx.clone()
     tx_data = tx.to_json()
     del tx_data['hash']
     del tx_data['nonce']
     self.assertEqual(data, tx_data)
     data_to_sign = tx.get_sighash_all()
     private_key = self.manager.wallet.get_private_key(self.unspent_address)
     public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
         data_to_sign, private_key)
     input_data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
     tx.inputs[0].data = input_data
     # XXX: tx.resolve is a bit CPU intensive, but not so much as to make this test disabled by default
     tx.resolve(False)
     self.assertTrue(self.manager.propagate_tx(tx))
Пример #21
0
 def test_spend_block(self):
     block = self.unspent_blocks[0]
     address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
     script = create_base_script(address).get_script()
     resp = (yield self.web.post('create_tx', {
         'inputs': [
             {
                 'tx_id': block.hash_hex,
                 'index': 0,
             }
         ],
         'outputs': [
             {
                 'address': address,
                 'value': 6400,
             }
         ]
     })).json_value()
     self.assertEqual(resp['success'], True)
     data = resp['data']
     hex_data = resp['hex_data']
     struct_bytes = bytes.fromhex(hex_data)
     tx = Transaction.create_from_struct(struct_bytes)
     tx_data = tx.to_json()
     del tx_data['hash']
     del tx_data['nonce']
     self.assertEqual(data, tx_data)
     self.assertEqual(len(tx.inputs), 1)
     self.assertEqual(tx.inputs[0].tx_id, block.hash)
     self.assertEqual(tx.inputs[0].index, 0)
     self.assertEqual(tx.inputs[0].data, b'')
     self.assertEqual(len(tx.outputs), 1)
     self.assertEqual(tx.outputs[0].value, 6400)
     self.assertEqual(tx.outputs[0].token_data, 0)
     self.assertEqual(tx.outputs[0].script, script)
Пример #22
0
    def _render_POST(self, tx: Transaction, request: Request) -> None:
        """ Resolves the request without stratum
            The transaction is completed and then sent to be mined in a thread
        """
        if tx.inputs:
            max_ts_spent_tx = max(tx.get_spent_tx(txin).timestamp for txin in tx.inputs)
            # Set tx timestamp as max between tx and inputs
            tx.timestamp = max(max_ts_spent_tx + 1, tx.timestamp)

        # Set parents
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)

        deferred = threads.deferToThreadPool(reactor, self.manager.pow_thread_pool,
                                             self._render_POST_thread, tx, request)
        deferred.addCallback(self._cb_tx_resolve, request)
        deferred.addErrback(self._err_tx_resolve, request)
Пример #23
0
    def test_balance_update1(self):
        # Tx2 is twin with tx1 but less acc weight, so it will get voided

        # Start balance
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        # Change of parents only, so it's a twin.
        # With less weight, so the balance will continue because tx1 will be the winner
        tx2 = Transaction.create_from_struct(self.tx1.get_struct())
        tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]]
        tx2.weight = 9
        tx2.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)
        self.run_to_completion()

        meta1 = self.tx1.get_metadata(force_reload=True)
        self.assertEqual(meta1.twins, [tx2.hash])

        meta2 = tx2.get_metadata(force_reload=True)
        self.assertEqual(meta2.voided_by, {tx2.hash})

        # Balance is the same
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        # Voided wallet history
        index_voided = 0
        output_voided = tx2.outputs[index_voided]
        address = output_voided.to_human_readable()['address']
        voided_unspent = UnspentTx(tx2.hash,
                                   index_voided,
                                   output_voided.value,
                                   tx2.timestamp,
                                   address,
                                   output_voided.token_data,
                                   voided=True)
        self.assertEqual(len(self.manager.wallet.voided_unspent), 1)
        voided_utxo = self.manager.wallet.voided_unspent.get(
            (voided_unspent.tx_id, index_voided))
        self.assertIsNotNone(voided_utxo)
        self.assertEqual(voided_utxo.to_dict(), voided_unspent.to_dict())

        input_voided = tx2.inputs[0]
        key = (input_voided.tx_id, input_voided.index)
        voided_spent = SpentTx(tx2.hash,
                               input_voided.tx_id,
                               input_voided.index,
                               self.blocks_tokens[0],
                               tx2.timestamp,
                               voided=True)
        self.assertEqual(len(self.manager.wallet.voided_spent), 1)
        self.assertEqual(len(self.manager.wallet.voided_spent[key]), 1)
        self.assertEqual(self.manager.wallet.voided_spent[key][0].to_dict(),
                         voided_spent.to_dict())
Пример #24
0
    def test_unknown_authority(self):
        wallet = self.manager.wallet
        tx = create_tokens(self.manager, self.address_b58, mint_amount=500)
        token_uid = tx.tokens[0]
        parents = self.manager.get_new_tx_parents()
        script = P2PKH.create_output_script(self.address)

        # try an unknown authority
        input1 = TxInput(tx.hash, 1, b'')
        input2 = TxInput(tx.hash, 2, b'')
        output = TxOutput((TxOutput.ALL_AUTHORITIES << 1), script, 0b10000001)
        tx2 = Transaction(weight=1,
                          inputs=[input1, input2],
                          outputs=[output],
                          parents=parents,
                          tokens=[token_uid],
                          storage=self.manager.tx_storage,
                          timestamp=int(self.clock.seconds()))
        data_to_sign = tx2.get_sighash_all()
        public_bytes, signature = wallet.get_input_aux_data(
            data_to_sign, wallet.get_private_key(self.address_b58))
        data = P2PKH.create_input_data(public_bytes, signature)
        tx2.inputs[0].data = data
        tx2.inputs[1].data = data
        tx2.resolve()
        with self.assertRaises(InvalidToken):
            tx2.verify()
Пример #25
0
    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()
Пример #26
0
    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()
Пример #27
0
    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 execute(args: Namespace, priv_key_password: str) -> None:
    from hathor.transaction import Transaction
    from hathor.wallet.util import generate_signature

    tx = Transaction.create_from_struct(bytes.fromhex(args.partial_tx))
    assert isinstance(tx, Transaction)

    signature = generate_signature(tx, bytes.fromhex(args.private_key), password=priv_key_password.encode())
    print('Signature: ', signature.hex())
Пример #29
0
    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(clear_input_data=True)
        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()
Пример #30
0
    def test_tips_twin(self):
        add_new_blocks(self.manager, 6, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        self.assertEqual(
            len(self.manager.tx_storage.indexes.mempool_tips.get()), 0)

        tx1 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        tx2 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        tx3 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # 3 txs and the last one is still a tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx3.hash]))

        # A new tx with custom parents, so tx3 and tx4 will become two tips
        tx4 = add_new_transactions(self.manager,
                                   1,
                                   advance_clock=1,
                                   propagate=False)[0]
        tx4.parents = [tx1.hash, tx2.hash]
        tx4.resolve()
        self.manager.propagate_tx(tx4, fails_silently=False)
        self.manager.reactor.advance(10)
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx4.hash, tx3.hash]))

        # A twin tx with tx4, that will be voided initially, then won't change the tips
        tx5 = Transaction.create_from_struct(tx4.get_struct())
        tx5.parents = [tx2.hash, tx3.hash]
        tx5.resolve()
        self.manager.propagate_tx(tx5)
        self.manager.reactor.advance(10)

        # tx4 and tx5 are twins, so both are voided
        self.assertIsNotNone(tx4.get_metadata(force_reload=True).voided_by)
        self.assertIsNotNone(tx5.get_metadata(force_reload=True).voided_by)
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx3.hash]))

        # add new tx confirming tx5, which will become valid and tx4 becomes voided
        tx6 = add_new_transactions(self.manager,
                                   1,
                                   advance_clock=1,
                                   propagate=False)[0]
        tx6.parents = [tx5.hash, tx2.hash]
        tx6.resolve()
        self.manager.propagate_tx(tx6, fails_silently=False)
        self.manager.reactor.advance(10)
        self.assertIsNotNone(tx4.get_metadata(force_reload=True).voided_by)
        self.assertIsNone(tx5.get_metadata(force_reload=True).voided_by)

        # tx6 is the only one left
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx6.hash]))