예제 #1
0
    def test_get_tips_histogram(self):
        # Add blocks to have funds
        add_new_blocks(self.manager, 2, 2)
        add_blocks_unlock_reward(self.manager)

        txs = add_new_transactions(self.manager, 10, 2)

        response1 = yield self.web.get("tips-histogram", {
            b'begin': txs[0].timestamp,
            b'end': txs[0].timestamp
        })
        data1 = response1.json_value()
        self.assertEqual(len(data1), 1)
        self.assertEqual([txs[0].timestamp, 1], data1[0])

        response2 = yield self.web.get("tips-histogram", {
            b'begin': txs[0].timestamp,
            b'end': txs[0].timestamp + 1
        })
        data2 = response2.json_value()
        self.assertEqual(len(data2), 2)
        self.assertEqual([txs[0].timestamp, 1], data2[0])
        self.assertEqual([txs[0].timestamp + 1, 1], data2[1])

        response3 = yield self.web.get("tips-histogram", {
            b'begin': txs[0].timestamp,
            b'end': txs[-1].timestamp
        })
        data3 = response3.json_value()
        self.assertEqual(len(data3), 19)
예제 #2
0
    def setUp(self):
        super().setUp()

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

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

        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')
        self.token_uid = tx.tokens[0]

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

        # Using token creation address as search address
        # Token creation address has change output for the genesis (1B - 0.01 HTR of token deposit)
        self.address_bytes = decode_address(self.address)
        add_new_blocks(self.manager,
                       5,
                       advance_clock=1,
                       address=self.address_bytes)
예제 #3
0
    def test_get_tips(self):
        genesis_txs = [
            tx for tx in self.manager.tx_storage.get_all_genesis()
            if not tx.is_block
        ]

        # Tips are only the genesis
        response1 = yield self.web.get("tips")
        data1 = response1.json_value()
        self.assertTrue(data1['success'])
        self.assertEqual(len(data1['tips']), len(genesis_txs))

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

        # Add blocks to have funds
        add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)

        # Add one tx, now you have only one tip
        tx = add_new_transactions(self.manager, 1)[0]

        response2 = yield self.web.get("tips")
        data2 = response2.json_value()
        self.assertTrue(data2['success'])
        self.assertEqual(len(data2['tips']), 1)

        # Getting tips sending timestamp as parameter
        response3 = yield self.web.get("tips",
                                       {b'timestamp': tx.timestamp - 1})
        data3 = response3.json_value()
        self.assertEqual(len(data3), 2)
예제 #4
0
        def setUp(self):
            super().setUp()

            self.manager = self.create_peer(network='testnet')

            self.hashes_before = set()
            for genesis in self.manager.tx_storage.get_all_genesis():
                self.hashes_before.add(genesis.hash)

            self.blocks_before = add_new_blocks(self.manager,
                                                3,
                                                advance_clock=1)
            self.blocks_before.extend(add_blocks_unlock_reward(self.manager))
            self.txs_before = add_new_transactions(self.manager, 5)
            for block in self.blocks_before:
                self.hashes_before.add(block.hash)
            for tx in self.txs_before:
                self.hashes_before.add(tx.hash)

            address = self.get_address(0)
            self.root_tx = add_new_tx(self.manager, address=address, value=100)

            self.blocks_after = add_blocks_unlock_reward(self.manager)
            self.txs_after = add_new_transactions(self.manager, 5)
            self.blocks_after.extend(
                add_new_blocks(self.manager, 3, advance_clock=1))

            self.hashes_after = set()
            for block in self.blocks_after:
                self.hashes_after.add(block.hash)
            for tx in self.txs_after:
                self.hashes_after.add(tx.hash)
