1use super::{
10 broadcast::NewBlockHashes, BlockAccessLists, BlockBodies, BlockHeaders, GetBlockAccessLists,
11 GetBlockBodies, GetBlockHeaders, GetNodeData, GetPooledTransactions, GetReceipts,
12 GetReceipts70, NewPooledTransactionHashes66, NewPooledTransactionHashes68, NodeData,
13 PooledTransactions, Receipts, Status, StatusEth69, Transactions,
14};
15use crate::{
16 status::StatusMessage, BlockRangeUpdate, BroadcastPoolTransactions, Cells,
17 EthNetworkPrimitives, EthVersion, GetCells, NetworkPrimitives, NewPooledTransactionHashes72,
18 RawCapabilityMessage, Receipts69, Receipts70, SharedTransactions,
19};
20use alloc::{boxed::Box, string::String, sync::Arc};
21use alloy_primitives::{
22 bytes::{Buf, BufMut},
23 Bytes,
24};
25use alloy_rlp::{length_of_length, Decodable, Encodable, Header};
26use core::fmt::Debug;
27
28pub const MAX_MESSAGE_SIZE: usize = 10 * 1024 * 1024;
31
32pub const TX_MEMORY_BUDGET_MULTIPLIER: usize = 2;
40
41#[derive(thiserror::Error, Debug)]
43pub enum MessageError {
44 #[error("message id {1:?} is invalid for version {0:?}")]
46 Invalid(EthVersion, EthMessageID),
47 #[error("expected status message but received {0:?}")]
49 ExpectedStatusMessage(EthMessageID),
50 #[error("RLP error: {0}")]
52 RlpError(#[from] alloy_rlp::Error),
53 #[error("{0}")]
55 Other(String),
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61pub struct ProtocolMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
62 pub message_type: EthMessageID,
64 #[cfg_attr(
66 feature = "serde",
67 serde(bound = "EthMessage<N>: serde::Serialize + serde::de::DeserializeOwned")
68 )]
69 pub message: EthMessage<N>,
70}
71
72impl<N: NetworkPrimitives> ProtocolMessage<N> {
73 pub fn decode_status(
78 version: EthVersion,
79 buf: &mut &[u8],
80 ) -> Result<StatusMessage, MessageError> {
81 let message_type = EthMessageID::decode(buf)?;
82
83 if message_type != EthMessageID::Status {
84 return Err(MessageError::ExpectedStatusMessage(message_type))
85 }
86
87 let status = if version < EthVersion::Eth69 {
88 StatusMessage::Legacy(Status::decode(buf)?)
89 } else {
90 StatusMessage::Eth69(StatusEth69::decode(buf)?)
91 };
92
93 Ok(status)
94 }
95
96 pub fn decode_message(version: EthVersion, buf: &mut &[u8]) -> Result<Self, MessageError> {
100 Self::decode_message_with_tx_memory_budget(version, buf, usize::MAX)
101 }
102
103 pub fn decode_message_with_tx_memory_budget(
109 version: EthVersion,
110 buf: &mut &[u8],
111 tx_memory_budget: usize,
112 ) -> Result<Self, MessageError> {
113 let message_type = EthMessageID::decode(buf)?;
114
115 let message = match message_type {
118 EthMessageID::Status => EthMessage::Status(if version < EthVersion::Eth69 {
119 StatusMessage::Legacy(Status::decode(buf)?)
120 } else {
121 StatusMessage::Eth69(StatusEth69::decode(buf)?)
122 }),
123 EthMessageID::NewBlockHashes => {
124 EthMessage::NewBlockHashes(NewBlockHashes::decode(buf)?)
125 }
126 EthMessageID::NewBlock => {
127 EthMessage::NewBlock(Box::new(N::NewBlockPayload::decode(buf)?))
128 }
129 EthMessageID::Transactions => EthMessage::Transactions(
130 Transactions::decode_with_memory_budget(buf, tx_memory_budget)?,
131 ),
132 EthMessageID::NewPooledTransactionHashes => {
133 if version >= EthVersion::Eth72 {
134 EthMessage::NewPooledTransactionHashes72(NewPooledTransactionHashes72::decode(
135 buf,
136 )?)
137 } else if version >= EthVersion::Eth68 {
138 EthMessage::NewPooledTransactionHashes68(NewPooledTransactionHashes68::decode(
139 buf,
140 )?)
141 } else {
142 EthMessage::NewPooledTransactionHashes66(NewPooledTransactionHashes66::decode(
143 buf,
144 )?)
145 }
146 }
147 EthMessageID::GetBlockHeaders => EthMessage::GetBlockHeaders(RequestPair::decode(buf)?),
148 EthMessageID::BlockHeaders => EthMessage::BlockHeaders(RequestPair::decode(buf)?),
149 EthMessageID::GetBlockBodies => EthMessage::GetBlockBodies(RequestPair::decode(buf)?),
150 EthMessageID::BlockBodies => EthMessage::BlockBodies(RequestPair::decode(buf)?),
151 EthMessageID::GetPooledTransactions => {
152 EthMessage::GetPooledTransactions(RequestPair::decode(buf)?)
153 }
154 EthMessageID::PooledTransactions => {
155 EthMessage::PooledTransactions(RequestPair::decode_with(buf, |buf| {
156 PooledTransactions::decode_with_memory_budget(buf, tx_memory_budget)
157 })?)
158 }
159 EthMessageID::GetNodeData => {
160 if version >= EthVersion::Eth67 {
161 return Err(MessageError::Invalid(version, EthMessageID::GetNodeData))
162 }
163 EthMessage::GetNodeData(RequestPair::decode(buf)?)
164 }
165 EthMessageID::NodeData => {
166 if version >= EthVersion::Eth67 {
167 return Err(MessageError::Invalid(version, EthMessageID::NodeData))
168 }
169 EthMessage::NodeData(RequestPair::decode(buf)?)
170 }
171 EthMessageID::GetReceipts => {
172 if version >= EthVersion::Eth70 {
173 EthMessage::GetReceipts70(RequestPair::decode(buf)?)
174 } else {
175 EthMessage::GetReceipts(RequestPair::decode(buf)?)
176 }
177 }
178 EthMessageID::Receipts => {
179 match version {
180 v if v >= EthVersion::Eth70 => {
181 EthMessage::Receipts70(RequestPair::decode(buf)?)
185 }
186 EthVersion::Eth69 => {
187 EthMessage::Receipts69(RequestPair::decode(buf)?)
189 }
190 _ => {
191 EthMessage::Receipts(RequestPair::decode(buf)?)
193 }
194 }
195 }
196 EthMessageID::BlockRangeUpdate => {
197 if version < EthVersion::Eth69 {
198 return Err(MessageError::Invalid(version, EthMessageID::BlockRangeUpdate))
199 }
200 EthMessage::BlockRangeUpdate(BlockRangeUpdate::decode(buf)?)
201 }
202 EthMessageID::GetBlockAccessLists => {
203 if version < EthVersion::Eth71 {
204 return Err(MessageError::Invalid(version, EthMessageID::GetBlockAccessLists))
205 }
206 EthMessage::GetBlockAccessLists(RequestPair::decode(buf)?)
207 }
208 EthMessageID::BlockAccessLists => {
209 if version < EthVersion::Eth71 {
210 return Err(MessageError::Invalid(version, EthMessageID::BlockAccessLists))
211 }
212 EthMessage::BlockAccessLists(RequestPair::decode(buf)?)
213 }
214 EthMessageID::Cells => {
215 if version < EthVersion::Eth72 {
216 return Err(MessageError::Invalid(version, EthMessageID::Cells))
217 }
218 EthMessage::Cells(RequestPair::decode(buf)?)
219 }
220 EthMessageID::GetCells => {
221 if version < EthVersion::Eth72 {
222 return Err(MessageError::Invalid(version, EthMessageID::GetCells))
223 }
224 EthMessage::GetCells(RequestPair::decode(buf)?)
225 }
226 EthMessageID::Other(_) => {
227 let raw_payload = Bytes::copy_from_slice(buf);
228 buf.advance(raw_payload.len());
229 EthMessage::Other(RawCapabilityMessage::new(
230 message_type.to_u8() as usize,
231 raw_payload.into(),
232 ))
233 }
234 };
235 Ok(Self { message_type, message })
236 }
237}
238
239impl<N: NetworkPrimitives> Encodable for ProtocolMessage<N> {
240 fn encode(&self, out: &mut dyn BufMut) {
243 self.message_type.encode(out);
244 self.message.encode(out);
245 }
246 fn length(&self) -> usize {
247 self.message_type.length() + self.message.length()
248 }
249}
250
251impl<N: NetworkPrimitives> From<EthMessage<N>> for ProtocolMessage<N> {
252 fn from(message: EthMessage<N>) -> Self {
253 Self { message_type: message.message_id(), message }
254 }
255}
256
257#[derive(Clone, Debug)]
259pub struct ProtocolBroadcastMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
260 pub message_type: EthMessageID,
262 pub message: EthBroadcastMessage<N>,
265}
266
267impl<N: NetworkPrimitives> Encodable for ProtocolBroadcastMessage<N> {
268 fn encode(&self, out: &mut dyn BufMut) {
271 self.message_type.encode(out);
272 self.message.encode(out);
273 }
274 fn length(&self) -> usize {
275 self.message_type.length() + self.message.length()
276 }
277}
278
279impl<N: NetworkPrimitives> From<EthBroadcastMessage<N>> for ProtocolBroadcastMessage<N> {
280 fn from(message: EthBroadcastMessage<N>) -> Self {
281 Self { message_type: message.message_id(), message }
282 }
283}
284
285#[derive(Clone, Debug, PartialEq, Eq)]
311#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
312pub enum EthMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
313 Status(StatusMessage),
315 NewBlockHashes(NewBlockHashes),
317 #[cfg_attr(
319 feature = "serde",
320 serde(bound = "N::NewBlockPayload: serde::Serialize + serde::de::DeserializeOwned")
321 )]
322 NewBlock(Box<N::NewBlockPayload>),
323 #[cfg_attr(
325 feature = "serde",
326 serde(bound = "N::BroadcastedTransaction: serde::Serialize + serde::de::DeserializeOwned")
327 )]
328 Transactions(Transactions<N::BroadcastedTransaction>),
329 NewPooledTransactionHashes66(NewPooledTransactionHashes66),
331 NewPooledTransactionHashes68(NewPooledTransactionHashes68),
333 NewPooledTransactionHashes72(NewPooledTransactionHashes72),
335
336 GetBlockHeaders(RequestPair<GetBlockHeaders>),
339 #[cfg_attr(
341 feature = "serde",
342 serde(bound = "N::BlockHeader: serde::Serialize + serde::de::DeserializeOwned")
343 )]
344 BlockHeaders(RequestPair<BlockHeaders<N::BlockHeader>>),
345 GetBlockBodies(RequestPair<GetBlockBodies>),
347 #[cfg_attr(
349 feature = "serde",
350 serde(bound = "N::BlockBody: serde::Serialize + serde::de::DeserializeOwned")
351 )]
352 BlockBodies(RequestPair<BlockBodies<N::BlockBody>>),
353 GetPooledTransactions(RequestPair<GetPooledTransactions>),
355 #[cfg_attr(
357 feature = "serde",
358 serde(bound = "N::PooledTransaction: serde::Serialize + serde::de::DeserializeOwned")
359 )]
360 PooledTransactions(RequestPair<PooledTransactions<N::PooledTransaction>>),
361 GetNodeData(RequestPair<GetNodeData>),
363 NodeData(RequestPair<NodeData>),
365 GetReceipts(RequestPair<GetReceipts>),
367 GetReceipts70(RequestPair<GetReceipts70>),
373 GetBlockAccessLists(RequestPair<GetBlockAccessLists>),
375 #[cfg_attr(
377 feature = "serde",
378 serde(bound = "N::Receipt: serde::Serialize + serde::de::DeserializeOwned")
379 )]
380 Receipts(RequestPair<Receipts<N::Receipt>>),
381 #[cfg_attr(
383 feature = "serde",
384 serde(bound = "N::Receipt: serde::Serialize + serde::de::DeserializeOwned")
385 )]
386 Receipts69(RequestPair<Receipts69<N::Receipt>>),
387 #[cfg_attr(
389 feature = "serde",
390 serde(bound = "N::Receipt: serde::Serialize + serde::de::DeserializeOwned")
391 )]
392 Receipts70(RequestPair<Receipts70<N::Receipt>>),
397 BlockAccessLists(RequestPair<BlockAccessLists>),
399 Cells(RequestPair<Cells>),
401 GetCells(RequestPair<GetCells>),
403 #[cfg_attr(
405 feature = "serde",
406 serde(bound = "N::BroadcastedTransaction: serde::Serialize + serde::de::DeserializeOwned")
407 )]
408 BlockRangeUpdate(BlockRangeUpdate),
409 Other(RawCapabilityMessage),
411}
412
413impl<N: NetworkPrimitives> EthMessage<N> {
414 pub const fn message_id(&self) -> EthMessageID {
416 match self {
417 Self::Status(_) => EthMessageID::Status,
418 Self::NewBlockHashes(_) => EthMessageID::NewBlockHashes,
419 Self::NewBlock(_) => EthMessageID::NewBlock,
420 Self::Transactions(_) => EthMessageID::Transactions,
421 Self::NewPooledTransactionHashes66(_) |
422 Self::NewPooledTransactionHashes68(_) |
423 Self::NewPooledTransactionHashes72(_) => EthMessageID::NewPooledTransactionHashes,
424 Self::GetBlockHeaders(_) => EthMessageID::GetBlockHeaders,
425 Self::BlockHeaders(_) => EthMessageID::BlockHeaders,
426 Self::GetBlockBodies(_) => EthMessageID::GetBlockBodies,
427 Self::BlockBodies(_) => EthMessageID::BlockBodies,
428 Self::GetPooledTransactions(_) => EthMessageID::GetPooledTransactions,
429 Self::PooledTransactions(_) => EthMessageID::PooledTransactions,
430 Self::GetNodeData(_) => EthMessageID::GetNodeData,
431 Self::NodeData(_) => EthMessageID::NodeData,
432 Self::GetReceipts(_) | Self::GetReceipts70(_) => EthMessageID::GetReceipts,
433 Self::Receipts(_) | Self::Receipts69(_) | Self::Receipts70(_) => EthMessageID::Receipts,
434 Self::BlockRangeUpdate(_) => EthMessageID::BlockRangeUpdate,
435 Self::GetBlockAccessLists(_) => EthMessageID::GetBlockAccessLists,
436 Self::BlockAccessLists(_) => EthMessageID::BlockAccessLists,
437 Self::Cells(_) => EthMessageID::Cells,
438 Self::GetCells(_) => EthMessageID::GetCells,
439 Self::Other(msg) => EthMessageID::Other(msg.id as u8),
440 }
441 }
442
443 pub const fn is_request(&self) -> bool {
445 matches!(
446 self,
447 Self::GetBlockBodies(_) |
448 Self::GetBlockHeaders(_) |
449 Self::GetReceipts(_) |
450 Self::GetReceipts70(_) |
451 Self::GetBlockAccessLists(_) |
452 Self::GetCells(_) |
453 Self::GetPooledTransactions(_) |
454 Self::GetNodeData(_)
455 )
456 }
457
458 pub const fn is_response(&self) -> bool {
460 matches!(
461 self,
462 Self::PooledTransactions(_) |
463 Self::Receipts(_) |
464 Self::Receipts69(_) |
465 Self::Receipts70(_) |
466 Self::BlockAccessLists(_) |
467 Self::BlockHeaders(_) |
468 Self::BlockBodies(_) |
469 Self::NodeData(_) |
470 Self::Cells(_)
471 )
472 }
473
474 pub fn map_versioned(self, version: EthVersion) -> Self {
479 if version >= EthVersion::Eth70 {
483 return match self {
484 Self::GetReceipts(pair) => {
485 let RequestPair { request_id, message } = pair;
486 let req = RequestPair {
487 request_id,
488 message: GetReceipts70 {
489 first_block_receipt_index: 0,
490 block_hashes: message.0,
491 },
492 };
493 Self::GetReceipts70(req)
494 }
495 other => other,
496 }
497 }
498
499 self
500 }
501}
502
503impl<N: NetworkPrimitives> Encodable for EthMessage<N> {
504 fn encode(&self, out: &mut dyn BufMut) {
505 match self {
506 Self::Status(status) => status.encode(out),
507 Self::NewBlockHashes(new_block_hashes) => new_block_hashes.encode(out),
508 Self::NewBlock(new_block) => new_block.encode(out),
509 Self::Transactions(transactions) => transactions.encode(out),
510 Self::NewPooledTransactionHashes66(hashes) => hashes.encode(out),
511 Self::NewPooledTransactionHashes68(hashes) => hashes.encode(out),
512 Self::NewPooledTransactionHashes72(hashes) => hashes.encode(out),
513 Self::GetBlockHeaders(request) => request.encode(out),
514 Self::BlockHeaders(headers) => headers.encode(out),
515 Self::GetBlockBodies(request) => request.encode(out),
516 Self::BlockBodies(bodies) => bodies.encode(out),
517 Self::GetPooledTransactions(request) => request.encode(out),
518 Self::PooledTransactions(transactions) => transactions.encode(out),
519 Self::GetNodeData(request) => request.encode(out),
520 Self::NodeData(data) => data.encode(out),
521 Self::GetReceipts(request) => request.encode(out),
522 Self::GetReceipts70(request) => request.encode(out),
523 Self::GetBlockAccessLists(request) => request.encode(out),
524 Self::GetCells(request) => request.encode(out),
525 Self::Receipts(receipts) => receipts.encode(out),
526 Self::Receipts69(receipt69) => receipt69.encode(out),
527 Self::Receipts70(receipt70) => receipt70.encode(out),
528 Self::BlockAccessLists(block_access_lists) => block_access_lists.encode(out),
529 Self::BlockRangeUpdate(block_range_update) => block_range_update.encode(out),
530 Self::Cells(cells) => cells.encode(out),
531 Self::Other(unknown) => out.put_slice(&unknown.payload),
532 }
533 }
534 fn length(&self) -> usize {
535 match self {
536 Self::Status(status) => status.length(),
537 Self::NewBlockHashes(new_block_hashes) => new_block_hashes.length(),
538 Self::NewBlock(new_block) => new_block.length(),
539 Self::Transactions(transactions) => transactions.length(),
540 Self::NewPooledTransactionHashes66(hashes) => hashes.length(),
541 Self::NewPooledTransactionHashes68(hashes) => hashes.length(),
542 Self::NewPooledTransactionHashes72(hashes) => hashes.length(),
543 Self::GetBlockHeaders(request) => request.length(),
544 Self::BlockHeaders(headers) => headers.length(),
545 Self::GetBlockBodies(request) => request.length(),
546 Self::BlockBodies(bodies) => bodies.length(),
547 Self::GetPooledTransactions(request) => request.length(),
548 Self::PooledTransactions(transactions) => transactions.length(),
549 Self::GetNodeData(request) => request.length(),
550 Self::NodeData(data) => data.length(),
551 Self::GetReceipts(request) => request.length(),
552 Self::GetReceipts70(request) => request.length(),
553 Self::GetBlockAccessLists(request) => request.length(),
554 Self::GetCells(request) => request.length(),
555 Self::Receipts(receipts) => receipts.length(),
556 Self::Receipts69(receipt69) => receipt69.length(),
557 Self::Receipts70(receipt70) => receipt70.length(),
558 Self::BlockAccessLists(block_access_lists) => block_access_lists.length(),
559 Self::BlockRangeUpdate(block_range_update) => block_range_update.length(),
560 Self::Cells(cells) => cells.length(),
561 Self::Other(unknown) => unknown.length(),
562 }
563 }
564}
565
566#[derive(Clone, Debug)]
574pub enum EthBroadcastMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
575 NewBlock(Arc<N::NewBlockPayload>),
577 Transactions(SharedTransactions<N::BroadcastedTransaction>),
579 BroadcastPoolTransactions(BroadcastPoolTransactions),
581}
582
583impl<N: NetworkPrimitives> EthBroadcastMessage<N> {
586 pub const fn message_id(&self) -> EthMessageID {
588 match self {
589 Self::NewBlock(_) => EthMessageID::NewBlock,
590 Self::Transactions(_) | Self::BroadcastPoolTransactions(_) => {
591 EthMessageID::Transactions
592 }
593 }
594 }
595
596 pub fn encoded(self) -> alloy_primitives::bytes::Bytes {
598 alloy_rlp::encode(ProtocolBroadcastMessage::from(self)).into()
599 }
600}
601
602impl<N: NetworkPrimitives> Encodable for EthBroadcastMessage<N> {
603 fn encode(&self, out: &mut dyn BufMut) {
604 match self {
605 Self::NewBlock(new_block) => new_block.encode(out),
606 Self::Transactions(transactions) => transactions.encode(out),
607 Self::BroadcastPoolTransactions(transactions) => transactions.encode(out),
608 }
609 }
610
611 fn length(&self) -> usize {
612 match self {
613 Self::NewBlock(new_block) => new_block.length(),
614 Self::Transactions(transactions) => transactions.length(),
615 Self::BroadcastPoolTransactions(transactions) => transactions.length(),
616 }
617 }
618}
619
620#[repr(u8)]
622#[derive(Clone, Copy, Debug, PartialEq, Eq)]
623#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
624pub enum EthMessageID {
625 Status = 0x00,
627 NewBlockHashes = 0x01,
629 Transactions = 0x02,
631 GetBlockHeaders = 0x03,
633 BlockHeaders = 0x04,
635 GetBlockBodies = 0x05,
637 BlockBodies = 0x06,
639 NewBlock = 0x07,
641 NewPooledTransactionHashes = 0x08,
643 GetPooledTransactions = 0x09,
645 PooledTransactions = 0x0a,
647 GetNodeData = 0x0d,
649 NodeData = 0x0e,
651 GetReceipts = 0x0f,
653 Receipts = 0x10,
655 BlockRangeUpdate = 0x11,
659 GetBlockAccessLists = 0x12,
663 BlockAccessLists = 0x13,
667
668 GetCells = 0x14,
672 Cells = 0x15,
676 Other(u8),
678}
679
680impl EthMessageID {
681 pub const fn to_u8(&self) -> u8 {
683 match self {
684 Self::Status => 0x00,
685 Self::NewBlockHashes => 0x01,
686 Self::Transactions => 0x02,
687 Self::GetBlockHeaders => 0x03,
688 Self::BlockHeaders => 0x04,
689 Self::GetBlockBodies => 0x05,
690 Self::BlockBodies => 0x06,
691 Self::NewBlock => 0x07,
692 Self::NewPooledTransactionHashes => 0x08,
693 Self::GetPooledTransactions => 0x09,
694 Self::PooledTransactions => 0x0a,
695 Self::GetNodeData => 0x0d,
696 Self::NodeData => 0x0e,
697 Self::GetReceipts => 0x0f,
698 Self::Receipts => 0x10,
699 Self::BlockRangeUpdate => 0x11,
700 Self::GetBlockAccessLists => 0x12,
701 Self::BlockAccessLists => 0x13,
702 Self::GetCells => 0x14,
703 Self::Cells => 0x15,
704 Self::Other(value) => *value, }
706 }
707
708 pub const fn max(version: EthVersion) -> u8 {
710 if version.is_eth72() {
711 Self::Cells.to_u8()
712 } else if version.is_eth71() {
713 Self::BlockAccessLists.to_u8()
714 } else if version.is_eth69_or_newer() {
715 Self::BlockRangeUpdate.to_u8()
716 } else {
717 Self::Receipts.to_u8()
718 }
719 }
720
721 pub const fn message_count(version: EthVersion) -> u8 {
727 Self::max(version) + 1
728 }
729}
730
731impl Encodable for EthMessageID {
732 fn encode(&self, out: &mut dyn BufMut) {
733 out.put_u8(self.to_u8());
734 }
735 fn length(&self) -> usize {
736 1
737 }
738}
739
740impl Decodable for EthMessageID {
741 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
742 let id = match buf.first().ok_or(alloy_rlp::Error::InputTooShort)? {
743 0x00 => Self::Status,
744 0x01 => Self::NewBlockHashes,
745 0x02 => Self::Transactions,
746 0x03 => Self::GetBlockHeaders,
747 0x04 => Self::BlockHeaders,
748 0x05 => Self::GetBlockBodies,
749 0x06 => Self::BlockBodies,
750 0x07 => Self::NewBlock,
751 0x08 => Self::NewPooledTransactionHashes,
752 0x09 => Self::GetPooledTransactions,
753 0x0a => Self::PooledTransactions,
754 0x0d => Self::GetNodeData,
755 0x0e => Self::NodeData,
756 0x0f => Self::GetReceipts,
757 0x10 => Self::Receipts,
758 0x11 => Self::BlockRangeUpdate,
759 0x12 => Self::GetBlockAccessLists,
760 0x13 => Self::BlockAccessLists,
761 0x14 => Self::GetCells,
762 0x15 => Self::Cells,
763 unknown => Self::Other(*unknown),
764 };
765 buf.advance(1);
766 Ok(id)
767 }
768}
769
770impl TryFrom<usize> for EthMessageID {
771 type Error = &'static str;
772
773 fn try_from(value: usize) -> Result<Self, Self::Error> {
774 match value {
775 0x00 => Ok(Self::Status),
776 0x01 => Ok(Self::NewBlockHashes),
777 0x02 => Ok(Self::Transactions),
778 0x03 => Ok(Self::GetBlockHeaders),
779 0x04 => Ok(Self::BlockHeaders),
780 0x05 => Ok(Self::GetBlockBodies),
781 0x06 => Ok(Self::BlockBodies),
782 0x07 => Ok(Self::NewBlock),
783 0x08 => Ok(Self::NewPooledTransactionHashes),
784 0x09 => Ok(Self::GetPooledTransactions),
785 0x0a => Ok(Self::PooledTransactions),
786 0x0d => Ok(Self::GetNodeData),
787 0x0e => Ok(Self::NodeData),
788 0x0f => Ok(Self::GetReceipts),
789 0x10 => Ok(Self::Receipts),
790 0x11 => Ok(Self::BlockRangeUpdate),
791 0x12 => Ok(Self::GetBlockAccessLists),
792 0x13 => Ok(Self::BlockAccessLists),
793 0x14 => Ok(Self::GetCells),
794 0x15 => Ok(Self::Cells),
795 _ => Err("Invalid message ID"),
796 }
797 }
798}
799
800#[derive(Clone, Debug, PartialEq, Eq)]
804#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
805#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
806pub struct RequestPair<T> {
807 pub request_id: u64,
809
810 pub message: T,
812}
813
814impl<T> RequestPair<T> {
815 pub fn map<F, R>(self, f: F) -> RequestPair<R>
817 where
818 F: FnOnce(T) -> R,
819 {
820 let Self { request_id, message } = self;
821 RequestPair { request_id, message: f(message) }
822 }
823
824 pub fn decode_with<F>(buf: &mut &[u8], decode_msg: F) -> alloy_rlp::Result<Self>
826 where
827 F: FnOnce(&mut &[u8]) -> alloy_rlp::Result<T>,
828 {
829 let header = Header::decode(buf)?;
830
831 let initial_length = buf.len();
832 let request_id = u64::decode(buf)?;
833 let message = decode_msg(buf)?;
834
835 let consumed_len = initial_length - buf.len();
836 if consumed_len != header.payload_length {
837 return Err(alloy_rlp::Error::UnexpectedLength)
838 }
839
840 Ok(Self { request_id, message })
841 }
842}
843
844impl<T> Encodable for RequestPair<T>
846where
847 T: Encodable,
848{
849 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
850 let header =
851 Header { list: true, payload_length: self.request_id.length() + self.message.length() };
852
853 header.encode(out);
854 self.request_id.encode(out);
855 self.message.encode(out);
856 }
857
858 fn length(&self) -> usize {
859 let mut length = 0;
860 length += self.request_id.length();
861 length += self.message.length();
862 length += length_of_length(length);
863 length
864 }
865}
866
867impl<T> Decodable for RequestPair<T>
869where
870 T: Decodable,
871{
872 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
873 let header = Header::decode(buf)?;
874
875 let initial_length = buf.len();
876 let request_id = u64::decode(buf)?;
877 let message = T::decode(buf)?;
878
879 let consumed_len = initial_length - buf.len();
882 if consumed_len != header.payload_length {
883 return Err(alloy_rlp::Error::UnexpectedLength)
884 }
885
886 Ok(Self { request_id, message })
887 }
888}
889
890#[cfg(test)]
891mod tests {
892 use super::MessageError;
893 use crate::{
894 message::RequestPair, BlockAccessLists, EthMessage, EthMessageID, EthNetworkPrimitives,
895 EthVersion, GetBlockAccessLists, GetNodeData, NodeData, ProtocolMessage,
896 RawCapabilityMessage,
897 };
898 use alloy_primitives::hex;
899 use alloy_rlp::{Decodable, Encodable, Error};
900 use reth_ethereum_primitives::BlockBody;
901
902 fn encode<T: Encodable>(value: T) -> Vec<u8> {
903 let mut buf = vec![];
904 value.encode(&mut buf);
905 buf
906 }
907
908 #[test]
909 fn test_removed_message_at_eth67() {
910 let get_node_data = EthMessage::<EthNetworkPrimitives>::GetNodeData(RequestPair {
911 request_id: 1337,
912 message: GetNodeData(vec![]),
913 });
914 let buf = encode(ProtocolMessage {
915 message_type: EthMessageID::GetNodeData,
916 message: get_node_data,
917 });
918 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
919 crate::EthVersion::Eth67,
920 &mut &buf[..],
921 );
922 assert!(matches!(msg, Err(MessageError::Invalid(..))));
923
924 let node_data = EthMessage::<EthNetworkPrimitives>::NodeData(RequestPair {
925 request_id: 1337,
926 message: NodeData(vec![]),
927 });
928 let buf =
929 encode(ProtocolMessage { message_type: EthMessageID::NodeData, message: node_data });
930 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
931 crate::EthVersion::Eth67,
932 &mut &buf[..],
933 );
934 assert!(matches!(msg, Err(MessageError::Invalid(..))));
935 }
936
937 #[test]
938 fn test_bal_message_version_gating() {
939 let get_block_access_lists =
940 EthMessage::<EthNetworkPrimitives>::GetBlockAccessLists(RequestPair {
941 request_id: 1337,
942 message: GetBlockAccessLists(vec![]),
943 });
944 let buf = encode(ProtocolMessage {
945 message_type: EthMessageID::GetBlockAccessLists,
946 message: get_block_access_lists,
947 });
948 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
949 EthVersion::Eth70,
950 &mut &buf[..],
951 );
952 assert!(matches!(
953 msg,
954 Err(MessageError::Invalid(EthVersion::Eth70, EthMessageID::GetBlockAccessLists))
955 ));
956
957 let block_access_lists =
958 EthMessage::<EthNetworkPrimitives>::BlockAccessLists(RequestPair {
959 request_id: 1337,
960 message: BlockAccessLists(vec![]),
961 });
962 let buf = encode(ProtocolMessage {
963 message_type: EthMessageID::BlockAccessLists,
964 message: block_access_lists,
965 });
966 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
967 EthVersion::Eth70,
968 &mut &buf[..],
969 );
970 assert!(matches!(
971 msg,
972 Err(MessageError::Invalid(EthVersion::Eth70, EthMessageID::BlockAccessLists))
973 ));
974 }
975
976 #[test]
977 fn test_bal_message_eth71_roundtrip() {
978 let msg = ProtocolMessage::from(EthMessage::<EthNetworkPrimitives>::GetBlockAccessLists(
979 RequestPair { request_id: 42, message: GetBlockAccessLists(vec![]) },
980 ));
981 let encoded = encode(msg.clone());
982 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
983 EthVersion::Eth71,
984 &mut &encoded[..],
985 )
986 .unwrap();
987
988 assert_eq!(decoded, msg);
989 }
990
991 #[test]
992 fn request_pair_encode() {
993 let request_pair = RequestPair { request_id: 1337, message: vec![5u8] };
994
995 let expected = hex!("c5820539c105");
1002 let got = encode(request_pair);
1003 assert_eq!(expected[..], got, "expected: {expected:X?}, got: {got:X?}",);
1004 }
1005
1006 #[test]
1007 fn request_pair_decode() {
1008 let raw_pair = &hex!("c5820539c105")[..];
1009
1010 let expected = RequestPair { request_id: 1337, message: vec![5u8] };
1011
1012 let got = RequestPair::<Vec<u8>>::decode(&mut &*raw_pair).unwrap();
1013 assert_eq!(expected.length(), raw_pair.len());
1014 assert_eq!(expected, got);
1015 }
1016
1017 #[test]
1018 fn malicious_request_pair_decode() {
1019 let raw_pair = &hex!("c5820539c20505")[..];
1029
1030 let result = RequestPair::<Vec<u8>>::decode(&mut &*raw_pair);
1031 assert!(matches!(result, Err(Error::UnexpectedLength)));
1032 }
1033
1034 #[test]
1035 fn empty_block_bodies_protocol() {
1036 let empty_block_bodies =
1037 ProtocolMessage::from(EthMessage::<EthNetworkPrimitives>::BlockBodies(RequestPair {
1038 request_id: 0,
1039 message: Default::default(),
1040 }));
1041 let mut buf = Vec::new();
1042 empty_block_bodies.encode(&mut buf);
1043 let decoded =
1044 ProtocolMessage::decode_message(EthVersion::Eth68, &mut buf.as_slice()).unwrap();
1045 assert_eq!(empty_block_bodies, decoded);
1046 }
1047
1048 #[test]
1049 fn empty_block_body_protocol() {
1050 let empty_block_bodies =
1051 ProtocolMessage::from(EthMessage::<EthNetworkPrimitives>::BlockBodies(RequestPair {
1052 request_id: 0,
1053 message: vec![BlockBody {
1054 transactions: vec![],
1055 ommers: vec![],
1056 withdrawals: Some(Default::default()),
1057 }]
1058 .into(),
1059 }));
1060 let mut buf = Vec::new();
1061 empty_block_bodies.encode(&mut buf);
1062 let decoded =
1063 ProtocolMessage::decode_message(EthVersion::Eth68, &mut buf.as_slice()).unwrap();
1064 assert_eq!(empty_block_bodies, decoded);
1065 }
1066
1067 #[test]
1068 fn decode_block_bodies_message() {
1069 let buf = hex!("06c48199c1c0");
1070 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
1071 EthVersion::Eth68,
1072 &mut &buf[..],
1073 )
1074 .unwrap_err();
1075 assert!(matches!(msg, MessageError::RlpError(alloy_rlp::Error::InputTooShort)));
1076 }
1077
1078 #[test]
1079 fn custom_message_roundtrip() {
1080 let custom_payload = vec![1, 2, 3, 4, 5];
1081 let custom_message = RawCapabilityMessage::new(0x20, custom_payload.into());
1082 let protocol_message = ProtocolMessage::<EthNetworkPrimitives> {
1083 message_type: EthMessageID::Other(0x20),
1084 message: EthMessage::Other(custom_message),
1085 };
1086
1087 let encoded = encode(protocol_message.clone());
1088 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
1089 EthVersion::Eth68,
1090 &mut &encoded[..],
1091 )
1092 .unwrap();
1093
1094 assert_eq!(protocol_message, decoded);
1095 }
1096
1097 #[test]
1098 fn custom_message_empty_payload_roundtrip() {
1099 let custom_message = RawCapabilityMessage::new(0x30, vec![].into());
1100 let protocol_message = ProtocolMessage::<EthNetworkPrimitives> {
1101 message_type: EthMessageID::Other(0x30),
1102 message: EthMessage::Other(custom_message),
1103 };
1104
1105 let encoded = encode(protocol_message.clone());
1106 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
1107 EthVersion::Eth68,
1108 &mut &encoded[..],
1109 )
1110 .unwrap();
1111
1112 assert_eq!(protocol_message, decoded);
1113 }
1114
1115 #[test]
1116 fn decode_status_success() {
1117 use crate::{Status, StatusMessage};
1118 use alloy_hardforks::{ForkHash, ForkId};
1119 use alloy_primitives::{B256, U256};
1120
1121 let status = Status {
1122 version: EthVersion::Eth68,
1123 chain: alloy_chains::Chain::mainnet(),
1124 total_difficulty: U256::from(100u64),
1125 blockhash: B256::random(),
1126 genesis: B256::random(),
1127 forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 },
1128 };
1129
1130 let protocol_message = ProtocolMessage::<EthNetworkPrimitives>::from(EthMessage::Status(
1131 StatusMessage::Legacy(status),
1132 ));
1133 let encoded = encode(protocol_message);
1134
1135 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_status(
1136 EthVersion::Eth68,
1137 &mut &encoded[..],
1138 )
1139 .unwrap();
1140
1141 assert!(matches!(decoded, StatusMessage::Legacy(s) if s == status));
1142 }
1143
1144 #[test]
1145 fn eth_message_id_max_includes_block_range_update() {
1146 assert_eq!(EthMessageID::max(EthVersion::Eth69), EthMessageID::BlockRangeUpdate.to_u8(),);
1147 assert_eq!(EthMessageID::max(EthVersion::Eth70), EthMessageID::BlockRangeUpdate.to_u8(),);
1148 assert_eq!(EthMessageID::max(EthVersion::Eth68), EthMessageID::Receipts.to_u8());
1149 }
1150
1151 #[test]
1152 fn decode_status_rejects_non_status() {
1153 let msg = EthMessage::<EthNetworkPrimitives>::GetBlockBodies(RequestPair {
1154 request_id: 1,
1155 message: crate::GetBlockBodies::default(),
1156 });
1157 let protocol_message =
1158 ProtocolMessage { message_type: EthMessageID::GetBlockBodies, message: msg };
1159 let encoded = encode(protocol_message);
1160
1161 let result = ProtocolMessage::<EthNetworkPrimitives>::decode_status(
1162 EthVersion::Eth68,
1163 &mut &encoded[..],
1164 );
1165
1166 assert!(matches!(
1167 result,
1168 Err(MessageError::ExpectedStatusMessage(EthMessageID::GetBlockBodies))
1169 ));
1170 }
1171}