Skip to content

Commit

Permalink
bugfix: python's api close was non-functional
Browse files Browse the repository at this point in the history
This fixes the python api close, add a regression test for that problem
and add general tests for it.
  • Loading branch information
hackaugusto committed Nov 22, 2017
1 parent 6522f98 commit 3832815
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 30 deletions.
15 changes: 8 additions & 7 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
Changelog
=========

* :feature:`1097` Added `--gas-price` command line option
* :feature:`1038` Introduce an upper limit for the ``settle_timeout`` attribute of the netting channel
* :bug:`1044` Rename ``/connection`` API endpoint to ``/connections`` for consistency
* :bug:`1138` REST and Python API close did not working if a transfer was made.
* :feature:`1097` Added `--gas-price` command line option.
* :feature:`1038` Introduce an upper limit for the ``settle_timeout`` attribute of the netting channel.
* :bug:`1044` Rename ``/connection`` API endpoint to ``/connections`` for consistency.
* :bug: `1049` Make raiden byzantium compatible by no longer relying on ``estimateGas``.
* :feature: `507` Making python's channels crash resilient (recoverable). Note, this is a breaking change, the serialization format of channel objects changed to a WAL compatible representation.
* :feature:`1037` Add ``show_default`` to CLI options
* :feature:`1037` Add ``show_default`` to CLI options.
* :feature:`670` Block raiden startup until ethereum node is fully synchronized.
* :feature:`1010` Add ``amount`` and ``target`` to ``EventTransferSentSuccess`` event
* :feature:`1010` Add ``amount`` and ``target`` to ``EventTransferSentSuccess`` event.
* :feature:`1022` Include an ``errors`` field in all unsuccessful API responses.
* :bug:`450` Removed ``block_number`` from contracts events, using block_number from block on which it was mined.
* :bug:`870` User selectable NAT traversal
* :bug:`870` User selectable NAT traversal.
* :feature:`921` Add ``/api/1/connection`` API endpoint returning information about all connected token networks.
* :bug:`1011` Remove ``settled`` attribute from the NettingChannel smart contract.