예제 #5
0
    def test_accumulated_weight_indirect_block(self):
        """ All new blocks belong to case (i).
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine 3 blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        tx_list = add_new_transactions(manager, 20, advance_clock=15)

        # Mine more 2 blocks in a row with no transactions between them
        blocks = add_new_blocks(manager, 2, weight=8)

        tx0 = tx_list[0]
        for block in blocks:
            self.assertNotIn(tx0.hash, block.parents)

        # All transactions and blocks should be verifying tx_list[0] directly or
        # indirectly.
        expected = 0
        for tx in tx_list:
            expected = sum_weights(expected, tx.weight)
        for block in blocks:
            expected = sum_weights(expected, block.weight)

        meta = tx0.update_accumulated_weight()
        self.assertAlmostEqual(meta.accumulated_weight, expected)
    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 redirect_stdout(f):
            execute(args, '123')
        # Transforming prints str in array
        output = f.getvalue().split('\n')
        # Last element is always empty string
        output.pop()

        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())))
    def setUp(self):
        super().setUp()

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

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

        address = self.get_address(0)
        value = 100

        self.initial_balance = sum(self.blocks_tokens[:3]) - 100

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

        add_blocks_unlock_reward(self.manager)

        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)
        self.run_to_completion()
예제 #8
0
    def test_dont_revert_block_high_weight_transaction_verify_other(self):
        """ A conflict transaction will be propagated and voided. But this transaction
        verifies its conflicting transaction. So, its accumulated weight will always be smaller
        than the others and it will never be executed.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager)
        meta = conflicting_tx.get_metadata()
        self.assertEqual(len(meta.conflict_with), 1)
        self.assertIn(list(meta.conflict_with)[0], conflicting_tx.parents)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        # These blocks will be voided later.
        blocks2 = add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a high weight.
        b0 = manager.generate_mining_block()
        b0.parents = [
            blocks[-1].hash, conflicting_tx.hash, conflicting_tx.parents[0]
        ]
        # b0.parents = [b0.parents[0], conflicting_tx.hash, conflicting_tx.parents[0]]
        b0.weight = 10
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot2')

        self.assertNotEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        for block in blocks2:
            meta = block.get_metadata()
            self.assertIsNone(meta.voided_by)

        self.assertConsensusValid(manager)
예제 #9
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]))
예제 #10
0
    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()

        # Get sighashall is different with and without data
        self.assertNotEqual(tx.get_sighash_all(), tx.get_sighash_all(clear_input_data=False))

        # 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()
예제 #11
0
    def test_dont_revert_block_low_weight(self):
        """ A conflict transaction will be propagated and voided.
        A new block with low weight will verify it, which won't be enough to flip to executed.
        So, it will remain voided.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager,
                                                 use_same_parents=True)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})
        for parent_hash in conflicting_tx.parents:
            self.assertNotIn(parent_hash, meta.conflict_with)

        # These blocks will be voided later.
        add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a low weight.
        # So, it is not enough to revert and this block will be voided as well.
        b0 = manager.generate_mining_block()
        b0.parents = [
            blocks[-1].hash, conflicting_tx.hash, conflicting_tx.parents[0]
        ]
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot1')

        self.assertNotEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        b0_meta = b0.get_metadata()
        self.assertEqual(b0_meta.voided_by, {b0.hash, conflicting_tx.hash})

        self.assertConsensusValid(manager)
예제 #12
0
    def test_dont_revert_block_high_weight_verify_both(self):
        """ A conflicting transaction will be propagated and voided. But the block with high weight
        verifies both the conflicting transactions, so this block will always be voided.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager,
                                                 use_same_parents=True)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})
        for parent_hash in conflicting_tx.parents:
            self.assertNotIn(parent_hash, meta.conflict_with)

        # Add two blocks.
        blocks2 = add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a high weight.
        b0 = manager.generate_mining_block()
        b0.parents = [
            b0.parents[0], conflicting_tx.hash, conflicting_tx.parents[0]
        ]
        b0.weight = 10
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot3')

        self.assertNotEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        for block in blocks2:
            meta = block.get_metadata()
            self.assertIsNone(meta.voided_by)

        self.assertConsensusValid(manager)
