def mediate_mediated_transfer(self, message): # pylint: disable=too-many-locals identifier = message.identifier amount = message.lock.amount target = message.target token = message.token graph = self.channelgraphs[token] routes = get_best_routes( graph, self.protocol.nodeaddresses_networkstatuses, self.address, target, amount, lock_timeout=None, ) available_routes = [ route for route in map(route_to_routestate, routes) if route.state == CHANNEL_STATE_OPENED ] from_channel = graph.partneraddress_channel[message.sender] from_route = channel_to_routestate(from_channel, message.sender) our_address = self.address from_transfer = lockedtransfer_from_message(message) route_state = RoutesState(available_routes) block_number = self.get_block_number() init_mediator = ActionInitMediator( our_address, from_transfer, route_state, from_route, block_number, ) state_manager = StateManager(mediator.state_transition, None) self.state_machine_event_handler.log_and_dispatch(state_manager, init_mediator) self.identifier_to_statemanagers[identifier].append(state_manager)
def mediate_mediated_transfer(self, message): # pylint: disable=too-many-locals identifier = message.identifier amount = message.lock.amount target = message.target token = message.token graph = self.token_to_channelgraph[token] available_routes = get_best_routes( graph, self.protocol.nodeaddresses_networkstatuses, self.address, target, amount, message.sender, ) from_channel = graph.partneraddress_to_channel[message.sender] from_route = channel_to_routestate(from_channel, message.sender) our_address = self.address from_transfer = lockedtransfer_from_message(message) route_state = RoutesState(available_routes) block_number = self.get_block_number() init_mediator = ActionInitMediator( our_address, from_transfer, route_state, from_route, block_number, ) state_manager = StateManager(mediator.state_transition, None) self.state_machine_event_handler.log_and_dispatch(state_manager, init_mediator) self.identifier_to_statemanagers[identifier].append(state_manager)
def start_mediated_transfer(self, token_address, amount, identifier, target): # pylint: disable=too-many-locals async_result = AsyncResult() graph = self.token_to_channelgraph[token_address] available_routes = get_best_routes( graph, self.protocol.nodeaddresses_networkstatuses, self.address, target, amount, None, ) if not available_routes: async_result.set(False) return async_result self.protocol.start_health_check(target) if identifier is None: identifier = create_default_identifier() route_state = RoutesState(available_routes) our_address = self.address block_number = self.get_block_number() transfer_state = LockedTransferState( identifier=identifier, amount=amount, token=token_address, initiator=self.address, target=target, expiration=None, hashlock=None, secret=None, ) # Issue #489 # # Raiden may fail after a state change using the random generator is # handled but right before the snapshot is taken. If that happens on # the next initialization when raiden is recovering and applying the # pending state changes a new secret will be generated and the # resulting events won't match, this breaks the architecture model, # since it's assumed the re-execution of a state change will always # produce the same events. # # TODO: Removed the secret generator from the InitiatorState and add # the secret into all state changes that require one, this way the # secret will be serialized with the state change and the recovery will # use the same /random/ secret. random_generator = RandomSecretGenerator() init_initiator = ActionInitInitiator( our_address=our_address, transfer=transfer_state, routes=route_state, random_generator=random_generator, block_number=block_number, ) state_manager = StateManager(initiator.state_transition, None) self.state_machine_event_handler.log_and_dispatch( state_manager, init_initiator) # TODO: implement the network timeout raiden.config['msg_timeout'] and # cancel the current transfer if it hapens (issue #374) self.identifier_to_statemanagers[identifier].append(state_manager) self.identifier_to_results[identifier].append(async_result) return async_result
def _run(self): # pylint: disable=method-hidden,too-many-locals fee = 0 raiden = self.raiden tokenswap = self.tokenswap # this is the MediatedTransfer that wil pay the maker's half of the # swap, not necessarily from him maker_paying_transfer = self.from_mediated_transfer # this is the address of the node that the taker actually has a channel # with (might or might not be the maker) maker_payer_hop = maker_paying_transfer.sender assert tokenswap.identifier == maker_paying_transfer.identifier assert tokenswap.from_token == maker_paying_transfer.token assert tokenswap.from_amount == maker_paying_transfer.lock.amount assert tokenswap.from_nodeaddress == maker_paying_transfer.initiator maker_receiving_token = tokenswap.to_token to_amount = tokenswap.to_amount identifier = maker_paying_transfer.identifier hashlock = maker_paying_transfer.lock.hashlock maker_address = maker_paying_transfer.initiator taker_receiving_token = maker_paying_transfer.token taker_paying_token = maker_receiving_token from_graph = raiden.token_to_channelgraph[taker_receiving_token] from_channel = from_graph.partneraddress_to_channel[maker_payer_hop] to_graph = raiden.token_to_channelgraph[maker_receiving_token] # update the channel's distributable and merkle tree from_channel.register_transfer( raiden.get_block_number(), maker_paying_transfer, ) # register the task to receive Refund/Secrect/RevealSecret messages raiden.greenlet_task_dispatcher.register_task(self, hashlock) raiden.register_channel_for_hashlock(taker_receiving_token, from_channel, hashlock) # send to the maker a secret request informing how much the taker will # be _paid_, this is used to inform the maker that his part of the # mediated transfer is okay secret_request = SecretRequest( identifier, maker_paying_transfer.lock.hashlock, maker_paying_transfer.lock.amount, ) raiden.sign(secret_request) raiden.send_async(maker_address, secret_request) lock_expiration = maker_paying_transfer.lock.expiration - raiden.config['reveal_timeout'] # Note: taker may only try different routes if a RefundTransfer is # received, because the maker is the node controlling the secret available_routes = get_best_routes( to_graph, raiden.protocol.nodeaddresses_networkstatuses, raiden.address, maker_address, maker_paying_transfer.lock.amount, previous_address=None, ) if not available_routes: if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'TAKER TOKEN SWAP FAILED, NO ROUTES', from_=pex(node_address), to=pex(maker_address), ) return first_transfer = None for route in available_routes: taker_paying_channel = to_graph.get_channel_by_contract_address( route.channel_address, ) taker_paying_hop = route.node_address if log.isEnabledFor(logging.DEBUG): log.debug( 'TAKER TOKEN SWAP', from_=pex(maker_paying_transfer.target), to=pex(maker_address), msghash=pex(maker_paying_transfer.hash), hashlock=pex(hashlock), ) # make a paying MediatedTransfer with same hashlock/identifier and the # taker's paying token/amount taker_paying_transfer = taker_paying_channel.create_mediatedtransfer( raiden.address, maker_address, fee, to_amount, identifier, lock_expiration, hashlock, ) raiden.sign(taker_paying_transfer) taker_paying_channel.register_transfer( raiden.get_block_number(), taker_paying_transfer, ) if not first_transfer: first_transfer = taker_paying_transfer if log.isEnabledFor(logging.DEBUG): log.debug( 'EXCHANGE TRANSFER NEW PATH', path=lpex(taker_paying_hop), hashlock=pex(hashlock), ) # register the task to receive Refund/Secrect/RevealSecret messages raiden.register_channel_for_hashlock( maker_receiving_token, taker_paying_channel, hashlock, ) response, secret = self.send_and_wait_valid( raiden, taker_paying_transfer, maker_payer_hop, ) # only refunds for `maker_receiving_token` must be considered # (check send_and_wait_valid) if isinstance(response, RefundTransfer): if response.lock.amount != taker_paying_transfer.amount: log.info( 'Partner %s sent an invalid refund message with an invalid amount', pex(taker_paying_hop), ) raiden.greenlet_task_dispatcher.unregister_task(self, hashlock, False) return else: taker_paying_channel.register_transfer( raiden.get_block_number(), response, ) elif isinstance(response, RevealSecret): # the secret was registered by the message handler # wait for the taker_paying_hop to reveal the secret prior to # unlocking locally if response.sender != taker_paying_hop: response = self.wait_reveal_secret( raiden, taker_paying_hop, taker_paying_transfer.lock.expiration, ) # unlock and send the Secret message raiden.handle_secret( identifier, taker_paying_token, response.secret, None, hashlock, ) # if the secret arrived early, withdraw it, otherwise send the # RevealSecret forward in the maker-path if secret: raiden.handle_secret( identifier, taker_receiving_token, response.secret, secret, hashlock, ) # wait for the withdraw in case it did not happen yet self._wait_for_unlock_or_close( raiden, from_graph, from_channel, maker_paying_transfer, ) return # the lock expired else: if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'TAKER TOKEN SWAP FAILED', from_=pex(node_address), to=pex(maker_address), ) self.async_result.set(False) return # no route is available, wait for the sent mediated transfer to expire self._wait_expiration(raiden, first_transfer) if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'TAKER TOKEN SWAP FAILED', from_=pex(node_address), to=pex(maker_address), ) self.async_result.set(False)
def _run(self): # pylint: disable=method-hidden,too-many-locals tokenswap = self.tokenswap raiden = self.raiden identifier = tokenswap.identifier from_token = tokenswap.from_token from_amount = tokenswap.from_amount to_token = tokenswap.to_token to_amount = tokenswap.to_amount to_nodeaddress = tokenswap.to_nodeaddress from_graph = raiden.token_to_channelgraph[from_token] to_graph = raiden.token_to_channelgraph[to_token] from_routes = get_best_routes( from_graph, raiden.protocol.nodeaddresses_networkstatuses, raiden.address, to_nodeaddress, from_amount, previous_address=None, ) fee = 0 for route in from_routes: # for each new path a new secret must be used secret = sha3(hex(random.getrandbits(256))) hashlock = sha3(secret) from_channel = from_graph.get_channel_by_contract_address(route.channel_address) raiden.greenlet_task_dispatcher.register_task(self, hashlock) raiden.register_channel_for_hashlock(from_token, from_channel, hashlock) block_number = raiden.get_block_number() lock_expiration = block_number + from_channel.settle_timeout from_mediated_transfer = from_channel.create_mediatedtransfer( raiden.address, to_nodeaddress, fee, from_amount, identifier, lock_expiration, hashlock, ) raiden.sign(from_mediated_transfer) from_channel.register_transfer( # must be the same block number used to compute lock_expiration block_number, from_mediated_transfer, ) # wait for the SecretRequest and MediatedTransfer to_mediated_transfer = self.send_and_wait_valid_state( raiden, route.node_address, to_nodeaddress, from_mediated_transfer, to_token, to_amount, ) if to_mediated_transfer is None: # the initiator can unregister right away since it knows the # secret wont be revealed raiden.greenlet_task_dispatcher.unregister_task(self, hashlock, False) elif isinstance(to_mediated_transfer, MediatedTransfer): to_hop = to_mediated_transfer.sender to_channel = to_graph.partneraddress_to_channel[to_hop] to_channel.register_transfer( raiden.get_block_number(), to_mediated_transfer, ) raiden.register_channel_for_hashlock(to_token, to_channel, hashlock) # A swap is composed of two mediated transfers, we need to # reveal the secret to both, since the maker is one of the ends # we just need to send the reveal secret directly to the taker. reveal_secret = RevealSecret(secret) raiden.sign(reveal_secret) raiden.send_async(to_nodeaddress, reveal_secret) from_channel.register_secret(secret) # Register the secret with the to_channel and send the # RevealSecret message to the node that is paying the to_token # (this node might, or might not be the same as the taker), # then wait for the withdraw. raiden.handle_secret( identifier, to_token, secret, None, hashlock, ) to_channel = to_graph.partneraddress_to_channel[to_mediated_transfer.sender] self._wait_for_unlock_or_close( raiden, to_graph, to_channel, to_mediated_transfer, ) # unlock the from_token and optimistically reveal the secret # forward raiden.handle_secret( identifier, from_token, secret, None, hashlock, ) raiden.greenlet_task_dispatcher.unregister_task(self, hashlock, True) self.async_result.set(True) return if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'MAKER TOKEN SWAP FAILED', node=pex(node_address), to=pex(to_nodeaddress), ) # all routes failed self.async_result.set(False)
def start_mediated_transfer(self, token_address, amount, identifier, target): # pylint: disable=too-many-locals async_result = AsyncResult() graph = self.token_to_channelgraph[token_address] available_routes = get_best_routes( graph, self.protocol.nodeaddresses_networkstatuses, self.address, target, amount, None, ) if not available_routes: async_result.set(False) return async_result self.protocol.start_health_check(target) if identifier is None: identifier = create_default_identifier() route_state = RoutesState(available_routes) our_address = self.address block_number = self.get_block_number() transfer_state = LockedTransferState( identifier=identifier, amount=amount, token=token_address, initiator=self.address, target=target, expiration=None, hashlock=None, secret=None, ) # Issue #489 # # Raiden may fail after a state change using the random generator is # handled but right before the snapshot is taken. If that happens on # the next initialization when raiden is recovering and applying the # pending state changes a new secret will be generated and the # resulting events won't match, this breaks the architecture model, # since it's assumed the re-execution of a state change will always # produce the same events. # # TODO: Removed the secret generator from the InitiatorState and add # the secret into all state changes that require one, this way the # secret will be serialized with the state change and the recovery will # use the same /random/ secret. random_generator = RandomSecretGenerator() init_initiator = ActionInitInitiator( our_address=our_address, transfer=transfer_state, routes=route_state, random_generator=random_generator, block_number=block_number, ) state_manager = StateManager(initiator.state_transition, None) self.state_machine_event_handler.log_and_dispatch(state_manager, init_initiator) # TODO: implement the network timeout raiden.config['msg_timeout'] and # cancel the current transfer if it hapens (issue #374) self.identifier_to_statemanagers[identifier].append(state_manager) self.identifier_to_results[identifier].append(async_result) return async_result
def _run(self): # pylint: disable=method-hidden,too-many-locals fee = 0 raiden = self.raiden tokenswap = self.tokenswap # this is the MediatedTransfer that wil pay the maker's half of the # swap, not necessarily from him maker_paying_transfer = self.from_mediated_transfer # this is the address of the node that the taker actually has a channel # with (might or might not be the maker) maker_payer_hop = maker_paying_transfer.sender assert tokenswap.identifier == maker_paying_transfer.identifier assert tokenswap.from_token == maker_paying_transfer.token assert tokenswap.from_amount == maker_paying_transfer.lock.amount assert tokenswap.from_nodeaddress == maker_paying_transfer.initiator maker_receiving_token = tokenswap.to_token to_amount = tokenswap.to_amount identifier = maker_paying_transfer.identifier hashlock = maker_paying_transfer.lock.hashlock maker_address = maker_paying_transfer.initiator taker_receiving_token = maker_paying_transfer.token taker_paying_token = maker_receiving_token from_graph = raiden.token_to_channelgraph[taker_receiving_token] from_channel = from_graph.partneraddress_to_channel[maker_payer_hop] to_graph = raiden.token_to_channelgraph[maker_receiving_token] # update the channel's distributable and merkle tree from_channel.register_transfer( raiden.get_block_number(), maker_paying_transfer, ) # register the task to receive Refund/Secrect/RevealSecret messages raiden.greenlet_task_dispatcher.register_task(self, hashlock) raiden.register_channel_for_hashlock(taker_receiving_token, from_channel, hashlock) # send to the maker a secret request informing how much the taker will # be _paid_, this is used to inform the maker that his part of the # mediated transfer is okay secret_request = SecretRequest( identifier, maker_paying_transfer.lock.hashlock, maker_paying_transfer.lock.amount, ) raiden.sign(secret_request) raiden.send_async(maker_address, secret_request) lock_expiration = maker_paying_transfer.lock.expiration - raiden.config['reveal_timeout'] # Note: taker may only try different routes if a RefundTransfer is # received, because the maker is the node controlling the secret available_routes = get_best_routes( to_graph, raiden.protocol.nodeaddresses_networkstatuses, raiden.address, maker_address, maker_paying_transfer.lock.amount, previous_address=None, ) if not available_routes: if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'TAKER TOKEN SWAP FAILED, NO ROUTES', from_=pex(node_address), to=pex(maker_address), ) return first_transfer = None for route in available_routes: taker_paying_channel = to_graph.get_channel_by_contract_address( route.channel_address, ) taker_paying_hop = route.node_address if log.isEnabledFor(logging.DEBUG): log.debug( 'TAKER TOKEN SWAP', from_=pex(maker_paying_transfer.target), to=pex(maker_address), msghash=pex(maker_paying_transfer.hash), hashlock=pex(hashlock), ) # make a paying MediatedTransfer with same hashlock/identifier and the # taker's paying token/amount taker_paying_transfer = taker_paying_channel.create_mediatedtransfer( raiden.address, maker_address, fee, to_amount, identifier, lock_expiration, hashlock, ) raiden.sign(taker_paying_transfer) taker_paying_channel.register_transfer( raiden.get_block_number(), taker_paying_transfer, ) if not first_transfer: first_transfer = taker_paying_transfer if log.isEnabledFor(logging.DEBUG): log.debug( 'EXCHANGE TRANSFER NEW PATH', path=lpex(str(t).encode() for t in taker_paying_hop), hashlock=pex(hashlock), ) # register the task to receive Refund/Secrect/RevealSecret messages raiden.register_channel_for_hashlock( maker_receiving_token, taker_paying_channel, hashlock, ) response, secret = self.send_and_wait_valid( raiden, taker_paying_transfer, maker_payer_hop, ) # only refunds for `maker_receiving_token` must be considered # (check send_and_wait_valid) if isinstance(response, RefundTransfer): if response.lock.amount != taker_paying_transfer.amount: log.info( 'Partner %s sent an invalid refund message with an invalid amount', pex(taker_paying_hop), ) raiden.greenlet_task_dispatcher.unregister_task(self, hashlock, False) return else: taker_paying_channel.register_transfer( raiden.get_block_number(), response, ) elif isinstance(response, RevealSecret): # the secret was registered by the message handler # wait for the taker_paying_hop to reveal the secret prior to # unlocking locally if response.sender != taker_paying_hop: response = self.wait_reveal_secret( raiden, taker_paying_hop, taker_paying_transfer.lock.expiration, ) # unlock and send the Secret message raiden.handle_secret( identifier, taker_paying_token, response.secret, None, hashlock, ) # if the secret arrived early, withdraw it, otherwise send the # RevealSecret forward in the maker-path if secret: raiden.handle_secret( identifier, taker_receiving_token, response.secret, secret, hashlock, ) # wait for the withdraw in case it did not happen yet self._wait_for_unlock_or_close( raiden, from_graph, from_channel, maker_paying_transfer, ) return # the lock expired else: if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'TAKER TOKEN SWAP FAILED', from_=pex(node_address), to=pex(maker_address), ) return # no route is available, wait for the sent mediated transfer to expire self._wait_expiration(raiden, first_transfer) if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'TAKER TOKEN SWAP FAILED', from_=pex(node_address), to=pex(maker_address), )
def _run(self): # pylint: disable=method-hidden,too-many-locals tokenswap = self.tokenswap raiden = self.raiden identifier = tokenswap.identifier from_token = tokenswap.from_token from_amount = tokenswap.from_amount to_token = tokenswap.to_token to_amount = tokenswap.to_amount to_nodeaddress = tokenswap.to_nodeaddress from_graph = raiden.token_to_channelgraph[from_token] to_graph = raiden.token_to_channelgraph[to_token] from_routes = get_best_routes( from_graph, raiden.protocol.nodeaddresses_networkstatuses, raiden.address, to_nodeaddress, from_amount, previous_address=None, ) fee = 0 for route in from_routes: # for each new path a new secret must be used secret = sha3(hex(random.getrandbits(256)).encode()) hashlock = sha3(secret) from_channel = from_graph.get_channel_by_contract_address(route.channel_address) raiden.greenlet_task_dispatcher.register_task(self, hashlock) raiden.register_channel_for_hashlock(from_token, from_channel, hashlock) block_number = raiden.get_block_number() lock_expiration = block_number + from_channel.settle_timeout from_mediated_transfer = from_channel.create_mediatedtransfer( raiden.address, to_nodeaddress, fee, from_amount, identifier, lock_expiration, hashlock, ) raiden.sign(from_mediated_transfer) from_channel.register_transfer( # must be the same block number used to compute lock_expiration block_number, from_mediated_transfer, ) # wait for the SecretRequest and MediatedTransfer to_mediated_transfer = self.send_and_wait_valid_state( raiden, route.node_address, to_nodeaddress, from_mediated_transfer, to_token, to_amount, ) if to_mediated_transfer is None: # the initiator can unregister right away since it knows the # secret wont be revealed raiden.greenlet_task_dispatcher.unregister_task(self, hashlock, False) elif isinstance(to_mediated_transfer, MediatedTransfer): to_hop = to_mediated_transfer.sender to_channel = to_graph.partneraddress_to_channel[to_hop] to_channel.register_transfer( raiden.get_block_number(), to_mediated_transfer, ) raiden.register_channel_for_hashlock(to_token, to_channel, hashlock) # A swap is composed of two mediated transfers, we need to # reveal the secret to both. Since the maker is one of the ends # we just need to send the reveal secret directly to the taker. reveal_secret = RevealSecret(secret) raiden.sign(reveal_secret) raiden.send_async(to_nodeaddress, reveal_secret) from_channel.register_secret(secret) # Register the secret with the to_channel and send the # RevealSecret message to the node that is paying the to_token # (this node might, or might not be the same as the taker), # then wait for the withdraw. raiden.handle_secret( identifier, to_token, secret, None, hashlock, ) to_channel = to_graph.partneraddress_to_channel[to_mediated_transfer.sender] self._wait_for_unlock_or_close( raiden, to_graph, to_channel, to_mediated_transfer, ) # unlock the from_token and optimistically reveal the secret # forward raiden.handle_secret( identifier, from_token, secret, None, hashlock, ) raiden.greenlet_task_dispatcher.unregister_task(self, hashlock, True) self.async_result.set(True) return if log.isEnabledFor(logging.DEBUG): node_address = raiden.address log.debug( 'MAKER TOKEN SWAP FAILED', node=pex(node_address), to=pex(to_nodeaddress), ) # all routes failed self.async_result.set(False)