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_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. """ 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