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 setUp(self): with set_qrl_dir('no_data'): self.state = State() self.alice = get_alice_xmss() self.bob = get_bob_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.bob.address: OptimizedAddressState.get_default(self.bob.address) } self.params = { "symbol": b'QRL', "name": b'Quantum Resistant Ledger', "owner": self.alice.address, "decimals": 15, "initial_balances": [], "fee": 1, "xmss_pk": self.alice.pk } self.unused_chain_manager_mock = Mock(autospec=ChainManager, name='unused ChainManager')
def setUp(self): with set_qrl_dir('no_data'): self.state = State() self.alice = get_alice_xmss() self.bob = get_bob_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.bob.address: OptimizedAddressState.get_default(self.bob.address) } self.params = { "token_txhash": b'I declare the TEST token', "addrs_to": [self.bob.address], "amounts": [100], "fee": 1, "xmss_pk": self.alice.pk } self.unused_chain_manager_mock = Mock(autospec=ChainManager, name='unused ChainManager')
def _validate_custom(self): for amount in self.amounts: if amount == 0: logger.warning('Amount cannot be 0 - %s', self.amounts) logger.warning('TransferTokenTransaction') return False if self.fee < 0: logger.info('TransferTokenTransaction [%s] Invalid Fee = %d', bin2hstr(self.txhash), self.fee) return False if len(self.addrs_to) != len(self.amounts): logger.warning( '[TransferTokenTransaction] Mismatch number of addresses to & amounts' ) logger.warning('>> Length of addresses_to %s', len(self.addrs_to)) logger.warning('>> Length of amounts %s', len(self.amounts)) return False if not OptimizedAddressState.address_is_valid(self.addr_from): logger.warning( '[TransferTokenTransaction] Invalid address addr_from: %s', bin2hstr(self.addr_from)) return False for addr_to in self.addrs_to: if not OptimizedAddressState.address_is_valid(addr_to): logger.warning( '[TransferTokenTransaction] Invalid address addr_to: %s', bin2hstr(addr_to)) return False return True
def test_apply_multi_sig_create_txn2(self): """ Features Tested - Multi Sig Create txn with duplicate signatories Expectation - It should result into custom validation error throwing ValueError exception :return: """ addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.bob.address: OptimizedAddressState.get_default(self.bob.address), self.random.address: OptimizedAddressState.get_default(self.random.address), self.random_signer.address: OptimizedAddressState.get_default(self.random_signer.address), } addresses_state[self.random_signer.address].pbdata.balance = 200 modified_signatories = list(self.signatories) modified_weights = list(self.weights) # Appending bob as signatory second time modified_signatories.append(self.bob.address) modified_weights.append(2) tx = None with self.assertRaises(ValueError): tx = MultiSigCreate.create(modified_signatories, modified_weights, self.threshold, 1, self.random_signer.pk) self.assertIsNone(tx)
def test_apply(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 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) 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)
def test_validate_all(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.random.pk, master_addr=self.alice.address) tx.sign(self.random) tx._data.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) 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, self.random.address: random_address_state, multi_sig_address: multi_sig_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._data.nonce = 2 result = tx.validate_all(state_container) self.assertFalse(result) # False as nonce is invalid
def test_revert_transfer_txn(self, m_logger): """ Alice has sent 100 coins to Bob, using 1 as Transaction fee. Now we need to undo this. """ 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) } 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) 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)) self.tx.apply(self.state, state_container) self.tx.revert(self.state, state_container) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, ots_key)) self.assertEqual(0, addresses_state[self.alice.address].nonce) self.assertEqual(200, addresses_state[self.alice.address].balance) self.assertEqual(0, addresses_state[self.bob.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])
def test_get_optimized_address_state2(self): alice_xmss = get_alice_xmss() alice_address = alice_xmss.address address_state = OptimizedAddressState.get_optimized_address_state(self.state, alice_address) addresses_state = { alice_address: address_state } self.assertTrue(isinstance(address_state.address, bytes)) OptimizedAddressState.put_optimized_addresses_state(self.state, addresses_state) address_state = OptimizedAddressState.get_optimized_address_state(self.state, alice_address) self.assertTrue(isinstance(address_state.address, bytes))
def _validate_custom(self): if len(self.signatories) == 0: logger.warning("[MultiSigCreate] No Signatories found") return False for weight in self.weights: if weight == 0: logger.warning('Weight cannot be 0 - %s', weight) logger.warning('Invalid MultiSigCreate Transaction') return False if self.fee < 0: logger.warning('MultiSigCreate [%s] Invalid Fee = %d', bin2hstr(self.txhash), self.fee) return False if self.total_weight < self.threshold: logger.warning( '[MultiSigCreate] Validation failed for %s because: Insufficient weight', bin2hstr(self.txhash)) logger.warning('Total weight: %s, Threshold: %s', self.total_weight, self.threshold) return False if len(set(self.signatories)) != len(self.signatories): logger.warning( '[MultiSigCreate] Signatories list include duplicate signatories' ) return False if len(self.signatories) != len(self.weights): logger.warning( '[MultiSigCreate] Mismatch number of signatories & weights') logger.warning('>> Length of signatories %s', len(self.signatories)) logger.warning('>> Length of weights %s', len(self.weights)) return False if not OptimizedAddressState.address_is_valid(self.addr_from): logger.warning('[MultiSigCreate] Invalid address addr_from: %s', bin2hstr(self.addr_from)) return False for signatory in self.signatories: if not OptimizedAddressState.address_is_valid(signatory): logger.warning('[MultiSigCreate] Invalid address addr_to: %s', bin2hstr(signatory)) return False return True
def test_revert_state_changes_for_PK_master_slave_XMSS(self, m_logger): addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.slave.address: OptimizedAddressState.get_default(self.slave.address) } addresses_state[self.alice.address].pbdata.balance = 101 addresses_state[self.slave.address].pbdata.nonce = 1 tx = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[100], message_data=None, fee=1, xmss_pk=self.slave.pk, master_addr=self.alice.address) tx.sign(self.slave) ots_key = self.slave.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.slave.address, ots_key) state_container.paginated_bitfield.put_addresses_bitfield(None) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.slave.address, ots_key)) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, self.alice.ots_index)) 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.slave.address, ots_key)) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, self.alice.ots_index))
def test_apply_state_changes_for_PK_master_slave_XMSS(self, m_logger): """ If the TX was signed by a slave XMSS, the slave XMSS's AddressState should be updated (not the master's). :return: """ tx = TransferTransaction.create(addrs_to=[self.bob.address], amounts=[100], message_data=None, fee=1, xmss_pk=self.slave.pk, master_addr=self.alice.address) addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.slave.address: OptimizedAddressState.get_default(self.slave.address) } tx.sign(self.slave) ots_key = self.slave.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.slave.address, ots_key)) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, self.alice.ots_index)) tx._apply_state_changes_for_PK(state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.slave.address, ots_key)) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, self.alice.ots_index)) self.assertEqual(1, addresses_state[self.slave.address].nonce) self.assertEqual(0, addresses_state[self.alice.address].nonce)
def test_revert_transfer_txn_tx_sends_to_self(self, m_logger): """ Alice sent coins to herself, but she still lost the Transaction fee. Undo this. """ addresses_state = { self.alice.address: OptimizedAddressState.get_default(self.alice.address), self.bob.address: OptimizedAddressState.get_default(self.bob.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) 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(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, ots_key)) self.assertEqual(200, addresses_state[self.alice.address].balance) self.assertEqual(0, addresses_state[self.bob.address].balance)
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 validate_all(self, state_container: StateContainer, check_nonce=True) -> bool: if self.pbdata.WhichOneof('transactionType') == 'coinbase': if not self._validate_extended(state_container): return False return True if not self.validate(True): # It also calls _validate_custom return False if not self.validate_slave(state_container): return False if not self._validate_extended(state_container): return False addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) addr_from_pk_state = state_container.addresses_state[addr_from_pk] expected_nonce = addr_from_pk_state.nonce + 1 if check_nonce and self.nonce != expected_nonce: logger.warning('nonce incorrect, invalid tx') logger.warning('subtype: %s', self.type) logger.warning('%s actual: %s expected: %s', OptimizedAddressState.bin_to_qaddress(addr_from_pk), self.nonce, expected_nonce) return False if state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(addr_from_pk_state.address, self.ots_key): logger.warning('pubkey reuse detected: invalid tx %s', bin2hstr(self.txhash)) logger.warning('subtype: %s', self.type) return False return True
def get_block_to_mine(self, wallet_address, tx_pool, last_block, last_block_difficulty) -> list: dev_config = self._chain_manager.get_config_by_block_number(last_block.block_number + 1) try: mining_address = bytes(hstr2bin(wallet_address[1:].decode())) if not OptimizedAddressState.address_is_valid(mining_address): raise ValueError("[get_block_to_mine] Invalid Wallet Address %s", wallet_address) except Exception as e: raise ValueError("Error while decoding wallet address %s", e) if self._mining_block: if last_block.headerhash == self._mining_block.prev_headerhash: if self._mining_block.transactions[0].coinbase.addr_to == mining_address: return [bin2hstr(self._mining_block.mining_blob(dev_config)), int(bin2hstr(self._current_difficulty), 16)] else: self._mining_block.update_mining_address(dev_config, mining_address) # Updates only Miner Address self.prepare_next_unmined_block_template(mining_address, tx_pool, last_block, last_block_difficulty, dev_config=dev_config) return [bin2hstr(self._mining_block.mining_blob(dev_config)), int(bin2hstr(self._current_difficulty), 16)]
def test_get_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) total_hashes = 25 expected_full_hash = [] for i in range(0, total_hashes): tx_hash = b'p_tx_hash_' + i.to_bytes( 8, byteorder='big', signed=False) p.insert(alice_address_state, tx_hash) expected_full_hash.append(tx_hash) p.put_paginated_data(None) found_full_hash = [] expected_data_count = [10, 10, 5] for i in range(0, (total_hashes // config.dev.data_per_page) + 1): data = p.get_paginated_data( self.alice.address, (i + 1) * config.dev.data_per_page - 1) self.assertEqual(len(data), expected_data_count[i]) found_full_hash.extend(data) self.assertEqual(expected_full_hash, found_full_hash)
def test_revert_paginated_data2(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[-1::-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'), 0) self.assertEqual( len(p.get_paginated_data(alice_address_state.address, 22)), 0) self.assertEqual( len(p.get_paginated_data(alice_address_state.address, 12)), 0) self.assertEqual( len(p.get_paginated_data(alice_address_state.address, 2)), 0)
def _load_multi_sig_spend_txn_hashes(self, address: bytes, item_per_page: int, page_number: int, mode: int) -> list: if OptimizedAddressState.address_is_valid(address): address_state = self._chain_manager.get_optimized_address_state(address) elif MultiSigAddressState.address_is_valid(address): address_state = self._chain_manager.get_multi_sig_address_state(address) else: return [] start_item_index = max(0, address_state.multi_sig_spend_count() - item_per_page * page_number) end_item_index = min(address_state.multi_sig_spend_count(), start_item_index + item_per_page) if mode > 0: start_item_index = 0 end_item_index = address_state.multi_sig_spend_count() transaction_hashes = self._chain_manager.get_multi_sig_spend_txn_hashes(address, start_item_index) actual_start_item_index = (start_item_index // config.dev.data_per_page) * config.dev.data_per_page multi_sig_spend_txn_hashes = transaction_hashes[start_item_index - actual_start_item_index:] while actual_start_item_index < end_item_index and len(multi_sig_spend_txn_hashes) < item_per_page: actual_start_item_index += config.dev.data_per_page multi_sig_spend_txn_hashes.extend(self._chain_manager.get_multi_sig_spend_txn_hashes(address, actual_start_item_index)) return multi_sig_spend_txn_hashes[:item_per_page][-1::-1]
def get_optimized_address_state(self, address: bytes) -> OptimizedAddressState: if address != config.dev.coinbase_address and not OptimizedAddressState.address_is_valid(address): raise ValueError("Invalid Address") address_state = self._chain_manager.get_optimized_address_state(address) return address_state
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_ots_key_reuse(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) paginated_bitfield = PaginatedBitfield(True, state._db) alice_xmss = get_alice_xmss(12) address = alice_xmss.address address_state = OptimizedAddressState.get_default(address) addresses_state = {address: address_state} bitfield_data = paginated_bitfield.get_paginated_data(address, 1) self.assertFalse(paginated_bitfield.ots_key_reuse(bitfield_data, 0)) paginated_bitfield.set_ots_key(addresses_state, address, 0) bitfield_data = paginated_bitfield.get_paginated_data(address, 1) # False, as bitfield has been set but has not been written to state. self.assertFalse(paginated_bitfield.ots_key_reuse(bitfield_data, 0)) # Writing bitfield to the state. paginated_bitfield.put_addresses_bitfield(None) bitfield_data = paginated_bitfield.get_paginated_data(address, 1) self.assertTrue(paginated_bitfield.ots_key_reuse(bitfield_data, 0))
def test_set_ots_key2(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(0, 2**alice_xmss.height)) 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) self.assertEqual(address_state.ots_bitfield_used_page, 4)
def test_insert(self): with set_qrl_dir('no_data'): state = State() p = PaginatedData(b'p_tx_hash', True, state._db) p.insert(OptimizedAddressState.get_default(b'a'), b'10') self.assertEqual(p.get_value(b'a', 0), [b'10'])
def test_getHeight(self): with set_qrl_dir('no_data'): db_state = State() alice_xmss = get_alice_xmss() optimized_address_state = OptimizedAddressState.create(address=alice_xmss.address, nonce=25, balance=10, ots_bitfield_used_page=0, transaction_hash_count=0, tokens_count=0, lattice_pk_count=0, slaves_count=0, multi_sig_address_count=0) addresses_state = {optimized_address_state.address: optimized_address_state} AddressState.put_addresses_state(db_state, addresses_state) p2p_factory = Mock(spec=P2PFactory) chain_manager = ChainManager(db_state) chain_manager._last_block = Mock() chain_manager._last_block.block_number = 100 qrlnode = QRLNode(mining_address=b'') qrlnode.set_chain_manager(chain_manager) qrlnode._p2pfactory = p2p_factory qrlnode._peer_addresses = ['127.0.0.1', '192.168.1.1'] service = PublicAPIService(qrlnode) context = Mock(spec=ServicerContext) request = qrl_pb2.GetHeightReq() response = service.GetHeight(request=request, context=context) self.assertEqual(response.height, 100)
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_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 _validate_extended(self, state_container: StateContainer): if len(self.symbol ) > state_container.current_dev_config.max_token_symbol_length: logger.warning('Token Symbol Length exceeds maximum limit') logger.warning('Found Symbol Length %s', len(self.symbol)) logger.warning( 'Expected Symbol length %s', state_container.current_dev_config.max_token_symbol_length) return False if len(self.name ) > state_container.current_dev_config.max_token_name_length: logger.warning('Token Name Length exceeds maximum limit') logger.warning('Found Name Length %s', len(self.symbol)) logger.warning( 'Expected Name length %s', state_container.current_dev_config.max_token_name_length) return False if not OptimizedAddressState.address_is_valid(self.addr_from): logger.warning('Invalid address addr_from: %s', bin2hstr(self.addr_from)) return False tx_balance = state_container.addresses_state[self.addr_from].balance if not OptimizedAddressState.address_is_valid(self.owner): logger.warning('Invalid address owner_addr: %s', bin2hstr(self.owner)) return False for address_balance in self.initial_balances: if not OptimizedAddressState.address_is_valid( address_balance.address): logger.warning('Invalid address in initial_balances: %s', bin2hstr(address_balance.address)) return False if tx_balance < self.fee: logger.warning( 'TokenTxn State validation failed for %s because: Insufficient funds', bin2hstr(self.txhash)) logger.warning('balance: %s, Fee: %s', tx_balance, self.fee) return False return True
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)