Beispiel #1
0
def is_channel_close_needed(payer_channel, transfer_pair, block_number):
    """ True if this node needs to close the channel to unlock on-chain.
    Only close the channel to unlock on chain if the corresponding payee node
    has received, this prevents attacks were the payee node burns it's payment
    to force a close with the payer channel.
    """
    payee_received = transfer_pair.payee_state in STATE_TRANSFER_PAID
    payer_payed = transfer_pair.payer_state in STATE_TRANSFER_PAID

    payer_channel_open = channel.get_status(payer_channel) == CHANNEL_STATE_OPENED
    already_closing = channel.get_status(payer_channel) == CHANNEL_STATE_CLOSING

    safe_to_wait = is_safe_to_wait(
        transfer_pair.payer_transfer.lock.expiration,
        payer_channel.reveal_timeout,
        block_number,
    )

    return (
        payee_received and
        not payer_payed and

        payer_channel_open and
        not already_closing and
        not safe_to_wait
    )
Beispiel #2
0
def test_handle_block_closed_channel():
    channel_state = factories.create(
        factories.NettingChannelStateProperties(
            close_transaction=TransactionExecutionStatus(
                finished_block_number=50,
                result=TransactionExecutionStatus.SUCCESS),
            settle_timeout=50,
        ))
    pseudo_random_generator = random.Random()
    block = Block(block_number=90,
                  gas_limit=100000,
                  block_hash=factories.make_block_hash())
    before_settle = handle_block(
        channel_state=channel_state,
        state_change=block,
        block_number=block.block_number,
        pseudo_random_generator=pseudo_random_generator,
    )
    assert get_status(before_settle.new_state) == ChannelState.STATE_CLOSED
    assert not before_settle.events

    block = Block(block_number=102,
                  gas_limit=100000,
                  block_hash=factories.make_block_hash())
    after_settle = handle_block(
        channel_state=before_settle.new_state,
        state_change=block,
        block_number=block.block_number,
        pseudo_random_generator=pseudo_random_generator,
    )
    assert get_status(after_settle.new_state) == ChannelState.STATE_SETTLING
    assert after_settle.events
Beispiel #3
0
def is_channel_close_needed(payer_channel, transfer_pair, block_number):
    """ True if this node needs to close the channel to unlock on-chain.
    Only close the channel to unlock on chain if the corresponding payee node
    has received, this prevents attacks were the payee node burns it's payment
    to force a close with the payer channel.
    """
    payee_received = transfer_pair.payee_state in STATE_TRANSFER_PAID
    payer_payed = transfer_pair.payer_state in STATE_TRANSFER_PAID

    payer_channel_open = channel.get_status(payer_channel) == CHANNEL_STATE_OPENED
    already_closing = channel.get_status(payer_channel) == CHANNEL_STATE_CLOSING

    safe_to_wait = is_safe_to_wait(
        transfer_pair.payer_transfer.lock.expiration,
        payer_channel.reveal_timeout,
        block_number,
    )

    return (
        payee_received and
        not payer_payed and

        payer_channel_open and
        not already_closing and
        not safe_to_wait
    )
Beispiel #4
0
def test_set_settled():
    channel = factories.create(
        factories.NettingChannelStateProperties(
            settle_transaction=TransactionExecutionStatus(
                finished_block_number=None, result=None)))

    assert get_status(channel) == ChannelState.STATE_SETTLING
    set_settled(channel, block_number=100)
    assert get_status(channel) == ChannelState.STATE_SETTLED
Beispiel #5
0
def test_get_status():
    failed_status = TransactionExecutionStatus(
        finished_block_number=10, result=TransactionExecutionStatus.FAILURE)

    close_failed = factories.create(
        factories.NettingChannelStateProperties(
            close_transaction=failed_status))
    assert get_status(close_failed) == ChannelState.STATE_UNUSABLE

    settle_failed = factories.create(
        factories.NettingChannelStateProperties(
            settle_transaction=failed_status))
    assert get_status(settle_failed) == ChannelState.STATE_UNUSABLE
Beispiel #6
0
def wait_for_settle(
        raiden: RaidenService,
        payment_network_id: typing.PaymentNetworkID,
        token_address: typing.TokenAddress,
        channel_ids: typing.List[typing.ChannelID],
        retry_timeout: float,
) -> None:
    """Wait until all channels are settled.

    Note:
        This does not time out, use gevent.Timeout.
    """
    if not isinstance(channel_ids, list):
        raise ValueError('channel_ids must be a list')

    channel_ids = list(channel_ids)

    while channel_ids:
        last_id = channel_ids[-1]
        channel_state = views.get_channelstate_by_id(
            views.state_from_raiden(raiden),
            payment_network_id,
            token_address,
            last_id,
        )

        channel_is_settled = (
            channel_state is None or
            channel.get_status(channel_state) == CHANNEL_STATE_SETTLED
        )

        if channel_is_settled:
            channel_ids.pop()
        else:
            gevent.sleep(retry_timeout)
Beispiel #7
0
    def _close(self, registry_address, channel_state):
        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors='Attempted to close an already closed channel',
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.channel_close(
                registry_address,
                channel_state.token_address,
                channel_state.partner_state.address,
            )
        except EthNodeCommunicationError:
            return api_response(
                result='',
                status_code=HTTPStatus.ACCEPTED,
            )
        except InsufficientFunds as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.PAYMENT_REQUIRED,
            )

        updated_channel_state = self.raiden_api.get_channel(
            registry_address,
            channel_state.token_address,
            channel_state.partner_state.address,
        )

        result = self.channel_schema.dump(updated_channel_state)
        return api_response(result=result.data)
Beispiel #8
0
    def _close(self, registry_address, channel_state):
        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors='Attempted to close an already closed channel',
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.channel_close(
                registry_address,
                channel_state.token_address,
                channel_state.partner_state.address,
            )
        except ChannelBusyError as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.CONFLICT,
            )

        updated_channel_state = self.raiden_api.get_channel(
            registry_address,
            channel_state.identifier,
        )

        result = self.channel_schema.dump(
            channelstate_to_api_dict(updated_channel_state))

        return api_response(result=checksummed_response_dict(result.data))
Beispiel #9
0
    def _close(self, registry_address, channel_state):
        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors='Attempted to close an already closed channel',
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.channel_close(
                registry_address,
                channel_state.token_address,
                channel_state.partner_state.address,
            )
        except EthNodeCommunicationError:
            return api_response(
                result='',
                status_code=HTTPStatus.ACCEPTED,
            )
        except InsufficientFunds as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.PAYMENT_REQUIRED,
            )

        updated_channel_state = self.raiden_api.get_channel(
            registry_address,
            channel_state.token_address,
            channel_state.partner_state.address,
        )

        result = self.channel_schema.dump(updated_channel_state)
        return api_response(result=result.data)
