def replay_wal( storage: SerializedSQLiteStorage, token_network_address: TokenNetworkAddress, partner_address: Address, translator: Optional[Translator] = None, ) -> None: all_state_changes = storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) state_manager = StateManager(state_transition=node.state_transition, current_state=None) wal = WriteAheadLog(state_manager, storage) for _, state_change in enumerate(all_state_changes): # Dispatching the state changes one-by-one to easy debugging _, events = wal.state_manager.dispatch([state_change]) chain_state = wal.state_manager.current_state msg = "Chain state must never be cleared up." assert chain_state, msg channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, to_canonical_address(token_network_address), to_canonical_address(partner_address), ) if not channel_state: continue ### # Customize this to filter things further somewhere around here. # An example would be to add `import pdb; pdb.set_trace()` # and inspect the state. ### print_state_change(state_change, translator=translator) print_events(chain.from_iterable(events), translator=translator)
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 __init__(self, message_handler=None, state_transition=None): self.chain = MockChain() self.private_key, self.address = factories.make_privatekey_address() self.chain.node_address = self.address self.message_handler = message_handler if state_transition is None: state_transition = node.state_transition serializer = JSONSerializer state_manager = StateManager(state_transition, None) storage = SQLiteStorage(':memory:', serializer) self.wal = WriteAheadLog(state_manager, storage) state_change = ActionInitChain( random.Random(), 0, self.chain.node_address, self.chain.network_id, ) self.wal.log_and_dispatch(state_change)
def restore_to_state_change( transition_function: typing.Callable, storage: SQLiteStorage, state_change_identifier: int, ) -> 'WriteAheadLog': msg = "state change identifier 'latest' or an integer greater than zero" assert state_change_identifier == 'latest' or state_change_identifier > 0, msg from_state_change_id, chain_state = storage.get_snapshot_closest_to_state_change( state_change_identifier=state_change_identifier, ) if chain_state is not None: log.debug( 'Restoring from snapshot', from_state_change_id=from_state_change_id, to_state_change_id=state_change_identifier, ) else: log.debug( 'No snapshot found, replaying all state changes', to_state_change_id=state_change_identifier, ) unapplied_state_changes = storage.get_statechanges_by_identifier( from_identifier=from_state_change_id, to_identifier=state_change_identifier, ) state_manager = StateManager(transition_function, chain_state) wal = WriteAheadLog(state_manager, storage) log.debug('Replaying state changes', num_state_changes=len(unapplied_state_changes)) for state_change in unapplied_state_changes: wal.state_manager.dispatch(state_change) return wal
def restore_from_latest_snapshot(transition_function, storage): events = list() snapshot = storage.get_state_snapshot() if snapshot: last_applied_state_change_id, state = snapshot unapplied_state_changes = storage.get_statechanges_by_identifier( from_identifier=last_applied_state_change_id, to_identifier='latest', ) else: state = None unapplied_state_changes = storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) state_manager = StateManager(transition_function, state) wal = WriteAheadLog(state_manager, storage) for state_change in unapplied_state_changes: events.extend(state_manager.dispatch(state_change)) return wal, events
def target_mediated_transfer(self, message): graph = self.channelgraphs[message.token] from_channel = graph.partneraddress_channel[message.sender] from_route = channel_to_routestate(from_channel, message.sender) from_transfer = lockedtransfer_from_message(message) our_address = self.address block_number = self.get_block_number() init_target = ActionInitTarget( our_address, from_route, from_transfer, block_number, ) state_manager = StateManager(target_task.state_transition, None) all_events = state_manager.dispatch(init_target) for event in all_events: self.state_machine_event_handler.on_event(event) identifier = message.identifier self.identifier_statemanager[identifier].append(state_manager)
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 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 test_init_with_usable_routes(): amount = factories.UNIT_TRANSFER_AMOUNT block_number = 1 mediator_address = factories.HOP1 target_address = factories.HOP2 our_address = factories.ADDR secret_generator = SequenceGenerator() routes = [factories.make_route(mediator_address, available_balance=amount)] init_state_change = make_init_statechange( routes, target_address, block_number=block_number, our_address=our_address, secret_generator=secret_generator, ) expiration = block_number + factories.HOP1_TIMEOUT initiator_state_machine = StateManager( initiator.state_transition, None, ) assert initiator_state_machine.current_state is None events = initiator_state_machine.dispatch(init_state_change, ) initiator_state = initiator_state_machine.current_state assert isinstance(initiator_state, InitiatorState) assert initiator_state.our_address == our_address transfer = initiator_state.transfer assert isinstance(transfer, LockedTransferState) assert transfer.amount == amount assert transfer.target == target_address assert transfer.secret == secret_generator.secrets[0] assert transfer.hashlock == sha3(transfer.secret) assert len( events ), 'we have a valid route, the mediated transfer event must be emited' mediated_transfers = [ e for e in events if isinstance(e, SendMediatedTransfer) ] assert len(mediated_transfers ) == 1, 'mediated_transfer should /not/ split the transfer' mediated_transfer = mediated_transfers[0] assert mediated_transfer.token == factories.UNIT_TOKEN_ADDRESS assert mediated_transfer.amount == amount, 'transfer amount mismatch' assert mediated_transfer.expiration == expiration, 'transfer expiration mismatch' assert mediated_transfer.hashlock == sha3( secret_generator.secrets[0]), 'wrong hashlock' assert mediated_transfer.receiver == mediator_address, 'wrong mediator address' assert initiator_state.route == routes[0] assert len(initiator_state.routes.available_routes) == 0 assert len(initiator_state.routes.ignored_routes) == 0 assert len(initiator_state.routes.refunded_routes) == 0 assert len(initiator_state.routes.canceled_routes) == 0
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