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_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_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
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_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_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
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
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_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
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]]
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]]
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'
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'
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
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_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_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
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