def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = 11 payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=1, locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash initiator = payee_transfer.initiator initiator_channel = factories.create( factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( address=initiator, balance=100))) routes = [factories.make_route_from_channel(initiator_channel)] transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=routes) transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1, ) secret2 = factories.make_secret(2) locked_amount2 = 13 transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=2, locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=routes) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2, ) payment_mapping = {secrethash1: task1, secrethash2: task2} view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = TokenAmount(11) payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=PaymentID(1), locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash route_state = RouteState(route=[payee_transfer.target]) transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=[route_state]) # pylint: disable=E1101 transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1) secret2 = factories.make_secret(2) locked_amount2 = TokenAmount(13) transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=PaymentID(2), locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=[route_state]) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2) payment_mapping = { secrethash1: cast(TransferTask, task1), secrethash2: cast(TransferTask, task2), } view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
def handle_init(state_change, channelidentifiers_to_channels, block_number): routes = state_change.routes from_route = state_change.from_route from_transfer = state_change.from_transfer payer_channel = channelidentifiers_to_channels.get( from_route.channel_identifier) # There is no corresponding channel for the message, ignore it if not payer_channel: return TransitionResult(None, []) mediator_state = MediatorTransferState(from_transfer.lock.secrethash) is_valid, _ = channel.handle_receive_lockedtransfer( payer_channel, from_transfer, ) if not is_valid: return TransitionResult(None, []) iteration = mediate_transfer( mediator_state, routes, payer_channel, channelidentifiers_to_channels, from_transfer, block_number, ) return iteration
def set_onchain_secret( state: MediatorTransferState, channelidentifiers_to_channels: typing.ChannelMap, secret: typing.Secret, secrethash: typing.SecretHash, block_number: typing.BlockNumber, ) -> typing.List[Event]: """ Set the secret to all mediated transfers. The secret should have been learned from the secret registry. """ state.secret = secret for pair in state.transfers_pair: payer_channel = channelidentifiers_to_channels[ pair.payer_transfer.balance_proof.channel_identifier ] channel.register_onchain_secret( payer_channel, secret, secrethash, block_number, ) payee_channel = channelidentifiers_to_channels[ pair.payee_transfer.balance_proof.channel_identifier ] channel.register_onchain_secret( channel_state=payee_channel, secret=secret, secrethash=secrethash, secret_reveal_block_number=block_number, ) # Like the off-chain secret reveal, the secret should never be revealed # on-chain if there is a waiting transfer. if state.waiting_transfer: payer_channel = channelidentifiers_to_channels[ state.waiting_transfer.transfer.balance_proof.channel_identifier ] channel.register_onchain_secret( channel_state=payer_channel, secret=secret, secrethash=secrethash, secret_reveal_block_number=block_number, ) unexpected_reveal = EventUnexpectedSecretReveal( secrethash=secrethash, reason='The mediator has a waiting transfer.', ) return [unexpected_reveal] return list()
def set_offchain_secret( state: MediatorTransferState, channelidentifiers_to_channels: typing.ChannelMap, secret: typing.Secret, secrethash: typing.SecretHash, ) -> typing.List[Event]: """ Set the secret to all mediated transfers. """ state.secret = secret for pair in state.transfers_pair: payer_channel = channelidentifiers_to_channels[ pair.payer_transfer.balance_proof.channel_identifier ] channel.register_offchain_secret( payer_channel, secret, secrethash, ) payee_channel = channelidentifiers_to_channels[ pair.payee_transfer.balance_proof.channel_identifier ] channel.register_offchain_secret( payee_channel, secret, secrethash, ) # The secret should never be revealed if `waiting_transfer` is not None. # For this to happen this node must have received a transfer, which it did # *not* mediate, and neverthless the secret was revealed. # # This can only be possible if the initiator reveals the secret without the # target's secret request, or if the node which sent the `waiting_transfer` # has sent another transfer which reached the target (meaning someone along # the path will lose tokens). if state.waiting_transfer: payer_channel = channelidentifiers_to_channels[ state.waiting_transfer.transfer.balance_proof.channel_identifier ] channel.register_offchain_secret( payer_channel, secret, secrethash, ) unexpected_reveal = EventUnexpectedSecretReveal( secrethash=secrethash, reason='The mediator has a waiting transfer.', ) return [unexpected_reveal] return list()
def test_handle_node_change_network_state(chain_state, netting_channel_state, monkeypatch): state_change = ActionChangeNodeNetworkState( node_address=factories.make_address(), network_state=NetworkState.REACHABLE) transition_result = handle_action_change_node_network_state( chain_state, state_change) # no events if no mediator tasks are there to apply to assert not transition_result.events mediator_state = MediatorTransferState( secrethash=UNIT_SECRETHASH, routes=[ RouteState( route=[netting_channel_state.partner_state.address], forward_channel_id=netting_channel_state.canonical_identifier. channel_identifier, ) ], ) subtask = MediatorTask( token_network_address=netting_channel_state.canonical_identifier. token_network_address, mediator_state=mediator_state, ) chain_state.payment_mapping.secrethashes_to_task[UNIT_SECRETHASH] = subtask lock = factories.HashTimeLockState(amount=0, expiration=2, secrethash=UNIT_SECRETHASH) netting_channel_state.partner_state.secrethashes_to_lockedlocks[ UNIT_SECRETHASH] = lock netting_channel_state.partner_state.pending_locks = PendingLocksState( [bytes(lock.encoded)]) result = object() monkeypatch.setattr( raiden.transfer.node, "subdispatch_mediatortask", lambda *args, **kwargs: TransitionResult(chain_state, [result]), ) transition_result = handle_action_change_node_network_state( chain_state, state_change) assert transition_result.events == [result]
def handle_init( state_change: ActionInitMediator, channelidentifiers_to_channels: typing.ChannelMap, pseudo_random_generator: random.Random, block_number: typing.BlockNumber, ): routes = state_change.routes from_route = state_change.from_route from_transfer = state_change.from_transfer payer_channel = channelidentifiers_to_channels.get( from_route.channel_identifier) # There is no corresponding channel for the message, ignore it if not payer_channel: return TransitionResult(None, []) mediator_state = MediatorTransferState(from_transfer.lock.secrethash) is_valid, events, _ = channel.handle_receive_lockedtransfer( payer_channel, from_transfer, ) if not is_valid: # If the balance proof is not valid, do *not* create a task. Otherwise it's # possible for an attacker to send multiple invalid transfers, and increase # the memory usage of this Node. return TransitionResult(None, events) iteration = mediate_transfer( mediator_state, routes, payer_channel, channelidentifiers_to_channels, pseudo_random_generator, from_transfer, block_number, ) events.extend(iteration.events) return TransitionResult(iteration.new_state, events)
def handle_init( state_change, channelidentifiers_to_channels, pseudo_random_generator, block_number, ): routes = state_change.routes from_route = state_change.from_route from_transfer = state_change.from_transfer payer_channel = channelidentifiers_to_channels.get( from_route.channel_identifier) # There is no corresponding channel for the message, ignore it if not payer_channel: return TransitionResult(None, []) mediator_state = MediatorTransferState(from_transfer.lock.secrethash) is_valid, events, _ = channel.handle_receive_lockedtransfer( payer_channel, from_transfer, ) if not is_valid: return TransitionResult(None, events) iteration = mediate_transfer( state_change.payment_network_identifier, mediator_state, routes, payer_channel, channelidentifiers_to_channels, pseudo_random_generator, from_transfer, block_number, ) events.extend(iteration.events) return TransitionResult(iteration.new_state, events)
def test_regression_onchain_secret_reveal_must_update_channel_state(): """ If a secret is learned off-chain and then on-chain, the state of the lock must be updated in the channel. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(2, block_number=10) mediator_state = MediatorTransferState( secrethash=UNIT_SECRETHASH, routes=setup.channels.get_routes(), ) mediator_state.transfers_pair = setup.transfers_pair secret = UNIT_SECRET secrethash = UNIT_SECRETHASH payer_channel = mediator.get_payer_channel(setup.channel_map, setup.transfers_pair[0]) payee_channel = mediator.get_payee_channel(setup.channel_map, setup.transfers_pair[0]) lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveSecretReveal(secret, payee_channel.partner_state.address), 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, block_hash=setup.block_hash, ) assert secrethash in payer_channel.partner_state.secrethashes_to_unlockedlocks secret_registry_address = factories.make_address() transaction_hash = factories.make_address() mediator.state_transition( mediator_state=mediator_state, state_change=ContractReceiveSecretReveal( transaction_hash=transaction_hash, secret_registry_address=secret_registry_address, secrethash=secrethash, secret=secret, block_number=setup.block_number, block_hash=setup.block_hash, ), 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, block_hash=setup.block_hash, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks # 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 expired_message = message_from_sendevent(send_lock_expired, setup.channels.our_address(0)) expired_message.sign(LocalSigner(setup.channels.partner_privatekeys[0])) balance_proof = balanceproof_from_envelope(expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = channel.get_sender_expiration_threshold(lock) mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=factories.make_block_hash(), ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks
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_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_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, ) channelmap = { channel1.identifier: channel1, payer_channel.identifier: payer_channel, } possible_routes = [factories.route_from_channel(channel1)] mediator_state = MediatorTransferState(UNIT_SECRETHASH) initial_iteration = mediator.mediate_transfer( mediator_state, possible_routes, payer_channel, channelmap, pseudo_random_generator, payer_transfer, 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 + 1): block_state_change = Block(block_number) block_iteration = mediator.handle_block( channelmap, new_state, block_state_change, block_number, ) 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, channelmap, 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(expired_block_number) block_iteration = mediator.handle_block( channelmap, paid_state, expired_block_state_change, expired_block_number, )
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, }, })
def test_regression_onchain_secret_reveal_must_update_channel_state(): """ If a secret is learned off-chain and then on-chain, the state of the lock must be updated in the channel. """ amount = 10 block_number = 10 pseudo_random_generator = random.Random() channel_map, transfers_pair = factories.make_transfers_pair( [HOP2_KEY, HOP3_KEY], amount, block_number, ) mediator_state = MediatorTransferState(UNIT_SECRETHASH) mediator_state.transfers_pair = transfers_pair secret = UNIT_SECRET secrethash = UNIT_SECRETHASH payer_channelid = transfers_pair[ 0].payer_transfer.balance_proof.channel_identifier payee_channelid = transfers_pair[ 0].payee_transfer.balance_proof.channel_identifier payer_channel = channel_map[payer_channelid] payee_channel = channel_map[payee_channelid] lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveSecretReveal(secret, payee_channel.partner_state.address), channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_unlockedlocks secret_registry_address = factories.make_address() transaction_hash = factories.make_address() mediator.state_transition( mediator_state=mediator_state, state_change=ContractReceiveSecretReveal( transaction_hash, secret_registry_address, secrethash, secret, block_number, ), channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks # 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 mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks
def test_regression_onchain_secret_reveal_must_update_channel_state(): """ If a secret is learned off-chain and then on-chain, the state of the lock must be updated in the channel. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(2, block_number=10) mediator_state = MediatorTransferState(UNIT_SECRETHASH) mediator_state.transfers_pair = setup.transfers_pair secret = UNIT_SECRET secrethash = UNIT_SECRETHASH payer_channel = mediator.get_payer_channel(setup.channel_map, setup.transfers_pair[0]) payee_channel = mediator.get_payee_channel(setup.channel_map, setup.transfers_pair[0]) lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveSecretReveal(secret, payee_channel.partner_state.address), channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_unlockedlocks secret_registry_address = factories.make_address() transaction_hash = factories.make_address() mediator.state_transition( mediator_state=mediator_state, state_change=ContractReceiveSecretReveal( transaction_hash, secret_registry_address, secrethash, secret, setup.block_number, ), channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks # 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 expired_message = message_from_sendevent(send_lock_expired, setup.channels.our_address(0)) expired_message.sign(setup.channels.partner_privatekeys[0]) balance_proof = balanceproof_from_envelope(expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = channel.get_sender_expiration_threshold(lock) mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks
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(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, }, }) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert must_contain_entry(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. 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