Beispiel #1
0
def make_transfer_pair(payer,
                       payee,
                       initiator,
                       target,
                       amount,
                       expiration,
                       secret=None,
                       reveal_timeout=factories.UNIT_REVEAL_TIMEOUT):

    payer_expiration = expiration
    payee_expiration = expiration - reveal_timeout

    return MediationPairState(
        factories.make_route(payer, amount),
        factories.make_transfer(amount,
                                initiator,
                                target,
                                payer_expiration,
                                secret=secret),
        factories.make_route(payee, amount),
        factories.make_transfer(amount,
                                initiator,
                                target,
                                payee_expiration,
                                secret=secret),
    )
def test_no_valid_routes():
    from_route, from_transfer = factories.make_from(
        amount=factories.UNIT_TRANSFER_AMOUNT,
        target=factories.HOP2,
        from_expiration=factories.HOP1_TIMEOUT,
    )

    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT - 1),
        factories.make_route(factories.HOP3, available_balance=1),
    ]

    init_state_change = make_init_statechange(
        from_transfer,
        from_route,
        routes,
    )

    mediator_state_machine = StateManager(
        mediator.state_transition,
        None,
    )

    assert mediator_state_machine.current_state is None

    events = mediator_state_machine.dispatch(
        init_state_change,
    )

    assert mediator_state_machine.current_state is None

    assert len(events) == 1
    assert isinstance(events[0], SendRefundTransfer)
def test_next_route():
    target = factories.HOP1
    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT),
        factories.make_route(factories.HOP3, available_balance=factories.UNIT_TRANSFER_AMOUNT - 1),
        factories.make_route(factories.HOP4, available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    state = make_initiator_state(routes, target)

    assert state.route == routes[0], 'a initialized state must use the first valid route'

    assert state.routes.available_routes == routes[1:]
    assert not state.routes.ignored_routes
    assert not state.routes.refunded_routes
    assert not state.routes.canceled_routes

    with pytest.raises(AssertionError, message='cannot try a new route while one is in use'):
        initiator.try_new_route(state)

    state.routes.canceled_routes.append(state.route)
    state.route = None
    initiator.try_new_route(state)

    # HOP3 should be ignored because it doesnt have enough balance
    assert len(state.routes.ignored_routes) == 1

    assert not state.routes.available_routes
    assert not state.routes.refunded_routes
    assert state.routes.canceled_routes
def test_no_valid_routes():
    from_route, from_transfer = factories.make_from(
        amount=factories.UNIT_TRANSFER_AMOUNT,
        target=factories.HOP2,
        from_expiration=factories.HOP1_TIMEOUT,
    )

    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT - 1),
        factories.make_route(factories.HOP3, available_balance=1),
    ]

    init_state_change = make_init_statechange(
        from_transfer,
        from_route,
        routes,
    )

    mediator_state_machine = StateManager(
        mediator.state_transition,
        None,
    )

    assert mediator_state_machine.current_state is None

    events = mediator_state_machine.dispatch(
        init_state_change,
    )

    assert mediator_state_machine.current_state is None

    assert len(events) == 1
    assert isinstance(events[0], SendRefundTransfer)
Beispiel #5
0
def test_get_timeout_blocks():
    amount = 10
    initiator = factories.HOP1
    next_hop = factories.HOP2

    settle_timeout = 30
    block_number = 5

    route = factories.make_route(
        next_hop,
        amount,
        settle_timeout=settle_timeout,
    )

    early_expire = 10
    early_transfer = factories.make_transfer(amount, initiator, next_hop,
                                             early_expire)
    early_block = mediator.get_timeout_blocks(route, early_transfer,
                                              block_number)
    assert early_block == 5 - mediator.TRANSIT_BLOCKS, 'must use the lock expiration'

    equal_expire = 30
    equal_transfer = factories.make_transfer(amount, initiator, next_hop,
                                             equal_expire)
    equal_block = mediator.get_timeout_blocks(route, equal_transfer,
                                              block_number)
    assert equal_block == 25 - mediator.TRANSIT_BLOCKS

    large_expire = 70
    large_transfer = factories.make_transfer(amount, initiator, next_hop,
                                             large_expire)
    large_block = mediator.get_timeout_blocks(route, large_transfer,
                                              block_number)
    assert large_block == 30 - mediator.TRANSIT_BLOCKS, 'must use the settle timeout'

    closed_route = factories.make_route(
        next_hop,
        amount,
        settle_timeout=settle_timeout,
        closed_block=2,
    )

    large_block = mediator.get_timeout_blocks(closed_route, large_transfer,
                                              block_number)
    assert large_block == 27 - mediator.TRANSIT_BLOCKS, 'must use the close block'

    # the computed timeout may be negative, in which case the calling code must /not/ use it
    negative_block_number = large_expire
    negative_block = mediator.get_timeout_blocks(route, large_transfer,
                                                 negative_block_number)
    assert negative_block == -mediator.TRANSIT_BLOCKS
