def test_apply_token_txn(self): """ Alice creates a token. Obviously, she gives herself some of this token. But she also gives Bob some tokens too. """ initial_balances = [qrl_pb2.AddressAmount(address=self.alice.address, amount=1000), qrl_pb2.AddressAmount(address=self.bob.address, amount=2000)] self.params["initial_balances"] = initial_balances tx = TokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_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=1, total_coin_supply=1000, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) # According to the State, Alice has 100 coins, and Bob has 0 coins. # After applying the Transaction, Alice and Bob should have 1000 tokens, and Alice's balance should be 99. # AddressState.transaction_hashes now also reference the TokenTransaction that created the Tokens. self.assertFalse(state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(self.alice.address, 0)) tx.apply(self.state, state_container) self.assertTrue(state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(self.alice.address, 0)) self.assertEqual(addresses_state[self.alice.address].balance, 99) self.assertEqual(addresses_state[self.bob.address].balance, 0) self.assertEqual(2, 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]) self.assertEqual(2, len(state_container.tokens.data)) self.assertIn((self.alice.address, tx.txhash), state_container.tokens.data) self.assertEqual(1000, state_container.tokens.data[(self.alice.address, tx.txhash)].balance) self.assertIn((self.bob.address, tx.txhash), state_container.tokens.data) self.assertEqual(2000, state_container.tokens.data[(self.bob.address, tx.txhash)].balance)
def test_revert_token_txn_owner_not_in_address_state(self): """ In this case, Alice didn't give herself any tokens. How generous! She gave them all to Bob. But we want to revert this. """ initial_balances = [qrl_pb2.AddressAmount(address=self.bob.address, amount=1000)] self.params["initial_balances"] = initial_balances tx = TokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) addresses_state[self.alice.address].pbdata.balance = 100 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) 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(2, 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([], 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([], state_container.paginated_tx_hash.key_value[storage_key]) self.assertEqual(1, len(state_container.tokens.data)) self.assertIn((self.bob.address, tx.txhash), state_container.tokens.data) self.assertEqual(0, state_container.tokens.data[(self.bob.address, tx.txhash)].balance)
def mock_new_state_container(self): addresses_state = {alice.address: self.alice_address_state} self.chain_manager.new_state_container.return_value = 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=5, total_coin_supply=0, current_dev_config=config.dev, write_access=False, my_db=None, batch=None)
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_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_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_revert_transfer_token_txn(self): """ Alice has 1000 tokens and 100 QRL, Bob has none. Alice sends some tokens to Bob. Let's undo this. """ 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 tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=1000) 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( tokens.data[(self.alice.address, tx.token_txhash)].balance, 1000) self.assertEqual( tokens.data[(self.bob.address, tx.token_txhash)].balance, 0)
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_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 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_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_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_revert_transfer_token_txn_send_tokens_to_self(self): """ Alice has 1000 tokens and 100 QRL. She sends some tokens to herself. Can we undo this? """ self.params["addrs_to"] = [self.alice.address] tx = TransferTokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) addresses_state[self.alice.address].pbdata.balance = 100 tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=100) 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) # Unfortunately importing mock.call results in some sort of ValueError so I can't check the arguments. self.assertEqual( tokens.data[(self.alice.address, tx.token_txhash)].balance, 100)
def test_apply_state_changes_owner_not_in_address_state(self): """ In this case, Alice didn't give herself any tokens. How generous! She gave them all to Bob. """ initial_balances = [qrl_pb2.AddressAmount(address=self.bob.address, amount=1000)] self.params["initial_balances"] = initial_balances tx = TokenTransaction.create(**self.params) tx.sign(self.alice) # Signing the TX also generates the txhash, which we need to generate the AddressState properly. addresses_state = dict(self.addresses_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=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(addresses_state[self.bob.address].balance, 0) self.assertEqual(2, 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]) self.assertEqual(1, len(state_container.tokens.data)) self.assertIn((self.bob.address, tx.txhash), state_container.tokens.data) self.assertEqual(1000, state_container.tokens.data[(self.bob.address, tx.txhash)].balance)
def test_revert_transfer_token_txn_empty_addresses_state(self): """ If we didn't have any AddressStates for the addresses involved in this test, do nothing """ tx = TransferTokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=100) 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( 100, tokens.data[(self.alice.address, tx.token_txhash)].balance) self.assertEqual( 0, tokens.data[(self.bob.address, tx.token_txhash)].balance)
def test_validate_slave_signing_xmss_state_has_no_slave_permissions_in_state(self, m_logger): bob = get_bob_xmss() # Let's say Alice is Bob's master. self.params["master_addr"] = self.alice.address self.params["xmss_pk"] = bob.pk # We need to add extra data to the mock AddressState. tx = MessageTransaction.create(**self.params) tx.sign(self.alice) state_container = StateContainer(addresses_state=dict(), 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) result = tx.validate_slave(state_container) self.assertFalse(result)
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_apply_transfer_token_txn_send_tokens_to_self(self): """ Alice has 1000 tokens and 100 QRL. She sends some tokens to herself. What happens next? """ self.params["addrs_to"] = [self.alice.address] tx = TransferTokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=100) 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) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, 0)) tx.apply(self.state, state_container) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, 0)) self.assertEqual(addresses_state[self.alice.address].balance, 99) self.assertEqual( tokens.data[(self.alice.address, tx.token_txhash)].balance, 100)
def test_validate_extended(self): multi_sig_address = MultiSigAddressState.generate_multi_sig_address( b'') spend_tx = MultiSigSpend.create(multi_sig_address=multi_sig_address, addrs_to=[self.alice.address], amounts=[100], expiry_block_number=15000, fee=0, xmss_pk=self.alice.pk) spend_tx.sign(self.alice) tx = MultiSigVote.create(shared_key=spend_tx.txhash, unvote=False, 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, } vote_stats = { spend_tx.txhash: VoteStats.create(multi_sig_address=multi_sig_address, shared_key=spend_tx.txhash, signatories=multi_sig_address_state.signatories, expiry_block_number=spend_tx.expiry_block_number), } multi_sig_spend_txs = { spend_tx.txhash: spend_tx, } 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=multi_sig_spend_txs, votes_stats=vote_stats, 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) tx._data.multi_sig_vote.unvote = True result = tx._validate_extended(state_container) self.assertFalse(result) tx._data.multi_sig_vote.unvote = False 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) state_container.block_number = 15000 result = tx._validate_extended(state_container) self.assertTrue(result) state_container.block_number = 15001 result = tx._validate_extended(state_container) self.assertFalse(result)
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_multi_sig_create_txn(self): 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 tx = MultiSigCreate.create(self.signatories, self.weights, self.threshold, 1, self.random_signer.pk) tx.sign(self.random_signer) 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.random_signer.address, tx.ots_key)) tx.apply(self.state, state_container) self.assertEqual(200 - tx.fee, addresses_state[self.random_signer.address].balance) storage_key = state_container.paginated_tx_hash.generate_key( self.random_signer.address, 1) self.assertEqual( [tx.txhash], state_container.paginated_tx_hash.key_value[storage_key]) for signatory_address in self.signatories: storage_key = state_container.paginated_tx_hash.generate_key( signatory_address, 1) self.assertEqual( [tx.txhash], state_container.paginated_tx_hash.key_value[storage_key]) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.random_signer.address, tx.ots_key)) AddressState.put_addresses_state(self.state, addresses_state) state_container.paginated_multisig_address.put_paginated_data(None) multi_sig_addresses_state = MultiSigAddressState.get_multi_sig_address_state_by_address( self.state._db, MultiSigAddressState.generate_multi_sig_address(tx.txhash)) self.assertEqual(self.signatories, multi_sig_addresses_state.signatories) self.assertEqual(self.weights, multi_sig_addresses_state.weights) self.assertEqual(self.threshold, multi_sig_addresses_state.threshold) for signatory_address in self.signatories: multi_sig_addresses = state_container.paginated_multisig_address.get_paginated_data( signatory_address, 1) self.assertEqual(len(multi_sig_addresses), 1) tx.revert(self.state, state_container) state_container.paginated_multisig_address.put_paginated_data(None) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.random_signer.address, tx.ots_key)) self.assertIsNone( MultiSigAddressState.get_multi_sig_address_state_by_address( self.state._db, MultiSigAddressState.generate_multi_sig_address(tx.txhash))) for signatory_address in self.signatories: multi_sig_addresses = state_container.paginated_multisig_address.get_paginated_data( signatory_address, 1) self.assertEqual(len(multi_sig_addresses), 0)
def test_revert(self): multi_sig_address = MultiSigAddressState.generate_multi_sig_address( b'') spend_tx = MultiSigSpend.create(multi_sig_address=multi_sig_address, addrs_to=[self.alice.address], amounts=[100], expiry_block_number=15000, fee=0, xmss_pk=self.alice.pk) spend_tx.sign(self.alice) tx = MultiSigVote.create(shared_key=spend_tx.txhash, unvote=False, 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, } vote_stats = { spend_tx.txhash: VoteStats.create(multi_sig_address=multi_sig_address, shared_key=spend_tx.txhash, signatories=multi_sig_address_state.signatories, expiry_block_number=spend_tx.expiry_block_number), } multi_sig_spend_txs = { spend_tx.txhash: spend_tx, } 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=multi_sig_spend_txs, votes_stats=vote_stats, block_number=10, 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(spend_tx.txhash, state_container.votes_stats) vote_stats = state_container.votes_stats[spend_tx.txhash] unvote, index = vote_stats.get_unvote_by_address(tx.addr_from) self.assertNotEqual(index, -1) self.assertFalse(unvote) self.assertEqual(vote_stats.shared_key, spend_tx.txhash) self.assertEqual(vote_stats.total_weight, 4) self.assertEqual(vote_stats.signatories, multi_sig_address_state.signatories) tx.revert(self.state, state_container) self.assertIn(spend_tx.txhash, state_container.votes_stats) vote_stats = state_container.votes_stats[spend_tx.txhash] unvote, index = vote_stats.get_unvote_by_address(tx.addr_from) self.assertNotEqual(index, -1) self.assertTrue(unvote) self.assertEqual(vote_stats.shared_key, spend_tx.txhash) self.assertEqual(vote_stats.total_weight, 0) self.assertEqual(vote_stats.signatories, multi_sig_address_state.signatories) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, tx.ots_key))
def test_validate(self): # 1. Set up all the mocking so that Block.validate() should pass m_chain_manager = Mock(name='Mock ChainManager') attrs_all_pass = { 'get_block_is_duplicate.return_value': False, 'validate_mining_nonce.return_value': True, 'get_config_by_block_number.return_value': config.dev, 'new_state_container.return_value': StateContainer(None, None, None, None, None, None, 5, None, config.dev, False, None, None) } m_chain_manager.configure_mock(**attrs_all_pass) self.block._validate_parent_child_relation = Mock(return_value=True) result = self.block.validate(m_chain_manager, OrderedDict()) self.assertTrue(result) # 2. Switch the mock checks one by one to invalid, and make sure that validate() returns False # The Block is already in the State (a duplicate) m_chain_manager.get_block_is_duplicate.return_value = True result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) m_chain_manager.get_block_is_duplicate.return_value = False # The mining nonce is invalid m_chain_manager.validate_mining_nonce.return_value = False result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) m_chain_manager.validate_mining_nonce.return_value = True # No parent block found, and it's not in future_blocks either m_chain_manager.get_block.return_value = None result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) m_chain_manager.get_block.return_value = Mock(name='mock Block') # The parent_block is not actually Block's parent self.block._validate_parent_child_relation.return_value = False result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) self.block._validate_parent_child_relation.return_value = True # Block.transactions is [] (it should at least have the CoinBase TX in there) with patch('qrl.core.Block.Block.transactions', new_callable=PropertyMock) as m_transactions: m_transactions.return_value = [] result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) # There was a problem with the CoinBase TX with patch('qrl.core.txs.CoinBase.CoinBase._validate_extended') as m_validate_extended: m_validate_extended.return_value = False result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) m_validate_extended.return_value = None m_validate_extended.side_effect = Exception result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result) # The BlockHeader doesn't fit with this Block self.blockheader.validate.return_value = False result = self.block.validate(m_chain_manager, OrderedDict()) self.assertFalse(result)
def test_validate_extended(self, m_validate_slave, m_logger): """ TokenTransaction._validate_extended checks for: 1. valid master/slave 2. from address is valid 3. owner address is valid 4. addresses that own the initial balances are valid 5. that the AddressState has enough coins to pay the Transaction fee (because no coins are being transferred) 6. OTS key reuse """ alice_address_state = OptimizedAddressState.get_default( self.alice.address) alice_address_state.pbdata.balance = 100 tx = TokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = {alice_address_state.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=1, total_coin_supply=1000, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) result = tx._validate_extended(state_container) self.assertTrue(result) m_validate_slave.return_value = False result = tx.validate_all(state_container) self.assertFalse(result) m_validate_slave.return_value = True with patch('qrl.core.txs.TokenTransaction.TokenTransaction.addr_from', new_callable=PropertyMock) as m_addr_from: m_addr_from.return_value = b'Invalid Address' result = tx._validate_extended(state_container) self.assertFalse(result) with patch('qrl.core.txs.TokenTransaction.TokenTransaction.owner', new_callable=PropertyMock) as m_owner: m_owner.return_value = b'Invalid Address' result = tx._validate_extended(state_container) self.assertFalse(result) with patch( 'qrl.core.txs.TokenTransaction.TokenTransaction.initial_balances', new_callable=PropertyMock) as m_address_balance: m_address_balance.return_value = [ qrl_pb2.AddressAmount(address=b'Invalid Address 1', amount=1000), qrl_pb2.AddressAmount(address=b'Invalid Address 2', amount=1000) ] result = tx._validate_extended(state_container) self.assertFalse(result) 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) # Token symbol too long tx = self.make_tx(symbol=b'QRLSQRLSQRL') tx.sign(self.alice) self.assertFalse(tx._validate_extended(state_container)) # Token name too long tx = self.make_tx(name=b'Quantum Resistant LedgerQuantum') tx.sign(self.alice) self.assertFalse(tx._validate_extended(state_container)) # Token symbol missing with self.assertRaises(ValueError): tx = self.make_tx(symbol=b'') tx.sign(self.alice) self.assertFalse(tx._validate_extended(state_container)) # Token name missing with self.assertRaises(ValueError): tx = self.make_tx(name=b'') tx.sign(self.alice) tx._validate_extended(state_container) # Empty initial_balances with self.assertRaises(ValueError): tx = self.make_tx(initial_balances=[]) tx.sign(self.alice) self.assertFalse(tx._validate_extended(state_container)) # Invalid initial balances... 0! with self.assertRaises(ValueError): initial_balances_0_0 = [ qrl_pb2.AddressAmount(address=self.alice.address, amount=0), qrl_pb2.AddressAmount(address=self.bob.address, amount=0) ] tx = self.make_tx(initial_balances=initial_balances_0_0) tx.sign(self.alice) self.assertFalse(tx._validate_extended(state_container)) # Fee is -1 with patch('qrl.core.txs.TokenTransaction.TokenTransaction.fee', new_callable=PropertyMock) as m_fee: m_fee.return_value = -1 with self.assertRaises(ValueError): tx = self.make_tx() tx.sign(self.alice)
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_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)
def test_validate_extended(self, m_validate_slave, m_logger): """ TransferTokenTransaction._validate_extended checks for: 1. valid master/slave 2. negative fee, negative total token amounts transferred 3. addr_from has enough funds for the fee 4. if addr_from owns any tokens to begin with 5. if addr_from has enough tokens 6. addr_from ots_key reuse """ alice_address_state = OptimizedAddressState.get_default(self.alice.address) alice_address_state.pbdata.balance = 100 params = self.default_params() tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) addresses_state = { alice_address_state.address: alice_address_state } tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = qrl_pb2.TokenBalance(balance=1000, decimals=0, delete=False) 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) result = tx._validate_extended(state_container) self.assertTrue(result) # Invalid master XMSS/slave XMSS relationship m_validate_slave.return_value = False state_container.tokens = Indexer(b'token', None) state_container.tokens.data[(self.alice.address, tx.token_txhash)] = qrl_pb2.TokenBalance(balance=1000, decimals=0, delete=False) result = tx.validate_all(state_container) self.assertFalse(result) m_validate_slave.return_value = True # fee = -1 with patch('qrl.core.txs.TransferTokenTransaction.TransferTokenTransaction.fee', new_callable=PropertyMock) as m_fee: m_fee.return_value = -1 tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = qrl_pb2.TokenBalance(balance=1000, decimals=0, delete=False) state_container.tokens = tokens result = tx._validate_extended(state_container) self.assertFalse(result) # total_amount = -1 with patch('qrl.core.txs.TransferTokenTransaction.TransferTokenTransaction.total_amount', new_callable=PropertyMock) as m_total_amount: m_total_amount.return_value = -100 tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = qrl_pb2.TokenBalance(balance=1000, decimals=0, delete=False) state_container.tokens = tokens result = tx._validate_extended(state_container) self.assertFalse(result) # balance = 0, cannot pay the Transaction fee alice_address_state.pbdata.balance = 0 tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = qrl_pb2.TokenBalance(balance=1000, decimals=0, delete=False) state_container.tokens = tokens result = tx._validate_extended(state_container) self.assertFalse(result) alice_address_state.pbdata.balance = 100 # addr_from doesn't have these tokens state_container.tokens = Indexer(b'token', None) result = tx._validate_extended(state_container) self.assertFalse(result) # addr_from doesn't have enough tokens tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = qrl_pb2.TokenBalance(balance=99, decimals=0, delete=False) state_container.tokens = tokens result = tx._validate_extended(state_container) self.assertFalse(result) # addr_from_pk has used this OTS key before addresses_state = { self.alice.address: alice_address_state } state_container.addresses_state = addresses_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) slave = get_slave_xmss() params = self.default_params() params["addrs_to"] = [self.bob.address, slave.address, self.alice.address] params["amounts"] = [2, 3, 5] tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) 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 = 1 state_container.current_dev_config = m_config.dev self.assertFalse(tx._validate_extended(state_container=state_container))