예제 #13
0
 def test_insuficient_funds(self):
     add_blocks_unlock_reward(self.manager)
     # create transaction spending some value
     new_address = self.wallet.get_unused_address()
     out = WalletOutputInfo(decode_address(new_address),
                            self.TOKENS,
                            timelock=None)
     with self.assertRaises(InsufficientFunds):
         self.wallet.prepare_transaction_compute_inputs(
             Transaction, [out], self.tx_storage)
예제 #14
0
    def setUp(self):
        super().setUp()

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

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

        self.parser = create_parser()
예제 #15
0
    def test_token(self):
        resource = StubSite(TokenResource(self.manager))

        # test invalid token id
        response = yield resource.get('thin_wallet/token',
                                      {b'id': 'vvvv'.encode()})
        data = response.json_value()
        self.assertFalse(data['success'])

        # test missing token id
        response = yield resource.get('thin_wallet/token')
        data = response.json_value()
        self.assertFalse(data['success'])

        # test unknown token id
        unknown_uid = '00000000228ed1dd74a2e1b920c1d64bf81dc63875dce4fac486001073b45a27'.encode(
        )
        response = yield resource.get('thin_wallet/token',
                                      {b'id': unknown_uid})
        data = response.json_value()
        self.assertFalse(data['success'])

        # test success case
        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        token_name = 'MyTestToken'
        token_symbol = 'MTT'
        amount = 150
        tx = create_tokens(self.manager,
                           mint_amount=amount,
                           token_name=token_name,
                           token_symbol=token_symbol)
        token_uid = tx.tokens[0]
        response = yield resource.get('thin_wallet/token',
                                      {b'id': token_uid.hex().encode()})
        data = response.json_value()
        self.assertTrue(data['success'])
        self.assertEqual(len(data['mint']), 1)
        self.assertEqual(len(data['melt']), 1)
        self.assertEqual(data['mint'][0]['tx_id'], tx.hash_hex)
        self.assertEqual(data['melt'][0]['tx_id'], tx.hash_hex)
        self.assertEqual(data['mint'][0]['index'], 1)
        self.assertEqual(data['melt'][0]['index'], 2)
        self.assertEqual(data['total'], amount)
        self.assertEqual(data['name'], token_name)
        self.assertEqual(data['symbol'], token_symbol)

        # test no wallet index
        manager2 = self.create_peer(self.network, unlock_wallet=True)
        resource2 = StubSite(TokenResource(manager2))
        response2 = yield resource2.get('thin_wallet/token')
        data2 = response2.json_value()
        self.assertEqual(response2.responseCode, 503)
        self.assertFalse(data2['success'])
예제 #16
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(CreateTxResource(self.manager))
     self.manager.wallet.unlock(b'MYPASS')
     self.spent_blocks = add_new_blocks(self.manager, 10)
     self.unspent_blocks = add_blocks_unlock_reward(self.manager)
     add_blocks_unlock_reward(self.manager)
     self.unspent_address = self.manager.wallet.get_unused_address()
     self.unspent_tx = add_new_tx(self.manager, self.unspent_address, 100)
     self.unspent_tx2 = add_new_tx(self.manager, self.unspent_address, 200)
     self.unspent_tx3 = add_new_tx(self.manager, self.unspent_address, 300)
     add_blocks_unlock_reward(self.manager)
예제 #17
0
    def test_prepare_transaction(self):
        block = add_new_block(self.manager, advance_clock=5)
        w = self.manager.wallet
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 1, timelock=None)
        with self.assertRaises(InsufficientFunds):
            w.prepare_transaction_compute_inputs(Transaction, [out],
                                                 self.storage,
                                                 timestamp=block.timestamp)

        # now it should work
        add_blocks_unlock_reward(self.manager)
        w.prepare_transaction_compute_inputs(Transaction, [out], self.storage)
