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 test_receive_lockedtransfer_invalidnonce(raiden_network, number_of_nodes, deposit, token_addresses, reveal_timeout, network_wait): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) amount = 10 payment_identifier = PaymentID(1) secrethash = transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=PaymentAmount(10), identifier=payment_identifier, timeout=network_wait * number_of_nodes, ) repeated_nonce = Nonce(1) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=repeated_nonce, token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(amount), locked_amount=TokenAmount(amount), recipient=app1.raiden.address, locksroot=make_locksroot(), lock=Lock(amount=PaymentWithFeeAmount(amount), expiration=expiration, secrethash=UNIT_SECRETHASH), target=app2.raiden.address, initiator=app0.raiden.address, signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[ RouteMetadata(route=[app1.raiden.address, app2.raiden.address]) ]), ) sign_and_inject(mediated_transfer_message, app0.raiden.signer, app1) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount, [], app1, deposit + amount, [], )
def test_restore_queueids_to_queues(chain_state, netting_channel_state): """Test that withdraw messages are restorable if they exist in chain_state.queueids_to_queues. """ recipient = netting_channel_state.partner_state.address queue_identifier = QueueIdentifier( recipient=recipient, canonical_identifier=netting_channel_state.canonical_identifier) msg_args = dict( recipient=recipient, canonical_identifier=netting_channel_state.canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=WithdrawAmount(1), participant=recipient, expiration=BlockExpiration(10), nonce=Nonce(15), ) messages = [ SendWithdrawRequest(**msg_args), SendWithdrawConfirmation(**msg_args), SendWithdrawExpired(**msg_args), ] chain_state.queueids_to_queues[queue_identifier] = messages serialized_chain_state = JSONSerializer.serialize(chain_state) deserialized_chain_state = JSONSerializer.deserialize( serialized_chain_state) assert chain_state == deserialized_chain_state
def test_events_loaded_from_storage_should_deserialize(tmp_path): filename = f"{tmp_path}/v{RAIDEN_DB_VERSION}_log.db" storage = SerializedSQLiteStorage(filename, serializer=JSONSerializer()) # Satisfy the foreign-key constraint for state change ID ids = storage.write_state_changes([ ActionInitChain( pseudo_random_generator=random.Random(), block_number=1, block_hash=b"", our_address=factories.make_address(), chain_id=1, ) ]) canonical_identifier = factories.make_canonical_identifier() recipient = factories.make_address() participant = factories.make_address() event = SendWithdrawRequest( recipient=recipient, canonical_identifier=canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=1, participant=participant, expiration=10, nonce=15, ) storage.write_events([(ids[0], event)]) stored_events = storage.get_events() assert stored_events[0] == event
def test_regression_revealsecret_after_secret( raiden_network: List[App], token_addresses: List[TokenAddress]) -> None: """ A RevealSecret message received after a Unlock message must be cleanly handled. """ app0, app1, app2 = raiden_network token = token_addresses[0] identifier = PaymentID(1) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token) assert token_network_address, "The fixtures must register the token" payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount=PaymentAmount(1), target=TargetAddress(app2.raiden.address), identifier=identifier, ) with watch_for_unlock_failures(*raiden_network): assert payment_status.payment_done.wait() assert app1.raiden.wal, "The fixtures must start the app." event = raiden_events_search_for_item(app1.raiden, SendSecretReveal, {}) assert event reveal_secret = RevealSecret( message_identifier=make_message_identifier(), secret=event.secret, signature=EMPTY_SIGNATURE, ) app2.raiden.sign(reveal_secret) app1.raiden.on_messages([reveal_secret])
def test_events_loaded_from_storage_should_deserialize(tmp_path): filename = Path(f"{tmp_path}/v{RAIDEN_DB_VERSION}_log.db") storage = SerializedSQLiteStorage(filename, serializer=JSONSerializer()) # Satisfy the foreign-key constraint for state change ID ids = storage.write_state_changes([ Block( block_number=BlockNumber(1), gas_limit=BlockGasLimit(1), block_hash=factories.make_block_hash(), ) ]) canonical_identifier = factories.make_canonical_identifier() recipient = factories.make_address() participant = factories.make_address() event = SendWithdrawRequest( recipient=recipient, canonical_identifier=canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=WithdrawAmount(1), participant=participant, expiration=BlockExpiration(10), nonce=Nonce(15), ) storage.write_events([(ids[0], event)]) stored_events = storage.get_events() assert stored_events[0] == event
def test_regression_revealsecret_after_secret(raiden_network, token_addresses, transport_protocol): """ A RevealSecret message received after a Unlock message must be cleanly handled. """ app0, app1, app2 = raiden_network token = token_addresses[0] identifier = 1 token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token) payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount=1, target=app2.raiden.address, identifier=identifier) with watch_for_unlock_failures(*raiden_network): assert payment_status.payment_done.wait() event = search_for_item(app1.raiden.wal.storage.get_events(), SendSecretReveal, {}) assert event reveal_secret = RevealSecret( message_identifier=make_message_identifier(), secret=event.secret, signature=EMPTY_SIGNATURE, ) app2.raiden.sign(reveal_secret) if transport_protocol is TransportProtocol.MATRIX: app1.raiden.transport._receive_message(reveal_secret) # pylint: disable=protected-access else: raise TypeError("Unknown TransportProtocol")
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 make_message(sign: bool = True) -> Message: message = SecretRequest( message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), secrethash=factories.UNIT_SECRETHASH, amount=PaymentAmount(1), expiration=BlockExpiration(10), signature=EMPTY_SIGNATURE, ) if sign: message.sign(LocalSigner(factories.HOP1_KEY)) return message
def test_received_lockedtransfer_closedchannel(raiden_network, reveal_timeout, token_addresses, deposit): app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) RaidenAPI(app1.raiden).channel_close(registry_address, token_address, app0.raiden.address) app0.raiden.proxy_manager.wait_until_block( target_block_number=app0.raiden.rpc_client.block_number() + 1) # Now receive one mediated transfer for the closed channel lock_amount = TokenAmount(10) payment_identifier = PaymentID(1) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(1), token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(0), locked_amount=lock_amount, recipient=app1.raiden.address, locksroot=make_locksroot(), lock=Lock( amount=PaymentWithFeeAmount(lock_amount), expiration=expiration, secrethash=UNIT_SECRETHASH, ), target=app1.raiden.address, initiator=app0.raiden.address, signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[RouteMetadata(route=[app1.raiden.address])]), ) sign_and_inject(mediated_transfer_message, app0.raiden.signer, app1) # The local state must not change since the channel is already closed assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def test_inplace_delete_message_queue(chain_state): sender = factories.make_address() canonical_identifier = factories.make_canonical_identifier() message_id = factories.make_message_identifier() delivered_state_change = ReceiveDelivered(sender=sender, message_identifier=message_id) processed_state_change = ReceiveProcessed(sender=sender, message_identifier=message_id) global_identifier = QueueIdentifier( recipient=sender, canonical_identifier=CANONICAL_IDENTIFIER_GLOBAL_QUEUE) chain_state.queueids_to_queues[global_identifier] = None assert global_identifier in chain_state.queueids_to_queues, "queue mapping insertion failed" inplace_delete_message_queue(chain_state=chain_state, state_change=delivered_state_change, queueid=global_identifier) assert global_identifier not in chain_state.queueids_to_queues, "did not clear queue" chain_state.queueids_to_queues[global_identifier] = [ SendMessageEvent( recipient=sender, canonical_identifier=canonical_identifier, message_identifier=message_id, ) ] assert global_identifier in chain_state.queueids_to_queues, "queue mapping insertion failed" handle_receive_delivered(chain_state=chain_state, state_change=delivered_state_change) assert global_identifier not in chain_state.queueids_to_queues, "did not clear queue" queue_identifier = QueueIdentifier( recipient=sender, canonical_identifier=canonical_identifier) assert queue_identifier not in chain_state.queueids_to_queues, "queue not empty" chain_state.queueids_to_queues[queue_identifier] = [ SendMessageEvent( recipient=sender, canonical_identifier=canonical_identifier, message_identifier=message_id, ) ] assert queue_identifier in chain_state.queueids_to_queues, "queue mapping not mutable" handle_receive_processed(chain_state=chain_state, state_change=processed_state_change) assert queue_identifier not in chain_state.queueids_to_queues, "queue did not clear"
def test_receive_lockedtransfer_invalidrecipient(raiden_network, token_addresses, reveal_timeout, deposit): app0, app1 = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) payment_identifier = PaymentID(1) invalid_recipient = make_address() lock_amount = TokenAmount(10) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(1), token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(0), locked_amount=lock_amount, recipient=invalid_recipient, locksroot=make_locksroot(), lock=Lock( amount=PaymentWithFeeAmount(lock_amount), expiration=expiration, secrethash=UNIT_SECRETHASH, ), target=app1.raiden.address, initiator=app0.raiden.address, signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[RouteMetadata(route=[app1.raiden.address])]), ) sign_and_inject(mediated_transfer_message, app0.raiden.signer, app1) assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def test_receive_lockedtransfer_invalidsender( raiden_network: List[RaidenService], token_addresses, deposit, reveal_timeout ): app0, app1 = raiden_network token_address = token_addresses[0] other_key, other_address = make_privkey_address() token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address ) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) lock_amount = LockedAmount(10) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), nonce=Nonce(1), token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(0), locked_amount=lock_amount, recipient=app0.address, locksroot=make_locksroot(), lock=Lock( amount=PaymentWithFeeAmount(lock_amount), expiration=expiration, secrethash=UNIT_SECRETHASH, ), target=TargetAddress(app0.address), initiator=InitiatorAddress(other_address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[RouteMetadata(route=[app0.address])]), ) sign_and_inject(mediated_transfer_message, LocalSigner(other_key), app0) assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def action_init_initiator_to_action_init_target( action: ActionInitInitiator, channel: NettingChannelState, block_number: BlockNumber, route_state: RouteState, address: Address, private_key: PrivateKey, ) -> ActionInitTarget: transfer = signed_transfer_from_description( private_key=private_key, description=action.transfer, channel=channel, message_id=factories.make_message_identifier(), block_number=block_number, route_state=route_state, route_states=action.routes, ) from_hop = HopState(node_address=address, channel_identifier=channel.identifier) return ActionInitTarget(from_hop=from_hop, transfer=transfer, sender=address, balance_proof=transfer.balance_proof)
# messages that don't depend on randomness for that test. But right now, we # don't have that. random.seed(1) message_factories = ( factories.LockedTransferProperties(), factories.RefundTransferProperties(), factories.LockExpiredProperties(), factories.UnlockProperties(), ) messages = [factories.create(factory) for factory in message_factories] # TODO Handle these with factories once #5091 is implemented messages.append( Delivered( delivered_message_identifier=factories.make_message_identifier(), signature=factories.make_signature(), ) ) messages.append( Processed( message_identifier=factories.make_message_identifier(), signature=factories.make_signature(), ) ) messages.append( RevealSecret( message_identifier=factories.make_message_identifier(), secret=factories.make_secret(), signature=factories.make_signature(), )
def test_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. Also, for issue: https://github.com/raiden-network/raiden/issues/3170 It was noticed that when receiving the same refund transfer twice, the mediator would detect an invalid refund and clear the mediator state. So the test also checks that mediator rejects the duplicate transfer and keeps the mediator state unchanged. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState( secrethash=UNIT_SECRETHASH, routes=setup.channels.get_routes() ) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=lock_expiration, payment_identifier=UNIT_TRANSFER_IDENTIFIER, canonical_identifier=canonical_identifier, sender=setup.channels.partner_address(2), pkey=setup.channels.partner_privatekeys[2], message_identifier=factories.make_message_identifier(), ) ) # All three channels have been used routes = [] refund_state_change = ReceiveTransferRefund(transfer=received_transfer, routes=routes) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock(end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH) token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier assert search_for_item( iteration.events, SendRefundTransfer, { "recipient": setup.channels.partner_address(0), "queue_identifier": { "recipient": setup.channels.partner_address(0), "channel_identifier": first_payer_transfer.balance_proof.channel_identifier, }, "transfer": { "payment_identifier": UNIT_TRANSFER_IDENTIFIER, "token": UNIT_TOKEN_ADDRESS, "balance_proof": { "transferred_amount": 0, "locked_amount": 10, "locksroot": lock.lockhash, "token_network_identifier": token_network_identifier, "channel_identifier": first_payer_transfer.balance_proof.channel_identifier, "chain_id": first_payer_transfer.balance_proof.chain_id, }, "lock": { "amount": lock.amount, "expiration": lock.expiration, "secrethash": lock.secrethash, }, "initiator": UNIT_TRANSFER_INITIATOR, "target": UNIT_TRANSFER_TARGET, }, }, ) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert search_for_item(duplicate_iteration.events, SendRefundTransfer, {}) is None assert duplicate_iteration.new_state is not None assert duplicate_iteration.new_state == iteration.new_state
def test_regression_multiple_revealsecret( raiden_network: List[App], token_addresses: List[TokenAddress]) -> None: """ Multiple RevealSecret messages arriving at the same time must be handled properly. Unlock handling followed these steps: The Unlock message arrives The secret is registered The channel is updated and the correspoding lock is removed * A balance proof for the new channel state is created and sent to the payer The channel is unregistered for the given secrethash The step marked with an asterisk above introduced a context-switch. This allowed a second Reveal Unlock message to be handled before the channel was unregistered. And because the channel was already updated an exception was raised for an unknown secret. """ app0, app1 = raiden_network token = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token) assert token_network_address channelstate_0_1 = get_channelstate(app0, app1, token_network_address) payment_identifier = PaymentID(1) secret, secrethash = make_secret_with_hash() expiration = BlockExpiration(app0.raiden.get_block_number() + 100) lock_amount = PaymentWithFeeAmount(10) lock = Lock(amount=lock_amount, expiration=expiration, secrethash=secrethash) nonce = Nonce(1) transferred_amount = TokenAmount(0) mediated_transfer = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=nonce, token_network_address=token_network_address, token=token, channel_identifier=channelstate_0_1.identifier, transferred_amount=transferred_amount, locked_amount=LockedAmount(lock_amount), recipient=app1.raiden.address, locksroot=Locksroot(lock.lockhash), lock=lock, target=TargetAddress(app1.raiden.address), initiator=InitiatorAddress(app0.raiden.address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[ RouteMetadata(route=[app0.raiden.address, app1.raiden.address]) ]), ) app0.raiden.sign(mediated_transfer) app1.raiden.on_messages([mediated_transfer]) reveal_secret = RevealSecret(message_identifier=make_message_identifier(), secret=secret, signature=EMPTY_SIGNATURE) app0.raiden.sign(reveal_secret) token_network_address = channelstate_0_1.token_network_address unlock = Unlock( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(mediated_transfer.nonce + 1), token_network_address=token_network_address, channel_identifier=channelstate_0_1.identifier, transferred_amount=TokenAmount(lock_amount), locked_amount=LockedAmount(0), locksroot=LOCKSROOT_OF_NO_LOCKS, secret=secret, signature=EMPTY_SIGNATURE, ) app0.raiden.sign(unlock) messages = [unlock, reveal_secret] receive_method = app1.raiden.on_messages wait = set( gevent.spawn_later(0.1, receive_method, [data]) for data in messages) gevent.joinall(wait, raise_error=True)
def test_receive_secrethashtransfer_unknown( raiden_network: List[RaidenService], token_addresses): app0 = raiden_network[0] token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address) assert token_network_address other_key = HOP1_KEY other_signer = LocalSigner(other_key) canonical_identifier = factories.make_canonical_identifier( token_network_address=token_network_address) amount = TokenAmount(10) locksroot = Locksroot(make_32bytes()) refund_transfer_message = factories.create( factories.RefundTransferProperties( payment_identifier=PaymentID(1), nonce=Nonce(1), token=token_address, canonical_identifier=canonical_identifier, transferred_amount=amount, recipient=TargetAddress(app0.address), locksroot=locksroot, amount=amount, secret=UNIT_SECRET, )) sign_and_inject(refund_transfer_message, other_signer, app0) unlock = Unlock( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), nonce=Nonce(1), channel_identifier=canonical_identifier.channel_identifier, token_network_address=token_network_address, transferred_amount=amount, locked_amount=LockedAmount(0), locksroot=locksroot, secret=UNIT_SECRET, signature=EMPTY_SIGNATURE, ) sign_and_inject(unlock, other_signer, app0) secret_request_message = SecretRequest( message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), secrethash=UNIT_SECRETHASH, amount=PaymentAmount(1), expiration=refund_transfer_message.lock.expiration, signature=EMPTY_SIGNATURE, ) sign_and_inject(secret_request_message, other_signer, app0) reveal_secret_message = RevealSecret( message_identifier=make_message_identifier(), secret=UNIT_SECRET, signature=EMPTY_SIGNATURE) sign_and_inject(reveal_secret_message, other_signer, app0)
def test_encoding_and_decoding(): message_factories = ( factories.LockedTransferProperties(), factories.RefundTransferProperties(), factories.LockExpiredProperties(), factories.UnlockProperties(), ) messages = [factories.create(factory) for factory in message_factories] # TODO Handle these with factories once #5091 is implemented messages.append( Delivered( delivered_message_identifier=factories.make_message_identifier(), signature=factories.make_signature(), )) messages.append( Processed( message_identifier=factories.make_message_identifier(), signature=factories.make_signature(), )) messages.append( RevealSecret( message_identifier=factories.make_message_identifier(), secret=factories.make_secret(), signature=factories.make_signature(), )) messages.append( SecretRequest( message_identifier=factories.make_message_identifier(), payment_identifier=factories.make_payment_id(), secrethash=factories.make_secret_hash(), amount=factories.make_token_amount(), expiration=factories.make_block_number(), signature=factories.make_signature(), )) messages.append( WithdrawRequest( message_identifier=factories.make_message_identifier(), chain_id=factories.make_chain_id(), token_network_address=factories.make_token_network_address(), channel_identifier=factories.make_channel_identifier(), participant=factories.make_address(), total_withdraw=factories.make_token_amount(), nonce=factories.make_nonce(), expiration=factories.make_block_number(), signature=factories.make_signature(), )) messages.append( WithdrawConfirmation( message_identifier=factories.make_message_identifier(), chain_id=factories.make_chain_id(), token_network_address=factories.make_token_network_address(), channel_identifier=factories.make_channel_identifier(), participant=factories.make_address(), total_withdraw=factories.make_token_amount(), nonce=factories.make_nonce(), expiration=factories.make_block_number(), signature=factories.make_signature(), )) messages.append( WithdrawExpired( message_identifier=factories.make_message_identifier(), chain_id=factories.make_chain_id(), token_network_address=factories.make_token_network_address(), channel_identifier=factories.make_channel_identifier(), participant=factories.make_address(), total_withdraw=factories.make_token_amount(), nonce=factories.make_nonce(), expiration=factories.make_block_number(), signature=factories.make_signature(), )) messages.append( PFSCapacityUpdate( canonical_identifier=factories.make_canonical_identifier(), updating_participant=factories.make_address(), other_participant=factories.make_address(), updating_nonce=factories.make_nonce(), other_nonce=factories.make_nonce(), updating_capacity=factories.make_token_amount(), other_capacity=factories.make_token_amount(), reveal_timeout=factories.make_uint64(), signature=factories.make_signature(), )) messages.append( PFSFeeUpdate( canonical_identifier=factories.make_canonical_identifier(), updating_participant=factories.make_address(), fee_schedule=factories.create( factories.FeeScheduleStateProperties()), timestamp=datetime.now(), signature=factories.make_signature(), )) messages.append( RequestMonitoring( reward_amount=factories.make_token_amount(), balance_proof=SignedBlindedBalanceProof. from_balance_proof_signed_state( factories.create( factories.BalanceProofSignedStateProperties())), monitoring_service_contract_address=factories.make_address(), non_closing_participant=factories.make_address(), non_closing_signature=factories.make_signature(), signature=factories.make_signature(), )) for message in messages: serialized = MessageSerializer.serialize(message) deserialized = MessageSerializer.deserialize(serialized) assert deserialized == message
def make_receive_transfer_mediated( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, pending_locks: PendingLocksState = None, locked_amount: Optional[PaymentWithFeeAmount] = None, chain_id: Optional[ChainID] = None, ) -> LockedTransferSignedState: 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: locks = make_empty_pending_locks_state() locks.locks.append(lock.encoded) else: assert bytes(lock.encoded) in pending_locks.locks locks = pending_locks if locked_amount is None: locked_amount = lock.amount assert locked_amount >= lock.amount locksroot = compute_locksroot(locks) payment_identifier = PaymentID(nonce) transfer_target = make_target_address() transfer_initiator = make_initiator_address() chain_id = chain_id or channel_state.chain_id transfer_metadata = Metadata(routes=[ RouteMetadata( route=[channel_state.our_state.address, Address(transfer_target)]) ]) mediated_transfer_msg = LockedTransfer( chain_id=chain_id, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=nonce, token_network_address=channel_state.token_network_address, token=channel_state.token_address, channel_identifier=channel_state.identifier, transferred_amount=transferred_amount, locked_amount=TokenAmount(locked_amount), recipient=channel_state.partner_state.address, locksroot=locksroot, lock=Lock(amount=lock.amount, expiration=lock.expiration, secrethash=lock.secrethash), target=transfer_target, initiator=transfer_initiator, signature=EMPTY_SIGNATURE, fee=0, metadata=transfer_metadata, ) mediated_transfer_msg.sign(signer) receive_lockedtransfer = LockedTransferSignedState( payment_identifier=payment_identifier, token=channel_state.token_address, lock=lock, initiator=transfer_initiator, target=transfer_target, message_identifier=make_message_identifier(), balance_proof=balanceproof_from_envelope(mediated_transfer_msg), routes=[ route_metadata.route for route_metadata in transfer_metadata.routes ], ) return receive_lockedtransfer
def test_initiator_skips_used_routes(): defaults = factories.NettingChannelStateProperties( our_state=factories.NettingChannelEndStateProperties.OUR_STATE, partner_state=factories.NettingChannelEndStateProperties(balance=10), 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)) ] test_chain_state = factories.make_chain_state(number_of_channels=1, properties=properties, defaults=defaults) channels = test_chain_state.channel_set bob = channels.channels[0].partner_state.address routes = [[ factories.UNIT_OUR_ADDRESS, bob, factories.UNIT_TRANSFER_TARGET ]] transfer = factories.create( factories.TransferDescriptionProperties( initiator=factories.UNIT_OUR_ADDRESS, target=factories.UNIT_TRANSFER_TARGET)) init_action = factories.initiator_make_init_action( channels=channels, routes=routes, transfer=transfer, estimated_fee=FeeAmount(0)) transition_result = handle_action_init_initiator( chain_state=test_chain_state.chain_state, state_change=init_action) chain_state = transition_result.new_state assert transfer.secrethash in chain_state.payment_mapping.secrethashes_to_task initiator_task = chain_state.payment_mapping.secrethashes_to_task[ transfer.secrethash] initiator_state = initiator_task.manager_state assert len(initiator_state.routes) == 1, "Should have one route" assert len( initiator_state.routes[0].route) == 3, "Route should not be pruned" assert initiator_state.routes[0].route == routes[ 0], "Should have test route" events = transition_result.events assert isinstance(events[-1], SendLockedTransfer) locked_transfer = initiator_state.initiator_transfers[ transfer.secrethash].transfer received_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=locked_transfer.lock.expiration, payment_identifier=locked_transfer.payment_identifier, canonical_identifier=locked_transfer.balance_proof. canonical_identifier, initiator=factories.UNIT_OUR_ADDRESS, sender=bob, pkey=factories.HOP1_KEY, message_identifier=factories.make_message_identifier(), routes=[], secret=transfer.secret, )) role = views.get_transfer_role(chain_state=chain_state, secrethash=locked_transfer.lock.secrethash) assert role == "initiator", "Should keep initiator role" failed_route_state_change = ReceiveTransferCancelRoute( transfer=received_transfer, balance_proof=received_transfer.balance_proof, sender=received_transfer.balance_proof.sender, # pylint: disable=no-member ) state_transition(chain_state=chain_state, state_change=failed_route_state_change) reroute_state_change = ActionTransferReroute( transfer=received_transfer, balance_proof=received_transfer.balance_proof, sender=received_transfer.balance_proof.sender, # pylint: disable=no-member secret=factories.make_secret(), ) iteration = state_transition(chain_state=chain_state, state_change=reroute_state_change) assert search_for_item(iteration.events, SendLockedTransfer, {}) is None
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