def make_receive_transfer_direct( payment_network_identifier, channel_state, privkey, nonce, transferred_amount, locksroot=EMPTY_MERKLE_ROOT): 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.') identifier = nonce mediated_transfer_msg = DirectTransfer( identifier, nonce, channel_state.token_address, channel_state.identifier, transferred_amount, channel_state.partner_state.address, locksroot, ) mediated_transfer_msg.sign(privkey, address) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_directtransfer = ReceiveTransferDirect( payment_network_identifier, channel_state.token_address, identifier, balance_proof, ) return receive_directtransfer
def claim_lock(app_chain, identifier, token, secret): """ Unlock a pending transfer. """ secrethash = sha3(secret) for from_, to_ in zip(app_chain[:-1], app_chain[1:]): from_channel = get_channelstate(from_, to_, token) partner_channel = get_channelstate(to_, from_, token) unlock_lock = channel.send_unlock( from_channel, identifier, secret, secrethash, ) secret_message = Secret( unlock_lock.identifier, unlock_lock.balance_proof.nonce, unlock_lock.balance_proof.channel_address, unlock_lock.balance_proof.transferred_amount, unlock_lock.balance_proof.locksroot, unlock_lock.secret, ) from_.raiden.sign(secret_message) balance_proof = balanceproof_from_envelope(secret_message) receive_unlock = ReceiveUnlock( unlock_lock.secret, balance_proof, ) is_valid, msg = channel.handle_unlock( partner_channel, receive_unlock, ) assert is_valid, msg
def handle_message_secret(raiden: 'RaidenService', message: Secret): balance_proof = balanceproof_from_envelope(message) state_change = ReceiveUnlock( message.secret, balance_proof, ) raiden.handle_state_change(state_change)
def handle_message_secret(raiden: RaidenService, message: Secret): balance_proof = balanceproof_from_envelope(message) state_change = ReceiveUnlock( message_identifier=message.message_identifier, secret=message.secret, balance_proof=balance_proof, ) raiden.handle_state_change(state_change)
def handle_message_secret(self, raiden: RaidenService, message: Secret): balance_proof = balanceproof_from_envelope(message) state_change = ReceiveUnlock( message_identifier=message.message_identifier, secret=message.secret, balance_proof=balance_proof, ) raiden.handle_state_change(state_change)
def handle_message_unlock(raiden: RaidenService, message: Unlock) -> None: balance_proof = balanceproof_from_envelope(message) state_change = ReceiveUnlock( message_identifier=message.message_identifier, secret=message.secret, balance_proof=balance_proof, ) raiden.handle_and_track_state_change(state_change)
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 make_mediated_transfer(registry_address, from_channel, partner_channel, initiator, target, lock, pkey, secret=None): """ Helper to create and register a mediated transfer from `from_channel` to `partner_channel`.""" payment_identifier = channel.get_next_nonce(from_channel.our_state) message_identifier = random.randint(0, UINT64_MAX) lockedtransfer = channel.send_lockedtransfer( registry_address, from_channel, initiator, target, lock.amount, message_identifier, payment_identifier, lock.expiration, lock.secrethash, ) mediated_transfer_msg = LockedTransfer.from_event(lockedtransfer) address = privatekey_to_address(pkey) sign_key = PrivateKey(pkey) mediated_transfer_msg.sign(sign_key, address) # compute the signature balance_proof = balanceproof_from_envelope(mediated_transfer_msg) lockedtransfer.balance_proof = balance_proof # if this fails it's not the right key for the current `from_channel` assert mediated_transfer_msg.sender == from_channel.our_state.address receive_lockedtransfer = lockedtransfersigned_from_message( mediated_transfer_msg) channel.handle_receive_lockedtransfer( partner_channel, receive_lockedtransfer, ) if secret is not None: random_sender = make_address() from_secretreveal = ReceiveSecretReveal(secret, random_sender) channel.handle_receive_secretreveal(from_channel, from_secretreveal) partner_secretreveal = ReceiveSecretReveal(secret, random_sender) channel.handle_receive_secretreveal(partner_channel, partner_secretreveal) return mediated_transfer_msg
def make_receive_transfer_mediated( channel_state, privkey, nonce, transferred_amount, lock, merkletree_leaves=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 = [[lock.lockhash]] else: assert lock.lockhash in merkletree_leaves layers = compute_layers(merkletree_leaves) locksroot = layers[MERKLEROOT][0] message_identifier = random.randint(0, UINT64_MAX) payment_identifier = nonce transfer_target = factories.make_address() transfer_initiator = factories.make_address() mediated_transfer_msg = LockedTransfer( message_identifier, payment_identifier, nonce, channel_state.token_address, channel_state.identifier, transferred_amount, channel_state.partner_state.address, locksroot, lock, transfer_target, transfer_initiator, ) mediated_transfer_msg.sign(privkey, address) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_lockedtransfer = LockedTransferSignedState( payment_identifier, channel_state.token_address, balance_proof, lock, transfer_initiator, transfer_target, ) return receive_lockedtransfer
def handle_message_directtransfer(raiden: RaidenService, message: DirectTransfer): token_network_identifier = message.token_network_address balance_proof = balanceproof_from_envelope(message) direct_transfer = ReceiveTransferDirect( token_network_identifier, message.message_identifier, message.payment_identifier, balance_proof, ) raiden.handle_state_change(direct_transfer)
def test_update_non_closing_balance_proof(): dict_data = { "type": "Secret", "chain_id": 33, "message_identifier": 4174357123961474742, "payment_identifier": 5100335212362582814, "secret": "0xd1b2cb5b175436f60b6e59be64f4c7b59b3569b8f877c55f66c8f8a6ba8055f4", "nonce": 2, "token_network_address": "0x013b47e5eb40a476dc0e9a212d376899288561a2", "channel_identifier": 14, "transferred_amount": 20000000, "locked_amount": 0, "locksroot": "0x0000000000000000000000000000000000000000000000000000000000000000", "signature": "0x94d6dba985096b6259151664367443bcd83c5e8cc1913c34bd3542b4ac1b4e7772696e145445625eef4167080fddb3ebe730c71319bee66235864661d9dddc2b1c" } # dict_data = {"type": "Secret", "chain_id": 33, "message_identifier": 18237677588114994956, "payment_identifier": 1322351847924173620, "secret": "0xa4678d1f1db376f20854619fc8aa8021f88f318e14ff600aa051e8e4ded5d023", "nonce": 2, "token_network_address": "0x7351ed719de72db92a54c99ef2c4d287f69672a1", "channel_identifier": 3, "transferred_amount": 100000000000000000, "locked_amount": 0, "locksroot": "0x0000000000000000000000000000000000000000000000000000000000000000", "signature": "0x5c805ba51ac4776d879c276d54c1ed97905399e227e7b9ef50aa4f36605ac25e5ab707641c4bd85a0d89549841beaf4f0e06c839ad5460aaf26d4c68b9af822c1b"} balance_proof_msg = Unlock.from_dict(dict_data) balance_proof = balanceproof_from_envelope(balance_proof_msg) non_closing_signature = create_balance_proof_update_signature( "0x013b47e5eb40a476dc0e9a212d376899288561a2", 14, balance_proof.balance_hash, 2, balance_proof.message_hash, decode_hex( "0x94d6dba985096b6259151664367443bcd83c5e8cc1913c34bd3542b4ac1b4e7772696e145445625eef4167080fddb3ebe730c71319bee66235864661d9dddc2b1c" )) our_signed_data = pack_balance_proof_update( nonce=balance_proof.nonce, balance_hash=balance_proof.balance_hash, additional_hash=balance_proof.message_hash, canonical_identifier=balance_proof.canonical_identifier, partner_signature=Signature( decode_hex( "0x94d6dba985096b6259151664367443bcd83c5e8cc1913c34bd3542b4ac1b4e7772696e145445625eef4167080fddb3ebe730c71319bee66235864661d9dddc2b1c" ))) print("Update non consling blanace proof signature " + non_closing_signature.hex()) our_recovered_address = recover(data=our_signed_data, signature=Signature(non_closing_signature)) assert our_recovered_address == to_canonical_address( "0x7ca28d3d760b4aa2b79e8d42cbdc187c7df9af40")
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, 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 handle_message_directtransfer(self, raiden: RaidenService, message: DirectTransfer): token_network_identifier = message.token_network_address balance_proof = balanceproof_from_envelope(message) direct_transfer = ReceiveTransferDirect( token_network_identifier, message.message_identifier, message.payment_identifier, balance_proof, ) raiden.handle_state_change(direct_transfer)
def make_direct_transfer_from_channel( payment_network_identifier, from_channel, partner_channel, amount, pkey, ): """ Helper to create and register a direct transfer from `from_channel` to `partner_channel`.""" payment_identifier = channel.get_next_nonce(from_channel.our_state) pseudo_random_generator = random.Random() state_change = ActionTransferDirect( payment_network_identifier, from_channel.token_address, from_channel.partner_state.address, payment_identifier, amount, ) iteration = channel.handle_send_directtransfer( from_channel, state_change, pseudo_random_generator, ) assert isinstance(iteration.events[0], SendDirectTransfer) direct_transfer_message = DirectTransfer.from_event(iteration.events[0]) address = privatekey_to_address(pkey) sign_key = PrivateKey(pkey) direct_transfer_message.sign(sign_key, address) # if this fails it's not the right key for the current `from_channel` assert direct_transfer_message.sender == from_channel.our_state.address balance_proof = balanceproof_from_envelope(direct_transfer_message) message_identifier = random.randint(0, UINT64_MAX) receive_direct = ReceiveTransferDirect( payment_network_identifier, from_channel.token_address, message_identifier, payment_identifier, balance_proof, ) channel.handle_receive_directtransfer( partner_channel, receive_direct, ) return direct_transfer_message
def handle_message_directtransfer(raiden: 'RaidenService', message: DirectTransfer): payment_network_identifier = raiden.default_registry.address token_address = message.token balance_proof = balanceproof_from_envelope(message) direct_transfer = ReceiveTransferDirect( payment_network_identifier, token_address, message.payment_identifier, balance_proof, ) raiden.handle_state_change(direct_transfer)
def increase_transferred_amount( payment_network_identifier, from_channel, partner_channel, amount, pkey, ): # increasing the transferred amount by a value larger than distributable # would put one end of the channel in a negative balance, which is forbidden distributable_from_to = channel.get_distributable( from_channel.our_state, from_channel.partner_state, ) assert distributable_from_to >= amount, 'operation would end up in a incosistent state' message_identifier = random.randint(0, UINT64_MAX) payment_identifier = 1 registry_address = make_address() event = channel.send_directtransfer( registry_address, from_channel, amount, payment_identifier, message_identifier, ) direct_transfer_message = DirectTransfer.from_event(event) address = privatekey_to_address(pkey) sign_key = PrivateKey(pkey) direct_transfer_message.sign(sign_key, address) # if this fails it's not the right key for the current `from_channel` assert direct_transfer_message.sender == from_channel.our_state.address balance_proof = balanceproof_from_envelope(direct_transfer_message) receive_direct = ReceiveTransferDirect( payment_network_identifier, from_channel.token_address, message_identifier, payment_identifier, balance_proof, ) channel.handle_receive_directtransfer( partner_channel, receive_direct, ) return direct_transfer_message
def make_mediated_transfer( from_channel, partner_channel, initiator, target, lock, pkey, secret=None, ): """ Helper to create and register a mediated transfer from `from_channel` to `partner_channel`.""" payment_identifier = channel.get_next_nonce(from_channel.our_state) message_identifier = random.randint(0, UINT64_MAX) lockedtransfer = channel.send_lockedtransfer( from_channel, initiator, target, lock.amount, message_identifier, payment_identifier, lock.expiration, lock.secrethash, ) mediated_transfer_msg = LockedTransfer.from_event(lockedtransfer) sign_key = PrivateKey(pkey) mediated_transfer_msg.sign(sign_key) # compute the signature balance_proof = balanceproof_from_envelope(mediated_transfer_msg) lockedtransfer.balance_proof = balance_proof # if this fails it's not the right key for the current `from_channel` assert mediated_transfer_msg.sender == from_channel.our_state.address receive_lockedtransfer = lockedtransfersigned_from_message(mediated_transfer_msg) channel.handle_receive_lockedtransfer( partner_channel, receive_lockedtransfer, ) if secret is not None: secrethash = sha3(secret) channel.register_secret(from_channel, secret, secrethash) channel.register_secret(partner_channel, secret, secrethash) return mediated_transfer_msg
def make_mediated_transfer( from_channel, partner_channel, initiator, target, lock, pkey, secret=None, ): """ Helper to create and register a mediated transfer from `from_channel` to `partner_channel`.""" payment_identifier = channel.get_next_nonce(from_channel.our_state) message_identifier = random.randint(0, UINT64_MAX) lockedtransfer = channel.send_lockedtransfer( from_channel, initiator, target, lock.amount, message_identifier, payment_identifier, lock.expiration, lock.secrethash, ) mediated_transfer_msg = LockedTransfer.from_event(lockedtransfer) sign_key = PrivateKey(pkey) mediated_transfer_msg.sign(sign_key, NETWORKNAME_TO_ID[TESTS]) # compute the signature balance_proof = balanceproof_from_envelope(mediated_transfer_msg) lockedtransfer.balance_proof = balance_proof # if this fails it's not the right key for the current `from_channel` assert mediated_transfer_msg.sender == from_channel.our_state.address receive_lockedtransfer = lockedtransfersigned_from_message(mediated_transfer_msg) channel.handle_receive_lockedtransfer( partner_channel, receive_lockedtransfer, ) if secret is not None: secrethash = sha3(secret) channel.register_secret(from_channel, secret, secrethash) channel.register_secret(partner_channel, secret, secrethash) return mediated_transfer_msg
def make_receive_transfer_direct( payment_network_identifier, channel_state, privkey, nonce, transferred_amount, locksroot=EMPTY_MERKLE_ROOT, registry_address=UNIT_REGISTRY_IDENTIFIER, locked_amount=None, ): 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 locked_amount is None: locked_amount = channel.get_amount_locked(channel_state.our_state) message_identifier = random.randint(0, UINT64_MAX) payment_identifier = nonce mediated_transfer_msg = DirectTransfer( message_identifier, payment_identifier, nonce, registry_address, channel_state.token_address, channel_state.identifier, transferred_amount, locked_amount, channel_state.partner_state.address, locksroot, ) mediated_transfer_msg.sign(privkey, address) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_directtransfer = ReceiveTransferDirect( payment_network_identifier, channel_state.token_address, message_identifier, payment_identifier, balance_proof, ) return receive_directtransfer
def lockedtransfersigned_from_message(message: "LockedTransfer") -> "LockedTransferSignedState": """ Create LockedTransferSignedState from a LockedTransfer message. """ balance_proof = balanceproof_from_envelope(message) lock = HashTimeLockState(message.lock.amount, message.lock.expiration, message.lock.secrethash) transfer_state = LockedTransferSignedState( message.message_identifier, message.payment_identifier, message.token, balance_proof, lock, message.initiator, message.target, ) return transfer_state
def handle_message_unlock(raiden: RaidenService, message: Unlock, is_light_client=False) -> None: balance_proof = balanceproof_from_envelope(message) if is_light_client: state_change = ReceiveUnlockLight( message_identifier=message.message_identifier, secret=message.secret, balance_proof=balance_proof, signed_unlock=message) raiden.handle_and_track_state_change(state_change) else: state_change = ReceiveUnlock( message_identifier=message.message_identifier, secret=message.secret, balance_proof=balance_proof, ) raiden.handle_and_track_state_change(state_change)
def claim_lock(app_chain, payment_identifier, token_network_identifier, secret): """ Unlock a pending transfer. """ secrethash = sha3(secret) for from_, to_ in zip(app_chain[:-1], app_chain[1:]): from_channel = get_channelstate(from_, to_, token_network_identifier) partner_channel = get_channelstate(to_, from_, token_network_identifier) unlock_lock = channel.send_unlock( from_channel, random.randint(0, UINT64_MAX), payment_identifier, secret, secrethash, ) secret_message = Secret( chain_id=unlock_lock.balance_proof.chain_id, message_identifier=unlock_lock.message_identifier, payment_identifier=unlock_lock.payment_identifier, nonce=unlock_lock.balance_proof.nonce, token_network_address=partner_channel.token_network_identifier, channel_identifier=unlock_lock.balance_proof.channel_identifier, transferred_amount=unlock_lock.balance_proof.transferred_amount, locked_amount=unlock_lock.balance_proof.locked_amount, locksroot=unlock_lock.balance_proof.locksroot, secret=unlock_lock.secret, ) from_.raiden.sign(secret_message) balance_proof = balanceproof_from_envelope(secret_message) receive_unlock = ReceiveUnlock( message_identifier=random.randint(0, UINT64_MAX), secret=unlock_lock.secret, balance_proof=balance_proof, ) is_valid, _, msg = channel.handle_unlock( partner_channel, receive_unlock, ) assert is_valid, msg
def claim_lock(app_chain, payment_identifier, token_network_identifier, secret): """ Unlock a pending transfer. """ secrethash = sha3(secret) for from_, to_ in zip(app_chain[:-1], app_chain[1:]): from_channel = get_channelstate(from_, to_, token_network_identifier) partner_channel = get_channelstate(to_, from_, token_network_identifier) unlock_lock = channel.send_unlock( from_channel, random.randint(0, UINT64_MAX), payment_identifier, secret, secrethash, ) secret_message = Secret( unlock_lock.balance_proof.chain_id, unlock_lock.message_identifier, unlock_lock.payment_identifier, unlock_lock.balance_proof.nonce, partner_channel.token_network_identifier, unlock_lock.balance_proof.channel_address, unlock_lock.balance_proof.transferred_amount, unlock_lock.balance_proof.locked_amount, unlock_lock.balance_proof.locksroot, unlock_lock.secret, ) from_.raiden.sign(secret_message) balance_proof = balanceproof_from_envelope(secret_message) receive_unlock = ReceiveUnlock( message_identifier=random.randint(0, UINT64_MAX), secret=unlock_lock.secret, balance_proof=balance_proof, ) is_valid, _, msg = channel.handle_unlock( partner_channel, receive_unlock, ) assert is_valid, msg
def lockedtransfersigned_from_message(message): """ Create LockedTransferSignedState from a LockedTransfer message. """ balance_proof = balanceproof_from_envelope(message) lock = HashTimeLockState( message.lock.amount, message.lock.expiration, message.lock.secrethash, ) transfer_state = LockedTransferSignedState( message.message_identifier, message.payment_identifier, message.token, balance_proof, lock, message.initiator, message.target, ) return transfer_state
def make_balance_proof( signer: Signer = None, message_identifier=None, payment_identifier=0, nonce=1, token_network_addresss=ADDRESS, token=ADDRESS, channel_identifier=UNIT_CHANNEL_ID, transferred_amount=0, locked_amount=None, amount=1, expiration=1, locksroot=EMPTY_MERKLE_ROOT, recipient=ADDRESS, target=ADDRESS, initiator=ADDRESS, fee=0, ): mediated_transfer = make_mediated_transfer( message_identifier=message_identifier, payment_identifier=payment_identifier, nonce=nonce, token_network_addresss=token_network_addresss, token=token, channel_identifier=channel_identifier, transferred_amount=transferred_amount, locked_amount=locked_amount, amount=amount, expiration=expiration, locksroot=locksroot, recipient=recipient, target=target, initiator=initiator, fee=fee, ) mediated_transfer.sign(signer) balance_proof = balanceproof_from_envelope(mediated_transfer) return balance_proof
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 = 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_regression_onchain_secret_reveal_must_update_channel_state(): """ If a secret is learned off-chain and then on-chain, the state of the lock must be updated in the channel. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(2, block_number=10) mediator_state = MediatorTransferState(UNIT_SECRETHASH) mediator_state.transfers_pair = setup.transfers_pair secret = UNIT_SECRET secrethash = UNIT_SECRETHASH payer_channel = mediator.get_payer_channel(setup.channel_map, setup.transfers_pair[0]) payee_channel = mediator.get_payee_channel(setup.channel_map, setup.transfers_pair[0]) lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveSecretReveal(secret, payee_channel.partner_state.address), channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_unlockedlocks secret_registry_address = factories.make_address() transaction_hash = factories.make_address() mediator.state_transition( mediator_state=mediator_state, state_change=ContractReceiveSecretReveal( transaction_hash, secret_registry_address, secrethash, secret, setup.block_number, ), channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=payer_channel.partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=payer_channel.chain_id, token_network_identifier=payer_channel.token_network_identifier, channel_identifier=payer_channel.identifier, recipient=payer_channel.our_state.address, ) assert send_lock_expired expired_message = message_from_sendevent(send_lock_expired, setup.channels.our_address(0)) expired_message.sign(setup.channels.partner_privatekeys[0]) balance_proof = balanceproof_from_envelope(expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = channel.get_sender_expiration_threshold(lock) mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks
def test_interwoven_transfers(): """Can keep doing transactions even if not all secrets have been released.""" number_of_transfers = 100 balance_for_all_transfers = 11 * number_of_transfers lock_amounts = cycle([1, 3, 5, 7, 11]) lock_secrets = [ format(i, '>032').encode() for i in range(number_of_transfers) ] our_model, _ = create_model(70) partner_model, privkey2 = create_model(balance_for_all_transfers) channel_state = create_channel_from_models(our_model, partner_model) block_number = 1000 nonce = 0 transferred_amount = 0 our_model_current = our_model partner_model_current = partner_model for i, (lock_amount, lock_secret) in enumerate(zip(lock_amounts, lock_secrets)): nonce += 1 block_number += 1 lock_expiration = block_number + channel_state.settle_timeout - 1 lock_secrethash = sha3(lock_secret) lock = HashTimeLockState( lock_amount, lock_expiration, lock_secrethash, ) merkletree_leaves = list(partner_model_current.merkletree_leaves) merkletree_leaves.append(lock.lockhash) partner_model_current = partner_model_current._replace( distributable=partner_model_current.distributable - lock_amount, amount_locked=partner_model_current.amount_locked + lock_amount, next_nonce=partner_model_current.next_nonce + 1, merkletree_leaves=merkletree_leaves, ) receive_lockedtransfer = make_receive_transfer_mediated( channel_state, privkey2, nonce, transferred_amount, lock, merkletree_leaves=merkletree_leaves, ) is_valid, msg = channel.handle_receive_lockedtransfer( channel_state, receive_lockedtransfer, ) assert is_valid, msg assert_partner_state( channel_state.our_state, channel_state.partner_state, our_model_current, ) assert_partner_state( channel_state.partner_state, channel_state.our_state, partner_model_current, ) # claim a transaction at every other iteration, leaving the current one # in place if i % 2: # Update our model: # - Increase nonce because the secret is a new balance proof # - The lock is removed from the merkle tree, the balance proof must be updated # - The locksroot must have unlocked lock removed # - The transferred amount must be increased by the lock amount # - This changes the balance for both participants: # - the sender balance and locked amount is decremented by the lock amount # - the receiver balance and distributable is incremented by the lock amount nonce += 1 transferred_amount += lock_amount merkletree_leaves = list(partner_model_current.merkletree_leaves) merkletree_leaves.remove(lock.lockhash) tree = compute_layers(merkletree_leaves) locksroot = tree[MERKLEROOT][0] partner_model_current = partner_model_current._replace( amount_locked=partner_model_current.amount_locked - lock_amount, balance=partner_model_current.balance - lock_amount, next_nonce=partner_model_current.next_nonce + 1, merkletree_leaves=merkletree_leaves, ) our_model_current = our_model_current._replace( balance=our_model_current.balance + lock_amount, distributable=our_model_current.distributable + lock_amount, ) message_identifier = random.randint(0, UINT64_MAX) secret_message = Secret( message_identifier=message_identifier, payment_identifier=nonce, nonce=nonce, channel=channel_state.identifier, transferred_amount=transferred_amount, locksroot=locksroot, secret=lock_secret, ) secret_message.sign(privkey2, channel_state.partner_state.address) balance_proof = balanceproof_from_envelope(secret_message) unlock_state_change = ReceiveUnlock( lock_secret, balance_proof, ) is_valid, msg = channel.handle_unlock(channel_state, unlock_state_change) assert is_valid, msg assert_partner_state( channel_state.our_state, channel_state.partner_state, our_model_current, ) assert_partner_state( channel_state.partner_state, channel_state.our_state, partner_model_current, )
def test_channelstate_receive_lockedtransfer(): """Tests receiving a mediated transfer. The transfer is done in three steps: - a mediated transfer including a lock in its balance proof is sent - the secret is revealed - the unlocked balance proof is sent updating the transferred_amount """ our_model1, _ = create_model(70) partner_model1, privkey2 = create_model(100) channel_state = create_channel_from_models(our_model1, partner_model1) # Step 1: Simulate receiving a transfer # - The receiver end state doesnt change # - The lock must be registered with the sender end lock_amount = 30 lock_expiration = 10 lock_secret = sha3(b'test_end_state') lock_secrethash = sha3(lock_secret) lock = HashTimeLockState( lock_amount, lock_expiration, lock_secrethash, ) nonce = 1 transferred_amount = 0 receive_lockedtransfer = make_receive_transfer_mediated( channel_state, privkey2, nonce, transferred_amount, lock, ) is_valid, msg = channel.handle_receive_lockedtransfer( channel_state, receive_lockedtransfer, ) assert is_valid, msg our_model2 = our_model1 partner_model2 = partner_model1._replace( distributable=partner_model1.distributable - lock_amount, amount_locked=lock_amount, next_nonce=2, merkletree_leaves=[lock.lockhash], ) assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model2) assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model2) # Step 2: Simulate learning the secret # - Registers the secret, this must not change the balance/locked amount channel.register_secret(channel_state, lock_secret, lock_secrethash) assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model2) assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model2) # Step 3: Simulate unlocking the lock # - Update the balances transferred_amount = 0 message_identifier = random.randint(0, UINT64_MAX) secret_message = Secret( message_identifier=message_identifier, payment_identifier=1, nonce=2, channel=channel_state.identifier, transferred_amount=transferred_amount + lock_amount, locksroot=EMPTY_MERKLE_ROOT, secret=lock_secret, ) secret_message.sign(privkey2, channel_state.partner_state.address) balance_proof = balanceproof_from_envelope(secret_message) unlock_state_change = ReceiveUnlock( lock_secret, balance_proof, ) is_valid, msg = channel.handle_unlock(channel_state, unlock_state_change) assert is_valid, msg our_model3 = our_model2._replace( balance=our_model2.balance + lock_amount, distributable=our_model2.balance + lock_amount, ) partner_model3 = partner_model2._replace( balance=partner_model2.balance - lock_amount, amount_locked=0, next_nonce=3, merkletree_leaves=[], ) assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model3) assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model3)
def make_receive_transfer_mediated( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, merkletree_leaves: List[Keccak256] = None, locked_amount: Optional[LockedAmount] = None, chain_id: Optional[ChainID] = None, ) -> LockedTransferSignedState: 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 = [[lock.lockhash]] else: assert lock.lockhash in merkletree_leaves layers = compute_layers(merkletree_leaves) if locked_amount is None: locked_amount = lock.amount assert locked_amount >= lock.amount locksroot = layers[MERKLEROOT][0] payment_identifier = nonce payment_hash_invoice = make_payment_hash_invoice() transfer_target = make_address() transfer_initiator = make_address() chain_id = chain_id or channel_state.chain_id mediated_transfer_msg = LockedTransfer( chain_id=chain_id, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, payment_hash_invoice=payment_hash_invoice, nonce=nonce, token_network_address=channel_state.token_network_identifier, token=channel_state.token_address, channel_identifier=channel_state.identifier, transferred_amount=transferred_amount, locked_amount=locked_amount, recipient=channel_state.partner_state.address, locksroot=locksroot, lock=lock, target=transfer_target, initiator=transfer_initiator, ) mediated_transfer_msg.sign(signer) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_lockedtransfer = LockedTransferSignedState( random.randint(0, UINT64_MAX), payment_identifier, payment_hash_invoice, channel_state.token_address, balance_proof, lock, transfer_initiator, transfer_target, ) 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 = 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_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 make_receive_transfer_mediated( channel_state, privkey, nonce, transferred_amount, lock, merkletree_leaves=None, token_network_address=UNIT_REGISTRY_IDENTIFIER, locked_amount=None, chain_id=UNIT_CHAIN_ID, ): 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 = [[lock.lockhash]] else: assert lock.lockhash in merkletree_leaves layers = compute_layers(merkletree_leaves) if locked_amount is None: locked_amount = lock.amount assert locked_amount >= lock.amount locksroot = layers[MERKLEROOT][0] payment_identifier = nonce transfer_target = make_address() transfer_initiator = make_address() mediated_transfer_msg = LockedTransfer( chain_id=chain_id, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=nonce, token_network_address=token_network_address, token=channel_state.token_address, channel_identifier=channel_state.identifier, transferred_amount=transferred_amount, locked_amount=locked_amount, recipient=channel_state.partner_state.address, locksroot=locksroot, lock=lock, target=transfer_target, initiator=transfer_initiator, ) mediated_transfer_msg.sign(privkey) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_lockedtransfer = LockedTransferSignedState( random.randint(0, UINT64_MAX), payment_identifier, channel_state.token_address, balance_proof, lock, transfer_initiator, transfer_target, ) return receive_lockedtransfer
def test_regression_onchain_secret_reveal_must_update_channel_state(): """ If a secret is learned off-chain and then on-chain, the state of the lock must be updated in the channel. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(2, block_number=10) mediator_state = MediatorTransferState( secrethash=UNIT_SECRETHASH, routes=setup.channels.get_routes(), ) mediator_state.transfers_pair = setup.transfers_pair secret = UNIT_SECRET secrethash = UNIT_SECRETHASH payer_channel = mediator.get_payer_channel(setup.channel_map, setup.transfers_pair[0]) payee_channel = mediator.get_payee_channel(setup.channel_map, setup.transfers_pair[0]) lock = payer_channel.partner_state.secrethashes_to_lockedlocks[secrethash] mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveSecretReveal(secret, payee_channel.partner_state.address), channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, block_hash=setup.block_hash, ) assert secrethash in payer_channel.partner_state.secrethashes_to_unlockedlocks secret_registry_address = factories.make_address() transaction_hash = factories.make_address() mediator.state_transition( mediator_state=mediator_state, state_change=ContractReceiveSecretReveal( transaction_hash=transaction_hash, secret_registry_address=secret_registry_address, secrethash=secrethash, secret=secret, block_number=setup.block_number, block_hash=setup.block_hash, ), channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, block_hash=setup.block_hash, ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=payer_channel.partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=payer_channel.chain_id, token_network_identifier=payer_channel.token_network_identifier, channel_identifier=payer_channel.identifier, recipient=payer_channel.our_state.address, ) assert send_lock_expired expired_message = message_from_sendevent(send_lock_expired, setup.channels.our_address(0)) expired_message.sign(LocalSigner(setup.channels.partner_privatekeys[0])) balance_proof = balanceproof_from_envelope(expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) expired_block_number = channel.get_sender_expiration_threshold(lock) mediator.state_transition( mediator_state=mediator_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=factories.make_block_hash(), ) assert secrethash in payer_channel.partner_state.secrethashes_to_onchain_unlockedlocks
def test_regression_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