def make_transfer_description( payment_network_identifier: typing.PaymentNetworkID = EMPTY, payment_identifier: typing.PaymentID = EMPTY, amount: typing.TokenAmount = EMPTY, token_network: typing.TokenNetworkID = EMPTY, initiator: typing.InitiatorAddress = EMPTY, target: typing.TargetAddress = EMPTY, secret: typing.Secret = EMPTY, ) -> TransferDescriptionWithSecretState: payment_network_identifier = if_empty( payment_network_identifier, UNIT_PAYMENT_NETWORK_IDENTIFIER, ) payment_identifier = if_empty(payment_identifier, UNIT_TRANSFER_IDENTIFIER) amount = if_empty(amount, UNIT_TRANSFER_AMOUNT) token_network = if_empty(token_network, UNIT_TOKEN_NETWORK_ADDRESS) initiator = if_empty(initiator, UNIT_TRANSFER_INITIATOR) target = if_empty(target, UNIT_TRANSFER_TARGET) secret = if_empty(secret, random_secret()) return TransferDescriptionWithSecretState( payment_network_identifier=payment_network_identifier, payment_identifier=payment_identifier, amount=amount, token_network_identifier=token_network, initiator=initiator, target=target, secret=secret, )
def mediated_transfer_async( self, token_network_identifier: TokenNetworkID, amount: TokenAmount, target: TargetAddress, identifier: PaymentID, ) -> AsyncResult: """ Transfer `amount` between this node and `target`. This method will start an asynchronous transfer, the transfer might fail or succeed depending on a couple of factors: - Existence of a path that can be used, through the usage of direct or intermediary channels. - Network speed, making the transfer sufficiently fast so it doesn't expire. """ secret = random_secret() async_result = self.start_mediated_transfer_with_secret( token_network_identifier, amount, target, identifier, secret, ) return async_result
def start_mediated_transfer( self, token_network_identifier: typing.TokenNetworkID, amount: typing.TokenAmount, target: typing.Address, identifier: typing.PaymentID, ): self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() if identifier in self.identifier_to_results: return self.identifier_to_results[identifier] async_result = AsyncResult() self.identifier_to_results[identifier] = async_result secret = random_secret() init_initiator_statechange = initiator_init( self, identifier, amount, secret, token_network_identifier, target, ) # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return async_result
def mediated_transfer_async( self, token_network_identifier: typing.TokenNetworkID, amount: typing.TokenAmount, target: typing.Address, identifier: typing.PaymentID, ): """ Transfer `amount` between this node and `target`. This method will start an asynchronous transfer, the transfer might fail or succeed depending on a couple of factors: - Existence of a path that can be used, through the usage of direct or intermediary channels. - Network speed, making the transfer sufficiently fast so it doesn't expire. """ secret = random_secret() async_result = self.start_mediated_transfer_with_secret( token_network_identifier, amount, target, identifier, secret, ) return async_result
def handle_message_refundtransfer(raiden: RaidenService, message: RefundTransfer): token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) routes = get_best_routes( chain_state, token_network_address, raiden.address, from_transfer.target, from_transfer.lock.amount, message.sender, ) role = views.get_transfer_role( chain_state, from_transfer.lock.secrethash, ) if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( message.sender, routes, from_transfer, secret, ) else: state_change = ReceiveTransferRefund( message.sender, from_transfer, routes, ) raiden.handle_state_change(state_change)
def start_mediated_transfer( self, token_network_identifier, amount, target, identifier, ): self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() assert identifier not in self.identifier_to_results async_result = RaidenAsyncResult() self.identifier_to_results[identifier].append(async_result) secret = random_secret() init_initiator_statechange = initiator_init( self, identifier, amount, secret, token_network_identifier, target, ) # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return async_result
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT block_number = 1 refund_pkey, refund_address = factories.make_privkey_address() channel1 = factories.make_channel( our_balance=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, token_address=UNIT_TOKEN_ADDRESS, ) channelmap = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, block_number, ) original_transfer = current_state.initiator.transfer channel_identifier = current_state.initiator.channel_identifier channel_state = channelmap[channel_identifier] expiration = original_transfer.lock.expiration - channel_state.reveal_timeout - TRANSIT_BLOCKS refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, expiration, UNIT_SECRET, identifier=original_transfer.identifier, channel_identifier=channel1.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( sender=channel_state.partner_state.address, routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, block_number, ) assert iteration.new_state is None assert len(iteration.events) == 2 unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventTransferSentFailed)) assert unlocked_failed assert sent_failed
def handle_message_refundtransfer(self, raiden: RaidenService, message: RefundTransfer): token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) routes = get_best_routes( chain_state, token_network_address, raiden.address, from_transfer.target, from_transfer.lock.amount, message.sender, ) role = views.get_transfer_role( chain_state, from_transfer.lock.secrethash, ) if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( routes=routes, transfer=from_transfer, secret=secret, ) else: state_change = ReceiveTransferRefund( transfer=from_transfer, routes=routes, ) raiden.handle_state_change(state_change)
def _(properties, defaults=None) -> TransferDescriptionWithSecretState: properties: TransferDescriptionProperties = create_properties( properties, defaults) params = {key: value for key, value in properties.__dict__.items()} if params["secret"] == GENERATE: params["secret"] = random_secret() return TransferDescriptionWithSecretState(**params)
def mediated_transfer_async( self, token_network_identifier: TokenNetworkID, amount: PaymentAmount, target: TargetAddress, identifier: PaymentID, secret: Secret = None, secret_hash: SecretHash = None, ) -> PaymentStatus: """ Transfer `amount` between this node and `target`. This method will start an asynchronous transfer, the transfer might fail or succeed depending on a couple of factors: - Existence of a path that can be used, through the usage of direct or intermediary channels. - Network speed, making the transfer sufficiently fast so it doesn't expire. """ if secret is None: secret = random_secret() payment_status = self.start_mediated_transfer_with_secret( token_network_identifier, amount, target, identifier, secret, secret_hash, ) return payment_status
def start_mediated_transfer2(self, token_address, amount, identifier, target): self.protocol.start_health_check(target) if identifier is None: identifier = create_default_identifier() assert identifier not in self.identifier_to_results async_result = AsyncResult() self.identifier_to_results[identifier].append(async_result) secret = random_secret() init_initiator_statechange = initiator_init( self, identifier, amount, secret, token_address, target, ) # TODO: implement the network timeout raiden.config['msg_timeout'] and # cancel the current transfer if it happens (issue #374) # # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return async_result
def __init__(self, chain_state: ChainState, channel_state: NettingChannelState): self.secret = random_secret() self.secrethash = SecretHash(sha3(self.secret)) self.lock_expiration = get_initial_lock_expiration( chain_state.block_number, channel_state.reveal_timeout) self.payment_identifier = PaymentID(create_default_identifier()) self.message_identifier = MessageID( message_identifier_from_prng(chain_state.pseudo_random_generator))
def test_refund_transfer_invalid_sender(): amount = UNIT_TRANSFER_AMOUNT block_number = 1 pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channelmap = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, pseudo_random_generator, block_number, ) original_transfer = current_state.initiator.transfer channel_identifier = current_state.initiator.channel_identifier channel_state = channelmap[channel_identifier] expiration = original_transfer.lock.expiration - channel_state.reveal_timeout - TRANSIT_BLOCKS refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, expiration, UNIT_SECRET, ) wrong_sender_address = factories.HOP3 state_change = ReceiveTransferRefundCancelRoute( sender=wrong_sender_address, routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) before_state = deepcopy(current_state) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, pseudo_random_generator, block_number, ) assert iteration.new_state is not None assert not iteration.events assert iteration.new_state == before_state
def run_test_event_transfer_received_success( token_addresses, raiden_chain, ): sender_apps = raiden_chain[:-1] target_app = raiden_chain[-1] token_address = token_addresses[0] registry_address = target_app.raiden.default_registry.address target_address = target_app.raiden.address message_handler = WaitForMessage() target_app.raiden.message_handler = message_handler wait_for = list() for amount, app in enumerate(sender_apps, 1): secret = random_secret() wait = message_handler.wait_for_message( Unlock, {'secret': secret}, ) wait_for.append((wait, app.raiden.address, amount)) RaidenAPI(app.raiden).transfer_async( registry_address=registry_address, token_address=token_address, amount=amount, identifier=amount, target=target_address, secret=to_hex(secret), secret_hash=to_hex(sha3(secret)), ) for wait, sender, amount in wait_for: wait.wait() assert search_for_item( target_app.raiden.wal.storage.get_events(), EventPaymentReceivedSuccess, { 'amount': amount, 'identifier': amount, 'initiator': sender, 'payment_network_identifier': registry_address, # 'token_network_identifier': , }, )
def make_transfer_description( payment_network_identifier=UNIT_PAYMENT_NETWORK_IDENTIFIER, payment_identifier=UNIT_TRANSFER_IDENTIFIER, amount=UNIT_TRANSFER_AMOUNT, token_network=UNIT_TOKEN_NETWORK_ADDRESS, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, secret=None, ): return TransferDescriptionWithSecretState( payment_network_identifier=payment_network_identifier, payment_identifier=payment_identifier, amount=amount, token_network_identifier=token_network, initiator=initiator, target=target, secret=secret or random_secret(), )
def handle_message_refundtransfer(raiden: "RaidenService", message: RefundTransfer) -> None: chain_state = views.state_from_raiden(raiden) from_transfer = lockedtransfersigned_from_message(message=message) role = views.get_transfer_role( chain_state=chain_state, secrethash=from_transfer.lock.secrethash) state_changes: List[StateChange] = [] if role == "initiator": old_secret = views.get_transfer_secret( chain_state, from_transfer.lock.secrethash) is_secret_known = old_secret is not None and old_secret != ABSENT_SECRET state_changes.append( ReceiveTransferCancelRoute( transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member )) # Currently, the only case where we can be initiators and not # know the secret is if the transfer is part of an atomic swap. In # the case of an atomic swap, we will not try to re-route the # transfer. In all other cases we can try to find another route # (and generate a new secret) if is_secret_known: state_changes.append( ActionTransferReroute( transfer=from_transfer, balance_proof=from_transfer.balance_proof, # pylint: disable=no-member sender=from_transfer.balance_proof.sender, # pylint: disable=no-member secret=random_secret(), )) else: state_changes.append( ReceiveTransferRefund( transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member )) raiden.handle_and_track_state_changes(state_changes)
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert iteration.new_state is None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed
def handle_message_refundtransfer(raiden: RaidenService, message: RefundTransfer) -> None: token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) # FIXME: Shouldn't request routes here routes, _ = get_best_routes( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_address), one_to_n_address=raiden.default_one_to_n_address, from_address=InitiatorAddress(raiden.address), to_address=from_transfer.target, amount=PaymentAmount( from_transfer.lock.amount), # FIXME: mypy; deprecated by #3863 previous_address=message.sender, config=raiden.config, privkey=raiden.privkey, ) role = views.get_transfer_role( chain_state=chain_state, secrethash=from_transfer.lock.secrethash) state_change: StateChange if role == "initiator": old_secret = views.get_transfer_secret( chain_state, from_transfer.lock.secrethash) # We currently don't allow multi routes if the initiator does not # hold the secret. In such case we remove all other possible routes # which allow the API call to return with with an error message. if old_secret == EMPTY_SECRET: routes = list() secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( routes=routes, transfer=from_transfer, secret=secret) else: state_change = ReceiveTransferRefund(transfer=from_transfer, routes=routes) raiden.handle_and_track_state_change(state_change)
def handle_message_refundtransfer(raiden: 'RaidenService', message: RefundTransfer): registry_address = message.registry_address from_transfer = lockedtransfersigned_from_message(message) node_state = views.state_from_raiden(raiden) routes = get_best_routes( node_state, registry_address, from_transfer.token, raiden.address, from_transfer.target, from_transfer.lock.amount, message.sender, ) role = views.get_transfer_role( node_state, from_transfer.lock.secrethash, ) if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( registry_address, message.sender, routes, from_transfer, secret, ) else: state_change = ReceiveTransferRefund( message.message_identifier, message.sender, from_transfer, ) raiden.handle_state_change(state_change)
def start_send_crosstansfer(self, cross_id, identifier=None): cross_data = self.wal.get_crosstransaction_by_identifier(cross_id) print(cross_data) amount = cross_data[4] target = cross_data[2] btc_amount = cross_data[5] token_network_identifier = cross_data[3] self.transport.start_health_check(target) secret = random_secret() init_initiator_statechange = initiator_init( self, cross_id, amount, secret, token_network_identifier, target, ) print("init_initiator_statechange: ", init_initiator_statechange) self.handle_cross_state_change(init_initiator_statechange, cross_id, secret, btc_amount)
def handle_message_refundtransfer(raiden: RaidenService, message: RefundTransfer): token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) routes = get_best_routes( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_address), from_address=InitiatorAddress(raiden.address), to_address=from_transfer.target, amount=from_transfer.lock.amount, previous_address=message.sender, config=raiden.config, privkey=raiden.privkey, ) role = views.get_transfer_role( chain_state, from_transfer.lock.secrethash, ) state_change: StateChange if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( routes=routes, transfer=from_transfer, secret=secret, ) else: state_change = ReceiveTransferRefund( transfer=from_transfer, routes=routes, ) raiden.handle_and_track_state_change(state_change)
def start_mediated_transfer( self, token_network_identifier, amount, target, identifier, ): self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() assert identifier not in self.identifier_to_results async_result = AsyncResult() self.identifier_to_results[identifier].append(async_result) secret = random_secret() init_initiator_statechange = initiator_init( self, identifier, amount, secret, token_network_identifier, target, ) # TODO: implement the network timeout raiden.config['msg_timeout'] and # cancel the current transfer if it happens (issue #374) # # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return async_result
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) current_state = iteration.new_state # As per the description of the issue here: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # We can fail the payment but can't delete the payment task if there are no # more routes, but we have to wait for the lock expiration assert iteration.new_state is not None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed invalid_balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof.token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, ) balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof.token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, private_key=refund_pkey, ) invalid_lock_expired_state_change = ReceiveLockExpired( invalid_balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) lock_expired_state_change = ReceiveLockExpired( balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) before_expiry_block = original_transfer.lock.expiration - 1 expiry_block = channel.get_sender_expiration_threshold(original_transfer.lock) # a block before lock expiration, no events should be emitted current_state = iteration.new_state state_change = Block( block_number=before_expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert not iteration.events assert iteration.new_state, 'payment task should not be deleted at this block' # process an invalid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, invalid_lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) assert events.must_contain_entry(iteration.events, EventInvalidReceivedLockExpired, {}) # process a valid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) # now we get to the lock expiration block current_state = iteration.new_state state_change = Block( block_number=expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) # Since there was a refund transfer the payment task must not have been deleted assert iteration.new_state is not None # process the lock expired message after lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, expiry_block, ) # should be accepted assert events.must_contain_entry(iteration.events, SendProcessed, {}) assert iteration.new_state, 'payment task should be there waiting for next block' # process the the block after lock expiration current_state = iteration.new_state state_change = Block( block_number=expiry_block + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block + 1, ) assert iteration.new_state is None, 'from this point on the payment task should go'
def test_refund_transfer_next_route(): amount = UNIT_TRANSFER_AMOUNT our_address = factories.ADDR refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, partner_balance=amount, our_address=our_address, partner_address=refund_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel3 = factories.make_channel( our_balance=amount, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channelmap = { channel1.identifier: channel1, channel2.identifier: channel2, channel3.identifier: channel3, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), factories.route_from_channel(channel3), ] block_number = 10 current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, pseudo_random_generator, block_number, ) original_transfer = current_state.initiator.transfer channel_identifier = current_state.initiator.channel_identifier channel_state = channelmap[channel_identifier] expiration = original_transfer.lock.expiration - channel_state.reveal_timeout - TRANSIT_BLOCKS refund_transfer = factories.make_signed_transfer( amount, our_address, original_transfer.target, expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=channel1.identifier, pkey=refund_pkey, sender=refund_address, ) assert channel_state.partner_state.address == refund_address state_change = ReceiveTransferRefundCancelRoute( sender=refund_address, routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, pseudo_random_generator, block_number, ) assert iteration.new_state is not None route_cancelled = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) new_transfer = next(e for e in iteration.events if isinstance(e, SendLockedTransfer)) assert route_cancelled, 'The previous transfer must be cancelled' assert new_transfer, 'No mediated transfer event emitted, should have tried a new route' msg = 'the new transfer must use a new secret / secrethash' assert new_transfer.transfer.lock.secrethash != refund_transfer.lock.secrethash, msg assert iteration.new_state.initiator is not None
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) current_state = iteration.new_state # As per the description of the issue here: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # We can fail the payment but can't delete the payment task if there are no # more routes, but we have to wait for the lock expiration assert iteration.new_state is not None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed invalid_balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof. token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, ) balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof. token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, private_key=refund_pkey, ) invalid_lock_expired_state_change = ReceiveLockExpired( invalid_balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) lock_expired_state_change = ReceiveLockExpired( balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) before_expiry_block = original_transfer.lock.expiration - 1 expiry_block = original_transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # a block before lock expiration, no events should be emitted current_state = iteration.new_state state_change = Block( block_number=before_expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert not iteration.events assert iteration.new_state, 'payment task should not be deleted at this block' # process an invalid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, invalid_lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) assert events.must_contain_entry(iteration.events, EventInvalidReceivedLockExpired, {}) # process a valid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) # now we get to the lock expiration block current_state = iteration.new_state state_change = Block( block_number=expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) # Since there was a refund transfer the payment task must not have been deleted assert iteration.new_state is not None # process the lock expired message after lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, expiry_block, ) # should be accepted assert events.must_contain_entry(iteration.events, SendProcessed, {}) assert iteration.new_state, 'payment task should be there waiting for next block' # process the the block after lock expiration current_state = iteration.new_state state_change = Block( block_number=expiry_block + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block + 1, ) assert iteration.new_state is None, 'from this point on the payment task should go'
def transfer_and_assert_path( path: List[App], token_address: TokenAddress, amount: PaymentAmount, identifier: PaymentID, fee: FeeAmount = 0, timeout: float = 10, ) -> None: """ Nice to read shortcut to make successful LockedTransfer. Note: This utility *does not enforce the path*, however it does check the provided path is used in totality. It's the responsability of the caller to ensure the path will be used. All nodes in `path` are synched. """ assert identifier is not None, "The identifier must be provided" secret = random_secret() first_app = path[0] payment_network_identifier = first_app.raiden.default_registry.address token_network_address = views.get_token_network_identifier_by_token_address( chain_state=views.state_from_app(first_app), payment_network_id=payment_network_identifier, token_address=token_address, ) for app in path: assert isinstance(app.raiden.message_handler, WaitForMessage) msg = "The apps must be on the same payment network" assert app.raiden.default_registry.address == payment_network_identifier, msg app_token_network_address = views.get_token_network_identifier_by_token_address( chain_state=views.state_from_app(app), payment_network_id=payment_network_identifier, token_address=token_address, ) msg = "The apps must be synchronized with the blockchain" assert token_network_address == app_token_network_address, msg pairs = zip(path[:-1], path[1:]) receiving = list() for from_app, to_app in pairs: from_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_app(from_app), token_network_id=token_network_address, partner_address=to_app.raiden.address, ) to_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_app(to_app), token_network_id=token_network_address, partner_address=from_app.raiden.address, ) msg = (f"{pex(from_app.raiden.address)} does not have a channel with " f"{pex(to_app.raiden.address)} needed to transfer through the " f"path {lpex(app.raiden.address for app in path)}.") assert from_channel_state, msg assert to_channel_state, msg msg = (f"channel among {pex(from_app.raiden.address)} and " f"{pex(to_app.raiden.address)} must be open to be used for a " f"transfer") assert channel.get_status( from_channel_state) == CHANNEL_STATE_OPENED, msg assert channel.get_status( to_channel_state) == CHANNEL_STATE_OPENED, msg receiving.append((to_app, to_channel_state.identifier)) results = [ app.raiden.message_handler.wait_for_message( Unlock, { "channel_identifier": channel_identifier, "token_network_address": token_network_address, "payment_identifier": identifier, "secret": secret, }, ) for app, channel_identifier in receiving ] last_app = path[-1] payment_status = first_app.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_address, amount=amount, fee=fee, target=last_app.raiden.address, identifier=identifier, payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE, secret=secret, ) with Timeout(seconds=timeout): gevent.wait(results) msg = (f"transfer from {pex(first_app.raiden.address)} " f"to {pex(last_app.raiden.address)} failed.") assert payment_status.payment_done.get(), msg
def test_refund_transfer_next_route(): amount = UNIT_TRANSFER_AMOUNT our_address = factories.ADDR refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, partner_balance=amount, our_address=our_address, partner_address=refund_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel3 = factories.make_channel( our_balance=amount, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, channel3.identifier: channel3, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), factories.route_from_channel(channel3), ] block_number = 10 current_state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, pseudo_random_generator, block_number, ) original_transfer = current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, our_address, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=channel1.identifier, pkey=refund_pkey, sender=refund_address, ) assert channel1.partner_state.address == refund_address state_change = ReceiveTransferRefundCancelRoute( routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert iteration.new_state is not None route_cancelled = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) new_transfer = next(e for e in iteration.events if isinstance(e, SendLockedTransfer)) assert route_cancelled, 'The previous transfer must be cancelled' assert new_transfer, 'No mediated transfer event emitted, should have tried a new route' msg = 'the new transfer must use a new secret / secrethash' assert new_transfer.transfer.lock.secrethash != refund_transfer.lock.secrethash, msg assert iteration.new_state.initiator is not None