예제 #1
0
    def test_check_stale_txn(self, m_is_full_transaction_pool, m_config):
        """
        Stale Transactions are Transactions that were supposed to go into block 5, but for some reason didn't make it.
        They languish in TransactionPool until check_stale_txn() checks the Pool and updates the tx_info to make them
        go into a higher block.
        For each stale transaction, P2PFactory.broadcast_tx() will be called.
        """

        # Redefine at what point should txs be considered stale
        m_config.user.stale_transaction_threshold = 2
        bob_xmss = get_bob_xmss(4)
        alice_xmss = get_alice_xmss(4)

        tx1 = TransferTransaction.create(addrs_to=[bob_xmss.address],
                                         amounts=[1000000],
                                         fee=1,
                                         xmss_pk=alice_xmss.pk)
        tx1.sign(alice_xmss)
        tx2 = TransferTransaction.create(addrs_to=[bob_xmss.address],
                                         amounts=[10000],
                                         fee=1,
                                         xmss_pk=alice_xmss.pk)
        tx2.sign(alice_xmss)
        m_broadcast_tx = Mock(
            name='Mock Broadcast TX function (in P2PFactory)')
        self.txpool.add_tx_to_pool(tx1, 5)
        self.txpool.add_tx_to_pool(tx2, 5)
        self.txpool.set_broadcast_tx(m_broadcast_tx)

        with set_qrl_dir('no_data'):
            state = State()
            self.txpool.check_stale_txn(state, 8)

            self.assertEqual(m_broadcast_tx.call_count, 0)

            m = MockFunction()
            bob_address_state = AddressState.get_default(bob_xmss.address)
            bob_address_state.pbdata.balance = 1000000000000
            m.put(bob_xmss.address, bob_address_state)
            state.get_address_state = m.get
            tx3 = TransferTransaction.create(addrs_to=[alice_xmss.address],
                                             amounts=[10000],
                                             fee=1,
                                             xmss_pk=bob_xmss.pk)
            tx3.sign(bob_xmss)
            self.txpool.add_tx_to_pool(tx3, 5)
            self.txpool.check_stale_txn(state, 8)

            self.assertEqual(m_broadcast_tx.call_count, 1)
