def events_for_balanceproof(transfers_pair, block_number):
    """ 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_open = pair.payee_route.state == CHANNEL_STATE_OPENED

        # XXX: All nodes must close the channel and withdraw 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, block_number)

        if payee_channel_open and payee_knows_secret and not payee_payed and lock_valid:
            pair.payee_state = 'payee_balance_proof'
            balance_proof = SendBalanceProof(
                pair.payee_transfer.identifier,
                pair.payee_route.channel_address,
                pair.payee_transfer.token,
                pair.payee_route.node_address,
                pair.payee_transfer.secret,
            )
            unlock_success = EventUnlockSuccess(
                pair.payer_transfer.identifier,
                pair.payer_transfer.hashlock,
            )
            events.append(balance_proof)
            events.append(unlock_success)

    return events
Example #2
0
def events_for_unlock_lock(
    initiator_state: InitiatorTransferState,
    channel_state: NettingChannelState,
    secret: typing.Secret,
    secrethash: typing.SecretHash,
    pseudo_random_generator: random.Random,
):
    # 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=channel_state,
        message_identifier=message_identifier,
        payment_identifier=transfer_description.payment_identifier,
        secret=secret,
        secrethash=secrethash,
    )

    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,
    )

    return [unlock_lock, payment_sent_success, unlock_success]
Example #3
0
def handle_secretreveal(state, state_change):
    """ Send a balance proof to the next hop with the current mediated transfer
    lock removed and the balance updated.
    """
    if state_change.sender == state.route.node_address:
        # next hop learned the secret, unlock the token locally and send the
        # withdraw message to next hop
        transfer = state.transfer
        unlock_lock = SendBalanceProof(
            transfer.identifier,
            state.route.channel_address,
            transfer.token,
            state.route.node_address,
            transfer.secret,
        )

        transfer_success = EventTransferSentSuccess(
            transfer.identifier,
            transfer.amount,
            transfer.target,
        )

        unlock_success = EventUnlockSuccess(
            transfer.identifier,
            transfer.hashlock,
        )

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

    return iteration
Example #4
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
def handle_contractwithdraw(state, state_change):
    """ Handle a NettingChannelUnlock state change. """
    assert sha3(
        state.secret
    ) == state.hashlock, 'secret must be validated by the smart contract'

    # For all but the last pair in transfer pair a refund transfer ocurred,
    # meaning the same channel was used twice, once when this node sent the
    # mediated transfer and once when the refund transfer was received. A
    # ContractReceiveWithdraw state change may be used for each.

    events = list()

    # This node withdrew the refund
    if state_change.receiver == state.our_address:
        for previous_pos, pair in enumerate(state.transfers_pair, -1):
            if pair.payer_route.channel_address == state_change.channel_address:
                # always set the contract_withdraw regardless of the previous
                # state (even expired)
                pair.payer_state = 'payer_contract_withdraw'

                withdraw = EventWithdrawSuccess(
                    pair.payer_transfer.identifier,
                    pair.payer_transfer.hashlock,
                )
                events.append(withdraw)

                # if the current pair is backed by a refund set the sent
                # mediated transfer to a 'secret known' state
                if previous_pos > -1:
                    previous_pair = state.transfers_pair[previous_pos]

                    if previous_pair.payee_state not in STATE_TRANSFER_FINAL:
                        previous_pair.payee_state = 'payee_refund_withdraw'

    # A partner withdrew the mediated transfer
    else:
        for pair in state.transfers_pair:
            if pair.payer_route.channel_address == state_change.channel_address:
                unlock = EventUnlockSuccess(
                    pair.payee_transfer.identifier,
                    pair.payee_transfer.hashlock,
                )
                events.append(unlock)

                pair.payee_state = 'payee_contract_withdraw'

    iteration = secret_learned(
        state,
        state_change.secret,
        state_change.receiver,
        'payee_contract_withdraw',
    )

    iteration.events.extend(events)

    return iteration
Example #6
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
Example #7
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
Example #8
0
def handle_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretReveal,
    channel_state: NettingChannelState,
) -> 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.hashlock
                              == initiator_state.transfer_description.hashlock)

    # 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
        # withdraw message to next hop
        transfer_description = initiator_state.transfer_description

        unlock_lock = channel.send_unlock(
            channel_state,
            transfer_description.identifier,
            state_change.secret,
            state_change.hashlock,
        )

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

        unlock_success = EventUnlockSuccess(
            transfer_description.identifier,
            transfer_description.hashlock,
        )

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

    return iteration
Example #9
0
def events_for_unlock_base(
    initiator_state: InitiatorTransferState,
    channel_state: NettingChannelState,
    secret: Secret
) -> List[Event]:
    transfer_description = initiator_state.transfer_description
    payment_sent_success = EventPaymentSentSuccess(
        payment_network_identifier=channel_state.payment_network_identifier,
        token_network_identifier=TokenNetworkID(channel_state.token_network_identifier),
        identifier=transfer_description.payment_identifier,
        amount=transfer_description.amount,
        target=transfer_description.target,
        secret=secret,
    )

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

    return [payment_sent_success, unlock_success]
Example #10
0
def events_for_unlock_lock(
    initiator_state: InitiatorTransferState,
    channel_state: NettingChannelState,
    secret: Secret,
    secrethash: SecretHash,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> List[Event]:
    """ Unlocks the lock offchain, and emits the events for the successful payment. """
    # 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=channel_state,
        message_identifier=message_identifier,
        payment_identifier=transfer_description.payment_identifier,
        secret=secret,
        secrethash=secrethash,
        block_number=block_number,
    )

    payment_sent_success = EventPaymentSentSuccess(
        token_network_registry_address=channel_state.
        token_network_registry_address,
        token_network_address=channel_state.token_network_address,
        identifier=transfer_description.payment_identifier,
        amount=transfer_description.amount,
        target=transfer_description.target,
        secret=secret,
        route=initiator_state.route.route,
    )

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

    return [unlock_lock, payment_sent_success, unlock_success]
Example #11
0
def handle_contractunlock(
    state,
    state_change,
    channelidentifiers_to_channels,
    pseudo_random_generator,
    block_number,
):
    """ Handle a NettingChannelUnlock state change. """
    assert sha3(
        state.secret
    ) == state.secrethash, 'secret must be validated by the smart contract'

    # For all but the last pair in transfer pair a refund transfer ocurred,
    # meaning the same channel was used twice, once when this node sent the
    # mediated transfer and once when the refund transfer was received. A
    # ContractReceiveChannelUnlock state change may be used for each.

    events = list()

    # This node withdrew the refund
    if state_change.receiver == state.our_address:
        for previous_pos, pair in enumerate(state.transfers_pair, -1):
            payer_channel = get_payer_channel(channelidentifiers_to_channels,
                                              pair)
            if payer_channel.identifier == state_change.channel_identifier:
                # always set the contract_unlock regardless of the previous
                # state (even expired)
                pair.payer_state = 'payer_contract_unlock'

                unlock = EventUnlockClaimSuccess(
                    pair.payer_transfer.payment_identifier,
                    pair.payer_transfer.lock.secrethash,
                )
                events.append(unlock)

                # if the current pair is backed by a refund set the sent
                # mediated transfer to a 'secret known' state
                if previous_pos > -1:
                    previous_pair = state.transfers_pair[previous_pos]

                    if previous_pair.payee_state not in STATE_TRANSFER_FINAL:
                        previous_pair.payee_state = 'payee_refund_unlock'

    # A partner withdrew the mediated transfer
    else:
        for pair in state.transfers_pair:
            payee_channel = get_payee_channel(channelidentifiers_to_channels,
                                              pair)
            if payee_channel.identifier == state_change.channel_identifier:
                unlock = EventUnlockSuccess(
                    pair.payee_transfer.payment_identifier,
                    pair.payee_transfer.lock.secrethash,
                )
                events.append(unlock)

                pair.payee_state = 'payee_contract_unlock'

    iteration = secret_learned(
        state,
        channelidentifiers_to_channels,
        pseudo_random_generator,
        block_number,
        state_change.secret,
        state_change.secrethash,
        state_change.receiver,
        'payee_contract_unlock',
        False,
    )

    iteration.events.extend(events)

    return iteration