예제 #18
0
    def test_tips_winner(self):
        add_new_block(self.manager, 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]
        # tx1 will be the tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx1.hash]))

        tx2 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx2 will be the tip now
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx2.hash]))

        tx3 = Transaction.create_from_struct(tx2.get_struct())
        tx3.parents = [tx2.parents[1], tx2.parents[0]]
        tx3.resolve()

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

        meta1 = tx2.get_metadata(force_reload=True)
        self.assertEqual(meta1.conflict_with, [tx3.hash])
        self.assertEqual(meta1.voided_by, {tx2.hash})
        self.assertEqual(meta1.twins, [tx3.hash])
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx1.hash]))

        self.manager.reactor.advance(10)

        # Creating a new block that confirms tx3, then is will become valid and voiding tx2
        new_block = add_new_block(self.manager, propagate=False)
        new_block.parents = [new_block.parents[0], tx1.hash, tx3.hash]
        new_block.resolve()
        new_block.verify()
        self.manager.propagate_tx(new_block, fails_silently=False)

        self.manager.reactor.advance(10)

        self.assertIsNone(
            self.manager.tx_storage.get_metadata(tx3.hash).voided_by)
        self.assertIsNotNone(
            self.manager.tx_storage.get_metadata(tx2.hash).voided_by)
        # The block confirms tx3, so it's not a tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(), set())
예제 #19
0
    def test_choose_inputs(self):
        blocks = add_new_blocks(self.manager, 1, advance_clock=15)
        blocks_tokens = [
            sum(txout.value for txout in blk.outputs) for blk in blocks
        ]
        add_blocks_unlock_reward(self.manager)

        address = self.manager.wallet.get_unused_address(mark_as_used=False)

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=blocks_tokens[0],
                             timelock=int(self.clock.seconds()) + 10)
        ]

        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(1)

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

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

        with self.assertRaises(InsufficientFunds):
            self.manager.wallet.prepare_transaction_compute_inputs(
                Transaction, outputs, self.manager.tx_storage)

        self.clock.advance(10)

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

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, blocks_tokens[0]))
예제 #20
0
    def test_script_too_big(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        tx = self.get_tx()

        # Invalid tx (output script is too long)
        tx.outputs[0].script = b'*' * (settings.PUSHTX_MAX_OUTPUT_SCRIPT_SIZE +
                                       1)
        tx.resolve()
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertFalse(data['success'])
        self.assertEqual('Transaction is non standard.', data['message'])
예제 #21
0
    def test_non_standard_script(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        tx = self.get_tx()

        # Invalid tx (output script is too long)
        tx.outputs[0].script = b'*' * 5
        tx.resolve()
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertFalse(data['success'])
        expected = 'Transaction is non standard.'
        self.assertEqual(expected, data['message'])
예제 #22
0
    def test_get_one(self):
        genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis()
                          if x.is_block)
        response_success = yield self.web.get(
            "transaction", {b'id': bytes(genesis_tx.hash.hex(), 'utf-8')})
        data_success = response_success.json_value()
        self.assertTrue(data_success['success'])
        dict_test = genesis_tx.to_json(decode_script=True)
        dict_test['raw'] = genesis_tx.get_struct().hex()
        dict_test['nonce'] = str(dict_test['nonce'])
        if genesis_tx.is_block:
            dict_test['height'] = genesis_tx.calculate_height()
        self.assertEqual(data_success['tx'], dict_test)

        # Test sending hash that does not exist
        response_error1 = yield self.web.get(
            "transaction", {
                b'id':
                b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a5533848'
            })
        data_error1 = response_error1.json_value()
        self.assertFalse(data_error1['success'])

        # Test sending invalid hash
        response_error2 = yield self.web.get(
            "transaction", {
                b'id':
                b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a553384'
            })
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Adding blocks to have funds
        add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = add_new_transactions(self.manager, 1)[0]

        tx2 = Transaction.create_from_struct(tx.get_struct())
        tx2.parents = [tx.parents[1], tx.parents[0]]
        tx2.resolve()

        self.manager.propagate_tx(tx2)

        # Now we get a tx with conflict, voided_by and twin
        response_conflict = yield self.web.get(
            "transaction", {b'id': bytes(tx2.hash.hex(), 'utf-8')})
        data_conflict = response_conflict.json_value()
        self.assertTrue(data_conflict['success'])
예제 #23
0
    def setUp(self):
        super().setUp()
        self.tx_storage = ModifiedTransactionMemoryStorage()
        self.network = 'testnet'
        self.manager = self.create_peer(self.network,
                                        tx_storage=self.tx_storage)

        self.all_hashes = set()
        for tx in self.manager.tx_storage.get_all_transactions():
            self.all_hashes.add(tx.hash)

        # generate blocks and transactions where blk1 is spent by tx1
        self.blk1 = add_new_block(self.manager, advance_clock=15)
        self.block_list = add_blocks_unlock_reward(self.manager)

        self.tx_list = add_new_transactions(self.manager, 5, advance_clock=15)
        self.tx1 = self.tx_list[0]
        self.assertTrue(self.tx1.inputs[0].tx_id == self.blk1.hash)

        self.block_list2 = add_new_blocks(self.manager, 8, advance_clock=15)

        # collect all hashes
        self.all_hashes.add(self.blk1.hash)
        self.all_hashes.update(x.hash for x in self.block_list)
        self.all_hashes.update(x.hash for x in self.tx_list)
        self.all_hashes.update(x.hash for x in self.block_list2)
예제 #24
0
    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()
예제 #25
0
    def test_maybe_spent_txs(self):
        add_new_block(self.manager, advance_clock=15)
        blocks = add_blocks_unlock_reward(self.manager)
        w = self.manager.wallet
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 1, timelock=None)
        tx1 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        self.assertEqual(len(tx1.inputs), 1)
        _input = tx1.inputs[0]
        key = (_input.tx_id, _input.index)
        self.assertNotIn(key, w.unspent_txs[settings.HATHOR_TOKEN_UID])
        self.assertIn(key, w.maybe_spent_txs[settings.HATHOR_TOKEN_UID])
        self.run_to_completion()
        self.assertIn(key, w.unspent_txs[settings.HATHOR_TOKEN_UID])
        self.assertEqual(0, len(w.maybe_spent_txs[settings.HATHOR_TOKEN_UID]))

        # when we receive the new tx it will remove from maybe_spent
        tx2 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx2.storage = self.manager.tx_storage
        tx2.timestamp = max(
            tx2.get_spent_tx(txin).timestamp for txin in tx2.inputs) + 1
        tx2.parents = self.manager.get_new_tx_parents(tx2.timestamp)
        tx2.weight = 1
        tx2.timestamp = blocks[-1].timestamp + 1
        tx2.resolve()
        self.assertTrue(self.manager.on_new_tx(tx2, fails_silently=False))
        self.clock.advance(2)
        self.assertEqual(0, len(w.maybe_spent_txs[settings.HATHOR_TOKEN_UID]))