* :release:`0.1.0 <2017-09-12>`
* :release:`0.1.0 <2017-09-12>`.
* :feature:`-` This is the `Raiden Developer Preview <https://github.com/raiden-network/raiden/releases/tag/v0.1.0>`_ release. Introduces a raiden test network on ropsten, the API and all the basic functionality required to use Raiden in Dapps. For more information read the `blog post <https://medium.com/@raiden_network/raiden-network-developer-preview-dad83ec3fc23>`_ or the `documentation of v0.1.0 <http://raiden-network.readthedocs.io/en/v0.1.0/>`_.
24 changes: 12 additions & 12 deletions raiden/api/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,10 +459,7 @@ def get_channel_list(self, token_address=None, partner_address=None):
filtered by a token address and/or partner address.
Raises:
KeyError:
- An error occurred when the given partner address isn't associated
with the given token address.
- An error occurred when the token address is unknown to the node.
KeyError: An error occurred when the token address is unknown to the node.
"""

if token_address and not isaddress(token_address):
Expand All @@ -471,19 +468,22 @@ def get_channel_list(self, token_address=None, partner_address=None):
if partner_address and not isaddress(partner_address):
raise InvalidAddress('Expected binary address format for partner in get_channel_list')

result = None
result = list()

if token_address and partner_address:
graph = self.raiden.token_to_channelgraph[token_address]

# Let it raise the KeyError
channel = graph.partneraddress_to_channel[partner_address]
result = [channel]
channel = graph.partneraddress_to_channel.get(partner_address)

if channel:
result = [channel]

elif token_address:
graph = self.raiden.token_to_channelgraph[token_address]
token_channels = graph.address_to_channel.values()
result = token_channels
graph = self.raiden.token_to_channelgraph.get(token_address)

if graph:
token_channels = graph.address_to_channel.values()
result = token_channels

elif partner_address:
partner_channels = [
Expand Down Expand Up @@ -598,7 +598,7 @@ def close(self, token_address, partner_address):
graph = self.raiden.token_to_channelgraph[token_address]
channel = graph.partneraddress_to_channel[partner_address]

balance_proof = channel.our_state.balance_proof
balance_proof = channel.partner_state.balance_proof
channel.external_state.close(balance_proof)

return channel
Expand Down
159 changes: 159 additions & 0 deletions raiden/tests/api/test_pythonapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
import pytest

from raiden.api.python import RaidenAPI
from raiden.exceptions import InvalidState
from raiden.network.protocol import (
NODE_NETWORK_REACHABLE,
NODE_NETWORK_UNKNOWN,
)
from raiden.transfer.state import (
CHANNEL_STATE_CLOSED,
CHANNEL_STATE_OPENED,
CHANNEL_STATE_SETTLED,
)
from raiden.utils import get_contract_path
from raiden.tests.utils.blockchain import wait_until_block


@pytest.mark.parametrize('privatekey_seed', ['test_token_addresses:{}'])
@pytest.mark.parametrize('number_of_nodes', [2])
@pytest.mark.parametrize('number_of_tokens', [2])
def test_token_addresses(raiden_network, token_addresses):
node1, node2 = raiden_network
token_address = token_addresses[0]

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

assert api1.address == node1.raiden.address

assert set(api1.tokens) == set(token_addresses)
assert set(api1.get_tokens_list()) == set(token_addresses)

channels = api1.get_channel_list(token_address, api2.address)
assert api1.get_channel_list(token_address) == channels
assert len(api1.get_channel_list()) == 2

assert api1.get_node_network_state(api2.address) == NODE_NETWORK_REACHABLE


@pytest.mark.parametrize('privatekey_seed', ['test_token_registration:{}'])
@pytest.mark.parametrize('number_of_nodes', [1])
@pytest.mark.parametrize('number_of_tokens', [0])
def test_token_registration(blockchain_type, raiden_network, tester_state):
if blockchain_type == 'tester':
pytest.skip(
'current version of the pyethereum dependency does not support the REVERT opcode'
)

node1 = raiden_network[0]
token_amount = 1000

token_address = node1.raiden.chain.deploy_contract(
contract_name='HumanStandardToken',
contract_path=get_contract_path('HumanStandardToken.sol'),
constructor_parameters=(token_amount, 'raiden', 2, 'Rd'),
)

api1 = RaidenAPI(node1.raiden)
assert not api1.get_tokens_list()

assert api1.manager_address_if_token_registered(token_address) is None

node1.raiden.poll_blockchain_events()
assert not api1.get_tokens_list()

api1.register_token(token_address)

assert api1.manager_address_if_token_registered(token_address) is not None
assert api1.get_tokens_list() == [token_address]


@pytest.mark.parametrize('privatekey_seed', ['test_channel_lifetime:{}'])
@pytest.mark.parametrize('number_of_nodes', [2])
@pytest.mark.parametrize('channels_per_node', [0])
def test_channel_lifecycle(blockchain_type, raiden_network, token_addresses, deposit):
if blockchain_type == 'tester':
pytest.skip(
'there is not support ATM for retrieving events from tester'
)

node1, node2 = raiden_network
token_address = token_addresses[0]

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

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

# this is a synchronous api
api1.open(token_address, api2.address)
channels = api1.get_channel_list(token_address, api2.address)
assert len(channels) == 1
channel12 = channels[0]

event_list1 = api1.get_channel_events(
channel12.channel_address,
channel12.external_state.opened_block,
)
assert event_list1 == []

# the channel has no deposit yet
assert channel12.state == CHANNEL_STATE_OPENED

api1.deposit(token_address, api2.address, deposit)

assert channel12.state == CHANNEL_STATE_OPENED
assert channel12.balance == deposit
assert channel12.contract_balance == deposit
assert api1.get_channel_list(token_address, api2.address) == [channel12]

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

event_list2 = api1.get_channel_events(
channel12.channel_address,
channel12.external_state.opened_block,
)
assert any(
(
event['_event_type'] == 'ChannelNewBalance' and
event['participant'] == api1.address.encode('hex')
)
for event in event_list2
)

with pytest.raises(InvalidState):
api1.settle(token_address, api2.address)

api1.close(token_address, api2.address)
node1.raiden.poll_blockchain_events()

event_list3 = api1.get_channel_events(
channel12.channel_address,
channel12.external_state.opened_block,
)
assert len(event_list3) > len(event_list2)
assert any(
(
event['_event_type'] == 'ChannelClosed' and
event['closing_address'] == api1.address.encode('hex')
)
for event in event_list3
)
assert channel12.state == CHANNEL_STATE_CLOSED

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

node1.raiden.poll_blockchain_events()
assert channel12.state == CHANNEL_STATE_SETTLED
23 changes: 23 additions & 0 deletions raiden/tests/api/test_pythonapi_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
import pytest

from raiden.api.python import RaidenAPI


@pytest.mark.parametrize('privatekey_seed', ['test_close_regression:{}'])
@pytest.mark.parametrize('number_of_nodes', [2])
@pytest.mark.parametrize('channels_per_node', [1])
def test_close_regression(raiden_network, token_addresses):
""" The python api was using the wrong balance proof to close the channel,
thus the close was failling if a transfer was made.
"""
node1, node2 = raiden_network
token_address = token_addresses[0]

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

amount = 10
assert api1.transfer(token_address, amount, api2.address)

api1.close(token_address, api2.address)
3 changes: 1 addition & 2 deletions raiden/tests/integration/test_snapshotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ def test_snapshotting(raiden_network, token_addresses):
channel_0_1 = api0.get_channel_list(token_addresses[0], app1.raiden.address)
channel_0_2 = api0.get_channel_list(token_addresses[0], app2.raiden.address)

with pytest.raises(KeyError):
api1.get_channel_list(token_addresses[0], app2.raiden.address)
assert not api1.get_channel_list(token_addresses[0], app2.raiden.address)

assert len(channel_0_1) == 1
assert len(channel_0_2) == 1
Expand Down
16 changes: 7 additions & 9 deletions raiden/tests/unit/api/test_python_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,14 @@ def test_get_channel_list(raiden_network, token_addresses):
assert channel1 in api1.get_channel_list(token_addresses[0], app0.raiden.address)
assert not api1.get_channel_list(partner_address=app2.raiden.address)

with pytest.raises(KeyError):
api1.get_channel_list(
token_address=token_addresses[0],
partner_address=app2.raiden.address,
)
assert not api1.get_channel_list(
token_address=token_addresses[0],
partner_address=app2.raiden.address,
)

with pytest.raises(KeyError):
api2.get_channel_list(
token_address=app2.raiden.address,
)
assert not api2.get_channel_list(
token_address=app2.raiden.address,
)


@pytest.mark.parametrize('channels_per_node', [0])
Expand Down

0 comments on commit 3832815

Please sign in to comment.