def test_update_used_page_in_address_state(self): alice_xmss = get_alice_xmss(4) address = alice_xmss.address address_state = OptimizedAddressState.get_default(address) addresses_state = {address: address_state} paginated_bitfield = PaginatedBitfield(True, self.state._db) key = paginated_bitfield.generate_bitfield_key(address, 1) paginated_bitfield.update_used_page_in_address_state(address, addresses_state, 1) self.assertEqual(address_state.ots_bitfield_used_page, 0) for i in range(0, 16): paginated_bitfield.set_ots_key(addresses_state, address, i) if i != 15: self.assertEqual(address_state.ots_bitfield_used_page, 0) else: self.assertEqual(address_state.ots_bitfield_used_page, 1) for i in range(0, 16): ots_bitfield = paginated_bitfield.key_value[key] ots_key_index = i % config.dev.ots_tracking_per_page offset = ots_key_index >> 3 relative = ots_key_index % 8 bitfield = bytearray(ots_bitfield[offset]) self.assertEqual(bytes([bitfield[0] >> relative & 1]), b'\x01') self.assertEqual(address_state.ots_bitfield_used_page, 1)
def test_update_used_page_in_address_state4(self, mock_ots_bitfield_size, mock_ots_tracking_per_page): mock_ots_bitfield_size.return_value = ceil(config.dev.ots_tracking_per_page / 8) alice_xmss = get_alice_xmss(12) address = alice_xmss.address address_state = OptimizedAddressState.get_default(address) addresses_state = {address: address_state} paginated_bitfield = PaginatedBitfield(True, self.state._db) paginated_bitfield.update_used_page_in_address_state(address, addresses_state, 1) self.assertEqual(address_state.ots_bitfield_used_page, 0) for i in range(2048, 3072): paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, 0) for i in range(1024, 2048): paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, 0) for i in range(0, 1024): paginated_bitfield.set_ots_key(addresses_state, address, i) if i + 1 == 1024: self.assertEqual(address_state.ots_bitfield_used_page, 3) for i in range(3072, 2 ** alice_xmss.height): paginated_bitfield.set_ots_key(addresses_state, address, i) if i + 1 == 2 ** alice_xmss.height: self.assertEqual(address_state.ots_bitfield_used_page, 4) else: self.assertEqual(address_state.ots_bitfield_used_page, 3) self.assertEqual(address_state.ots_bitfield_used_page, 4)
def test_sync_state_change_synced(self): chain_manager = Mock() chain_manager.height = 0 chain_manager.get_block = MagicMock(return_value=GenesisBlock()) chain_manager.last_block = GenesisBlock() chain_manager.tx_pool = Mock() chain_manager.tx_pool.transaction_pool = [] chain_manager.tx_pool.transactions = chain_manager.tx_pool.transaction_pool get_block_metadata_response = Mock() get_block_metadata_response.block_difficulty = StringToUInt256('2') chain_manager.get_block_metadata = MagicMock( return_value=get_block_metadata_response) alice_xmss = get_alice_xmss() chain_manager._state.get_address_state = MagicMock( return_value=OptimizedAddressState.get_default(alice_xmss.address)) chain_manager._state.get_measurement = MagicMock(return_value=60) p2p_factory = Mock() sync_state = Mock() time_provider = Mock() # Setting mining enabled False, when update_note_state set to synced, # starts miner which is not exited properly by unit test with set_mining_enabled(False): node = POW(chain_manager=chain_manager, p2p_factory=p2p_factory, sync_state=sync_state, time_provider=time_provider, mining_address=get_random_xmss().address, mining_thread_count=0) self.assertIsNotNone(node) node.update_node_state(ESyncState.synced)
def test_apply_transfer_txn_tx_sends_to_self(self, m_logger): """ If you send coins to yourself, you should only lose the fee for the Transaction. """ addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.bob.address: OptimizedAddressState.get_default(self.bob.address), self.slave.address: OptimizedAddressState.get_default(self.slave.address) } addresses_state[self.alice.address].pbdata.balance = 200 tx = TransferTransaction.create(addrs_to=[self.alice.address], amounts=[100], message_data=None, fee=1, xmss_pk=self.alice.pk) tx.sign(self.alice) ots_key = self.alice.ots_index - 1 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) tx.apply(self.state, state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.assertEqual(199, addresses_state[self.alice.address].balance) storage_key = state_container.paginated_tx_hash.generate_key( self.alice.address, 1) self.assertIn(tx.txhash, state_container.paginated_tx_hash.key_value[storage_key])
def test_apply_coinbase_txn(self, m_logger): """ Alice earned some coins. """ 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 = 1000000000000000 tx = CoinBase.create(config.dev, self.amount, self.alice.address, self.mock_blockheader.block_number) 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) # self.state.apply(tx, addresses_state) tx.apply(self.state, state_container) reward = int( block_reward(self.mock_blockheader.block_number, config.dev)) self.assertEqual(1000000000000000 - reward, addresses_state[config.dev.coinbase_address].balance) storage_key = state_container.paginated_tx_hash.generate_key( config.dev.coinbase_address, 1) self.assertEqual( [tx.txhash], state_container.paginated_tx_hash.key_value[storage_key]) self.assertEqual(tx.amount, addresses_state[self.alice.address].balance) storage_key = state_container.paginated_tx_hash.generate_key( self.alice.address, 1) self.assertEqual( [tx.txhash], state_container.paginated_tx_hash.key_value[storage_key])
def test_apply_transfer_txn_multi_send(self, m_logger): """ Test that apply_state_changes() also works with multiple recipients. """ addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.bob.address: OptimizedAddressState.get_default(self.bob.address), self.slave.address: OptimizedAddressState.get_default(self.slave.address) } addresses_state[self.alice.address].pbdata.balance = 200 tx_multisend = TransferTransaction.create( addrs_to=[self.bob.address, self.slave.address], amounts=[20, 20], message_data=None, fee=1, xmss_pk=self.alice.pk) tx_multisend.sign(self.alice) ots_key = self.alice.ots_index - 1 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) tx_multisend.apply(self.state, state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.assertEqual(159, addresses_state[self.alice.address].balance) self.assertEqual(20, addresses_state[self.bob.address].balance) self.assertEqual(20, addresses_state[self.slave.address].balance)
def test_check_stale_txn(self, m_is_full_transaction_pool, m_config): """ Stale Transactions are Transactions that were supposed to go into block 5, but for some reason didn't make it. They languish in TransactionPool until check_stale_txn() checks the Pool and updates the tx_info to make them go into a higher block. For each stale transaction, P2PFactory.broadcast_tx() will be called. """ # Redefine at what point should txs be considered stale m_config.user.stale_transaction_threshold = 2 bob_xmss = get_bob_xmss(4) alice_xmss = get_alice_xmss(4) tx1 = TransferTransaction.create(addrs_to=[bob_xmss.address], amounts=[1000000], message_data=None, fee=1, xmss_pk=alice_xmss.pk) tx1.sign(alice_xmss) tx2 = TransferTransaction.create(addrs_to=[bob_xmss.address], amounts=[10000], message_data=None, fee=1, xmss_pk=alice_xmss.pk) tx2.sign(alice_xmss) m_broadcast_tx = Mock( name='Mock Broadcast TX function (in P2PFactory)') self.txpool.add_tx_to_pool(tx1, 5) self.txpool.add_tx_to_pool(tx2, 5) self.txpool.set_broadcast_tx(m_broadcast_tx) with set_qrl_dir('no_data'): state = State() chain_manager = ChainManager(state) self.txpool.check_stale_txn(chain_manager.new_state_container, chain_manager.update_state_container, 8) self.assertEqual(m_broadcast_tx.call_count, 0) m = MockFunction() bob_address_state = OptimizedAddressState.get_default( bob_xmss.address) bob_address_state.pbdata.balance = 1000000000000 m.put(bob_xmss.address, bob_address_state) chain_manager.get_optimized_address_state = m.get tx3 = TransferTransaction.create(addrs_to=[alice_xmss.address], amounts=[10000], message_data=None, fee=1, xmss_pk=bob_xmss.pk) tx3.sign(bob_xmss) self.txpool.add_tx_to_pool(tx3, 5) self.txpool.check_stale_txn(chain_manager.new_state_container, chain_manager.update_state_container, 8) self.assertEqual(m_broadcast_tx.call_count, 1)
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_validate_all(self): tx = MultiSigCreate.create(signatories=self.signatories, weights=self.weights, threshold=self.threshold, fee=5, xmss_pk=self.random.pk, master_addr=self.alice.address) tx.sign(self.random) tx.pbdata.nonce = 1 alice_address_state = OptimizedAddressState.get_default( address=self.alice.address) alice_address_state.pbdata.balance = 5 random_address_state = OptimizedAddressState.get_default( address=self.random.address) addresses_state = { self.alice.address: alice_address_state, self.random.address: random_address_state, } slaves = Indexer(b'slave', None) slaves.data[(self.alice.address, self.random.pk)] = SlaveMetadata(access_type=0) state_container = StateContainer(addresses_state=addresses_state, tokens=Indexer(b'token', None), slaves=slaves, lattice_pk=Indexer( b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=10, total_coin_supply=100, current_dev_config=config.dev, write_access=False, my_db=self.state._db, batch=None) result = tx.validate_all(state_container) self.assertTrue(result) tx.pbdata.nonce = 2 result = tx.validate_all(state_container) self.assertFalse(result) # False as nonce is invalid
def test_put_paginated_data(self, mock_dev_config): with set_qrl_dir('no_data'): state = State() p = PaginatedData(b'p_tx_hash', True, state._db) alice_address_state = OptimizedAddressState.get_default( address=self.alice.address) for i in range(0, 10): p.insert( alice_address_state, b'p_tx_hash_' + i.to_bytes(8, byteorder='big', signed=False)) self.assertEqual( alice_address_state.get_counter_by_name(p.name), i + 1) p.put_paginated_data(None) self.assertEqual(alice_address_state.get_counter_by_name(p.name), 10) for i in range(10, 25): p.insert( alice_address_state, b'p_tx_hash_' + i.to_bytes(8, byteorder='big', signed=False)) self.assertEqual( alice_address_state.get_counter_by_name(p.name), i + 1) p.put_paginated_data(None) self.assertEqual(alice_address_state.get_counter_by_name(p.name), 25) self.assertEqual(len(p.key_value), 0) pages_data = [] for i in range(0, (25 // config.dev.data_per_page) + 1): data = p.get_paginated_data( self.alice.address, (i + 1) * config.dev.data_per_page - 1) pages_data.append(data) self.assertEqual(len(pages_data), 3) self.assertEqual(len(pages_data[0]), 10) for i in range(0, 10): self.assertEqual( pages_data[0][i], b'p_tx_hash_' + i.to_bytes(8, byteorder='big', signed=False)) self.assertEqual(len(pages_data[1]), 10) for i in range(10, 20): self.assertEqual( pages_data[1][i - 10], b'p_tx_hash_' + i.to_bytes(8, byteorder='big', signed=False)) self.assertEqual(len(pages_data[2]), 5) for i in range(20, 25): self.assertEqual( pages_data[2][i - 20], b'p_tx_hash_' + i.to_bytes(8, byteorder='big', signed=False))
def test_revert_transfer_token_txn_multi_send(self): """ Alice has 1100 tokens and 100 QRL, Bob and Slave have none. Alice sends some tokens to Bob and Slave. Undo this. """ slave = get_slave_xmss() self.params["addrs_to"] = [self.bob.address, slave.address] self.params["amounts"] = [100, 100] tx = TransferTokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) addresses_state[self.alice.address].pbdata.balance = 100 addresses_state[self.bob.address].pbdata.balance = 0 addresses_state[slave.address] = OptimizedAddressState.get_default( slave.address) tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=1100) state_container = StateContainer(addresses_state=addresses_state, tokens=tokens, slaves=Indexer(b'slave', None), lattice_pk=Indexer( b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=1, total_coin_supply=1000, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) state_container.paginated_bitfield.set_ots_key(addresses_state, self.alice.address, 0) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, 0)) tx.apply(self.state, state_container) tx.revert(self.state, state_container) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, 0)) self.assertEqual(addresses_state[self.alice.address].balance, 100) self.assertEqual(addresses_state[self.bob.address].balance, 0) self.assertEqual(addresses_state[slave.address].balance, 0) self.assertEqual( tokens.data[(self.alice.address, tx.token_txhash)].balance, 1100) self.assertEqual( tokens.data[(self.bob.address, tx.token_txhash)].balance, 0) self.assertEqual(tokens.data[(slave.address, tx.token_txhash)].balance, 0)
def test_apply_token_txn_signed_by_slave_xmss(self): """ Alice creates a token, gives herself and Bob some tokens. But she uses a XMSS slave to sign it. """ initial_balances = [qrl_pb2.AddressAmount(address=self.alice.address, amount=1000), qrl_pb2.AddressAmount(address=self.bob.address, amount=2000)] slave = get_slave_xmss() self.params["initial_balances"] = initial_balances self.params["xmss_pk"] = slave.pk self.params["master_addr"] = self.alice.address tx = TokenTransaction.create(**self.params) tx.sign(slave) # Now that we have the Slave XMSS address, we should add it to AddressState so that apply_state_changes() # can do something with it addresses_state = dict(self.addresses_state) addresses_state[slave.address] = OptimizedAddressState.get_default(slave.address) 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=1, total_coin_supply=1000, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) tx.apply(self.state, state_container) self.assertEqual(addresses_state[self.alice.address].balance, 99) self.assertEqual(3, len(state_container.paginated_tx_hash.key_value)) storage_key = state_container.paginated_tx_hash.generate_key(self.alice.address, 1) self.assertIn(storage_key, state_container.paginated_tx_hash.key_value) self.assertEqual([tx.txhash], state_container.paginated_tx_hash.key_value[storage_key]) storage_key = state_container.paginated_tx_hash.generate_key(self.bob.address, 1) self.assertIn(storage_key, state_container.paginated_tx_hash.key_value) self.assertEqual([tx.txhash], state_container.paginated_tx_hash.key_value[storage_key]) storage_key = state_container.paginated_tx_hash.generate_key(slave.address, 1) self.assertIn(storage_key, state_container.paginated_tx_hash.key_value) self.assertEqual([tx.txhash], state_container.paginated_tx_hash.key_value[storage_key]) self.assertEqual(2, len(state_container.tokens.data)) self.assertIn((self.bob.address, tx.txhash), state_container.tokens.data) self.assertEqual(1000, state_container.tokens.data[(self.alice.address, tx.txhash)].balance) self.assertEqual(2000, state_container.tokens.data[(self.bob.address, tx.txhash)].balance) self.assertTrue(state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(slave.address, 0)) self.assertFalse(state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(self.alice.address, 0)) self.assertFalse(state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(self.bob.address, 0))
def test_apply_transfer_txn(self, m_logger): """ apply_state_changes() is the part that actually updates everybody's balances. Then it forwards the addresses_state to _apply_state_changes_for_PK(), which updates everybody's addresses's nonce, OTS key index, and associated TX hashes If there is no AddressState for a particular Address, nothing is done. """ self.tx.sign(self.alice) ots_key = self.alice.ots_index - 1 addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.bob.address: OptimizedAddressState.get_default(self.bob.address), self.slave.address: OptimizedAddressState.get_default(self.slave.address) } addresses_state[self.alice.address].pbdata.balance = 200 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.tx.apply(self.state, state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) # Now Alice should have 99 coins left (200 - 100 - 1) and Bob should have 100 coins. self.assertEqual(99, addresses_state[self.alice.address].balance) self.assertEqual(100, addresses_state[self.bob.address].balance)
def test_unset_ots_key(self, mock_ots_bitfield_size, mock_ots_tracking_per_page): """ Randomly using OTS key :return: """ with set_qrl_dir('no_data'): state = State() mock_ots_bitfield_size.return_value = ceil(config.dev.ots_tracking_per_page / 8) alice_xmss = get_alice_xmss(12) address = alice_xmss.address address_state = OptimizedAddressState.get_default(address) addresses_state = {address: address_state} paginated_bitfield = PaginatedBitfield(True, state._db) paginated_bitfield.update_used_page_in_address_state(address, addresses_state, 1) self.assertEqual(address_state.ots_bitfield_used_page, 0) ots_indexes = list(range(3072, 2 ** alice_xmss.height)) random.shuffle(ots_indexes) for i in ots_indexes: paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, 0) ots_indexes = list(range(2048, 3072)) random.shuffle(ots_indexes) for i in ots_indexes: paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, 0) ots_indexes = list(range(1024, 2048)) random.shuffle(ots_indexes) for i in ots_indexes: paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, 0) ots_indexes = list(range(0, 1024)) random.shuffle(ots_indexes) for i in ots_indexes: paginated_bitfield.set_ots_key(addresses_state, address, i) if i == ots_indexes[-1]: self.assertEqual(address_state.ots_bitfield_used_page, 4) else: self.assertEqual(address_state.ots_bitfield_used_page, 0) self.assertEqual(address_state.ots_bitfield_used_page, 4) paginated_bitfield.unset_ots_key(addresses_state, address, 2049) self.assertEqual(address_state.ots_bitfield_used_page, 2) paginated_bitfield.unset_ots_key(addresses_state, address, 1023) self.assertEqual(address_state.ots_bitfield_used_page, 0) paginated_bitfield.set_ots_key(addresses_state, address, 1023) self.assertEqual(address_state.ots_bitfield_used_page, 2)
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_load_bitfield_and_ots_key_reuse(self): with set_qrl_dir('no_data'): state = State() alice_xmss = get_alice_xmss(4) paginated_bitfield = PaginatedBitfield(True, state._db) self.assertFalse(paginated_bitfield.load_bitfield_and_ots_key_reuse(alice_xmss.address, 0)) addresses_state = { alice_xmss.address: OptimizedAddressState.get_default(alice_xmss.address) } paginated_bitfield.set_ots_key(addresses_state, alice_xmss.address, 0) self.assertTrue(paginated_bitfield.load_bitfield_and_ots_key_reuse(alice_xmss.address, 0))
def setUp(self): with set_qrl_dir('no_data'): self.state = State() self.chain_manager = ChainManager(self.state) self.alice = get_alice_xmss() self.bob = get_bob_xmss() self.slave = get_slave_xmss() self.tx1 = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[10], message_data=None, fee=1, xmss_pk=self.alice.pk) self.tx2 = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[10], message_data=None, fee=1, xmss_pk=self.slave.pk, master_addr=self.alice.address) self.tx1._data.nonce = 3 self.tx2._data.nonce = 6 self.tx1.sign(self.alice) self.tx2.sign(self.slave) self.block_attrs = { "dev_config": config.dev, "block_number": 5, "prev_headerhash": bytes(sha2_256(b'test')), "prev_timestamp": 10, "transactions": [self.tx1, self.tx2], "miner_address": self.alice.address, "seed_height": 0, "seed_hash": None, } self.coinbase_addrstate_attrs = OptimizedAddressState.get_default(config.dev.coinbase_address) self.coinbase_addrstate_attrs.update_balance(None, int(config.dev.coin_remaining_at_genesis * config.dev.shor_per_quanta)) self.bob_addrstate_attrs = OptimizedAddressState.get_default(self.bob.address) self.bob_addrstate_attrs.update_balance(None, 20) self.alice_addrstate_attrs = OptimizedAddressState.get_default(self.alice.address) self.alice_addrstate_attrs.update_balance(None, 100) self.alice_addrstate_attrs.pbdata.nonce = 2 self.slave_addrstate_attrs = OptimizedAddressState.get_default(self.slave.address) self.slave_addrstate_attrs.pbdata.nonce = 5
def setUp(self): with set_qrl_dir('no_data'): self.state = State() self.alice = get_alice_xmss() self.slave = get_slave_xmss() alice_address_state = OptimizedAddressState.get_default( self.alice.address) alice_address_state.pbdata.balance = 100 self.addresses_state = { self.alice.address: alice_address_state, self.slave.address: OptimizedAddressState.get_default(self.slave.address) } self.params = { "slave_pks": [self.slave.pk], "access_types": [0], "fee": 1, "xmss_pk": self.alice.pk } self.unused_chain_manager_mock = Mock(autospec=ChainManager, name='unused ChainManager')
def test_validate_extended(self, m_logger): """ validate_extended() handles these parts of the validation: 1. Master/slave 2. balance, amount + fee from AddressState 3. OTS key reuse from AddressState :return: """ alice_address_state = OptimizedAddressState.get_default( self.alice.address) addresses_state = {self.alice.address: alice_address_state} alice_address_state.pbdata.balance = 200 self.tx.validate_slave = Mock(autospec=Transaction.validate_slave, return_value=True) self.tx.sign(self.alice) 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) result = self.tx.validate_all(state_container) self.assertTrue(result) # Suppose there was ots key reuse. The function should then return false. state_container.paginated_bitfield.set_ots_key(addresses_state, self.tx.addr_from, self.tx.ots_key) result = self.tx.validate_all(state_container) self.assertFalse(result) # Suppose the address doesn't have enough coins. alice_address_state.pbdata.balance = 99 state_container.paginated_bitfield.set_ots_key(addresses_state, self.tx.addr_from, self.tx.ots_key) result = self.tx.validate_all(state_container) self.assertFalse(result)
def test_update_used_page_in_address_state2(self, mock_ots_bitfield_size, mock_ots_tracking_per_page): mock_ots_bitfield_size.return_value = ceil(config.dev.ots_tracking_per_page / 8) alice_xmss = get_alice_xmss(12) address = alice_xmss.address address_state = OptimizedAddressState.get_default(address) addresses_state = {address: address_state} paginated_bitfield = PaginatedBitfield(True, self.state._db) paginated_bitfield.update_used_page_in_address_state(address, addresses_state, 1) self.assertEqual(address_state.ots_bitfield_used_page, 0) factor = min(config.dev.ots_tracking_per_page, 2 ** alice_xmss.height) for i in range(0, 2 ** alice_xmss.height): paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, (i + 1) // factor) self.assertEqual(address_state.ots_bitfield_used_page, 4)
def test_validate_extended(self): """ 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 = MultiSigCreate.create(signatories=self.signatories, weights=self.weights, threshold=self.threshold, fee=5, xmss_pk=self.alice.pk) tx.sign(self.alice) alice_address_state = OptimizedAddressState.get_default( address=self.alice.address) alice_address_state.pbdata.balance = 5 addresses_state = { self.alice.address: alice_address_state, } 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=10, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=None, batch=None) result = tx._validate_extended(state_container) self.assertTrue(result) alice_address_state.pbdata.balance = 4 result = tx._validate_extended(state_container) self.assertFalse( result) # False due to insufficient balance to pay the txn fee alice_address_state.pbdata.balance = 6 result = tx._validate_extended(state_container) self.assertTrue(result)
def test_revert_state_changes_for_PK(self, m_logger): """ This is just an undo function. :return: """ addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address) } addresses_state[self.alice.address].pbdata.balance = 101 addresses_state[self.alice.address].pbdata.nonce = 1 tx = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[100], message_data=None, fee=1, xmss_pk=self.alice.pk) tx.sign(self.alice) ots_key = self.alice.ots_index - 1 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) state_container.paginated_bitfield.set_ots_key(addresses_state, self.alice.address, ots_key) state_container.paginated_bitfield.put_addresses_bitfield(None) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) tx._apply_state_changes_for_PK(state_container) tx._revert_state_changes_for_PK(state_container) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key))
def setUp(self): with set_qrl_dir('no_data'): self.state = State() self.alice = get_alice_xmss() alice_address_state = OptimizedAddressState.get_default( self.alice.address) alice_address_state.pbdata.balance = 100 self.addresses_state = {self.alice.address: alice_address_state} self.params = { "message_hash": b'Test Message', "addr_to": None, "fee": 1, "xmss_pk": self.alice.pk } self.unused_chain_manager_mock = Mock(autospec=ChainManager, name='unused ChainManager')
def test_unset_ots_key3(self, mock_ots_bitfield_size, mock_ots_tracking_per_page): """ Features Tested - Sequentially marking OTS indexes as used - Sequentially marking OTS indexes as unused - ots_bitfield_used_page value with each OTS index being used Expectation - The ots_bitfield_used_page must increase by 1 for every sequential 1024 (ots_tracking_per_page) ots indexes marked as used :param mock_ots_bitfield_size: :param mock_ots_tracking_per_page: :return: """ with set_qrl_dir('no_data'): state = State() mock_ots_bitfield_size.return_value = ceil(config.dev.ots_tracking_per_page / 8) alice_xmss = get_alice_xmss(12) address = alice_xmss.address address_state = OptimizedAddressState.get_default(address) addresses_state = {address: address_state} paginated_bitfield = PaginatedBitfield(True, state._db) paginated_bitfield.update_used_page_in_address_state(address, addresses_state, 1) self.assertEqual(address_state.ots_bitfield_used_page, 0) total_ots = 2 ** alice_xmss.height for i in range(0, total_ots + 1): paginated_bitfield.set_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, (i + 1) // config.dev.ots_tracking_per_page) self.assertEqual(address_state.ots_bitfield_used_page, total_ots // config.dev.ots_tracking_per_page) self.assertEqual(total_ots // config.dev.ots_tracking_per_page, 4) paginated_bitfield.unset_ots_key(addresses_state, address, total_ots - 1) self.assertEqual(address_state.ots_bitfield_used_page, 3) for i in range(total_ots - 2, -1, -1): paginated_bitfield.unset_ots_key(addresses_state, address, i) self.assertEqual(address_state.ots_bitfield_used_page, i // config.dev.ots_tracking_per_page)
def test_revert_paginated_data(self, mock_dev_config): with set_qrl_dir('no_data'): state = State() p = PaginatedData(b'p_tx_hash', True, state._db) alice_address_state = OptimizedAddressState.get_default( address=self.alice.address) for i in range(0, 25): p.insert( alice_address_state, b'p_tx_hash_' + i.to_bytes(8, byteorder='big', signed=False)) p.put_paginated_data(None) full_hash = [] for i in range(0, (25 // config.dev.data_per_page) + 1): data = p.get_paginated_data( self.alice.address, (i + 1) * config.dev.data_per_page - 1) full_hash.extend(data) for tx_hash in full_hash[24:18:-1]: p.remove(alice_address_state, tx_hash) p.put_paginated_data(None) self.assertEqual( alice_address_state.get_counter_by_name(b'p_tx_hash'), 19) # page 2 data count must be 0 self.assertEqual( len( p.get_paginated_data(alice_address_state.address, 2 * config.dev.data_per_page)), 0) # page 1 data count must be 9 self.assertEqual( len( p.get_paginated_data(alice_address_state.address, config.dev.data_per_page)), 9) # page 0 data count must be 10 self.assertEqual( len(p.get_paginated_data(alice_address_state.address, 0)), 10)
def test_apply_state_changes_for_PK(self, m_logger): """ This updates the node's AddressState database with which OTS index a particular address should be on, and what tx hashes is this address associated with. Curiously enough, if the TX was signed by a master XMSS tree, it doesn't add this tx's txhash to the list of txs that address is associated with. :return: """ addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address) } self.tx.sign(self.alice) ots_key = self.alice.ots_index - 1 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.tx._apply_state_changes_for_PK(state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.assertEqual(1, addresses_state[self.alice.address].nonce)
def test_revert_transfer_txn_multi_send(self, m_logger): """ Alice has sent 20 coins to Bob and Slave each, using 1 as Transaction fee. Now we need to undo this. """ tx_multisend = TransferTransaction.create( addrs_to=[self.bob.address, self.slave.address], amounts=[20, 20], message_data=None, fee=1, xmss_pk=self.alice.pk) tx_multisend.sign(self.alice) ots_key = self.alice.ots_index - 1 addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.bob.address: OptimizedAddressState.get_default(self.bob.address), self.slave.address: OptimizedAddressState.get_default(self.slave.address) } 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) state_container.paginated_bitfield.set_ots_key(addresses_state, self.alice.address, ots_key) state_container.paginated_bitfield.put_addresses_bitfield(None) addresses_state[self.alice.address].pbdata.balance = 200 addresses_state[self.bob.address].pbdata.balance = 0 addresses_state[self.slave.address].pbdata.balance = 0 self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) tx_multisend.apply(self.state, state_container) tx_multisend.revert(self.state, state_container) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.assertEqual(200, addresses_state[self.alice.address].balance) self.assertEqual(0, addresses_state[self.bob.address].balance) self.assertEqual(0, addresses_state[self.slave.address].balance) storage_key = state_container.paginated_tx_hash.generate_key( self.alice.address, 1) self.assertEqual( [], state_container.paginated_tx_hash.key_value[storage_key]) storage_key = state_container.paginated_tx_hash.generate_key( self.bob.address, 1) self.assertEqual( [], state_container.paginated_tx_hash.key_value[storage_key]) storage_key = state_container.paginated_tx_hash.generate_key( self.slave.address, 1) self.assertEqual( [], state_container.paginated_tx_hash.key_value[storage_key])
def test_validate_extended(self, m_validate_slave, m_logger): """ SlaveTransaction._validate_extended checks for: 1. valid master/slave 2. negative fee, 3. addr_from has enough funds for the fee 4. addr_from ots_key reuse """ alice_address_state = OptimizedAddressState.get_default( self.alice.address) alice_address_state.pbdata.balance = 100 addresses_state = {alice_address_state.address: alice_address_state} tx = SlaveTransaction.create(**self.params) tx.sign(self.alice) 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=1, 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.assertTrue(result) # Invalid master XMSS/slave XMSS relationship m_validate_slave.return_value = False result = tx.validate_all(state_container) self.assertFalse(result) m_validate_slave.return_value = True # fee = -1 with patch('qrl.core.txs.SlaveTransaction.SlaveTransaction.fee', new_callable=PropertyMock) as m_fee: m_fee.return_value = -1 result = tx._validate_custom() self.assertFalse(result) # balance = 0, cannot pay the Transaction fee alice_address_state.pbdata.balance = 0 result = tx._validate_extended(state_container) self.assertFalse(result) alice_address_state.pbdata.balance = 100 addresses_state = {self.alice.address: alice_address_state} # addr_from_pk has used this OTS key before state_container.paginated_bitfield.set_ots_key( addresses_state, alice_address_state.address, tx.ots_key) result = tx.validate_all(state_container) self.assertFalse(result) bob = get_bob_xmss() # Too many slave_pks with patch('qrl.core.config', autospec=True) as m_config: m_config.dev = config.dev.create(config.dev.prev_state_key, config.dev.current_state_key, b'', 10, True, True) m_config.dev.pbdata.transaction.multi_output_limit = 2 state_container.current_dev_config = m_config.dev params = self.params.copy() params["slave_pks"] = [self.alice.pk, bob.pk, self.slave.pk] params["access_types"] = [0, 0, 0] tx = SlaveTransaction.create(**params) self.assertFalse(tx._validate_extended(state_container))
def test_revert(self): multi_sig_address = MultiSigAddressState.generate_multi_sig_address( b'') multi_sig_address_state = MultiSigAddressState.create( creation_tx_hash=b'', balance=100, signatories=[self.alice.address, self.bob.address], weights=[4, 6], threshold=5, transaction_hash_count=0) alice_address_state = OptimizedAddressState.get_default( self.alice.address) alice_address_state.pbdata.balance = 5 alice_address_state.update_ots_bitfield_used_page() alice_address_state.used_ots_key_count += 1 alice_address_state.update_multi_sig_address_count() bob_address_state = OptimizedAddressState.get_default(self.bob.address) addresses_state = { self.alice.address: alice_address_state, self.bob.address: bob_address_state, multi_sig_address: multi_sig_address_state, } tx = MultiSigSpend.create(multi_sig_address=multi_sig_address, addrs_to=[self.bob.address], amounts=[100], expiry_block_number=15000, fee=5, xmss_pk=self.alice.pk) tx.sign(self.alice) tx._data.nonce = 1 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=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, tx.ots_key)) tx.apply(self.state, state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, tx.ots_key)) self.assertIn(tx.txhash, state_container.votes_stats) vote_stats = state_container.votes_stats[tx.txhash] self.assertEqual(vote_stats.shared_key, tx.txhash) self.assertEqual(vote_stats.total_weight, 0) self.assertEqual(vote_stats.signatories, multi_sig_address_state.signatories) tx.revert(self.state, state_container) self.assertNotIn(tx.txhash, state_container.votes_stats) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, tx.ots_key))
def test_validate_extended(self): """ TODO: Check by signing txn from a non signatory address """ multi_sig_address = MultiSigAddressState.generate_multi_sig_address( b'') tx = MultiSigSpend.create(multi_sig_address=multi_sig_address, addrs_to=[self.bob.address], amounts=[100], expiry_block_number=15000, fee=5, xmss_pk=self.alice.pk) tx.sign(self.alice) alice_address_state = OptimizedAddressState.get_default( address=self.alice.address) alice_address_state.pbdata.balance = 5 multi_sig_address_state = MultiSigAddressState.create( creation_tx_hash=b'', balance=100, signatories=[self.alice.address, self.bob.address], weights=[4, 6], threshold=5, transaction_hash_count=0) addresses_state = { self.alice.address: alice_address_state, multi_sig_address: multi_sig_address_state, } 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=10, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=None, batch=None) result = tx._validate_extended(state_container) self.assertTrue(result) alice_address_state.pbdata.balance = 0 result = tx._validate_extended(state_container) self.assertFalse(result) alice_address_state.pbdata.balance = 5 result = tx._validate_extended(state_container) self.assertTrue(result) multi_sig_address_state.pbdata.balance = 99 result = tx._validate_extended(state_container) self.assertFalse(result) multi_sig_address_state.pbdata.balance = 100 result = tx._validate_extended(state_container) self.assertTrue(result) tx.pbdata.multi_sig_spend.expiry_block_number = 10 result = tx._validate_extended(state_container) self.assertFalse(result) tx.pbdata.multi_sig_spend.expiry_block_number = 15000 result = tx._validate_extended(state_container) self.assertTrue(result)