def test_set_affected_address(self, m_logger): result = set() params = self.default_params() tx = TransferTokenTransaction.create(**params) tx.set_affected_address(result) self.assertEqual(2, len(result)) params = self.default_params() params["addrs_to"] = [self.bob.address, get_slave_xmss().address] params["amounts"] = [100, 200] tx = TransferTokenTransaction.create(**params) tx.set_affected_address(result) self.assertEqual(3, len(result))
def test_create(self, m_logger): tx = TransferTokenTransaction.create(token_txhash=b'000000000000000', addrs_to=[self.bob.address], amounts=[200000], fee=1, xmss_pk=self.alice.pk) self.assertTrue(tx)
def test_remove_transfer_token_metadata(self): alice_xmss = get_alice_xmss() bob_xmss = get_bob_xmss() token_transaction = get_token_transaction(alice_xmss, bob_xmss) TokenMetadata.create_token_metadata(self.state, token_transaction, None) transfer_token = TransferTokenTransaction.create( token_txhash=token_transaction.txhash, addrs_to=[alice_xmss.address], amounts=[100000000], fee=1, xmss_pk=bob_xmss.pk) transfer_token.sign(alice_xmss) TokenMetadata.update_token_metadata(self.state, transfer_token, None) token_metadata = TokenMetadata.get_token_metadata( self.state, transfer_token.token_txhash) self.assertIn(transfer_token.txhash, token_metadata.transfer_token_tx_hashes) TokenMetadata.remove_transfer_token_metadata(self.state, transfer_token, None) token_metadata = TokenMetadata.get_token_metadata( self.state, transfer_token.token_txhash) self.assertNotIn(transfer_token.txhash, token_metadata.transfer_token_tx_hashes)
def test_to_json(self, m_logger): tx = TransferTokenTransaction.create(token_txhash=b'000000000000000', addrs_to=[self.bob.address], amounts=[200000], fee=1, xmss_pk=self.alice.pk) txjson = tx.to_json() self.assertEqual(json.loads(test_json_TransferToken), json.loads(txjson))
def test_broadcast_tx(self, m_reactor, m_logger): # broadcast_tx() should handle all Transaction Types self.factory.broadcast_tx(MessageTransaction()) self.factory.broadcast_tx(TransferTransaction()) self.factory.broadcast_tx(TokenTransaction()) self.factory.broadcast_tx(TransferTokenTransaction()) self.factory.broadcast_tx(SlaveTransaction()) with self.assertRaises(ValueError): m_tx = Mock(autospec=TransferTransaction, txhash=bhstr2bin('deadbeef')) self.factory.broadcast_tx(m_tx)
def test_revert_transfer_token_txn_multi_send(self): """ Alice has 1100 tokens and 100 xrd, Bob and Slave have none. Alice sends some tokens to Bob and Slave. Undo this. """ slave = get_slave_xmss() self.params["addrs_to"] = [self.bob.address, slave.address] self.params["amounts"] = [100, 100] tx = TransferTokenTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) addresses_state[self.alice.address].pbdata.balance = 100 addresses_state[self.bob.address].pbdata.balance = 0 addresses_state[slave.address] = OptimizedAddressState.get_default( slave.address) tokens = Indexer(b'token', None) tokens.data[(self.alice.address, tx.token_txhash)] = TokenBalance(balance=1100) state_container = StateContainer(addresses_state=addresses_state, tokens=tokens, slaves=Indexer(b'slave', None), lattice_pk=Indexer( b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=1, total_coin_supply=1000, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) state_container.paginated_bitfield.set_ots_key(addresses_state, self.alice.address, 0) self.assertTrue( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, 0)) tx.apply(self.state, state_container) tx.revert(self.state, state_container) self.assertFalse( state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse( self.alice.address, 0)) self.assertEqual(addresses_state[self.alice.address].balance, 100) self.assertEqual(addresses_state[self.bob.address].balance, 0) self.assertEqual(addresses_state[slave.address].balance, 0) self.assertEqual( tokens.data[(self.alice.address, tx.token_txhash)].balance, 1100) self.assertEqual( tokens.data[(self.bob.address, tx.token_txhash)].balance, 0) self.assertEqual(tokens.data[(slave.address, tx.token_txhash)].balance, 0)
def test_validate_tx(self, m_logger): tx = TransferTokenTransaction.create(token_txhash=b'000000000000000', addrs_to=[self.bob.address], amounts=[200000], fee=1, xmss_pk=self.alice.pk) # We must sign the tx before validation will work. tx.sign(self.alice) # We have not touched the tx: validation should pass. self.assertTrue(tx.validate_or_raise())
def test_validate_tx2(self, m_logger): params = self.default_params() tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) self.assertTrue(tx.validate_or_raise()) tx._data.transaction_hash = b'abc' # Should fail, as we have modified with invalid transaction_hash with self.assertRaises(ValueError): tx.validate_or_raise()
def create_transfer_token_txn(addrs_to: list, token_txhash: bytes, amounts: list, fee: int, xmss_pk: bytes, master_addr: bytes): return TransferTokenTransaction.create(token_txhash, addrs_to, amounts, fee, xmss_pk, master_addr)
def tx_transfertoken(ctx, src, master, token_txhash, dsts, amounts, decimals, fee, ots_key_index): """ Create Transfer Token Transaction, which moves tokens from src to dst. """ if decimals > 19: click.echo("The number of decimal cannot exceed 19 under any configuration") quit(1) try: addresses_dst, shor_amounts = _parse_dsts_amounts(dsts, amounts, token_decimals=decimals) bin_token_txhash = parse_hexblob(token_txhash) master_addr = None if master: master_addr = parse_qaddress(master) # FIXME: This could be problematic. Check fee_shor = _quanta_to_shor(fee) _, src_xmss = _select_wallet(ctx, src) if not src_xmss: click.echo("A local wallet is required to sign the transaction") quit(1) address_src_pk = src_xmss.pk ots_key_index = validate_ots_index(ots_key_index, src_xmss) src_xmss.set_ots_index(ots_key_index) except KeyboardInterrupt: click.echo("Terminated by user") quit(1) except Exception as e: click.echo("Error validating arguments: {}".format(e)) quit(1) try: stub = ctx.obj.get_stub_public_api() tx = TransferTokenTransaction.create(token_txhash=bin_token_txhash, addrs_to=addresses_dst, amounts=shor_amounts, fee=fee_shor, xmss_pk=address_src_pk, master_addr=master_addr) tx.sign(src_xmss) push_transaction_req = xrd_pb2.PushTransactionReq(transaction_signed=tx.pbdata) push_transaction_resp = stub.PushTransaction(push_transaction_req, timeout=CONNECTION_TIMEOUT) print(push_transaction_resp.error_code) except Exception as e: print("Error {}".format(str(e)))
def test_state_validate_tx_custom(self, m_logger): """ TransferTokenTransaction._validate_custom() checks for: 1. 0 amounts 2. fee < 0 3. multi-send: too many recipients 4. multi-send: recipient addresses and amounts not the same length 5. invalid addr_from 6. invalid addr_to """ slave = get_slave_xmss() params = self.default_params() tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) result = tx.validate_or_raise() self.assertTrue(result) params = self.default_params() params["addrs_to"] = [self.bob.address, slave.address] params["amounts"] = [1, 0] with self.assertRaises(ValueError): tx = TransferTokenTransaction.create(**params) # Protobuf validation doesn't allow negative fees already params = self.default_params() tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) with patch('xrd.core.txs.Transaction.Transaction.fee', new_callable=PropertyMock) as m_fee: m_fee.return_value = -1 with self.assertRaises(ValueError): tx.validate_or_raise() # TX signing already fails if addrs_to and amounts are unequal length params = self.default_params() tx = TransferTokenTransaction.create(**params) tx.sign(self.alice) with patch( 'xrd.core.txs.TransferTokenTransaction.TransferTokenTransaction.addrs_to', new_callable=PropertyMock) as m_addrs_to: m_addrs_to.return_value = [self.bob.address, slave.address] with self.assertRaises(ValueError): tx.validate_or_raise() params = self.default_params() params["master_addr"] = b'Bad xrd Address' with self.assertRaises(ValueError): tx = TransferTokenTransaction.create(**params) params = self.default_params() params["addrs_to"] = [self.bob.address, b'Bad xrd address'] params["amounts"] = [100, 200] with self.assertRaises(ValueError): tx = TransferTokenTransaction.create(**params)
def relay_transfer_token_txn(self, qaddresses_to: list, amounts: list, token_txhash: str, fee: int, master_qaddress, signer_address: str, ots_index: int): self.authenticate() index, xmss = self._get_wallet_index_xmss(signer_address, ots_index) self.verify_ots(signer_address, xmss, user_ots_index=ots_index) tx = TransferTokenTransaction.create( token_txhash=bytes(hstr2bin(token_txhash)), addrs_to=self.qaddresses_to_address(qaddresses_to), amounts=amounts, fee=fee, xmss_pk=xmss.pk, master_addr=self.qaddress_to_address(master_qaddress)) self.sign_and_push_transaction(tx, xmss, index) return self.to_plain_transaction(tx.pbdata)
def test_revert_transfer_token_txn_send_tokens_to_self(self): """ Alice has 1000 tokens and 100 xrd. 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 relay_transfer_token_txn_by_slave(self, qaddresses_to: list, amounts: list, token_txhash: str, fee: int, master_qaddress): self.authenticate() index, group_index, slave_index, slave_xmss = self.get_slave_xmss( master_qaddress) if slave_index == -1: raise Exception("No Slave Found") tx = TransferTokenTransaction.create( token_txhash=bytes(hstr2bin(token_txhash)), addrs_to=self.qaddresses_to_address(qaddresses_to), amounts=amounts, fee=fee, xmss_pk=slave_xmss.pk, master_addr=self.qaddress_to_address(master_qaddress)) self.sign_and_push_transaction(tx, slave_xmss, index, group_index, slave_index) return self.to_plain_transaction(tx.pbdata)
def test_apply_transfer_token_txn(self): """ Alice has 1000 tokens and 100 xrd, Bob has none. Alice sends some tokens to Bob. """ 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=150) tokens.data[(self.bob.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( tokens.data[(self.alice.address, tx.token_txhash)].balance, 50) self.assertEqual( tokens.data[(self.bob.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_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)] = xrd_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)] = xrd_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( 'xrd.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)] = xrd_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( 'xrd.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)] = xrd_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)] = xrd_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)] = xrd_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('xrd.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))