Beispiel #10
0
    def _close(self, registry_address: typing.PaymentNetworkID,
               channel_state: NettingChannelState):
        log.debug(
            "Closing channel",
            node=pex(self.raiden_api.address),
            registry_address=to_checksum_address(registry_address),
            channel_identifier=channel_state.identifier,
        )

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors="Attempted to close an already closed channel",
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.channel_close(registry_address,
                                          channel_state.token_address,
                                          channel_state.partner_state.address)
        except InsufficientFunds as e:
            return api_error(errors=str(e),
                             status_code=HTTPStatus.PAYMENT_REQUIRED)

        updated_channel_state = self.raiden_api.get_channel(
            registry_address, channel_state.token_address,
            channel_state.partner_state.address)

        result = self.channel_schema.dump(updated_channel_state)
        return api_response(result=result.data)
Beispiel #11
0
def handle_onchain_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ContractReceiveSecretReveal,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:
    """ When a secret is revealed on-chain all nodes learn the secret.

    This check the on-chain secret corresponds to the one used by the
    initiator, and if valid a new balance proof is sent to the next hop with
    the current lock removed from the merkle tree and the transferred amount
    updated.
    """
    is_valid_secret = state_change.secrethash == initiator_state.transfer.lock.secrethash
    is_channel_open = channel.get_status(channel_state) == CHANNEL_STATE_OPENED
    is_lock_expired = state_change.block_number > initiator_state.transfer.lock.expiration

    is_lock_unlocked = (is_valid_secret and is_channel_open
                        and not is_lock_expired)

    if is_lock_unlocked:
        events = events_for_unlock_lock(
            initiator_state,
            channel_state,
            state_change.secret,
            state_change.secrethash,
            pseudo_random_generator,
        )
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Beispiel #12
0
def wait_for_close(raiden, payment_network_id, token_network_id, channel_ids,
                   poll_timeout):
    """Wait until all channels are closed.

    Note:
        This does not time out, use gevent.Timeout.
    """
    channel_ids = list(channel_ids)

    while channel_ids:
        first_id = channel_ids[0]
        channel_state = views.get_channelstate_by_id(
            views.state_from_raiden(raiden),
            payment_network_id,
            token_network_id,
            first_id,
        )

        channel_is_settled = (channel_state is None
                              or channel.get_status(channel_state)
                              in CHANNEL_AFTER_CLOSE_STATES)

        if channel_is_settled:
            channel_ids.pop()
        else:
            gevent.sleep(poll_timeout)
Beispiel #13
0
def wait_for_settle(raiden, payment_network_id, token_network_id, channel_ids,
                    poll_timeout):
    """Wait until all channels are settled.

    Note:
        This does not time out, use gevent.Timeout.
    """
    if not isinstance(channel_ids, list):
        raise ValueError('channel_ids must be a list')

    channel_ids = list(channel_ids)

    while channel_ids:
        first_id = channel_ids[0]
        channel_state = views.get_channelstate_by_id(
            views.state_from_raiden(raiden),
            payment_network_id,
            token_network_id,
            first_id,
        )

        channel_is_settled = (channel_state is None
                              or channel.get_status(channel_state)
                              == CHANNEL_STATE_SETTLED)

        if channel_is_settled:
            channel_ids.pop()
        else:
            gevent.sleep(poll_timeout)
Beispiel #14
0
def wait_for_close(
    raiden: RaidenService,
    payment_network_id: typing.PaymentNetworkID,
    token_address: typing.Address,
    channel_ids: typing.List[typing.ChannelID],
    retry_timeout: float,
) -> None:
    """Wait until all channels are closed.

    Note:
        This does not time out, use gevent.Timeout.
    """
    channel_ids = list(channel_ids)

    while channel_ids:
        first_id = channel_ids[0]
        channel_state = views.get_channelstate_by_id(
            views.state_from_raiden(raiden),
            payment_network_id,
            token_address,
            first_id,
        )

        channel_is_settled = (channel_state is None
                              or channel.get_status(channel_state)
                              in CHANNEL_AFTER_CLOSE_STATES)

        if channel_is_settled:
            channel_ids.pop()
        else:
            gevent.sleep(retry_timeout)
Beispiel #15
0
def handle_offchain_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretReveal,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:
    """ Once the next hop proves it knows the secret, the initiator can unlock
    the mediated transfer.

    This will validate the secret, and if valid a new balance proof is sent to
    the next hop with the current lock removed from the merkle tree and the
    transferred amount updated.
    """
    is_valid_secret_reveal = (
        state_change.sender == channel_state.partner_state.address
        and state_change.secrethash
        == initiator_state.transfer_description.secrethash)
    is_channel_open = channel.get_status(channel_state) == CHANNEL_STATE_OPENED

    if is_valid_secret_reveal and is_channel_open:
        events = events_for_unlock_lock(
            initiator_state,
            channel_state,
            state_change.secret,
            state_change.secrethash,
            pseudo_random_generator,
        )
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Beispiel #16
0
def wait_for_close(
        raiden: RaidenService,
        payment_network_id: typing.PaymentNetworkID,
        token_address: typing.TokenAddress,
        channel_ids: typing.List[typing.ChannelID],
        retry_timeout: float,
) -> None:
    """Wait until all channels are closed.

    Note:
        This does not time out, use gevent.Timeout.
    """
    channel_ids = list(channel_ids)

    while channel_ids:
        last_id = channel_ids[-1]
        channel_state = views.get_channelstate_by_id(
            views.state_from_raiden(raiden),
            payment_network_id,
            token_address,
            last_id,
        )

        channel_is_settled = (
            channel_state is None or
            channel.get_status(channel_state) in CHANNEL_AFTER_CLOSE_STATES
        )

        if channel_is_settled:
            channel_ids.pop()
        else:
            gevent.sleep(retry_timeout)
