1use super::crypto::{self, NodeKey};
12use super::eth::{self, code, Announcement, HeaderRequest, Hello, Origin, Status};
13use super::rlp::Rlp;
14use super::rlpx::{self, FrameError, FrameReader, FrameWriter};
15use crate::utils::clock::Instant;
16use anyhow::{bail, ensure, Context, Result};
17use secp256k1::PublicKey;
18use serde_json::{json, Map, Value};
19use std::collections::HashMap;
20use std::sync::Arc;
21use std::time::Duration;
22use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
23use tokio::sync::{mpsc, Mutex};
24
25pub const MAX_MESSAGE_BYTES: usize = 4 * 1024 * 1024;
28pub const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(10);
30pub const IDLE_TIMEOUT: Duration = Duration::from_secs(60);
32pub const PING_INTERVAL: Duration = Duration::from_secs(15);
34pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(20);
36pub const WRITE_TIMEOUT: Duration = Duration::from_secs(20);
38pub const MAX_OUTSTANDING: usize = 16;
40pub const MAX_OWED: usize = 64;
42pub const MAX_REQUEST_HEADERS: u64 = 64;
44pub const MAX_REQUEST_HASHES: usize = 256;
46pub const MAX_ANSWER_ITEMS: usize = 1024;
49pub const MAX_PEER_HASHES: usize = 4096;
51
52pub type BoxRead = Box<dyn AsyncRead + Send + Unpin>;
53pub type BoxWrite = Box<dyn AsyncWrite + Send + Unpin>;
54
55pub struct Writer {
58 codec: FrameWriter,
59 half: BoxWrite,
60}
61
62pub type SharedWriter = Arc<Mutex<Writer>>;
63
64pub async fn send(w: &SharedWriter, msg_code: u64, payload: &[u8]) -> Result<usize> {
66 ensure!(
67 payload.len() <= MAX_MESSAGE_BYTES,
68 "a {}-byte message is above the {MAX_MESSAGE_BYTES}-byte bound",
69 payload.len()
70 );
71 let mut g = w.lock().await;
72 let frame = g.codec.encode(msg_code, payload)?;
73 let half = &mut g.half;
74 tokio::time::timeout(WRITE_TIMEOUT, async {
75 half.write_all(&frame).await?;
76 half.flush().await
77 })
78 .await
79 .context("writing a frame timed out")??;
80 Ok(frame.len())
81}
82
83pub async fn disconnect(w: &SharedWriter, reason: u64) {
85 let _ = send(w, code::DISCONNECT, ð::encode_disconnect(reason)).await;
86 close(w).await;
87}
88
89pub async fn close(w: &SharedWriter) {
90 let mut g = w.lock().await;
91 let _ = tokio::time::timeout(Duration::from_secs(2), g.half.shutdown()).await;
92}
93
94pub enum Role {
96 Dial(PublicKey),
97 Listen,
98}
99
100pub struct Established {
102 pub reader: FrameReader,
103 pub read_half: BoxRead,
104 pub writer: SharedWriter,
105 pub hello: Hello,
106 pub eth_version: u64,
107 pub remote_id: [u8; 64],
108}
109
110#[derive(Debug)]
112pub enum EstablishError {
113 Transport(String),
115 PeerDisconnected(u64),
117 Refused { reason: u64, detail: String },
119}
120
121impl std::fmt::Display for EstablishError {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 EstablishError::Transport(m) => write!(f, "{m}"),
125 EstablishError::PeerDisconnected(r) => {
126 write!(f, "the peer disconnected: {}", eth::reason_name(*r))
127 }
128 EstablishError::Refused { reason, detail } => {
129 write!(f, "refused ({}): {detail}", eth::reason_name(*reason))
130 }
131 }
132 }
133}
134
135pub async fn establish(
139 mut read_half: BoxRead,
140 mut write_half: BoxWrite,
141 key: &NodeKey,
142 role: Role,
143 client_id: &str,
144 listen_port: u16,
145) -> std::result::Result<Established, EstablishError> {
146 let transport = |e: anyhow::Error| EstablishError::Transport(format!("{e:#}"));
147 tokio::time::timeout(HANDSHAKE_TIMEOUT, async move {
148 let secrets = match role {
149 Role::Dial(remote) => {
150 rlpx::initiate(&mut read_half, &mut write_half, key, &remote).await
151 }
152 Role::Listen => rlpx::respond(&mut read_half, &mut write_half, key).await,
153 }
154 .map_err(transport)?;
155 let remote_id = crypto::node_id(&secrets.remote);
156 let (mut reader, codec) = rlpx::codec(secrets, MAX_MESSAGE_BYTES);
157 let writer: SharedWriter = Arc::new(Mutex::new(Writer {
158 codec,
159 half: write_half,
160 }));
161 let ours = Hello::ours(client_id, listen_port, key.id());
162 send(&writer, code::HELLO, &ours.encode())
163 .await
164 .map_err(transport)?;
165 let (msg, payload) = reader.read(&mut read_half).await.map_err(|e| match e {
166 FrameError::Io(e) => EstablishError::Transport(format!("reading Hello: {e}")),
167 other => EstablishError::Transport(format!("reading Hello: {other}")),
168 })?;
169 if msg == code::DISCONNECT {
170 return Err(EstablishError::PeerDisconnected(eth::decode_disconnect(
171 &payload,
172 )));
173 }
174 if msg != code::HELLO {
175 disconnect(&writer, 0x02).await;
176 return Err(EstablishError::Refused {
177 reason: 0x02,
178 detail: format!("the first message is {msg:#04x}, not Hello"),
179 });
180 }
181 let hello = match Hello::decode(&payload) {
182 Ok(h) => h,
183 Err(e) => {
184 disconnect(&writer, 0x02).await;
185 return Err(EstablishError::Refused {
186 reason: 0x02,
187 detail: format!("Hello: {e:#}"),
188 });
189 }
190 };
191 let snappy = hello.version >= eth::BASE_VERSION;
193 reader.set_snappy(snappy);
194 writer.lock().await.codec.set_snappy(snappy);
195 if hello.id != remote_id {
196 disconnect(&writer, 0x09).await;
197 return Err(EstablishError::Refused {
198 reason: 0x09,
199 detail: "the Hello names a different node than the handshake authenticated".into(),
200 });
201 }
202 if hello.id == key.id() {
203 disconnect(&writer, 0x0a).await;
204 return Err(EstablishError::Refused {
205 reason: 0x0a,
206 detail: "the peer is this node".into(),
207 });
208 }
209 let Some(eth_version) = hello.shared_eth() else {
210 disconnect(&writer, 0x03).await;
211 return Err(EstablishError::Refused {
212 reason: 0x03,
213 detail: format!(
214 "no shared eth version: the peer offers {:?}, NetGet speaks eth/68 and eth/69",
215 hello.caps_strings()
216 ),
217 });
218 };
219 Ok(Established {
220 reader,
221 read_half,
222 writer,
223 hello,
224 eth_version,
225 remote_id,
226 })
227 })
228 .await
229 .unwrap_or_else(|_| {
230 Err(EstablishError::Transport(format!(
231 "the handshake did not finish within {}s",
232 HANDSHAKE_TIMEOUT.as_secs()
233 )))
234 })
235}
236
237#[derive(Debug)]
239pub enum Inbound {
240 Message { code: u64, payload: Vec<u8> },
241 Ended(End),
242}
243
244#[derive(Debug, Clone)]
246pub struct End {
247 pub reason: String,
248 pub decision: Option<&'static str>,
250}
251
252impl End {
253 pub fn plain(reason: impl Into<String>) -> End {
254 End {
255 reason: reason.into(),
256 decision: None,
257 }
258 }
259}
260
261pub async fn read_loop(
263 mut reader: FrameReader,
264 mut half: BoxRead,
265 writer: SharedWriter,
266 tx: mpsc::Sender<Inbound>,
267) {
268 let end = loop {
269 match reader.read(&mut half).await {
270 Ok((code::PING, _)) => {
271 if send(&writer, code::PONG, ð::empty_payload())
272 .await
273 .is_err()
274 {
275 break End::plain("the connection could not be written");
276 }
277 }
278 Ok((code::PONG, _)) => {}
279 Ok((code, payload)) => {
280 if tx.send(Inbound::Message { code, payload }).await.is_err() {
281 return;
282 }
283 }
284 Err(FrameError::Io(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
285 break End::plain("the peer closed the connection");
286 }
287 Err(FrameError::Io(e)) if e.kind() == std::io::ErrorKind::TimedOut => {
288 break End {
289 reason: format!("no frame for {}s", IDLE_TIMEOUT.as_secs()),
290 decision: Some("fail_closed_idle_timeout"),
291 };
292 }
293 Err(FrameError::Io(e)) => break End::plain(format!("read failed: {e}")),
294 Err(e @ FrameError::TooLarge { .. }) => {
295 disconnect(&writer, 0x02).await;
296 break End {
297 reason: format!("message too large: {e}"),
298 decision: Some("fail_closed_message_too_large"),
299 };
300 }
301 Err(FrameError::Protocol(m)) => {
302 disconnect(&writer, 0x02).await;
303 break End {
304 reason: m,
305 decision: Some("protocol_error"),
306 };
307 }
308 }
309 };
310 let _ = tx.send(Inbound::Ended(end)).await;
311}
312
313#[derive(Debug, Clone, Default)]
319pub struct StatusSpec {
320 pub fields: Map<String, Value>,
321 pub mirror_peer: bool,
322}
323
324#[derive(Debug, Clone)]
326pub enum Command {
327 SendStatus(StatusSpec),
328 SendHeaders {
329 request_id: Option<u64>,
330 headers: Vec<Rlp>,
331 },
332 SendBodies {
333 request_id: Option<u64>,
334 bodies: Vec<Rlp>,
335 },
336 SendPooled {
337 request_id: Option<u64>,
338 transactions: Vec<(Rlp, [u8; 32])>,
339 },
340 RequestHeaders(HeaderRequest),
341 RequestBodies(Vec<[u8; 32]>),
342 RequestPooled(Vec<[u8; 32]>),
343 Disconnect(u64),
344}
345
346pub const SEND_STATUS: &str = "eth_send_status";
347pub const SEND_HEADERS: &str = "eth_send_block_headers";
348pub const SEND_BODIES: &str = "eth_send_block_bodies";
349pub const SEND_POOLED: &str = "eth_send_pooled_transactions";
350pub const REQUEST_HEADERS: &str = "eth_request_block_headers";
351pub const REQUEST_BODIES: &str = "eth_request_block_bodies";
352pub const REQUEST_POOLED: &str = "eth_request_pooled_transactions";
353pub const DISCONNECT: &str = "devp2p_disconnect";
354
355const STATUS_FIELDS: &[&str] = &[
356 "network_id",
357 "genesis_hash",
358 "fork_hash",
359 "fork_next",
360 "forks",
361 "earliest_block",
362 "latest_block",
363 "latest_block_hash",
364 "total_difficulty",
365 "head_hash",
366];
367
368fn request_id(v: &Value) -> Result<Option<u64>> {
369 match v.get("request_id") {
370 None | Some(Value::Null) => Ok(None),
371 Some(r) => Ok(Some(eth::parse_u64(r, "request_id")?)),
372 }
373}
374
375fn array<'a>(v: &'a Value, key: &str, max: usize) -> Result<&'a Vec<Value>> {
376 let a = v[key]
377 .as_array()
378 .with_context(|| format!("{key} is an array"))?;
379 ensure!(
380 a.len() <= max,
381 "{key} holds {} entries, above {max}",
382 a.len()
383 );
384 Ok(a)
385}
386
387fn hashes(v: &Value, key: &str, max: usize) -> Result<Vec<[u8; 32]>> {
388 let a = array(v, key, max)?;
389 ensure!(!a.is_empty(), "{key} names at least one hash");
390 a.iter().map(|h| eth::parse_fixed::<32>(h, key)).collect()
391}
392
393pub fn parse_command(v: &Value) -> Result<Command> {
396 let kind = v["type"].as_str().context("an action has a type")?;
397 Ok(match kind {
398 SEND_STATUS => {
399 let obj = v.as_object().context("action")?;
400 let mut fields = Map::new();
401 for (k, val) in obj {
402 if k == "type" || k == "mirror_peer" || val.is_null() {
403 continue;
404 }
405 ensure!(
406 STATUS_FIELDS.contains(&k.as_str()),
407 "{k:?} is not a Status field; the fields are {}",
408 STATUS_FIELDS.join(", ")
409 );
410 fields.insert(k.clone(), val.clone());
411 }
412 let spec = StatusSpec {
413 fields,
414 mirror_peer: v["mirror_peer"].as_bool().unwrap_or(false),
415 };
416 check_status_fields(&spec)?;
418 Command::SendStatus(spec)
419 }
420 SEND_HEADERS => {
421 let headers = array(v, "headers", MAX_ANSWER_ITEMS)?
422 .iter()
423 .enumerate()
424 .map(|(i, h)| eth::header_from_json(h).with_context(|| format!("headers[{i}]")))
425 .collect::<Result<Vec<_>>>()?;
426 Command::SendHeaders {
427 request_id: request_id(v)?,
428 headers,
429 }
430 }
431 SEND_BODIES => {
432 let bodies = array(v, "bodies", MAX_ANSWER_ITEMS)?
433 .iter()
434 .enumerate()
435 .map(|(i, b)| eth::body_from_json(b).with_context(|| format!("bodies[{i}]")))
436 .collect::<Result<Vec<_>>>()?;
437 Command::SendBodies {
438 request_id: request_id(v)?,
439 bodies,
440 }
441 }
442 SEND_POOLED => {
443 let transactions = array(v, "transactions", MAX_ANSWER_ITEMS)?
444 .iter()
445 .enumerate()
446 .map(|(i, t)| {
447 eth::transaction_from_hex(t).with_context(|| format!("transactions[{i}]"))
448 })
449 .collect::<Result<Vec<_>>>()?;
450 Command::SendPooled {
451 request_id: request_id(v)?,
452 transactions,
453 }
454 }
455 REQUEST_HEADERS => {
456 let origin = Origin::from_json(&v["origin"])?;
457 let num = |k: &str, d: u64| -> Result<u64> {
458 match v.get(k) {
459 None | Some(Value::Null) => Ok(d),
460 Some(x) => eth::parse_u64(x, k),
461 }
462 };
463 let amount = num("amount", 1)?;
464 ensure!(
465 (1..=MAX_REQUEST_HEADERS).contains(&amount),
466 "amount is 1 to {MAX_REQUEST_HEADERS}"
467 );
468 let skip = num("skip", 0)?;
469 ensure!(skip <= 1_000_000, "skip is at most 1000000");
470 Command::RequestHeaders(HeaderRequest {
471 origin,
472 amount,
473 skip,
474 reverse: v["reverse"].as_bool().unwrap_or(false),
475 })
476 }
477 REQUEST_BODIES => Command::RequestBodies(hashes(v, "hashes", MAX_REQUEST_HASHES)?),
478 REQUEST_POOLED => Command::RequestPooled(hashes(v, "hashes", MAX_REQUEST_HASHES)?),
479 DISCONNECT | "close_connection" => Command::Disconnect(eth::reason_code(
480 v["reason"].as_str().unwrap_or("requested"),
481 )?),
482 other => bail!("unknown devp2p action {other:?}"),
483 })
484}
485
486fn check_status_fields(spec: &StatusSpec) -> Result<()> {
487 for (k, v) in &spec.fields {
488 match k.as_str() {
489 "network_id" | "fork_next" | "earliest_block" | "latest_block" => {
490 eth::parse_u64(v, k)?;
491 }
492 "genesis_hash" | "latest_block_hash" | "head_hash" => {
493 eth::parse_fixed::<32>(v, k)?;
494 }
495 "fork_hash" => {
496 eth::parse_fixed::<4>(v, k)?;
497 }
498 "total_difficulty" => {
499 eth::parse_big(v, k)?;
500 }
501 "forks" => {
502 let a = v
503 .as_array()
504 .context("forks is an array of block numbers or timestamps")?;
505 ensure!(a.len() <= 64, "at most 64 forks");
506 for f in a {
507 eth::parse_u64(f, "forks entry")?;
508 }
509 }
510 _ => {}
511 }
512 }
513 Ok(())
514}
515
516#[derive(Debug, Clone, Default)]
518pub struct StatusDefaults {
519 pub network_id: Option<u64>,
520 pub genesis: Option<[u8; 32]>,
521 pub forks: Vec<u64>,
522 pub fork_next: Option<u64>,
523 pub latest_block: Option<u64>,
524 pub latest_block_hash: Option<[u8; 32]>,
525}
526
527impl StatusDefaults {
528 pub fn to_json(&self) -> Value {
529 json!({
530 "network_id": self.network_id,
531 "genesis_hash": self.genesis.map(|g| eth::hex0x(&g)),
532 "forks": self.forks,
533 "fork_next": self.fork_next,
534 "latest_block": self.latest_block,
535 "latest_block_hash": self.latest_block_hash.map(|h| eth::hex0x(&h)),
536 })
537 }
538}
539
540pub fn build_status(
544 spec: &StatusSpec,
545 version: u64,
546 peer: Option<&Status>,
547 defaults: &StatusDefaults,
548) -> Result<Status> {
549 let mirror = if spec.mirror_peer {
550 Some(peer.context(
551 "mirror_peer: the peer has not sent its Status yet, so there is nothing to mirror",
552 )?)
553 } else {
554 None
555 };
556 let f = |k: &str| spec.fields.get(k);
557 let missing = |k: &str| {
558 anyhow::anyhow!(
559 "Status needs {k}: give it in eth_send_status, set mirror_peer, or configure it at startup"
560 )
561 };
562 let network_id = match f("network_id") {
563 Some(v) => eth::parse_u64(v, "network_id")?,
564 None => mirror
565 .map(|p| p.network_id)
566 .or(defaults.network_id)
567 .ok_or_else(|| missing("network_id"))?,
568 };
569 let genesis = match f("genesis_hash") {
570 Some(v) => eth::parse_fixed(v, "genesis_hash")?,
571 None => mirror
572 .map(|p| p.genesis)
573 .or(defaults.genesis)
574 .ok_or_else(|| missing("genesis_hash"))?,
575 };
576 let forks = match f("forks") {
577 Some(Value::Array(a)) => a
578 .iter()
579 .map(|x| eth::parse_u64(x, "forks entry"))
580 .collect::<Result<Vec<_>>>()?,
581 _ => defaults.forks.clone(),
582 };
583 let fork_next_given = match f("fork_next") {
584 Some(v) => Some(eth::parse_u64(v, "fork_next")?),
585 None => None,
586 };
587 let (fork_hash, fork_next) = match f("fork_hash") {
588 Some(v) => (
589 eth::parse_fixed(v, "fork_hash")?,
590 fork_next_given.unwrap_or(0),
591 ),
592 None => match mirror {
593 Some(p) if f("forks").is_none() => {
594 (p.fork_hash, fork_next_given.unwrap_or(p.fork_next))
595 }
596 _ => (
597 eth::fork_hash(&genesis, &forks),
598 fork_next_given.or(defaults.fork_next).unwrap_or(0),
599 ),
600 },
601 };
602 let u = |k: &str, from_peer: Option<u64>, default: Option<u64>| -> Result<u64> {
603 Ok(match f(k) {
604 Some(v) => eth::parse_u64(v, k)?,
605 None => from_peer.or(default).unwrap_or(0),
606 })
607 };
608 let h = |k: &str, from_peer: Option<[u8; 32]>, default: Option<[u8; 32]>| -> Result<[u8; 32]> {
609 Ok(match f(k) {
610 Some(v) => eth::parse_fixed(v, k)?,
611 None => from_peer.or(default).unwrap_or(genesis),
612 })
613 };
614 let mut s = Status {
615 version,
616 network_id,
617 genesis,
618 fork_hash,
619 fork_next,
620 total_difficulty: Vec::new(),
621 head: [0; 32],
622 earliest_block: 0,
623 latest_block: 0,
624 latest_block_hash: [0; 32],
625 };
626 if version >= 69 {
627 s.earliest_block = u("earliest_block", mirror.map(|p| p.earliest_block), Some(0))?;
628 s.latest_block = u(
629 "latest_block",
630 mirror.map(|p| p.latest_block),
631 defaults.latest_block,
632 )?;
633 s.latest_block_hash = h(
634 "latest_block_hash",
635 mirror.map(|p| p.latest_block_hash),
636 defaults.latest_block_hash,
637 )?;
638 ensure!(
639 s.earliest_block <= s.latest_block,
640 "earliest_block {} is after latest_block {}",
641 s.earliest_block,
642 s.latest_block
643 );
644 ensure!(s.latest_block_hash != [0; 32], "latest_block_hash is zero");
645 } else {
646 s.total_difficulty = match f("total_difficulty") {
647 Some(v) => eth::parse_big(v, "total_difficulty")?,
648 None => mirror
649 .map(|p| p.total_difficulty.clone())
650 .unwrap_or_default(),
651 };
652 s.head = h(
653 "head_hash",
654 mirror.map(|p| p.head),
655 defaults.latest_block_hash,
656 )?;
657 }
658 Ok(s)
659}
660
661pub fn compatible(ours: &Status, theirs: &Status) -> std::result::Result<(), String> {
663 if ours.network_id != theirs.network_id {
664 return Err(format!(
665 "network id {} is not the peer's {}",
666 ours.network_id, theirs.network_id
667 ));
668 }
669 if ours.genesis != theirs.genesis {
670 return Err(format!(
671 "genesis {} is not the peer's {}",
672 eth::hex0x(&ours.genesis),
673 eth::hex0x(&theirs.genesis)
674 ));
675 }
676 Ok(())
677}
678
679#[derive(Debug, Clone)]
685enum Asked {
686 Headers(HeaderRequest),
687 Bodies(Vec<[u8; 32]>),
688 Pooled(Vec<[u8; 32]>),
689}
690
691struct Outstanding {
692 asked: Asked,
693 deadline: Instant,
694 depth: u32,
695}
696
697#[derive(Debug, Clone)]
699enum Owed {
700 Headers(HeaderRequest),
701 Bodies(usize),
702 Pooled(Vec<[u8; 32]>),
703}
704
705#[derive(Debug, Clone)]
707pub struct PeerEvent {
708 pub kind: PeerEventKind,
709 pub data: Value,
710 pub request_id: Option<u64>,
712 pub depth: u32,
714}
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq)]
717pub enum PeerEventKind {
718 HeadersRequested,
719 BodiesRequested,
720 PooledRequested,
721 Announced,
722 HeadersReceived,
723 BodiesReceived,
724 PooledReceived,
725 RequestTimedOut,
726}
727
728pub struct Session {
730 pub writer: SharedWriter,
731 pub eth_version: u64,
732 pub peer_status: Option<Status>,
733 pub our_status: Option<Status>,
734 next_request_id: u64,
735 outstanding: HashMap<u64, Outstanding>,
736 owed: HashMap<u64, Owed>,
737 pub bytes_sent: u64,
739}
740
741#[derive(Debug)]
743pub enum Applied {
744 Sent(String),
745 StatusSent(Status),
746 Disconnected(u64),
747}
748
749fn eth_code(c: u64) -> u64 {
750 eth::ETH_OFFSET + c
751}
752
753impl Session {
754 pub fn new(writer: SharedWriter, eth_version: u64) -> Session {
755 Session {
756 writer,
757 eth_version,
758 peer_status: None,
759 our_status: None,
760 next_request_id: rand::random::<u32>() as u64,
761 outstanding: HashMap::new(),
762 owed: HashMap::new(),
763 bytes_sent: 0,
764 }
765 }
766
767 pub fn ready(&self) -> bool {
769 self.peer_status.is_some() && self.our_status.is_some()
770 }
771
772 pub fn owes(&self, request_id: u64) -> bool {
774 self.owed.contains_key(&request_id)
775 }
776
777 async fn send_eth(&mut self, c: u64, payload: &[u8]) -> Result<()> {
778 let n = send(&self.writer, eth_code(c), payload).await?;
779 self.bytes_sent += n as u64;
780 Ok(())
781 }
782
783 pub fn take_status(&mut self, msg_code: u64, payload: &[u8]) -> Result<Option<Status>> {
785 if msg_code != eth_code(code::STATUS) {
786 return Ok(None);
787 }
788 ensure!(self.peer_status.is_none(), "a second Status");
789 let s = Status::decode(payload, self.eth_version)?;
790 self.peer_status = Some(s.clone());
791 Ok(Some(s))
792 }
793
794 pub fn expire(&mut self) -> Vec<PeerEvent> {
796 let now = Instant::now();
797 let expired: Vec<u64> = self
798 .outstanding
799 .iter()
800 .filter(|(_, o)| o.deadline <= now)
801 .map(|(id, _)| *id)
802 .collect();
803 expired
804 .into_iter()
805 .filter_map(|id| self.outstanding.remove(&id).map(|o| (id, o)))
806 .map(|(id, o)| PeerEvent {
807 kind: PeerEventKind::RequestTimedOut,
808 data: json!({"request_id": id, "request": asked_json(&o.asked), "timeout_secs": REQUEST_TIMEOUT.as_secs()}),
809 request_id: None,
810 depth: o.depth,
811 })
812 .collect()
813 }
814
815 pub async fn on_message(&mut self, msg_code: u64, payload: &[u8]) -> Result<Option<PeerEvent>> {
819 if msg_code < eth::ETH_OFFSET {
820 bail!("base protocol message {msg_code:#04x} after Hello");
821 }
822 let c = msg_code - eth::ETH_OFFSET;
823 ensure!(
824 c < eth::eth_length(self.eth_version),
825 "message {msg_code:#04x} is outside eth/{}",
826 self.eth_version
827 );
828 ensure!(
829 self.peer_status.is_some(),
830 "message {msg_code:#04x} before the peer's Status"
831 );
832 let event = |kind, data, request_id, depth| {
833 Ok(Some(PeerEvent {
834 kind,
835 data,
836 request_id,
837 depth,
838 }))
839 };
840 match c {
841 code::STATUS => bail!("a second Status"),
842 code::GET_BLOCK_HEADERS => {
843 let (id, req) = HeaderRequest::decode(payload)?;
844 self.owe(id, Owed::Headers(req.clone()))?;
845 event(
846 PeerEventKind::HeadersRequested,
847 json!({"request_id": id, "origin": req.origin.to_json(), "amount": req.amount, "skip": req.skip, "reverse": req.reverse}),
848 Some(id),
849 0,
850 )
851 }
852 code::GET_BLOCK_BODIES => {
853 let (id, hashes) =
854 eth::decode_hash_request(payload, MAX_PEER_HASHES, "GetBlockBodies")?;
855 self.owe(id, Owed::Bodies(hashes.len()))?;
856 event(
857 PeerEventKind::BodiesRequested,
858 json!({"request_id": id, "hashes": hashes.iter().map(|h| eth::hex0x(h)).collect::<Vec<_>>()}),
859 Some(id),
860 0,
861 )
862 }
863 code::GET_POOLED_TRANSACTIONS => {
864 let (id, hashes) =
865 eth::decode_hash_request(payload, MAX_PEER_HASHES, "GetPooledTransactions")?;
866 self.owe(id, Owed::Pooled(hashes.clone()))?;
867 event(
868 PeerEventKind::PooledRequested,
869 json!({"request_id": id, "hashes": hashes.iter().map(|h| eth::hex0x(h)).collect::<Vec<_>>()}),
870 Some(id),
871 0,
872 )
873 }
874 code::GET_RECEIPTS => {
875 let (id, _) = eth::decode_hash_request(payload, MAX_PEER_HASHES, "GetReceipts")?;
878 self.send_eth(code::RECEIPTS, ð::encode_response(id, Vec::new()))
879 .await?;
880 Ok(None)
881 }
882 code::NEW_POOLED_TRANSACTION_HASHES => {
883 let a = Announcement::decode(payload, MAX_PEER_HASHES)?;
884 event(
885 PeerEventKind::Announced,
886 json!({
887 "transactions": a.hashes.iter().zip(&a.types).zip(&a.sizes)
888 .map(|((h, t), s)| json!({"hash": eth::hex0x(h), "type": t, "size": s}))
889 .collect::<Vec<_>>()
890 }),
891 None,
892 0,
893 )
894 }
895 code::BLOCK_HEADERS => self.answer_for(payload, "BlockHeaders"),
896 code::BLOCK_BODIES => self.answer_for(payload, "BlockBodies"),
897 code::POOLED_TRANSACTIONS => self.answer_for(payload, "PooledTransactions"),
898 code::BLOCK_RANGE_UPDATE => {
899 let (earliest, latest, hash) = eth::decode_block_range(payload)?;
900 if let Some(p) = self.peer_status.as_mut() {
901 p.earliest_block = earliest;
902 p.latest_block = latest;
903 p.latest_block_hash = hash;
904 }
905 Ok(None)
906 }
907 code::TRANSACTIONS | code::NEW_BLOCK_HASHES | code::NEW_BLOCK | code::RECEIPTS => {
908 Ok(None)
910 }
911 other => bail!("eth message {other:#04x} is not handled"),
912 }
913 }
914
915 fn owe(&mut self, id: u64, owed: Owed) -> Result<()> {
916 ensure!(
917 self.owed.len() < MAX_OWED,
918 "the peer has {MAX_OWED} requests unanswered"
919 );
920 ensure!(
921 !self.owed.contains_key(&id),
922 "the peer reused request id {id} while it is still unanswered"
923 );
924 self.owed.insert(id, owed);
925 Ok(())
926 }
927
928 fn answer_for(&mut self, payload: &[u8], what: &str) -> Result<Option<PeerEvent>> {
930 let (id, items) = eth::decode_response(payload, MAX_ANSWER_ITEMS, what)?;
931 let Some(o) = self.outstanding.remove(&id) else {
932 tracing::debug!(
933 "devp2p {what} for request {id}, which NetGet did not send or gave up on"
934 );
935 return Ok(None);
936 };
937 let depth = o.depth + 1;
938 let request = asked_json(&o.asked);
939 match o.asked {
940 Asked::Headers(req) => {
941 ensure!(
942 items.len() as u64 <= req.amount,
943 "{} headers for a request of {}",
944 items.len(),
945 req.amount
946 );
947 let headers = items
948 .iter()
949 .map(eth::header_to_json)
950 .collect::<Result<Vec<_>>>()?;
951 let (matches, linked) = check_headers(&req, &items);
952 Ok(Some(PeerEvent {
953 kind: PeerEventKind::HeadersReceived,
954 data: json!({"request_id": id, "request": request, "count": headers.len(),
955 "headers": headers, "matches_request": matches, "linked": linked}),
956 request_id: None,
957 depth,
958 }))
959 }
960 Asked::Bodies(hashes) => {
961 ensure!(
962 items.len() <= hashes.len(),
963 "{} bodies for {} hashes",
964 items.len(),
965 hashes.len()
966 );
967 let bodies = items
968 .iter()
969 .zip(&hashes)
970 .map(|(b, h)| {
971 let mut j = eth::body_json(b)?;
972 j["block_hash"] = json!(eth::hex0x(h));
973 Ok(j)
974 })
975 .collect::<Result<Vec<_>>>()?;
976 Ok(Some(PeerEvent {
977 kind: PeerEventKind::BodiesReceived,
978 data: json!({"request_id": id, "request": request, "count": bodies.len(), "bodies": bodies}),
979 request_id: None,
980 depth,
981 }))
982 }
983 Asked::Pooled(hashes) => {
984 let txs = items
985 .iter()
986 .map(eth::transaction_json)
987 .collect::<Result<Vec<_>>>()?;
988 let asked: Vec<String> = hashes.iter().map(|h| eth::hex0x(h)).collect();
989 let unasked = txs
990 .iter()
991 .filter(|t| !asked.iter().any(|a| Some(a.as_str()) == t["hash"].as_str()))
992 .count();
993 Ok(Some(PeerEvent {
994 kind: PeerEventKind::PooledReceived,
995 data: json!({"request_id": id, "request": request, "count": txs.len(),
996 "transactions": txs, "unrequested": unasked}),
997 request_id: None,
998 depth,
999 }))
1000 }
1001 }
1002 }
1003
1004 pub async fn apply(
1008 &mut self,
1009 command: Command,
1010 answering: Option<u64>,
1011 depth: u32,
1012 defaults: &StatusDefaults,
1013 ) -> Result<Applied> {
1014 let reply_id = |given: Option<u64>| {
1015 given
1016 .or(answering)
1017 .context("request_id is required: this is not an answer to a request event")
1018 };
1019 match command {
1020 Command::Disconnect(reason) => {
1021 disconnect(&self.writer, reason).await;
1022 Ok(Applied::Disconnected(reason))
1023 }
1024 Command::SendStatus(spec) => {
1025 ensure!(self.our_status.is_none(), "Status was already sent");
1026 let status =
1027 build_status(&spec, self.eth_version, self.peer_status.as_ref(), defaults)?;
1028 self.send_eth(code::STATUS, &status.encode()).await?;
1029 self.our_status = Some(status.clone());
1030 Ok(Applied::StatusSent(status))
1031 }
1032 _ if !self.ready() => {
1033 bail!("the eth handshake has not finished: both Statuses must be exchanged first")
1034 }
1035 Command::SendHeaders {
1036 request_id,
1037 headers,
1038 } => {
1039 let id = reply_id(request_id)?;
1040 let Some(Owed::Headers(req)) = self.owed.get(&id).cloned() else {
1041 bail!("there is no unanswered GetBlockHeaders {id}");
1042 };
1043 validate_answer(&req, &headers)?;
1044 let hashes: Vec<String> = headers
1045 .iter()
1046 .map(|h| eth::hex0x(ð::header_hash(h)))
1047 .collect();
1048 self.send_eth(code::BLOCK_HEADERS, ð::encode_response(id, headers))
1049 .await?;
1050 self.owed.remove(&id);
1051 Ok(Applied::Sent(format!(
1052 "sent {} headers for request {id}: {}",
1053 hashes.len(),
1054 hashes.join(", ")
1055 )))
1056 }
1057 Command::SendBodies { request_id, bodies } => {
1058 let id = reply_id(request_id)?;
1059 let Some(Owed::Bodies(n)) = self.owed.get(&id).cloned() else {
1060 bail!("there is no unanswered GetBlockBodies {id}");
1061 };
1062 ensure!(
1063 bodies.len() <= n,
1064 "{} bodies for a request naming {n} blocks",
1065 bodies.len()
1066 );
1067 let count = bodies.len();
1068 self.send_eth(code::BLOCK_BODIES, ð::encode_response(id, bodies))
1069 .await?;
1070 self.owed.remove(&id);
1071 Ok(Applied::Sent(format!(
1072 "sent {count} bodies for request {id}"
1073 )))
1074 }
1075 Command::SendPooled {
1076 request_id,
1077 transactions,
1078 } => {
1079 let id = reply_id(request_id)?;
1080 let Some(Owed::Pooled(asked)) = self.owed.get(&id).cloned() else {
1081 bail!("there is no unanswered GetPooledTransactions {id}");
1082 };
1083 for (_, h) in &transactions {
1084 ensure!(
1085 asked.contains(h),
1086 "transaction {} was not asked for",
1087 eth::hex0x(h)
1088 );
1089 }
1090 let count = transactions.len();
1091 let items = transactions.into_iter().map(|(t, _)| t).collect();
1092 self.send_eth(code::POOLED_TRANSACTIONS, ð::encode_response(id, items))
1093 .await?;
1094 self.owed.remove(&id);
1095 Ok(Applied::Sent(format!(
1096 "sent {count} transactions for request {id}"
1097 )))
1098 }
1099 Command::RequestHeaders(req) => {
1100 let id = self.next_id()?;
1101 self.send_eth(code::GET_BLOCK_HEADERS, &req.encode(id))
1102 .await?;
1103 let what = format!(
1104 "asked for {} headers from {:?} (request {id})",
1105 req.amount, req.origin
1106 );
1107 self.track(id, Asked::Headers(req), depth);
1108 Ok(Applied::Sent(what))
1109 }
1110 Command::RequestBodies(hashes) => {
1111 let id = self.next_id()?;
1112 self.send_eth(
1113 code::GET_BLOCK_BODIES,
1114 ð::encode_hash_request(id, &hashes),
1115 )
1116 .await?;
1117 let what = format!("asked for {} bodies (request {id})", hashes.len());
1118 self.track(id, Asked::Bodies(hashes), depth);
1119 Ok(Applied::Sent(what))
1120 }
1121 Command::RequestPooled(hashes) => {
1122 let id = self.next_id()?;
1123 self.send_eth(
1124 code::GET_POOLED_TRANSACTIONS,
1125 ð::encode_hash_request(id, &hashes),
1126 )
1127 .await?;
1128 let what = format!("asked for {} transactions (request {id})", hashes.len());
1129 self.track(id, Asked::Pooled(hashes), depth);
1130 Ok(Applied::Sent(what))
1131 }
1132 }
1133 }
1134
1135 fn next_id(&mut self) -> Result<u64> {
1136 ensure!(
1137 self.outstanding.len() < MAX_OUTSTANDING,
1138 "{MAX_OUTSTANDING} requests are already waiting for answers"
1139 );
1140 self.next_request_id = self.next_request_id.wrapping_add(1);
1141 Ok(self.next_request_id)
1142 }
1143
1144 fn track(&mut self, id: u64, asked: Asked, depth: u32) {
1145 self.outstanding.insert(
1146 id,
1147 Outstanding {
1148 asked,
1149 deadline: Instant::now() + REQUEST_TIMEOUT,
1150 depth,
1151 },
1152 );
1153 }
1154}
1155
1156fn asked_json(a: &Asked) -> Value {
1157 match a {
1158 Asked::Headers(r) => json!({"kind": "headers", "query": r.to_json()}),
1159 Asked::Bodies(h) => {
1160 json!({"kind": "bodies", "hashes": h.iter().map(|x| eth::hex0x(x)).collect::<Vec<_>>()})
1161 }
1162 Asked::Pooled(h) => {
1163 json!({"kind": "transactions", "hashes": h.iter().map(|x| eth::hex0x(x)).collect::<Vec<_>>()})
1164 }
1165 }
1166}
1167
1168fn check_headers(req: &HeaderRequest, headers: &[Rlp]) -> (bool, bool) {
1171 let numbers: Vec<Option<u64>> = headers.iter().map(|h| eth::header_number(h).ok()).collect();
1172 let first = match req.origin {
1173 Origin::Number(n) => Some(n),
1174 Origin::Hash(h) => headers
1175 .first()
1176 .filter(|f| eth::header_hash(f) == h)
1177 .and_then(|f| eth::header_number(f).ok()),
1178 };
1179 let matches = headers.is_empty()
1180 || first.is_some_and(|first| {
1181 numbers
1182 .iter()
1183 .enumerate()
1184 .all(|(i, n)| *n == req.number_at(first, i as u64))
1185 });
1186 (matches, linked(req, headers))
1187}
1188
1189fn linked(req: &HeaderRequest, headers: &[Rlp]) -> bool {
1190 if req.skip != 0 {
1191 return true;
1192 }
1193 headers.windows(2).all(|w| {
1194 let (older, newer) = if req.reverse {
1195 (&w[1], &w[0])
1196 } else {
1197 (&w[0], &w[1])
1198 };
1199 eth::header_parent(newer).ok() == Some(eth::header_hash(older))
1200 })
1201}
1202
1203pub fn validate_answer(req: &HeaderRequest, headers: &[Rlp]) -> Result<()> {
1207 ensure!(
1208 headers.len() as u64 <= req.amount,
1209 "{} headers for a request of {}",
1210 headers.len(),
1211 req.amount
1212 );
1213 let Some(first) = headers.first() else {
1214 return Ok(());
1215 };
1216 let first_number = eth::header_number(first)?;
1217 match req.origin {
1218 Origin::Number(n) => ensure!(
1219 first_number == n,
1220 "the first header is block {first_number}, the request starts at {n}"
1221 ),
1222 Origin::Hash(h) => ensure!(
1223 eth::header_hash(first) == h,
1224 "the first header's hash is {}, the request starts at {}",
1225 eth::hex0x(ð::header_hash(first)),
1226 eth::hex0x(&h)
1227 ),
1228 }
1229 for (i, h) in headers.iter().enumerate() {
1230 let n = eth::header_number(h)?;
1231 let want = req.number_at(first_number, i as u64);
1232 ensure!(
1233 Some(n) == want,
1234 "headers[{i}] is block {n}, the request asks for {want:?} there"
1235 );
1236 }
1237 ensure!(
1238 linked(req, headers),
1239 "the headers do not form a chain: a header's parentHash is not the previous header's hash"
1240 );
1241 Ok(())
1242}