Beispiel #6
0
def test_refund_transfer_next_route():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [
        factories.make_route(mediator_address, available_balance=amount),
        factories.make_route(factories.HOP2, available_balance=amount),
    ]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    transfer = factories.make_transfer(
        amount,
        our_address,
        target_address,
        block_number + factories.UNIT_SETTLE_TIMEOUT,
    )

    state_change = ReceiveTransferRefund(
        sender=mediator_address,
        transfer=transfer,
    )

    prior_state = deepcopy(current_state)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )
    assert initiator_state_machine.current_state is not None

    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 1
    assert any(
        isinstance(e, SendMediatedTransfer) for e in events
    ), 'No mediated transfer event emitted, should have tried a new route'

    assert initiator_state_machine.current_state is not None
    assert initiator_state_machine.current_state.routes.canceled_routes[
        0] == prior_state.route
def test_refund_transfer_next_route():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [
        factories.make_route(mediator_address, available_balance=amount),
        factories.make_route(factories.HOP2, available_balance=amount),
    ]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    transfer = factories.make_transfer(
        amount,
        our_address,
        target_address,
        block_number + factories.UNIT_SETTLE_TIMEOUT,
    )

    state_change = ReceiveTransferRefund(
        sender=mediator_address,
        transfer=transfer,
    )

    prior_state = deepcopy(current_state)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )
    assert initiator_state_machine.current_state is not None

    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 1
    assert any(
        isinstance(e, SendMediatedTransfer) for e in events
    ), 'No mediated transfer event emitted, should have tried a new route'

    assert initiator_state_machine.current_state is not None
    assert initiator_state_machine.current_state.routes.canceled_routes[0] == prior_state.route
def test_cancel_transfer():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [
        factories.make_route(mediator_address, available_balance=amount),
    ]

    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    state_change = ActionCancelTransfer(
        identifier=identifier
    )

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )
    assert initiator_state_machine.current_state is not None

    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 1
    assert isinstance(events[0], EventTransferSentFailed)
    assert initiator_state_machine.current_state is None
def test_state_wait_secretrequest_valid():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    hashlock = current_state.transfer.hashlock

    state_change = ReceiveSecretRequest(
        identifier=identifier,
        amount=amount,
        hashlock=hashlock,
        sender=target_address,
    )

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )

    events = initiator_state_machine.dispatch(state_change)
    assert all(isinstance(event, SendRevealSecret) for event in events)
    assert len(events) == 1
Beispiel #10
0
def test_events_for_withdraw():
    """ On-chain withdraw must be done if the channel is closed, regardless of
    the unsafe region.
    """
    amount = 3
    expire = 10
    initiator = factories.HOP1

    transfer = factories.make_transfer(
        amount,
        initiator,
        factories.ADDR,
        expire,
        secret=factories.UNIT_SECRET,
    )
    route = factories.make_route(
        initiator,
        amount,
    )

    events = target.events_for_withdraw(
        transfer,
        route,
    )
    assert len(events) == 0

    route.state = CHANNEL_STATE_CLOSED
    events = target.events_for_withdraw(
        transfer,
        route,
    )
    assert isinstance(events[0], ContractSendWithdraw)
    assert events[0].channel_address == route.channel_address
def test_events_for_withdraw():
    """ On-chain withdraw must be done if the channel is closed, regardless of
    the unsafe region.
    """
    amount = 3
    expire = 10
    initiator = factories.HOP1

    transfer = factories.make_transfer(
        amount,
        initiator,
        factories.ADDR,
        expire,
        secret=factories.UNIT_SECRET,
    )
    route = factories.make_route(
        initiator,
        amount,
    )

    events = target.events_for_withdraw(
        transfer,
        route,
    )
    assert len(events) == 0

    route.state = CHANNEL_STATE_CLOSED
    events = target.events_for_withdraw(
        transfer,
        route,
    )
    assert isinstance(events[0], ContractSendWithdraw)
    assert events[0].channel_address == route.channel_address
Beispiel #12
0
def test_state_wait_secretrequest_valid():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    hashlock = current_state.transfer.hashlock

    state_change = ReceiveSecretRequest(
        identifier=identifier,
        amount=amount,
        hashlock=hashlock,
        sender=target_address,
    )

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )

    events = initiator_state_machine.dispatch(state_change)
    assert all(isinstance(event, SendRevealSecret) for event in events)
    assert len(events) == 1
Beispiel #13
0
def test_cancel_transfer():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [
        factories.make_route(mediator_address, available_balance=amount),
    ]

    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    state_change = ActionCancelTransfer(identifier=identifier)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )
    assert initiator_state_machine.current_state is not None

    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 1
    assert isinstance(events[0], EventTransferSentFailed)
    assert initiator_state_machine.current_state is None
Beispiel #14
0
def test_next_route_amount():
    """ Routes that dont have enough available_balance must be ignored. """
    amount = 10
    reveal_timeout = 30
    timeout_blocks = reveal_timeout + 10
    routes = [
        factories.make_route(
            factories.HOP2,
            available_balance=amount * 2,
            reveal_timeout=reveal_timeout,
        ),
        factories.make_route(
            factories.HOP1,
            available_balance=amount + 1,
            reveal_timeout=reveal_timeout,
        ),
        factories.make_route(
            factories.HOP3,
            available_balance=amount // 2,
            reveal_timeout=reveal_timeout,
        ),
        factories.make_route(
            factories.HOP4,
            available_balance=amount,
            reveal_timeout=reveal_timeout,
        ),
    ]

    routes_state = RoutesState(
        list(routes))  # copy because the list will be modified inplace

    route1 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route1 == routes[0]
    assert routes_state.available_routes == routes[1:]
    assert routes_state.ignored_routes == list()

    route2 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route2 == routes[1]
    assert routes_state.available_routes == routes[2:]
    assert routes_state.ignored_routes == list()

    route3 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route3 == routes[3]
    assert routes_state.available_routes == list()
    assert routes_state.ignored_routes == [routes[2]]

    assert mediator.next_route(routes_state, timeout_blocks, amount) is None
