def handle_message_refundtransfer(self, raiden: RaidenService, message: RefundTransfer): token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) routes = get_best_routes( chain_state, token_network_address, raiden.address, from_transfer.target, from_transfer.lock.amount, message.sender, ) role = views.get_transfer_role( chain_state, from_transfer.lock.secrethash, ) if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( routes=routes, transfer=from_transfer, secret=secret, ) else: state_change = ReceiveTransferRefund( transfer=from_transfer, routes=routes, ) raiden.handle_state_change(state_change)
def message_refundtransfer(self, message): self.balance_proof(message) graph = self.raiden.token_to_channelgraph[message.token] if not graph.has_channel(self.raiden.address, message.sender): raise UnknownAddress( 'Direct transfer from node without an existing channel: {}'. format(pex(message.sender), )) channel = graph.partneraddress_to_channel[message.sender] channel.register_transfer( self.raiden.get_block_number(), message, ) transfer_state = LockedTransferState( identifier=message.identifier, amount=message.lock.amount, token=message.token, initiator=message.initiator, target=message.target, expiration=message.lock.expiration, hashlock=message.lock.hashlock, secret=None, ) state_change = ReceiveTransferRefund( message.sender, transfer_state, ) self.raiden.state_machine_event_handler.log_and_dispatch_by_identifier( message.identifier, state_change, )
def message_refundtransfer(self, message): self.balance_proof(message) self.raiden.greenlet_task_dispatcher.dispatch_message( message, message.lock.hashlock, ) transfer_state = LockedTransferState( identifier=message.identifier, amount=message.lock.amount, token=message.token, initiator=message.initiator, target=message.target, expiration=message.lock.expiration, hashlock=message.lock.hashlock, secret=None, ) state_change = ReceiveTransferRefund( message.sender, transfer_state, ) self.raiden.state_machine_event_handler.log_and_dispatch_by_identifier( message.identifier, state_change, )
def test_invalid_instantiation_receive_transfer_refund(additional_args): wrong_type_transfer = factories.create(factories.TransferDescriptionProperties()) secret = factories.UNIT_SECRET with pytest.raises(ValueError): ReceiveTransferRefund(transfer=wrong_type_transfer, **additional_args) with pytest.raises(ValueError): ActionTransferReroute(transfer=wrong_type_transfer, secret=secret, **additional_args)
def test_refund_transfer_next_route(): identifier = 1 amount = factories.UNIT_TRANSFER_AMOUNT block_number = 1 mediator_address = factories.HOP1 target_address = factories.HOP2 our_address = factories.ADDR routes = [ factories.make_route(mediator_address, available_balance=amount), factories.make_route(factories.HOP2, available_balance=amount), ] current_state = make_initiator_state( routes, target_address, block_number=block_number, our_address=our_address, secret_generator=SequenceGenerator(), identifier=identifier, ) transfer = factories.make_transfer( amount, our_address, target_address, block_number + factories.UNIT_SETTLE_TIMEOUT, ) state_change = ReceiveTransferRefund( sender=mediator_address, transfer=transfer, ) prior_state = deepcopy(current_state) initiator_state_machine = StateManager( initiator.state_transition, current_state, ) assert initiator_state_machine.current_state is not None events = initiator_state_machine.dispatch(state_change) assert len(events) == 1 assert any( isinstance(e, SendMediatedTransfer) for e in events ), 'No mediated transfer event emitted, should have tried a new route' assert initiator_state_machine.current_state is not None assert initiator_state_machine.current_state.routes.canceled_routes[ 0] == prior_state.route
def handle_message_refundtransfer( raiden: "RaidenService", message: RefundTransfer ) -> List[StateChange]: chain_state = views.state_from_raiden(raiden) from_transfer = lockedtransfersigned_from_message(message=message) role = views.get_transfer_role( chain_state=chain_state, secrethash=from_transfer.lock.secrethash ) state_changes: List[StateChange] = [] if role == "initiator": old_secret = views.get_transfer_secret(chain_state, from_transfer.lock.secrethash) is_secret_known = old_secret is not None and old_secret != ABSENT_SECRET state_changes.append( ReceiveTransferCancelRoute( transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) ) # Currently, the only case where we can be initiators and not # know the secret is if the transfer is part of an atomic swap. In # the case of an atomic swap, we will not try to re-route the # transfer. In all other cases we can try to find another route # (and generate a new secret) if is_secret_known: state_changes.append( ActionTransferReroute( transfer=from_transfer, balance_proof=from_transfer.balance_proof, # pylint: disable=no-member sender=from_transfer.balance_proof.sender, # pylint: disable=no-member secret=random_secret(), ) ) else: state_changes.append( ReceiveTransferRefund( transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) ) return state_changes
def message_refundtransfer(self, message): self.raiden.greenlet_task_dispatcher.dispatch_message( message, message.lock.hashlock, ) if message.identifier in self.raiden.identifier_statemanager: identifier = message.identifier token_address = message.token target = message.target amount = message.lock.amount expiration = message.lock.expiration hashlock = message.lock.hashlock manager = self.raiden.identifier_statemanager[identifier] if isinstance(manager.current_state, InitiatorState): initiator_address = self.raiden.address elif isinstance(manager.current_state, MediatorState): last_pair = manager.current_state.transfers_pair[-1] initiator_address = last_pair.payee_transfer.initiator else: # TODO: emit a proper event for the reject message return transfer_state = LockedTransferState( identifier=identifier, amount=amount, token=token_address, initiator=initiator_address, target=target, expiration=expiration, hashlock=hashlock, secret=None, ) state_change = ReceiveTransferRefund( message.sender, transfer_state, ) self.raiden.state_machine_event_handler.dispatch_by_identifier( message.identifier, state_change, )
def test_refund_transfer_invalid_sender(): identifier = 1 amount = factories.UNIT_TRANSFER_AMOUNT block_number = 1 mediator_address = factories.HOP1 target_address = factories.HOP2 our_address = factories.ADDR routes = [ factories.make_route(mediator_address, available_balance=amount), ] current_state = make_initiator_state( routes, target_address, block_number=block_number, our_address=our_address, secret_generator=SequenceGenerator(), identifier=identifier, ) transfer = factories.make_transfer( amount, our_address, target_address, block_number + factories.UNIT_SETTLE_TIMEOUT, ) state_change = ReceiveTransferRefund( sender=our_address, transfer=transfer, ) prior_state = deepcopy(current_state) initiator_state_machine = StateManager( initiator.state_transition, current_state, ) assert initiator_state_machine.current_state is not None events = initiator_state_machine.dispatch(state_change) assert len(events) == 0 assert initiator_state_machine.current_state is not None assert_state_equal(initiator_state_machine.current_state, prior_state)
def handle_message_refundtransfer(raiden: RaidenService, message: RefundTransfer) -> None: token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) # FIXME: Shouldn't request routes here routes, _ = get_best_routes( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_address), one_to_n_address=raiden.default_one_to_n_address, from_address=InitiatorAddress(raiden.address), to_address=from_transfer.target, amount=PaymentAmount( from_transfer.lock.amount), # FIXME: mypy; deprecated by #3863 previous_address=message.sender, config=raiden.config, privkey=raiden.privkey, ) role = views.get_transfer_role( chain_state=chain_state, secrethash=from_transfer.lock.secrethash) state_change: StateChange if role == "initiator": old_secret = views.get_transfer_secret( chain_state, from_transfer.lock.secrethash) # We currently don't allow multi routes if the initiator does not # hold the secret. In such case we remove all other possible routes # which allow the API call to return with with an error message. if old_secret == EMPTY_SECRET: routes = list() secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( routes=routes, transfer=from_transfer, secret=secret) else: state_change = ReceiveTransferRefund(transfer=from_transfer, routes=routes) raiden.handle_and_track_state_change(state_change)
def handle_message_refundtransfer(raiden: 'RaidenService', message: RefundTransfer): registry_address = message.registry_address from_transfer = lockedtransfersigned_from_message(message) node_state = views.state_from_raiden(raiden) routes = get_best_routes( node_state, registry_address, from_transfer.token, raiden.address, from_transfer.target, from_transfer.lock.amount, message.sender, ) role = views.get_transfer_role( node_state, from_transfer.lock.secrethash, ) if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( registry_address, message.sender, routes, from_transfer, secret, ) else: state_change = ReceiveTransferRefund( message.message_identifier, message.sender, from_transfer, ) raiden.handle_state_change(state_change)
def handle_message_refundtransfer(raiden: RaidenService, message: RefundTransfer): token_network_address = message.token_network_address from_transfer = lockedtransfersigned_from_message(message) chain_state = views.state_from_raiden(raiden) routes = get_best_routes( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_address), from_address=InitiatorAddress(raiden.address), to_address=from_transfer.target, amount=from_transfer.lock.amount, previous_address=message.sender, config=raiden.config, privkey=raiden.privkey, ) role = views.get_transfer_role( chain_state, from_transfer.lock.secrethash, ) state_change: StateChange if role == 'initiator': secret = random_secret() state_change = ReceiveTransferRefundCancelRoute( routes=routes, transfer=from_transfer, secret=secret, ) else: state_change = ReceiveTransferRefund( transfer=from_transfer, routes=routes, ) raiden.handle_and_track_state_change(state_change)
def 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_mediator_skips_used_routes(): prng = random.Random() block_number = 3 defaults = factories.NettingChannelStateProperties( our_state=factories.NettingChannelEndStateProperties.OUR_STATE, partner_state=factories.NettingChannelEndStateProperties( balance=UNIT_TRANSFER_AMOUNT), open_transaction=factories.TransactionExecutionStatusProperties( started_block_number=1, finished_block_number=2, result="success"), ) properties = [ factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( privatekey=factories.HOP1_KEY, address=factories.HOP1)), factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( privatekey=factories.HOP2_KEY, address=factories.HOP2)), factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( privatekey=factories.HOP3_KEY, address=factories.HOP3)), ] channels = factories.make_channel_set(properties=properties, number_of_channels=3, defaults=defaults) bob = channels.channels[1].partner_state.address charlie = channels.channels[2].partner_state.address dave = factories.make_address() eric = factories.make_address() locked_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=10, routes=[ [ factories.UNIT_OUR_ADDRESS, bob, dave, factories.UNIT_TRANSFER_TARGET ], [ factories.UNIT_OUR_ADDRESS, bob, eric, factories.UNIT_TRANSFER_TARGET ], [ factories.UNIT_OUR_ADDRESS, charlie, eric, factories.UNIT_TRANSFER_TARGET ], ], canonical_identifier=channels.channels[0].canonical_identifier, pkey=factories.HOP1_KEY, sender=factories.HOP1, )) init_action = factories.mediator_make_init_action(channels=channels, transfer=locked_transfer) nodeaddresses_to_networkstates = { channel.partner_state.address: NetworkState.REACHABLE for channel in channels.channels } transition_result = mediator.handle_init( state_change=init_action, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=nodeaddresses_to_networkstates, pseudo_random_generator=prng, block_number=block_number, ) mediator_state = transition_result.new_state events = transition_result.events assert mediator_state is not None assert events assert len(mediator_state.routes) == 3 assert mediator_state.routes[0].route[1] == bob assert mediator_state.routes[1].route[1] == bob assert mediator_state.routes[2].route[1] == charlie # now we receive a refund from whoever we forwarded to (should be HOP2) assert isinstance(events[-1], SendLockedTransfer) assert events[-1].recipient == factories.HOP2 last_pair = mediator_state.transfers_pair[-1] canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier lock_expiration = last_pair.payee_transfer.lock.expiration payment_identifier = last_pair.payee_transfer.payment_identifier received_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=lock_expiration, payment_identifier=payment_identifier, canonical_identifier=canonical_identifier, sender=factories.HOP2, pkey=factories.HOP2_KEY, message_identifier=factories.make_message_identifier(), )) refund_state_change = ReceiveTransferRefund( transfer=received_transfer, balance_proof=received_transfer.balance_proof, sender=received_transfer.balance_proof.sender, # pylint: disable=no-member ) transition_result = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=nodeaddresses_to_networkstates, pseudo_random_generator=prng, block_number=block_number, ) mediator_state = transition_result.new_state events = transition_result.events assert mediator_state is not None assert events assert mediator_state.transfers_pair[-1].payee_address == charlie # now we should have a forward transfer to HOP3 assert isinstance(events[-1], SendLockedTransfer) assert events[-1].recipient == factories.HOP3 # now we will receive a refund from HOP3 last_pair = mediator_state.transfers_pair[-1] canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier lock_expiration = last_pair.payee_transfer.lock.expiration payment_identifier = last_pair.payee_transfer.payment_identifier received_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=lock_expiration, payment_identifier=payment_identifier, canonical_identifier=canonical_identifier, sender=factories.HOP3, pkey=factories.HOP3_KEY, message_identifier=factories.make_message_identifier(), )) refund_state_change = ReceiveTransferRefund( transfer=received_transfer, balance_proof=received_transfer.balance_proof, sender=received_transfer.balance_proof.sender, # pylint: disable=no-member ) transition_result = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=nodeaddresses_to_networkstates, pseudo_random_generator=prng, block_number=block_number, ) mediator_state = transition_result.new_state events = transition_result.events assert mediator_state is not None assert events # no other routes available, so refund HOP1 assert isinstance(events[-1], SendRefundTransfer) assert events[-1].recipient == factories.HOP1
def test_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. Also, for issue: https://github.com/raiden-network/raiden/issues/3170 It was noticed that when receiving the same refund transfer twice, the mediator would detect an invalid refund and clear the mediator state. So the test also checks that mediator rejects the duplicate transfer and keeps the mediator state unchanged. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState(secrethash=UNIT_SECRETHASH, routes=[]) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=lock_expiration, payment_identifier=UNIT_TRANSFER_IDENTIFIER, canonical_identifier=canonical_identifier, sender=setup.channels.partner_address(2), pkey=setup.channels.partner_privatekeys[2], message_identifier=factories.make_message_identifier(), )) # All three channels have been used refund_state_change = ReceiveTransferRefund( transfer=received_transfer, balance_proof=received_transfer.balance_proof, sender=received_transfer.balance_proof.sender, # pylint: disable=no-member ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock(end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH) token_network_address = first_payer_transfer.balance_proof.token_network_address assert search_for_item( iteration.events, SendRefundTransfer, { "recipient": setup.channels.partner_address(0), "queue_identifier": { "recipient": setup.channels.partner_address(0), "canonical_identifier": { "chain_identifier": first_payer_transfer.balance_proof.chain_id, "token_network_address": token_network_address, "channel_identifier": first_payer_transfer.balance_proof.channel_identifier, }, }, "transfer": { "payment_identifier": UNIT_TRANSFER_IDENTIFIER, "token": UNIT_TOKEN_ADDRESS, "balance_proof": { "transferred_amount": 0, "locked_amount": UNIT_TRANSFER_AMOUNT, "locksroot": keccak(lock.encoded), "token_network_address": token_network_address, "channel_identifier": first_payer_transfer.balance_proof.channel_identifier, "chain_id": first_payer_transfer.balance_proof.chain_id, }, "lock": { "amount": lock.amount, "expiration": lock.expiration, "secrethash": lock.secrethash, }, "initiator": UNIT_TRANSFER_INITIATOR, "target": UNIT_TRANSFER_TARGET, }, }, ) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert search_for_item(duplicate_iteration.events, SendRefundTransfer, {}) is None assert duplicate_iteration.new_state is not None assert duplicate_iteration.new_state == iteration.new_state
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
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 test_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. Also, for issue: https://github.com/raiden-network/raiden/issues/3170 It was noticed that when receiving the same refund transfer twice, the mediator would detect an invalid refund and clear the mediator state. So the test also checks that mediator rejects the duplicate transfer and keeps the mediator state unchanged. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState( secrethash=UNIT_SECRETHASH, routes=setup.channels.get_routes(), ) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.make_signed_transfer_state( amount=UNIT_TRANSFER_AMOUNT, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, expiration=lock_expiration, secret=UNIT_SECRET, payment_identifier=UNIT_TRANSFER_IDENTIFIER, channel_identifier=channel_identifier, pkey=setup.channels.partner_privatekeys[2], sender=setup.channels.partner_address(2), ) # All three channels have been used routes = [] refund_state_change = ReceiveTransferRefund( transfer=received_transfer, routes=routes, ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock( end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH, ) token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier assert search_for_item( iteration.events, SendRefundTransfer, { 'recipient': setup.channels.partner_address(0), 'queue_identifier': { 'recipient': setup.channels.partner_address(0), 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, }, 'transfer': { 'payment_identifier': UNIT_TRANSFER_IDENTIFIER, 'token': UNIT_TOKEN_ADDRESS, 'balance_proof': { 'transferred_amount': 0, 'locked_amount': 10, 'locksroot': lock.lockhash, 'token_network_identifier': token_network_identifier, 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, 'chain_id': first_payer_transfer.balance_proof.chain_id, }, 'lock': { 'amount': lock.amount, 'expiration': lock.expiration, 'secrethash': lock.secrethash, }, 'initiator': UNIT_TRANSFER_INITIATOR, 'target': UNIT_TRANSFER_TARGET, }, }) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert search_for_item(duplicate_iteration.events, SendRefundTransfer, {}) is None assert duplicate_iteration.new_state is not None assert duplicate_iteration.new_state == iteration.new_state
def test_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState(UNIT_SECRETHASH) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.make_signed_transfer( amount=UNIT_TRANSFER_AMOUNT, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, expiration=lock_expiration, secret=UNIT_SECRET, payment_identifier=UNIT_TRANSFER_IDENTIFIER, channel_identifier=channel_identifier, pkey=setup.channels.partner_privatekeys[2], sender=setup.channels.partner_address(2), ) # All three channels have been used routes = [] refund_state_change = ReceiveTransferRefund( transfer=received_transfer, routes=routes, ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock( end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH, ) token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier assert must_contain_entry( iteration.events, SendRefundTransfer, { 'recipient': setup.channels.partner_address(0), 'queue_identifier': { 'recipient': setup.channels.partner_address(0), 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, }, 'transfer': { 'payment_identifier': UNIT_TRANSFER_IDENTIFIER, 'token': UNIT_TOKEN_ADDRESS, 'balance_proof': { 'transferred_amount': 0, 'locked_amount': 10, 'locksroot': lock.lockhash, 'token_network_identifier': token_network_identifier, 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, 'chain_id': first_payer_transfer.balance_proof.chain_id, }, 'lock': { 'amount': lock.amount, 'expiration': lock.expiration, 'secrethash': lock.secrethash, }, 'initiator': UNIT_TRANSFER_INITIATOR, 'target': UNIT_TRANSFER_TARGET, }, })