def test_txpool_3907_block_4098_4099(self, m_is_full_transaction_pool, m_config): """ TxPool = [3907, 4095-4100, 4200] Block = [4098, 4099] TxPool Afterwards = [3907, 4095, 4100, 4200] """ # Ensure that a "large OTS index" is 4096 m_config.dev.max_ots_tracking_index = 4096 m_block = Mock(autospec=Block) m_block.transactions = [CoinBase(), self.tx_4098, self.tx_4099] self.txpool.add_tx_to_pool(self.tx_3907, 5) self.txpool.remove_tx_in_block_from_pool(m_block) txs_in_txpool = [ t[1].transaction for t in self.txpool.transaction_pool ] # 3907 should also be in the Pool since it is exempt from the counter self.assertEqual(len(self.txpool.transaction_pool), 4) self.assertNotIn(self.tx_4097, txs_in_txpool) self.assertNotIn(self.tx_4098, txs_in_txpool) self.assertNotIn(self.tx_4099, txs_in_txpool) self.assertIn(self.tx_3907, txs_in_txpool) self.assertIn(self.tx_4095, txs_in_txpool) self.assertIn(self.tx_4100, txs_in_txpool) self.assertIn(self.tx_4200, txs_in_txpool)
def test_revert_coinbase_txn(self, m_logger): """ Alice earned some coins. Undo this. """ tx = CoinBase.create(config.dev, self.amount, self.alice.address, self.mock_blockheader.block_number) addresses_state = { config.dev.coinbase_address: OptimizedAddressState.get_default(config.dev.coinbase_address), self.alice.address: OptimizedAddressState.get_default(self.alice.address), } addresses_state[config.dev.coinbase_address].pbdata.balance = 1000000 state_container = StateContainer(addresses_state=addresses_state, tokens=Indexer(b'token', None), slaves=Indexer(b'slave', None), lattice_pk=Indexer(b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=self.mock_blockheader.block_number, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) tx.apply(self.state, state_container) tx.revert(self.state, state_container) self.assertEqual(1000000, addresses_state[config.dev.coinbase_address].balance) storage_key = state_container.paginated_tx_hash.generate_key(config.dev.coinbase_address, 1) self.assertEqual([], state_container.paginated_tx_hash.key_value[storage_key]) self.assertEqual(0, addresses_state[self.alice.address].balance) storage_key = state_container.paginated_tx_hash.generate_key(config.dev.coinbase_address, 1) self.assertEqual([], state_container.paginated_tx_hash.key_value[storage_key])
def test_txpool_4095_4096_4097_otherppl_block_4098_4099(self, m_is_full_transaction_pool, m_config): """ TxPool = [4096-4100, 4200, 4095-4097_otherppl] Block = [4200] TxPool Afterwards = [4095, 4095-4097_otherppl] """ # Ensure that a "large OTS index" is 4096 m_config.dev.max_ots_tracking_index = 4096 m_block = Mock(autospec=Block) m_block.transactions = [CoinBase(), self.tx_4200] tx_other_4095 = make_tx(name='Mock TX 4095', txhash=b'h4095_other', ots_key=4095, PK='otherppl') tx_other_4096 = make_tx(name='Mock TX 4096', txhash=b'h4096_other', ots_key=4096, PK='otherppl') tx_other_4097 = make_tx(name='Mock TX 4097', txhash=b'h4097_other', ots_key=4097, PK='otherppl') self.txpool.add_tx_to_pool(tx_other_4095, 5) self.txpool.add_tx_to_pool(tx_other_4096, 5) self.txpool.add_tx_to_pool(tx_other_4097, 5) self.txpool.remove_tx_in_block_from_pool(m_block) txs_in_txpool = [t[1].transaction for t in self.txpool.transaction_pool] self.assertEqual(len(self.txpool.transaction_pool), 4) self.assertIn(self.tx_4095, txs_in_txpool) self.assertIn(tx_other_4095, txs_in_txpool) self.assertIn(tx_other_4096, txs_in_txpool) self.assertIn(tx_other_4097, txs_in_txpool)
def test_update_pending_tx_pool_rejects_coinbase_txs(self, m_is_full_pending_transaction_pool, m_logger): tx1 = CoinBase() ip = '127.0.0.1' m_is_full_pending_transaction_pool.return_value = False result = self.txpool.update_pending_tx_pool(tx1, ip) self.assertFalse(result)
def test_block_4098_4099(self, m_is_full_transaction_pool, m_config): """ TxPool = [4095-4100, 4200] Block = [4098, 4099] TxPool Afterwards = [4095, 4100, 4200] """ # Ensure that a "large OTS index" is 4096 m_config.dev.max_ots_tracking_index = 4096 m_block = Mock(autospec=Block) m_block.transactions = [CoinBase(), self.tx_4098, self.tx_4099] self.txpool.remove_tx_in_block_from_pool(m_block) txs_in_txpool = [ t[1].transaction for t in self.txpool.transaction_pool ] self.assertEqual(len(self.txpool.transaction_pool), 3) self.assertNotIn(self.tx_4097, txs_in_txpool) self.assertNotIn(self.tx_4098, txs_in_txpool) self.assertNotIn(self.tx_4099, txs_in_txpool) self.assertIn(self.tx_4095, txs_in_txpool) self.assertIn(self.tx_4100, txs_in_txpool) self.assertIn(self.tx_4200, txs_in_txpool)
def test_revert_state_changes(self, m_logger): """ Alice earned some coins. Undo this. """ tx = CoinBase.create(self.amount, self.alice.address, self.mock_blockheader.block_number) addresses_state = { config.dev.coinbase_address: Mock(autospec=AddressState, name='CoinBase AddressState', transaction_hashes=[tx.txhash], balance=1000000 - self.amount), self.alice.address: Mock(autospec=AddressState, name='alice AddressState', transaction_hashes=[tx.txhash], balance=self.amount), } unused_chain_manager_mock = Mock(autospec=ChainManager, name='unused ChainManager') tx.revert_state_changes(addresses_state, unused_chain_manager_mock) self.assertEqual(1000000, addresses_state[config.dev.coinbase_address].balance) self.assertEqual([], addresses_state[config.dev.coinbase_address].transaction_hashes) self.assertEqual(0, addresses_state[self.alice.address].balance) self.assertEqual([], addresses_state[self.alice.address].transaction_hashes) # A blank addresses_state doesn't get modified at all (but in practice, every node should have an AddressState # for the CoinBase addr addresses_state_empty = {} tx.revert_state_changes(addresses_state_empty, unused_chain_manager_mock) self.assertEqual({}, addresses_state_empty)
def test_affected_address(self, m_logger): # This transaction can only involve 2 addresses. affected_addresses = set() tx = CoinBase.create(config.dev, self.amount, self.alice.address, self.mock_blockheader.block_number) tx.set_affected_address(affected_addresses) self.assertEqual(2, len(affected_addresses))
def test_add_tx_from_block_to_pool(self, m_add_tx_to_pool, m_from_pbdata, m_logger): m_block = Mock(autospec=Block, block_number=5, headerhash=b'test block header') m_block.transactions = [CoinBase(), make_tx(), make_tx()] self.txpool.add_tx_from_block_to_pool(m_block, 5) self.assertEqual(m_add_tx_to_pool.call_count, 2) # 2 because the function ignores the Coinbase tx # If there is a problem adding to the tx_pool, the logger should be invoked. m_add_tx_to_pool.return_value = False self.txpool.add_tx_from_block_to_pool(m_block, 5) m_logger.warning.assert_called()
def test_from_txdict(self, m_logger): amount = self.mock_blockheader.block_reward + self.mock_blockheader.fee_reward tx = CoinBase.create(amount, self.alice.address, self.mock_blockheader.block_number) self.assertIsInstance(tx, CoinBase) # Test that common Transaction components were copied over. self.assertEqual(self.mock_blockheader.block_number + 1, tx.nonce) self.assertEqual('010300a1da274e68c88b0ccf448e0b1916fa789b01eb2ed4e9ad565ce264c9390782a9c61ac02f', bin2hstr(tx.addr_to)) self.assertEqual('222460cc57ab8683b46f1831fe6cf1832c7e3134baf74d33bfaf91741e19cba2', bin2hstr(tx.txhash)) self.assertEqual(tx.amount, 90)
def test_validate_custom(self, m_logger): """ CoinBase _validate_custom() only checks if fee == 0 """ tx = CoinBase.create(self.amount, self.alice.address, self.mock_blockheader.block_number) tx._data.fee = 1 result = tx._validate_custom() self.assertFalse(result) tx._data.fee = 0 result = tx._validate_custom() self.assertTrue(result)
def test_get_mini_transactions_by_address(self, mock_get_tx_metadata, mock_get_optimized_address_state, mock_get_paginated_data): """ QRLNode.get_transactions_by_address() returns all the changes in balance caused by a transaction. """ get_tx_metadata = GetTXMetadata() xmss = get_alice_xmss() xmss2 = get_random_xmss() addr_state = OptimizedAddressState.get_default(xmss.address) addr_state.pbdata.balance = 100 addr_state.pbdata.transaction_hash_count = 3 mock_get_optimized_address_state.return_value = addr_state tx1 = CoinBase.create(config.dev, 100, xmss.address, 5) get_tx_metadata.register_tx_metadata(tx1, 1) tx2 = TransferTransaction.create(addrs_to=[xmss2.address], amounts=[10], message_data=None, fee=1, xmss_pk=xmss.pk) tx2.sign(xmss) get_tx_metadata.register_tx_metadata(tx2, 1) tx3 = TransferTransaction.create(addrs_to=[xmss.address], amounts=[100], message_data=None, fee=1, xmss_pk=xmss2.pk) tx3.sign(xmss) get_tx_metadata.register_tx_metadata(tx3, 2) mock_get_paginated_data.return_value = [ tx1.txhash, tx2.txhash, tx3.txhash ] mock_get_tx_metadata.side_effect = get_tx_metadata.get_tx_metadata response = self.qrlnode.get_mini_transactions_by_address( alice.address, 3, 1) result, balance = response.mini_transactions, response.balance self.assertEqual(len(result), 3) self.assertEqual(result[0].amount, 100) self.assertEqual(result[0].out, False) self.assertEqual(result[1].amount, 11) self.assertEqual(result[1].out, True) self.assertEqual(result[2].amount, 100) self.assertEqual(result[2].out, False) self.assertEqual(balance, 100)
def test_extra_coinbase_tx(self, m_TransferTransaction_validate, m_TransferTransaction_validate_extended, m_TransferTransaction_apply_state_changes, m_CoinBase_apply_state_changes): address_states = self.generate_address_states( self.alice_addrstate_attrs, {}, self.slave_addrstate_attrs) coinbase_extra = CoinBase.create(500, self.alice.address, 5) self.block_attrs["transactions"] = [self.tx1, coinbase_extra, self.tx2] block = Block.create(**self.block_attrs) result = block.apply_state_changes(address_states) self.assertFalse(result)
def test_remove_tx_in_block_from_pool(self): m_block = Mock(autospec=Block) tx1 = make_tx(name='Mock TX 1', ots_key=1, PK=b'pk') tx2 = make_tx(name='Mock TX 2', ots_key=2, PK=b'pk') m_block.transactions = [CoinBase(), tx1, tx2] # To remove the tx from the pool we have to add it first! self.txpool.add_tx_to_pool(tx1, 5) self.txpool.add_tx_to_pool(tx2, 5) self.assertEqual(len(self.txpool.transaction_pool), 2) self.txpool.remove_tx_in_block_from_pool(m_block) self.assertEqual(len(self.txpool.transaction_pool), 0)
def create(dev_config: DevConfig, block_number: int, prev_headerhash: bytes, prev_timestamp: int, transactions: list, miner_address: bytes, seed_height: Optional[int], seed_hash: Optional[bytes]): block = Block() # Process transactions hashedtransactions = [] fee_reward = 0 for tx in transactions: fee_reward += tx.fee # Prepare coinbase tx total_reward_amount = BlockHeader.block_reward_calc( block_number, dev_config) + fee_reward coinbase_tx = CoinBase.create(dev_config, total_reward_amount, miner_address, block_number) hashedtransactions.append(coinbase_tx.txhash) Block._copy_tx_pbdata_into_block( block, coinbase_tx) # copy memory rather than sym link for tx in transactions: hashedtransactions.append(tx.txhash) Block._copy_tx_pbdata_into_block( block, tx) # copy memory rather than sym link txs_hash = merkle_tx_hash( hashedtransactions) # FIXME: Find a better name, type changes tmp_blockheader = BlockHeader.create(dev_config=dev_config, blocknumber=block_number, prev_headerhash=prev_headerhash, prev_timestamp=prev_timestamp, hashedtransactions=txs_hash, fee_reward=fee_reward, seed_height=seed_height, seed_hash=seed_hash) block.blockheader = tmp_blockheader block._data.header.MergeFrom(tmp_blockheader.pbdata) block.set_nonces(dev_config, 0, 0) return block
def test_get_transactions_by_address(self): """ QRLNode.get_transactions_by_address() returns all the changes in balance caused by a transaction. """ mock_get_tx_metadata = GetTXMetadata() xmss = get_alice_xmss() xmss2 = get_random_xmss() addr_state = AddressState.get_default(xmss.address) addr_state.pbdata.balance = 100 tx1 = CoinBase.create(100, xmss.address, 5) mock_get_tx_metadata.register_tx_metadata(tx1, 5) addr_state.transaction_hashes.append(tx1.txhash) tx2 = TransferTransaction.create(addrs_to=[xmss2.address], amounts=[10], fee=1, xmss_pk=xmss.pk) tx2.sign(xmss) mock_get_tx_metadata.register_tx_metadata(tx2, 99) addr_state.transaction_hashes.append(tx2.txhash) tx3 = TransferTransaction.create(addrs_to=[xmss.address], amounts=[100], fee=1, xmss_pk=xmss2.pk) tx3.sign(xmss) mock_get_tx_metadata.register_tx_metadata(tx3, 101) addr_state.transaction_hashes.append(tx3.txhash) self.db_state.get_address_state.return_value = addr_state self.db_state.get_tx_metadata = mock_get_tx_metadata.get_tx_metadata result, balance = self.qrlnode.get_transactions_by_address( alice.address) self.assertEqual(len(result), 3) self.assertEqual(result[0].amount, 100) self.assertEqual(result[0].out, False) self.assertEqual(result[1].amount, 11) self.assertEqual(result[1].out, True) self.assertEqual(result[2].amount, 100) self.assertEqual(result[2].out, False) self.assertEqual(balance, 189)
def test_extra_coinbase_tx(self, m_TransferTransaction_validate, m_TransferTransaction_validate_extended, m_TransferTransaction_apply_state_changes, m_CoinBase_apply_state_changes): get_optimized_address_state = MockFunction() get_optimized_address_state.put(self.coinbase_addrstate_attrs.address, self.coinbase_addrstate_attrs) get_optimized_address_state.put(self.bob_addrstate_attrs.address, self.bob_addrstate_attrs) get_optimized_address_state.put(self.alice_addrstate_attrs.address, self.alice_addrstate_attrs) get_optimized_address_state.put(self.slave_addrstate_attrs.address, self.slave_addrstate_attrs) coinbase_extra = CoinBase.create(config.dev, 500, self.alice.address, 5) self.block_attrs["transactions"] = [self.tx1, coinbase_extra, self.tx2] block = Block.create(**self.block_attrs) result = self.chain_manager._apply_state_changes(block, None) self.assertFalse(result)
def test_validate_extended(self, m_logger): """ CoinBase validate_extended() checks for 1. valid coinbase address (the coinbase address must be config.dev.coinbase_address) 2. valid addr_to then calls _validate_custom() """ tx = CoinBase.create(config.dev, self.amount, self.alice.address, self.mock_blockheader.block_number) tx._data.master_addr = self.alice.address addresses_state = { config.dev.coinbase_address: OptimizedAddressState.get_default(config.dev.coinbase_address), self.alice.address: OptimizedAddressState.get_default(self.alice.address), } addresses_state[config.dev.coinbase_address].pbdata.balance = 1000000 state_container = StateContainer( addresses_state=addresses_state, tokens=Indexer(b'token', None), slaves=Indexer(b'slave', None), lattice_pk=Indexer(b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=self.mock_blockheader.block_number, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) result = tx._validate_extended(state_container) self.assertFalse(result) tx._data.master_addr = config.dev.coinbase_address with patch('qrl.core.txs.CoinBase.CoinBase.addr_to', new_callable=PropertyMock) as m_addr_to: m_addr_to.return_value = b'Fake Address' result = tx._validate_extended(state_container) self.assertFalse(result) result = tx._validate_extended(state_container) self.assertTrue(result)
def test_block_1000(self, m_is_full_transaction_pool, m_config): """ TxPool = [4095-4100, 4200] Block = [1000] TxPool Afterwards = [4095-4100, 4200] """ # Ensure that a "large OTS index" is 4096 m_config.dev.max_ots_tracking_index = 4096 tx_1000 = make_tx(name='Mock TX 1000', txhash=b'h1000', ots_key=1000) m_block = Mock(autospec=Block) m_block.transactions = [CoinBase(), tx_1000] self.assertEqual(7, len(self.txpool.transaction_pool)) self.txpool.remove_tx_in_block_from_pool(m_block) txs_in_txpool = [t[1].transaction for t in self.txpool.transaction_pool] self.assertEqual(7, len(txs_in_txpool))
def block_params(self, msg_hash: bytes, block): if msg_hash not in self.requested_hash: return False params = self.requested_hash[msg_hash].params coinbase_tx = CoinBase.from_pbdata(block.transactions[0]) if coinbase_tx.addr_from != params.stake_selector: return False if block.block_number != params.block_number: return False if block.prev_headerhash != params.prev_headerhash: return False if block.reveal_hash != params.reveal_hash: return False return True
def create(block_number: int, prev_headerhash: bytes, prev_timestamp: int, transactions: list, miner_address: bytes): block = Block() # Process transactions hashedtransactions = [] fee_reward = 0 for tx in transactions: fee_reward += tx.fee # Prepare coinbase tx total_reward_amount = BlockHeader.block_reward_calc( block_number) + fee_reward coinbase_tx = CoinBase.create(total_reward_amount, miner_address, block_number) hashedtransactions.append(coinbase_tx.txhash) block._data.transactions.extend([coinbase_tx.pbdata ]) # copy memory rather than sym link for tx in transactions: hashedtransactions.append(tx.txhash) block._data.transactions.extend( [tx.pbdata]) # copy memory rather than sym link txs_hash = merkle_tx_hash( hashedtransactions) # FIXME: Find a better name, type changes tmp_blockheader = BlockHeader.create(blocknumber=block_number, prev_headerhash=prev_headerhash, prev_timestamp=prev_timestamp, hashedtransactions=txs_hash, fee_reward=fee_reward) block.blockheader = tmp_blockheader block._data.header.MergeFrom(tmp_blockheader.pbdata) block.set_nonces(0, 0) return block
def test_validate_extended(self, m_logger): """ CoinBase validate_extended() checks for 1. valid coinbase address (the coinbase address must be config.dev.coinbase_address) 2. valid addr_to then calls _validate_custom() """ tx = CoinBase.create(self.amount, self.alice.address, self.mock_blockheader.block_number) tx._data.master_addr = self.alice.address result = tx.validate_extended(self.mock_blockheader.block_number) self.assertFalse(result) tx._data.master_addr = config.dev.coinbase_address with patch('qrl.core.txs.CoinBase.CoinBase.addr_to', new_callable=PropertyMock) as m_addr_to: m_addr_to.return_value = b'Fake Address' result = tx.validate_extended(self.mock_blockheader.block_number) self.assertFalse(result) result = tx.validate_extended(self.mock_blockheader.block_number) self.assertTrue(result)
def test_apply_state_changes(self, m_logger): """ Alice earned some coins. """ addresses_state = { config.dev.coinbase_address: Mock(autospec=AddressState, name='CoinBase AddressState', transaction_hashes=[], balance=1000000), self.alice.address: Mock(autospec=AddressState, name='alice AddressState', transaction_hashes=[], balance=0), } tx = CoinBase.create(self.amount, self.alice.address, self.mock_blockheader.block_number) tx.apply_state_changes(addresses_state) self.assertEqual(1000000 - tx.amount, addresses_state[config.dev.coinbase_address].balance) self.assertEqual([tx.txhash], addresses_state[config.dev.coinbase_address].transaction_hashes) self.assertEqual(tx.amount, addresses_state[self.alice.address].balance) self.assertEqual([tx.txhash], addresses_state[self.alice.address].transaction_hashes) # A blank addresses_state doesn't get modified at all (but in practice, every node should have an AddressState # for the CoinBase addr addresses_state_empty = {} tx.apply_state_changes(addresses_state_empty) self.assertEqual({}, addresses_state_empty)
def test_to_json(self, m_logger): amount = self.mock_blockheader.block_reward + self.mock_blockheader.fee_reward tx = CoinBase.create(amount, self.alice.address, self.mock_blockheader.block_number) txjson = tx.to_json() self.assertEqual(json.loads(test_json_CoinBase), json.loads(txjson))
def test_create(self, m_logger): tx = CoinBase.create(self.amount, self.alice.address, self.mock_blockheader.block_number) self.assertIsInstance(tx, CoinBase)