예제 #26
0
    def setUp(self):
        super().setUp()
        self.manager = self.create_peer('testnet', unlock_wallet=True, wallet_index=True)

        self.genesis = self.manager.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]

        self.address_b58 = self.manager.wallet.get_unused_address()
        self.address = decode_address(self.address_b58)

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

        # add some blocks so we can spend the genesis outputs
        add_blocks_unlock_reward(self.manager)
예제 #27
0
 def test_push_nft(self) -> Generator:
     self.manager.wallet.unlock(b'MYPASS')
     blocks = add_new_blocks(self.manager, 5, advance_clock=15)
     add_blocks_unlock_reward(self.manager)
     # NFT creation tx
     script_type_out = parse_address_script(blocks[0].outputs[0].script)
     assert script_type_out is not None
     address = script_type_out.address
     tx3 = create_tokens(self.manager,
                         address,
                         mint_amount=100,
                         propagate=False,
                         nft_data='test')
     tx3_hex = tx3.get_struct().hex()
     response = yield self.push_tx({'hex_tx': tx3_hex})
     data = response.json_value()
     self.assertTrue(data['success'])
예제 #28
0
    def test_create_token_transaction(self):
        add_new_block(self.manager, advance_clock=5)
        add_blocks_unlock_reward(self.manager)
        tx = create_tokens(self.manager)

        tokens_created = tx.outputs[0].value
        token_uid = tx.tokens[0]
        address_b58 = self.manager.wallet.get_unused_address()
        address = decode_address(address_b58)

        _, hathor_balance = self.manager.wallet.balance[
            settings.HATHOR_TOKEN_UID]
        # prepare tx with hathors and another token
        # hathor tx
        hathor_out = WalletOutputInfo(address, hathor_balance, None)
        # token tx
        token_out = WalletOutputInfo(address, tokens_created - 20, None,
                                     token_uid.hex())

        tx2 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, [hathor_out, token_out])
        tx2.storage = self.manager.tx_storage
        tx2.timestamp = tx.timestamp + 1
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.resolve()
        tx2.verify()

        self.assertNotEqual(len(tx2.inputs), 0)
        token_dict = defaultdict(int)
        for _input in tx2.inputs:
            output_tx = self.manager.tx_storage.get_transaction(_input.tx_id)
            output = output_tx.outputs[_input.index]
            token_uid = output_tx.get_token_uid(output.get_token_index())
            token_dict[token_uid] += output.value

        # make sure balance is the same and we've checked both balances
        did_enter = 0
        for token_uid, value in token_dict.items():
            if token_uid == settings.HATHOR_TOKEN_UID:
                self.assertEqual(value, hathor_balance)
                did_enter += 1
            elif token_uid == token_uid:
                self.assertEqual(value, tokens_created)
                did_enter += 1

        self.assertEqual(did_enter, 2)
