def test_state_operators(): a_route = RouteState('opened', ADDRESS, ADDRESS2, 5, 5, 5, 5) b_route = RouteState('opened', ADDRESS, ADDRESS2, 5, 5, 5, 5) c_route = RouteState('closed', ADDRESS3, ADDRESS2, 1, 2, 3, 4) assert a_route == b_route assert not a_route != b_route assert a_route != c_route assert not a_route == c_route d_route = RouteState('opened', ADDRESS4, ADDRESS, 1, 2, 3, 4) a = RoutesState([a_route, d_route]) b = RoutesState([a_route, d_route]) c = RoutesState([a_route, c_route]) assert a == b assert not a != b assert a != c assert not a == c a = LockedTransferState(1, 2, ADDRESS, ADDRESS2, ADDRESS3, 4, HASH, 'secret') b = LockedTransferState(1, 2, ADDRESS, ADDRESS2, ADDRESS3, 4, HASH, 'secret') c = LockedTransferState(2, 4, ADDRESS3, ADDRESS4, ADDRESS, 4, HASH, 'secret') assert a == b assert not a != b assert a != c assert not a == c
def make_init_statechange(routes, target, amount=factories.UNIT_TRANSFER_AMOUNT, block_number=1, our_address=factories.ADDR, secret_generator=None, identifier=0, token=factories.UNIT_TOKEN_ADDRESS): if secret_generator is None: secret_generator = SequenceGenerator() transfer = factories.make_transfer( amount, initiator=our_address, target=target, identifier=identifier, token=token, secret=None, hashlock=None, expiration=None, ) init_state_change = ActionInitInitiator( our_address, transfer, RoutesState(routes), secret_generator, block_number, ) return init_state_change
def test_init_without_routes(): amount = factories.UNIT_TRANSFER_AMOUNT block_number = 1 our_address, target_address = factories.HOP1, factories.HOP3 routes = [] transfer = factories.make_transfer( amount, initiator=our_address, target=target_address, secret=None, hashlock=None, expiration=None, ) init_state_change = ActionInitInitiator( our_address, transfer, RoutesState(routes), SequenceGenerator(), block_number, ) initiator_state_machine = StateManager( initiator.state_transition, None, ) assert initiator_state_machine.current_state is None events = initiator_state_machine.dispatch(init_state_change, ) assert len(events) == 1 assert any(isinstance(e, EventTransferSentFailed) for e in events) assert initiator_state_machine.current_state is None
def test_next_transfer_pair(): timeout_blocks = 47 block_number = 3 balance = 10 payer_route = factories.make_route(factories.HOP1, balance) payer_transfer = factories.make_transfer(balance, factories.ADDR, expiration=50) routes = [ factories.make_route(factories.HOP2, available_balance=balance), ] routes_state = RoutesState(list(routes)) # copy because the list will be modified inplace pair, events = mediator.next_transfer_pair( payer_route, payer_transfer, routes_state, timeout_blocks, block_number, ) assert pair.payer_route == payer_route assert pair.payer_transfer == payer_transfer assert pair.payee_route == routes[0] assert pair.payee_transfer.expiration < pair.payer_transfer.expiration assert isinstance(events[0], SendMediatedTransfer) assert len(routes_state.available_routes) == 0
def test_set_secret(): amount = 10 block_number = 7 routes = [] routes_state = RoutesState(routes) state = MediatorState( factories.ADDR, routes_state, block_number, factories.UNIT_HASHLOCK, ) state.transfers_pair = make_transfers_pair( [factories.HOP1, factories.HOP2, factories.HOP3], factories.HOP6, amount, ) mediator.set_secret(state, factories.UNIT_SECRET) assert state.secret == factories.UNIT_SECRET for pair in state.transfers_pair: assert pair.payer_transfer.secret == factories.UNIT_SECRET assert pair.payee_transfer.secret == factories.UNIT_SECRET
def make_init_statechange(from_transfer, from_route, routes, our_address=factories.ADDR): block_number = 1 init_state_change = ActionInitMediator( our_address, from_transfer, RoutesState(routes), from_route, block_number, ) return init_state_change
def test_next_route_amount(): """ Routes that dont have enough available_balance must be ignored. """ amount = 10 reveal_timeout = 30 timeout_blocks = reveal_timeout + 10 routes = [ factories.make_route( factories.HOP2, available_balance=amount * 2, reveal_timeout=reveal_timeout, ), factories.make_route( factories.HOP1, available_balance=amount + 1, reveal_timeout=reveal_timeout, ), factories.make_route( factories.HOP3, available_balance=amount // 2, reveal_timeout=reveal_timeout, ), factories.make_route( factories.HOP4, available_balance=amount, reveal_timeout=reveal_timeout, ), ] routes_state = RoutesState( list(routes)) # copy because the list will be modified inplace route1 = mediator.next_route(routes_state, timeout_blocks, amount) assert route1 == routes[0] assert routes_state.available_routes == routes[1:] assert routes_state.ignored_routes == list() route2 = mediator.next_route(routes_state, timeout_blocks, amount) assert route2 == routes[1] assert routes_state.available_routes == routes[2:] assert routes_state.ignored_routes == list() route3 = mediator.next_route(routes_state, timeout_blocks, amount) assert route3 == routes[3] assert routes_state.available_routes == list() assert routes_state.ignored_routes == [routes[2]] assert mediator.next_route(routes_state, timeout_blocks, amount) is None
def test_next_transfer_pair(): timeout_blocks = 47 block_number = 3 balance = 10 initiator = factories.HOP1 target = factories.ADDR payer_route = factories.make_route(initiator, balance) payer_transfer = factories.make_transfer(balance, initiator, target, expiration=50) routes = [ factories.make_route(factories.HOP2, available_balance=balance), ] routes_state = RoutesState( list(routes)) # copy because the list will be modified inplace pair, events = mediator.next_transfer_pair( payer_route, payer_transfer, routes_state, timeout_blocks, block_number, ) assert pair.payer_route == payer_route assert pair.payer_transfer == payer_transfer assert pair.payee_route == routes[0] assert pair.payee_transfer.expiration < pair.payer_transfer.expiration assert isinstance(events[0], SendMediatedTransfer) transfer = events[0] assert transfer.identifier == payer_transfer.identifier assert transfer.token == payer_transfer.token assert transfer.amount == payer_transfer.amount assert transfer.hashlock == payer_transfer.hashlock assert transfer.initiator == payer_transfer.initiator assert transfer.target == payer_transfer.target assert transfer.expiration < payer_transfer.expiration assert transfer.receiver == pair.payee_route.node_address assert len(routes_state.available_routes) == 0
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 = graph.get_best_routes( 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) all_events = state_manager.dispatch(init_mediator) for event in all_events: self.state_machine_event_handler.on_event(event) self.identifier_statemanager[identifier].append(state_manager)
def make_init_statechange(routes, target, amount=factories.UNIT_TRANSFER_AMOUNT, block_number=1, our_address=factories.ADDR, secret_generator=None, identifier=0): if secret_generator is None: secret_generator = SequenceGenerator() init_state_change = ActionInitInitiator( our_address, make_hashlock_transfer(amount, target=target, identifier=identifier), RoutesState(routes), secret_generator, block_number, ) return init_state_change
def test_mediate_transfer(): amount = 10 block_number = 5 expiration = 30 routes = [ factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT), ] routes_state = RoutesState(routes) state = MediatorState( factories.ADDR, routes_state, block_number, factories.UNIT_HASHLOCK, ) payer_route, payer_transfer = make_from(amount, factories.HOP6, expiration) iteration = mediator.mediate_transfer( state, payer_route, payer_transfer, ) events_mediated = [ e for e in iteration.events if isinstance(e, SendMediatedTransfer) ] assert len(events_mediated) == 1 transfer = events_mediated[0] assert transfer.identifier == payer_transfer.identifier assert transfer.token == payer_transfer.token assert transfer.amount == payer_transfer.amount assert transfer.hashlock == payer_transfer.hashlock assert transfer.target == payer_transfer.target assert payer_transfer.expiration > transfer.expiration assert transfer.node_address == payer_route.node_address
def test_next_route_reveal_timeout(): """ Routes with a larger reveal timeout than timeout_blocks must be ignored. """ amount = 10 balance = 20 timeout_blocks = 10 routes = [ factories.make_route( factories.HOP1, available_balance=balance, reveal_timeout=timeout_blocks * 2, ), factories.make_route( factories.HOP2, available_balance=balance, reveal_timeout=timeout_blocks + 1, ), factories.make_route( factories.HOP3, available_balance=balance, reveal_timeout=timeout_blocks // 2, ), factories.make_route( factories.HOP4, available_balance=balance, reveal_timeout=timeout_blocks, ), ] routes_state = RoutesState( list(routes)) # copy because the list will be modified inplace route1 = mediator.next_route(routes_state, timeout_blocks, amount) assert route1 == routes[2] assert routes_state.available_routes == [ routes[3], ] assert routes_state.ignored_routes == [routes[0], routes[1]] assert mediator.next_route(routes_state, timeout_blocks, amount) is None assert routes_state.available_routes == list() assert routes_state.ignored_routes == [routes[0], routes[1], routes[3]]
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 start_mediated_transfer(self, token_address, amount, identifier, target): # pylint: disable=too-many-locals graph = self.channelgraphs[token_address] routes = graph.get_best_routes( self.address, target, amount, lock_timeout=None, ) available_routes = [ route for route in map(route_to_routestate, routes) if route.state == CHANNEL_STATE_OPENED ] # send ping to target to make sure we can receive something back from target async_result = self.protocol.send_ping(target) async_result.wait(timeout=0.5) # allow the ping to succeed if async_result.ready(): log.debug("transfer target received invitation ping") else: log.debug( "transfer target did not receive invitation ping, probably behing NAT" ) identifier = create_default_identifier(self.address, token_address, target) 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) async_result = AsyncResult() # 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