Beispiel #17
0
def next_channel_from_routes(
    available_routes: typing.List[RouteState],
    channelidentifiers_to_channels: typing.ChannelMap,
    transfer_amount: typing.TokenAmount,
) -> typing.Optional[NettingChannelState]:
    """ Returns the first channel that can be used to start the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        pending_transfers = channel.get_number_of_pending_transfers(
            channel_state.our_state)
        if pending_transfers >= MAXIMUM_PENDING_TRANSFERS:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Beispiel #18
0
def run_smoketests(
    raiden_service: RaidenService,
    transport: str,
    token_addresses,
    discovery_address,
    debug: bool = False,
):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        token_network_added_events = raiden_service.default_registry.filter_token_added_events(
        )
        events_token_addresses = [
            event['args']['token_address']
            for event in token_network_added_events
        ]

        assert events_token_addresses == token_addresses

        if transport == 'udp':
            discovery_addresses = list(chain.address_to_discovery.keys())
            assert len(discovery_addresses) == 1, repr(
                chain.address_to_discovery)
            assert discovery_addresses[0] == discovery_address
            discovery = chain.address_to_discovery[discovery_addresses[0]]
            assert discovery.endpoint_by_address(
                raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            decode_hex(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except:  # NOQA pylint: disable=bare-except
        error = traceback.format_exc()
        if debug:
            import pdb
            pdb.post_mortem()  # pylint: disable=no-member
        return error

    return None
Beispiel #19
0
    def _deposit(self, registry_address, channel_state, balance):
        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors="Can't deposit on a closed channel",
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.channel_deposit(
                registry_address,
                channel_state.token_address,
                channel_state.partner_state.address,
                balance,
            )
        except ChannelBusyError as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.CONFLICT,
            )
        except InsufficientFunds as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.PAYMENT_REQUIRED,
            )

        updated_channel_state = self.raiden_api.get_channel(
            registry_address,
            channel_state.identifier,
        )

        result = self.channel_schema.dump(
            channelstate_to_api_dict(updated_channel_state))
        return api_response(result=checksummed_response_dict(result.data))
Beispiel #20
0
def wait_for_settle(
    raiden: RaidenService,
    payment_network_id: typing.PaymentNetworkID,
    token_address: typing.TokenAddress,
    channel_ids: typing.List[typing.ChannelID],
    retry_timeout: float,
) -> None:
    """Wait until all channels are settled.

    Note:
        This does not time out, use gevent.Timeout.
    """
    if not isinstance(channel_ids, list):
        raise ValueError('channel_ids must be a list')

    channel_ids = list(channel_ids)

    while channel_ids:
        first_id = channel_ids[0]
        channel_state = views.get_channelstate_by_id(
            views.state_from_raiden(raiden),
            payment_network_id,
            token_address,
            first_id,
        )

        channel_is_settled = (channel_state is None
                              or channel.get_status(channel_state)
                              == CHANNEL_STATE_SETTLED)

        if channel_is_settled:
            channel_ids.pop()
        else:
            gevent.sleep(retry_timeout)
Beispiel #21
0
def next_channel_from_routes(
        available_routes: typing.List[RouteState],
        channelidentifiers_to_channels: ChannelMap,
        transfer_amount: typing.TokenAmount,
) -> typing.Optional[NettingChannelState]:
    """ Returns the first channel that can be used to start the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Beispiel #22
0
def handle_offchain_secretreveal_light(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRevealLight,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random
) -> TransitionResult[InitiatorTransferState]:
    """ Once the next hop proves it knows the secret, the initiator can unlock
    the mediated transfer.

    This will validate the secret, and if valid a new balance proof is sent to
    the next hop with the current lock removed from the merkle tree and the
    transferred amount updated.
    """
    iteration: TransitionResult[InitiatorTransferState]
    valid_reveal = is_valid_secret_reveal(
        state_change=state_change,
        transfer_secrethash=initiator_state.transfer_description.secrethash,
        secret=state_change.secret,
    )
    sent_by_partner = state_change.sender == channel_state.partner_state.address
    is_channel_open = channel.get_status(channel_state) == CHANNEL_STATE_OPENED

    if valid_reveal and is_channel_open and sent_by_partner:
        unlock_events = events_for_unlock_base(
            initiator_state=initiator_state,
            channel_state=channel_state,
            secret=state_change.secret,
        )

        transfer_description = initiator_state.transfer_description

        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        unlock_lock = channel.send_unlock(
            channel_state=channel_state,
            message_identifier=message_identifier,
            payment_identifier=transfer_description.payment_identifier,
            secret=state_change.secret,
            secrethash=state_change.secrethash,
        )
        unlock_msg = Unlock.from_event(unlock_lock)

        store_received_secret_reveal_event = StoreMessageEvent(state_change.secret_reveal_message.message_identifier,
                                                               transfer_description.payment_identifier, 9,
                                                               state_change.secret_reveal_message,
                                                               True)
        store_created_unlock_event = StoreMessageEvent(message_identifier, transfer_description.payment_identifier, 11,
                                                       unlock_msg, False)

        events = list()
        events.append(store_received_secret_reveal_event)
        events.append(store_created_unlock_event)
        events.extend(unlock_events)
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Beispiel #23
0
def events_for_balanceproof(
    channelidentifiers_to_channels,
    transfers_pair,
    pseudo_random_generator,
    block_number,
    secret,
    secrethash,
):
    """ While it's safe do the off-chain unlock. """

    events = list()
    for pair in reversed(transfers_pair):
        payee_knows_secret = pair.payee_state in STATE_SECRET_KNOWN
        payee_payed = pair.payee_state in STATE_TRANSFER_PAID

        payee_channel = get_payee_channel(channelidentifiers_to_channels, pair)
        payee_channel_open = channel.get_status(
            payee_channel) == CHANNEL_STATE_OPENED

        payer_channel = get_payer_channel(channelidentifiers_to_channels, pair)

        # The mediator must not send to the payee a balance proof if the lock
        # is in the danger zone, because the payer may not do the same and the
        # on-chain unlock may fail. If the lock is nearing it's expiration
        # block, then on-chain unlock should be done, and if successfull it can
        # be unlocked off-chain.
        is_safe_to_send_balanceproof, _ = is_safe_to_wait(
            pair.payer_transfer.lock.expiration,
            payer_channel.reveal_timeout,
            block_number,
        )

        should_send_balanceproof_to_payee = (payee_channel_open
                                             and payee_knows_secret
                                             and not payee_payed
                                             and is_safe_to_send_balanceproof)

        if should_send_balanceproof_to_payee:
            pair.payee_state = 'payee_balance_proof'

            message_identifier = message_identifier_from_prng(
                pseudo_random_generator)
            unlock_lock = channel.send_unlock(
                payee_channel,
                pair.payee_transfer.payment_identifier,
                message_identifier,
                secret,
                secrethash,
            )

            unlock_success = EventUnlockSuccess(
                pair.payer_transfer.payment_identifier,
                pair.payer_transfer.lock.secrethash,
            )
            events.append(unlock_lock)
            events.append(unlock_success)

    return events
Beispiel #24
0
def wait_for_channel_in_states(raiden: "RaidenService",
                               payment_network_id: PaymentNetworkID,
                               token_address: TokenAddress,
                               channel_ids: List[ChannelID],
                               retry_timeout: float,
                               target_states: Sequence[str],
                               partner_addresses: List[Address]) -> None:
    """Wait until all channels are in `target_states`.

    Raises:
        ValueError: If the token_address is not registered in the
            payment_network.

    Note:
        This does not time out, use gevent.Timeout.
    """
    chain_state = views.state_from_raiden(raiden)
    token_network = views.get_token_network_by_token_address(
        chain_state=chain_state,
        payment_network_id=payment_network_id,
        token_address=token_address)

    if token_network is None:
        raise ValueError(
            f"The token {token_address} is not registered on the network {payment_network_id}."
        )

    token_network_address = token_network.address

    list_cannonical_ids = [
        CanonicalIdentifier(
            chain_identifier=chain_state.chain_id,
            token_network_address=token_network_address,
            channel_identifier=channel_identifier,
        ) for channel_identifier in channel_ids
    ]

    while list_cannonical_ids:
        assert raiden, ALARM_TASK_ERROR_MSG
        assert raiden.alarm, ALARM_TASK_ERROR_MSG

        canonical_id = list_cannonical_ids[-1]
        chain_state = views.state_from_raiden(raiden)

        channel_state = views.get_channelstate_by_canonical_identifier_and_address(
            chain_state=chain_state,
            canonical_identifier=canonical_id,
            address=partner_addresses[0])

        channel_is_settled = (channel_state is None
                              or channel.get_status(channel_state)
                              in target_states)

        if channel_is_settled:
            list_cannonical_ids.pop()
        else:
            gevent.sleep(retry_timeout)
