def test_invalid_instantiation_action_init_mediator_and_target(additional_args): hop_state = HopState( node_address=factories.make_address(), channel_identifier=factories.make_channel_identifier(), ) route_state = RouteState( route=[factories.make_address()], forward_channel_id=factories.make_channel_identifier() ) not_a_route_state = object() valid_transfer = factories.create(factories.LockedTransferSignedStateProperties()) wrong_type_transfer = factories.create(factories.TransferDescriptionProperties()) with pytest.raises(ValueError): ActionInitMediator( from_transfer=wrong_type_transfer, from_hop=hop_state, route_states=[route_state], **additional_args, ) with pytest.raises(ValueError): ActionInitMediator( from_transfer=valid_transfer, from_hop=not_a_route_state, route_states=[route_state], **additional_args, ) with pytest.raises(ValueError): ActionInitTarget(transfer=wrong_type_transfer, from_hop=hop_state, **additional_args) with pytest.raises(ValueError): ActionInitTarget(transfer=valid_transfer, from_hop=not_a_route_state, **additional_args)
def handle_message_lockedtransfer( raiden: "RaidenService", message: LockedTransfer # pylint: disable=unused-argument ) -> List[StateChange]: secrethash = message.lock.secrethash # We must check if the secret was registered against the latest block, # even if the block is forked away and the transaction that registers # the secret is removed from the blockchain. The rationale here is that # someone else does know the secret, regardless of the chain state, so # the node must not use it to start a payment. # # For this particular case, it's preferable to use `latest` instead of # having a specific block_hash, because it's preferable to know if the secret # was ever known, rather than having a consistent view of the blockchain. registered = raiden.default_secret_registry.is_secret_registered( secrethash=secrethash, block_identifier=BLOCK_ID_LATEST ) if registered: log.warning( f"Ignoring received locked transfer with secrethash {to_hex(secrethash)} " f"since it is already registered in the secret registry" ) return [] assert message.sender, "Invalid message dispatched, it should be signed" from_transfer = lockedtransfersigned_from_message(message) from_hop = HopState( node_address=message.sender, # pylint: disable=E1101 channel_identifier=from_transfer.balance_proof.channel_identifier, ) if message.target == TargetAddress(raiden.address): raiden.immediate_health_check_for(Address(message.initiator)) return [ ActionInitTarget( from_hop=from_hop, transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, ) ] else: route_states = routing.resolve_routes( routes=message.metadata.routes, token_network_address=from_transfer.balance_proof.token_network_address, chain_state=views.state_from_raiden(raiden), ) return [ ActionInitMediator( from_hop=from_hop, route_states=route_states, from_transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, ) ]
def _action_init_mediator( self, transfer: LockedTransferSignedState) -> ActionInitMediator: initiator_channel = self.address_to_channel[transfer.initiator] target_channel = self.address_to_channel[transfer.target] return ActionInitMediator( [factories.make_route_from_channel(target_channel)], factories.make_route_to_channel(initiator_channel), transfer, )
def _action_init_mediator( self, transfer: LockedTransferSignedState) -> ActionInitMediator: initiator_channel = self.address_to_channel[transfer.initiator] target_channel = self.address_to_channel[transfer.target] return ActionInitMediator( route_states=[factories.make_route_from_channel(target_channel)], from_hop=factories.make_hop_to_channel(initiator_channel), from_transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, )
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 _action_init_mediator( self, transfer: LockedTransferSignedState, client_address ) -> WithOurAddress: client = self.address_to_client[client_address] initiator_channel = client.address_to_channel[transfer.initiator] target_channel = client.address_to_channel[transfer.target] action = ActionInitMediator( route_states=[factories.make_route_from_channel(target_channel)], from_hop=factories.make_hop_to_channel(initiator_channel), from_transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, ) return WithOurAddress(our_address=client_address, data=action)
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 mediator_init(raiden, transfer: LockedTransfer) -> ActionInitMediator: from_transfer = lockedtransfersigned_from_message(transfer) # Feedback token not used here, will be removed with source routing routes, _ = routing.get_best_routes( chain_state=views.state_from_raiden(raiden), token_network_id=TokenNetworkID( from_transfer.balance_proof.token_network_identifier), one_to_n_address=raiden.default_one_to_n_address, from_address=raiden.address, to_address=from_transfer.target, amount=PaymentAmount(from_transfer.lock.amount ), # FIXME: mypy; deprecated through #3863 previous_address=transfer.sender, config=raiden.config, privkey=raiden.privkey, ) from_route = RouteState(transfer.sender, from_transfer.balance_proof.channel_identifier) return ActionInitMediator(routes, from_route, from_transfer)
def mediator_init(raiden, transfer: LockedTransfer): from_transfer = lockedtransfersigned_from_message(transfer) routes = routing.get_best_routes( chain_state=views.state_from_raiden(raiden), token_network_id=TokenNetworkID( from_transfer.balance_proof.token_network_identifier), from_address=raiden.address, to_address=from_transfer.target, amount=PaymentAmount(from_transfer.lock.amount ), # FIXME: mypy; deprecated through #3863 previous_address=transfer.sender, config=raiden.config, privkey=raiden.privkey, ) from_route = RouteState(transfer.sender, from_transfer.balance_proof.channel_identifier) init_mediator_statechange = ActionInitMediator(routes, from_route, from_transfer) return init_mediator_statechange
def mediator_init(raiden, transfer: LockedTransfer): from_transfer = lockedtransfersigned_from_message(transfer) routes = routing.get_best_routes( views.state_from_raiden(raiden), from_transfer.balance_proof.token_network_identifier, raiden.address, from_transfer.target, from_transfer.lock.amount, transfer.sender, ) from_route = RouteState( transfer.sender, from_transfer.balance_proof.channel_address, ) init_mediator_statechange = ActionInitMediator( routes, from_route, from_transfer, ) return init_mediator_statechange
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 mediator_init(raiden, transfer): from_transfer = lockedtransfersigned_from_message(transfer) registry_address = raiden.default_registry.address routes = routing.get_best_routes( views.state_from_raiden(raiden), registry_address, from_transfer.token, raiden.address, from_transfer.target, from_transfer.lock.amount, transfer.sender, ) from_route = RouteState( transfer.sender, from_transfer.balance_proof.channel_address, ) init_mediator_statechange = ActionInitMediator( registry_address, routes, from_route, from_transfer, ) return init_mediator_statechange
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = make_channel_set([ NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=0), partner_state=NettingChannelEndStateProperties( balance=10, address=HOP2, privatekey=HOP2_KEY, ), ), ]) payer_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( sender=HOP2, pkey=HOP2_KEY, transfer=factories.LockedTransferProperties(expiration=30), )) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=5, block_hash=factories.make_block_hash(), ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert search_for_item(init_iteration.events, SendLockedTransfer, {}) is None assert search_for_item(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(LocalSigner(channels.partner_privatekeys[0])) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) # Regression: The mediator must still be able to process the block which # expires the lock expired_block_number = channel.get_sender_expiration_threshold(lock) block_hash = factories.make_block_hash() expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=block_hash, ), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=block_hash, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=block_hash, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[ 0].partner_state.secrethashes_to_lockedlocks
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found by when querying the database. """ serializer = JSONSerializer storage = SQLiteStorage(':memory:', serializer) counter = itertools.count() lock_expired = ReceiveLockExpired( balance_proof=make_signed_balance_proof_from_counter(counter), secrethash=sha3(factories.make_secret(next(counter))), message_identifier=next(counter), ) unlock = ReceiveUnlock( message_identifier=next(counter), secret=sha3(factories.make_secret(next(counter))), balance_proof=make_signed_balance_proof_from_counter(counter), ) transfer_refund = ReceiveTransferRefund( transfer=make_signed_transfer_from_counter(counter), routes=list(), ) transfer_refund_cancel_route = ReceiveTransferRefundCancelRoute( routes=list(), transfer=make_signed_transfer_from_counter(counter), secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( routes=list(), from_route=mediator_from_route, from_transfer=mediator_signed_transfer, ) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget( route=target_from_route, transfer=target_signed_transfer, ) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_refund_cancel_route, transfer_refund_cancel_route.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] timestamp = datetime.utcnow().isoformat(timespec='milliseconds') for state_change, _ in statechanges_balanceproofs: storage.write_state_change(state_change, timestamp) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof( storage=storage, chain_id=balance_proof.chain_id, token_network_identifier=balance_proof.token_network_identifier, channel_identifier=balance_proof.channel_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record.data == state_change
def mediator_make_init_action( channels: ChannelSet, transfer: LockedTransferSignedState) -> ActionInitMediator: return ActionInitMediator(channels.get_routes(1), channels.get_route(0), transfer)
def test_mediator_clear_pairs_after_batch_unlock( chain_state, token_network_state, our_address, ): """ Regression test for https://github.com/raiden-network/raiden/issues/2932 The mediator must also clear the transfer pairs once a ReceiveBatchUnlock where he is a participant is received. """ open_block_number = 10 pseudo_random_generator = random.Random() pkey, address = factories.make_privkey_address() amount = 30 our_balance = amount + 50 channel_state = factories.make_channel( our_balance=our_balance, our_address=our_address, partner_balance=our_balance, partner_address=address, token_network_identifier=token_network_state.address, ) payment_network_identifier = factories.make_payment_network_identifier() channel_new_state_change = ContractReceiveChannelNew( factories.make_transaction_hash(), token_network_state.address, channel_state, open_block_number, ) channel_new_iteration = token_network.state_transition( payment_network_identifier, token_network_state, channel_new_state_change, pseudo_random_generator, open_block_number, ) lock_amount = 30 lock_expiration = 20 lock_secret = sha3(b'test_end_state') lock_secrethash = sha3(lock_secret) lock = HashTimeLockState( lock_amount, lock_expiration, lock_secrethash, ) mediated_transfer = make_receive_transfer_mediated( channel_state=channel_state, privkey=pkey, nonce=1, transferred_amount=0, lock=lock, ) from_route = factories.route_from_channel(channel_state) init_mediator = ActionInitMediator( routes=[from_route], from_route=from_route, from_transfer=mediated_transfer, ) node.state_transition(chain_state, init_mediator) closed_block_number = open_block_number + 10 channel_close_state_change = ContractReceiveChannelClosed( factories.make_transaction_hash(), channel_state.partner_state.address, token_network_state.address, channel_state.identifier, closed_block_number, ) channel_closed_iteration = token_network.state_transition( payment_network_identifier, channel_new_iteration.new_state, channel_close_state_change, pseudo_random_generator, closed_block_number, ) settle_block_number = closed_block_number + channel_state.settle_timeout + 1 channel_settled_state_change = ContractReceiveChannelSettled( factories.make_transaction_hash(), token_network_state.address, channel_state.identifier, settle_block_number, ) channel_settled_iteration = token_network.state_transition( payment_network_identifier, channel_closed_iteration.new_state, channel_settled_state_change, pseudo_random_generator, closed_block_number, ) token_network_state_after_settle = channel_settled_iteration.new_state ids_to_channels = token_network_state_after_settle.channelidentifiers_to_channels assert len(ids_to_channels) == 1 assert channel_state.identifier in ids_to_channels block_number = closed_block_number + 1 channel_batch_unlock_state_change = ContractReceiveChannelBatchUnlock( transaction_hash=factories.make_transaction_hash(), token_network_identifier=token_network_state.address, participant=our_address, partner=address, locksroot=lock_secrethash, unlocked_amount=lock_amount, returned_tokens=0, block_number=block_number, ) channel_unlock_iteration = node.state_transition( chain_state=chain_state, state_change=channel_batch_unlock_state_change, ) chain_state = channel_unlock_iteration.new_state token_network_state = views.get_token_network_by_identifier( chain_state=chain_state, token_network_id=token_network_state.address, ) ids_to_channels = token_network_state.channelidentifiers_to_channels assert len(ids_to_channels) == 0 # Make sure that all is fine in the next block block = Block( block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = node.state_transition( chain_state=chain_state, state_change=block, ) assert iteration.new_state # Make sure that mediator task was cleared during the next block processing # since the channel was removed mediator_task = chain_state.payment_mapping.secrethashes_to_task.get( lock_secrethash) assert not mediator_task
def test_mediator_clear_pairs_after_batch_unlock(chain_state, token_network_state, our_address, channel_properties): """ Regression test for https://github.com/raiden-network/raiden/issues/2932 The mediator must also clear the transfer pairs once a ReceiveBatchUnlock where he is a participant is received. """ open_block_number = 10 open_block_hash = factories.make_block_hash() properties, pkey = channel_properties address = properties.partner_state.address channel_state = factories.create(properties) channel_new_state_change = ContractReceiveChannelNew( transaction_hash=factories.make_transaction_hash(), channel_state=channel_state, block_number=open_block_number, block_hash=open_block_hash, ) channel_new_iteration = token_network.state_transition( token_network_state=token_network_state, state_change=channel_new_state_change, block_number=open_block_number, block_hash=open_block_hash, ) lock_amount = 30 lock_expiration = 20 lock_secret = sha3(b"test_end_state") lock_secrethash = sha3(lock_secret) lock = HashTimeLockState(lock_amount, lock_expiration, lock_secrethash) mediated_transfer = make_receive_transfer_mediated( channel_state=channel_state, privkey=pkey, nonce=1, transferred_amount=0, lock=lock) from_route = factories.make_route_from_channel(channel_state) init_mediator = ActionInitMediator(routes=[from_route], from_route=from_route, from_transfer=mediated_transfer) node.state_transition(chain_state, init_mediator, None) closed_block_number = open_block_number + 10 closed_block_hash = factories.make_block_hash() channel_close_state_change = ContractReceiveChannelClosed( transaction_hash=factories.make_transaction_hash(), transaction_from=channel_state.partner_state.address, canonical_identifier=channel_state.canonical_identifier, block_number=closed_block_number, block_hash=closed_block_hash, ) channel_closed_iteration = token_network.state_transition( token_network_state=channel_new_iteration.new_state, state_change=channel_close_state_change, block_number=closed_block_number, block_hash=closed_block_hash, ) settle_block_number = closed_block_number + channel_state.settle_timeout + 1 channel_settled_state_change = ContractReceiveChannelSettled( transaction_hash=factories.make_transaction_hash(), canonical_identifier=channel_state.canonical_identifier, block_number=settle_block_number, block_hash=factories.make_block_hash(), our_onchain_locksroot=factories.make_32bytes(), partner_onchain_locksroot=EMPTY_MERKLE_ROOT, ) channel_settled_iteration = token_network.state_transition( token_network_state=channel_closed_iteration.new_state, state_change=channel_settled_state_change, block_number=closed_block_number, block_hash=closed_block_hash, ) token_network_state_after_settle = channel_settled_iteration.new_state ids_to_channels = token_network_state_after_settle.channelidentifiers_to_channels assert len(ids_to_channels) == 1 assert channel_state.identifier in ids_to_channels block_number = closed_block_number + 1 channel_batch_unlock_state_change = ContractReceiveChannelBatchUnlock( transaction_hash=factories.make_transaction_hash(), canonical_identifier=channel_state.canonical_identifier, participant=address, partner=our_address, locksroot=lock_secrethash, unlocked_amount=lock_amount, returned_tokens=0, block_number=block_number, block_hash=factories.make_block_hash(), ) channel_unlock_iteration = node.state_transition( chain_state=chain_state, state_change=channel_batch_unlock_state_change, storage=None) chain_state = channel_unlock_iteration.new_state token_network_state = views.get_token_network_by_identifier( chain_state=chain_state, token_network_id=token_network_state.address) ids_to_channels = token_network_state.channelidentifiers_to_channels assert len(ids_to_channels) == 0 # Make sure that all is fine in the next block block = Block(block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash()) iteration = node.state_transition(chain_state=chain_state, state_change=block, storage=None) assert iteration.new_state # Make sure that mediator task was cleared during the next block processing # since the channel was removed mediator_task = chain_state.payment_mapping.secrethashes_to_task.get( lock_secrethash) assert not mediator_task
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = factories.make_channel_set([ { 'our_state': { 'balance': 0 }, 'partner_state': { 'balance': 10, 'address': HOP2 }, 'open_transaction': factories.make_transaction_execution_status( finished_block_number=10, ), }, ]) payer_transfer = factories.make_default_signed_transfer_for( channels[0], initiator=HOP1, expiration=30, pkey=HOP2_KEY, sender=HOP2, ) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=5, ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert must_contain_entry(init_iteration.events, SendLockedTransfer, {}) is None assert must_contain_entry(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(HOP2_KEY) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # Regression: The mediator must still be able to process the block which # expires the lock expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=None, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[ 0].partner_state.secrethashes_to_lockedlocks
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found when querying the database. """ serializer = JSONSerializer() storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count() balance_proof = make_signed_balance_proof_from_counter(counter) lock_expired = ReceiveLockExpired( sender=balance_proof.sender, balance_proof=balance_proof, secrethash=factories.make_secret_hash(next(counter)), message_identifier=MessageID(next(counter)), ) received_balance_proof = make_signed_balance_proof_from_counter(counter) unlock = ReceiveUnlock( sender=received_balance_proof.sender, message_identifier=MessageID(next(counter)), secret=factories.make_secret(next(counter)), balance_proof=received_balance_proof, ) transfer = make_signed_transfer_from_counter(counter) transfer_refund = ReceiveTransferRefund( transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, # pylint: disable=no-member ) transfer = make_signed_transfer_from_counter(counter) transfer_reroute = ActionTransferReroute( transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, # pylint: disable=no-member secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( route_states=[ RouteState( route=[factories.make_address(), factories.make_address()], forward_channel_id=factories.make_channel_identifier(), ) ], from_hop=mediator_from_route, from_transfer=mediator_signed_transfer, balance_proof=mediator_signed_transfer.balance_proof, sender=mediator_signed_transfer.balance_proof.sender, # pylint: disable=no-member ) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget( from_hop=target_from_route, transfer=target_signed_transfer, balance_proof=target_signed_transfer.balance_proof, sender=target_signed_transfer.balance_proof.sender, # pylint: disable=no-member ) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_reroute, transfer_reroute.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] assert storage.count_state_changes() == 0 state_change_ids = storage.write_state_changes( [state_change for state_change, _ in statechanges_balanceproofs]) assert storage.count_state_changes() == len(statechanges_balanceproofs) msg_in_order = "Querying must return state changes in order" stored_statechanges_records = storage.get_statechanges_records_by_range( RANGE_ALL_STATE_CHANGES) assert len(stored_statechanges_records) == 6, msg_in_order pair_elements = zip(statechanges_balanceproofs, state_change_ids, stored_statechanges_records) for statechange_balanceproof, statechange_id, record in pair_elements: assert record.data == statechange_balanceproof[0], msg_in_order assert record.state_change_identifier == statechange_id, msg_in_order # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_range( Range( stored_statechanges_records[1].state_change_identifier, stored_statechanges_records[2].state_change_identifier, )) assert len(stored_statechanges) == 2 assert isinstance(stored_statechanges[0], ReceiveUnlock) assert isinstance(stored_statechanges[1], ReceiveTransferRefund) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record assert state_change_record.data == state_change state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, locksroot=balance_proof.locksroot, ) assert state_change_record assert state_change_record.data == state_change storage.close()
def test_regression_mediator_not_update_payer_state_twice(): """ Regression Test for https://github.com/raiden-network/raiden/issues/3086 Make sure that after a lock expired the mediator doesn't update the pair twice causing EventUnlockClaimFailed to be generated at every block. """ amount = 10 block_number = 5 initiator = HOP1 initiator_key = HOP1_KEY mediator_address = HOP2 target = HOP3 expiration = 30 pseudo_random_generator = random.Random() payer_channel = factories.make_channel( partner_balance=amount, our_balance=amount, our_address=mediator_address, partner_address=initiator, token_address=UNIT_TOKEN_ADDRESS, ) payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( channel_state=payer_channel, amount=amount, initiator=initiator, target=target, expiration=expiration, secret=UNIT_SECRET, sender=initiator, pkey=initiator_key, ) payee_channel = factories.make_channel( our_balance=amount, our_address=mediator_address, partner_address=target, token_address=UNIT_TOKEN_ADDRESS, ) available_routes = [factories.route_from_channel(payee_channel)] channel_map = { payee_channel.identifier: payee_channel, payer_channel.identifier: payer_channel, } init_state_change = ActionInitMediator( routes=available_routes, from_route=payer_route, from_transfer=payer_transfer, ) initial_state = None iteration = mediator.state_transition( mediator_state=initial_state, state_change=init_state_change, channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert iteration.new_state is not None current_state = iteration.new_state send_transfer = must_contain_entry(iteration.events, SendLockedTransfer, {}) assert send_transfer transfer = send_transfer.transfer block_expiration_number = transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 block = Block( block_number=block_expiration_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = mediator.state_transition( mediator_state=current_state, state_change=block, channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=block_expiration_number, ) msg = 'At the expiration block we should get an EventUnlockClaimFailed' assert must_contain_entry(iteration.events, EventUnlockClaimFailed, {}), msg current_state = iteration.new_state next_block = Block( block_number=block_expiration_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) # Initiator receives the secret reveal after the lock expired receive_secret = ReceiveSecretReveal( secret=UNIT_SECRET, sender=payee_channel.partner_state.address, ) iteration = mediator.state_transition( mediator_state=current_state, state_change=receive_secret, channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=next_block.block_number, ) current_state = iteration.new_state lock = payer_transfer.lock secrethash = lock.secrethash assert secrethash in payer_channel.partner_state.secrethashes_to_lockedlocks assert current_state.transfers_pair[0].payee_state == 'payee_expired' assert not channel.is_secret_known(payer_channel.partner_state, secrethash) safe_to_wait, _ = mediator.is_safe_to_wait( lock_expiration=lock.expiration, reveal_timeout=payer_channel.reveal_timeout, block_number=lock.expiration + 10, ) assert not safe_to_wait iteration = mediator.state_transition( mediator_state=current_state, state_change=next_block, channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=block_expiration_number, ) msg = 'At the next block we should not get the same event' assert not must_contain_entry(iteration.events, EventUnlockClaimFailed, {}), msg
def test_payer_enter_danger_zone_with_transfer_payed(): """ A mediator may have paid the next hop (payee), and didn't get paid by the previous hop (payer). When this happens, an assertion must not be hit, because it means the transfer must be unlocked on-chain. Issue: https://github.com/raiden-network/raiden/issues/1013 """ amount = 10 block_number = 5 target = HOP2 expiration = 30 pseudo_random_generator = random.Random() payer_channel = factories.make_channel( partner_balance=amount, partner_address=UNIT_TRANSFER_SENDER, token_address=UNIT_TOKEN_ADDRESS, ) payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( payer_channel, amount, HOP1, target, expiration, UNIT_SECRET, ) channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, ) channel_map = { channel1.identifier: channel1, payer_channel.identifier: payer_channel, } possible_routes = [factories.route_from_channel(channel1)] init_state_change = ActionInitMediator( possible_routes, payer_route, payer_transfer, ) initial_state = None initial_iteration = mediator.state_transition( initial_state, init_state_change, channel_map, pseudo_random_generator, block_number, ) send_transfer = must_contain_entry(initial_iteration.events, SendLockedTransfer, {}) assert send_transfer lock_expiration = send_transfer.transfer.lock.expiration new_state = initial_iteration.new_state for block_number in range(block_number, lock_expiration - channel1.reveal_timeout): block_state_change = Block( block_number=block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) block_iteration = mediator.handle_block( new_state, block_state_change, channel_map, pseudo_random_generator, ) new_state = block_iteration.new_state # send the balance proof, transitioning the payee state to paid assert new_state.transfers_pair[0].payee_state == 'payee_pending' receive_secret = ReceiveSecretReveal( UNIT_SECRET, channel1.partner_state.address, ) paid_iteration = mediator.state_transition( new_state, receive_secret, channel_map, pseudo_random_generator, block_number, ) paid_state = paid_iteration.new_state assert paid_state.transfers_pair[0].payee_state == 'payee_balance_proof' # move to the block in which the payee lock expires. This must not raise an # assertion expired_block_number = lock_expiration + 1 expired_block_state_change = Block( block_number=expired_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) block_iteration = mediator.handle_block( paid_state, expired_block_state_change, channel_map, pseudo_random_generator, )
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ amount = 10 block_number = 5 target = HOP2 expiration = 30 pseudo_random_generator = random.Random() payer_channel = factories.make_channel( partner_balance=amount, partner_address=HOP2, token_address=UNIT_TOKEN_ADDRESS, ) payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( payer_channel, amount, HOP1, target, expiration, UNIT_SECRET, pkey=HOP2_KEY, sender=HOP2, ) available_routes = [] channel_map = { payer_channel.identifier: payer_channel, } init_state_change = ActionInitMediator( available_routes, payer_route, payer_transfer, ) initial_state = None init_iteration = mediator.state_transition( initial_state, init_state_change, channel_map, pseudo_random_generator, block_number, ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert must_contain_entry(init_iteration.events, SendLockedTransfer, {}) is None assert must_contain_entry(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=payer_channel.partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=payer_channel.chain_id, token_network_identifier=payer_channel.token_network_identifier, channel_identifier=payer_channel.identifier, recipient=payer_channel.our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(HOP2_KEY) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # Regression: The mediator must still be able to process the block which # expires the lock expire_block_iteration = mediator.state_transition( init_iteration.new_state, Block( block_number=expired_block_number, gas_limit=0, block_hash=None, ), channel_map, pseudo_random_generator, expired_block_number, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( expire_block_iteration.new_state, ReceiveLockExpired( sender=payer_channel.partner_state.address, balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channel_map, pseudo_random_generator, expired_block_number, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in payer_channel.partner_state.secrethashes_to_lockedlocks
def test_mediator_clear_pairs_after_batch_unlock(chain_state, token_network_state, our_address, channel_properties): """ Regression test for https://github.com/raiden-network/raiden/issues/2932 The mediator must also clear the transfer pairs once a ReceiveBatchUnlock where he is a participant is received. """ open_block_number = 10 open_block_hash = factories.make_block_hash() pseudo_random_generator = random.Random() properties, pkey = channel_properties address = properties.partner_state.address channel_state = factories.create(properties) channel_new_state_change = ContractReceiveChannelNew( transaction_hash=factories.make_transaction_hash(), channel_state=channel_state, block_number=open_block_number, block_hash=open_block_hash, ) channel_new_iteration = token_network.state_transition( token_network_state=token_network_state, state_change=channel_new_state_change, block_number=open_block_number, block_hash=open_block_hash, pseudo_random_generator=pseudo_random_generator, ) lock_amount = 30 lock_expiration = 20 lock_secret = keccak(b"test_end_state") lock_secrethash = sha256(lock_secret).digest() lock = HashTimeLockState(lock_amount, lock_expiration, lock_secrethash) mediated_transfer = make_receive_transfer_mediated( channel_state=channel_state, privkey=pkey, nonce=1, transferred_amount=0, lock=lock) route_state = RouteState( route=[ channel_state.our_state.address, channel_state.partner_state.address ], forward_channel_id=channel_state.canonical_identifier. channel_identifier, ) from_hop = factories.make_hop_from_channel(channel_state) init_mediator = ActionInitMediator( route_states=[route_state], from_hop=from_hop, from_transfer=mediated_transfer, balance_proof=mediated_transfer.balance_proof, sender=mediated_transfer.balance_proof.sender, # pylint: disable=no-member ) node.state_transition(chain_state, init_mediator) closed_block_number = open_block_number + 10 closed_block_hash = factories.make_block_hash() channel_close_state_change = ContractReceiveChannelClosed( transaction_hash=factories.make_transaction_hash(), transaction_from=channel_state.partner_state.address, canonical_identifier=channel_state.canonical_identifier, block_number=closed_block_number, block_hash=closed_block_hash, ) channel_closed_iteration = token_network.state_transition( token_network_state=channel_new_iteration.new_state, state_change=channel_close_state_change, block_number=closed_block_number, block_hash=closed_block_hash, pseudo_random_generator=pseudo_random_generator, ) settle_block_number = closed_block_number + channel_state.settle_timeout + 1 channel_settled_state_change = ContractReceiveChannelSettled( transaction_hash=factories.make_transaction_hash(), canonical_identifier=channel_state.canonical_identifier, block_number=settle_block_number, block_hash=factories.make_block_hash(), our_onchain_locksroot=factories.make_32bytes(), partner_onchain_locksroot=LOCKSROOT_OF_NO_LOCKS, ) channel_settled_iteration = token_network.state_transition( token_network_state=channel_closed_iteration.new_state, state_change=channel_settled_state_change, block_number=closed_block_number, block_hash=closed_block_hash, pseudo_random_generator=pseudo_random_generator, ) token_network_state_after_settle = channel_settled_iteration.new_state ids_to_channels = token_network_state_after_settle.channelidentifiers_to_channels assert len(ids_to_channels) == 1 assert channel_state.identifier in ids_to_channels block_number = closed_block_number + 1 channel_batch_unlock_state_change = ContractReceiveChannelBatchUnlock( transaction_hash=factories.make_transaction_hash(), canonical_identifier=channel_state.canonical_identifier, receiver=address, sender=our_address, locksroot=compute_locksroot(PendingLocksState([bytes(lock.encoded)])), unlocked_amount=lock_amount, returned_tokens=0, block_number=block_number, block_hash=factories.make_block_hash(), ) channel_unlock_iteration = node.state_transition( chain_state=chain_state, state_change=channel_batch_unlock_state_change) chain_state = channel_unlock_iteration.new_state token_network_state = views.get_token_network_by_address( chain_state=chain_state, token_network_address=token_network_state.address) ids_to_channels = token_network_state.channelidentifiers_to_channels assert len(ids_to_channels) == 0 # Make sure that all is fine in the next block block = Block(block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash()) iteration = node.state_transition(chain_state=chain_state, state_change=block) assert iteration.new_state # Make sure that mediator task was cleared during the next block processing # since the channel was removed mediator_task = chain_state.payment_mapping.secrethashes_to_task.get( lock_secrethash) assert not mediator_task
def test_regression_mediator_not_update_payer_state_twice(): """ Regression Test for https://github.com/raiden-network/raiden/issues/3086 Make sure that after a lock expired the mediator doesn't update the pair twice causing EventUnlockClaimFailed to be generated at every block. """ pseudo_random_generator = random.Random() pair = factories.mediator_make_channel_pair() payer_channel, payee_channel = pair.channels payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( payer_channel, LONG_EXPIRATION) available_routes = [factories.route_from_channel(payee_channel)] init_state_change = ActionInitMediator( routes=available_routes, from_route=payer_route, from_transfer=payer_transfer, ) iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=5, block_hash=factories.make_block_hash(), ) assert iteration.new_state is not None current_state = iteration.new_state send_transfer = search_for_item(iteration.events, SendLockedTransfer, {}) assert send_transfer transfer = send_transfer.transfer block_expiration_number = channel.get_sender_expiration_threshold( transfer.lock) block = Block( block_number=block_expiration_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = mediator.state_transition( mediator_state=current_state, state_change=block, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_expiration_number, block_hash=factories.make_block_hash(), ) msg = 'At the expiration block we should get an EventUnlockClaimFailed' assert search_for_item(iteration.events, EventUnlockClaimFailed, {}), msg current_state = iteration.new_state next_block = Block( block_number=block_expiration_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) # Initiator receives the secret reveal after the lock expired receive_secret = ReceiveSecretReveal( secret=UNIT_SECRET, sender=payee_channel.partner_state.address, ) iteration = mediator.state_transition( mediator_state=current_state, state_change=receive_secret, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=next_block.block_number, block_hash=next_block.block_hash, ) current_state = iteration.new_state lock = payer_transfer.lock secrethash = lock.secrethash assert secrethash in payer_channel.partner_state.secrethashes_to_lockedlocks assert current_state.transfers_pair[0].payee_state == 'payee_expired' assert not channel.is_secret_known(payer_channel.partner_state, secrethash) safe_to_wait, _ = mediator.is_safe_to_wait( lock_expiration=lock.expiration, reveal_timeout=payer_channel.reveal_timeout, block_number=lock.expiration + 10, ) assert not safe_to_wait iteration = mediator.state_transition( mediator_state=current_state, state_change=next_block, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_expiration_number, block_hash=factories.make_block_hash(), ) msg = 'At the next block we should not get the same event' assert not search_for_item(iteration.events, EventUnlockClaimFailed, {}), msg
def test_regression_mediator_send_lock_expired_with_new_block(): """ The mediator must send the lock expired, but it must **not** clear itself if it has not **received** the corresponding message. """ amount = 10 block_number = 5 target = HOP2 expiration = 30 pseudo_random_generator = random.Random() payer_channel = factories.make_channel( partner_balance=amount, partner_address=UNIT_TRANSFER_SENDER, token_address=UNIT_TOKEN_ADDRESS, ) payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( payer_channel, amount, HOP1, target, expiration, UNIT_SECRET, ) channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, ) available_routes = [factories.route_from_channel(channel1)] channel_map = { channel1.identifier: channel1, payer_channel.identifier: payer_channel, } init_state_change = ActionInitMediator( available_routes, payer_route, payer_transfer, ) initial_state = None init_iteration = mediator.state_transition( initial_state, init_state_change, channel_map, pseudo_random_generator, block_number, ) assert init_iteration.new_state is not None send_transfer = must_contain_entry(init_iteration.events, SendLockedTransfer, {}) assert send_transfer transfer = send_transfer.transfer block_expiration_number = transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 block = Block( block_number=block_expiration_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = mediator.state_transition( init_iteration.new_state, block, channel_map, pseudo_random_generator, block_expiration_number, ) msg = ("The payer's lock has also expired, " "but it must not be removed locally (without an Expired lock)") assert transfer.lock.secrethash in payer_channel.partner_state.secrethashes_to_lockedlocks, msg msg = 'The payer has not yet sent an expired lock, the task can not be cleared yet' assert iteration.new_state is not None, msg assert must_contain_entry(iteration.events, SendLockExpired, { 'secrethash': transfer.lock.secrethash, }) assert transfer.lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found by when querying the database. """ serializer = JSONSerializer storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count() lock_expired = ReceiveLockExpired( balance_proof=make_signed_balance_proof_from_counter(counter), secrethash=sha3(factories.make_secret(next(counter))), message_identifier=next(counter), ) unlock = ReceiveUnlock( message_identifier=next(counter), secret=sha3(factories.make_secret(next(counter))), balance_proof=make_signed_balance_proof_from_counter(counter), ) transfer_refund = ReceiveTransferRefund( transfer=make_signed_transfer_from_counter(counter), routes=list()) transfer_refund_cancel_route = ReceiveTransferRefundCancelRoute( routes=list(), transfer=make_signed_transfer_from_counter(counter), secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( routes=list(), from_route=mediator_from_route, from_transfer=mediator_signed_transfer) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget(route=target_from_route, transfer=target_signed_transfer) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_refund_cancel_route, transfer_refund_cancel_route.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] timestamp = datetime.utcnow().isoformat(timespec="milliseconds") for state_change, _ in statechanges_balanceproofs: storage.write_state_change(state_change, timestamp) # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_identifier(1, "latest") assert isinstance(stored_statechanges[0], ReceiveLockExpired) assert isinstance(stored_statechanges[1], ReceiveUnlock) assert isinstance(stored_statechanges[2], ReceiveTransferRefund) assert isinstance(stored_statechanges[3], ReceiveTransferRefundCancelRoute) assert isinstance(stored_statechanges[4], ActionInitMediator) assert isinstance(stored_statechanges[5], ActionInitTarget) # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_identifier(1, 2) assert isinstance(stored_statechanges[0], ReceiveLockExpired) assert isinstance(stored_statechanges[1], ReceiveUnlock) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record.data == state_change