def test_next_route_amount():
    """ Routes that dont have enough available_balance must be ignored. """
    amount = 10
    reveal_timeout = 30
    timeout_blocks = reveal_timeout + 10
    routes = [
        factories.make_route(
            factories.HOP2,
            available_balance=amount * 2,
            reveal_timeout=reveal_timeout,
        ),
        factories.make_route(
            factories.HOP1,
            available_balance=amount + 1,
            reveal_timeout=reveal_timeout,
        ),
        factories.make_route(
            factories.HOP3,
            available_balance=amount // 2,
            reveal_timeout=reveal_timeout,
        ),
        factories.make_route(
            factories.HOP4,
            available_balance=amount,
            reveal_timeout=reveal_timeout,
        ),
    ]

    routes_state = RoutesState(list(routes))  # copy because the list will be modified inplace

    route1 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route1 == routes[0]
    assert routes_state.available_routes == routes[1:]
    assert routes_state.ignored_routes == list()

    route2 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route2 == routes[1]
    assert routes_state.available_routes == routes[2:]
    assert routes_state.ignored_routes == list()

    route3 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route3 == routes[3]
    assert routes_state.available_routes == list()
    assert routes_state.ignored_routes == [routes[2]]

    assert mediator.next_route(routes_state, timeout_blocks, amount) is None
Beispiel #16
0
def test_next_transfer_pair():
    timeout_blocks = 47
    block_number = 3
    balance = 10
    initiator = factories.HOP1
    target = factories.ADDR

    payer_route = factories.make_route(initiator, balance)
    payer_transfer = factories.make_transfer(balance,
                                             initiator,
                                             target,
                                             expiration=50)

    routes = [
        factories.make_route(factories.HOP2, available_balance=balance),
    ]
    routes_state = RoutesState(
        list(routes))  # copy because the list will be modified inplace

    pair, events = mediator.next_transfer_pair(
        payer_route,
        payer_transfer,
        routes_state,
        timeout_blocks,
        block_number,
    )

    assert pair.payer_route == payer_route
    assert pair.payer_transfer == payer_transfer
    assert pair.payee_route == routes[0]
    assert pair.payee_transfer.expiration < pair.payer_transfer.expiration

    assert isinstance(events[0], SendMediatedTransfer)
    transfer = events[0]
    assert transfer.identifier == payer_transfer.identifier
    assert transfer.token == payer_transfer.token
    assert transfer.amount == payer_transfer.amount
    assert transfer.hashlock == payer_transfer.hashlock
    assert transfer.initiator == payer_transfer.initiator
    assert transfer.target == payer_transfer.target
    assert transfer.expiration < payer_transfer.expiration
    assert transfer.receiver == pair.payee_route.node_address

    assert len(routes_state.available_routes) == 0
Beispiel #17
0
def test_state_wait_unlock_valid():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR
    secret_generator = SequenceGenerator()
    token = factories.UNIT_TOKEN_ADDRESS

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=secret_generator,
        identifier=identifier,
        token=token,
    )

    secret = secret_generator.secrets[0]
    assert secret is not None

    # setup the state for the wait unlock
    current_state.revealsecret = SendRevealSecret(
        identifier,
        secret,
        token,
        target_address,
        our_address,
    )

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )

    state_change = ReceiveSecretReveal(
        secret=secret,
        sender=mediator_address,
    )
    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 3

    assert any(isinstance(e, SendBalanceProof) for e in events)
    assert any(isinstance(e, EventTransferSentSuccess) for e in events)
    assert any(isinstance(e, EventUnlockSuccess) for e in events)

    balance_proof = next(e for e in events if isinstance(e, SendBalanceProof))
    complete = next(e for e in events
                    if isinstance(e, EventTransferSentSuccess))

    assert balance_proof.receiver == mediator_address
    assert complete.identifier == identifier
    assert initiator_state_machine.current_state is None, 'state must be cleaned'
def make_transfer_pair(
        payer,
        payee,
        initiator,
        target,
        amount,
        expiration,
        secret=None,
        reveal_timeout=factories.UNIT_REVEAL_TIMEOUT):

    payer_expiration = expiration
    payee_expiration = expiration - reveal_timeout

    return MediationPairState(
        factories.make_route(payer, amount),
        factories.make_transfer(amount, initiator, target, payer_expiration, secret=secret),
        factories.make_route(payee, amount),
        factories.make_transfer(amount, initiator, target, payee_expiration, secret=secret),
    )
