def test_getBlock(self): with set_qrl_dir('no_data'): db_state = State() p2p_factory = Mock(spec=P2PFactory) p2p_factory.pow = Mock(spec=POW) chain_manager = ChainManager(db_state) qrlnode = QRLNode(mining_address=b'') qrlnode.set_chain_manager(chain_manager) qrlnode._p2pfactory = p2p_factory qrlnode._pow = p2p_factory.pow qrlnode._peer_addresses = ['127.0.0.1', '192.168.1.1'] service = PublicAPIService(qrlnode) alice_xmss = get_alice_xmss() b = Block.create(dev_config=config.dev, block_number=1, prev_headerhash=sha256(b'reveal'), prev_timestamp=10, transactions=[], miner_address=alice_xmss.address, seed_height=0, seed_hash=None) Block.put_block(db_state, b, None) db_state.get_block = MagicMock(return_value=b) context = Mock(spec=ServicerContext) request = qrl_pb2.GetBlockReq(header_hash=b.headerhash) response = service.GetBlock(request=request, context=context) context.set_code.assert_not_called() self.assertEqual(1, response.block.header.block_number)
def validate_mining_nonce(self, state: State, blockheader: BlockHeader, enable_logging=False): parent_metadata = state.get_block_metadata( blockheader.prev_blockheaderhash) parent_block = state.get_block(blockheader.prev_blockheaderhash) measurement = state.get_measurement(blockheader.timestamp, blockheader.prev_blockheaderhash, parent_metadata) diff, target = DifficultyTracker.get( measurement=measurement, parent_difficulty=parent_metadata.block_difficulty) if enable_logging: logger.debug('-----------------START--------------------') logger.debug('Validate #%s', blockheader.block_number) logger.debug('block.timestamp %s', blockheader.timestamp) logger.debug('parent_block.timestamp %s', parent_block.timestamp) logger.debug('parent_block.difficulty %s', UInt256ToString(parent_metadata.block_difficulty)) logger.debug('diff : %s | target : %s', UInt256ToString(diff), target) logger.debug('-------------------END--------------------') if not self.verify_input_cached(blockheader.mining_blob, target): if enable_logging: logger.warning("PoW verification failed") qn = Qryptonight() tmp_hash = qn.hash(blockheader.mining_blob) logger.warning("{}".format(tmp_hash)) logger.debug('%s', blockheader.to_json()) return False return True
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())
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)