Skip to content

Commit 5534db7

Browse files
authored
Merge pull request #1169 from pipermerriam/piper/expand-round-trip-api-to-block-bodies
Expand round trip api to block bodies
2 parents ad21f73 + 916822b commit 5534db7

19 files changed

+1194
-324
lines changed

eth/db/trie.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import functools
2-
from typing import Dict, List, Tuple, Union
2+
from typing import Dict, Tuple, Union
33

44
import rlp
55
from trie import (
66
HexaryTrie,
77
)
88

9+
from eth_typing import Hash32
10+
911
from eth.constants import (
1012
BLANK_ROOT_HASH,
1113
)
1214
from eth.rlp.receipts import Receipt
1315
from eth.rlp.transactions import BaseTransaction
1416

17+
TransactionsOrReceipts = Union[Tuple[Receipt, ...], Tuple[BaseTransaction, ...]]
18+
TrieRootAndData = Tuple[Hash32, Dict[Hash32, bytes]]
19+
1520

16-
def make_trie_root_and_nodes(
17-
items: Union[List[Receipt], List[BaseTransaction]]) -> Tuple[bytes, Dict[bytes, bytes]]:
21+
def make_trie_root_and_nodes(items: TransactionsOrReceipts) -> TrieRootAndData:
1822
return _make_trie_root_and_nodes(tuple(rlp.encode(item) for item in items))
1923

2024

