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 revert_attempt_conversion(self, attempt: TransactionAttempt): if attempt.currency == "coin": cv = self._coin_integer_converter_service elif attempt.currency == "waves": cv = self._asset_integer_converter_service else: raise Exception("unknown currency") receivers = [] for i in range(0, len(attempt.receivers)): amount = cast(int, attempt.receivers[i].amount) # type: int receivers.append( TransactionAttemptReceiver( amount=cv.revert_amount_conversion(amount), address=attempt.receivers[i].address)) fee = attempt.fee if attempt.currency == "coin": fee = cv.revert_amount_conversion(cast(int, fee)) return TransactionAttempt(receivers=receivers, fee=fee, sender=attempt.sender, currency=attempt.currency)
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 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 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))
def _attempt_from_dict(self, data: dict) -> TransactionAttempt: return TransactionAttempt( currency=data[TransactionAttempt.DICT_CURRENCY], fee=int(data[TransactionAttempt.DICT_FEE]), sender=data[TransactionAttempt.DICT_SENDER], receivers=map_array(self._receiver_from_dict, data[TransactionAttempt.DICT_RECEIVERS]))