def qrlnode_with_mock_blockchain(num_blocks): start_time = time.time() with mock.patch('qrl.core.misc.ntp.getTime') as ntp_mock, \ set_data_dir('no_data'), \ State() as state, \ mock.patch('time.time') as time_mock: # noqa time_mock.return_value = start_time ntp_mock.return_value = start_time state.get_measurement = MagicMock(return_value=10000000) required_height = ceil(log(num_blocks, 2)) required_height = int(required_height + required_height % 2) alice_xmss = get_alice_xmss(xmss_height=required_height) bob_xmss = get_bob_xmss() genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_target = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock(return_value=(tmp_difficulty, tmp_target)) block_prev = state.get_block(genesis_block.headerhash) for block_idx in range(1, num_blocks): transactions = [] if block_idx == 1: slave_tx = SlaveTransaction.create(slave_pks=[bob_xmss.pk], access_types=[0], fee=0, xmss_pk=alice_xmss.pk) slave_tx.sign(alice_xmss) slave_tx._data.nonce = 2 transactions = [slave_tx] time_mock.return_value = time_mock.return_value + 60 ntp_mock.return_value = ntp_mock.return_value + 60 block_new = Block.create(block_number=block_idx, prevblock_headerhash=block_prev.headerhash, transactions=transactions, miner_address=alice_xmss.address) while not PoWValidator().validate_mining_nonce(state, block_new.blockheader, False): block_new.set_nonces(block_new.mining_nonce + 1, 0) chain_manager.add_block(block_new) block_prev = block_new qrlnode = QRLNode(state, mining_credit_wallet=alice_xmss.address) qrlnode.set_chain_manager(chain_manager) yield qrlnode
def test_simple_add_block(self): with set_data_dir('no_data'): with State() as state: state.get_measurement = MagicMock(return_value=10000000) alice_xmss = get_alice_xmss() genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_target = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock( return_value=(tmp_difficulty, tmp_target)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) with mock.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, prevblock_headerhash=genesis_block.headerhash, transactions=[], miner_address=alice_xmss.address) block_1.set_nonces(491, 0) from pyqrllib.pyqrllib import bin2hstr print(bin2hstr(block.headerhash)) # 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_1.blockheader, False): # block_1.set_nonces(block_1.mining_nonce + 1) # print(block_1.mining_nonce) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1)
def test_simple_add_block(self): with set_data_dir('no_data'): with State() as state: state.get_measurement = MagicMock(return_value=10000000) alice_xmss = get_alice_xmss() genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_boundary = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock( return_value=(tmp_difficulty, tmp_boundary)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) with mock.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, prevblock_headerhash=genesis_block.headerhash, transactions=[], signing_xmss=alice_xmss, master_address=alice_xmss.address, nonce=1) while not PoWValidator().validate_mining_nonce( state, block_1.blockheader, False): block_1.set_mining_nonce(block_1.mining_nonce + 1) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1)
def test_multi_output_transaction_add_block(self): with set_data_dir('no_data'): with State() as state: state.get_measurement = MagicMock(return_value=10000000) alice_xmss = get_alice_xmss() bob_xmss = get_bob_xmss() extended_seed = "010300cebc4e25553afa0aab899f7838e59e18a48852fa9dfd5" \ "ae78278c371902aa9e6e9c1fa8a196d2dba0cbfd2f2d212d16c" random_xmss = XMSS.from_extended_seed(hstr2bin(extended_seed)) transfer_transaction = TransferTransaction.create( addrs_to=[alice_xmss.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_xmss.pk) transfer_transaction._data.nonce = 1 transfer_transaction.sign(bob_xmss) genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_boundary = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock( return_value=(tmp_difficulty, tmp_boundary)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) with mock.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, prevblock_headerhash=genesis_block.headerhash, transactions=[transfer_transaction], miner_address=alice_xmss.address) block_1.set_nonces(274, 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_1.blockheader, False): # block_1.set_nonces(block_1.mining_nonce + 1) # print(block_1.mining_nonce) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) bob_addr_state = state.get_address(bob_xmss.address) alice_addr_state = state.get_address(alice_xmss.address) random_addr_state = state.get_address(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))
def test_orphan_block(self): """ Testing add_block logic in case of orphan_blocks. This test is expected to shift the mainchain towards block_2. :return: """ with mock.patch('qrl.core.config.DevConfig') as devconfig: devconfig.genesis_difficulty = 2 devconfig.minimum_minting_delay = 10 with set_data_dir('no_data'): with State( ) as state: # FIXME: Move state to temporary directory state.get_measurement = MagicMock(return_value=10000000) genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_target = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock( return_value=(tmp_difficulty, tmp_target)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) alice_xmss = get_alice_xmss() with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1521889325 # Very high to get an easy difficulty block_1 = Block.create( block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], miner_address=alice_xmss.address) block_1.set_nonces(29, 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_1.blockheader, False): # block_1.set_nonces(block_1.mining_nonce + 1) # print(block_1.mining_nonce) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) bob_xmss = get_bob_xmss() with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1521889326 + devconfig.minimum_minting_delay * 2 block = Block.create( block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], miner_address=bob_xmss.address) block.set_nonces(246, 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.blockheader, False): block.set_nonces(block.mining_nonce + 1) print(block.mining_nonce) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1521889327 + devconfig.minimum_minting_delay * 3 block_2 = Block.create( block_number=2, prevblock_headerhash=block.headerhash, transactions=[], miner_address=bob_xmss.address) block_2.set_nonces(31, 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) # print(block_2.mining_nonce) result = chain_manager.add_block(block_2) self.assertTrue(result) result = chain_manager.add_block(block) self.assertTrue(result) block = state.get_block(block.headerhash) self.assertIsNotNone(block) self.assertEqual(chain_manager.last_block.block_number, block_2.block_number) self.assertEqual(chain_manager.last_block.headerhash, block_2.headerhash)
def test_add_block(self, mock_difficulty_tracker_get): """ Testing add_block, with fork logic :return: """ with set_data_dir('no_data'): with State() as state: state.get_measurement = MagicMock(return_value=10000000) alice_xmss = get_alice_xmss() bob_xmss = get_bob_xmss() genesis_block = GenesisBlock() chain_manager = ChainManager(state) mock_difficulty_tracker_get.return_value = [ config.dev.mining_setpoint_blocktime, 2 ] chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() tmp_difficulty = StringToUInt256('2') tmp_target = DifficultyTracker.get_target(tmp_difficulty) mock_difficulty_tracker_get.return_value = [ tmp_difficulty, tmp_target ] block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) slave_tx = SlaveTransaction.create(slave_pks=[bob_xmss.pk], access_types=[0], fee=0, xmss_pk=alice_xmss.pk) slave_tx.sign(alice_xmss) slave_tx._data.nonce = 1 self.assertTrue(slave_tx.validate()) with mock.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, prevblock_headerhash=genesis_block.headerhash, transactions=[slave_tx], miner_address=alice_xmss.address) block_1.set_nonces(274, 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_1.blockheader, False): # block_1.set_mining_nonce(block_1.mining_nonce + 1) # print(block_1.mining_nonce) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) alice_state = chain_manager.get_address(alice_xmss.address) self.assertEqual(len(alice_state.slave_pks_access_type), 1) self.assertTrue( str(bob_xmss.pk) in alice_state.slave_pks_access_type) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1715270948 # Very high to get an easy difficulty block = Block.create( block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], miner_address=bob_xmss.address) block.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(state, block.blockheader, False): # block.set_nonces(block.mining_nonce + 1) # print(block.mining_nonce) result = chain_manager.add_block(block) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) block = state.get_block(block.headerhash) self.assertIsNotNone(block) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1815270948 # Very high to get an easy difficulty block_2 = Block.create( block_number=2, prevblock_headerhash=block.headerhash, transactions=[], miner_address=bob_xmss.address) block_2.set_nonces(0, 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_mining_nonce(block_2.mining_nonce + 1) # print(block_2.mining_nonce) result = chain_manager.add_block(block_2) self.assertTrue(result) self.assertEqual(chain_manager.last_block.block_number, block_2.block_number) self.assertEqual(chain_manager.last_block.to_json(), block_2.to_json())
def test_add_4(self, mock_difficulty_tracker_get): with set_qrl_dir('wallet_ver1'): with State() as state: with mocked_genesis() as custom_genesis: chain_manager = ChainManager(state) chain_manager._difficulty_tracker = Mock() tmp_difficulty = StringToUInt256('2') tmp_target = DifficultyTracker.get_target(tmp_difficulty) mock_difficulty_tracker_get.return_value = [ tmp_difficulty, tmp_target ] alice_xmss = get_alice_xmss() slave_xmss = XMSS( XmssFast(alice_xmss.seed, alice_xmss.height)) random_xmss1 = get_random_xmss() random_kyber1 = Kyber() random_dilithium1 = Dilithium() random_xmss2 = get_random_xmss() random_kyber2 = Kyber() random_dilithium2 = Dilithium() message = b'Hello World How are you?' prf512_seed = b'10192' custom_genesis.genesis_balance.extend([ qrl_pb2.GenesisBalance(address=random_xmss1.address, balance=65000000000000000) ]) custom_genesis.genesis_balance.extend([ qrl_pb2.GenesisBalance(address=random_xmss2.address, balance=65000000000000000) ]) chain_manager.load(custom_genesis) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1615270948 lattice_public_key_txn = LatticePublicKey.create( fee=1, kyber_pk=random_kyber1.getPK(), dilithium_pk=random_dilithium1.getPK(), xmss_pk=random_xmss1.pk) lattice_public_key_txn._data.nonce = 1 lattice_public_key_txn.sign(random_xmss1) genesis_block = GenesisBlock() tmp_block1 = Block.create( block_number=1, prev_block_headerhash=genesis_block.headerhash, prev_block_timestamp=genesis_block.timestamp, transactions=[lattice_public_key_txn], miner_address=slave_xmss.address) # Mine the nonce while not PoWValidator().validate_mining_nonce( state, tmp_block1.blockheader, False): tmp_block1.set_nonces(tmp_block1.mining_nonce + 1, 0) res = chain_manager.add_block(block=tmp_block1) self.assertTrue(res) # Need to move forward the time to align with block times time_mock.return_value += config.dev.minimum_minting_delay * 2 encrypted_eph_message = create_ephemeral_channel( msg_id=lattice_public_key_txn.txhash, ttl=time_mock.return_value, ttr=0, addr_from=random_xmss2.address, kyber_pk=random_kyber2.getPK(), kyber_sk=random_kyber2.getSK(), receiver_kyber_pk=random_kyber1.getPK(), dilithium_pk=random_dilithium2.getPK(), dilithium_sk=random_dilithium2.getSK(), prf512_seed=prf512_seed, data=message, nonce=1) chain_manager.state.update_ephemeral( encrypted_eph_message) eph_metadata = chain_manager.state.get_ephemeral_metadata( lattice_public_key_txn.txhash) # Decrypting Payload encrypted_eph_message = eph_metadata.encrypted_ephemeral_message_list[ 0] encrypted_payload = encrypted_eph_message.payload random_kyber1.kem_decode( encrypted_eph_message.channel.enc_aes256_symkey) aes_key = bytes(random_kyber1.getMyKey()) myAES = AES(aes_key) decrypted_payload = myAES.decrypt(encrypted_payload) ephemeral_channel_payload = EphemeralChannelPayload.from_json( decrypted_payload) self.assertEqual(ephemeral_channel_payload.prf512_seed, b'10192') self.assertEqual(ephemeral_channel_payload.data, b'Hello World How are you?') # TODO (cyyber): Add Ephemeral Testing code using Naive RNG tmp_block2 = Block.create( block_number=2, prev_block_headerhash=tmp_block1.headerhash, prev_block_timestamp=tmp_block1.timestamp, transactions=[], miner_address=slave_xmss.address) # Mine the nonce while not PoWValidator().validate_mining_nonce( state, tmp_block2.blockheader, False): tmp_block2.set_nonces(tmp_block2.mining_nonce + 1, 0) res = chain_manager.add_block(block=tmp_block2) self.assertTrue(res) # Need to move forward the time to align with block times time_mock.return_value += config.dev.minimum_minting_delay * 2 tmp_block3 = Block.create( block_number=3, prev_block_headerhash=tmp_block2.headerhash, prev_block_timestamp=tmp_block1.timestamp, transactions=[], miner_address=slave_xmss.address) # Mine the nonce while not PoWValidator().validate_mining_nonce( state, tmp_block3.blockheader, False): tmp_block3.set_nonces(tmp_block3.mining_nonce + 1, 0) res = chain_manager.add_block(block=tmp_block3) self.assertTrue(res) time_mock.return_value += config.dev.minimum_minting_delay tmp_block4 = Block.create( block_number=4, prev_block_headerhash=tmp_block3.headerhash, prev_block_timestamp=tmp_block1.timestamp, transactions=[], miner_address=slave_xmss.address) # Mine the nonce while not PoWValidator().validate_mining_nonce( state, tmp_block4.blockheader, False): tmp_block4.set_nonces(tmp_block4.mining_nonce + 1, 0) res = chain_manager.add_block(block=tmp_block4) self.assertTrue(res) address_state = chain_manager.get_address( random_xmss1.address) self.assertEqual( address_state.latticePK_list[0].kyber_pk, lattice_public_key_txn.kyber_pk) self.assertEqual( address_state.latticePK_list[0].dilithium_pk, lattice_public_key_txn.dilithium_pk) self.assertEqual(address_state.address, lattice_public_key_txn.addr_from) random_xmss1_state = chain_manager.get_address( random_xmss1.address) self.assertEqual(64999999999999999, random_xmss1_state.balance)
def test_add_block(self): """ Testing add_block, with fork logic :return: """ with set_data_dir('no_data'): with State() as state: state.get_measurement = MagicMock(return_value=10000000) alice_xmss = get_alice_xmss() bob_xmss = get_bob_xmss() genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_boundary = dt.get_boundary(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock(return_value=(tmp_difficulty, tmp_boundary)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) slave_tx = SlaveTransaction.create(addr_from=alice_xmss.get_address(), slave_pks=[bob_xmss.pk()], access_types=[0], fee=0, xmss_pk=alice_xmss.pk(), xmss_ots_index=alice_xmss.get_index()) slave_tx.sign(alice_xmss) slave_tx._data.nonce = 2 self.assertTrue(slave_tx.validate()) with mock.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(mining_nonce=10, block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[slave_tx], signing_xmss=alice_xmss, master_address=alice_xmss.get_address(), nonce=1) while not chain_manager.validate_mining_nonce(block_1, False): block_1.set_mining_nonce(block_1.mining_nonce + 1) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) alice_state = chain_manager.get_address(alice_xmss.get_address()) self.assertEqual(len(alice_state.slave_pks_access_type), 1) self.assertTrue(str(bob_xmss.pk()) in alice_state.slave_pks_access_type) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1715270948 # Very high to get an easy difficulty block = Block.create(mining_nonce=15, block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], signing_xmss=bob_xmss, master_address=bob_xmss.get_address(), nonce=1) while not chain_manager.validate_mining_nonce(block, False): block.set_mining_nonce(block.mining_nonce + 1) result = chain_manager.add_block(block) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) block = state.get_block(block.headerhash) self.assertIsNotNone(block) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1815270948 # Very high to get an easy difficulty block_2 = Block.create(mining_nonce=15, block_number=2, prevblock_headerhash=block.headerhash, transactions=[], signing_xmss=bob_xmss, master_address=bob_xmss.get_address(), nonce=2) while not chain_manager.validate_mining_nonce(block_2, False): block_2.set_mining_nonce(block_2.mining_nonce + 1) result = chain_manager.add_block(block_2) self.assertTrue(result) self.assertEqual(chain_manager.last_block.block_number, block_2.block_number) self.assertEqual(chain_manager.last_block.to_json(), block_2.to_json())
def test_orphan_block(self): """ Testing add_block logic in case of orphan_blocks :return: """ with mock.patch('qrl.core.config.DevConfig') as devconfig: devconfig.genesis_difficulty = 2 devconfig.minimum_minting_delay = 10 with set_data_dir('no_data'): with State() as state: # FIXME: Move state to temporary directory state.get_measurement = MagicMock(return_value=10000000) genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_boundary = dt.get_boundary(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock(return_value=(tmp_difficulty, tmp_boundary)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) alice_xmss = get_alice_xmss() with mock.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(mining_nonce=10, block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], signing_xmss=alice_xmss, master_address=alice_xmss.get_address(), nonce=1) block_1.set_mining_nonce(10) while not chain_manager.validate_mining_nonce(block_1, False): block_1.set_mining_nonce(block_1.mining_nonce + 1) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) bob_xmss = get_bob_xmss() with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1615270948 + devconfig.minimum_minting_delay * 2 block = Block.create(mining_nonce=18, block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], signing_xmss=bob_xmss, master_address=bob_xmss.get_address(), nonce=1) block.set_mining_nonce(18) while not chain_manager.validate_mining_nonce(block, False): block.set_mining_nonce(block.mining_nonce + 1) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1615270948 + devconfig.minimum_minting_delay * 3 block_2 = Block.create(mining_nonce=17, block_number=2, prevblock_headerhash=block.headerhash, transactions=[], signing_xmss=bob_xmss, master_address=bob_xmss.get_address(), nonce=2) block_2.set_mining_nonce(17) result = chain_manager.add_block(block_2) self.assertTrue(result) result = chain_manager.add_block(block) self.assertTrue(result) block = state.get_block(block.headerhash) self.assertIsNotNone(block) self.assertEqual(chain_manager.last_block.block_number, block_1.block_number) self.assertEqual(chain_manager.last_block.headerhash, block_1.headerhash)
def test_multi_output_transaction_add_block(self): with set_data_dir('no_data'): with State() as state: state.get_measurement = MagicMock(return_value=10000000) alice_xmss = get_alice_xmss() bob_xmss = get_bob_xmss() random_xmss = get_random_xmss() transfer_transaction = TransferTransaction.create( addr_from=bob_xmss.address, addrs_to=[alice_xmss.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_xmss.pk) transfer_transaction._data.nonce = 1 transfer_transaction.sign(bob_xmss) genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_boundary = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock( return_value=(tmp_difficulty, tmp_boundary)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) with mock.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, prevblock_headerhash=genesis_block.headerhash, transactions=[transfer_transaction], signing_xmss=alice_xmss, master_address=alice_xmss.address, nonce=1) while not PoWValidator().validate_mining_nonce( state, block_1.blockheader, False): block_1.set_mining_nonce(block_1.mining_nonce + 1) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) bob_addr_state = state.get_address(bob_xmss.address) alice_addr_state = state.get_address(alice_xmss.address) random_addr_state = state.get_address(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))
def test_orphan_block(self): """ Testing add_block logic in case of orphan_blocks :return: """ with mock.patch('qrl.core.config.DevConfig') as devconfig: devconfig.genesis_difficulty = 2 devconfig.minimum_minting_delay = 10 with set_data_dir('no_data'): with State( ) as state: # FIXME: Move state to temporary directory state.get_measurement = MagicMock(return_value=10000000) genesis_block = GenesisBlock() chain_manager = ChainManager(state) chain_manager.load(genesis_block) chain_manager._difficulty_tracker = Mock() dt = DifficultyTracker() tmp_difficulty = StringToUInt256('2') tmp_boundary = dt.get_target(tmp_difficulty) chain_manager._difficulty_tracker.get = MagicMock( return_value=(tmp_difficulty, tmp_boundary)) block = state.get_block(genesis_block.headerhash) self.assertIsNotNone(block) alice_xmss = get_alice_xmss() with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1519601174 # Very high to get an easy difficulty block_1 = Block.create( block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], signing_xmss=alice_xmss, master_address=alice_xmss.address, nonce=1) block_1.set_mining_nonce(10) while not PoWValidator().validate_mining_nonce( state, block_1.blockheader, False): block_1.set_mining_nonce(block_1.mining_nonce + 1) result = chain_manager.add_block(block_1) self.assertTrue(result) self.assertEqual(chain_manager.last_block, block_1) bob_xmss = get_bob_xmss() with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1519601174 + devconfig.minimum_minting_delay * 2 block = Block.create( block_number=1, prevblock_headerhash=genesis_block.headerhash, transactions=[], signing_xmss=bob_xmss, master_address=bob_xmss.address, nonce=1) block.set_mining_nonce(18) while not PoWValidator().validate_mining_nonce( state, block.blockheader, False): block.set_mining_nonce(block.mining_nonce + 1) with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1519601174 + devconfig.minimum_minting_delay * 3 block_2 = Block.create( block_number=2, prevblock_headerhash=block.headerhash, transactions=[], signing_xmss=bob_xmss, master_address=bob_xmss.address, nonce=2) block_2.set_mining_nonce(10) result = chain_manager.add_block(block_2) self.assertTrue(result) result = chain_manager.add_block(block) self.assertTrue(result) block = state.get_block(block.headerhash) self.assertIsNotNone(block) self.assertEqual(chain_manager.last_block.block_number, block_2.block_number) self.assertEqual(chain_manager.last_block.headerhash, block_2.headerhash)
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)
class TestChainManager(TestCase): def setUp(self): self.state = Mock(autospec=State) self.state.get_measurement.return_value = 10000000 del GenesisBlock.instance # Removing Singleton instance self.genesis_block = GenesisBlock() self.chain_manager = ChainManager(self.state) self.chain_manager.tx_pool = Mock() self.chain_manager._difficulty_tracker = Mock() def test_fork_recovery_failed(self): # When switching to the longer chain fails, _fork_recovery() must _rollback and restore the shorter chain. # Mock out irrelevant functions self.chain_manager._update_block_number_mapping = Mock() # Switching to the new chain should fail! self.chain_manager.add_chain = Mock(return_value=False) self.chain_manager._rollback = Mock() block_1 = create_m_block(1, self.genesis_block, alice.address) block_2 = create_m_block(2, block_1, alice.address) block_1_alt = create_m_block(1, self.genesis_block, alice.address) block_2_alt = create_m_block(2, block_1_alt, alice.address) block_3_alt = create_m_block(3, block_2_alt, alice.address) fork_state = qrlstateinfo_pb2.ForkState( initiator_headerhash=block_3_alt.headerhash, fork_point_headerhash=self.genesis_block.headerhash, old_mainchain_hash_path=[b.headerhash for b in [block_1, block_2]], new_mainchain_hash_path=[ b.headerhash for b in [block_1_alt, block_2_alt, block_3_alt] ]) # _fork_recovery() will _rollback() to the genesis block and go on the longer chain. # At this point, _rollback() should return the old hash path as a backup # in case switching to the longer chain fails. self.chain_manager._rollback.return_value = [ block_2.headerhash, block_1.headerhash ] self.chain_manager._fork_recovery(block_3_alt, fork_state) # _fork_recovery() should have _rollback()ed when trying to switch to the longer chain self.chain_manager._rollback.assert_any_call( self.genesis_block.headerhash, fork_state) # _fork_recovery() should have _rollback()ed to the genesis block when trying to restore the shorter chain self.chain_manager._rollback.assert_called_with( self.genesis_block.headerhash) def test_fork_recovery_rollbacked_already(self): # Switching to the new chain works, but test that if the _rollback() has already happened, _fork_recovery() does # not hiccup # Mock out irrelevant functions self.chain_manager._update_block_number_mapping = Mock() # Switching to the new chain should succeed! self.chain_manager.add_chain = Mock(return_value=True) self.chain_manager._rollback = Mock() block_1 = create_m_block(1, self.genesis_block, alice.address) block_2 = create_m_block(2, block_1, alice.address) block_1_alt = create_m_block(1, self.genesis_block, alice.address) block_2_alt = create_m_block(2, block_1_alt, alice.address) block_3_alt = create_m_block(3, block_2_alt, alice.address) fork_state = qrlstateinfo_pb2.ForkState( initiator_headerhash=block_3_alt.headerhash, fork_point_headerhash=self.genesis_block.headerhash, old_mainchain_hash_path=[b.headerhash for b in [block_1, block_2]], new_mainchain_hash_path=[ b.headerhash for b in [block_1_alt, block_2_alt, block_3_alt] ]) # State.get_block() should say that we are already on block_1_alt self.chain_manager._state.get_block.return_value = block_1_alt # _fork_recovery() will not call _rollback(), because it has already happened. self.chain_manager._fork_recovery(block_3_alt, fork_state) # _fork_recovery() should have _rollback()ed when trying to switch to the longer chain self.chain_manager._rollback.assert_not_called() @patch('qrl.core.config') @patch('qrl.core.ChainManager.ChainManager.height', new_callable=PropertyMock) def test_add_block_doesnt_add_blocks_beyond_reorg_limit( self, m_height, m_config): # If we are at height 40000, what's the use of adding a block that's height 1? Simply ignore that block. m_config.dev.reorg_limit = 22000 m_height.return_value = 40000 block_1 = create_m_block(1, self.genesis_block, alice.address) ans = self.chain_manager.add_block(block_1) self.assertFalse(ans) def test_add_block_refuses_to_add_too_large_blocks(self): # State.get_block_size_limit() calculates how large each Block should be from the last 10 confirmed blocks. self.state.get_block_size_limit.return_value = 5000000 block_1 = create_m_block(1, self.genesis_block, alice.address) block_1.size = 6000000 ans = self.chain_manager.add_block(block_1) self.assertFalse(ans) def test_get_fork_point_failure_modes(self): block_0 = create_m_block( 0, Mock(headerhash=b'Fake Genesis', timestamp=replacement_getTime()), alice.address) block_1 = create_m_block(1, block_0, alice.address) block_2 = create_m_block(2, block_1, alice.address) # If _get_fork_point() ever reaches block_number 0, that means the genesis block is different! # Mock self.state leads us back to block_0 self.state.get_block.side_effect = [block_2, block_1, block_0] with self.assertRaises(Exception): self.chain_manager._get_fork_point(block_2) # If _get_fork_point() cannot find a particular block while walking back to the fork point, something has gone # very wrong # Mock self.state leads us back through a broken chain self.state.get_block.side_effect = [block_2, None, block_0] with self.assertRaises(Exception): self.chain_manager._get_fork_point(block_2) def test_apply_block_fails_if_state_changes_fail(self): # ChainManager._apply_block() should fail if Block.apply_state_changes() fails. block = create_m_block(50, self.genesis_block, alice.address) block.apply_state_changes.return_value = False ans = self.chain_manager._apply_block(block, []) self.assertFalse(ans) def test_try_branch_add_block_fails_if_apply_block_fails(self): # ChainManager._try_branch_add_block() should fail if ChainManager._apply_block() fails self.chain_manager._apply_block = Mock(return_value=False) block = create_m_block(50, self.genesis_block, alice.address) block_added, fork_flag = self.chain_manager._try_branch_add_block( block, []) self.assertFalse(block_added) self.assertFalse(fork_flag) def test_add_chain_fails_if_fork_recovery_didnt_complete_successfully( self): block_1 = create_m_block(1, self.genesis_block, alice.address) block_2 = create_m_block(2, block_1, alice.address) block_1_alt = create_m_block(1, self.genesis_block, alice.address) block_2_alt = create_m_block(2, block_1_alt, alice.address) block_3_alt = create_m_block(3, block_2_alt, alice.address) fork_state = qrlstateinfo_pb2.ForkState( initiator_headerhash=block_3_alt.headerhash, fork_point_headerhash=self.genesis_block.headerhash, old_mainchain_hash_path=[b.headerhash for b in [block_1, block_2]], new_mainchain_hash_path=[ b.headerhash for b in [block_1_alt, block_2_alt, block_3_alt] ]) # We want to add_chain(block_*_alt chain), but we're still on block_1 (we should have rolled back to genesis) self.chain_manager._last_block = block_1 ans = self.chain_manager.add_chain( [block_1_alt.headerhash, block_2_alt.headerhash], fork_state) self.assertFalse(ans) def test_add_chain_fails_if_apply_block_fails(self): block_1 = create_m_block(1, self.genesis_block, alice.address) block_2 = create_m_block(2, block_1, alice.address) block_1_alt = create_m_block(1, self.genesis_block, alice.address) block_2_alt = create_m_block(2, block_1_alt, alice.address) block_3_alt = create_m_block(3, block_2_alt, alice.address) fork_state = qrlstateinfo_pb2.ForkState( initiator_headerhash=block_3_alt.headerhash, fork_point_headerhash=self.genesis_block.headerhash, old_mainchain_hash_path=[b.headerhash for b in [block_1, block_2]], new_mainchain_hash_path=[ b.headerhash for b in [block_1_alt, block_2_alt, block_3_alt] ]) # we want to add_chain(block_*_alt chain), but for some reason applying a Block to the State didn't work. self.chain_manager._apply_block = Mock(return_value=False) ans = self.chain_manager.add_chain( [block_1_alt.headerhash, block_2_alt.headerhash], fork_state) self.assertFalse(ans) def test_get_transaction(self): # get_transaction() is simply a wrapper for State.get_tx_metadata self.chain_manager.get_tx_metadata(b'txhash') self.state.get_tx_metadata.assert_called_once()