def test_get_timeout_blocks():
    amount = 10
    initiator = factories.HOP1
    next_hop = factories.HOP2

    settle_timeout = 30
    block_number = 5

    route = factories.make_route(
        next_hop,
        amount,
        settle_timeout=settle_timeout,
    )

    early_expire = 10
    early_transfer = factories.make_transfer(amount, initiator, next_hop, early_expire)
    early_block = mediator.get_timeout_blocks(route, early_transfer, block_number)
    assert early_block == 5 - mediator.TRANSIT_BLOCKS, 'must use the lock expiration'

    equal_expire = 30
    equal_transfer = factories.make_transfer(amount, initiator, next_hop, equal_expire)
    equal_block = mediator.get_timeout_blocks(route, equal_transfer, block_number)
    assert equal_block == 25 - mediator.TRANSIT_BLOCKS

    large_expire = 70
    large_transfer = factories.make_transfer(amount, initiator, next_hop, large_expire)
    large_block = mediator.get_timeout_blocks(route, large_transfer, block_number)
    assert large_block == 30 - mediator.TRANSIT_BLOCKS, 'must use the settle timeout'

    closed_route = factories.make_route(
        next_hop,
        amount,
        settle_timeout=settle_timeout,
        closed_block=2,
    )

    large_block = mediator.get_timeout_blocks(closed_route, large_transfer, block_number)
    assert large_block == 27 - mediator.TRANSIT_BLOCKS, 'must use the close block'

    # the computed timeout may be negative, in which case the calling code must /not/ use it
    negative_block_number = large_expire
    negative_block = mediator.get_timeout_blocks(route, large_transfer, negative_block_number)
    assert negative_block == -mediator.TRANSIT_BLOCKS
def test_state_wait_unlock_valid():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR
    secret_generator = SequenceGenerator()
    token = factories.UNIT_TOKEN_ADDRESS

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=secret_generator,
        identifier=identifier,
        token=token,
    )

    secret = secret_generator.secrets[0]
    assert secret is not None

    # setup the state for the wait unlock
    current_state.revealsecret = SendRevealSecret(
        identifier,
        secret,
        token,
        target_address,
        our_address,
    )

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )

    state_change = ReceiveSecretReveal(
        secret=secret,
        sender=mediator_address,
    )
    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 3

    assert any(isinstance(e, SendBalanceProof) for e in events)
    assert any(isinstance(e, EventTransferSentSuccess) for e in events)
    assert any(isinstance(e, EventUnlockSuccess) for e in events)

    balance_proof = next(e for e in events if isinstance(e, SendBalanceProof))
    complete = next(e for e in events if isinstance(e, EventTransferSentSuccess))

    assert balance_proof.receiver == mediator_address
    assert complete.identifier == identifier
    assert initiator_state_machine.current_state is None, 'state must be cleaned'
def test_next_transfer_pair():
    timeout_blocks = 47
    block_number = 3
    balance = 10
    initiator = factories.HOP1
    target = factories.ADDR

    payer_route = factories.make_route(initiator, balance)
    payer_transfer = factories.make_transfer(balance, initiator, target, expiration=50)

    routes = [
        factories.make_route(factories.HOP2, available_balance=balance),
    ]
    routes_state = RoutesState(list(routes))  # copy because the list will be modified inplace

    pair, events = mediator.next_transfer_pair(
        payer_route,
        payer_transfer,
        routes_state,
        timeout_blocks,
        block_number,
    )

    assert pair.payer_route == payer_route
    assert pair.payer_transfer == payer_transfer
    assert pair.payee_route == routes[0]
    assert pair.payee_transfer.expiration < pair.payer_transfer.expiration

    assert isinstance(events[0], SendMediatedTransfer)
    transfer = events[0]
    assert transfer.identifier == payer_transfer.identifier
    assert transfer.token == payer_transfer.token
    assert transfer.amount == payer_transfer.amount
    assert transfer.hashlock == payer_transfer.hashlock
    assert transfer.initiator == payer_transfer.initiator
    assert transfer.target == payer_transfer.target
    assert transfer.expiration < payer_transfer.expiration
    assert transfer.receiver == pair.payee_route.node_address

    assert len(routes_state.available_routes) == 0
Beispiel #22
0
def test_next_route_reveal_timeout():
    """ Routes with a larger reveal timeout than timeout_blocks must be ignored. """
    amount = 10
    balance = 20
    timeout_blocks = 10
    routes = [
        factories.make_route(
            factories.HOP1,
            available_balance=balance,
            reveal_timeout=timeout_blocks * 2,
        ),
        factories.make_route(
            factories.HOP2,
            available_balance=balance,
            reveal_timeout=timeout_blocks + 1,
        ),
        factories.make_route(
            factories.HOP3,
            available_balance=balance,
            reveal_timeout=timeout_blocks // 2,
        ),
        factories.make_route(
            factories.HOP4,
            available_balance=balance,
            reveal_timeout=timeout_blocks,
        ),
    ]

    routes_state = RoutesState(
        list(routes))  # copy because the list will be modified inplace
    route1 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route1 == routes[2]
    assert routes_state.available_routes == [
        routes[3],
    ]
    assert routes_state.ignored_routes == [routes[0], routes[1]]

    assert mediator.next_route(routes_state, timeout_blocks, amount) is None
    assert routes_state.available_routes == list()
    assert routes_state.ignored_routes == [routes[0], routes[1], routes[3]]
