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_subdispatch_to_paymenttask_target(chain_state, netting_channel_state): target_state = TargetTransferState( from_hop=HopState( node_address=netting_channel_state.partner_state.address, channel_identifier=netting_channel_state.canonical_identifier. channel_identifier, ), transfer=factories.create( factories.LockedTransferSignedStateProperties()), secret=UNIT_SECRET, ) subtask = TargetTask( canonical_identifier=netting_channel_state.canonical_identifier, target_state=target_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)]) state_change = Block( block_number=chain_state.block_number, gas_limit=GAS_LIMIT, block_hash=chain_state.block_hash, ) transition_result = subdispatch_to_paymenttask(chain_state=chain_state, state_change=state_change, secrethash=UNIT_SECRETHASH) assert transition_result.events == [] assert transition_result.new_state == chain_state chain_state.block_number = 20 balance_proof: BalanceProofSignedState = factories.create( factories.BalanceProofSignedStateProperties( canonical_identifier=netting_channel_state.canonical_identifier, sender=netting_channel_state.partner_state.address, transferred_amount=0, pkey=factories.UNIT_TRANSFER_PKEY, locksroot=LOCKSROOT_OF_NO_LOCKS, )) state_change = ReceiveLockExpired( balance_proof=balance_proof, sender=netting_channel_state.partner_state.address, secrethash=UNIT_SECRETHASH, message_identifier=factories.make_message_identifier(), ) transition_result = subdispatch_to_paymenttask(chain_state=chain_state, state_change=state_change, secrethash=UNIT_SECRETHASH) msg = "ReceiveLockExpired should have cleared the task" assert UNIT_SECRETHASH not in chain_state.payment_mapping.secrethashes_to_task, msg assert len( transition_result.events), "ReceiveLockExpired should generate events" assert transition_result.new_state == chain_state
def handle_message_lockexpired(self, raiden: RaidenService, message: LockExpired): balance_proof = balanceproof_from_envelope(message) state_change = ReceiveLockExpired( balance_proof=balance_proof, secrethash=message.secrethash, message_identifier=message.message_identifier, ) raiden.handle_state_change(state_change)
def handle_message_lockexpired(raiden: "RaidenService", message: LockExpired) -> None: balance_proof = balanceproof_from_envelope(message) state_change = ReceiveLockExpired( sender=balance_proof.sender, balance_proof=balance_proof, secrethash=message.secrethash, message_identifier=message.message_identifier, ) raiden.handle_and_track_state_changes([state_change])
def handle_message_lockexpired( raiden: "RaidenService", message: LockExpired # pylint: disable=unused-argument ) -> List[StateChange]: balance_proof = balanceproof_from_envelope(message) lock_expired = ReceiveLockExpired( sender=balance_proof.sender, balance_proof=balance_proof, secrethash=message.secrethash, message_identifier=message.message_identifier, ) return [lock_expired]
def make_receive_expired_lock( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, merkletree_leaves: List[Keccak256] = None, locked_amount: LockedAmount = None, chain_id: ChainID = None, ) -> ReceiveLockExpired: if not isinstance(lock, HashTimeLockState): raise ValueError("lock must be of type HashTimeLockState") signer = LocalSigner(privkey) address = signer.address if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError("Private key does not match any of the participants.") if merkletree_leaves is None: layers = make_empty_merkle_tree().layers else: assert lock.lockhash not in merkletree_leaves layers = compute_layers(merkletree_leaves) locksroot = layers[MERKLEROOT][0] chain_id = chain_id or channel_state.chain_id lock_expired_msg = LockExpired( chain_id=chain_id, nonce=nonce, message_identifier=random.randint(0, UINT64_MAX), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, channel_identifier=channel_state.identifier, token_network_address=channel_state.token_network_identifier, recipient=channel_state.partner_state.address, secrethash=lock.secrethash, ) lock_expired_msg.sign(signer) balance_proof = balanceproof_from_envelope(lock_expired_msg) receive_lockedtransfer = ReceiveLockExpired( balance_proof=balance_proof, secrethash=lock.secrethash, message_identifier=random.randint(0, UINT64_MAX), ) return receive_lockedtransfer
def make_receive_expired_lock( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, locked_amount: LockedAmount, pending_locks: PendingLocksState = None, chain_id: ChainID = None, ) -> ReceiveLockExpired: typecheck(lock, HashTimeLockState) signer = LocalSigner(privkey) address = signer.address if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError("Private key does not match any of the participants.") if pending_locks is None: pending_locks = make_empty_pending_locks_state() else: assert lock.encoded not in pending_locks.locks locksroot = compute_locksroot(pending_locks) chain_id = chain_id or channel_state.chain_id lock_expired_msg = LockExpired( chain_id=chain_id, nonce=nonce, message_identifier=make_message_identifier(), transferred_amount=transferred_amount, locked_amount=TokenAmount(locked_amount), locksroot=locksroot, channel_identifier=channel_state.identifier, token_network_address=channel_state.token_network_address, recipient=channel_state.partner_state.address, secrethash=lock.secrethash, signature=EMPTY_SIGNATURE, ) lock_expired_msg.sign(signer) balance_proof = balanceproof_from_envelope(lock_expired_msg) receive_lockedtransfer = ReceiveLockExpired( balance_proof=balance_proof, secrethash=lock.secrethash, message_identifier=make_message_identifier(), sender=balance_proof.sender, ) return receive_lockedtransfer
def make_receive_expired_lock( channel_state, privkey, nonce, transferred_amount, lock, merkletree_leaves=None, locked_amount=None, chain_id=None, ): if not isinstance(lock, HashTimeLockState): raise ValueError('lock must be of type HashTimeLockState') address = privatekey_to_address(privkey.secret) if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError('Private key does not match any of the participants.') if merkletree_leaves is None: layers = EMPTY_MERKLE_TREE.layers else: assert lock.lockhash not in merkletree_leaves layers = compute_layers(merkletree_leaves) locksroot = layers[MERKLEROOT][0] chain_id = chain_id or channel_state.chain_id lock_expired_msg = LockExpired( chain_id=chain_id, nonce=nonce, message_identifier=random.randint(0, UINT64_MAX), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, channel_identifier=channel_state.identifier, token_network_address=channel_state.token_network_identifier, recipient=channel_state.partner_state.address, secrethash=lock.secrethash, ) lock_expired_msg.sign(privkey) balance_proof = balanceproof_from_envelope(lock_expired_msg) receive_lockedtransfer = ReceiveLockExpired( channel_state.partner_state.address, balance_proof, lock.secrethash, random.randint(0, UINT64_MAX), ) return receive_lockedtransfer
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_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_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) current_state = iteration.new_state # As per the description of the issue here: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # We can fail the payment but can't delete the payment task if there are no # more routes, but we have to wait for the lock expiration assert iteration.new_state is not None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed invalid_balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof. token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, ) balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof. token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, private_key=refund_pkey, ) invalid_lock_expired_state_change = ReceiveLockExpired( invalid_balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) lock_expired_state_change = ReceiveLockExpired( balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) before_expiry_block = original_transfer.lock.expiration - 1 expiry_block = original_transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # a block before lock expiration, no events should be emitted current_state = iteration.new_state state_change = Block( block_number=before_expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert not iteration.events assert iteration.new_state, 'payment task should not be deleted at this block' # process an invalid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, invalid_lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) assert events.must_contain_entry(iteration.events, EventInvalidReceivedLockExpired, {}) # process a valid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) # now we get to the lock expiration block current_state = iteration.new_state state_change = Block( block_number=expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) # Since there was a refund transfer the payment task must not have been deleted assert iteration.new_state is not None # process the lock expired message after lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, expiry_block, ) # should be accepted assert events.must_contain_entry(iteration.events, SendProcessed, {}) assert iteration.new_state, 'payment task should be there waiting for next block' # process the the block after lock expiration current_state = iteration.new_state state_change = Block( block_number=expiry_block + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block + 1, ) assert iteration.new_state is None, 'from this point on the payment task should go'
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found by when querying the database. """ serializer = JSONSerializer storage = SQLiteStorage(':memory:', serializer) counter = itertools.count() lock_expired = ReceiveLockExpired( balance_proof=make_signed_balance_proof_from_counter(counter), secrethash=sha3(factories.make_secret(next(counter))), message_identifier=next(counter), ) unlock = ReceiveUnlock( message_identifier=next(counter), secret=sha3(factories.make_secret(next(counter))), balance_proof=make_signed_balance_proof_from_counter(counter), ) transfer_refund = ReceiveTransferRefund( transfer=make_signed_transfer_from_counter(counter), routes=list(), ) transfer_refund_cancel_route = ReceiveTransferRefundCancelRoute( routes=list(), transfer=make_signed_transfer_from_counter(counter), secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( routes=list(), from_route=mediator_from_route, from_transfer=mediator_signed_transfer, ) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget( route=target_from_route, transfer=target_signed_transfer, ) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_refund_cancel_route, transfer_refund_cancel_route.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] timestamp = datetime.utcnow().isoformat(timespec='milliseconds') for state_change, _ in statechanges_balanceproofs: storage.write_state_change(state_change, timestamp) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof( storage=storage, chain_id=balance_proof.chain_id, token_network_identifier=balance_proof.token_network_identifier, channel_identifier=balance_proof.channel_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record.data == state_change
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ amount = 10 block_number = 5 target = HOP2 expiration = 30 pseudo_random_generator = random.Random() payer_channel = factories.make_channel( partner_balance=amount, partner_address=HOP2, token_address=UNIT_TOKEN_ADDRESS, ) payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( payer_channel, amount, HOP1, target, expiration, UNIT_SECRET, pkey=HOP2_KEY, sender=HOP2, ) available_routes = [] channel_map = { payer_channel.identifier: payer_channel, } init_state_change = ActionInitMediator( available_routes, payer_route, payer_transfer, ) initial_state = None init_iteration = mediator.state_transition( initial_state, init_state_change, channel_map, pseudo_random_generator, block_number, ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert must_contain_entry(init_iteration.events, SendLockedTransfer, {}) is None assert must_contain_entry(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=payer_channel.partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=payer_channel.chain_id, token_network_identifier=payer_channel.token_network_identifier, channel_identifier=payer_channel.identifier, recipient=payer_channel.our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(HOP2_KEY) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # Regression: The mediator must still be able to process the block which # expires the lock expire_block_iteration = mediator.state_transition( init_iteration.new_state, Block( block_number=expired_block_number, gas_limit=0, block_hash=None, ), channel_map, pseudo_random_generator, expired_block_number, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( expire_block_iteration.new_state, ReceiveLockExpired( sender=payer_channel.partner_state.address, balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channel_map, pseudo_random_generator, expired_block_number, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in payer_channel.partner_state.secrethashes_to_lockedlocks
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found when querying the database. """ serializer = JSONSerializer() storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count() balance_proof = make_signed_balance_proof_from_counter(counter) lock_expired = ReceiveLockExpired( sender=balance_proof.sender, balance_proof=balance_proof, secrethash=factories.make_secret_hash(next(counter)), message_identifier=MessageID(next(counter)), ) received_balance_proof = make_signed_balance_proof_from_counter(counter) unlock = ReceiveUnlock( sender=received_balance_proof.sender, message_identifier=MessageID(next(counter)), secret=factories.make_secret(next(counter)), balance_proof=received_balance_proof, ) transfer = make_signed_transfer_from_counter(counter) transfer_refund = ReceiveTransferRefund( transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, # pylint: disable=no-member ) transfer = make_signed_transfer_from_counter(counter) transfer_reroute = ActionTransferReroute( transfer=transfer, balance_proof=transfer.balance_proof, sender=transfer.balance_proof.sender, # pylint: disable=no-member secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( route_states=[ RouteState( route=[factories.make_address(), factories.make_address()], forward_channel_id=factories.make_channel_identifier(), ) ], from_hop=mediator_from_route, from_transfer=mediator_signed_transfer, balance_proof=mediator_signed_transfer.balance_proof, sender=mediator_signed_transfer.balance_proof.sender, # pylint: disable=no-member ) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget( from_hop=target_from_route, transfer=target_signed_transfer, balance_proof=target_signed_transfer.balance_proof, sender=target_signed_transfer.balance_proof.sender, # pylint: disable=no-member ) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_reroute, transfer_reroute.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] assert storage.count_state_changes() == 0 state_change_ids = storage.write_state_changes( [state_change for state_change, _ in statechanges_balanceproofs]) assert storage.count_state_changes() == len(statechanges_balanceproofs) msg_in_order = "Querying must return state changes in order" stored_statechanges_records = storage.get_statechanges_records_by_range( RANGE_ALL_STATE_CHANGES) assert len(stored_statechanges_records) == 6, msg_in_order pair_elements = zip(statechanges_balanceproofs, state_change_ids, stored_statechanges_records) for statechange_balanceproof, statechange_id, record in pair_elements: assert record.data == statechange_balanceproof[0], msg_in_order assert record.state_change_identifier == statechange_id, msg_in_order # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_range( Range( stored_statechanges_records[1].state_change_identifier, stored_statechanges_records[2].state_change_identifier, )) assert len(stored_statechanges) == 2 assert isinstance(stored_statechanges[0], ReceiveUnlock) assert isinstance(stored_statechanges[1], ReceiveTransferRefund) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record assert state_change_record.data == state_change state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, locksroot=balance_proof.locksroot, ) assert state_change_record assert state_change_record.data == state_change storage.close()
def test_target_receive_lock_expired(): lock_amount = 7 block_number = 1 initiator = factories.HOP6 pseudo_random_generator = random.Random() our_balance = 100 our_address = factories.make_address() partner_balance = 130 from_channel = factories.make_channel( our_address=our_address, our_balance=our_balance, partner_address=UNIT_TRANSFER_SENDER, partner_balance=partner_balance, ) from_route = factories.route_from_channel(from_channel) expiration = block_number + from_channel.settle_timeout - from_channel.reveal_timeout from_transfer = factories.make_signed_transfer_for( channel_state=from_channel, amount=lock_amount, initiator=initiator, target=our_address, expiration=expiration, secret=UNIT_SECRET, ) init = ActionInitTarget( from_route, from_transfer, ) init_transition = target.state_transition( None, init, from_channel, pseudo_random_generator, block_number, ) assert init_transition.new_state is not None assert init_transition.new_state.route == from_route assert init_transition.new_state.transfer == from_transfer balance_proof = factories.make_signed_balance_proof( 2, from_transfer.balance_proof.transferred_amount, 0, from_transfer.balance_proof.token_network_identifier, from_channel.identifier, EMPTY_MERKLE_ROOT, from_transfer.lock.secrethash, sender_address=UNIT_TRANSFER_SENDER, private_key=UNIT_TRANSFER_PKEY, ) 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( init_transition.new_state, lock_expired_state_change, from_channel, pseudo_random_generator, block_before_confirmed_expiration, ) assert not must_contain_entry(iteration.events, SendProcessed, {}) block_lock_expired = block_before_confirmed_expiration + 1 iteration = target.state_transition( init_transition.new_state, lock_expired_state_change, from_channel, pseudo_random_generator, block_lock_expired, ) assert must_contain_entry(iteration.events, SendProcessed, {})
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_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = factories.make_channel_set([ { 'our_state': { 'balance': 0 }, 'partner_state': { 'balance': 10, 'address': HOP2 }, 'open_transaction': factories.make_transaction_execution_status( finished_block_number=10, ), }, ]) payer_transfer = factories.make_default_signed_transfer_for( channels[0], initiator=HOP1, expiration=30, pkey=HOP2_KEY, sender=HOP2, ) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=5, ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert must_contain_entry(init_iteration.events, SendLockedTransfer, {}) is None assert must_contain_entry(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(HOP2_KEY) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # Regression: The mediator must still be able to process the block which # expires the lock expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=None, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[ 0].partner_state.secrethashes_to_lockedlocks
def test_get_state_change_with_balance_proof(): """ All state changes which contain a balance proof must be found by when querying the database. """ serializer = JSONSerializer storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count() lock_expired = ReceiveLockExpired( balance_proof=make_signed_balance_proof_from_counter(counter), secrethash=sha3(factories.make_secret(next(counter))), message_identifier=next(counter), ) unlock = ReceiveUnlock( message_identifier=next(counter), secret=sha3(factories.make_secret(next(counter))), balance_proof=make_signed_balance_proof_from_counter(counter), ) transfer_refund = ReceiveTransferRefund( transfer=make_signed_transfer_from_counter(counter), routes=list()) transfer_refund_cancel_route = ReceiveTransferRefundCancelRoute( routes=list(), transfer=make_signed_transfer_from_counter(counter), secret=sha3(factories.make_secret(next(counter))), ) mediator_from_route, mediator_signed_transfer = make_from_route_from_counter( counter) action_init_mediator = ActionInitMediator( routes=list(), from_route=mediator_from_route, from_transfer=mediator_signed_transfer) target_from_route, target_signed_transfer = make_from_route_from_counter( counter) action_init_target = ActionInitTarget(route=target_from_route, transfer=target_signed_transfer) statechanges_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (unlock, unlock.balance_proof), (transfer_refund, transfer_refund.transfer.balance_proof), (transfer_refund_cancel_route, transfer_refund_cancel_route.transfer.balance_proof), (action_init_mediator, action_init_mediator.from_transfer.balance_proof), (action_init_target, action_init_target.transfer.balance_proof), ] timestamp = datetime.utcnow().isoformat(timespec="milliseconds") for state_change, _ in statechanges_balanceproofs: storage.write_state_change(state_change, timestamp) # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_identifier(1, "latest") assert isinstance(stored_statechanges[0], ReceiveLockExpired) assert isinstance(stored_statechanges[1], ReceiveUnlock) assert isinstance(stored_statechanges[2], ReceiveTransferRefund) assert isinstance(stored_statechanges[3], ReceiveTransferRefundCancelRoute) assert isinstance(stored_statechanges[4], ActionInitMediator) assert isinstance(stored_statechanges[5], ActionInitTarget) # Make sure state changes are returned in the correct order in which they were stored stored_statechanges = storage.get_statechanges_by_identifier(1, 2) assert isinstance(stored_statechanges[0], ReceiveLockExpired) assert isinstance(stored_statechanges[1], ReceiveUnlock) for state_change, balance_proof in statechanges_balanceproofs: state_change_record = get_state_change_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, sender=balance_proof.sender, balance_hash=balance_proof.balance_hash, ) assert state_change_record.data == state_change
def test_message_handler(): """ Test for MessageHandler.on_message and the different methods it dispatches into. Each of them results in a call to a RaidenService method, which is checked with a Mock. """ our_address = factories.make_address() sender_privkey, sender = factories.make_privkey_address() signer = LocalSigner(sender_privkey) message_handler = MessageHandler() mock_raiden = Mock( address=our_address, default_secret_registry=Mock(is_secret_registered=lambda **_: False) ) properties = factories.LockedTransferProperties(sender=sender, pkey=sender_privkey) locked_transfer = factories.create(properties) message_handler.on_message(mock_raiden, locked_transfer) assert_method_call(mock_raiden, "mediate_mediated_transfer", locked_transfer) locked_transfer_for_us = factories.create(factories.replace(properties, target=our_address)) message_handler.on_message(mock_raiden, locked_transfer_for_us) assert_method_call(mock_raiden, "target_mediated_transfer", locked_transfer_for_us) mock_raiden.default_secret_registry.is_secret_registered = lambda **_: True message_handler.on_message(mock_raiden, locked_transfer) assert not mock_raiden.mediate_mediated_transfer.called assert not mock_raiden.target_mediated_transfer.called mock_raiden.default_secret_registry.is_secret_registered = lambda **_: False params = dict( payment_identifier=13, amount=14, expiration=15, secrethash=factories.UNIT_SECRETHASH ) secret_request = SecretRequest( message_identifier=16, signature=factories.EMPTY_SIGNATURE, **params ) secret_request.sign(signer) receive = ReceiveSecretRequest(sender=sender, **params) message_handler.on_message(mock_raiden, secret_request) assert_method_call(mock_raiden, "handle_and_track_state_changes", [receive]) secret = factories.make_secret() reveal_secret = RevealSecret( message_identifier=100, signature=factories.EMPTY_SIGNATURE, secret=secret ) reveal_secret.sign(signer) receive = ReceiveSecretReveal(sender=sender, secret=secret) message_handler.on_message(mock_raiden, reveal_secret) assert_method_call(mock_raiden, "handle_and_track_state_changes", [receive]) properties: factories.UnlockProperties = factories.create_properties( factories.UnlockProperties() ) unlock = factories.create(properties) unlock.sign(signer) balance_proof = factories.make_signed_balance_proof_from_unsigned( factories.create(properties.balance_proof), signer, unlock.message_hash ) receive = ReceiveUnlock( message_identifier=properties.message_identifier, secret=properties.secret, balance_proof=balance_proof, sender=sender, ) message_handler.on_message(mock_raiden, unlock) assert_method_call(mock_raiden, "handle_and_track_state_changes", [receive]) properties: factories.LockExpiredProperties = factories.create_properties( factories.LockExpiredProperties() ) lock_expired = factories.create(properties) lock_expired.sign(signer) balance_proof = factories.make_signed_balance_proof_from_unsigned( factories.create(properties.balance_proof), signer, lock_expired.message_hash ) receive = ReceiveLockExpired( balance_proof=balance_proof, message_identifier=properties.message_identifier, secrethash=properties.secrethash, # pylint: disable=no-member sender=sender, ) message_handler.on_message(mock_raiden, lock_expired) assert_method_call(mock_raiden, "handle_and_track_state_changes", [receive]) delivered = Delivered(delivered_message_identifier=1, signature=factories.EMPTY_SIGNATURE) delivered.sign(signer) receive = ReceiveDelivered(message_identifier=1, sender=sender) message_handler.on_message(mock_raiden, delivered) assert_method_call(mock_raiden, "handle_and_track_state_changes", [receive]) processed = Processed(message_identifier=42, signature=factories.EMPTY_SIGNATURE) processed.sign(signer) receive = ReceiveProcessed(message_identifier=42, sender=sender) message_handler.on_message(mock_raiden, processed) assert_method_call(mock_raiden, "handle_and_track_state_changes", [receive])