def test_handle_transaction_multi_receiver(self, mock_datetime: MagicMock): now = MagicMock() mock_datetime.utcnow.return_value = now gateway_coin_address = self._gateway_managed_receiver.address dst_waves_address = '039769' incoming_transaction = Transaction( tx='723968', receivers=[self._gateway_managed_receiver]) amount_after_fees = self._gateway_managed_receiver.amount - self._coin_standard_fee - self._gateway_fee attempts = [ TransactionAttempt(sender=gateway_coin_address, currency="coin", fee=self._coin_standard_fee, receivers=[ TransactionAttemptReceiver( address=self._gateway_address, amount=amount_after_fees), TransactionAttemptReceiver( address=self._gateway_owner_address, amount=self._gateway_fee) ]), TransactionAttempt(sender=self._gateway_waves_address, fee=self._waves_standard_fee, currency="waves", receivers=[ TransactionAttemptReceiver( address=dst_waves_address, amount=amount_after_fees) ]) ] trigger = AttemptListTrigger(tx=incoming_transaction.tx, currency="coin", receiver=0) attempt_list = TransactionAttemptList(trigger=trigger, attempts=attempts, created_at=now, last_modified=now) self._attempt_list_storage.find_by_trigger.return_value = None self._map_storage.get_waves_address_by_coin_address.return_value = dst_waves_address self._coin_transaction_consumer_impl.handle_transaction( incoming_transaction) self._attempt_list_storage.find_by_trigger.assert_any_call( AttemptListTrigger(tx=incoming_transaction.tx, receiver=0, currency="coin")) self._map_storage.get_waves_address_by_coin_address.assert_called_once_with( gateway_coin_address) self._attempt_service.continue_transaction_attempt_list.assert_not_called( ) self._attempt_list_storage.safely_save_attempt_list.assert_called_once_with( attempt_list)
def test_handle_receiver(self, mock_datetime: MagicMock): now = MagicMock() mock_datetime.utcnow.return_value = now coin_receiver = "82396457" amount_after_fees = self._gateway_waves_receiver.amount - 2 * self._coin_standard_fee - self._gateway_fee tx = "78265" self._coin_address_validation_service.validate_address.return_value = True attempts = [ TransactionAttempt(sender=self._gateway_coin_address_secret.public, fee=self._coin_standard_fee, currency="coin", receivers=[ TransactionAttemptReceiver( address=coin_receiver, amount=amount_after_fees) ]), TransactionAttempt(sender=self._gateway_coin_address_secret.public, fee=self._coin_standard_fee, currency="coin", receivers=[ TransactionAttemptReceiver( address=self._gateway_owner_address, amount=self._gateway_fee) ]) ] trigger = AttemptListTrigger(tx=tx, currency="waves", receiver=0) self._attempt_list_storage.find_by_trigger.return_value = None attempt_list = TransactionAttemptList(trigger=trigger, attempts=attempts, created_at=now, last_modified=now) self._waves_chain_query_service.get_coin_receiver_address_from_transaction.return_value = coin_receiver self._waves_transaction_consumer_impl._handle_receiver( tx, self._gateway_waves_receiver, 0) self._waves_chain_query_service.get_coin_receiver_address_from_transaction.assert_called_once_with( tx) self._attempt_service.continue_transaction_attempt_list.assert_not_called( ) self._attempt_list_storage.find_by_trigger.assert_called_once_with( AttemptListTrigger(tx=tx, currency="waves", receiver=0)) self._attempt_list_storage.safely_save_attempt_list.assert_called_once_with( attempt_list)
def test_update_attempt_list(self): trigger = AttemptListTrigger( tx="792873", receiver=1, currency="waves", senders=[TransactionSender(address="723789")]) attempt_list = TransactionAttemptList( trigger=trigger, attempts=[ TransactionAttempt( currency="coin", fee=100, receivers=[TransactionAttemptReceiver(address="973846", amount=234)], sender="739264857") ]) expected_query = dict() expected_query[TransactionAttemptList.DICT_ID] = attempt_list.attempt_list_id mock_find_one_result = MagicMock() mock_attempt_list_as_dict_result = MagicMock() self._collection.find_one_and_replace.return_value = mock_find_one_result self._serializer.attempt_list_as_dict.return_value = mock_attempt_list_as_dict_result self._storage.update_attempt_list(attempt_list) self._collection.find_one_and_replace.assert_called_once_with(expected_query, mock_attempt_list_as_dict_result) self._serializer.attempt_list_as_dict.assert_called_once_with(attempt_list)
def _trigger_from_dict(self, data: dict) -> AttemptListTrigger: if AttemptListTrigger.DICT_SENDERS_KEY in data: senders = map_array(self._sender_from_dict, data[AttemptListTrigger.DICT_SENDERS_KEY]) else: senders = None return AttemptListTrigger( tx=data[AttemptListTrigger.DICT_TX], receiver=data[AttemptListTrigger.DICT_RECEIVER], currency=data[AttemptListTrigger.DICT_CURRENCY], senders=senders)
def test_get_attempt_list_by_trigger(self): mock_find_by_trigger_result = MagicMock() mock_trigger = AttemptListTrigger(tx="2396487", receiver=3, currency="coin") self._attempt_list_storage.find_by_trigger.return_value = mock_find_by_trigger_result res = self._gateway_controller.get_attempt_list_by_trigger( mock_trigger) self.assertEqual(res, mock_find_by_trigger_result) self._attempt_list_storage.find_by_trigger.assert_called_once_with( mock_trigger)
def test_find_by_trigger_senders_is_none_not_found(self): trigger = AttemptListTrigger(tx="72935687", receiver=3, currency="2793658", senders=None) expected_query = dict() expected_query[TransactionAttemptList.DICT_TRIGGER + "." + AttemptListTrigger.DICT_RECEIVER] = trigger.receiver expected_query[TransactionAttemptList.DICT_TRIGGER + "." + AttemptListTrigger.DICT_CURRENCY] = trigger.currency expected_query[TransactionAttemptList.DICT_TRIGGER + "." + AttemptListTrigger.DICT_TX] = trigger.tx self._collection.find_one.return_value = None res = self._storage.find_by_trigger(trigger) self.assertIsNone(res) self._collection.find_one.assert_called_once_with(expected_query) self._serializer.attempt_list_from_dict.assert_not_called()
def _filter_receivers(self, transaction: Transaction) -> List[int]: res = list() for i in range(0, len(transaction.receivers)): if self._gateway_pywaves_address.address == transaction.receivers[ i].address: attempt_list = self._attempt_list_storage.find_by_trigger( AttemptListTrigger(tx=transaction.tx, currency="waves", receiver=i)) if attempt_list is None or not attempt_list.is_complete(): res.append(i) return res
def test_revert_attempt_list_conversion_waves(self): """Ensures that the fee is not converted when using the asset_integer_converter_service.""" trigger = AttemptListTrigger( tx="792873", receiver=1, currency="coin", senders=[TransactionSender(address="723789")]) attempt_list = TransactionAttemptList( trigger=trigger, attempts=[ TransactionAttempt( currency="waves", fee=100, receivers=[ TransactionAttemptReceiver(address="973846", amount=234), TransactionAttemptReceiver(address="9327468", amount=235) ], sender="739264857") ]) expected_attempt_list = TransactionAttemptList( trigger=trigger, attempts=[ TransactionAttempt( currency="waves", fee=100, receivers=[ TransactionAttemptReceiver(address="973846", amount=0.234), TransactionAttemptReceiver(address="9327468", amount=0.235) ], sender="739264857") ]) def mock_converter(value: Any): return value / 1000 self._asset_integer_converter_service.revert_amount_conversion.side_effect = mock_converter res = self._attempt_list_converter.revert_attempt_list_conversion( attempt_list) self.assertEqual(res, expected_attempt_list)
def test_filter_transaction_by_receivers_success(self): """ The filter should pass a transaction that has not yet been processed and contains a gateway managed address """ self._attempt_list_storage.gateway_transaction_exists.return_value = False self._map_storage.coin_address_exists.return_value = True self._attempt_list_storage.find_by_trigger.return_value = None transaction = Transaction(tx='723968', receivers=[self._gateway_managed_receiver]) res = self._coin_transaction_consumer_impl.filter_transaction( transaction) self.assertTrue(res) self._map_storage.coin_address_exists.assert_called_once_with( self._gateway_managed_receiver.address) self._attempt_list_storage.find_by_trigger.assert_called_once_with( AttemptListTrigger(tx=transaction.tx, receiver=0, currency="coin"))
def test_get_attempt_list_by_trigger(self): mock_trigger = AttemptListTrigger(tx="2937468", currency="coin", receiver=0) mock_attempt_list = MagicMock() mock_converter_result = MagicMock() self._gateway_controller.get_attempt_list_by_trigger.return_value = mock_attempt_list self._converter.revert_attempt_list_conversion.return_value = mock_converter_result res = self._proxy.get_attempt_list_by_trigger(mock_trigger) self._converter.revert_attempt_list_conversion.assert_called_once_with( mock_attempt_list) self._gateway_controller.get_attempt_list_by_trigger.assert_called_once_with( mock_trigger) self.assertEqual(res, mock_converter_result)
def setUp(self): self._attempt_list_serializer = TransactionAttemptListSerializer() trigger = AttemptListTrigger( tx="792873", receiver=1, currency="waves", senders=[TransactionSender(address="723789")]) self._attempt_list = TransactionAttemptList( trigger=trigger, attempts=[ TransactionAttempt( currency="coin", fee=100, receivers=[ TransactionAttemptReceiver(address="973846", amount=234), TransactionAttemptReceiver(address="9327468", amount=235) ], sender="739264857") ])
def _filter_receivers(self, transaction: Transaction) -> List[int]: """ Filters a given list of transaction receivers for those ones that are gateway managed, but not the main gateway coin holder. Those shall be further processed. """ res = list() for i in range(0, len(transaction.receivers)): address = transaction.receivers[i].address if self._is_gateway_managed_coin_address(address) and not self._is_gateway_coin_holder(address): attempt_list = self._attempt_list_storage.find_by_trigger( AttemptListTrigger(tx=transaction.tx, currency="coin", receiver=i)) if attempt_list is None or not attempt_list.is_complete(): res.append(i) return res
def test_continue_transaction_attempt_list(self, mock_datetime: MagicMock): now = MagicMock() mock_datetime.utcnow.return_value = now trigger = AttemptListTrigger( tx="792873", receiver=1, currency="waves", senders=[TransactionSender(address="723789")]) attempt_list = TransactionAttemptList( trigger=trigger, attempts=[ TransactionAttempt( currency="coin", fee=100, receivers=[TransactionAttemptReceiver(address="973846", amount=234)], sender="739264857") ], created_at=now, last_modified=now) expected_attempt_list = TransactionAttemptList( trigger=trigger, attempts=[ TransactionAttempt( currency="coin", fee=100, receivers=[TransactionAttemptReceiver(address="973846", amount=234)], sender="739264857"), ], transactions=["97238"], created_at=now, last_modified=now) mock_transaction = Transaction(tx="97238", receivers=[TransactionReceiver(address="973846", amount=234)]) mock_secret = "9723684" self._secret_service.get_secret_by_address.return_value = mock_secret self._transaction_service.send_coin.return_value = mock_transaction self._attempt_list_service.continue_transaction_attempt_list(attempt_list) self._storage.update_attempt_list.assert_called_once_with(expected_attempt_list) self._transaction_service.send_coin.assert_called_once_with(attempt_list.attempts[0], mock_secret) self._secret_service.get_secret_by_address("coin", "739264857")
def test_handle_receiver_invalid_address(self): coin_receiver = "82396457" amount_after_fees = self._gateway_waves_receiver.amount - self._coin_standard_fee - self._gateway_fee tx = "78265" self._coin_address_validation_service.validate_address.return_value = False attempts = [ TransactionAttempt(sender=self._gateway_coin_address_secret.public, fee=self._coin_standard_fee, currency="coin", receivers=[ TransactionAttemptReceiver( address=coin_receiver, amount=amount_after_fees), TransactionAttemptReceiver( address=self._gateway_owner_address, amount=self._gateway_fee) ]) ] trigger = AttemptListTrigger(tx=tx, currency="waves", receiver=0) self._attempt_list_storage.find_by_trigger.return_value = None self._waves_chain_query_service.get_coin_receiver_address_from_transaction.return_value = coin_receiver self._waves_transaction_consumer_impl._handle_receiver( tx, self._gateway_waves_receiver, 0) self._waves_chain_query_service.get_coin_receiver_address_from_transaction.assert_called_once_with( tx) self._coin_address_validation_service.validate_address.assert_called_once_with( coin_receiver) self._attempt_service.continue_transaction_attempt_list.assert_not_called( ) self._attempt_list_storage.find_by_trigger.assert_not_called()
def _handle_receiver(self, tx: str, receiver: TransactionReceiver, index: int, senders: List[TransactionSender] = None) -> None: # ---------- Pre-Calculation ---------- # fee to be used for the custom currency coin_fee = cast(int, self._fee_service.get_coin_fee()) # fee to be transferred to the Gateway owner gateway_fee = cast(int, self._fee_service.get_gateway_fee()) received_amount = cast(int, receiver.amount) # the amount of coins to be transferred after applying the fees if self._only_one_transaction_receiver: amount_after_fees = received_amount - 2 * coin_fee - gateway_fee else: amount_after_fees = received_amount - coin_fee - gateway_fee # receiver coin address coin_receiver = self._waves_chain_query_service.get_coin_receiver_address_from_transaction( tx) # ---------- Pre-Check ---------- if not self._coin_address_validation_service.validate_address( coin_receiver): self._logger.warning( "Received transaction %s with an invalid coin receiver address %s. " "Will be skipped.", tx, coin_receiver) return if self._gateway_coin_holder_secret.public == coin_receiver: self._logger.warning( "Received transaction %s with the address of the Gateway coin holder." "Will be skipped as an transaction to itself has no effect.", tx) return # transaction must have at least a specific amount to be processed if amount_after_fees < 0: self._logger.warning( "Received transaction %s with an amount of %s, but it is less than the at least " "required amount. Will be skipped.", tx, receiver.amount) return attempt_list = self._attempt_list_storage.find_by_trigger( AttemptListTrigger(tx=tx, currency="waves", receiver=index)) if attempt_list is None: trigger = AttemptListTrigger(tx=tx, receiver=index, currency="waves", senders=senders) attempts = list() if self._only_one_transaction_receiver: attempts.append( TransactionAttempt( sender=self._gateway_coin_holder_secret.public, fee=coin_fee, currency="coin", receivers=[ TransactionAttemptReceiver( address=self._gateway_owner_address, amount=gateway_fee) ])) attempts.append( TransactionAttempt( sender=self._gateway_coin_holder_secret.public, fee=coin_fee, currency="coin", receivers=[ TransactionAttemptReceiver( address=coin_receiver, amount=amount_after_fees) ])) else: attempts.append( TransactionAttempt( sender=self._gateway_coin_holder_secret.public, fee=coin_fee, currency="coin", receivers=[ TransactionAttemptReceiver( address=coin_receiver, amount=amount_after_fees), TransactionAttemptReceiver( address=self._gateway_owner_address, amount=gateway_fee) ])) attempt_list = TransactionAttemptList( trigger, attempts, last_modified=datetime.datetime.utcnow(), created_at=datetime.datetime.utcnow()) self._attempt_list_storage.safely_save_attempt_list(attempt_list) self._logger.info('Created new attempt list %s', str(attempt_list.attempt_list_id))
def _handle_transaction_receiver(self, tx: str, receiver: TransactionReceiver, index: int, senders: List[TransactionSender] = None): # ---------- Pre-Calculation ---------- # the gateway address that coins were transferred to gateway_managed_address = receiver.address # the custom currency fee to be used coin_fee = cast(int, self._fee_service.get_coin_fee()) # the waves fee to be used; not used in the calculation waves_fee = cast(int, self._fee_service.get_waves_fee()) # the fee for the gateway owner; service costs gateway_fee = cast(int, self._fee_service.get_gateway_fee()) # the amount of coins that were received by this receiver received_amount = cast(int, receiver.amount) # the waves address to receive the amount of coins receiver_waves_address = self._map_storage.get_waves_address_by_coin_address( gateway_managed_address) # the amount of coins to be transferred after applying the fees if self._only_one_transaction_receiver: amount_after_fees = received_amount - 2 * coin_fee - gateway_fee else: amount_after_fees = received_amount - coin_fee - gateway_fee # ---------- Pre-Check ---------- # transaction must have minimum amount to be handled if amount_after_fees <= 0: self._logger.warn( "Received transaction %s with an amount of %s, but it is less than the at least " "required amount. Will be skipped, but marked as processed.", tx, receiver.amount) return # no mapping was found for some reason; this in an internal error as this should # already be prevented by the filter method if receiver_waves_address is None: raise MappingNotFoundForCoinAddress(gateway_managed_address) attempt_list = self._attempt_list_storage.find_by_trigger( AttemptListTrigger(tx=tx, currency="coin", receiver=index)) if attempt_list is None: attempts = list() if self._only_one_transaction_receiver: attempts.append( TransactionAttempt( sender=gateway_managed_address, currency="coin", fee=coin_fee, receivers=[ TransactionAttemptReceiver( address=self._gateway_coin_address, amount=amount_after_fees), ])) attempts.append( TransactionAttempt( sender=gateway_managed_address, currency="coin", fee=coin_fee, receivers=[ TransactionAttemptReceiver( address=self._gateway_owner_address, amount=gateway_fee) ])) else: attempts.append( TransactionAttempt( sender=gateway_managed_address, currency="coin", fee=coin_fee, receivers=[ TransactionAttemptReceiver( address=self._gateway_coin_address, amount=amount_after_fees), TransactionAttemptReceiver( address=self._gateway_owner_address, amount=gateway_fee) ])) attempts.append( TransactionAttempt( sender=self._gateway_pywaves_address.address, fee=waves_fee, currency="waves", receivers=[ TransactionAttemptReceiver( address=receiver_waves_address, amount=amount_after_fees) ])) trigger = AttemptListTrigger(tx, index, "coin", senders=senders) attempt_list = TransactionAttemptList( trigger, attempts, last_modified=datetime.datetime.utcnow(), created_at=datetime.datetime.utcnow()) self._attempt_list_storage.safely_save_attempt_list(attempt_list) self._logger.info('Created new attempt list %s', str(attempt_list.attempt_list_id))