def test_events_for_onchain_secretreveal(): """ Secret must be registered on-chain when the unsafe region is reached and the secret is known. """ block_number = 10 expiration = block_number + 30 channels = make_channel_set([channel_properties]) from_transfer = make_target_transfer(channels[0], expiration=expiration) channel.handle_receive_lockedtransfer(channels[0], from_transfer) channel.register_offchain_secret(channels[0], UNIT_SECRET, UNIT_SECRETHASH) safe_to_wait = expiration - channels[0].reveal_timeout - 1 unsafe_to_wait = expiration - channels[0].reveal_timeout state = TargetTransferState(channels.get_route(0), from_transfer) events = target.events_for_onchain_secretreveal(state, channels[0], safe_to_wait) assert not events events = target.events_for_onchain_secretreveal(state, channels[0], unsafe_to_wait) msg = 'when its not safe to wait, the contract send must be emitted' assert search_for_item(events, ContractSendSecretReveal, {'secret': UNIT_SECRET}), msg msg = 'second call must not emit ContractSendSecretReveal again' assert not target.events_for_onchain_secretreveal(state, channels[0], unsafe_to_wait), msg
def test_handle_inittarget(): """ Init transfer must send a secret request if the expiration is valid. """ block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties]) transfer_properties = LockedTransferSignedStateProperties( amount=channels[0].partner_state.contract_balance, expiration=channels[0].reveal_timeout + block_number + 1, canonical_identifier=channels[0].canonical_identifier, transferred_amount=0, locked_amount=channels[0].partner_state.contract_balance, ) from_transfer = create(transfer_properties) state_change = ActionInitTarget(channels.get_route(0), from_transfer) iteration = target.handle_inittarget(state_change, channels[0], pseudo_random_generator, block_number) assert search_for_item( iteration.events, SendSecretRequest, { "payment_identifier": from_transfer.payment_identifier, "amount": from_transfer.lock.amount, "secrethash": from_transfer.lock.secrethash, "recipient": UNIT_TRANSFER_INITIATOR, }, ) assert search_for_item(iteration.events, SendProcessed, {})
def test_events_for_onchain_secretreveal(): """ Secret must be registered on-chain when the unsafe region is reached and the secret is known. """ block_number = 10 expiration = block_number + 30 channels = make_channel_set([channel_properties]) from_transfer = make_target_transfer(channels[0], expiration=expiration) channel.handle_receive_lockedtransfer(channels[0], from_transfer) channel.register_offchain_secret(channels[0], UNIT_SECRET, UNIT_SECRETHASH) safe_to_wait = expiration - channels[0].reveal_timeout - 1 unsafe_to_wait = expiration - channels[0].reveal_timeout state = TargetTransferState(channels.get_route(0), from_transfer) events = target.events_for_onchain_secretreveal(state, channels[0], safe_to_wait) assert not events events = target.events_for_onchain_secretreveal(state, channels[0], unsafe_to_wait) msg = 'when its not safe to wait, the contract send must be emitted' assert must_contain_entry(events, ContractSendSecretReveal, {'secret': UNIT_SECRET}), msg msg = 'second call must not emit ContractSendSecretReveal again' assert not target.events_for_onchain_secretreveal(state, channels[0], unsafe_to_wait), msg
def test_filter_channels_by_status_empty_excludes(): channel_states = factories.make_channel_set(number_of_channels=3).channels channel_states[1].close_transaction = channel_states[1].open_transaction channel_states[2].close_transaction = channel_states[2].open_transaction channel_states[2].settle_transaction = channel_states[2].open_transaction assert (filter_channels_by_status(channel_states=channel_states, exclude_states=None) == channel_states)
def test_target_reject_keccak_empty_hash(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) expiration = block_number + channels[0].settle_timeout - channels[ 0].reveal_timeout from_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( transfer=factories.LockedTransferProperties( amount=lock_amount, target=channels.our_address(0), expiration=expiration, secret=EMPTY_HASH, ), ), allow_invalid=True, ) init = ActionInitTarget(route=channels.get_route(0), transfer=from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is None
def test_events_for_onchain_secretreveal(): """ Secret must be registered on-chain when the unsafe region is reached and the secret is known. """ block_number = 10 expiration = block_number + 30 channels = make_channel_set([channel_properties]) from_transfer = make_target_transfer(channels[0], expiration=expiration) channel.handle_receive_lockedtransfer(channels[0], from_transfer) channel.register_offchain_secret(channels[0], UNIT_SECRET, UNIT_SECRETHASH) safe_to_wait = expiration - channels[0].reveal_timeout - 1 unsafe_to_wait = expiration - channels[0].reveal_timeout state = TargetTransferState(channels.get_route(0), from_transfer) events = target.events_for_onchain_secretreveal(state, channels[0], safe_to_wait) assert not events events = target.events_for_onchain_secretreveal(state, channels[0], unsafe_to_wait) assert events assert isinstance(events[0], ContractSendSecretReveal) assert events[0].secret == UNIT_SECRET
def test_target_accept_keccak_empty_hash(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) expiration = block_number + channels[0].settle_timeout - channels[ 0].reveal_timeout from_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( amount=lock_amount, target=channels.our_address(0), expiration=expiration, secret=EMPTY_SECRET, ), allow_invalid=True, ) init = ActionInitTarget( from_hop=channels.get_hop(0), transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state
def test_target_reject_keccak_empty_hash(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) expiration = block_number + channels[0].settle_timeout - channels[0].reveal_timeout from_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( transfer=factories.LockedTransferProperties( amount=lock_amount, target=channels.our_address(0), expiration=expiration, secret=EMPTY_HASH, ), ), allow_invalid=True, ) init = ActionInitTarget(route=channels.get_route(0), transfer=from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is None
def test_target_receive_lock_expired(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) expiration = block_number + channels[0].settle_timeout - channels[ 0].reveal_timeout from_transfer = make_target_transfer(channels[0], amount=lock_amount, block_number=block_number) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None assert init_transition.new_state.route == channels.get_route(0) assert init_transition.new_state.transfer == from_transfer balance_proof = create( BalanceProofSignedStateProperties( nonce=2, transferred_amount=from_transfer.balance_proof.transferred_amount, locked_amount=0, canonical_identifier=channels[0].canonical_identifier, message_hash=from_transfer.lock.secrethash, )) lock_expired_state_change = ReceiveLockExpired( balance_proof=balance_proof, secrethash=from_transfer.lock.secrethash, message_identifier=1) block_before_confirmed_expiration = expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS - 1 iteration = target.state_transition( target_state=init_transition.new_state, state_change=lock_expired_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_before_confirmed_expiration, ) assert not search_for_item(iteration.events, SendProcessed, {}) block_lock_expired = block_before_confirmed_expiration + 1 iteration = target.state_transition( target_state=init_transition.new_state, state_change=lock_expired_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_lock_expired, ) assert search_for_item(iteration.events, SendProcessed, {})
def test_is_balance_proof_usable_onchain_answer_is_false(): channel_state = factories.make_channel_set( number_of_channels=1).channels[0] balance_proof_wrong_channel = factories.create( factories.BalanceProofSignedStateProperties()) is_valid_balance_proof = is_balance_proof_usable_onchain( received_balance_proof=balance_proof_wrong_channel, channel_state=channel_state, sender_state=channel_state.partner_state, ) assert is_valid_balance_proof.fail error_message = is_valid_balance_proof.as_error_message assert error_message.startswith( "channel_identifier does not match. "), error_message wrong_token_network_canonical_identifier = replace( channel_state.canonical_identifier, token_network_address=factories.make_address()) balance_proof_wrong_token_network = factories.create( factories.BalanceProofSignedStateProperties( canonical_identifier=wrong_token_network_canonical_identifier)) is_valid_balance_proof = is_balance_proof_usable_onchain( received_balance_proof=balance_proof_wrong_token_network, channel_state=channel_state, sender_state=channel_state.partner_state, ) assert is_valid_balance_proof.fail error_message = is_valid_balance_proof.as_error_message assert error_message.startswith( "token_network_address does not match. "), error_message balance_proof_overflow = factories.create( factories.BalanceProofSignedStateProperties( transferred_amount=factories.UINT256_MAX, locked_amount=1, canonical_identifier=channel_state.canonical_identifier, )) is_valid_balance_proof = is_balance_proof_usable_onchain( received_balance_proof=balance_proof_overflow, channel_state=channel_state, sender_state=channel_state.partner_state, ) assert is_valid_balance_proof.fail msg = is_valid_balance_proof.as_error_message assert msg.startswith( "Balance proof total transferred amount would overflow "), msg assert str(factories.UINT256_MAX) in msg, msg assert str(factories.UINT256_MAX + 1) in msg, msg
def test_handle_inittarget_bad_expiration(): """ Init transfer must do nothing if the expiration is bad. """ block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties]) expiration = channels[0].reveal_timeout + block_number + 1 from_transfer = make_target_transfer(channels[0], expiration=expiration) channel.handle_receive_lockedtransfer(channels[0], from_transfer) state_change = ActionInitTarget(channels.get_route(0), from_transfer) iteration = target.handle_inittarget(state_change, channels[0], pseudo_random_generator, block_number) assert search_for_item(iteration.events, EventUnlockClaimFailed, {})
def make_target_state( our_address=factories.ADDR, amount=3, block_number=1, initiator=UNIT_TRANSFER_INITIATOR, expiration=None, pseudo_random_generator=None, ): pseudo_random_generator = pseudo_random_generator or random.Random() channels = make_channel_set( [ NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(address=our_address), partner_state=NettingChannelEndStateProperties( address=UNIT_TRANSFER_SENDER, balance=amount ), ) ] ) expiration = expiration or channels[0].reveal_timeout + block_number + 1 from_transfer = make_target_transfer(channels[0], amount, expiration, initiator) state_change = ActionInitTarget( from_hop=channels.get_hop(0), transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) iteration = target.handle_inittarget( state_change=state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) return TargetStateSetup( channel=channels[0], new_state=iteration.new_state, our_address=our_address, initiator=initiator, expiration=expiration, amount=amount, block_number=block_number, pseudo_random_generator=pseudo_random_generator, )
def test_filter_channels_by_status_empty_excludes(): channel_states = factories.make_channel_set(number_of_channels=3).channels channel_states[1].close_transaction = TransactionExecutionStatus( started_block_number=channel_states[1].open_transaction.started_block_number, finished_block_number=channel_states[1].open_transaction.finished_block_number, result=TransactionExecutionStatus.SUCCESS, ) channel_states[2].close_transaction = TransactionExecutionStatus( started_block_number=channel_states[2].open_transaction.started_block_number, finished_block_number=channel_states[2].open_transaction.finished_block_number, result=TransactionExecutionStatus.SUCCESS, ) channel_states[2].settle_transaction = channel_states[2].close_transaction assert ( filter_channels_by_status(channel_states=channel_states, exclude_states=None) == channel_states )
def test_target_lock_is_expired_if_secret_is_not_registered_onchain(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, block_number=1) init = ActionInitTarget( from_hop=channels.get_hop(0), transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None secret_reveal_iteration = target.state_transition( target_state=init_transition.new_state, state_change=ReceiveSecretReveal(UNIT_SECRET, channels[0].partner_state.address), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) expired_block_number = channel.get_receiver_expiration_threshold( from_transfer.lock.expiration) iteration = target.state_transition( target_state=secret_reveal_iteration.new_state, state_change=Block(expired_block_number, None, None), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert search_for_item(iteration.events, EventUnlockClaimFailed, {})
def test_is_balance_proof_usable_onchain_answer_is_false(): channel_state = factories.make_channel_set( number_of_channels=1).channels[0] balance_proof_wrong_channel = factories.create( factories.BalanceProofSignedStateProperties()) result, msg = is_balance_proof_usable_onchain( received_balance_proof=balance_proof_wrong_channel, channel_state=channel_state, sender_state=channel_state.partner_state, ) assert result is False, result assert msg.startswith("channel_identifier does not match. "), msg wrong_token_network_canonical_identifier = deepcopy( channel_state.canonical_identifier) wrong_token_network_canonical_identifier.token_network_address = factories.make_address( ) balance_proof_wrong_token_network = factories.create( factories.BalanceProofSignedStateProperties( canonical_identifier=wrong_token_network_canonical_identifier)) result, msg = is_balance_proof_usable_onchain( received_balance_proof=balance_proof_wrong_token_network, channel_state=channel_state, sender_state=channel_state.partner_state, ) assert result is False, result assert msg.startswith("token_network_identifier does not match. "), msg balance_proof_overflow = factories.create( factories.BalanceProofSignedStateProperties( transferred_amount=factories.UINT256_MAX, locked_amount=1, canonical_identifier=channel_state.canonical_identifier, )) result, msg = is_balance_proof_usable_onchain( received_balance_proof=balance_proof_overflow, channel_state=channel_state, sender_state=channel_state.partner_state, ) assert result is False, result assert msg.startswith( "Balance proof total transferred amount would overflow "), msg assert str(factories.UINT256_MAX) in msg, msg assert str(factories.UINT256_MAX + 1) in msg, msg
def test_handle_inittarget_bad_expiration(): """ Init transfer must do nothing if the expiration is bad. """ block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties]) expiration = channels[0].reveal_timeout + block_number + 1 from_transfer = make_target_transfer(channels[0], expiration=expiration) channel.handle_receive_lockedtransfer(channels[0], from_transfer) state_change = ActionInitTarget(channels.get_route(0), from_transfer) iteration = target.handle_inittarget( state_change, channels[0], pseudo_random_generator, block_number, ) assert must_contain_entry(iteration.events, EventUnlockClaimFailed, {})
def make_target_state( our_address=factories.ADDR, amount=3, block_number=1, initiator=UNIT_TRANSFER_INITIATOR, expiration=None, pseudo_random_generator=None, ): pseudo_random_generator = pseudo_random_generator or random.Random() channels = make_channel_set([ NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(address=our_address), partner_state=NettingChannelEndStateProperties( address=UNIT_TRANSFER_SENDER, balance=amount, ), ), ]) expiration = expiration or channels[0].reveal_timeout + block_number + 1 from_transfer = make_target_transfer(channels[0], amount, expiration, initiator) state_change = ActionInitTarget(route=channels.get_route(0), transfer=from_transfer) iteration = target.handle_inittarget( state_change=state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) return TargetStateSetup( channel=channels[0], new_state=iteration.new_state, our_address=our_address, initiator=initiator, expiration=expiration, amount=amount, block_number=block_number, pseudo_random_generator=pseudo_random_generator, )
def test_target_lock_is_expired_if_secret_is_not_registered_onchain(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, block_number=1) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None secret_reveal_iteration = target.state_transition( target_state=init_transition.new_state, state_change=ReceiveSecretReveal(UNIT_SECRET, channels[0].partner_state.address), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) expired_block_number = from_transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS iteration = target.state_transition( target_state=secret_reveal_iteration.new_state, state_change=Block(expired_block_number, None, None), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert must_contain_entry(iteration.events, EventUnlockClaimFailed, {})
def test_get_initial_payment_for_final_target_amount( flat_fee: FeeAmount, prop_fee: ProportionalFeeAmount, balance: TokenAmount, final_amount: PaymentAmount, initial_amount: PaymentWithFeeAmount, expected_fees: List[FeeAmount], ): prop_fee = ppm_fee_per_channel(prop_fee) channel_set = make_channel_set([ NettingChannelStateProperties( canonical_identifier=factories.create( CanonicalIdentifierProperties( channel_identifier=ChannelID(1))), our_state=NettingChannelEndStateProperties(balance=TokenAmount(0)), partner_state=NettingChannelEndStateProperties(balance=balance), fee_schedule=FeeScheduleState(flat=flat_fee, proportional=prop_fee), ), NettingChannelStateProperties( canonical_identifier=factories.create( CanonicalIdentifierProperties( channel_identifier=ChannelID(2))), our_state=NettingChannelEndStateProperties(balance=balance), partner_state=NettingChannelEndStateProperties( balance=TokenAmount(0)), fee_schedule=FeeScheduleState(flat=flat_fee, proportional=prop_fee), ), ]) calculation = get_initial_amount_for_amount_after_fees( amount_after_fees=final_amount, channels=[(channel_set.channels[0], channel_set.channels[1])], ) assert calculation is not None assert calculation.total_amount == initial_amount assert calculation.mediation_fees == expected_fees
def test_handle_inittarget(): """ Init transfer must send a secret request if the expiration is valid. """ block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties]) transfer_properties = LockedTransferSignedStateProperties( transfer=LockedTransferProperties( amount=channels[0].partner_state.contract_balance, expiration=channels[0].reveal_timeout + block_number + 1, balance_proof=BalanceProofProperties( channel_identifier=channels[0].identifier, token_network_identifier=channels[0].token_network_identifier, transferred_amount=0, locked_amount=channels[0].partner_state.contract_balance, ), ), ) from_transfer = create(transfer_properties) state_change = ActionInitTarget(channels.get_route(0), from_transfer) iteration = target.handle_inittarget( state_change, channels[0], pseudo_random_generator, block_number, ) assert must_contain_entry(iteration.events, SendSecretRequest, { 'payment_identifier': from_transfer.payment_identifier, 'amount': from_transfer.lock.amount, 'secrethash': from_transfer.lock.secrethash, 'recipient': UNIT_TRANSFER_INITIATOR, }) assert must_contain_entry(iteration.events, SendProcessed, {})
def test_handle_inittarget(): """ Init transfer must send a secret request if the expiration is valid. """ block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties]) transfer_properties = LockedTransferSignedStateProperties( transfer=LockedTransferProperties( amount=channels[0].partner_state.contract_balance, expiration=channels[0].reveal_timeout + block_number + 1, balance_proof=BalanceProofProperties( channel_identifier=channels[0].identifier, token_network_identifier=channels[0].token_network_identifier, transferred_amount=0, locked_amount=channels[0].partner_state.contract_balance, ), ), ) from_transfer = create(transfer_properties) state_change = ActionInitTarget(channels.get_route(0), from_transfer) iteration = target.handle_inittarget( state_change, channels[0], pseudo_random_generator, block_number, ) assert must_contain_entry( iteration.events, SendSecretRequest, { 'payment_identifier': from_transfer.payment_identifier, 'amount': from_transfer.lock.amount, 'secrethash': from_transfer.lock.secrethash, 'recipient': UNIT_TRANSFER_INITIATOR, }) assert must_contain_entry(iteration.events, SendProcessed, {})
def test_target_lock_is_expired_if_secret_is_not_registered_onchain(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, block_number=1) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None secret_reveal_iteration = target.state_transition( target_state=init_transition.new_state, state_change=ReceiveSecretReveal(UNIT_SECRET, channels[0].partner_state.address), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) expired_block_number = channel.get_receiver_expiration_threshold(from_transfer.lock) iteration = target.state_transition( target_state=secret_reveal_iteration.new_state, state_change=Block(expired_block_number, None, None), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert must_contain_entry(iteration.events, EventUnlockClaimFailed, {})
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = make_channel_set([ NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=0), partner_state=NettingChannelEndStateProperties( balance=10, address=HOP2, privatekey=HOP2_KEY, ), ), ]) payer_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( sender=HOP2, pkey=HOP2_KEY, transfer=factories.LockedTransferProperties(expiration=30), )) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=5, block_hash=factories.make_block_hash(), ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert search_for_item(init_iteration.events, SendLockedTransfer, {}) is None assert search_for_item(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(LocalSigner(channels.partner_privatekeys[0])) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) # Regression: The mediator must still be able to process the block which # expires the lock expired_block_number = channel.get_sender_expiration_threshold(lock) block_hash = factories.make_block_hash() expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=block_hash, ), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=block_hash, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=block_hash, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[ 0].partner_state.secrethashes_to_lockedlocks
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = make_channel_set([ NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=0), partner_state=NettingChannelEndStateProperties( balance=10, address=HOP2, privatekey=HOP2_KEY, ), ), ]) payer_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( sender=HOP2, pkey=HOP2_KEY, transfer=factories.LockedTransferProperties(expiration=30), )) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=5, ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert must_contain_entry(init_iteration.events, SendLockedTransfer, {}) is None assert must_contain_entry(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(channels.partner_privatekeys[0]) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) # Regression: The mediator must still be able to process the block which # expires the lock expired_block_number = channel.get_sender_expiration_threshold(lock) expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=None, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[0].partner_state.secrethashes_to_lockedlocks
def test_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_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = factories.make_channel_set([ { 'our_state': { 'balance': 0 }, 'partner_state': { 'balance': 10, 'address': HOP2 }, 'open_transaction': factories.make_transaction_execution_status( finished_block_number=10, ), }, ]) payer_transfer = factories.make_default_signed_transfer_for( channels[0], initiator=HOP1, expiration=30, pkey=HOP2_KEY, sender=HOP2, ) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=5, ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert must_contain_entry(init_iteration.events, SendLockedTransfer, {}) is None assert must_contain_entry(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(HOP2_KEY) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # Regression: The mediator must still be able to process the block which # expires the lock expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=None, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[ 0].partner_state.secrethashes_to_lockedlocks
def test_state_transition(): """ Happy case testing. """ lock_amount = 7 block_number = 1 initiator = factories.make_address() pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, initiator=initiator) init = ActionInitTarget( from_hop=channels.get_hop(0), transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None assert init_transition.new_state.from_hop == channels.get_hop(0) assert init_transition.new_state.transfer == from_transfer first_new_block = Block(block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash()) first_block_iteration = target.state_transition( target_state=init_transition.new_state, state_change=first_new_block, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=first_new_block.block_number, ) secret_reveal = ReceiveSecretReveal(secret=UNIT_SECRET, sender=initiator) reveal_iteration = target.state_transition( target_state=first_block_iteration.new_state, state_change=secret_reveal, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=first_new_block.block_number, ) assert reveal_iteration.events second_new_block = Block(block_number=block_number + 2, gas_limit=1, block_hash=factories.make_transaction_hash()) iteration = target.state_transition( target_state=init_transition.new_state, state_change=second_new_block, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=second_new_block.block_number, ) assert not iteration.events balance_proof = create( BalanceProofSignedStateProperties( nonce=from_transfer.balance_proof.nonce + 1, # pylint: disable=no-member transferred_amount=lock_amount, locked_amount=0, canonical_identifier=factories.make_canonical_identifier( token_network_address=channels[0].token_network_address, channel_identifier=channels.get_hop(0).channel_identifier, ), locksroot=LOCKSROOT_OF_NO_LOCKS, message_hash=b"\x00" * 32, # invalid )) balance_proof_state_change = ReceiveUnlock( message_identifier=random.randint(0, UINT64_MAX), secret=UNIT_SECRET, balance_proof=balance_proof, sender=balance_proof.sender, # pylint: disable=no-member ) proof_iteration = target.state_transition( target_state=init_transition.new_state, state_change=balance_proof_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number + 2, ) assert proof_iteration.new_state is None
def test_state_transition(): """ Happy case testing. """ lock_amount = 7 block_number = 1 initiator = factories.HOP6 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, initiator=initiator) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None assert init_transition.new_state.route == channels.get_route(0) assert init_transition.new_state.transfer == from_transfer first_new_block = Block( block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) first_block_iteration = target.state_transition( target_state=init_transition.new_state, state_change=first_new_block, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=first_new_block.block_number, ) secret_reveal = ReceiveSecretReveal(factories.UNIT_SECRET, initiator) reveal_iteration = target.state_transition( target_state=first_block_iteration.new_state, state_change=secret_reveal, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=first_new_block.block_number, ) assert reveal_iteration.events second_new_block = Block( block_number=block_number + 2, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = target.state_transition( target_state=init_transition.new_state, state_change=second_new_block, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=second_new_block.block_number, ) assert not iteration.events balance_proof = create( BalanceProofSignedStateProperties( balance_proof=BalanceProofProperties( nonce=from_transfer.balance_proof.nonce + 1, transferred_amount=lock_amount, locked_amount=0, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels.get_route(0).channel_identifier, locksroot=EMPTY_MERKLE_ROOT, ), message_hash=b'\x00' * 32, # invalid )) balance_proof_state_change = ReceiveUnlock( message_identifier=random.randint(0, UINT64_MAX), secret=UNIT_SECRET, balance_proof=balance_proof, ) proof_iteration = target.state_transition( target_state=init_transition.new_state, state_change=balance_proof_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number + 2, ) assert proof_iteration.new_state is None
def test_state_transition(): """ Happy case testing. """ lock_amount = 7 block_number = 1 initiator = factories.HOP6 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, initiator=initiator) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None assert init_transition.new_state.route == channels.get_route(0) assert init_transition.new_state.transfer == from_transfer first_new_block = Block( block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) first_block_iteration = target.state_transition( target_state=init_transition.new_state, state_change=first_new_block, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=first_new_block.block_number, ) secret_reveal = ReceiveSecretReveal(factories.UNIT_SECRET, initiator) reveal_iteration = target.state_transition( target_state=first_block_iteration.new_state, state_change=secret_reveal, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=first_new_block.block_number, ) assert reveal_iteration.events second_new_block = Block( block_number=block_number + 2, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = target.state_transition( target_state=init_transition.new_state, state_change=second_new_block, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=second_new_block.block_number, ) assert not iteration.events balance_proof = create(BalanceProofSignedStateProperties( balance_proof=BalanceProofProperties( nonce=from_transfer.balance_proof.nonce + 1, transferred_amount=lock_amount, locked_amount=0, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels.get_route(0).channel_identifier, locksroot=EMPTY_MERKLE_ROOT, ), message_hash=b'\x00' * 32, # invalid )) balance_proof_state_change = ReceiveUnlock( message_identifier=random.randint(0, UINT64_MAX), secret=UNIT_SECRET, balance_proof=balance_proof, ) proof_iteration = target.state_transition( target_state=init_transition.new_state, state_change=balance_proof_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number + 2, ) assert proof_iteration.new_state is None
def test_target_receive_lock_expired(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) expiration = block_number + channels[0].settle_timeout - channels[0].reveal_timeout from_transfer = make_target_transfer( channels[0], amount=lock_amount, block_number=block_number, ) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None assert init_transition.new_state.route == channels.get_route(0) assert init_transition.new_state.transfer == from_transfer balance_proof = create(BalanceProofSignedStateProperties( balance_proof=BalanceProofProperties( nonce=2, transferred_amount=from_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_identifier=from_transfer.balance_proof.token_network_identifier, channel_identifier=channels[0].identifier, ), message_hash=from_transfer.lock.secrethash, )) lock_expired_state_change = ReceiveLockExpired( balance_proof=balance_proof, secrethash=from_transfer.lock.secrethash, message_identifier=1, ) block_before_confirmed_expiration = expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS - 1 iteration = target.state_transition( target_state=init_transition.new_state, state_change=lock_expired_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_before_confirmed_expiration, ) assert not must_contain_entry(iteration.events, SendProcessed, {}) block_lock_expired = block_before_confirmed_expiration + 1 iteration = target.state_transition( target_state=init_transition.new_state, state_change=lock_expired_state_change, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_lock_expired, ) assert must_contain_entry(iteration.events, SendProcessed, {})