@@ -23,8 +27,8 @@ def make_trie_root_and_nodes(
2327
# as it's common for them to have duplicate receipt_roots. Given that, it probably makes sense to
2428
# use a relatively small cache size here.
2529
@functools.lru_cache(128)
26-
def _make_trie_root_and_nodes(items: Tuple[bytes, ...]) -> Tuple[bytes, Dict[bytes, bytes]]:
27-
kv_store = {} # type: Dict[bytes, bytes]
30+
def _make_trie_root_and_nodes(items: Tuple[bytes, ...]) -> TrieRootAndData:
31+
kv_store = {} # type: Dict[Hash32, bytes]
2832
trie = HexaryTrie(kv_store, BLANK_ROOT_HASH)
2933
with trie.squash_changes() as memory_trie:
3034
for index, item in enumerate(items):

p2p/peer.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,16 +1063,16 @@ async def request_stuff() -> None:
10631063
peer_pool.logger.info("Waiting for peer connection...")
10641064
await asyncio.sleep(0.2)
10651065
peer = peer_pool.highest_td_peer
1066-
headers = await peer.requests.get_block_headers(2440319, max_headers=100) # type: ignore
1067-
hashes = [header.hash for header in headers]
1066+
headers = await cast(ETHPeer, peer).requests.get_block_headers(2440319, max_headers=100)
1067+
hashes = tuple(header.hash for header in headers)
10681068
if peer_class == ETHPeer:
10691069
peer = cast(ETHPeer, peer)
1070-
peer.sub_proto.send_get_block_bodies(hashes)
1071-
peer.sub_proto.send_get_receipts(hashes)
1070+
peer.sub_proto._send_get_block_bodies(hashes)
1071+
peer.sub_proto._send_get_receipts(hashes)
10721072
else:
10731073
peer = cast(LESPeer, peer)
10741074
request_id = 1
1075-
peer.sub_proto.send_get_block_bodies(hashes, request_id + 1)
1075+
peer.sub_proto.send_get_block_bodies(list(hashes), request_id + 1)
10761076
peer.sub_proto.send_get_receipts(hashes[0], request_id + 2)
10771077

10781078
sigint_received = asyncio.Event()
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import os
2+
import random
3+
import time
4+
5+
import pytest
6+
7+
import rlp
8+
9+
from eth_hash.auto import keccak
10+
11+
from eth_utils import (
12+
to_tuple,
13+
big_endian_to_int,
14+
)
15+
16+
from eth.db.trie import make_trie_root_and_nodes
17+
from eth.rlp.headers import BlockHeader
18+
from eth.rlp.transactions import BaseTransactionFields
19+
20+
from p2p.exceptions import ValidationError
21+
22+
from trinity.rlp.block_body import BlockBody
23+
from trinity.protocol.eth.requests import BlockBodiesRequest
24+
25+
26+
def mk_uncle(block_number):
27+
return BlockHeader(
28+
state_root=os.urandom(32),
29+
difficulty=1000000,
30+
block_number=block_number,
31+
gas_limit=3141592,
32+
timestamp=int(time.time()),
33+
)
34+
35+
36+
def mk_transaction():
37+
return BaseTransactionFields(
38+
nonce=0,
39+
gas=21000,
40+
gas_price=1,
41+
to=os.urandom(20),
42+
value=random.randint(0, 100),
43+
data=b'',
44+
v=27,
45+
r=big_endian_to_int(os.urandom(32)),
46+
s=big_endian_to_int(os.urandom(32)),
47+
)
48+
49+
50+
def mk_header_and_body(block_number, num_transactions, num_uncles):
51+
transactions = tuple(mk_transaction() for _ in range(num_transactions))
52+
uncles = tuple(mk_uncle(block_number - 1) for _ in range(num_uncles))
53+
54+
transaction_root, trie_data = make_trie_root_and_nodes(transactions)
55+
uncles_hash = keccak(rlp.encode(uncles))
56+
57+
body = BlockBody(transactions=transactions, uncles=uncles)
58+
59+
header = BlockHeader(
60+
difficulty=1000000,
61+
block_number=block_number,
62+
gas_limit=3141592,
63+
timestamp=int(time.time()),
64+
transaction_root=transaction_root,
65+
uncles_hash=uncles_hash,
66+
)
67+
68+
return header, body, transaction_root, trie_data, uncles_hash
69+
70+
71+
@to_tuple
72+
def mk_headers(*counts):
73+
for idx, (num_transactions, num_uncles) in enumerate(counts, 1):
74+
yield mk_header_and_body(idx, num_transactions, num_uncles)
75+
76+
77+
def test_block_bodies_request_empty_response_is_valid():
78+
headers_bundle = mk_headers((2, 3), (8, 4), (0, 1), (0, 0))
79+
headers, _, _, _, _ = zip(*headers_bundle)
80+
request = BlockBodiesRequest(headers)
81+
request.validate_response(tuple(), tuple())
82+
83+
84+
def test_block_bodies_request_valid_with_full_response():
85+
headers_bundle = mk_headers((2, 3), (8, 4), (0, 1), (0, 0))
86+
headers, bodies, transactions_roots, trie_data_dicts, uncles_hashes = zip(*headers_bundle)
87+
transactions_bundles = tuple(zip(transactions_roots, trie_data_dicts))
88+
bodies_bundle = tuple(zip(bodies, transactions_bundles, uncles_hashes))
89+
request = BlockBodiesRequest(headers)
90+
request.validate_response(bodies, bodies_bundle)
91+
92+
93+
def test_block_bodies_request_valid_with_partial_response():
94+
headers_bundle = mk_headers((2, 3), (8, 4), (0, 1), (0, 0))
95+
headers, bodies, transactions_roots, trie_data_dicts, uncles_hashes = zip(*headers_bundle)
96+
transactions_bundles = tuple(zip(transactions_roots, trie_data_dicts))
97+
bodies_bundle = tuple(zip(bodies, transactions_bundles, uncles_hashes))
98+
request = BlockBodiesRequest(headers)
99+
100+
request.validate_response(bodies[:2], bodies_bundle[:2])
101+
request.validate_response(bodies[2:], bodies_bundle[2:])
102+
request.validate_response(
103+
(bodies[0], bodies[2], bodies[3]),
104+
(bodies_bundle[0], bodies_bundle[2], bodies_bundle[3]),
105+
)
106+
107+
108+
def test_block_bodies_request_with_fully_invalid_response():
109+
headers_bundle = mk_headers((2, 3), (8, 4), (0, 1), (0, 0))
110+
headers, _, _, _, _ = zip(*headers_bundle)
111+
112+
wrong_headers_bundle = mk_headers((3, 2), (4, 8), (1, 0), (0, 0))
113+
w_headers, w_bodies, w_transactions_roots, w_trie_data_dicts, w_uncles_hashes = zip(
114+
*wrong_headers_bundle
115+
)
116+
w_transactions_bundles = tuple(zip(w_transactions_roots, w_trie_data_dicts))
117+
w_bodies_bundle = tuple(zip(w_bodies, w_transactions_bundles, w_uncles_hashes))
118+
119+
request = BlockBodiesRequest(headers)
120+
with pytest.raises(ValidationError):
121+
request.validate_response(w_bodies, w_bodies_bundle)
122+
123+
124+
def test_block_bodies_request_with_extra_unrequested_bodies():
125+
headers_bundle = mk_headers((2, 3), (8, 4), (0, 1), (0, 0))
126+
headers, bodies, transactions_roots, trie_data_dicts, uncles_hashes = zip(*headers_bundle)
127+
transactions_bundles = tuple(zip(transactions_roots, trie_data_dicts))
128+
bodies_bundle = tuple(zip(bodies, transactions_bundles, uncles_hashes))
129+
request = BlockBodiesRequest(headers)
130+
131+
wrong_headers_bundle = mk_headers((3, 2), (4, 8), (1, 0), (0, 0))
132+
w_headers, w_bodies, w_transactions_roots, w_trie_data_dicts, w_uncles_hashes = zip(
133+
*wrong_headers_bundle
134+
)
135+
w_transactions_bundles = tuple(zip(w_transactions_roots, w_trie_data_dicts))
136+
w_bodies_bundle = tuple(zip(w_bodies, w_transactions_bundles, w_uncles_hashes))
137+
138+
request = BlockBodiesRequest(headers)
139+
with pytest.raises(ValidationError):
140+
request.validate_response(
141+
bodies + w_bodies,
142+
bodies_bundle + w_bodies_bundle,
143+
)

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,28 @@ def test_node_data_request_empty_response_is_valid():
2828
node_keys, _ = mk_node_data(10)
2929
request = NodeDataRequest(node_keys)
3030

31-
request.validate_response(tuple())
31+
request.validate_response(tuple(), tuple())
3232

3333

3434
def test_node_data_request_with_full_response():
3535
node_keys, nodes = mk_node_data(10)
3636
request = NodeDataRequest(node_keys)
3737
node_data = tuple(zip(node_keys, nodes))
3838

39-
request.validate_response(node_data)
39+
request.validate_response(nodes, node_data)
4040

4141

4242
def test_node_data_request_with_partial_response():
4343
node_keys, nodes = mk_node_data(10)
4444
request = NodeDataRequest(node_keys)
4545
node_data = tuple(zip(node_keys, nodes))
4646

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]))
47+
request.validate_response(nodes[3:], node_data[3:])
48+
request.validate_response(nodes[:3], node_data[:3])
49+
request.validate_response(
50+
(nodes[1], nodes[8], nodes[4]),
51+
(node_data[1], node_data[8], node_data[4]),
52+
)
5053

5154

5255
def test_node_data_request_with_fully_invalid_response():
@@ -58,7 +61,7 @@ def test_node_data_request_with_fully_invalid_response():
5861
other_node_data = tuple((keccak(node), node) for node in other_nodes)
5962

6063
with pytest.raises(ValidationError):
61-
request.validate_response(other_node_data)
64+
request.validate_response(other_nodes, other_node_data)
6265

6366

6467
def test_node_data_request_with_extra_unrequested_nodes():
@@ -71,4 +74,7 @@ def test_node_data_request_with_extra_unrequested_nodes():
7174
other_node_data = tuple((keccak(node), node) for node in other_nodes)
7275

7376
with pytest.raises(ValidationError):
74-
request.validate_response(node_data + other_node_data)
77+
request.validate_response(
78+
nodes + other_nodes,
79+
node_data + other_node_data,
80+
)

0 commit comments

Comments
 (0)