예제 #2
0
class TestState(TestCase):
    def setUp(self):
        with set_qrl_dir('no_data'):
            self.state = State()
        self.m_db = MagicMock(name='mock DB', autospec=db.DB)

    def test_create_state(self):
        self.assertIsNotNone(self.state)  # to avoid warning (unused variable)

    def test_release_state(self):
        self.assertIsNotNone(self.state)  # to avoid warning (unused variable)

    def test_get_address_state(self):
        alice_xmss = get_alice_xmss()

        alice_address = alice_xmss.address
        address_state = self.state.get_address_state(alice_address)
        self.assertTrue(isinstance(address_state.address, bytes))

        alice_address = bytearray(alice_xmss.address)
        with self.assertRaises(TypeError):
            self.state.get_address_state(alice_address)

        alice_address = alice_xmss.address
        address_state = self.state.get_address_state(alice_address)
        addresses_state = {alice_address: address_state}
        self.assertTrue(isinstance(address_state.address, bytes))
        self.state.put_addresses_state(addresses_state)

        address_state = self.state.get_address_state(alice_address)
        self.assertTrue(isinstance(address_state.address, bytes))

    def test_get_all_address_state(self):
        addresses_state = self.state.get_all_address_state()
        self.assertEqual(len(addresses_state), 0)

        alice_xmss = get_alice_xmss()
        alice_address = alice_xmss.address
        address_state = self.state.get_address_state(alice_address)
        addresses_state = {alice_address: address_state}
        self.assertTrue(isinstance(address_state.address, bytes))
        self.state.put_addresses_state(addresses_state)

        addresses_state = self.state.get_all_address_state()
        self.assertEqual(len(addresses_state), 1)

        bob_xmss = get_bob_xmss()
        bob_address = bob_xmss.address
        address_state = self.state.get_address_state(bob_address)
        addresses_state = {bob_address: address_state}
        self.assertTrue(isinstance(address_state.address, bytes))
        self.state.put_addresses_state(addresses_state)

        addresses_state = self.state.get_all_address_state()
        self.assertEqual(len(addresses_state), 2)

    def test_basic_state_funcs(self):
        alice_xmss = get_alice_xmss()
        self.assertTrue(self.state.get_address_is_used(alice_xmss.address))
        self.assertEqual(self.state._return_all_addresses(), [])
        batch = self.state.batch
        self.assertIsNotNone(batch)
        self.state.write_batch(batch)
        self.assertEqual(self.state.total_coin_supply, 0)

    def test_get_address_nonce(self):
        alice_xmss = get_alice_xmss()
        self.assertEqual(self.state.get_address_nonce(alice_xmss.address), 0)

    def test_get_address_balance(self):
        alice_xmss = get_alice_xmss()
        self.assertEqual(self.state.get_address_balance(alice_xmss.address), 0)

    def test_get_address2(self):
        alice_xmss = get_alice_xmss()

        alice_address = alice_xmss.address
        address_state = self.state.get_address_state(alice_address)
        addresses_state = {alice_address: address_state}
        self.assertTrue(isinstance(address_state.address, bytes))
        self.state.put_addresses_state(addresses_state)
        address_state = self.state.get_address_state(alice_address)
        self.assertTrue(isinstance(address_state.address, bytes))

    def test_create_token_metadata(self):
        alice_xmss = get_alice_xmss()
        bob_xmss = get_bob_xmss()

        token_transaction = get_token_transaction(alice_xmss, bob_xmss)
        self.state.create_token_metadata(token_transaction)

        token_metadata = self.state.get_token_metadata(
            token_transaction.txhash)
        self.assertEqual(token_metadata.token_txhash, token_transaction.txhash)
        self.assertEqual(token_metadata.transfer_token_tx_hashes[0],
                         token_transaction.txhash)

    def test_update_token_metadata(self):
        alice_xmss = get_alice_xmss()
        bob_xmss = get_bob_xmss()

        token_transaction = get_token_transaction(alice_xmss, bob_xmss)
        self.state.create_token_metadata(token_transaction)

        transfer_token_transaction = TransferTokenTransaction.create(
            token_txhash=token_transaction.txhash,
            addrs_to=[alice_xmss.address],
            amounts=[100000000],
            fee=1,
            xmss_pk=bob_xmss.pk)

        self.state.update_token_metadata(transfer_token_transaction)

        token_metadata = self.state.get_token_metadata(
            token_transaction.txhash)
        self.assertEqual(len(token_metadata.transfer_token_tx_hashes), 2)
        self.assertEqual(token_metadata.transfer_token_tx_hashes[0],
                         token_transaction.txhash)
        self.assertEqual(token_metadata.transfer_token_tx_hashes[1],
                         transfer_token_transaction.txhash)

    def test_get_token_metadata(self):
        token_txhash = bytes(sha2_256(b'alpha'))
        token_metadata = TokenMetadata.create(
            token_txhash,
            [bytes(sha2_256(b'delta')),
             bytes(sha2_256(b'gamma'))])
        self.state._db.get_raw = MagicMock(
            return_value=token_metadata.serialize())
        self.assertEqual(
            self.state.get_token_metadata(token_txhash).to_json(),
            token_metadata.to_json())

    def test_remove_transfer_token_metadata(self):
        alice_xmss = get_alice_xmss()
        bob_xmss = get_bob_xmss()

        token_transaction = get_token_transaction(alice_xmss, bob_xmss)
        self.state.create_token_metadata(token_transaction)

        transfer_token = TransferTokenTransaction.create(
            token_txhash=token_transaction.txhash,
            addrs_to=[alice_xmss.address],
            amounts=[100000000],
            fee=1,
            xmss_pk=bob_xmss.pk)
        transfer_token.sign(alice_xmss)

        self.state.update_token_metadata(transfer_token)
        token_metadata = self.state.get_token_metadata(
            transfer_token.token_txhash)
        self.assertIn(transfer_token.txhash,
                      token_metadata.transfer_token_tx_hashes)

        self.state.remove_transfer_token_metadata(transfer_token)
        token_metadata = self.state.get_token_metadata(
            transfer_token.token_txhash)
        self.assertNotIn(transfer_token.txhash,
                         token_metadata.transfer_token_tx_hashes)

    def test_remove_token_metadata(self):
        alice_xmss = get_alice_xmss()
        bob_xmss = get_bob_xmss()

        token_tx = get_token_transaction(alice_xmss, bob_xmss)
        self.state.create_token_metadata(token_tx)

        token_metadata = self.state.get_token_metadata(token_tx.txhash)
        self.assertEqual(token_metadata.token_txhash, token_tx.txhash)
        self.state.remove_token_metadata(token_tx)
        self.assertIsNone(self.state.get_token_metadata(token_tx.txhash))

    def test_address_used(self):
        alice_xmss = get_alice_xmss()
        self.assertTrue(self.state.get_address_is_used(alice_xmss.address))

    def test_return_all_addresses(self):
        self.assertEqual(self.state._return_all_addresses(), [])

    def test_get_batch(self):
        self.assertIsNotNone(self.state.batch)

    def test_write_batch(self):
        batch = self.state.batch
        block = Block.create(block_number=10,
                             prev_headerhash=b'aa',
                             prev_timestamp=10,
                             transactions=[],
                             miner_address=b'aa')
        self.state.put_block(block, batch)
        self.assertIsNone(self.state.get_block(block.headerhash))
        self.state.write_batch(batch)
        block2 = self.state.get_block(block.headerhash)
        self.assertEqual(block.headerhash, block2.headerhash)

    def test_update_total_coin_supply(self):
        self.assertEqual(self.state.total_coin_supply, 0)
        self.state._update_total_coin_supply(100)
        self.assertEqual(self.state.total_coin_supply, 100)

    def test_total_coin_supply(self):
        self.assertEqual(self.state.total_coin_supply, 0)

    def test_get_measurement(self):
        def block(headerhash):
            nth_block = Block()
            if headerhash == b'test_block_1':
                nth_block.blockheader._data.timestamp_seconds = 50000
            elif headerhash == b'test_block_2':
                nth_block.blockheader._data.timestamp_seconds = 80000
            elif headerhash == b'test_block_3':
                nth_block.blockheader._data.timestamp_seconds = 90000
            return nth_block

        parent_metadata = BlockMetadata.create(block_difficulty=b'\x00' * 32,
                                               cumulative_difficulty=b'\x00' *
                                               32,
                                               child_headerhashes=[])

        measurement = self.state.get_measurement(
            block_timestamp=100000,
            parent_headerhash=b'',
            parent_metadata=parent_metadata)

        # Test Case, when count_headerhashes equals 0
        self.assertEqual(measurement, config.dev.mining_setpoint_blocktime)

        self.state.get_block = MagicMock(side_effect=block)
        parent_metadata.update_last_headerhashes([], b'test_block_1')

        measurement = self.state.get_measurement(
            block_timestamp=100000,
            parent_headerhash=b'test_block_1',
            parent_metadata=parent_metadata)

        # Test Case, when count_headerhashes equals 1
        self.assertEqual(
            measurement,
            (100000 - 50000 + config.dev.mining_setpoint_blocktime) // 2)

        parent_metadata.update_last_headerhashes([b'test_block_1'],
                                                 b'test_block_2')

        measurement = self.state.get_measurement(
            block_timestamp=100000,
            parent_headerhash=b'test_block_2',
            parent_metadata=parent_metadata)

        # Test Case, when count_headerhashes is greater than 1
        # but less than config.dev.N_measurement
        self.assertEqual(
            measurement,
            (100000 - 80000 + config.dev.mining_setpoint_blocktime) // 2)

        parent_metadata.update_last_headerhashes(
            [b'test_block_3'] * config.dev.N_measurement, b'test_block_2')

        measurement = self.state.get_measurement(
            block_timestamp=100000,
            parent_headerhash=b'test_block_2',
            parent_metadata=parent_metadata)

        # Test Case, when count_headerhashes is greater than config.dev.N_measurement
        self.assertEqual(measurement,
                         (100000 - 90000) // config.dev.N_measurement)

    def test_delete(self):
        block = Block()
        self.state.put_block(block, None)
        block1 = self.state.get_block(block.headerhash)
        self.assertEqual(block.serialize(), block1.serialize())
        self.state._delete(block.headerhash, None)
        self.assertIsNone(self.state.get_block(block.headerhash))

    def test_get_block_size_limit(self):
        alice_xmss = get_alice_xmss()
        blocks = gen_blocks(20, self.state, alice_xmss.address)
        self.assertEqual(self.state.get_block_size_limit(blocks[-1]), 1048576)

        # get_block_size_limit() should return None if it couldn't get any blocks from db
        with patch('qrl.core.State.State.get_block', return_value=None):
            self.assertIsNone(self.state.get_block_size_limit(blocks[-1]))

    def test_put_block_metadata(self):
        block_metadata = BlockMetadata.create()
        block_metadata.update_last_headerhashes([b'test1', b'test2'], b'test3')

        self.state.put_block_metadata(b'block_headerhash', block_metadata,
                                      None)
        self.state.put_block_metadata(b'block_headerhash2',
                                      BlockMetadata.create(), None)

        self.assertEqual(
            self.state.get_block_metadata(b'block_headerhash').to_json(),
            block_metadata.to_json())

        expected_json = b'{\n  "blockDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",\n  ' \
                        b'"cumulativeDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="\n}'

        self.assertEqual(
            self.state.get_block_metadata(b'block_headerhash2').to_json(),
            expected_json)

    def test_get_block_metadata(self):
        self.assertIsNone(self.state.get_block_metadata(b'test1'))
        self.state.put_block_metadata(b'block_headerhash2',
                                      BlockMetadata.create(), None)

        tmp_json = self.state.get_block_metadata(
            b'block_headerhash2').to_json()

        expected_json = b'{\n  "blockDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",\n  ' \
                        b'"cumulativeDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="\n}'

        self.assertEqual(tmp_json, expected_json)

    def test_prepare_address_list(self):
        block = Block.create(block_number=10,
                             prev_headerhash=b'',
                             prev_timestamp=10,
                             transactions=[],
                             miner_address=get_some_address(1))
        # Test Case: without any transactions of block
        self.assertEqual(self.state.prepare_address_list(block),
                         {config.dev.coinbase_address,
                          get_some_address(1)})

        alice_xmss = get_alice_xmss()
        block = Block.create(
            block_number=10,
            prev_headerhash=b'',
            prev_timestamp=10,
            transactions=[
                TransferTransaction.create(
                    addrs_to=[get_some_address(2),
                              get_some_address(3)],
                    amounts=[100, 100],
                    fee=0,
                    xmss_pk=alice_xmss.pk)
            ],
            miner_address=get_some_address(1))

        # Test Case, with one Transaction
        self.assertEqual(
            self.state.prepare_address_list(block), {
                config.dev.coinbase_address,
                get_some_address(1),
                get_some_address(2),
                get_some_address(3), alice_xmss.address
            })

    def test_put_addresses_state(self):
        alice_xmss = get_alice_xmss()
        alice_state = AddressState.get_default(alice_xmss.address)
        addresses_state = {
            alice_state.address: alice_state,
            b'test1': AddressState.get_default(b'test1')
        }
        self.state.put_addresses_state(addresses_state, None)
        alice_state2 = self.state.get_address_state(alice_xmss.address)
        self.assertEqual(alice_state.serialize(), alice_state2.serialize())
        test_state = self.state.get_address_state(b'test1')
        self.assertEqual(test_state.serialize(),
                         AddressState.get_default(b'test1').serialize())

    def test_get_state_mainchain(self):
        alice_xmss = get_alice_xmss()
        alice_state = AddressState.get_default(alice_xmss.address)
        alice_state.increase_nonce()
        alice_state.balance += 1000
        addresses_state = {
            alice_state.address: alice_state,
            b'test1': AddressState.get_default(b'test1')
        }
        self.state.put_addresses_state(addresses_state, None)
        addresses_state1 = self.state.get_state_mainchain(
            {alice_state.address, b'test1'})

        self.assertEqual(addresses_state[alice_state.address].serialize(),
                         addresses_state1[alice_state.address].serialize())
        self.assertEqual(addresses_state[b'test1'].serialize(),
                         addresses_state1[b'test1'].serialize())

    def test_get_block_datapoint(self):
        # Test Case: When block not found
        self.assertIsNone(self.state.get_block_datapoint(b'test'))

        alice_xmss = get_alice_xmss()
        blocks = gen_blocks(20, self.state, alice_xmss.address)
        for i in range(1, 20):
            datapoint = self.state.get_block_datapoint(blocks[i].headerhash)
            self.assertEqual(datapoint.difficulty, "256")
            self.assertEqual(datapoint.timestamp, 1615270947 + i)
            self.assertEqual(datapoint.header_hash, blocks[i].headerhash)
            self.assertEqual(datapoint.header_hash_prev,
                             blocks[i - 1].headerhash)

    def test_put_block_number_mapping(self):
        bm = qrl_pb2.BlockNumberMapping()
        self.state.put_block_number_mapping(0, bm, None)
        read_bm = self.state.get_block_number_mapping(0)
        self.assertEqual(bm.SerializeToString(), read_bm.SerializeToString())
        self.assertIsNone(self.state.get_block_by_number(4))

    def test_get_block_number_mapping(self):
        self.assertIsNone(self.state.get_block_number_mapping(0))
        bm = qrl_pb2.BlockNumberMapping()
        self.state.put_block_number_mapping(0, bm, None)
        read_bm = self.state.get_block_number_mapping(0)
        self.assertEqual(bm.SerializeToString(), read_bm.SerializeToString())

    def test_get_block_by_number(self):
        bm = qrl_pb2.BlockNumberMapping()
        self.state.put_block_number_mapping(0, bm, None)
        self.assertIsNone(self.state.get_block_by_number(4))

    def test_update_mainchain_height(self):
        self.state.update_mainchain_height(5, None)
        self.assertEqual(self.state.get_mainchain_height(), 5)

    def test_get_mainchain_height(self):
        # Test Case: Check default value
        self.assertEqual(self.state.get_mainchain_height(), -1)

        self.state.update_mainchain_height(15, None)
        self.assertEqual(self.state.get_mainchain_height(), 15)

        self.state.update_mainchain_height(5, None)
        self.assertEqual(self.state.get_mainchain_height(), 5)

    def test_last_block(self):
        def get_block_by_number(block_number):
            block = Block()
            block.blockheader._data.block_number = block_number
            return block

        self.assertIsNone(self.state.last_block)
        self.state.get_block_by_number = MagicMock(
            side_effect=get_block_by_number)

        self.state.update_mainchain_height(10, None)
        self.assertEqual(self.state.last_block.block_number, 10)

        self.state.update_mainchain_height(1, None)
        self.assertEqual(self.state.last_block.block_number, 1)

    def test_update_last_tx(self):
        alice_xmss = get_alice_xmss()
        # Test Case: When there is no last txns
        self.assertEqual(self.state.get_last_txs(), [])

        block = Block()
        tx1 = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block._data.transactions.extend([tx1.pbdata])
        self.state._update_last_tx(block, None)
        last_txns = self.state.get_last_txs()

        # Test Case: When there is only 1 last txns
        self.assertEqual(len(last_txns), 1)
        self.assertEqual(last_txns[0].to_json(), tx1.to_json())

        block = Block()
        tx2 = TransferTransaction.create(
            addrs_to=[get_some_address(2),
                      get_some_address(3)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)

        tx3 = TransferTransaction.create(
            addrs_to=[get_some_address(4),
                      get_some_address(5)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block._data.transactions.extend([tx2.pbdata, tx3.pbdata])
        self.state._update_last_tx(block, None)
        last_txns = self.state.get_last_txs()

        # Test Case: When there are 3 last txns
        self.assertEqual(len(last_txns), 3)
        self.assertEqual(last_txns[0].to_json(), tx3.to_json())
        self.assertEqual(last_txns[1].to_json(), tx2.to_json())
        self.assertEqual(last_txns[2].to_json(), tx1.to_json())

    def test_get_last_txs(self):
        self.assertEqual(self.state.get_last_txs(), [])

        alice_xmss = get_alice_xmss()
        block = Block()
        tx1 = TransferTransaction.create(
            addrs_to=[get_some_address(0),
                      get_some_address(1)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block._data.transactions.extend([tx1.pbdata])
        self.state._update_last_tx(block, None)
        last_txns = self.state.get_last_txs()

        # Test Case: When there is only 1 last txns
        self.assertEqual(len(last_txns), 1)
        self.assertEqual(last_txns[0].to_json(), tx1.to_json())

    def test_remove_last_tx(self):
        # Test Case: When there is no last txns
        self.assertEqual(self.state.get_last_txs(), [])

        alice_xmss = get_alice_xmss()

        block = Block()
        tx1 = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block._data.transactions.extend([tx1.pbdata])
        self.state._update_last_tx(block, None)
        last_txns = self.state.get_last_txs()

        self.assertEqual(last_txns[0].to_json(), tx1.to_json())

        self.state._remove_last_tx(block, None)
        last_txns = self.state.get_last_txs()
        self.assertEqual(last_txns, [])

    def test_rollback_tx_metadata(self):
        alice_xmss = get_alice_xmss()

        tx1 = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)

        block = Block.create(block_number=5,
                             prev_headerhash=b'',
                             prev_timestamp=10,
                             transactions=[tx1],
                             miner_address=b'')

        self.state.update_tx_metadata(block=block, batch=None)

        tx_metadata = self.state.get_tx_metadata(tx1.txhash)

        self.assertEqual(tx_metadata[0].to_json(), tx1.to_json())
        self.state.rollback_tx_metadata(block, None)
        self.assertIsNone(self.state.get_tx_metadata(tx1.txhash))

    def test_update_tx_metadata(self):
        alice_xmss = get_alice_xmss()
        tx = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block_number = 5
        self.state.put_tx_metadata(tx, block_number, 10000, None)

        tx_metadata = self.state.get_tx_metadata(tx.txhash)
        self.assertEqual(tx_metadata[0].to_json(), tx.to_json())
        self.assertEqual(tx_metadata[1], block_number)

    def test_remove_tx_metadata(self):
        self.assertIsNone(self.state.get_tx_metadata(b'test1'))

        alice_xmss = get_alice_xmss()
        tx = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block_number = 5
        self.state.put_tx_metadata(tx, block_number, 10000, None)

        tx_metadata = self.state.get_tx_metadata(tx.txhash)
        self.assertEqual(tx_metadata[0].to_json(), tx.to_json())
        self.assertEqual(tx_metadata[1], block_number)

        self.state.remove_tx_metadata(tx, None)
        self.assertIsNone(self.state.get_tx_metadata(tx.txhash))

    def test_put_tx_metadata(self):
        self.assertIsNone(self.state.get_tx_metadata(b'test1'))

        alice_xmss = get_alice_xmss()
        tx = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block_number = 5
        self.state.put_tx_metadata(tx, block_number, 10000, None)

        tx_metadata = self.state.get_tx_metadata(tx.txhash)
        self.assertEqual(tx_metadata[0].to_json(), tx.to_json())
        self.assertEqual(tx_metadata[1], block_number)

    def test_get_tx_metadata(self):
        self.assertIsNone(self.state.get_tx_metadata(b'test1'))

        alice_xmss = get_alice_xmss()
        tx = TransferTransaction.create(
            addrs_to=[get_some_address(1),
                      get_some_address(2)],
            amounts=[1, 2],
            fee=0,
            xmss_pk=alice_xmss.pk)
        block_number = 5
        timestamp = 10000
        self.state.put_tx_metadata(tx, block_number, timestamp, None)

        tx_metadata = self.state.get_tx_metadata(tx.txhash)
        self.assertEqual(tx_metadata[0].to_json(), tx.to_json())
        self.assertEqual(tx_metadata[1], block_number)

    def test_increase_txn_count(self):
        self.assertEqual(self.state.get_txn_count(b'q1'), 0)

        self.state._increase_txn_count(0, b'q1')
        self.assertEqual(self.state.get_txn_count(b'q1'), 1)

        self.state._increase_txn_count(5, b'q1')
        self.assertEqual(self.state.get_txn_count(b'q1'), 6)

    def test_decrease_txn_count(self):
        self.assertEqual(self.state.get_txn_count(b'q1'), 0)

        with self.assertRaises(ValueError):
            self.state._decrease_txn_count(0, b'q1')

        self.state._decrease_txn_count(5, b'q1')
        self.assertEqual(self.state.get_txn_count(b'q1'), 4)

    def test_get_txn_count(self):
        self.assertEqual(self.state.get_txn_count(b'q1'), 0)

        self.state._increase_txn_count(10, b'q1')
        self.assertEqual(self.state.get_txn_count(b'q1'), 11)

    def test_fork_state(self):
        fork_state = qrlstateinfo_pb2.ForkState(
            initiator_headerhash=b'block2_right',
            fork_point_headerhash=b'block0_base_of_fork',
            old_mainchain_hash_path=[b'block1_right', b'block2_right'],
            new_mainchain_hash_path=[b'block1_left', b'block2_left'])
        self.assertIsNone(self.state.get_fork_state())

        self.state.put_fork_state(fork_state)
        self.assertEqual(fork_state, self.state.get_fork_state())

        self.state.delete_fork_state()
        self.assertIsNone(self.state.get_fork_state())
예제 #3
0
class TestChainManagerReal(TestCase):
    def setUp(self):
        with set_qrl_dir('no_data'):
            self.state = State()
            self.state.get_measurement = Mock(return_value=10000000)

            del GenesisBlock.instance  # Removing Singleton instance
            self.genesis_block = GenesisBlock()

            self.chain_manager = ChainManager(self.state)
            self.chain_manager._difficulty_tracker = Mock()

    def test_load(self):
        # load() has the following tasks:
        # Write Genesis Block into State immediately
        # Register block_number <-> blockhash mapping
        # Calculate difficulty Metadata for Genesis Block
        # Generate AddressStates from Genesis Block balances
        # Apply Genesis Block's transactions to the state
        self.chain_manager.load(self.genesis_block)
        block = self.state.get_block(GenesisBlock().headerhash)
        self.assertIsNotNone(block)

    def test_load_twice(self):
        self.chain_manager.load(self.genesis_block)

        # If we call load() a second time, it should check to see if we are forked and initiate recovery.
        # First we pretend we are not forked.
        self.state.get_fork_state = Mock(return_value=None)
        self.chain_manager._fork_recovery = Mock(name='mock _fork_recovery()')
        self.chain_manager.load(self.genesis_block)
        self.chain_manager._fork_recovery.assert_not_called()

        # If we pretend we are forked, it should call _fork_recovery().
        m_fork_state = Mock(autospec=qrlstateinfo_pb2.ForkState,
                            initiator_headerhash=self.genesis_block.headerhash)
        self.state.get_fork_state.return_value = m_fork_state
        self.chain_manager.load(self.genesis_block)
        self.chain_manager._fork_recovery.assert_called_with(
            self.genesis_block, m_fork_state)

    @patch('qrl.core.misc.ntp.getTime')
    def test_simple_add_block(self, time_mock):
        # Simply test that adding a block on top of the genesis block works.
        self.chain_manager._difficulty_tracker.get.return_value = ask_difficulty_tracker(
            '2')
        self.chain_manager.load(self.genesis_block)

        time_mock.return_value = 1615270948  # Very high to get an easy difficulty

        block_1 = Block.create(block_number=1,
                               prev_headerhash=self.genesis_block.headerhash,
                               prev_timestamp=self.genesis_block.timestamp,
                               transactions=[],
                               miner_address=alice.address)
        block_1.set_nonces(201, 0)

        # Uncomment only to determine the correct mining_nonce of above blocks
        # from qrl.core.PoWValidator import PoWValidator
        # while not PoWValidator().validate_mining_nonce(self.state, block_1.blockheader, False):
        #     block_1.set_nonces(block_1.mining_nonce + 1)
        #     print(block_1.mining_nonce)
        self.assertTrue(block_1.validate(self.chain_manager, {}))
        result = self.chain_manager.add_block(block_1)

        self.assertTrue(result)
        self.assertEqual(self.chain_manager.last_block, block_1)

    @set_default_balance_size()
    @patch('qrl.core.misc.ntp.getTime')
    def test_multi_output_transaction_add_block(self, time_mock):
        # Test that adding block with a multi-output Transaction updates everybody's balances correctly.
        self.chain_manager.load(self.genesis_block)

        extended_seed = "010300cebc4e25553afa0aab899f7838e59e18a48852fa9dfd5" \
                        "ae78278c371902aa9e6e9c1fa8a196d2dba0cbfd2f2d212d16c"
        random_xmss = XMSS.from_extended_seed(hstr2bin(extended_seed))

        transfer_transaction = TransferTransaction.create(
            addrs_to=[alice.address, random_xmss.address],
            amounts=[
                40 * int(config.dev.shor_per_quanta),
                59 * int(config.dev.shor_per_quanta)
            ],
            fee=1 * config.dev.shor_per_quanta,
            xmss_pk=bob.pk)
        transfer_transaction._data.nonce = 1
        transfer_transaction.sign(bob)

        time_mock.return_value = 1615270948  # Very high to get an easy difficulty

        block_1 = Block.create(block_number=1,
                               prev_headerhash=self.genesis_block.headerhash,
                               prev_timestamp=self.genesis_block.timestamp,
                               transactions=[transfer_transaction],
                               miner_address=alice.address)
        block_1.set_nonces(129, 0)

        # Uncomment only to determine the correct mining_nonce of above blocks
        # from qrl.core.PoWValidator import PoWValidator
        # while not PoWValidator().validate_mining_nonce(self.state, block_1.blockheader, False):
        #     block_1.set_nonces(block_1.mining_nonce + 1)
        #     print(block_1.mining_nonce)
        self.assertTrue(block_1.validate(self.chain_manager, {}))
        result = self.chain_manager.add_block(block_1)

        self.assertTrue(result)
        self.assertEqual(self.chain_manager.last_block, block_1)

        bob_addr_state = self.state.get_address_state(bob.address)
        alice_addr_state = self.state.get_address_state(alice.address)
        random_addr_state = self.state.get_address_state(random_xmss.address)

        self.assertEqual(bob_addr_state.balance, 0)
        self.assertEqual(
            alice_addr_state.balance, 140 * int(config.dev.shor_per_quanta) +
            block_1.block_reward + block_1.fee_reward)
        self.assertEqual(random_addr_state.balance,
                         159 * int(config.dev.shor_per_quanta))

    @patch("qrl.core.DifficultyTracker.DifficultyTracker.get")
    def test_add_block(self, mock_difficulty_tracker_get):
        """
        Add block_1 on genesis block (that registers Bob as Alice's slave)
        Add a competing fork_block on genesis block (without the SlaveTransaction)
        Add block_2 on fork_block (without the SlaveTransaction)
        Bob should be free from slavery now.
        """
        mock_difficulty_tracker_get.return_value = ask_difficulty_tracker('2')
        self.chain_manager.load(self.genesis_block)

        # Add block_1 on genesis block.
        slave_tx = SlaveTransaction.create(slave_pks=[bob.pk],
                                           access_types=[0],
                                           fee=0,
                                           xmss_pk=alice.pk)
        slave_tx.sign(alice)
        slave_tx._data.nonce = 1
        self.assertTrue(slave_tx.validate())
        with patch('qrl.core.misc.ntp.getTime') as time_mock:
            time_mock.return_value = 1615270948  # Very high to get an easy difficulty

            block_1 = Block.create(
                block_number=1,
                prev_headerhash=self.genesis_block.headerhash,
                prev_timestamp=self.genesis_block.timestamp,
                transactions=[slave_tx],
                miner_address=alice.address)
            block_1.set_nonces(2, 0)
            # Uncomment only to determine the correct mining_nonce of above blocks
            # from qrl.core.PoWValidator import PoWValidator
            # while not PoWValidator().validate_mining_nonce(self.state, block_1.blockheader, False):
            #     block_1.set_nonces(block_1.mining_nonce + 1)
            #     print(block_1.mining_nonce)
            self.assertTrue(block_1.validate(self.chain_manager, {}))
            result = self.chain_manager.add_block(block_1)

        self.assertTrue(result)
        self.assertEqual(self.chain_manager.last_block, block_1)

        # Yes, Bob is Alice's slave.
        alice_state = self.chain_manager.get_address_state(alice.address)
        self.assertEqual(len(alice_state.slave_pks_access_type), 1)
        self.assertTrue(str(bob.pk) in alice_state.slave_pks_access_type)

        # Add fork block on genesis block
        with patch('qrl.core.misc.ntp.getTime') as time_mock:
            time_mock.return_value = 1715270948  # Very high to get an easy difficulty
            fork_block = Block.create(
                block_number=1,
                prev_headerhash=self.genesis_block.headerhash,
                prev_timestamp=self.genesis_block.timestamp,
                transactions=[],
                miner_address=bob.address)

            fork_block.set_nonces(4, 0)
            # Uncomment only to determine the correct mining_nonce of above blocks
            # from qrl.core.PoWValidator import PoWValidator
            # while not PoWValidator().validate_mining_nonce(self.state, fork_block.blockheader, False):
            #     fork_block.set_nonces(fork_block.mining_nonce + 1)
            #     print(fork_block.mining_nonce)
            self.assertTrue(fork_block.validate(self.chain_manager, {}))
            result = self.chain_manager.add_block(fork_block)

        self.assertTrue(result)
        self.assertEqual(self.chain_manager.last_block, block_1)

        fork_block = self.state.get_block(fork_block.headerhash)
        self.assertIsNotNone(fork_block)

        # Add block_2 on fork_block.
        with patch('qrl.core.misc.ntp.getTime') as time_mock:
            time_mock.return_value = 1815270948  # Very high to get an easy difficulty
            block_2 = fork_block.create(block_number=2,
                                        prev_headerhash=fork_block.headerhash,
                                        prev_timestamp=fork_block.timestamp,
                                        transactions=[],
                                        miner_address=bob.address)

            block_2.set_nonces(1, 0)
            # Uncomment only to determine the correct mining_nonce of above blocks
            # from qrl.core.PoWValidator import PoWValidator
            # while not PoWValidator().validate_mining_nonce(state, block_2.blockheader, False):
            #     block_2.set_nonces(block_2.mining_nonce + 1, 0)
            #     print(block_2.mining_nonce)
            self.assertTrue(block_2.validate(self.chain_manager, {}))
            result = self.chain_manager.add_block(block_2)

        self.assertTrue(result)
        self.assertEqual(self.chain_manager.last_block.block_number,
                         block_2.block_number)
        self.assertEqual(self.chain_manager.last_block.serialize(),
                         block_2.serialize())

        # Now we are on the forked chain, Bob is no longer Alice's slave.
        alice_state = self.chain_manager.get_address_state(alice.address)
        self.assertFalse(str(bob.pk) in alice_state.slave_pks_access_type)

    @patch('qrl.core.misc.ntp.getTime', new=replacement_getTime)
    def test_get_headerhashes(self):
        block_1 = create_block(1, self.genesis_block, alice.address)
        block_2 = create_block(2, block_1, alice.address)

        self.chain_manager.load(self.genesis_block)
        self.chain_manager.add_block(block_1)
        self.chain_manager.add_block(block_2)

        node_header_hash = self.chain_manager.get_headerhashes(0)
        self.assertEqual(node_header_hash.headerhashes, [
            self.genesis_block.headerhash, block_1.headerhash,
            block_2.headerhash
        ])

    @patch('qrl.core.misc.ntp.getTime', new=replacement_getTime)
    def test_fork_recovery(self):
        # When the node finds that it has been on the slower chain all this time, it runs _fork_recovery() to _rollback
        # to an earlier state and switch to the longer chain.

        block_1 = create_block(1, self.genesis_block, alice.address)

        block_2 = create_block(2, block_1, alice.address)
        block_3 = create_block(3, block_2, alice.address)
        self.main_chain = [block_2, block_3]

        block_2_alt = create_block(2, block_1, bob.address)
        block_3_alt = create_block(3, block_2_alt, bob.address)
        block_4_alt = create_block(4, block_3_alt, bob.address)
        self.alt_chain = [block_2_alt, block_3_alt, block_4_alt]

        self.chain_manager.load(self.genesis_block)

        self.chain_manager.add_block(block_1)
        self.chain_manager.add_block(block_2)
        self.chain_manager.add_block(block_3)

        # Start adding the forked block to the chain manager. It accepts, and does nothing else.
        self.chain_manager.add_block(block_2_alt)
        self.assertEqual(self.chain_manager.last_block, block_3)

        # We lengthen the fork. Still, the chain manager stays on the first chain.
        self.chain_manager.add_block(block_3_alt)
        self.assertEqual(self.chain_manager.last_block, block_3)

        # When it is obvious that the fork is longer (has a higher cum. diff), the chain manager invokes _fork_recovery()
        # and switches over to the fork
        self.chain_manager.add_block(block_4_alt)
        self.assertEqual(self.chain_manager.last_block, block_4_alt)