Beispiel #23
0
def test_secret_learned():
    from_route, from_transfer = factories.make_from(
        amount=factories.UNIT_TRANSFER_AMOUNT,
        target=factories.HOP2,
        from_expiration=factories.HOP1_TIMEOUT,
    )

    routes = [
        factories.make_route(factories.HOP2,
                             available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    state = make_mediator_state(
        from_transfer,
        from_route,
        list(routes),
    )

    secret = factories.UNIT_SECRET
    payee_address = factories.HOP2

    iteration = mediator.secret_learned(
        state,
        secret,
        payee_address,
        'payee_secret_revealed',
    )
    transfer_pair = iteration.new_state.transfers_pair[0]

    assert from_transfer.expiration > transfer_pair.payee_transfer.expiration
    assert transfer_pair.payee_transfer.almost_equal(from_transfer)
    assert transfer_pair.payee_route == routes[0]

    assert transfer_pair.payer_route == from_route
    assert transfer_pair.payer_transfer == from_transfer

    assert iteration.new_state.secret == secret
    assert transfer_pair.payee_transfer.secret == secret
    assert transfer_pair.payer_transfer.secret == secret

    assert transfer_pair.payee_state == 'payee_balance_proof'
    assert transfer_pair.payer_state == 'payer_secret_revealed'

    reveal_events = [
        e for e in iteration.events if isinstance(e, SendRevealSecret)
    ]
    assert len(reveal_events) == 1

    balance_events = [
        e for e in iteration.events if isinstance(e, SendBalanceProof)
    ]
    assert len(balance_events) == 1
def test_next_route_reveal_timeout():
    """ Routes with a larger reveal timeout than timeout_blocks must be ignored. """
    amount = 10
    balance = 20
    timeout_blocks = 10
    routes = [
        factories.make_route(
            factories.HOP1,
            available_balance=balance,
            reveal_timeout=timeout_blocks * 2,
        ),
        factories.make_route(
            factories.HOP2,
            available_balance=balance,
            reveal_timeout=timeout_blocks + 1,
        ),
        factories.make_route(
            factories.HOP3,
            available_balance=balance,
            reveal_timeout=timeout_blocks // 2,
        ),
        factories.make_route(
            factories.HOP4,
            available_balance=balance,
            reveal_timeout=timeout_blocks,
        ),
    ]

    routes_state = RoutesState(list(routes))  # copy because the list will be modified inplace
    route1 = mediator.next_route(routes_state, timeout_blocks, amount)
    assert route1 == routes[2]
    assert routes_state.available_routes == [routes[3], ]
    assert routes_state.ignored_routes == [routes[0], routes[1]]

    assert mediator.next_route(routes_state, timeout_blocks, amount) is None
    assert routes_state.available_routes == list()
    assert routes_state.ignored_routes == [routes[0], routes[1], routes[3]]
Beispiel #25
0
def test_init_mediator():
    from_route, from_transfer = factories.make_from(
        amount=factories.UNIT_TRANSFER_AMOUNT,
        target=factories.HOP2,
        from_expiration=factories.HOP1_TIMEOUT,
    )

    routes = [
        factories.make_route(factories.HOP2,
                             available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    init_state_change = make_init_statechange(
        from_transfer,
        from_route,
        routes,
    )

    mediator_state_machine = StateManager(
        mediator.state_transition,
        None,
    )

    assert mediator_state_machine.current_state is None

    events = mediator_state_machine.dispatch(init_state_change, )

    mediator_state = mediator_state_machine.current_state
    assert isinstance(mediator_state, MediatorState)
    assert mediator_state.our_address == factories.ADDR
    assert mediator_state.block_number == init_state_change.block_number
    assert mediator_state.transfers_pair[0].payer_transfer == from_transfer
    assert mediator_state.transfers_pair[0].payer_route == from_route

    assert len(
        events
    ), 'we have a valid route, the mediated transfer event must be emited'

    mediated_transfers = [
        e for e in events if isinstance(e, SendMediatedTransfer)
    ]
    assert len(mediated_transfers
               ) == 1, 'mediated_transfer should /not/ split the transfer'
    mediated_transfer = mediated_transfers[0]

    assert mediated_transfer.token == from_transfer.token, 'transfer token address mismatch'
    assert mediated_transfer.amount == from_transfer.amount, 'transfer amount mismatch'
    assert mediated_transfer.expiration < from_transfer.expiration, 'transfer expiration mismatch'
    assert mediated_transfer.hashlock == from_transfer.hashlock, 'wrong hashlock'
Beispiel #26
0
def test_next_route():
    target = factories.HOP1
    routes = [
        factories.make_route(factories.HOP2,
                             available_balance=factories.UNIT_TRANSFER_AMOUNT),
        factories.make_route(factories.HOP3,
                             available_balance=factories.UNIT_TRANSFER_AMOUNT -
                             1),
        factories.make_route(factories.HOP4,
                             available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    state = make_initiator_state(routes, target)

    assert state.route == routes[
        0], 'a initialized state must use the first valid route'

    assert state.routes.available_routes == routes[1:]
    assert not state.routes.ignored_routes
    assert not state.routes.refunded_routes
    assert not state.routes.canceled_routes

    with pytest.raises(AssertionError,
                       message='cannot try a new route while one is in use'):
        initiator.try_new_route(state)

    state.routes.canceled_routes.append(state.route)
    state.route = None
    initiator.try_new_route(state)

    # HOP3 should be ignored because it doesnt have enough balance
    assert len(state.routes.ignored_routes) == 1

    assert not state.routes.available_routes
    assert not state.routes.refunded_routes
    assert state.routes.canceled_routes
def test_init_mediator():
    from_route, from_transfer = factories.make_from(
        amount=factories.UNIT_TRANSFER_AMOUNT,
        target=factories.HOP2,
        from_expiration=factories.HOP1_TIMEOUT,
    )

    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    init_state_change = make_init_statechange(
        from_transfer,
        from_route,
        routes,
    )

    mediator_state_machine = StateManager(
        mediator.state_transition,
        None,
    )

    assert mediator_state_machine.current_state is None

    events = mediator_state_machine.dispatch(
        init_state_change,
    )

    mediator_state = mediator_state_machine.current_state
    assert isinstance(mediator_state, MediatorState)
    assert mediator_state.our_address == factories.ADDR
    assert mediator_state.block_number == init_state_change.block_number
    assert mediator_state.transfers_pair[0].payer_transfer == from_transfer
    assert mediator_state.transfers_pair[0].payer_route == from_route

    assert len(events), 'we have a valid route, the mediated transfer event must be emited'

    mediated_transfers = [
        e for e in events
        if isinstance(e, SendMediatedTransfer)
    ]
    assert len(mediated_transfers) == 1, 'mediated_transfer should /not/ split the transfer'
    mediated_transfer = mediated_transfers[0]

    assert mediated_transfer.token == from_transfer.token, 'transfer token address mismatch'
    assert mediated_transfer.amount == from_transfer.amount, 'transfer amount mismatch'
    assert mediated_transfer.expiration < from_transfer.expiration, 'transfer expiration mismatch'
    assert mediated_transfer.hashlock == from_transfer.hashlock, 'wrong hashlock'
Beispiel #28
0
def test_state_wait_unlock_invalid():
    identifier = identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR
    secret_generator = SequenceGenerator()
    token = factories.UNIT_TOKEN_ADDRESS

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=secret_generator,
        token=token,
    )

    secret = secret_generator.secrets[0]
    assert secret is not None

    current_state.revealsecret = SendRevealSecret(
        identifier,
        secret,
        token,
        target_address,
        our_address,
    )

    before_state = deepcopy(current_state)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )

    state_change = ReceiveSecretReveal(
        secret=secret,
        sender=factories.ADDR,
    )
    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 0
    assert initiator_state_machine.current_state.revealsecret is not None
    assert_state_equal(initiator_state_machine.current_state, current_state)
    assert_state_equal(current_state, before_state)
def test_state_wait_unlock_invalid():
    identifier = identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR
    secret_generator = SequenceGenerator()
    token = factories.UNIT_TOKEN_ADDRESS

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=secret_generator,
        token=token,
    )

    secret = secret_generator.secrets[0]
    assert secret is not None

    current_state.revealsecret = SendRevealSecret(
        identifier,
        secret,
        token,
        target_address,
        our_address,
    )

    before_state = deepcopy(current_state)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )

    state_change = ReceiveSecretReveal(
        secret=secret,
        sender=factories.ADDR,
    )
    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 0
    assert initiator_state_machine.current_state.revealsecret is not None
    assert_state_equal(initiator_state_machine.current_state, current_state)
    assert_state_equal(current_state, before_state)
def test_secret_learned():
    from_route, from_transfer = factories.make_from(
        amount=factories.UNIT_TRANSFER_AMOUNT,
        target=factories.HOP2,
        from_expiration=factories.HOP1_TIMEOUT,
    )

    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    state = make_mediator_state(
        from_transfer,
        from_route,
        list(routes),
    )

    secret = factories.UNIT_SECRET
    payee_address = factories.HOP2

    iteration = mediator.secret_learned(
        state,
        secret,
        payee_address,
        'payee_secret_revealed',
    )
    transfer_pair = iteration.new_state.transfers_pair[0]

    assert from_transfer.expiration > transfer_pair.payee_transfer.expiration
    assert transfer_pair.payee_transfer.almost_equal(from_transfer)
    assert transfer_pair.payee_route == routes[0]

    assert transfer_pair.payer_route == from_route
    assert transfer_pair.payer_transfer == from_transfer

    assert iteration.new_state.secret == secret
    assert transfer_pair.payee_transfer.secret == secret
    assert transfer_pair.payer_transfer.secret == secret

    assert transfer_pair.payee_state == 'payee_balance_proof'
    assert transfer_pair.payer_state == 'payer_secret_revealed'

    reveal_events = [e for e in iteration.events if isinstance(e, SendRevealSecret)]
    assert len(reveal_events) == 1

    balance_events = [e for e in iteration.events if isinstance(e, SendBalanceProof)]
    assert len(balance_events) == 1
Beispiel #31
0
def test_refund_transfer_invalid_sender():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [
        factories.make_route(mediator_address, available_balance=amount),
    ]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    transfer = factories.make_transfer(
        amount,
        our_address,
        target_address,
        block_number + factories.UNIT_SETTLE_TIMEOUT,
    )

    state_change = ReceiveTransferRefund(
        sender=our_address,
        transfer=transfer,
    )

    prior_state = deepcopy(current_state)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )
    assert initiator_state_machine.current_state is not None

    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 0
    assert initiator_state_machine.current_state is not None
    assert_state_equal(initiator_state_machine.current_state, prior_state)
def test_refund_transfer_invalid_sender():
    identifier = 1
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR

    routes = [
        factories.make_route(mediator_address, available_balance=amount),
    ]
    current_state = make_initiator_state(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=SequenceGenerator(),
        identifier=identifier,
    )

    transfer = factories.make_transfer(
        amount,
        our_address,
        target_address,
        block_number + factories.UNIT_SETTLE_TIMEOUT,
    )

    state_change = ReceiveTransferRefund(
        sender=our_address,
        transfer=transfer,
    )

    prior_state = deepcopy(current_state)

    initiator_state_machine = StateManager(
        initiator.state_transition,
        current_state,
    )
    assert initiator_state_machine.current_state is not None

    events = initiator_state_machine.dispatch(state_change)
    assert len(events) == 0
    assert initiator_state_machine.current_state is not None
    assert_state_equal(initiator_state_machine.current_state, prior_state)
Beispiel #33
0
def test_write_read_log(tmpdir, in_memory_database):
    log = init_database(tmpdir, in_memory_database)

    block_number = 1337
    block = Block(block_number)
    identifier = 42
    balance = 79
    route = factories.make_route(factories.ADDR, balance)
    action_route_change = ActionRouteChange(identifier, route)
    contract_receive_withdraw = ContractReceiveWithdraw(
        factories.ADDR,
        factories.UNIT_SECRET,
        factories.HOP1
    )

    assert log.log(block) == 1
    assert log.log(action_route_change) == 2
    assert log.log(contract_receive_withdraw) == 3

    result1 = log.get_state_change_by_id(1)
    result2 = log.get_state_change_by_id(2)
    result3 = log.get_state_change_by_id(3)

    assert isinstance(result1, Block)
    assert result1.block_number == block_number
    assert isinstance(result2, ActionRouteChange)
    assert result2.identifier == identifier
    assert isinstance(result2.route, RouteState)
    assert result2.route == route
    assert isinstance(result3, ContractReceiveWithdraw)
    assert result3.channel_address == factories.ADDR
    assert result3.secret == factories.UNIT_SECRET
    assert result3.receiver == factories.HOP1

    # Make sure state snapshot can only go for corresponding state change ids
    with pytest.raises(sqlite3.IntegrityError):
        log.storage.write_state_snapshot(34, 'AAAA')
    # Make sure we can only have a single state snapshot
    assert log.storage.get_state_snapshot() is None
    log.storage.write_state_snapshot(1, 'AAAA')
    assert (1, 'AAAA') == log.storage.get_state_snapshot()
    log.storage.write_state_snapshot(2, 'BBBB')
    assert (2, 'BBBB') == log.storage.get_state_snapshot()
def test_events_for_refund():
    amount = 10
    expiration = 30
    reveal_timeout = 17
    timeout_blocks = expiration
    block_number = 1
    initiator = factories.HOP1
    target = factories.HOP6

    refund_route = factories.make_route(
        initiator,
        amount,
        reveal_timeout=reveal_timeout,
    )

    refund_transfer = factories.make_transfer(
        amount,
        initiator,
        target,
        expiration,
    )

    small_timeout_blocks = reveal_timeout
    small_refund_events = mediator.events_for_refund_transfer(
        refund_route,
        refund_transfer,
        small_timeout_blocks,
        block_number,
    )
    assert len(small_refund_events) == 0

    refund_events = mediator.events_for_refund_transfer(
        refund_route,
        refund_transfer,
        timeout_blocks,
        block_number,
    )
    assert refund_events[0].expiration < block_number + timeout_blocks
    assert refund_events[0].amount == amount
    assert refund_events[0].hashlock == refund_transfer.hashlock
    assert refund_events[0].receiver == refund_route.node_address
def test_events_for_refund():
    amount = 10
    expiration = 30
    reveal_timeout = 17
    timeout_blocks = expiration
    block_number = 1
    initiator = factories.HOP1
    target = factories.HOP6

    refund_route = factories.make_route(
        initiator,
        amount,
        reveal_timeout=reveal_timeout,
    )

    refund_transfer = factories.make_transfer(
        amount,
        initiator,
        target,
        expiration,
    )

    small_timeout_blocks = reveal_timeout
    small_refund_events = mediator.events_for_refund_transfer(
        refund_route,
        refund_transfer,
        small_timeout_blocks,
        block_number,
    )
    assert len(small_refund_events) == 0

    refund_events = mediator.events_for_refund_transfer(
        refund_route,
        refund_transfer,
        timeout_blocks,
        block_number,
    )
    assert refund_events[0].expiration < block_number + timeout_blocks
    assert refund_events[0].amount == amount
    assert refund_events[0].hashlock == refund_transfer.hashlock
    assert refund_events[0].receiver == refund_route.node_address
def test_mediate_transfer():
    amount = 10
    block_number = 5
    expiration = 30

    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    routes_state = RoutesState(routes)
    state = MediatorState(
        factories.ADDR,
        routes_state,
        block_number,
        factories.UNIT_HASHLOCK,
    )

    payer_route, payer_transfer = factories.make_from(amount, factories.HOP6, expiration)

    iteration = mediator.mediate_transfer(
        state,
        payer_route,
        payer_transfer,
    )

    events_mediated = [
        e
        for e in iteration.events
        if isinstance(e, SendMediatedTransfer)
    ]

    assert len(events_mediated) == 1
    transfer = events_mediated[0]
    assert transfer.identifier == payer_transfer.identifier
    assert transfer.token == payer_transfer.token
    assert transfer.amount == payer_transfer.amount
    assert transfer.hashlock == payer_transfer.hashlock
    assert transfer.target == payer_transfer.target
    assert payer_transfer.expiration > transfer.expiration
    assert transfer.receiver == routes[0].node_address
def test_mediate_transfer():
    amount = 10
    block_number = 5
    expiration = 30

    routes = [
        factories.make_route(factories.HOP2, available_balance=factories.UNIT_TRANSFER_AMOUNT),
    ]

    routes_state = RoutesState(routes)
    state = MediatorState(
        factories.ADDR,
        routes_state,
        block_number,
        factories.UNIT_HASHLOCK,
    )

    payer_route, payer_transfer = factories.make_from(amount, factories.HOP6, expiration)

    iteration = mediator.mediate_transfer(
        state,
        payer_route,
        payer_transfer,
    )

    events_mediated = [
        e
        for e in iteration.events
        if isinstance(e, SendMediatedTransfer)
    ]

    assert len(events_mediated) == 1
    transfer = events_mediated[0]
    assert transfer.identifier == payer_transfer.identifier
    assert transfer.token == payer_transfer.token
    assert transfer.amount == payer_transfer.amount
    assert transfer.hashlock == payer_transfer.hashlock
    assert transfer.target == payer_transfer.target
    assert payer_transfer.expiration > transfer.expiration
    assert transfer.receiver == routes[0].node_address
def test_init_with_usable_routes():
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR
    secret_generator = SequenceGenerator()

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    init_state_change = make_init_statechange(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=secret_generator,
    )

    expiration = block_number + factories.HOP1_TIMEOUT

    initiator_state_machine = StateManager(
        initiator.state_transition,
        None,
    )

    assert initiator_state_machine.current_state is None

    events = initiator_state_machine.dispatch(
        init_state_change,
    )

    initiator_state = initiator_state_machine.current_state
    assert isinstance(initiator_state, InitiatorState)
    assert initiator_state.our_address == our_address

    transfer = initiator_state.transfer
    assert isinstance(transfer, LockedTransferState)
    assert transfer.amount == amount
    assert transfer.target == target_address
    assert transfer.secret == secret_generator.secrets[0]
    assert transfer.hashlock == sha3(transfer.secret)

    assert len(events), 'we have a valid route, the mediated transfer event must be emited'

    mediated_transfers = [
        e for e in events
        if isinstance(e, SendMediatedTransfer)
    ]
    assert len(mediated_transfers) == 1, 'mediated_transfer should /not/ split the transfer'
    mediated_transfer = mediated_transfers[0]

    assert mediated_transfer.token == factories.UNIT_TOKEN_ADDRESS
    assert mediated_transfer.amount == amount, 'transfer amount mismatch'
    assert mediated_transfer.expiration == expiration, 'transfer expiration mismatch'

    secret_hash = sha3(secret_generator.secrets[0])
    assert mediated_transfer.hashlock == secret_hash, 'wrong hashlock'
    assert mediated_transfer.receiver == mediator_address, 'wrong mediator address'

    assert initiator_state.route == routes[0]
    assert len(initiator_state.routes.available_routes) == 0
    assert len(initiator_state.routes.ignored_routes) == 0
    assert len(initiator_state.routes.refunded_routes) == 0
    assert len(initiator_state.routes.canceled_routes) == 0
Beispiel #39
0
def test_init_with_usable_routes():
    amount = factories.UNIT_TRANSFER_AMOUNT
    block_number = 1
    mediator_address = factories.HOP1
    target_address = factories.HOP2
    our_address = factories.ADDR
    secret_generator = SequenceGenerator()

    routes = [factories.make_route(mediator_address, available_balance=amount)]
    init_state_change = make_init_statechange(
        routes,
        target_address,
        block_number=block_number,
        our_address=our_address,
        secret_generator=secret_generator,
    )

    expiration = block_number + factories.HOP1_TIMEOUT

    initiator_state_machine = StateManager(
        initiator.state_transition,
        None,
    )

    assert initiator_state_machine.current_state is None

    events = initiator_state_machine.dispatch(init_state_change, )

    initiator_state = initiator_state_machine.current_state
    assert isinstance(initiator_state, InitiatorState)
    assert initiator_state.our_address == our_address

    transfer = initiator_state.transfer
    assert isinstance(transfer, LockedTransferState)
    assert transfer.amount == amount
    assert transfer.target == target_address
    assert transfer.secret == secret_generator.secrets[0]
    assert transfer.hashlock == sha3(transfer.secret)

    assert len(
        events
    ), 'we have a valid route, the mediated transfer event must be emited'

    mediated_transfers = [
        e for e in events if isinstance(e, SendMediatedTransfer)
    ]
    assert len(mediated_transfers
               ) == 1, 'mediated_transfer should /not/ split the transfer'
    mediated_transfer = mediated_transfers[0]

    assert mediated_transfer.token == factories.UNIT_TOKEN_ADDRESS
    assert mediated_transfer.amount == amount, 'transfer amount mismatch'
    assert mediated_transfer.expiration == expiration, 'transfer expiration mismatch'
    assert mediated_transfer.hashlock == sha3(
        secret_generator.secrets[0]), 'wrong hashlock'
    assert mediated_transfer.receiver == mediator_address, 'wrong mediator address'

    assert initiator_state.route == routes[0]
    assert len(initiator_state.routes.available_routes) == 0
    assert len(initiator_state.routes.ignored_routes) == 0
    assert len(initiator_state.routes.refunded_routes) == 0
    assert len(initiator_state.routes.canceled_routes) == 0