예제 #29
0
    def test_twin_tx(self):
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)

        address = self.get_address(0)
        value1 = 100
        value2 = 101

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=int(value1),
                             timelock=None),
            WalletOutputInfo(address=decode_address(address),
                             value=int(value2),
                             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()

        # Change of parents only, so it's a twin
        tx2 = Transaction.create_from_struct(tx1.get_struct())
        tx2.parents = [tx1.parents[1], tx1.parents[0]]
        tx2.resolve()
        self.assertNotEqual(tx1.hash, tx2.hash)

        self.manager.propagate_tx(tx1)
        self.run_to_completion()

        wallet_data = self.manager.tx_storage.wallet_index.get_from_address(
            address)
        self.assertEqual(len(wallet_data), 1)
        self.assertEqual(wallet_data, [tx1.hash])

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

        wallet_data = self.manager.tx_storage.wallet_index.get_from_address(
            address)
        self.assertEqual(len(wallet_data), 2)
        self.assertEqual(set(wallet_data), set([tx1.hash, tx2.hash]))
예제 #30
0
    def test_get(self):
        genesis = list(self.manager.tx_storage.get_all_genesis())
        genesis.sort(key=lambda x: x.timestamp)
        self.assertTrue(genesis[0].is_block)
        for tx in genesis[1:]:
            self.assertTrue(tx.is_transaction)

        genesis_tx = genesis[1]
        response_success = yield self.web.get(
            "decode_tx",
            {b'hex_tx': bytes(genesis_tx.get_struct().hex(), 'utf-8')})
        data_success = response_success.json_value()

        self.assertTrue(data_success['success'])
        data_genesis = genesis_tx.to_json(decode_script=True)
        data_genesis['raw'] = genesis_tx.get_struct().hex()
        data_genesis['nonce'] = str(data_genesis['nonce'])
        self.assertEqual(data_success['tx'], data_genesis)
        self.assertTrue('meta' in data_success)
        self.assertTrue('spent_outputs' in data_success)

        # Invalid hex
        response_error1 = yield self.web.get("decode_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("decode_tx", {b'hex_tx': b'a12c'})
        data_error2 = response_error2.json_value()

        self.assertFalse(data_error2['success'])

        # Token creation tx
        script_type_out = parse_address_script(genesis[0].outputs[0].script)
        address = script_type_out.address
        add_blocks_unlock_reward(self.manager)
        tx2 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=True)
        response = yield self.web.get(
            'decode_tx', {b'hex_tx': bytes(tx2.get_struct().hex(), 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])