def test_apply_transfer_token_txn_multi_send(self): """ Alice has 1000 tokens and 100 QRL, Bob and Slave have none. Alice sends some tokens to Bob and Slave. """ slave = get_slave_xmss() params = self.params.copy() params["addrs_to"] = [self.bob.address, slave.address] params["amounts"] = [100, 100] tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) addresses_state[slave.address] = OptimizedAddressState.get_default( slave.address) tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=200) tokens.data[(self.bob.address, tx.token_txhash)] = TokenBalance(balance=0) tokens.data[(slave.address, tx.token_txhash)] = TokenBalance(balance=0) 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(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, 0) self.assertEqual( tokens.data[(self.bob.address, tx.token_txhash)].balance, 100) self.assertEqual(tokens.data[(slave.address, tx.token_txhash)].balance, 100)
def apply(self, state: State, state_container: StateContainer) -> bool: state_container.tokens.data[( self.addr_from, self.token_txhash)].balance -= self.total_amount decimals = state_container.tokens.data[(self.addr_from, self.token_txhash)].decimals address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee, subtract=True) state_container.paginated_tx_hash.insert(address_state, self.txhash) for index in range(0, len(self.addrs_to)): addr_to = self.addrs_to[index] amount = self.amounts[index] address_state = state_container.addresses_state[addr_to] # If receiver doesn't have this token before, then initialize token balance data into state # before adding the new balance. if (addr_to, self.token_txhash) not in state_container.tokens.data: state_container.tokens.data[( addr_to, self.token_txhash)] = TokenBalance(balance=0, decimals=decimals, tx_hash=self.txhash, delete=False) state_container.paginated_tokens_hash.insert( address_state, self.token_txhash) state_container.tokens.data[(addr_to, self.token_txhash)].balance += amount if self.addr_from != addr_to: state_container.paginated_tx_hash.insert( address_state, self.txhash) return self._apply_state_changes_for_PK(state_container)
def apply(self, state: State, state_container: StateContainer) -> bool: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True # If a QRL address has been mentioned multiple times in initial balance # then check if that address has already been initialized with some token # balance, if found, then add the new balance the already initialized balance if (initial_balance.address, self.txhash) in state_container.tokens.data: state_container.tokens.data[( initial_balance.address, self.txhash)].balance += initial_balance.amount else: state_container.tokens.data[( initial_balance.address, self.txhash)] = TokenBalance( balance=initial_balance.amount, decimals=self.decimals, tx_hash=self.txhash, delete=False) address_state = state_container.addresses_state[ initial_balance.address] state_container.paginated_tx_hash.insert(address_state, self.txhash) state_container.paginated_tokens_hash.insert( address_state, self.txhash) if not owner_processed: address_state = state_container.addresses_state[self.owner] state_container.paginated_tx_hash.insert(address_state, self.txhash) address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee, subtract=True) if not addr_from_processed and self.addr_from != self.owner: state_container.paginated_tx_hash.insert(address_state, self.txhash) address_state = state_container.addresses_state[addr_from_pk] if self.addr_from != addr_from_pk and addr_from_pk != self.owner: if not addr_from_pk_processed: state_container.paginated_tx_hash.insert( address_state, self.txhash) address_state.increase_nonce() state_container.paginated_bitfield.set_ots_key( state_container.addresses_state, addr_from_pk, self.ots_key) return True
def revert(self, state: State, state_container: StateContainer) -> bool: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True address_state = state_container.addresses_state[ initial_balance.address] state_container.tokens.data[(initial_balance.address, self.txhash)] = TokenBalance( balance=0, delete=True) state_container.paginated_tx_hash.remove(address_state, self.txhash) state_container.paginated_tokens_hash.remove( address_state, self.txhash) if not owner_processed: address_state = state_container.addresses_state[self.owner] state_container.paginated_tx_hash.remove(address_state, self.txhash) address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee) if not addr_from_processed and self.addr_from != self.owner: state_container.paginated_tx_hash.remove(address_state, self.txhash) address_state = state_container.addresses_state[addr_from_pk] if self.addr_from != addr_from_pk and addr_from_pk != self.owner: if not addr_from_pk_processed: state_container.paginated_tx_hash.remove( address_state, self.txhash) address_state.decrease_nonce() state_container.paginated_bitfield.unset_ots_key( state_container.addresses_state, addr_from_pk, self.ots_key) return True
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_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_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_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)