def make_transfer_pair(payer, payee, initiator, target, amount, expiration, secret=None, reveal_timeout=factories.UNIT_REVEAL_TIMEOUT): payer_expiration = expiration payee_expiration = expiration - reveal_timeout return MediationPairState( factories.make_route(payer, amount), factories.make_transfer(amount, initiator, target, payer_expiration, secret=secret), factories.make_route(payee, amount), factories.make_transfer(amount, initiator, target, payee_expiration, secret=secret), )
def next_transfer_pair(payer_route, payer_transfer, routes_state, timeout_blocks, block_number): """ Given a payer transfer tries a new route to proceed with the mediation. Args: payer_route (RouteState): The previous route in the path that provides the token for the mediation. payer_transfer (LockedTransferState): The transfer received from the payer_route. routes_state (RoutesState): Current available routes that may be used, it's assumed that the available_routes list is ordered from best to worst. timeout_blocks (int): Base number of available blocks used to compute the lock timeout. block_number (int): The current block number. """ assert timeout_blocks > 0 assert timeout_blocks <= payer_transfer.expiration - block_number transfer_pair = None mediated_events = list() payee_route = next_route( routes_state, timeout_blocks, payer_transfer.amount, ) if payee_route: assert payee_route.reveal_timeout < timeout_blocks lock_timeout = timeout_blocks - payee_route.reveal_timeout lock_expiration = lock_timeout + block_number payee_transfer = LockedTransferState( payer_transfer.identifier, payer_transfer.amount, payer_transfer.token, payer_transfer.initiator, payer_transfer.target, lock_expiration, payer_transfer.hashlock, payer_transfer.secret, ) transfer_pair = MediationPairState( payer_route, payer_transfer, payee_route, payee_transfer, ) mediated_events = [ mediatedtransfer(payee_transfer, payee_route.node_address), ] return ( transfer_pair, mediated_events, )
def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = 11 payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=1, locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash initiator = payee_transfer.initiator initiator_channel = factories.create( factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( address=initiator, balance=100))) routes = [factories.make_route_from_channel(initiator_channel)] transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=routes) transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1, ) secret2 = factories.make_secret(2) locked_amount2 = 13 transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=2, locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=routes) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2, ) payment_mapping = {secrethash1: task1, secrethash2: task2} view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = TokenAmount(11) payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=PaymentID(1), locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash route_state = RouteState(route=[payee_transfer.target]) transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=[route_state]) # pylint: disable=E1101 transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1) secret2 = factories.make_secret(2) locked_amount2 = TokenAmount(13) transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=PaymentID(2), locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=[route_state]) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2) payment_mapping = { secrethash1: cast(TransferTask, task1), secrethash2: cast(TransferTask, task2), } view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
def next_transfer_pair( payer_transfer: LockedTransferSignedState, available_routes: List['RouteState'], channelidentifiers_to_channels: Dict, pseudo_random_generator: random.Random, block_number: typing.BlockNumber, ): """ Given a payer transfer tries a new route to proceed with the mediation. Args: payer_transfer: The transfer received from the payer_channel. available_routes: Current available routes that may be used, it's assumed that the routes list is ordered from best to worst. channelidentifiers_to_channels: All the channels available for this transfer. pseudo_random_generator: Number generator to generate a message id. block_number: The current block number. """ transfer_pair = None mediated_events = list() lock_timeout = payer_transfer.lock.expiration - block_number payee_channel = next_channel_from_routes( available_routes, channelidentifiers_to_channels, payer_transfer.lock.amount, lock_timeout, ) if payee_channel: assert payee_channel.settle_timeout >= lock_timeout assert payee_channel.token_address == payer_transfer.token message_identifier = message_identifier_from_prng( pseudo_random_generator) lockedtransfer_event = channel.send_lockedtransfer( payee_channel, payer_transfer.initiator, payer_transfer.target, payer_transfer.lock.amount, message_identifier, payer_transfer.payment_identifier, payer_transfer.lock.expiration, payer_transfer.lock.secrethash, ) assert lockedtransfer_event transfer_pair = MediationPairState( payer_transfer, payee_channel.partner_state.address, lockedtransfer_event.transfer, ) mediated_events = [lockedtransfer_event] return ( transfer_pair, mediated_events, )
def next_transfer_pair(registry_address: typing.Address, payer_transfer: LockedTransferSignedState, available_routes: List['RouteState'], channelidentifiers_to_channels: Dict, pseudo_random_generator: random.Random, timeout_blocks: int, block_number: int): """ Given a payer transfer tries a new route to proceed with the mediation. Args: payer_transfer: The transfer received from the payer_channel. routes: Current available routes that may be used, it's assumed that the routes list is ordered from best to worst. timeout_blocks: Base number of available blocks used to compute the lock timeout. block_number: The current block number. """ assert timeout_blocks > 0 assert timeout_blocks <= payer_transfer.lock.expiration - block_number transfer_pair = None mediated_events = list() payee_channel = next_channel_from_routes( available_routes, channelidentifiers_to_channels, payer_transfer.lock.amount, timeout_blocks, ) if payee_channel: assert payee_channel.reveal_timeout < timeout_blocks assert payee_channel.token_address == payer_transfer.token lock_timeout = timeout_blocks - payee_channel.reveal_timeout lock_expiration = lock_timeout + block_number message_identifier = message_identifier_from_prng( pseudo_random_generator) lockedtransfer_event = channel.send_lockedtransfer( registry_address, payee_channel, payer_transfer.initiator, payer_transfer.target, payer_transfer.lock.amount, message_identifier, payer_transfer.payment_identifier, lock_expiration, payer_transfer.lock.secrethash) assert lockedtransfer_event transfer_pair = MediationPairState( payer_transfer, payee_channel.partner_state.address, lockedtransfer_event.transfer, ) mediated_events = [lockedtransfer_event] return ( transfer_pair, mediated_events, )
def backward_transfer_pair( backward_channel: NettingChannelState, payer_transfer: LockedTransferSignedState, pseudo_random_generator: random.Random, block_number: typing.BlockNumber, ) -> typing.Tuple[typing.Optional[MediationPairState], typing.List[Event]]: """ Sends a transfer backwards, allowing the previous hop to try a new route. When all the routes available for this node failed, send a transfer backwards with the same amount and secrethash, allowing the previous hop to do a retry. Args: backward_channel: The original channel which sent the mediated transfer to this node. payer_transfer: The *latest* payer transfer which is backing the mediation. block_number: The current block number. Returns: The mediator pair and the correspoding refund event. """ transfer_pair = None events = list() lock = payer_transfer.lock lock_timeout = lock.expiration - block_number # Ensure the refund transfer's lock has a safe expiration, otherwise don't # do anything and wait for the received lock to expire. if is_channel_usable(backward_channel, lock.amount, lock_timeout): message_identifier = message_identifier_from_prng(pseudo_random_generator) refund_transfer = channel.send_refundtransfer( channel_state=backward_channel, initiator=payer_transfer.initiator, target=payer_transfer.target, amount=lock.amount, message_identifier=message_identifier, payment_identifier=payer_transfer.payment_identifier, expiration=lock.expiration, secrethash=lock.secrethash, ) transfer_pair = MediationPairState( payer_transfer, backward_channel.partner_state.address, refund_transfer.transfer, ) events.append(refund_transfer) return (transfer_pair, events)
def test_invalid_instantiation_mediation_pair_state(): valid = MediationPairState( payer_transfer=factories.create(factories.LockedTransferSignedStateProperties()), payee_address=factories.make_address(), payee_transfer=factories.create(factories.LockedTransferUnsignedStateProperties()), ) unsigned_transfer = factories.create(factories.LockedTransferUnsignedStateProperties()) with pytest.raises(ValueError): replace(valid, payer_transfer=unsigned_transfer) signed_transfer = factories.create(factories.LockedTransferSignedStateProperties()) with pytest.raises(ValueError): replace(valid, payee_transfer=signed_transfer) hex_instead_of_binary = factories.make_checksum_address() with pytest.raises(ValueError): replace(valid, payee_address=hex_instead_of_binary)
def make_transfers_pair(number_of_channels: int, amount: int = UNIT_TRANSFER_AMOUNT, block_number: int = 5) -> MediatorTransfersPair: deposit = 5 * amount defaults = create_properties( NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=deposit), partner_state=NettingChannelEndStateProperties(balance=deposit), open_transaction=TransactionExecutionStatusProperties( finished_block_number=10), )) properties_list = [ NettingChannelStateProperties( canonical_identifier=make_canonical_identifier( channel_identifier=i), our_state=NettingChannelEndStateProperties( address=ChannelSet.ADDRESSES[0], privatekey=ChannelSet.PKEYS[0]), partner_state=NettingChannelEndStateProperties( address=ChannelSet.ADDRESSES[i + 1], privatekey=ChannelSet.PKEYS[i + 1]), ) for i in range(number_of_channels) ] channels = make_channel_set(properties_list, defaults) lock_expiration = block_number + UNIT_REVEAL_TIMEOUT * 2 pseudo_random_generator = random.Random() transfers_pairs = list() for payer_index in range(number_of_channels - 1): payee_index = payer_index + 1 receiver_channel = channels[payer_index] received_transfer = create( LockedTransferSignedStateProperties( amount=amount, expiration=lock_expiration, payment_identifier=UNIT_TRANSFER_IDENTIFIER, canonical_identifier=receiver_channel.canonical_identifier, sender=channels.partner_address(payer_index), pkey=channels.partner_privatekeys[payer_index], )) is_valid, _, msg = channel.handle_receive_lockedtransfer( receiver_channel, received_transfer) assert is_valid, msg message_identifier = message_identifier_from_prng( pseudo_random_generator) lockedtransfer_event = channel.send_lockedtransfer( channel_state=channels[payee_index], initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, amount=amount, message_identifier=message_identifier, payment_identifier=UNIT_TRANSFER_IDENTIFIER, expiration=lock_expiration, secrethash=UNIT_SECRETHASH, ) assert lockedtransfer_event lock_timeout = lock_expiration - block_number assert mediator.is_channel_usable( candidate_channel_state=channels[payee_index], transfer_amount=amount, lock_timeout=lock_timeout, ) sent_transfer = lockedtransfer_event.transfer pair = MediationPairState(received_transfer, lockedtransfer_event.recipient, sent_transfer) transfers_pairs.append(pair) return MediatorTransfersPair( channels=channels, transfers_pair=transfers_pairs, amount=amount, block_number=block_number, block_hash=make_block_hash(), )
def make_transfers_pair(privatekeys, amount, block_number): transfers_pair = list() channel_map = dict() pseudo_random_generator = random.Random() addresses = list() for pkey in privatekeys: pubkey = pkey.public_key.format(compressed=False) address = publickey_to_address(pubkey) addresses.append(address) key_address = list(zip(privatekeys, addresses)) deposit_amount = amount * 5 channels_state = { address: make_channel( our_address=HOP1, our_balance=deposit_amount, partner_balance=deposit_amount, partner_address=address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) for address in addresses } lock_expiration = block_number + UNIT_REVEAL_TIMEOUT * 2 for (payer_key, payer_address), payee_address in zip(key_address[:-1], addresses[1:]): pay_channel = channels_state[payee_address] receive_channel = channels_state[payer_address] received_transfer = make_signed_transfer( amount=amount, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, expiration=lock_expiration, secret=UNIT_SECRET, payment_identifier=UNIT_TRANSFER_IDENTIFIER, channel_identifier=receive_channel.identifier, pkey=payer_key, sender=payer_address, ) is_valid, _, msg = channel.handle_receive_lockedtransfer( receive_channel, received_transfer, ) assert is_valid, msg message_identifier = message_identifier_from_prng( pseudo_random_generator) lockedtransfer_event = channel.send_lockedtransfer( channel_state=pay_channel, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, amount=amount, message_identifier=message_identifier, payment_identifier=UNIT_TRANSFER_IDENTIFIER, expiration=lock_expiration, secrethash=UNIT_SECRETHASH, ) assert lockedtransfer_event lock_timeout = lock_expiration - block_number assert mediator.is_channel_usable( candidate_channel_state=pay_channel, transfer_amount=amount, lock_timeout=lock_timeout, ) sent_transfer = lockedtransfer_event.transfer pair = MediationPairState( received_transfer, lockedtransfer_event.recipient, sent_transfer, ) transfers_pair.append(pair) channel_map[receive_channel.identifier] = receive_channel channel_map[pay_channel.identifier] = pay_channel assert channel.is_lock_locked(receive_channel.partner_state, UNIT_SECRETHASH) assert channel.is_lock_locked(pay_channel.our_state, UNIT_SECRETHASH) return channel_map, transfers_pair