Skip to content

Commit e42af53

Browse files
flcl42fjl
andauthored
cmd/devp2p/internal/ethtest: accept responses in any order (#32834)
In both `TestSimultaneousRequests` and `TestSameRequestID`, we send two concurrent requests. The client under test is free to respond in either order, so we need to handle responses both ways. Also fixes an issue where some generated blob transactions didn't have any blobs. --------- Co-authored-by: Felix Lange <[email protected]>
1 parent 064ab70 commit e42af53

File tree

2 files changed

+63
-40
lines changed

2 files changed

+63
-40
lines changed

cmd/devp2p/internal/ethtest/protocol.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,9 @@ func protoOffset(proto Proto) uint64 {
8686
panic("unhandled protocol")
8787
}
8888
}
89+
90+
// msgTypePtr is the constraint for protocol message types.
91+
type msgTypePtr[U any] interface {
92+
*U
93+
Kind() byte
94+
}

cmd/devp2p/internal/ethtest/suite.go

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ to check if the node disconnects after receiving multiple invalid requests.`)
196196
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
197197
t.Log(`This test requests blocks headers from the node, performing two requests
198198
concurrently, with different request IDs.`)
199+
199200
conn, err := s.dialAndPeer(nil)
200201
if err != nil {
201202
t.Fatalf("peering failed: %v", err)
@@ -235,37 +236,36 @@ concurrently, with different request IDs.`)
235236
}
236237

237238
// Wait for responses.
238-
headers1 := new(eth.BlockHeadersPacket)
239-
if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers1); err != nil {
240-
t.Fatalf("error reading block headers msg: %v", err)
241-
}
242-
if got, want := headers1.RequestId, req1.RequestId; got != want {
243-
t.Fatalf("unexpected request id in response: got %d, want %d", got, want)
244-
}
245-
headers2 := new(eth.BlockHeadersPacket)
246-
if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers2); err != nil {
247-
t.Fatalf("error reading block headers msg: %v", err)
248-
}
249-
if got, want := headers2.RequestId, req2.RequestId; got != want {
250-
t.Fatalf("unexpected request id in response: got %d, want %d", got, want)
239+
// Note they can arrive in either order.
240+
resp, err := collectResponses(conn, 2, func(msg *eth.BlockHeadersPacket) uint64 {
241+
if msg.RequestId != 111 && msg.RequestId != 222 {
242+
t.Fatalf("response with unknown request ID: %v", msg.RequestId)
243+
}
244+
return msg.RequestId
245+
})
246+
if err != nil {
247+
t.Fatal(err)
251248
}
252249

253-
// Check received headers for accuracy.
250+
// Check if headers match.
251+
resp1 := resp[111]
254252
if expected, err := s.chain.GetHeaders(req1); err != nil {
255253
t.Fatalf("failed to get expected headers for request 1: %v", err)
256-
} else if !headersMatch(expected, headers1.BlockHeadersRequest) {
257-
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers1)
254+
} else if !headersMatch(expected, resp1.BlockHeadersRequest) {
255+
t.Fatalf("header mismatch for request ID %v: \nexpected %v \ngot %v", 111, expected, resp1)
258256
}
257+
resp2 := resp[222]
259258
if expected, err := s.chain.GetHeaders(req2); err != nil {
260259
t.Fatalf("failed to get expected headers for request 2: %v", err)
261-
} else if !headersMatch(expected, headers2.BlockHeadersRequest) {
262-
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers2)
260+
} else if !headersMatch(expected, resp2.BlockHeadersRequest) {
261+
t.Fatalf("header mismatch for request ID %v: \nexpected %v \ngot %v", 222, expected, resp2)
263262
}
264263
}
265264

266265
func (s *Suite) TestSameRequestID(t *utesting.T) {
267266
t.Log(`This test requests block headers, performing two concurrent requests with the
268267
same request ID. The node should handle the request by responding to both requests.`)
268+
269269
conn, err := s.dialAndPeer(nil)
270270
if err != nil {
271271
t.Fatalf("peering failed: %v", err)
@@ -289,7 +289,7 @@ same request ID. The node should handle the request by responding to both reques
289289
Origin: eth.HashOrNumber{
290290
Number: 33,
291291
},
292-
Amount: 2,
292+
Amount: 3,
293293
},
294294
}
295295