Beispiel #25
0
def run_smoketests(raiden_service: RaidenService, test_config: Dict, debug: bool = False):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        assert (
            raiden_service.default_registry.address ==
            to_canonical_address(test_config['contracts']['registry_address'])
        )
        assert (
            raiden_service.default_secret_registry.address ==
            to_canonical_address(test_config['contracts']['secret_registry_address'])
        )

        token_network_added_events = raiden_service.default_registry.filter_token_added_events()
        token_addresses = [event['args']['token_address'] for event in token_network_added_events]

        assert token_addresses == [test_config['contracts']['token_address']]

        if test_config.get('transport') == 'udp':
            assert len(chain.address_to_discovery.keys()) == 1, repr(chain.address_to_discovery)
            assert (
                list(chain.address_to_discovery.keys())[0] ==
                to_canonical_address(test_config['contracts']['discovery_address'])
            )
            discovery = list(chain.address_to_discovery.values())[0]
            assert discovery.endpoint_by_address(raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            unhexlify(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except Exception:
        error = traceback.format_exc()
        if debug:
            import pdb
            pdb.post_mortem()
        return error
Beispiel #26
0
def run_smoketests(raiden_service: RaidenService, test_config: Dict, debug: bool = False):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        assert (
            raiden_service.default_registry.address ==
            to_canonical_address(test_config['contracts']['registry_address'])
        )
        assert (
            raiden_service.default_secret_registry.address ==
            to_canonical_address(test_config['contracts']['secret_registry_address'])
        )

        token_network_added_events = raiden_service.default_registry.filter_token_added_events()
        token_addresses = [event['args']['token_address'] for event in token_network_added_events]

        assert token_addresses == [test_config['contracts']['token_address']]

        if test_config.get('transport') == 'udp':
            assert len(chain.address_to_discovery.keys()) == 1, repr(chain.address_to_discovery)
            assert (
                list(chain.address_to_discovery.keys())[0] ==
                to_canonical_address(test_config['contracts']['discovery_address'])
            )
            discovery = list(chain.address_to_discovery.values())[0]
            assert discovery.endpoint_by_address(raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            unhexlify(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except Exception:
        error = traceback.format_exc()
        if debug:
            import pdb
            pdb.post_mortem()
        return error
Beispiel #27
0
def handle_onchain_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ContractReceiveSecretReveal,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[InitiatorTransferState]]:
    """ When a secret is revealed on-chain all nodes learn the secret.

    This check the on-chain secret corresponds to the one used by the
    initiator, and if valid a new balance proof is sent to the next hop with
    the current lock removed from the pending locks and the transferred amount
    updated.
    """
    iteration: TransitionResult[Optional[InitiatorTransferState]]
    secret = state_change.secret
    secrethash = initiator_state.transfer_description.secrethash
    is_valid_secret = is_valid_secret_reveal(state_change=state_change,
                                             transfer_secrethash=secrethash)
    is_channel_open = channel.get_status(
        channel_state) == ChannelState.STATE_OPENED
    is_lock_expired = state_change.block_number > initiator_state.transfer.lock.expiration

    is_lock_unlocked = is_valid_secret and not is_lock_expired

    if is_lock_unlocked:
        channel.register_onchain_secret(
            channel_state=channel_state,
            secret=secret,
            secrethash=secrethash,
            secret_reveal_block_number=state_change.block_number,
        )

    lock = initiator_state.transfer.lock
    expired = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=lock,
        block_number=block_number,
        lock_expiration_threshold=lock.expiration,
    )

    if is_lock_unlocked and is_channel_open and not expired:
        events = events_for_unlock_lock(
            initiator_state,
            channel_state,
            state_change.secret,
            state_change.secrethash,
            pseudo_random_generator,
            block_number,
        )
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Beispiel #28
0
def wait_for_channel_in_states(
    raiden: RaidenService,
    payment_network_id: typing.PaymentNetworkID,
    token_address: typing.TokenAddress,
    channel_ids: typing.List[typing.ChannelID],
    retry_timeout: float,
    target_states: typing.Tuple[str],
) -> None:
    """Wait until all channels are in `target_states`.

    Raises:
        ValueError: If the token_address is not registered in the
            payment_network.

    Note:
        This does not time out, use gevent.Timeout.
    """
    chain_state = views.state_from_raiden(raiden)
    token_network = views.get_token_network_by_token_address(
        chain_state=chain_state,
        payment_network_id=payment_network_id,
        token_address=token_address,
    )

    if token_network is None:
        raise ValueError(
            f'The token {token_address} is not registered on the network {payment_network_id}.',
        )

    token_network_address = token_network.address

    list_cannonical_ids = [
        CanonicalIdentifier(
            chain_identifier=chain_state.chain_id,
            token_network_address=token_network_address,
            channel_identifier=channel_identifier,
        ) for channel_identifier in channel_ids
    ]

    while list_cannonical_ids:
        canonical_id = list_cannonical_ids[-1]
        chain_state = views.state_from_raiden(raiden)

        channel_state = views.get_channelstate_by_canonical_identifier(
            chain_state=chain_state,
            canonical_identifier=canonical_id,
        )

        channel_is_settled = (channel_state is None
                              or channel.get_status(channel_state)
                              in target_states)

        if channel_is_settled:
            list_cannonical_ids.pop()
        else:
            gevent.sleep(retry_timeout)
Beispiel #29
0
def smoketest_perform_tests(
    raiden_service: RaidenService,
    transport: str,
    token_addresses,
    discovery_address,
):
    """ Perform high level tests designed to quickly discover broken functionality. """
    try:
        chain = raiden_service.chain
        token_network_added_events = raiden_service.default_registry.filter_token_added_events(
        )
        events_token_addresses = [
            event['args']['token_address']
            for event in token_network_added_events
        ]

        assert events_token_addresses == token_addresses

        if transport == 'udp':
            discovery_addresses = list(chain.address_to_discovery.keys())
            assert len(discovery_addresses) == 1, repr(
                chain.address_to_discovery)
            assert discovery_addresses[0] == discovery_address
            discovery = chain.address_to_discovery[discovery_addresses[0]]
            assert discovery.endpoint_by_address(
                raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_identifiers(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            decode_hex(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except:  # NOQA pylint: disable=bare-except
        error = traceback.format_exc()
        return error

    return None
Beispiel #30
0
    def _deposit(
        self,
        registry_address: typing.PaymentNetworkID,
        channel_state: NettingChannelState,
        total_deposit: typing.TokenAmount,
    ):
        log.debug(
            'Depositing to channel',
            registry_address=to_checksum_address(registry_address),
            channel_identifier=channel_state.identifier,
            total_deposit=total_deposit,
        )

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors="Can't set total deposit on a closed channel",
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.set_total_channel_deposit(
                registry_address,
                channel_state.token_address,
                channel_state.partner_state.address,
                total_deposit,
            )
        except EthNodeCommunicationError:
            return api_response(
                result='',
                status_code=HTTPStatus.ACCEPTED,
            )
        except InsufficientFunds as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.PAYMENT_REQUIRED,
            )
        except DepositOverLimit as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.CONFLICT,
            )
        except DepositMismatch as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.CONFLICT,
            )

        updated_channel_state = self.raiden_api.get_channel(
            registry_address,
            channel_state.token_address,
            channel_state.partner_state.address,
        )

        result = self.channel_schema.dump(updated_channel_state)
        return api_response(result=result.data)
Beispiel #31
0
def get_channelstate_settled(
        chain_state: ChainState, payment_network_id: PaymentNetworkID,
        token_address: TokenAddress) -> List[NettingChannelState]:
    """Return the state of settled channels in a token network."""
    return get_channelstate_filter(
        chain_state,
        payment_network_id,
        token_address,
        lambda channel_state: channel.get_status(channel_state) ==
        CHANNEL_STATE_SETTLED,
    )
Beispiel #32
0
def get_channelstate_settling(
        chain_state: ChainState,
        payment_network_id: typing.PaymentNetworkID,
        token_address: typing.TokenAddress,
) -> typing.List[NettingChannelState]:
    """Return the state of settling channels in a token network."""
    return get_channelstate_filter(
        chain_state,
        payment_network_id,
        token_address,
        lambda channel_state: channel.get_status(channel_state) == CHANNEL_STATE_SETTLING,
    )
Beispiel #33
0
    def __init__(self, api: RaidenAPI, token_address: TokenAddress) -> None:
        assert isinstance(api, RaidenAPI)
        self.ready = Event()

        self.api = api
        self.token_address = token_address

        existing_channels = self.api.get_channel_list(
            api.raiden.default_registry.address, self.token_address
        )

        open_channels = [
            channel_state
            for channel_state in existing_channels
            if channel.get_status(channel_state) == ChannelState.STATE_OPENED
        ]

        if len(open_channels) == 0:
            token_proxy = self.api.raiden.proxy_manager.token(self.token_address)
            if not token_proxy.balance_of(self.api.raiden.address) > 0:
                raise ValueError(
                    f"Not enough funds for echo node "
                    f"{to_checksum_address(self.api.raiden.address)} for token "
                    f"{to_checksum_address(self.token_address)}"
                )

            # Using the balance of the node as funds
            funds = TokenAmount(token_proxy.balance_of(self.api.raiden.address))

            self.api.token_network_connect(
                registry_address=self.api.raiden.default_registry.address,
                token_address=self.token_address,
                funds=funds,
                initial_channel_target=10,
                joinable_funds_target=0.5,
            )

        self.num_seen_events = 0
        self.received_transfers: Queue[EventPaymentReceivedSuccess] = Queue()

        # This is used to signal REMOVE_CALLBACK and stop echo_workers
        self.stop_signal: Optional[bool] = None

        self.greenlets: Set[Greenlet] = set()
        self.lock = BoundedSemaphore()
        self.seen_transfers: Deque[EventPaymentReceivedSuccess] = deque(list(), TRANSFER_MEMORY)
        self.num_handled_transfers = 0
        self.lottery_pool = Queue()

        # register ourselves with the raiden alarm task
        self.api.raiden.alarm.register_callback(self.echo_node_alarm_callback)
        self.echo_worker_greenlet = gevent.spawn(self.echo_worker)
        log.info("Echo node started")
Beispiel #34
0
def handle_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretReveal,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:
    """ Send a balance proof to the next hop with the current mediated transfer
    lock removed and the balance updated.
    """
    is_valid_secret_reveal = (
        state_change.sender == channel_state.partner_state.address
        and state_change.secrethash
        == initiator_state.transfer_description.secrethash)

    # If the channel is closed the balance proof must not be sent
    is_channel_open = channel.get_status(channel_state) == CHANNEL_STATE_OPENED

    if is_valid_secret_reveal and is_channel_open:
        # next hop learned the secret, unlock the token locally and send the
        # lock claim message to next hop
        transfer_description = initiator_state.transfer_description

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        unlock_lock = channel.send_unlock(
            channel_state,
            transfer_description.payment_identifier,
            message_identifier,
            state_change.secret,
            state_change.secrethash,
        )

        # TODO: Emit these events after on-chain unlock
        payment_sent_success = EventPaymentSentSuccess(
            payment_network_identifier=channel_state.
            payment_network_identifier,
            token_network_identifier=channel_state.token_network_identifier,
            identifier=transfer_description.payment_identifier,
            amount=transfer_description.amount,
            target=transfer_description.target,
        )

        unlock_success = EventUnlockSuccess(
            transfer_description.payment_identifier,
            transfer_description.secrethash,
        )

        iteration = TransitionResult(
            None, [payment_sent_success, unlock_success, unlock_lock])
    else:
        iteration = TransitionResult(initiator_state, list())

    return iteration
Beispiel #35
0
def get_channelstate_closing(
        chain_state: ChainState,
        payment_network_id: PaymentNetworkID,
        token_address: TokenAddress,
) -> List[NettingChannelState]:
    """Return the state of closing channels in a token network."""
    return get_channelstate_filter(
        chain_state,
        payment_network_id,
        token_address,
        lambda channel_state: channel.get_status(channel_state) == CHANNEL_STATE_CLOSING,
    )
Beispiel #36
0
def get_channelstate_settled(
    chain_state: ChainState,
    token_network_registry_address: TokenNetworkRegistryAddress,
    token_address: TokenAddress,
) -> List[NettingChannelState]:
    """Return the state of settled channels in a token network."""
    return get_channelstate_filter(
        chain_state,
        token_network_registry_address,
        token_address,
        lambda channel_state: channel.get_status(channel_state) == ChannelState.STATE_SETTLED,
    )
Beispiel #37
0
def get_channelstate_settled(
        chain_state: ChainState,
        payment_network_id: PaymentNetworkID,
        token_address: TokenAddress,
) -> List[NettingChannelState]:
    """Return the state of settled channels in a token network."""
    return get_channelstate_filter(
        chain_state,
        payment_network_id,
        token_address,
        lambda channel_state: channel.get_status(channel_state) == CHANNEL_STATE_SETTLED,
    )
Beispiel #38
0
def events_for_onchain_secretreveal(
        channel_state: NettingChannelState,
        block_number: typing.BlockNumber,
        secret: typing.Secret,
) -> typing.List[Event]:
    events = list()

    if not isinstance(secret, typing.T_Secret):
        raise ValueError('secret must be a Secret instance')

    if get_status(channel_state) in CHANNEL_STATES_PRIOR_TO_CLOSED + (CHANNEL_STATE_CLOSED, ):
        reveal_event = ContractSendSecretReveal(secret)
        events.append(reveal_event)

    return events
Beispiel #39
0
def handle_secretreveal(
        initiator_state: InitiatorTransferState,
        state_change: ReceiveSecretReveal,
        channel_state: NettingChannelState,
        pseudo_random_generator: random.Random,
) -> TransitionResult:
    """ Send a balance proof to the next hop with the current mediated transfer
    lock removed and the balance updated.
    """
    is_valid_secret_reveal = (
        state_change.sender == channel_state.partner_state.address and
        state_change.secrethash == initiator_state.transfer_description.secrethash
    )

    # If the channel is closed the balance proof must not be sent
    is_channel_open = channel.get_status(channel_state) == CHANNEL_STATE_OPENED

    if is_valid_secret_reveal and is_channel_open:
        # next hop learned the secret, unlock the token locally and send the
        # lock claim message to next hop
        transfer_description = initiator_state.transfer_description

        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        unlock_lock = channel.send_unlock(
            channel_state,
            transfer_description.payment_identifier,
            message_identifier,
            state_change.secret,
            state_change.secrethash,
        )

        # TODO: Emit these events after on-chain unlock
        transfer_success = EventTransferSentSuccess(
            transfer_description.payment_identifier,
            transfer_description.amount,
            transfer_description.target,
        )

        unlock_success = EventUnlockSuccess(
            transfer_description.payment_identifier,
            transfer_description.secrethash,
        )

        iteration = TransitionResult(None, [transfer_success, unlock_success, unlock_lock])
    else:
        iteration = TransitionResult(initiator_state, list())

    return iteration
Beispiel #40
0
def check_channel_constraints(
        channel_state: NettingChannelState,
        from_address: typing.InitiatorAddress,
        partner_address: typing.Address,
        amount: int,
        network_statuses: Dict[typing.Address, str],
        routing_module: str,
) -> bool:
    # check channel state
    if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
        log.info(
            'Channel is not opened, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            routing_source=routing_module,
        )
        return False

    # check channel distributable
    distributable = channel.get_distributable(
        channel_state.our_state,
        channel_state.partner_state,
    )

    if amount > distributable:
        log.info(
            'Channel doesnt have enough funds, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            amount=amount,
            distributable=distributable,
            routing_source=routing_module,
        )
        return False

    # check channel partner reachability
    network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN)
    if network_state != NODE_NETWORK_REACHABLE:
        log.info(
            'Partner for channel isn\'t reachable, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            status=network_state,
            routing_source=routing_module,
        )
        return False

    return True
Beispiel #41
0
def filter_channels_by_status(
        channel_states: List[NettingChannelState],
        exclude_states=None,
) -> List[NettingChannelState]:
    """ Filter the list of channels by excluding ones
    for which the state exists in `exclude_states`. """

    if exclude_states is None:
        exclude_states = []

    states = []
    for channel_state in channel_states:
        if channel.get_status(channel_state) not in exclude_states:
            states.append(channel_state)

    return states
Beispiel #42
0
def next_channel_from_routes(
        available_routes: List['RouteState'],
        channelidentifiers_to_channels: Dict,
        transfer_amount: int,
        timeout_blocks: int,
) -> NettingChannelState:
    """ Returns the first route that may be used to mediated the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    Args:
        available_routes: Current available routes that may be used, it's
            assumed that the available_routes list is ordered from best to
            worst.
        channelidentifiers_to_channels: Mapping from channel identifier
            to NettingChannelState.
        transfer_amount: The amount of tokens that will be transferred
            through the given route.
        timeout_blocks: Base number of available blocks used to compute
            the lock timeout.
    Returns:
        The next route.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        lock_timeout = timeout_blocks - channel_state.reveal_timeout
        if lock_timeout <= 0:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Beispiel #43
0
def events_for_balanceproof(
        channelidentifiers_to_channels,
        transfers_pair,
        pseudo_random_generator,
        block_number,
        secret,
        secrethash,
):
    """ Send the balance proof to nodes that know the secret. """

    events = list()
    for pair in reversed(transfers_pair):
        payee_knows_secret = pair.payee_state in STATE_SECRET_KNOWN
        payee_payed = pair.payee_state in STATE_TRANSFER_PAID

        payee_channel = get_payee_channel(channelidentifiers_to_channels, pair)
        payee_channel_open = channel.get_status(payee_channel) == CHANNEL_STATE_OPENED

        # XXX: All nodes must close the channel and unlock on-chain if the
        # lock is nearing it's expiration block, what should be the strategy
        # for sending a balance proof to a node that knowns the secret but has
        # not gone on-chain while near the expiration? (The problem is how to
        # define the unsafe region, since that is a local configuration)
        lock_valid = is_lock_valid(pair.payee_transfer.lock.expiration, block_number)

        if payee_channel_open and payee_knows_secret and not payee_payed and lock_valid:
            pair.payee_state = 'payee_balance_proof'

            message_identifier = message_identifier_from_prng(pseudo_random_generator)
            unlock_lock = channel.send_unlock(
                payee_channel,
                pair.payee_transfer.payment_identifier,
                message_identifier,
                secret,
                secrethash,
            )

            unlock_success = EventUnlockSuccess(
                pair.payer_transfer.payment_identifier,
                pair.payer_transfer.lock.secrethash,
            )
            events.append(unlock_lock)
            events.append(unlock_success)

    return events
Beispiel #44
0
    def __init__(self, api, token_address):
        assert isinstance(api, RaidenAPI)
        self.ready = Event()

        self.api = api
        self.token_address = token_address

        existing_channels = self.api.get_channel_list(
            api.raiden.default_registry.address,
            self.token_address,
        )

        open_channels = [
            channel_state
            for channel_state in existing_channels
            if channel.get_status(channel_state) == CHANNEL_STATE_OPENED
        ]

        if len(open_channels) == 0:
            token = self.api.raiden.chain.token(self.token_address)
            if not token.balance_of(self.api.raiden.address) > 0:
                raise ValueError('not enough funds for echo node %s for token %s' % (
                    pex(self.api.raiden.address),
                    pex(self.token_address),
                ))
            self.api.token_network_connect(
                self.api.raiden.default_registry.address,
                self.token_address,
                token.balance_of(self.api.raiden.address),
                initial_channel_target=10,
                joinable_funds_target=.5,
            )

        self.last_poll_offset = 0
        self.received_transfers = Queue()
        self.stop_signal = None  # used to signal REMOVE_CALLBACK and stop echo_workers
        self.greenlets = list()
        self.lock = BoundedSemaphore()
        self.seen_transfers = deque(list(), TRANSFER_MEMORY)
        self.num_handled_transfers = 0
        self.lottery_pool = Queue()
        # register ourselves with the raiden alarm task
        self.api.raiden.alarm.register_callback(self.echo_node_alarm_callback)
        self.echo_worker_greenlet = gevent.spawn(self.echo_worker)
        log.info('Echo node started')
Beispiel #45
0
def events_for_unlock_if_closed(
        channelidentifiers_to_channels,
        transfers_pair,
        secret,
        secrethash):
    """ Unlock on chain if the payer channel is closed and the secret is known.
    If a channel is closed because of another task a balance proof will not be
    received, so there is no reason to wait for the unsafe region before
    calling close.
    This may break the reverse reveal order:
        Path: A -- B -- C -- B -- D
        B learned the secret from D and has revealed to C.
        C has not confirmed yet.
        channel(A, B).closed is True.
        B will unlock on channel(A, B) before C's confirmation.
        A may learn the secret faster than other nodes.
    """
    events = list()
    pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)

    for pair in pending_transfers_pairs:
        payer_channel = get_payer_channel(channelidentifiers_to_channels, pair)

        payer_channel_open = channel.get_status(payer_channel) == CHANNEL_STATE_OPENED

        # The unlock is done by the channel
        if not payer_channel_open:
            pair.payer_state = 'payer_waiting_unlock'

            partner_state = payer_channel.partner_state
            lock = channel.get_lock(partner_state, secrethash)
            unlock_proof = channel.compute_proof_for_lock(
                partner_state,
                secret,
                lock,
            )
            unlock = ContractSendChannelBatchUnlock(
                payer_channel.token_network_identifier,
                payer_channel.identifier,
                [unlock_proof],
            )
            events.append(unlock)

    return events
Beispiel #46
0
    def _deposit(self, registry_address, channel_state, total_deposit):
        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            return api_error(
                errors="Can't set total deposit on a closed channel",
                status_code=HTTPStatus.CONFLICT,
            )

        try:
            self.raiden_api.set_total_channel_deposit(
                registry_address,
                channel_state.token_address,
                channel_state.partner_state.address,
                total_deposit,
            )
        except EthNodeCommunicationError:
            return api_response(
                result='',
                status_code=HTTPStatus.ACCEPTED,
            )
        except InsufficientFunds as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.PAYMENT_REQUIRED,
            )
        except DepositOverLimit as e:
            return api_error(
                errors=str(e),
                status_code=HTTPStatus.EXPECTATION_FAILED,
            )

        updated_channel_state = self.raiden_api.get_channel(
            registry_address,
            channel_state.token_address,
            channel_state.partner_state.address,
        )

        result = self.channel_schema.dump(updated_channel_state)
        return api_response(result=result.data)
Beispiel #47
0
def test_channel_lifecycle(raiden_network, token_addresses, deposit, transport_config):
    node1, node2 = raiden_network
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(node1),
        node1.raiden.default_registry.address,
        token_address,
    )

    api1 = RaidenAPI(node1.raiden)
    api2 = RaidenAPI(node2.raiden)

    registry_address = node1.raiden.default_registry.address

    # nodes don't have a channel, so they are not healthchecking
    assert api1.get_node_network_state(api2.address) == NODE_NETWORK_UNKNOWN
    assert api2.get_node_network_state(api1.address) == NODE_NETWORK_UNKNOWN
    assert not api1.get_channel_list(registry_address, token_address, api2.address)

    # open is a synchronous api
    api1.channel_open(node1.raiden.default_registry.address, token_address, api2.address)
    channels = api1.get_channel_list(registry_address, token_address, api2.address)
    assert len(channels) == 1

    channel12 = get_channelstate(node1, node2, token_network_identifier)
    assert channel.get_status(channel12) == CHANNEL_STATE_OPENED

    event_list1 = api1.get_channel_events(
        token_address,
        channel12.partner_state.address,
        channel12.open_transaction.finished_block_number,
    )
    assert any(
        (
            event['event'] == EVENT_CHANNEL_OPENED and
            is_same_address(
                event['args']['participant1'],
                to_normalized_address(api1.address),
            ) and
            is_same_address(
                event['args']['participant2'],
                to_normalized_address(api2.address),
            )
        )
        for event in event_list1
    )

    token_events = api1.get_token_network_events(
        token_address,
        channel12.open_transaction.finished_block_number,
    )
    assert token_events[0]['event'] == EVENT_CHANNEL_OPENED

    registry_address = api1.raiden.default_registry.address
    # Load the new state with the deposit
    api1.set_total_channel_deposit(
        registry_address,
        token_address,
        api2.address,
        deposit,
    )

    # let's make sure it's idempotent
    api1.set_total_channel_deposit(
        registry_address,
        token_address,
        api2.address,
        deposit,
    )

    channel12 = get_channelstate(node1, node2, token_network_identifier)

    assert channel.get_status(channel12) == CHANNEL_STATE_OPENED
    assert channel.get_balance(channel12.our_state, channel12.partner_state) == deposit
    assert channel12.our_state.contract_balance == deposit
    assert api1.get_channel_list(registry_address, token_address, api2.address) == [channel12]

    # there is a channel open, they must be healthchecking each other
    assert api1.get_node_network_state(api2.address) == NODE_NETWORK_REACHABLE
    assert api2.get_node_network_state(api1.address) == NODE_NETWORK_REACHABLE

    event_list2 = api1.get_channel_events(
        token_address,
        channel12.partner_state.address,
        channel12.open_transaction.finished_block_number,
    )
    assert any(
        (
            event['event'] == EVENT_CHANNEL_DEPOSIT and
            is_same_address(
                event['args']['participant'],
                to_normalized_address(api1.address),
            ) and
            event['args']['total_deposit'] == deposit
        )
        for event in event_list2
    )

    api1.channel_close(registry_address, token_address, api2.address)

    # Load the new state with the channel closed
    channel12 = get_channelstate(node1, node2, token_network_identifier)

    event_list3 = api1.get_channel_events(
        token_address,
        channel12.partner_state.address,
        channel12.open_transaction.finished_block_number,
    )
    assert len(event_list3) > len(event_list2)
    assert any(
        (
            event['event'] == EVENT_CHANNEL_CLOSED and
            is_same_address(
                event['args']['closing_participant'],
                to_normalized_address(api1.address),
            )
        )
        for event in event_list3
    )
    assert channel.get_status(channel12) == CHANNEL_STATE_CLOSED

    settlement_block = (
        channel12.close_transaction.finished_block_number +
        channel12.settle_timeout +
        10  # arbitrary number of additional blocks, used to wait for the settle() call
    )
    wait_until_block(node1.raiden.chain, settlement_block)

    # Load the new state with the channel settled
    channel12 = get_channelstate(node1, node2, token_network_identifier)

    assert channel.get_status(channel12) == CHANNEL_STATE_SETTLED
Beispiel #48
0
 def get_state(self, channel_state):
     return channel.get_status(channel_state)
Beispiel #49
0
def test_channel_lifecycle(raiden_network, token_addresses, deposit, transport_protocol):
    node1, node2 = raiden_network
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(node1),
        node1.raiden.default_registry.address,
        token_address,
    )

    api1 = RaidenAPI(node1.raiden)
    api2 = RaidenAPI(node2.raiden)

    registry_address = node1.raiden.default_registry.address

    # nodes don't have a channel, so they are not healthchecking
    assert api1.get_node_network_state(api2.address) == NODE_NETWORK_UNKNOWN
    assert api2.get_node_network_state(api1.address) == NODE_NETWORK_UNKNOWN
    assert not api1.get_channel_list(registry_address, token_address, api2.address)

    # Make sure invalid arguments to get_channel_list are caught
    with pytest.raises(UnknownTokenAddress):
        api1.get_channel_list(
            registry_address=registry_address,
            token_address=None,
            partner_address=api2.address,
        )

    # open is a synchronous api
    api1.channel_open(node1.raiden.default_registry.address, token_address, api2.address)
    channels = api1.get_channel_list(registry_address, token_address, api2.address)
    assert len(channels) == 1

    channel12 = get_channelstate(node1, node2, token_network_identifier)
    assert channel.get_status(channel12) == CHANNEL_STATE_OPENED

    event_list1 = api1.get_blockchain_events_channel(
        token_address,
        channel12.partner_state.address,
    )
    assert any(
        (
            event['event'] == ChannelEvent.OPENED and
            is_same_address(
                event['args']['participant1'],
                to_normalized_address(api1.address),
            ) and
            is_same_address(
                event['args']['participant2'],
                to_normalized_address(api2.address),
            )
        )
        for event in event_list1
    )

    token_events = api1.get_blockchain_events_token_network(
        token_address,
    )
    assert token_events[0]['event'] == ChannelEvent.OPENED

    registry_address = api1.raiden.default_registry.address
    # Load the new state with the deposit
    api1.set_total_channel_deposit(
        registry_address,
        token_address,
        api2.address,
        deposit,
    )

    # let's make sure it's idempotent. Same deposit should raise deposit mismatch limit
    with pytest.raises(DepositMismatch):
        api1.set_total_channel_deposit(
            registry_address,
            token_address,
            api2.address,
            deposit,
        )

    channel12 = get_channelstate(node1, node2, token_network_identifier)

    assert channel.get_status(channel12) == CHANNEL_STATE_OPENED
    assert channel.get_balance(channel12.our_state, channel12.partner_state) == deposit
    assert channel12.our_state.contract_balance == deposit
    assert api1.get_channel_list(registry_address, token_address, api2.address) == [channel12]

    # there is a channel open, they must be healthchecking each other
    assert api1.get_node_network_state(api2.address) == NODE_NETWORK_REACHABLE
    assert api2.get_node_network_state(api1.address) == NODE_NETWORK_REACHABLE

    event_list2 = api1.get_blockchain_events_channel(
        token_address,
        channel12.partner_state.address,
    )
    assert any(
        (
            event['event'] == ChannelEvent.DEPOSIT and
            is_same_address(
                event['args']['participant'],
                to_normalized_address(api1.address),
            ) and
            event['args']['total_deposit'] == deposit
        )
        for event in event_list2
    )

    api1.channel_close(registry_address, token_address, api2.address)

    # Load the new state with the channel closed
    channel12 = get_channelstate(node1, node2, token_network_identifier)

    event_list3 = api1.get_blockchain_events_channel(
        token_address,
        channel12.partner_state.address,
    )
    assert len(event_list3) > len(event_list2)
    assert any(
        (
            event['event'] == ChannelEvent.CLOSED and
            is_same_address(
                event['args']['closing_participant'],
                to_normalized_address(api1.address),
            )
        )
        for event in event_list3
    )
    assert channel.get_status(channel12) == CHANNEL_STATE_CLOSED

    settlement_block = (
        channel12.close_transaction.finished_block_number +
        channel12.settle_timeout +
        10  # arbitrary number of additional blocks, used to wait for the settle() call
    )
    wait_until_block(node1.raiden.chain, settlement_block + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS)

    state_changes = node1.raiden.wal.storage.get_statechanges_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )

    assert must_contain_entry(state_changes, ContractReceiveChannelSettled, {
        'token_network_identifier': token_network_identifier,
        'channel_identifier': channel12.identifier,
    })
def is_channel_open_and_funded(channel_state):
    return (
        channel.get_status(channel_state) == CHANNEL_STATE_OPENED and
        channel_state.our_state.contract_balance > 0 and
        channel_state.partner_state.contract_balance > 0
    )
Beispiel #51
0
def get_best_routes(
        chain_state: ChainState,
        token_network_id: typing.Address,
        from_address: typing.Address,
        to_address: typing.Address,
        amount: int,
        previous_address: typing.Address,
) -> List[RouteState]:
    """ Returns a list of channels that can be used to make a transfer.

    This will filter out channels that are not open and don't have enough
    capacity.
    """
    # TODO: Route ranking.
    # Rate each route to optimize the fee price/quality of each route and add a
    # rate from in the range [0.0,1.0].

    available_routes = list()

    token_network = views.get_token_network_by_identifier(
        chain_state,
        token_network_id,
    )

    network_statuses = views.get_networkstatuses(chain_state)

    neighbors_heap = get_ordered_partners(
        token_network.network_graph.network,
        from_address,
        to_address,
    )

    if not neighbors_heap:
        log.warning(
            'No routes available from %s to %s' % (pex(from_address), pex(to_address)),
        )

    while neighbors_heap:
        _, partner_address = heappop(neighbors_heap)

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            token_network_id,
            partner_address,
        )

        # don't send the message backwards
        if partner_address == previous_address:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            log.info(
                'channel %s - %s is not opened, ignoring' %
                (pex(from_address), pex(partner_address)),
            )
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )

        if amount > distributable:
            log.info(
                'channel %s - %s doesnt have enough funds [%s], ignoring' %
                (pex(from_address), pex(partner_address), amount),
            )
            continue

        network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN)
        if network_state != NODE_NETWORK_REACHABLE:
            log.info(
                'partner for channel %s - %s is not %s, ignoring' %
                (pex(from_address), pex(partner_address), NODE_NETWORK_REACHABLE),
            )
            continue

        route_state = RouteState(partner_address, channel_state.identifier)
        available_routes.append(route_state)

    return available_routes
 def _assume_channel_opened(self, action):
     if not self.failing_path_2:
         needed_channel = self.address_to_channel[action.transfer.target]
         assume(channel.get_status(needed_channel) == channel.CHANNEL_STATE_OPENED)