Skip to content

Commit e3c74b2

Browse files
authored
Merge pull request #1149 from pipermerriam/piper/expand-round-trip-to-other-apis
Expand round trip to GetNodeData and NodeData commands
2 parents 01d327f + 502e40a commit e3c74b2

17 files changed

+412
-118
lines changed

tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828

2929
@pytest.fixture(autouse=True, scope="session")
30-
def vm_logger(namespaces=LOGGING_NAMESPACES):
30+
def _stdout_logging(namespaces=LOGGING_NAMESPACES):
3131
for namespace in namespaces:
3232
logger = logging.getLogger(namespace)
3333

@@ -43,14 +43,14 @@ def vm_logger(namespaces=LOGGING_NAMESPACES):
4343

4444
logger.addHandler(handler)
4545

46-
return logger
46+
logger.info('Set level for logger: %s', namespace)
4747

4848

4949
# Uncomment this to have logs from tests written to a file. This is useful for
5050
# debugging when you need to dump the VM output from test runs.
5151
"""
5252
@pytest.yield_fixture(autouse=True)
53-
def vm_file_logger(request):
53+
def _file_logging(request):
5454
import datetime
5555
import os
5656

tests/json-fixtures/test_state.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import os
23

34
import pytest
@@ -51,16 +52,14 @@
5152
ForkName
5253
)
5354

54-
from tests.conftest import vm_logger
55-
5655

5756
ROOT_PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
5857

5958

6059
BASE_FIXTURE_PATH = os.path.join(ROOT_PROJECT_DIR, 'fixtures', 'GeneralStateTests')
6160

6261

63-
LOGGER = vm_logger()
62+
logger = logging.getLogger('eth.tests.fixtures.GeneralStateTests')
6463

6564

6665
@to_tuple
@@ -314,7 +313,7 @@ def test_state_fixtures(fixture, fixture_vm_class):
314313
except ValidationError as err:
315314
block = vm.block
316315
transaction_error = err
317-
LOGGER.warn("Got transaction error", exc_info=True)
316+
logger.warn("Got transaction error", exc_info=True)
318317
else:
319318
transaction_error = False
320319

tests/p2p/test_peer_collect_sub_proto_msgs.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
from trinity.protocol.eth.peer import ETHPeer
77
from trinity.protocol.eth.commands import GetBlockHeaders, GetNodeData
8-
from trinity.protocol.eth.requests import HeaderRequest
8+
from trinity.protocol.eth.requests import (
9+
HeaderRequest,
10+
NodeDataRequest,
11+
)
912

1013
from tests.trinity.core.peer_helpers import (
1114
get_directly_linked_peers,
@@ -27,11 +30,11 @@ async def test_peer_subscriber_filters_messages(request, event_loop):
2730

2831
with peer.collect_sub_proto_messages() as collector:
2932
assert collector in peer._subscribers
30-
remote.sub_proto.send_get_node_data([b'\x00' * 32])
33+
remote.sub_proto.send_get_node_data(NodeDataRequest([b'\x00' * 32]))
3134
remote.sub_proto.send_get_block_headers(HeaderRequest(0, 1, 0, False))
32-
remote.sub_proto.send_get_node_data([b'\x00' * 32])
35+
remote.sub_proto.send_get_node_data(NodeDataRequest([b'\x00' * 32]))
3336
remote.sub_proto.send_get_block_headers(HeaderRequest(1, 1, 0, False))
34-
remote.sub_proto.send_get_node_data([b'\x00' * 32])
37+
remote.sub_proto.send_get_node_data(NodeDataRequest([b'\x00' * 32]))
3538
await asyncio.sleep(0.01)
3639

3740
assert collector not in peer._subscribers

tests/p2p/test_peer_subscriber.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88

99
from trinity.protocol.eth.peer import ETHPeer
1010
from trinity.protocol.eth.commands import GetBlockHeaders
11-
from trinity.protocol.eth.requests import HeaderRequest
11+
from trinity.protocol.eth.requests import (
12+
HeaderRequest,
13+
NodeDataRequest,
14+
)
1215

1316
from tests.trinity.core.peer_helpers import (
1417
get_directly_linked_peers,
@@ -45,11 +48,11 @@ async def test_peer_subscriber_filters_messages(request, event_loop):
4548
peer.add_subscriber(header_subscriber)
4649
peer.add_subscriber(all_subscriber)
4750

48-
remote.sub_proto.send_get_node_data([b'\x00' * 32])
51+
remote.sub_proto.send_get_node_data(NodeDataRequest([b'\x00' * 32]))
4952
remote.sub_proto.send_get_block_headers(HeaderRequest(0, 1, 0, False))
50-
remote.sub_proto.send_get_node_data([b'\x00' * 32])
53+
remote.sub_proto.send_get_node_data(NodeDataRequest([b'\x00' * 32]))
5154
remote.sub_proto.send_get_block_headers(HeaderRequest(1, 1, 0, False))
52-
remote.sub_proto.send_get_node_data([b'\x00' * 32])
55+
remote.sub_proto.send_get_node_data(NodeDataRequest([b'\x00' * 32]))
5356

5457
# yeild to let remote and peer transmit.
5558
await asyncio.sleep(0.01)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import os
2+
import random
3+
4+
import pytest
5+
6+
from eth_utils import keccak
7+
8+
from p2p.exceptions import ValidationError
9+
10+
from trinity.protocol.eth.requests import NodeDataRequest
11+
12+
13+
def mk_node():
14+
node_length = random.randint(0, 2048)
15+
node = os.urandom(node_length)
16+
return node
17+
18+
19+
def mk_node_data(n):
20+
if n == 0:
21+
return tuple(), tuple()
22+
nodes = tuple(set(mk_node() for _ in range(n)))
23+
node_keys = tuple(keccak(node) for node in nodes)
24+
return node_keys, nodes
25+
26+
27+
def test_node_data_request_empty_response_is_valid():
28+
node_keys, _ = mk_node_data(10)
29+
request = NodeDataRequest(node_keys)
30+
31+
request.validate_response(tuple())
32+
33+
34+
def test_node_data_request_with_full_response():
35+
node_keys, nodes = mk_node_data(10)
36+
request = NodeDataRequest(node_keys)
37+
node_data = tuple(zip(node_keys, nodes))
38+
39+
request.validate_response(node_data)
40+
41+
42+
def test_node_data_request_with_partial_response():
43+
node_keys, nodes = mk_node_data(10)
44+
request = NodeDataRequest(node_keys)
45+
node_data = tuple(zip(node_keys, nodes))
46+
47+
request.validate_response(node_data[3:])
48+
request.validate_response(node_data[:3])
49+
request.validate_response((node_data[1], node_data[8], node_data[4]))
50+
51+
52+
def test_node_data_request_with_fully_invalid_response():
53+
node_keys, nodes = mk_node_data(10)
54+
request = NodeDataRequest(node_keys)
55+
56+
# construct a unique set of other nodes
57+
other_nodes = tuple(set(mk_node() for _ in range(10)).difference(nodes))
58+
other_node_data = tuple((keccak(node), node) for node in other_nodes)
59+
60+
with pytest.raises(ValidationError):
61+
request.validate_response(other_node_data)
62+
63+
64+
def test_node_data_request_with_extra_unrequested_nodes():
65+
node_keys, nodes = mk_node_data(10)
66+
request = NodeDataRequest(node_keys)
67+
node_data = tuple(zip(node_keys, nodes))
68+
69+
# construct a unique set of other nodes
70+
other_nodes = tuple(set(mk_node() for _ in range(10)).difference(nodes))
71+
other_node_data = tuple((keccak(node), node) for node in other_nodes)
72+
73+
with pytest.raises(ValidationError):
74+
request.validate_response(node_data + other_node_data)
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import asyncio
2+
import os
3+
import random
4+
5+
import pytest
6+
7+
from eth_utils import (
8+
keccak,
9+
)
10+
11+
from trinity.protocol.eth.peer import ETHPeer
12+
13+
from tests.trinity.core.peer_helpers import (
14+
get_directly_linked_peers,
15+
)
16+
17+
18+
@pytest.fixture
19+
async def eth_peer_and_remote(request, event_loop):
20+
peer, remote = await get_directly_linked_peers(
21+
request,
22+
event_loop,
23+
peer1_class=ETHPeer,
24+
peer2_class=ETHPeer,
25+
)
26+
return peer, remote
27+
28+
29+
def mk_node():
30+
node_length = random.randint(0, 2048)
31+
node = os.urandom(node_length)
32+
return node
33+
34+
35+
def mk_node_data(n):
36+
if n == 0:
37+
return tuple(), tuple()
38+
nodes = tuple(set(mk_node() for _ in range(n)))
39+
node_keys = tuple(keccak(node) for node in nodes)
40+
return node_keys, nodes
41+
42+
43+
@pytest.mark.parametrize(
44+
'node_keys,nodes',
45+
(
46+
(
47+
(keccak(b''),),
48+
(b'',),
49+
),
50+
mk_node_data(1),
51+
mk_node_data(4),
52+
mk_node_data(20),
53+
mk_node_data(128),
54+
mk_node_data(384),
55+
)
56+
)
57+
@pytest.mark.asyncio
58+
async def test_eth_peer_get_node_data_round_trip(eth_peer_and_remote, node_keys, nodes):
59+
peer, remote = eth_peer_and_remote
60+
node_data = tuple(zip(node_keys, nodes))
61+
62+
async def send_node_data():
63+
remote.sub_proto.send_node_data(nodes)
64+
65+
asyncio.ensure_future(send_node_data())
66+
response = await peer.requests.get_node_data(node_keys)
67+
68+
assert len(response) == len(node_keys)
69+
assert response == node_data
70+
71+
72+
@pytest.mark.asyncio
73+
async def test_eth_peer_get_headers_round_trip_partial_response(eth_peer_and_remote):
74+
peer, remote = eth_peer_and_remote
75+
76+
node_keys, nodes = mk_node_data(32)
77+
node_data = tuple(zip(node_keys, nodes))
78+
79+
async def send_responses():
80+
remote.sub_proto.send_transactions([])
81+
await asyncio.sleep(0)
82+
remote.sub_proto.send_node_data(nodes[:10])
83+
await asyncio.sleep(0)
84+
85+
asyncio.ensure_future(send_responses())
86+
response = await peer.requests.get_node_data(node_keys)
87+
88+
assert len(response) == 10
89+
assert response[:10] == node_data[:10]
90+
91+
92+
@pytest.mark.asyncio
93+
async def test_eth_peer_get_headers_round_trip_with_noise(eth_peer_and_remote):
94+
peer, remote = eth_peer_and_remote
95+
96+
node_keys, nodes = mk_node_data(32)
97+
node_data = tuple(zip(node_keys, nodes))
98+
99+
async def send_responses():
100+
remote.sub_proto.send_transactions([])
101+
await asyncio.sleep(0)
102+
remote.sub_proto.send_node_data(nodes)
103+
await asyncio.sleep(0)
104+
105+
asyncio.ensure_future(send_responses())
106+
response = await peer.requests.get_node_data(node_keys)
107+
108+
assert len(response) == len(nodes)
109+
assert response == node_data
110+
111+
112+
@pytest.mark.asyncio
113+
async def test_eth_peer_get_headers_round_trip_does_not_match_invalid_response(eth_peer_and_remote):
114+
peer, remote = eth_peer_and_remote
115+
116+
node_keys, nodes = mk_node_data(32)
117+
node_data = tuple(zip(node_keys, nodes))
118+
119+
wrong_nodes = tuple(set(mk_node() for _ in range(32)).difference(nodes))
120+
121+
async def send_responses():
122+
remote.sub_proto.send_node_data(wrong_nodes)
123+
await asyncio.sleep(0)
124+
remote.sub_proto.send_transactions([])
125+
await asyncio.sleep(0)
126+
remote.sub_proto.send_node_data(nodes)
127+
await asyncio.sleep(0)
128+
129+
asyncio.ensure_future(send_responses())
130+
response = await peer.requests.get_node_data(node_keys)
131+
132+
assert len(response) == len(nodes)
133+
assert response == node_data

tests/trinity/core/p2p-proto/test_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ async def mock_do_handshake(peer):
130130
async def test_peer_pool_connect(monkeypatch, event_loop, receiver_server_with_dumb_peer):
131131
started_peers = []
132132

133-
def mock_start_peer(peer):
133+
async def mock_start_peer(peer):
134134
nonlocal started_peers
135135
started_peers.append(peer)
136136

trinity/p2p/handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async def handle_get_node_data(self, peer: ETHPeer, node_hashes: List[Hash32]) -
7777
continue
7878
nodes.append(node)
7979
self.logger.trace("Replying to %s with %d trie nodes", peer, len(nodes))
80-
peer.sub_proto.send_node_data(nodes)
80+
peer.sub_proto.send_node_data(tuple(nodes))
8181

8282
async def lookup_headers(self,
8383
request: BaseHeaderRequest) -> Tuple[BlockHeader, ...]:

0 commit comments

Comments
 (0)