@@ -301,33 +301,50 @@ same request ID. The node should handle the request by responding to both reques
301301
t.Fatalf("failed to write to connection: %v", err)
302302
}
303303

304-
// Wait for the responses.
305-
headers1 := new(eth.BlockHeadersPacket)
306-
if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers1); err != nil {
307-
t.Fatalf("error reading from connection: %v", err)
308-
}
309-
if got, want := headers1.RequestId, request1.RequestId; got != want {
310-
t.Fatalf("unexpected request id: got %d, want %d", got, want)
311-
}
312-
headers2 := new(eth.BlockHeadersPacket)
313-
if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers2); err != nil {
314-
t.Fatalf("error reading from connection: %v", err)
315-
}
316-
if got, want := headers2.RequestId, request2.RequestId; got != want {
317-
t.Fatalf("unexpected request id: got %d, want %d", got, want)
304+
// Wait for the responses. They can arrive in either order, and we can't tell them
305+
// apart by their request ID, so use the number of headers instead.
306+
resp, err := collectResponses(conn, 2, func(msg *eth.BlockHeadersPacket) uint64 {
307+
id := uint64(len(msg.BlockHeadersRequest))
308+
if id != 2 && id != 3 {
309+
t.Fatalf("invalid number of headers in response: %d", id)
310+
}
311+
return id
312+
})
313+
if err != nil {
314+
t.Fatal(err)
318315
}
319316

320317
// Check if headers match.
318+
resp1 := resp[2]
321319
if expected, err := s.chain.GetHeaders(request1); err != nil {
322-
t.Fatalf("failed to get expected block headers: %v", err)
323-
} else if !headersMatch(expected, headers1.BlockHeadersRequest) {
324-
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers1)
320+
t.Fatalf("failed to get expected headers for request 1: %v", err)
321+
} else if !headersMatch(expected, resp1.BlockHeadersRequest) {
322+
t.Fatalf("headers mismatch: \nexpected %v \ngot %v", expected, resp1)
325323
}
324+
resp2 := resp[3]
326325
if expected, err := s.chain.GetHeaders(request2); err != nil {
327-
t.Fatalf("failed to get expected block headers: %v", err)
328-
} else if !headersMatch(expected, headers2.BlockHeadersRequest) {
329-
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers2)
326+
t.Fatalf("failed to get expected headers for request 2: %v", err)
327+
} else if !headersMatch(expected, resp2.BlockHeadersRequest) {
328+
t.Fatalf("headers mismatch: \nexpected %v \ngot %v", expected, resp2)
329+
}
330+
}
331+
332+
// collectResponses waits for n messages of type T on the given connection.
333+
// The messsages are collected according to the 'identity' function.
334+
func collectResponses[T any, P msgTypePtr[T]](conn *Conn, n int, identity func(P) uint64) (map[uint64]P, error) {
335+
resp := make(map[uint64]P, n)
336+
for range n {
337+
r := new(T)
338+
if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, r); err != nil {
339+
return resp, fmt.Errorf("read error: %v", err)
340+
}
341+
id := identity(r)
342+
if resp[id] != nil {
343+
return resp, fmt.Errorf("duplicate response %v", r)
344+
}
345+
resp[id] = r
330346
}
347+
return resp, nil
331348
}
332349

333350
func (s *Suite) TestZeroRequestID(t *utesting.T) {
@@ -887,7 +904,7 @@ func (s *Suite) makeBlobTxs(count, blobs int, discriminator byte) (txs types.Tra
887904
from, nonce := s.chain.GetSender(5)
888905
for i := 0; i < count; i++ {
889906
// Make blob data, max of 2 blobs per tx.
890-
blobdata := make([]byte, blobs%3)
907+
blobdata := make([]byte, min(blobs, 2))
891908
for i := range blobdata {
892909
blobdata[i] = discriminator
893910
blobs -= 1

0 commit comments

Comments
 (0)