def setUp(self): m_state = Mock(name='A Mock State', autospec=State) m_state.get_address_state.return_value = Mock( name='A Mock AddressState', autospec=OptimizedAddressState) self.chain_manager = Mock(autospec=ChainManager) self.chain_manager._state = m_state tx_attrs = { 'validate.return_value': True, # Custom validation for different Transaction Types 'validate_extended.return_value': True, # Master/slave XMSS tree validation; balance & fee, OTS key reuse 'validate_transaction_pool.return_value': True # checks for OTS key reuse within TransactionPool only } self.tx1 = make_tx(name='Mock TX 1', **tx_attrs) self.tx2 = make_tx(name='Mock TX 2', **tx_attrs) self.tx3 = make_tx(name='Mock TX 3', **tx_attrs) self.tx4 = make_tx(name='Mock TX 4', **tx_attrs) self.m_txpool = Mock(autospec=TransactionPool) self.m_txpool.get_pending_transaction.side_effect = [ (self.tx1, replacement_getTime()), (self.tx2, replacement_getTime()), (self.tx3, replacement_getTime()), (self.tx4, replacement_getTime()) ] self.m_broadcast_tx = Mock(autospec=P2PFactory.broadcast_tx) self.txnprocessor = TxnProcessor(chain_manager=self.chain_manager, transaction_pool_obj=self.m_txpool, broadcast_tx=self.m_broadcast_tx)
def test_validate_transaction_pool_reusing_ots_index(self, m_logger): """ Two different TransferTransactions. They are signed with the same OTS indexe, from the same public key. Therefore they should conflict. :return: """ tx = self.tx tx2 = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[100], message_data=None, fee=5, xmss_pk=self.alice.pk) # alice_clone's OTS index is still at 10, while self.alice will be at 11 after signing. alice_clone = get_alice_xmss() alice_clone.set_ots_index(10) tx.sign(self.alice) tx2.sign(alice_clone) tx_info = Mock(autospec=TransactionInfo, transaction=tx) tx2_info = Mock(autospec=TransactionInfo, transaction=tx2) transaction_pool = [(replacement_getTime(), tx_info), (replacement_getTime(), tx2_info)] result = tx.validate_transaction_pool(transaction_pool) self.assertFalse(result)
def test_is_future_block(self): self.blockheader.timestamp = replacement_getTime() + config.dev.block_max_drift + 1 result = self.block.is_future_block(config.dev) self.assertTrue(result) self.blockheader.timestamp = replacement_getTime() result = self.block.is_future_block(config.dev) self.assertFalse(result)
def test_is_full_transaction_pool(self, m_config): m_config.user.transaction_pool_size = 2 result = self.txpool.is_full_transaction_pool() self.assertFalse(result) tx1 = make_tx(fee=1) tx2 = make_tx(fee=2) self.txpool.add_tx_to_pool(tx1, 1, replacement_getTime()) self.txpool.add_tx_to_pool(tx2, 1, replacement_getTime()) result = self.txpool.is_full_transaction_pool() self.assertTrue(result)
def test_create_block_does_not_include_invalid_txs_from_txpool( self, m_create, m_logger): self.mock_new_state_container() m_tx = Mock(autospec=TransferTransaction, name='mock TransferTransaction') m_tx.validate_all.return_value = False m_tx.configure_mock(**self.m_tx_args) m_create.return_value = Mock(autospec=Block, name='mock Block', size=205) self.txpool.add_tx_to_pool(m_tx, 1, replacement_getTime()) self.miner.create_block(last_block=self.parent_block, mining_nonce=0, tx_pool=self.txpool, miner_address=self.m_mining_address) seed_block = self.chain_manager.get_block_by_number( self.miner._qn.get_seed_height(self.parent_block.block_number + 1)) m_create.assert_called_with(dev_config=config.dev, block_number=1, prev_headerhash=b'', prev_timestamp=0, transactions=[], miner_address=alice.address, seed_height=seed_block.block_number, seed_hash=seed_block.headerhash)
def test_get_tx_index_from_pool(self): tx1 = make_tx(txhash=b'red') tx2 = make_tx(txhash=b'blue') tx3 = make_tx(txhash=b'qrlpink') self.txpool.add_tx_to_pool(tx1, 1, replacement_getTime()) self.txpool.add_tx_to_pool(tx2, 1, replacement_getTime()) self.txpool.add_tx_to_pool(tx3, 1, replacement_getTime()) idx = self.txpool.get_tx_index_from_pool(b'qrlpink') self.assertEqual(idx, 2) idx = self.txpool.get_tx_index_from_pool(b'red') self.assertEqual(idx, 0) idx = self.txpool.get_tx_index_from_pool(b'ultraviolet') self.assertEqual(idx, -1)
def test_remove_tx_from_pool(self): tx1 = make_tx(txhash=b'red') tx2 = make_tx(txhash=b'blue') tx3 = make_tx(txhash=b'qrlpink') self.txpool.add_tx_to_pool(tx1, 1, replacement_getTime()) # If we try to remove a tx that wasn't there, the transaction pool should be untouched self.assertEqual(len(self.txpool.transaction_pool), 1) self.txpool.remove_tx_from_pool(tx2) self.assertEqual(len(self.txpool.transaction_pool), 1) # Now let's remove a tx from the heap. The size should decrease. self.txpool.add_tx_to_pool(tx2, 1, replacement_getTime()) self.txpool.add_tx_to_pool(tx3, 1, replacement_getTime()) self.assertEqual(len(self.txpool.transaction_pool), 3) self.txpool.remove_tx_from_pool(tx2) self.assertEqual(len(self.txpool.transaction_pool), 2)
def test_validate_transaction_pool(self, m_logger): """ Two TransferTransactions. Although they're the same, they are signed with different OTS indexes. Therefore they should not conflict when they are both in the TransactionPool. :return: """ tx = self.tx tx2 = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[100], fee=1, xmss_pk=self.alice.pk) tx.sign(self.alice) tx2.sign(self.alice) tx_info = Mock(autospec=TransactionInfo, transaction=tx) tx2_info = Mock(autospec=TransactionInfo, transaction=tx2) transaction_pool = [(replacement_getTime(), tx_info), (replacement_getTime(), tx2_info)] result = tx.validate_transaction_pool(transaction_pool) self.assertTrue(result)
def test_update_pending_tx_pool_tx_already_validated(self, m_is_full_pending_transaction_pool): """ If the tx is already in TransactionPool.transaction_pool, do not add it to pending_tx_pool. """ tx1 = make_tx() ip = '127.0.0.1' m_is_full_pending_transaction_pool.return_value = False self.txpool.add_tx_to_pool(tx1, 1, replacement_getTime()) result = self.txpool.update_pending_tx_pool(tx1, ip) self.assertFalse(result)
def test_create_block_with_one_transaction(self, m_create, m_logger): m_tx = Mock(autospec=TransferTransaction, name='mock TransferTransaction') m_tx.configure_mock(**self.m_tx_args) m_create.return_value = Mock(autospec=Block, name='mock Block', size=205) self.txpool.add_tx_to_pool(m_tx, 1, replacement_getTime()) self.miner.create_block(last_block=self.parent_block, mining_nonce=0, tx_pool=self.txpool, miner_address=self.m_mining_address) m_create.assert_called_with(block_number=1, prev_headerhash=b'', prev_timestamp=0, transactions=[m_tx], miner_address=alice.address)
def test_validate_transaction_pool_different_pk_same_ots_index( self, m_logger): """ Two TransferTransactions. They are signed with the same OTS indexes, but from different public keys. Therefore they should NOT conflict. :return: """ tx = self.tx tx2 = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[100], fee=1, xmss_pk=self.bob.pk) tx.sign(self.alice) tx2.sign(self.bob) tx_info = Mock(autospec=TransactionInfo, transaction=tx) tx2_info = Mock(autospec=TransactionInfo, transaction=tx2) transaction_pool = [(replacement_getTime(), tx_info), (replacement_getTime(), tx2_info)] result = tx.validate_transaction_pool(transaction_pool) self.assertTrue(result)
def create_m_block(block_number, previous_block, miner_address): mock_block = Mock(autospec=Block, name="Mock Block {}".format(block_number), block_number=block_number, prev_headerhash=previous_block.headerhash, prev_block_timestamp=previous_block.timestamp, transactions=[], miner_address=miner_address, headerhash="Mock Block {} {}".format( block_number, urandom(6)).encode(), timestamp=replacement_getTime()) mock_block.serialize.return_value = "Mock Block {}".format( block_number).encode() return mock_block
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_add_tx_to_pool_while_full(self, m_is_full_func): m_is_full_func.return_value = True tx = make_tx() result = self.txpool.add_tx_to_pool(tx, 1, replacement_getTime()) self.assertFalse(result) # refused to add to the pool self.assertEqual(len(self.txpool.transactions), 0) # remains untouched
def test_add_tx_to_pool(self): tx = make_tx() result = self.txpool.add_tx_to_pool(tx, 1, replacement_getTime()) self.assertTrue(result) self.assertEqual(len(self.txpool.transactions), 1)