def test_initiator_log_directransfer_action( raiden_chain, token_addresses, deposit): """ The action that start a direct transfer must be logged in the WAL. """ token_address = token_addresses[0] amount = int(deposit / 2.) identifier = 13 app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking direct_transfer( app0, app1, token_address, amount, identifier, ) app0_state_changes = get_all_state_changes(app0.raiden.transaction_log) direct_transfers = [ state_change for _, state_change in app0_state_changes if isinstance(state_change, ActionTransferDirect) ] assert direct_transfers[0] == ActionTransferDirect( identifier, amount, token_address, app1.raiden.address, )
def direct_transfer_async(self, token_network_identifier, amount, target, identifier): """ Do a direct transfer with target. Direct transfers are non cancellable and non expirable, since these transfers are a signed balance proof with the transferred amount incremented. Because the transfer is non cancellable, there is a level of trust with the target. After the message is sent the target is effectively paid and then it is not possible to revert. The async result will be set to False iff there is no direct channel with the target or the payer does not have balance to complete the transfer, otherwise because the transfer is non expirable the async result *will never be set to False* and if the message is sent it will hang until the target node acknowledge the message. This transfer should be used as an optimization, since only two packets are required to complete the transfer (from the payers perspective), whereas the mediated transfer requires 6 messages. """ self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() direct_transfer = ActionTransferDirect( token_network_identifier, target, identifier, amount, ) self.handle_state_change(direct_transfer)
def direct_transfer_async(self, token_network_identifier, amount, target, identifier): """ Do a direct transfer with target. Direct transfers are non cancellable and non expirable, since these transfers are a signed balance proof with the transferred amount incremented. Because the transfer is non cancellable, there is a level of trust with the target. After the message is sent the target is effectively paid and then it is not possible to revert. The async result will be set to False iff there is no direct channel with the target or the payer does not have balance to complete the transfer, otherwise because the transfer is non expirable the async result *will never be set to False* and if the message is sent it will hang until the target node acknowledge the message. This transfer should be used as an optimization, since only two packets are required to complete the transfer (from the payers perspective), whereas the mediated transfer requires 6 messages. """ self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() payment_status = self.targets_to_identifiers_to_statuses[target].get( identifier) if payment_status: if not payment_status.matches(PaymentType.DIRECT, token_network_identifier, amount): raise PaymentConflict( 'Another payment with the same id is in flight', ) return payment_status.payment_done direct_transfer = ActionTransferDirect( token_network_identifier, target, identifier, amount, ) payment_status = PaymentStatus( payment_type=PaymentType.DIRECT, payment_identifier=identifier, amount=amount, token_network_identifier=token_network_identifier, payment_done=AsyncResult(), ) self.targets_to_identifiers_to_statuses[target][ identifier] = payment_status self.handle_state_change(direct_transfer) return payment_status.payment_done
def test_transfer_statechange_operators(): # pylint: disable=unneeded-not a = Block(2) b = Block(2) c = Block(3) assert a == b assert not a != b assert a != c assert not a == c a = ActionCancelPayment(2) b = ActionCancelPayment(2) c = ActionCancelPayment(3) assert a == b assert not a != b assert a != c assert not a == c token_network_identifier = factories.make_address() a = ActionTransferDirect( token_network_identifier, receiver_address=ADDRESS, payment_identifier=2, amount=2, ) b = ActionTransferDirect( token_network_identifier, receiver_address=ADDRESS, payment_identifier=2, amount=2, ) c = ActionTransferDirect( token_network_identifier, receiver_address=ADDRESS2, # different recipient payment_identifier=2, amount=2, ) assert a == b assert not a != b assert a != c assert not a == c
def make_direct_transfer_from_channel( payment_network_identifier, from_channel, partner_channel, amount, pkey, ): """ Helper to create and register a direct transfer from `from_channel` to `partner_channel`.""" payment_identifier = channel.get_next_nonce(from_channel.our_state) pseudo_random_generator = random.Random() state_change = ActionTransferDirect( payment_network_identifier, from_channel.token_address, from_channel.partner_state.address, payment_identifier, amount, ) iteration = channel.handle_send_directtransfer( from_channel, state_change, pseudo_random_generator, ) assert isinstance(iteration.events[0], SendDirectTransfer) direct_transfer_message = DirectTransfer.from_event(iteration.events[0]) address = privatekey_to_address(pkey) sign_key = PrivateKey(pkey) direct_transfer_message.sign(sign_key, address) # if this fails it's not the right key for the current `from_channel` assert direct_transfer_message.sender == from_channel.our_state.address balance_proof = balanceproof_from_envelope(direct_transfer_message) message_identifier = random.randint(0, UINT64_MAX) receive_direct = ReceiveTransferDirect( payment_network_identifier, from_channel.token_address, message_identifier, payment_identifier, balance_proof, ) channel.handle_receive_directtransfer( partner_channel, receive_direct, ) return direct_transfer_message
def test_transfer_statechange_operators(): a = Block(2) b = Block(2) c = Block(3) assert a == b assert not a != b assert a != c assert not a == c a = ActionCancelTransfer(2) b = ActionCancelTransfer(2) c = ActionCancelTransfer(3) assert a == b assert not a != b assert a != c assert not a == c a = ActionTransferDirect(2, 2, ADDRESS, ADDRESS) b = ActionTransferDirect(2, 2, ADDRESS, ADDRESS) c = ActionTransferDirect(3, 4, ADDRESS, ADDRESS2) assert a == b assert not a != b assert a != c assert not a == c a = ReceiveTransferDirect(2, 2, ADDRESS, ADDRESS) b = ReceiveTransferDirect(2, 2, ADDRESS, ADDRESS) c = ReceiveTransferDirect(3, 4, ADDRESS, ADDRESS2) assert a == b assert not a != b assert a != c assert not a == c
def direct_transfer_async(self, token_address, amount, target, identifier): """ Do a direct tranfer with target. Direct transfers are non cancellable and non expirable, since these transfers are a signed balance proof with the transferred amount incremented. Because the transfer is non cancellable, there is a level of trust with the target. After the message is sent the target is effectively paid and then it is not possible to revert. The async result will be set to False iff there is no direct channel with the target or the payer does not have balance to complete the transfer, otherwise because the transfer is non expirable the async result *will never be set to False* and if the message is sent it will hang until the target node acknowledge the message. This transfer should be used as an optimization, since only two packets are required to complete the transfer (from the payer's perspective), whereas the mediated transfer requires 6 messages. """ graph = self.token_to_channelgraph[token_address] direct_channel = graph.partneraddress_to_channel.get(target) direct_channel_with_capacity = (direct_channel and direct_channel.can_transfer and amount <= direct_channel.distributable) if direct_channel_with_capacity: direct_transfer = direct_channel.create_directtransfer( amount, identifier) self.sign(direct_transfer) direct_channel.register_transfer( self.get_block_number(), direct_transfer, ) direct_transfer_state_change = ActionTransferDirect( identifier, amount, token_address, direct_channel.partner_state.address, ) # TODO: add the transfer sent event state_change_id = self.transaction_log.log( direct_transfer_state_change) # TODO: This should be set once the direct transfer is acknowledged transfer_success = EventTransferSentSuccess( identifier, amount, target, ) self.transaction_log.log_events(state_change_id, [transfer_success], self.get_block_number()) async_result = self.protocol.send_async( direct_channel.partner_state.address, direct_transfer, ) else: async_result = AsyncResult() async_result.set(False) return async_result
def _direct_or_mediated_transfer(self, token_address, amount, identifier, direct_channel): """ Check the direct channel and if possible use it, otherwise start a mediated transfer. """ if not direct_channel.can_transfer: log.info( 'DIRECT CHANNEL %s > %s is closed or has no funding', pex(direct_channel.our_state.address), pex(direct_channel.partner_state.address), ) async_result = self._mediated_transfer( token_address, amount, identifier, direct_channel.partner_state.address, ) elif amount > direct_channel.distributable: log.info( 'DIRECT CHANNEL %s > %s doesnt have enough funds [%s]', pex(direct_channel.our_state.address), pex(direct_channel.partner_state.address), amount, ) async_result = self._mediated_transfer( token_address, amount, identifier, direct_channel.partner_state.address, ) else: direct_transfer = direct_channel.create_directtransfer(amount, identifier) self.sign(direct_transfer) direct_channel.register_transfer( self.get_block_number(), direct_transfer, ) direct_transfer_state_change = ActionTransferDirect( identifier, amount, token_address, direct_channel.partner_state.address, ) # TODO: add the transfer sent event state_change_id = self.transaction_log.log(direct_transfer_state_change) # TODO: This should be set once the direct transfer is acknowledged transfer_success = EventTransferSentSuccess( identifier, ) self.transaction_log.log_events( state_change_id, [transfer_success], self.get_block_number() ) async_result = self.protocol.send_async( direct_channel.partner_state.address, direct_transfer, ) return async_result