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::{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 payload_length = self.request_id.length() + self.message.length();
860 Header { list: true, payload_length }.length_with_payload()
861 }
862}
863
864impl<T> Decodable for RequestPair<T>
866where
867 T: Decodable,
868{
869 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
870 let header = Header::decode(buf)?;
871
872 let initial_length = buf.len();
873 let request_id = u64::decode(buf)?;
874 let message = T::decode(buf)?;
875
876 let consumed_len = initial_length - buf.len();
879 if consumed_len != header.payload_length {
880 return Err(alloy_rlp::Error::UnexpectedLength)
881 }
882
883 Ok(Self { request_id, message })
884 }
885}
886
887#[cfg(test)]
888mod tests {
889 use super::MessageError;
890 use crate::{
891 message::RequestPair, BlockAccessLists, EthMessage, EthMessageID, EthNetworkPrimitives,
892 EthVersion, GetBlockAccessLists, GetNodeData, NodeData, ProtocolMessage,
893 RawCapabilityMessage,
894 };
895 use alloy_primitives::hex;
896 use alloy_rlp::{Decodable, Encodable, Error};
897 use reth_ethereum_primitives::BlockBody;
898
899 fn encode<T: Encodable>(value: T) -> Vec<u8> {
900 let mut buf = vec![];
901 value.encode(&mut buf);
902 buf
903 }
904
905 #[test]
906 fn test_removed_message_at_eth67() {
907 let get_node_data = EthMessage::<EthNetworkPrimitives>::GetNodeData(RequestPair {
908 request_id: 1337,
909 message: GetNodeData(vec![]),
910 });
911 let buf = encode(ProtocolMessage {
912 message_type: EthMessageID::GetNodeData,
913 message: get_node_data,
914 });
915 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
916 crate::EthVersion::Eth67,
917 &mut &buf[..],
918 );
919 assert!(matches!(msg, Err(MessageError::Invalid(..))));
920
921 let node_data = EthMessage::<EthNetworkPrimitives>::NodeData(RequestPair {
922 request_id: 1337,
923 message: NodeData(vec![]),
924 });
925 let buf =
926 encode(ProtocolMessage { message_type: EthMessageID::NodeData, message: node_data });
927 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
928 crate::EthVersion::Eth67,
929 &mut &buf[..],
930 );
931 assert!(matches!(msg, Err(MessageError::Invalid(..))));
932 }
933
934 #[test]
935 fn test_bal_message_version_gating() {
936 let get_block_access_lists =
937 EthMessage::<EthNetworkPrimitives>::GetBlockAccessLists(RequestPair {
938 request_id: 1337,
939 message: GetBlockAccessLists(vec![]),
940 });
941 let buf = encode(ProtocolMessage {
942 message_type: EthMessageID::GetBlockAccessLists,
943 message: get_block_access_lists,
944 });
945 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
946 EthVersion::Eth70,
947 &mut &buf[..],
948 );
949 assert!(matches!(
950 msg,
951 Err(MessageError::Invalid(EthVersion::Eth70, EthMessageID::GetBlockAccessLists))
952 ));
953
954 let block_access_lists =
955 EthMessage::<EthNetworkPrimitives>::BlockAccessLists(RequestPair {
956 request_id: 1337,
957 message: BlockAccessLists(vec![]),
958 });
959 let buf = encode(ProtocolMessage {
960 message_type: EthMessageID::BlockAccessLists,
961 message: block_access_lists,
962 });
963 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
964 EthVersion::Eth70,
965 &mut &buf[..],
966 );
967 assert!(matches!(
968 msg,
969 Err(MessageError::Invalid(EthVersion::Eth70, EthMessageID::BlockAccessLists))
970 ));
971 }
972
973 #[test]
974 fn test_bal_message_eth71_roundtrip() {
975 let msg = ProtocolMessage::from(EthMessage::<EthNetworkPrimitives>::GetBlockAccessLists(
976 RequestPair { request_id: 42, message: GetBlockAccessLists(vec![]) },
977 ));
978 let encoded = encode(msg.clone());
979 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
980 EthVersion::Eth71,
981 &mut &encoded[..],
982 )
983 .unwrap();
984
985 assert_eq!(decoded, msg);
986 }
987
988 #[test]
989 fn request_pair_encode() {
990 let request_pair = RequestPair { request_id: 1337, message: vec![5u8] };
991
992 let expected = hex!("c5820539c105");
999 let got = encode(request_pair);
1000 assert_eq!(expected[..], got, "expected: {expected:X?}, got: {got:X?}",);
1001 }
1002
1003 #[test]
1004 fn request_pair_decode() {
1005 let raw_pair = &hex!("c5820539c105")[..];
1006
1007 let expected = RequestPair { request_id: 1337, message: vec![5u8] };
1008
1009 let got = RequestPair::<Vec<u8>>::decode(&mut &*raw_pair).unwrap();
1010 assert_eq!(expected.length(), raw_pair.len());
1011 assert_eq!(expected, got);
1012 }
1013
1014 #[test]
1015 fn malicious_request_pair_decode() {
1016 let raw_pair = &hex!("c5820539c20505")[..];
1026
1027 let result = RequestPair::<Vec<u8>>::decode(&mut &*raw_pair);
1028 assert!(matches!(result, Err(Error::UnexpectedLength)));
1029 }
1030
1031 #[test]
1032 fn empty_block_bodies_protocol() {
1033 let empty_block_bodies =
1034 ProtocolMessage::from(EthMessage::<EthNetworkPrimitives>::BlockBodies(RequestPair {
1035 request_id: 0,
1036 message: Default::default(),
1037 }));
1038 let mut buf = Vec::new();
1039 empty_block_bodies.encode(&mut buf);
1040 let decoded =
1041 ProtocolMessage::decode_message(EthVersion::Eth68, &mut buf.as_slice()).unwrap();
1042 assert_eq!(empty_block_bodies, decoded);
1043 }
1044
1045 #[test]
1046 fn empty_block_body_protocol() {
1047 let empty_block_bodies =
1048 ProtocolMessage::from(EthMessage::<EthNetworkPrimitives>::BlockBodies(RequestPair {
1049 request_id: 0,
1050 message: vec![BlockBody {
1051 transactions: vec![],
1052 ommers: vec![],
1053 withdrawals: Some(Default::default()),
1054 }]
1055 .into(),
1056 }));
1057 let mut buf = Vec::new();
1058 empty_block_bodies.encode(&mut buf);
1059 let decoded =
1060 ProtocolMessage::decode_message(EthVersion::Eth68, &mut buf.as_slice()).unwrap();
1061 assert_eq!(empty_block_bodies, decoded);
1062 }
1063
1064 #[test]
1065 fn decode_block_bodies_message() {
1066 let buf = hex!("06c48199c1c0");
1067 let msg = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
1068 EthVersion::Eth68,
1069 &mut &buf[..],
1070 )
1071 .unwrap_err();
1072 assert!(matches!(msg, MessageError::RlpError(alloy_rlp::Error::InputTooShort)));
1073 }
1074
1075 #[test]
1076 fn custom_message_roundtrip() {
1077 let custom_payload = vec![1, 2, 3, 4, 5];
1078 let custom_message = RawCapabilityMessage::new(0x20, custom_payload.into());
1079 let protocol_message = ProtocolMessage::<EthNetworkPrimitives> {
1080 message_type: EthMessageID::Other(0x20),
1081 message: EthMessage::Other(custom_message),
1082 };
1083
1084 let encoded = encode(protocol_message.clone());
1085 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
1086 EthVersion::Eth68,
1087 &mut &encoded[..],
1088 )
1089 .unwrap();
1090
1091 assert_eq!(protocol_message, decoded);
1092 }
1093
1094 #[test]
1095 fn custom_message_empty_payload_roundtrip() {
1096 let custom_message = RawCapabilityMessage::new(0x30, vec![].into());
1097 let protocol_message = ProtocolMessage::<EthNetworkPrimitives> {
1098 message_type: EthMessageID::Other(0x30),
1099 message: EthMessage::Other(custom_message),
1100 };
1101
1102 let encoded = encode(protocol_message.clone());
1103 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_message(
1104 EthVersion::Eth68,
1105 &mut &encoded[..],
1106 )
1107 .unwrap();
1108
1109 assert_eq!(protocol_message, decoded);
1110 }
1111
1112 #[test]
1113 fn decode_status_success() {
1114 use crate::{Status, StatusMessage};
1115 use alloy_hardforks::{ForkHash, ForkId};
1116 use alloy_primitives::{B256, U256};
1117
1118 let status = Status {
1119 version: EthVersion::Eth68,
1120 chain: alloy_chains::Chain::mainnet(),
1121 total_difficulty: U256::from(100u64),
1122 blockhash: B256::random(),
1123 genesis: B256::random(),
1124 forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 },
1125 };
1126
1127 let protocol_message = ProtocolMessage::<EthNetworkPrimitives>::from(EthMessage::Status(
1128 StatusMessage::Legacy(status),
1129 ));
1130 let encoded = encode(protocol_message);
1131
1132 let decoded = ProtocolMessage::<EthNetworkPrimitives>::decode_status(
1133 EthVersion::Eth68,
1134 &mut &encoded[..],
1135 )
1136 .unwrap();
1137
1138 assert!(matches!(decoded, StatusMessage::Legacy(s) if s == status));
1139 }
1140
1141 #[test]
1142 fn eth_message_id_max_includes_block_range_update() {
1143 assert_eq!(EthMessageID::max(EthVersion::Eth69), EthMessageID::BlockRangeUpdate.to_u8(),);
1144 assert_eq!(EthMessageID::max(EthVersion::Eth70), EthMessageID::BlockRangeUpdate.to_u8(),);
1145 assert_eq!(EthMessageID::max(EthVersion::Eth68), EthMessageID::Receipts.to_u8());
1146 }
1147
1148 #[test]
1149 fn decode_status_rejects_non_status() {
1150 let msg = EthMessage::<EthNetworkPrimitives>::GetBlockBodies(RequestPair {
1151 request_id: 1,
1152 message: crate::GetBlockBodies::default(),
1153 });
1154 let protocol_message =
1155 ProtocolMessage { message_type: EthMessageID::GetBlockBodies, message: msg };
1156 let encoded = encode(protocol_message);
1157
1158 let result = ProtocolMessage::<EthNetworkPrimitives>::decode_status(
1159 EthVersion::Eth68,
1160 &mut &encoded[..],
1161 );
1162
1163 assert!(matches!(
1164 result,
1165 Err(MessageError::ExpectedStatusMessage(EthMessageID::GetBlockBodies))
1166 ));
1167 }
1168}