def test_handle_block_equal_block_number(): """ Nothing changes. """ setup = make_target_state() new_block = Block(block_number=1, gas_limit=1, block_hash=factories.make_transaction_hash()) iteration = target.state_transition( target_state=setup.new_state, state_change=new_block, channel_state=setup.channel, pseudo_random_generator=random.Random(), block_number=new_block.block_number, storage=None) assert iteration.new_state assert not iteration.events
def test_handle_block_lower_block_number(): """ Nothing changes. """ setup = make_target_state(block_number=10) new_block = Block( block_number=setup.block_number - 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = target.state_transition( target_state=setup.new_state, state_change=new_block, channel_state=setup.channel, pseudo_random_generator=setup.pseudo_random_generator, block_number=new_block.block_number, ) assert iteration.new_state assert not iteration.events
def _callback_new_block(self, latest_block): """Called once a new block is detected by the alarm task. Note: This should be called only once per block, otherwise there will be duplicated `Block` state changes in the log. Therefore this method should be called only once a new block is mined with the corresponding block data from the AlarmTask. """ # User facing APIs, which have on-chain side-effects, force polled the # blockchain to update the node's state. This force poll is used to # provide a consistent view to the user, e.g. a channel open call waits # for the transaction to be mined and force polled the event to update # the node's state. This pattern introduced a race with the alarm task # and the task which served the user request, because the events are # returned only once per filter. The lock below is to protect against # these races (introduced by the commit # 3686b3275ff7c0b669a6d5e2b34109c3bdf1921d) with self.event_poll_lock: latest_block_number = latest_block['number'] for event in self.blockchain_events.poll_blockchain_events( latest_block_number): # These state changes will be procesed with a block_number # which is /larger/ than the ChainState's block_number. on_blockchain_event(self, event) # On restart the Raiden node will re-create the filters with the # ethereum node. These filters will have the from_block set to the # value of the latest Block state change. To avoid missing events # the Block state change is dispatched only after all of the events # have been processed. # # This means on some corner cases a few events may be applied # twice, this will happen if the node crashed and some events have # been processed but the Block state change has not been # dispatched. state_change = Block( block_number=latest_block_number, gas_limit=latest_block['gasLimit'], block_hash=bytes(latest_block['hash']), ) self.handle_state_change(state_change)
def _callback_new_block(self, current_block_number, chain_id): """Called once a new block is detected by the alarm task. Note: This should be called only once per block, otherwise there will be duplicated `Block` state changes in the log. Therefore this method should be called only once a new block is mined with the appropriate block_number argument from the AlarmTask. """ # Raiden relies on blockchain events to update its off-chain state, # therefore some APIs /used/ to forcefully poll for events. # # This was done for APIs which have on-chain side-effects, e.g. # openning a channel, where polling the event is required to update # off-chain state to providing a consistent view to the caller, e.g. # the channel exists after the API call returns. # # That pattern introduced a race, because the events are returned only # once per filter, and this method would be called concurrently by the # API and the AlarmTask. The following lock is necessary, to ensure the # expected side-effects are properly applied (introduced by the commit # 3686b3275ff7c0b669a6d5e2b34109c3bdf1921d) with self.event_poll_lock: for event in self.blockchain_events.poll_blockchain_events( current_block_number): # These state changes will be procesed with a block_number # which is /larger/ than the ChainState's block_number. on_blockchain_event(self, event, current_block_number, chain_id) # On restart the Raiden node will re-create the filters with the # ethereum node. These filters will have the from_block set to the # value of the latest Block state change. To avoid missing events # the Block state change is dispatched only after all of the events # have been processed. # # This means on some corner cases a few events may be applied # twice, this will happen if the node crashed and some events have # been processed but the Block state change has not been # dispatched. state_change = Block(current_block_number) self.handle_state_change(state_change, current_block_number)
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_settle_with_locked_mediated_transfer_for_closing_party( deposit, settle_timeout, reveal_timeout, tester_state, tester_channels, tester_token): """ Test settle with a locked mediated transfer for the closing address. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial0 = tester_token.balanceOf(address0, sender=pkey0) initial1 = tester_token.balanceOf(address1, sender=pkey0) transferred_amount0 = 30 increase_transferred_amount(channel0, channel1, transferred_amount0) expiration0 = tester_state.block.number + reveal_timeout + 5 new_block = Block(tester_state.block.number) channel0.state_transition(new_block) channel1.state_transition(new_block) lock0 = Lock(amount=29, expiration=expiration0, hashlock=sha3('lock1')) mediated = make_mediated_transfer( channel0, channel1, address0, address1, lock0, pkey0, tester_state.block.number, ) transfer_data = str(mediated.packed().data) nettingchannel.close(transfer_data, sender=pkey1) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey1) # the balances only change by transferred_amount because the lock was /not/ unlocked balance0 = initial0 + deposit - transferred_amount0 balance1 = initial1 + transferred_amount0 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0 assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1
def test_handle_block_lower_block_number(): """ Nothing changes. """ initiator = factories.HOP6 our_address = factories.ADDR amount = 3 block_number = 1 expire = block_number + factories.UNIT_REVEAL_TIMEOUT state = make_target_state( our_address, amount, block_number, initiator, expire, ) new_block = Block(block_number - 1) iteration = target.state_transition(state, new_block) assert iteration.new_state.block_number == block_number
def test_restore_without_snapshot(): wal = new_wal() wal.log_and_dispatch(Block(5)) wal.log_and_dispatch(Block(7)) wal.log_and_dispatch(Block(8)) newwal = restore_from_latest_snapshot( state_transtion_acc, wal.storage, ) aggregate = newwal.state_manager.current_state assert aggregate.state_changes == [Block(5), Block(7), Block(8)]
def test_unlock_twice(reveal_timeout, tester_channels, tester_state): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] lock_expiration = tester_state.block.number + reveal_timeout + 5 secret = 'secretsecretsecretsecretsecretse' new_block = Block(tester_state.block.number) channel0.state_transition(new_block) channel1.state_transition(new_block) lock = Lock(17, lock_expiration, sha3(secret)) mediated0 = make_mediated_transfer( channel1, channel0, privatekey_to_address(pkey1), privatekey_to_address(pkey0), lock, pkey1, tester_state.block.number, secret, ) mediated0_data = str(mediated0.packed().data) unlock_proofs = list(channel0.our_state.balance_proof.get_known_unlocks()) assert len(unlock_proofs) == 1 proof = unlock_proofs[0] nettingchannel.close(mediated0_data, sender=pkey0) nettingchannel.unlock( proof.lock_encoded, ''.join(proof.merkle_proof), proof.secret, sender=pkey0, ) with pytest.raises(TransactionFailed): nettingchannel.unlock( proof.lock_encoded, ''.join(proof.merkle_proof), proof.secret, sender=pkey0, )
def test_handle_block(): """ Increase the block number. """ setup = make_target_state() new_block = Block( block_number=setup.block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = target.state_transition( target_state=setup.new_state, state_change=new_block, channel_state=setup.channel, pseudo_random_generator=setup.pseudo_random_generator, block_number=new_block.block_number, storage=None) assert iteration.new_state assert not iteration.events
def test_target_lock_is_expired_if_secret_is_not_registered_onchain(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, block_number=1) init = ActionInitTarget( from_hop=channels.get_hop(0), transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None secret_reveal_iteration = target.state_transition( target_state=init_transition.new_state, state_change=ReceiveSecretReveal(UNIT_SECRET, channels[0].partner_state.address), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) expired_block_number = channel.get_receiver_expiration_threshold(from_transfer.lock.expiration) iteration = target.state_transition( target_state=secret_reveal_iteration.new_state, state_change=Block(expired_block_number, None, None), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert search_for_item(iteration.events, EventUnlockClaimFailed, {})
def test_initiator_lock_expired_must_not_be_sent_if_channel_is_closed(): """ If the channel is closed there is no rason to send balance proofs off-chain, so a remove expired lock must not be sent when the channel is closed. """ block_number = 10 setup = setup_initiator_tests(amount=UNIT_TRANSFER_AMOUNT * 2, block_number=block_number) channel_closed = ContractReceiveChannelClosed( transaction_hash=factories.make_transaction_hash(), transaction_from=factories.make_address(), token_network_identifier=setup.channel.token_network_identifier, channel_identifier=setup.channel.identifier, block_number=block_number, ) channel_close_transition = channel.state_transition( channel_state=setup.channel, state_change=channel_closed, pseudo_random_generator=setup.prng, block_number=block_number, ) channel_state = channel_close_transition.new_state expiration_block_number = setup.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) channel_map = {channel_state.identifier: channel_state} iteration = initiator_manager.state_transition( setup.current_state, block, channel_map, setup.prng, expiration_block_number, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) is None
def test_unlock_expired_lock(reveal_timeout, tester_channels, tester_state): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] lock_timeout = reveal_timeout + 5 lock_expiration = tester_state.block.number + lock_timeout secret = 'expiredlockexpiredlockexpiredloc' hashlock = sha3(secret) new_block = Block(tester_state.block.number) channel0.state_transition(new_block) channel1.state_transition(new_block) lock1 = Lock(amount=31, expiration=lock_expiration, hashlock=hashlock) mediated0 = make_mediated_transfer( channel1, channel0, privatekey_to_address(pkey0), privatekey_to_address(pkey1), lock1, pkey1, tester_state.block.number, secret, ) mediated0_data = str(mediated0.packed().data) nettingchannel.close(mediated0_data, sender=pkey0) # expire the lock tester_state.mine(number_of_blocks=lock_timeout + 1) unlock_proofs = list(channel0.our_state.balance_proof.get_known_unlocks()) proof = unlock_proofs[0] with pytest.raises(TransactionFailed): nettingchannel.unlock( proof.lock_encoded, ''.join(proof.merkle_proof), proof.secret, sender=pkey0, )
def test_target_lock_is_expired_if_secret_is_not_registered_onchain(): lock_amount = 7 block_number = 1 pseudo_random_generator = random.Random() channels = make_channel_set([channel_properties2]) from_transfer = make_target_transfer(channels[0], amount=lock_amount, block_number=1) init = ActionInitTarget(channels.get_route(0), from_transfer) init_transition = target.state_transition( target_state=None, state_change=init, channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) assert init_transition.new_state is not None secret_reveal_iteration = target.state_transition( target_state=init_transition.new_state, state_change=ReceiveSecretReveal(UNIT_SECRET, channels[0].partner_state.address), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) expired_block_number = from_transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS iteration = target.state_transition( target_state=secret_reveal_iteration.new_state, state_change=Block(expired_block_number, None, None), channel_state=channels[0], pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, ) assert must_contain_entry(iteration.events, EventUnlockClaimFailed, {})
def test_upgrade_manager_restores_backup(tmp_path, monkeypatch): db_path = tmp_path / Path("v17_log.db") old_db_filename = tmp_path / Path("v16_log.db") with patch("raiden.storage.sqlite.RAIDEN_DB_VERSION", new=16), SQLiteStorage(str(old_db_filename)) as storage: state_change = Block( block_number=BlockNumber(0), gas_limit=BlockGasLimit(1000), block_hash=factories.make_block_hash(), ) block_data = JSONSerializer.serialize(state_change) storage.write_state_changes(state_changes=[block_data]) storage.update_version() upgrade_functions = [UpgradeRecord(from_version=16, function=Mock())] upgrade_functions[0].function.return_value = 17 web3, _ = create_fake_web3_for_block_hash(number_of_blocks=1) with monkeypatch.context() as m: m.setattr(raiden.utils.upgrades, "UPGRADES_LIST", upgrade_functions) m.setattr(raiden.utils.upgrades, "RAIDEN_DB_VERSION", 19) UpgradeManager(db_filename=db_path, web3=web3).run() # Once restored, the state changes written above should be # in the restored database with SQLiteStorage(str(db_path)) as storage: state_change_record = storage.get_latest_state_change_by_data_field( FilteredDBQuery( filters=[{ "_type": "raiden.transfer.state_change.Block" }], main_operator=Operator.NONE, inner_operator=Operator.NONE, )) assert state_change_record.data is not None
def test_handle_block(): """ Increase the block number. """ initiator = factories.HOP6 our_address = factories.ADDR amount = 3 block_number = 1 from_channel, state = make_target_state( our_address, amount, block_number, initiator, ) new_block = Block(block_number + 1) iteration = target.state_transition( state, new_block, from_channel, new_block.block_number, ) assert iteration.new_state assert not iteration.events
def test_handle_block_lower_block_number(): """ Nothing changes. """ initiator = factories.HOP6 our_address = factories.ADDR amount = 3 block_number = 10 from_channel, state = make_target_state( our_address, amount, block_number, initiator, ) new_block = Block(block_number - 1) iteration = target.state_transition( state, new_block, from_channel, new_block.block_number, ) assert iteration.new_state assert not iteration.events
def test_withdraw( tester_registry_address, deposit, settle_timeout, reveal_timeout, tester_channels, tester_chain, tester_token, ): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] pseudo_random_generator = random.Random() address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) lock_amount = 31 lock_expiration = tester_chain.block.number + reveal_timeout + 5 secret = b'secretsecretsecretsecretsecretse' secrethash = sha3(secret) new_block = Block(tester_chain.block.number) channel.state_transition( channel0, new_block, pseudo_random_generator, new_block.block_number, ) channel.state_transition( channel1, new_block, pseudo_random_generator, new_block.block_number, ) lock0 = Lock(lock_amount, lock_expiration, secrethash) mediated0 = make_mediated_transfer( tester_registry_address, channel0, channel1, address0, address1, lock0, pkey0, secret, ) # withdraw the pending transfer sent to us by our partner lock_state = lockstate_from_lock(mediated0.lock) proof = channel.compute_proof_for_lock( channel1.partner_state, secret, lock_state, ) mediated0_hash = sha3(mediated0.packed().data[:-65]) nettingchannel.close( mediated0.nonce, mediated0.transferred_amount, mediated0.locksroot, mediated0_hash, mediated0.signature, sender=pkey1, ) tester_chain.mine(number_of_blocks=1) nettingchannel.withdraw( proof.lock_encoded, b''.join(proof.merkle_proof), proof.secret, sender=pkey1, ) tester_chain.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock0.amount balance1 = initial_balance1 + deposit + lock0.amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0
def test_payer_enter_danger_zone_with_transfer_payed(): """ A mediator may have paid the next hop (payee), and didn't get paid by the previous hop (payer). When this happens, an assertion must not be hit, because it means the transfer must be unlocked on-chain. Issue: https://github.com/raiden-network/raiden/issues/1013 """ amount = 10 block_number = 5 target = HOP2 expiration = 30 pseudo_random_generator = random.Random() payer_channel = factories.make_channel( partner_balance=amount, partner_address=UNIT_TRANSFER_SENDER, token_address=UNIT_TOKEN_ADDRESS, ) payer_transfer = factories.make_signed_transfer_for( payer_channel, amount, HOP1, target, expiration, UNIT_SECRET, ) channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, ) channelmap = { channel1.identifier: channel1, payer_channel.identifier: payer_channel, } possible_routes = [factories.route_from_channel(channel1)] mediator_state = MediatorTransferState(UNIT_SECRETHASH) initial_iteration = mediator.mediate_transfer( mediator_state, possible_routes, payer_channel, channelmap, pseudo_random_generator, payer_transfer, block_number, ) send_transfer = must_contain_entry(initial_iteration.events, SendLockedTransfer, {}) assert send_transfer lock_expiration = send_transfer.transfer.lock.expiration new_state = initial_iteration.new_state for block_number in range(block_number, lock_expiration + 1): block_state_change = Block(block_number) block_iteration = mediator.handle_block( channelmap, new_state, block_state_change, block_number, ) new_state = block_iteration.new_state # send the balance proof, transitioning the payee state to paid assert new_state.transfers_pair[0].payee_state == 'payee_pending' receive_secret = ReceiveSecretReveal( UNIT_SECRET, channel1.partner_state.address, ) paid_iteration = mediator.state_transition( new_state, receive_secret, channelmap, pseudo_random_generator, block_number, ) paid_state = paid_iteration.new_state assert paid_state.transfers_pair[0].payee_state == 'payee_balance_proof' # move to the block in which the payee lock expires. This must not raise an # assertion expired_block_number = lock_expiration + 1 expired_block_state_change = Block(expired_block_number) block_iteration = mediator.handle_block( channelmap, paid_state, expired_block_state_change, expired_block_number, )
def test_deposit_must_wait_for_confirmation(): block_number = 10 confirmed_deposit_block_number = block_number + DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK + 1 our_model1, _ = create_model(0) partner_model1, _ = create_model(0) channel_state = create_channel_from_models(our_model1, partner_model1) payment_network_identifier = factories.make_address() token_address = factories.make_address() deposit_amount = 10 balance1_new = our_model1.balance + deposit_amount our_model2 = our_model1._replace( balance=balance1_new, distributable=balance1_new, contract_balance=balance1_new, ) partner_model2 = partner_model1 assert channel_state.our_state.contract_balance == 0 assert channel_state.partner_state.contract_balance == 0 deposit_transaction = TransactionChannelNewBalance( channel_state.our_state.address, deposit_amount, block_number, ) new_balance = ContractReceiveChannelNewBalance( payment_network_identifier, token_address, channel_state.identifier, deposit_transaction, ) pseudo_random_generator = random.Random() iteration = channel.state_transition( deepcopy(channel_state), new_balance, pseudo_random_generator, block_number, ) unconfirmed_state = iteration.new_state for block_number in range(block_number, confirmed_deposit_block_number): unconfirmed_block = Block(block_number) iteration = channel.state_transition( deepcopy(unconfirmed_state), unconfirmed_block, pseudo_random_generator, block_number, ) unconfirmed_state = iteration.new_state assert_partner_state( unconfirmed_state.our_state, unconfirmed_state.partner_state, our_model1, ) assert_partner_state( unconfirmed_state.partner_state, unconfirmed_state.our_state, partner_model1, ) confirmed_block = Block(confirmed_deposit_block_number) iteration = channel.state_transition( deepcopy(unconfirmed_state), confirmed_block, pseudo_random_generator, confirmed_deposit_block_number, ) confirmed_state = iteration.new_state assert_partner_state(confirmed_state.our_state, confirmed_state.partner_state, our_model2) assert_partner_state(confirmed_state.partner_state, confirmed_state.our_state, partner_model2)
def test_settle_two_locked_mediated_transfer_messages(deposit, settle_timeout, reveal_timeout, tester_state, tester_channels, tester_token): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey1) transferred_amount0 = 30 increase_transferred_amount(channel0, channel1, transferred_amount0) transferred_amount1 = 70 increase_transferred_amount(channel1, channel0, transferred_amount1) expiration0 = tester_state.block.number + reveal_timeout + 5 new_block = Block(tester_state.block.number) channel0.state_transition(new_block) channel1.state_transition(new_block) lock0 = Lock(amount=29, expiration=expiration0, hashlock=sha3('lock1')) mediated0 = make_mediated_transfer( channel0, channel1, address0, address1, lock0, pkey0, tester_state.block.number, ) mediated0_data = str(mediated0.packed().data) lock_expiration1 = tester_state.block.number + reveal_timeout + 5 lock1 = Lock(amount=31, expiration=lock_expiration1, hashlock=sha3('lock2')) mediated1 = make_mediated_transfer( channel1, channel0, address1, address0, lock1, pkey1, tester_state.block.number, ) mediated1_data = str(mediated1.packed().data) nettingchannel.close(mediated0_data, sender=pkey1) nettingchannel.updateTransfer(mediated1_data, sender=pkey0) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) # the balances only change by transferred_amount because the lock was /not/ unlocked balance0 = initial_balance0 + deposit - transferred_amount0 + transferred_amount1 balance1 = initial_balance1 + deposit + transferred_amount0 - transferred_amount1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey1) == 0 assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey1) == balance1
def test_withdraw_both_participants( deposit, settle_timeout, reveal_timeout, tester_channels, tester_state, tester_token): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) secret = 'secretsecretsecretsecretsecretse' hashlock = sha3(secret) lock_amount = 31 lock01_expiration = tester_state.block.number + settle_timeout - 1 * reveal_timeout lock10_expiration = tester_state.block.number + settle_timeout - 2 * reveal_timeout new_block = Block(tester_state.block.number) channel0.state_transition(new_block) channel1.state_transition(new_block) # using the same hashlock and amount is intentional lock01 = Lock(lock_amount, lock01_expiration, hashlock) lock10 = Lock(lock_amount, lock10_expiration, hashlock) mediated01 = make_mediated_transfer( channel0, channel1, address0, address1, lock01, pkey0, tester_state.block.number, secret, ) mediated01_data = str(mediated01.packed().data) mediated10 = make_mediated_transfer( channel1, channel0, address1, address0, lock10, pkey1, tester_state.block.number, secret, ) mediated10_data = str(mediated10.packed().data) nettingchannel.close(mediated01_data, sender=pkey1) tester_state.mine(number_of_blocks=1) nettingchannel.updateTransfer(mediated10_data, sender=pkey0) tester_state.mine(number_of_blocks=1) proof01 = channel1.our_state.balance_proof.compute_proof_for_lock( secret, mediated01.lock, ) nettingchannel.withdraw( proof01.lock_encoded, ''.join(proof01.merkle_proof), proof01.secret, sender=pkey1, ) proof10 = channel0.our_state.balance_proof.compute_proof_for_lock( secret, mediated10.lock, ) nettingchannel.withdraw( proof10.lock_encoded, ''.join(proof10.merkle_proof), proof10.secret, sender=pkey0, ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock01.amount + lock10.amount balance1 = initial_balance1 + deposit + lock01.amount - lock10.amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0
def test_withdraw( deposit, settle_timeout, reveal_timeout, tester_channels, tester_state, tester_token): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) lock_amount = 31 lock_expiration = tester_state.block.number + reveal_timeout + 5 secret = 'secretsecretsecretsecretsecretse' hashlock = sha3(secret) new_block = Block(tester_state.block.number) channel0.state_transition(new_block) channel1.state_transition(new_block) lock0 = Lock(lock_amount, lock_expiration, hashlock) mediated0 = make_mediated_transfer( channel0, channel1, address0, address1, lock0, pkey0, tester_state.block.number, secret, ) mediated0_data = str(mediated0.packed().data) proof = channel1.our_state.balance_proof.compute_proof_for_lock( secret, mediated0.lock, ) nettingchannel.close(mediated0_data, sender=pkey1) tester_state.mine(number_of_blocks=1) nettingchannel.withdraw( proof.lock_encoded, ''.join(proof.merkle_proof), proof.secret, sender=pkey1, ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock0.amount balance1 = initial_balance1 + deposit + lock0.amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0
def test_regression_mediator_not_update_payer_state_twice(): """ Regression Test for https://github.com/raiden-network/raiden/issues/3086 Make sure that after a lock expired the mediator doesn't update the pair twice causing EventUnlockClaimFailed to be generated at every block. """ pseudo_random_generator = random.Random() pair = factories.mediator_make_channel_pair() payer_channel, payee_channel = pair.channels payer_route = factories.route_from_channel(payer_channel) payer_transfer = factories.make_signed_transfer_for( payer_channel, LONG_EXPIRATION) available_routes = [factories.route_from_channel(payee_channel)] init_state_change = ActionInitMediator( routes=available_routes, from_route=payer_route, from_transfer=payer_transfer, ) iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=5, block_hash=factories.make_block_hash(), ) assert iteration.new_state is not None current_state = iteration.new_state send_transfer = search_for_item(iteration.events, SendLockedTransfer, {}) assert send_transfer transfer = send_transfer.transfer block_expiration_number = channel.get_sender_expiration_threshold( transfer.lock) block = Block( block_number=block_expiration_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = mediator.state_transition( mediator_state=current_state, state_change=block, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_expiration_number, block_hash=factories.make_block_hash(), ) msg = 'At the expiration block we should get an EventUnlockClaimFailed' assert search_for_item(iteration.events, EventUnlockClaimFailed, {}), msg current_state = iteration.new_state next_block = Block( block_number=block_expiration_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) # Initiator receives the secret reveal after the lock expired receive_secret = ReceiveSecretReveal( secret=UNIT_SECRET, sender=payee_channel.partner_state.address, ) iteration = mediator.state_transition( mediator_state=current_state, state_change=receive_secret, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=next_block.block_number, block_hash=next_block.block_hash, ) current_state = iteration.new_state lock = payer_transfer.lock secrethash = lock.secrethash assert secrethash in payer_channel.partner_state.secrethashes_to_lockedlocks assert current_state.transfers_pair[0].payee_state == 'payee_expired' assert not channel.is_secret_known(payer_channel.partner_state, secrethash) safe_to_wait, _ = mediator.is_safe_to_wait( lock_expiration=lock.expiration, reveal_timeout=payer_channel.reveal_timeout, block_number=lock.expiration + 10, ) assert not safe_to_wait iteration = mediator.state_transition( mediator_state=current_state, state_change=next_block, channelidentifiers_to_channels=pair.channel_map, nodeaddresses_to_networkstates=pair.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_expiration_number, block_hash=factories.make_block_hash(), ) msg = 'At the next block we should not get the same event' assert not search_for_item(iteration.events, EventUnlockClaimFailed, {}), msg
def test_write_read_log(): wal = new_wal() block_number = 1337 block = Block(block_number) unlocked_amount = 10 returned_amount = 5 participant = factories.make_address() partner = factories.make_address() locksroot = sha3(b'test_write_read_log') contract_receive_unlock = ContractReceiveChannelBatchUnlock( factories.make_transaction_hash(), factories.make_address(), participant, partner, locksroot, unlocked_amount, returned_amount, ) state_changes1 = wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) count1 = len(state_changes1) wal.log_and_dispatch(block) state_changes2 = wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) count2 = len(state_changes2) assert count1 + 1 == count2 wal.log_and_dispatch(contract_receive_unlock) state_changes3 = wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) count3 = len(state_changes3) assert count2 + 1 == count3 result1, result2 = state_changes3[-2:] assert isinstance(result1, Block) assert result1.block_number == block_number assert isinstance(result2, ContractReceiveChannelBatchUnlock) assert result2.participant == participant assert result2.partner == partner assert result2.locksroot == locksroot assert result2.unlocked_amount == unlocked_amount assert result2.returned_tokens == returned_amount # Make sure state snapshot can only go for corresponding state change ids with pytest.raises(sqlite3.IntegrityError): wal.storage.write_state_snapshot(34, 'AAAA') # Make sure we can only have a single state snapshot assert wal.storage.get_latest_state_snapshot() is None wal.storage.write_state_snapshot(1, 'AAAA') assert wal.storage.get_latest_state_snapshot() == (1, 'AAAA') wal.storage.write_state_snapshot(2, 'BBBB') assert wal.storage.get_latest_state_snapshot() == (2, 'BBBB')
def test_withdraw_both_participants( tester_registry_address, deposit, settle_timeout, reveal_timeout, tester_channels, tester_chain, tester_token, ): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] pseudo_random_generator = random.Random() address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) secret = b'secretsecretsecretsecretsecretse' secrethash = sha3(secret) lock_amount = 31 lock01_expiration = tester_chain.block.number + settle_timeout - 1 * reveal_timeout lock10_expiration = tester_chain.block.number + settle_timeout - 2 * reveal_timeout new_block = Block(tester_chain.block.number) channel.state_transition( channel0, new_block, pseudo_random_generator, new_block.block_number, ) channel.state_transition( channel1, new_block, pseudo_random_generator, new_block.block_number, ) # using the same secrethash and amount is intentional lock01 = Lock(lock_amount, lock01_expiration, secrethash) lock10 = Lock(lock_amount, lock10_expiration, secrethash) mediated01 = make_mediated_transfer( tester_registry_address, channel0, channel1, address0, address1, lock01, pkey0, secret, ) mediated10 = make_mediated_transfer( tester_registry_address, channel1, channel0, address1, address0, lock10, pkey1, secret, ) mediated01_hash = sha3(mediated01.packed().data[:-65]) nettingchannel.close( mediated01.nonce, mediated01.transferred_amount, mediated01.locksroot, mediated01_hash, mediated01.signature, sender=pkey1, ) tester_chain.mine(number_of_blocks=1) mediated10_hash = sha3(mediated10.packed().data[:-65]) nettingchannel.updateTransfer( mediated10.nonce, mediated10.transferred_amount, mediated10.locksroot, mediated10_hash, mediated10.signature, sender=pkey0, ) tester_chain.mine(number_of_blocks=1) lock_state01 = lockstate_from_lock(mediated01.lock) proof01 = channel.compute_proof_for_lock( channel1.partner_state, secret, lock_state01, ) nettingchannel.withdraw( proof01.lock_encoded, b''.join(proof01.merkle_proof), proof01.secret, sender=pkey1, ) lock_state10 = lockstate_from_lock(mediated10.lock) proof10 = channel.compute_proof_for_lock( channel0.partner_state, secret, lock_state10, ) nettingchannel.withdraw( proof10.lock_encoded, b''.join(proof10.merkle_proof), proof10.secret, sender=pkey0, ) tester_chain.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock01.amount + lock10.amount balance1 = initial_balance1 + deposit + lock01.amount - lock10.amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0
def test_regression_mediator_task_no_routes(): """ The mediator must only be cleared after the waiting transfer's lock has been handled. If a node receives a transfer to mediate, but there is no route available (because there is no sufficient capacity or the partner nodes are offline), and a refund is not possible, the mediator task must not be cleared, otherwise followup remove expired lock messages wont be processed and the nodes will get out of sync. """ pseudo_random_generator = random.Random() channels = make_channel_set([ NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=0), partner_state=NettingChannelEndStateProperties( balance=10, address=HOP2, privatekey=HOP2_KEY, ), ), ]) payer_transfer = factories.make_signed_transfer_for( channels[0], factories.LockedTransferSignedStateProperties( sender=HOP2, pkey=HOP2_KEY, transfer=factories.LockedTransferProperties(expiration=30), )) init_state_change = ActionInitMediator( channels.get_routes(), channels.get_route(0), payer_transfer, ) init_iteration = mediator.state_transition( mediator_state=None, state_change=init_state_change, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=5, block_hash=factories.make_block_hash(), ) msg = 'The task must not be cleared, even if there is no route to forward the transfer' assert init_iteration.new_state is not None, msg assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer assert search_for_item(init_iteration.events, SendLockedTransfer, {}) is None assert search_for_item(init_iteration.events, SendRefundTransfer, {}) is None secrethash = UNIT_SECRETHASH lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash] # Creates a transfer as it was from the *partner* send_lock_expired, _ = channel.create_sendexpiredlock( sender_end_state=channels[0].partner_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, chain_id=channels[0].chain_id, token_network_identifier=channels[0].token_network_identifier, channel_identifier=channels[0].identifier, recipient=channels[0].our_state.address, ) assert send_lock_expired lock_expired_message = message_from_sendevent(send_lock_expired, HOP1) lock_expired_message.sign(LocalSigner(channels.partner_privatekeys[0])) balance_proof = balanceproof_from_envelope(lock_expired_message) message_identifier = message_identifier_from_prng(pseudo_random_generator) # Regression: The mediator must still be able to process the block which # expires the lock expired_block_number = channel.get_sender_expiration_threshold(lock) block_hash = factories.make_block_hash() expire_block_iteration = mediator.state_transition( mediator_state=init_iteration.new_state, state_change=Block( block_number=expired_block_number, gas_limit=0, block_hash=block_hash, ), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=block_hash, ) assert expire_block_iteration.new_state is not None receive_expired_iteration = mediator.state_transition( mediator_state=expire_block_iteration.new_state, state_change=ReceiveLockExpired( balance_proof=balance_proof, secrethash=secrethash, message_identifier=message_identifier, ), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=expired_block_number, block_hash=block_hash, ) msg = 'The only used channel had the lock cleared, the task must be cleared' assert receive_expired_iteration.new_state is None, msg assert secrethash not in channels[ 0].partner_state.secrethashes_to_lockedlocks
def test_withdraw_lock_with_a_large_expiration(deposit, tester_channels, tester_state, tester_token, settle_timeout): """ Withdraw must accept a lock that expires after the settlement period. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) # use a really large expiration lock_expiration = tester_state.block.number + settle_timeout * 5 # work around for the python expiration validation bad_block_number = lock_expiration - 10 channel0.state_transition(Block(bad_block_number)) lock_amount = 29 secret = sha3('test_withdraw_lock_with_a_large_expiration') lock_hashlock = sha3(secret) lock = Lock( amount=lock_amount, expiration=lock_expiration, hashlock=lock_hashlock, ) mediated0 = make_mediated_transfer( channel0, channel1, address0, address1, lock, pkey0, bad_block_number, secret, ) nettingchannel.close(sender=pkey0) mediated0_hash = sha3(mediated0.packed().data[:-65]) nettingchannel.updateTransfer( mediated0.nonce, mediated0.transferred_amount, mediated0.locksroot, mediated0_hash, mediated0.signature, sender=pkey1, ) unlock_proofs = list(channel1.our_state.balance_proof.get_known_unlocks()) proof = unlock_proofs[0] nettingchannel.withdraw( proof.lock_encoded, ''.join(proof.merkle_proof), proof.secret, sender=pkey1, ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock_amount balance1 = initial_balance1 + deposit + lock_amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0
def test_payer_enter_danger_zone_with_transfer_payed(): """ A mediator may have paid the next hop (payee), and didn't get paid by the previous hop (payer). When this happens, an assertion must not be hit, because it means the transfer must be unlocked on-chain. Issue: https://github.com/raiden-network/raiden/issues/1013 """ block_number = 5 pseudo_random_generator = random.Random() channels = factories.mediator_make_channel_pair() payer_transfer = factories.make_signed_transfer_for( channels[0], LONG_EXPIRATION) initial_iteration = mediator.state_transition( mediator_state=None, state_change=factories.mediator_make_init_action( channels, payer_transfer), channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_number, block_hash=factories.make_block_hash(), ) send_transfer = search_for_item(initial_iteration.events, SendLockedTransfer, {}) assert send_transfer lock_expiration = send_transfer.transfer.lock.expiration new_state = initial_iteration.new_state for block_number in range(block_number, lock_expiration - channels[1].reveal_timeout): block_state_change = Block( block_number=block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) block_iteration = mediator.handle_block( new_state, block_state_change, channels.channel_map, pseudo_random_generator, ) new_state = block_iteration.new_state # send the balance proof, transitioning the payee state to paid assert new_state.transfers_pair[0].payee_state == 'payee_pending' receive_secret = ReceiveSecretReveal( UNIT_SECRET, channels[1].partner_state.address, ) paid_iteration = mediator.state_transition( mediator_state=new_state, state_change=receive_secret, channelidentifiers_to_channels=channels.channel_map, nodeaddresses_to_networkstates=channels.nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_number, block_hash=factories.make_block_hash(), ) paid_state = paid_iteration.new_state assert paid_state.transfers_pair[0].payee_state == 'payee_balance_proof' # move to the block in which the payee lock expires. This must not raise an # assertion expired_block_number = lock_expiration + 1 expired_block_state_change = Block( block_number=expired_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) block_iteration = mediator.handle_block( mediator_state=paid_state, state_change=expired_block_state_change, channelidentifiers_to_channels=channels.channel_map, pseudo_random_generator=pseudo_random_generator, )
def test_initiator_lock_expired(): amount = UNIT_TRANSFER_AMOUNT * 2 block_number = 1 refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) pseudo_random_generator = random.Random() channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), ] block_number = 10 current_state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, pseudo_random_generator, block_number, ) transfer = current_state.initiator.transfer assert transfer.lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks # Trigger lock expiry state_change = Block( block_number=transfer.lock.expiration + DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert events.must_contain_entry(iteration.events, SendLockExpired, { 'balance_proof': { 'nonce': 2, 'transferred_amount': 0, 'locked_amount': 0, }, 'secrethash': transfer.lock.secrethash, 'recipient': channel1.partner_state.address, }) assert transfer.lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks # Create 2 other transfers transfer2_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer2'), channel_map, pseudo_random_generator, 30, ) transfer2_lock = transfer2_state.initiator.transfer.lock transfer3_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer3'), channel_map, pseudo_random_generator, 32, ) transfer3_lock = transfer3_state.initiator.transfer.lock assert len(channel1.our_state.secrethashes_to_lockedlocks) == 2 assert transfer2_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks expiration_block_number = transfer2_lock.expiration + DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( transfer2_state, block, channel_map, pseudo_random_generator, expiration_block_number, ) # Transfer 2 expired assert transfer2_lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks # Transfer 3 is still there assert transfer3_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks