Skip to content

Commit f27cfff

Browse files
authored
feat(l1): implement eth/69 (#2860)
### **Motivation** A new version of the eth protocol has been released (eth/69) and some clients are supporting both eth/68 and eth/69 at the same time. We need a way of being able to encode/decode the msg from both versions of the protocol. Also, we need a way to remove easily the old version when it becomes deprecated. The new version includes changes in the status and receipts messages and a new message RangeBlockUpdate. ### **Description** A new file structure was created to accommodate the new implementation: crates/networking/p2p/rlpx/eth/ crates/networking/p2p/rlpx/eth/eth68/ -> (status.rs and receipt.rs) crates/networking/p2p/rlpx/eth/eth69/ -> (status.rs and receipt.rs) In the new eth/69 protocol the messages status and receipts have been changed, so a new intermediate structure was created to address this. The encode for each version is decided by the type of the inner struct, but the decode is not that straight forward as we don't know what version of msg we received. For the status msg was easier, as we received the eth version inside the msg we can decide which version of the protocol we are using. For the receipt msg, the only difference is the bloom field. A new function `has_bloom` has been implemented to detect when a msg have the bloom field to process it as a eth/68. The newer eth/69 receipts msg won't have the bloom field (it can be calculated from the logs). Also, as the new structure is a proxy to the different version of the structure some new getters were needed. The new msg BlockRangeUpdate was introduced with eth/69. This message includes the earliest block available, the latest block available and the its hash. This messages is been send once every epoch (aprox 6 mins). In some places, the `receipts` need to be encoded using the _bloom_ field to validate the receipt_root of a block header. The function encode_inner_with_bloom was used for these cases. ### **Future improvements** - We're not using the received `BlockRangeUpdate` message, we can use this information to discard peers quickly. - The function `has_bloom` probably could be improved or at least not use private decode functions. - As mentioned before, this implementation is made with the assumption that eth/68 is going to be deprecated in the future. - We are currently not running the latest version of hive that includes the Closes #2805 & #2785 References: [EIP-7642](https://eips.ethereum.org/EIPS/eip-7642#rationale) [geth implementation](ethereum/go-ethereum#29158) [eth/69](https://github.com/ethereum/devp2p/blob/master/caps/eth.md)
1 parent c2f75cd commit f27cfff

File tree

27 files changed

+811
-167
lines changed

27 files changed

+811
-167
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/common/rlp/decode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ pub fn decode_bytes(data: &[u8]) -> Result<(&[u8], &[u8]), RLPDecodeError> {
513513
/// Pads a slice of bytes with zeros on the left to make it a fixed size slice.
514514
/// The size of the data must be less than or equal to the size of the output array.
515515
#[inline]
516-
pub(crate) fn static_left_pad<const N: usize>(data: &[u8]) -> Result<[u8; N], RLPDecodeError> {
516+
pub fn static_left_pad<const N: usize>(data: &[u8]) -> Result<[u8; N], RLPDecodeError> {
517517
let mut result = [0; N];
518518

519519
if data.is_empty() {

crates/common/rlp/encode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl<T: RLPEncode> RLPEncode for Vec<T> {
248248
}
249249
}
250250

251-
pub(crate) fn encode_length(total_len: usize, buf: &mut dyn BufMut) {
251+
pub fn encode_length(total_len: usize, buf: &mut dyn BufMut) {
252252
if total_len < 56 {
253253
buf.put_u8(0xc0 + total_len as u8);
254254
} else {

crates/common/rlp/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub enum RLPDecodeError {
1515
UnexpectedString,
1616
#[error("InvalidCompression")]
1717
InvalidCompression(#[from] snap::Error),
18+
#[error("IncompatibleProtocol")]
19+
IncompatibleProtocol,
1820
#[error("{0}")]
1921
Custom(String),
2022
}

crates/common/types/block.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ pub fn compute_receipts_root(receipts: &[Receipt]) -> H256 {
256256
let iter = receipts
257257
.iter()
258258
.enumerate()
259-
.map(|(idx, receipt)| (idx.encode_to_vec(), receipt.encode_inner()));
259+
.map(|(idx, receipt)| (idx.encode_to_vec(), receipt.encode_inner_with_bloom()));
260260
Trie::compute_hash_from_unsorted_iter(iter)
261261
}
262262

crates/common/types/receipt.rs

Lines changed: 135 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,106 @@ pub struct Receipt {
1717
pub tx_type: TxType,
1818
pub succeeded: bool,
1919
pub cumulative_gas_used: u64,
20-
pub bloom: Bloom,
2120
pub logs: Vec<Log>,
2221
}
2322

2423
impl Receipt {
24+
pub fn new(tx_type: TxType, succeeded: bool, cumulative_gas_used: u64, logs: Vec<Log>) -> Self {
25+
Self {
26+
tx_type,
27+
succeeded,
28+
cumulative_gas_used,
29+
logs,
30+
}
31+
}
32+
33+
pub fn encode_inner(&self) -> Vec<u8> {
34+
let mut encoded_data = vec![];
35+
let tx_type: u8 = self.tx_type as u8;
36+
Encoder::new(&mut encoded_data)
37+
.encode_field(&tx_type)
38+
.encode_field(&self.succeeded)
39+
.encode_field(&self.cumulative_gas_used)
40+
.encode_field(&self.logs)
41+
.finish();
42+
encoded_data
43+
}
44+
45+
pub fn encode_inner_with_bloom(&self) -> Vec<u8> {
46+
let mut encode_buff = match self.tx_type {
47+
TxType::Legacy => {
48+
vec![]
49+
}
50+
_ => {
51+
vec![self.tx_type as u8]
52+
}
53+
};
54+
let bloom = bloom_from_logs(&self.logs);
55+
Encoder::new(&mut encode_buff)
56+
.encode_field(&self.succeeded)
57+
.encode_field(&self.cumulative_gas_used)
58+
.encode_field(&bloom)
59+
.encode_field(&self.logs)
60+
.finish();
61+
encode_buff
62+
}
63+
}
64+
65+
pub fn bloom_from_logs(logs: &[Log]) -> Bloom {
66+
let mut bloom = Bloom::zero();
67+
for log in logs {
68+
bloom.accrue(BloomInput::Raw(log.address.as_ref()));
69+
for topic in log.topics.iter() {
70+
bloom.accrue(BloomInput::Raw(topic.as_ref()));
71+
}
72+
}
73+
bloom
74+
}
75+
76+
impl RLPEncode for Receipt {
77+
fn encode(&self, buf: &mut dyn bytes::BufMut) {
78+
let encoded_inner = self.encode_inner();
79+
buf.put_slice(&encoded_inner);
80+
}
81+
}
82+
83+
impl RLPDecode for Receipt {
84+
fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
85+
let decoder = Decoder::new(rlp)?;
86+
let (tx_type, decoder): (u8, _) = decoder.decode_field("tx-type")?;
87+
let (succeeded, decoder) = decoder.decode_field("succeeded")?;
88+
let (cumulative_gas_used, decoder) = decoder.decode_field("cumulative_gas_used")?;
89+
let (logs, decoder) = decoder.decode_field("logs")?;
90+
91+
let Some(tx_type) = TxType::from_u8(tx_type) else {
92+
return Err(RLPDecodeError::Custom(
93+
"Invalid transaction type".to_string(),
94+
));
95+
};
96+
97+
Ok((
98+
Receipt {
99+
tx_type,
100+
succeeded,
101+
cumulative_gas_used,
102+
logs,
103+
},
104+
decoder.finish()?,
105+
))
106+
}
107+
}
108+
109+
/// Result of a transaction
110+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
111+
pub struct ReceiptWithBloom {
112+
pub tx_type: TxType,
113+
pub succeeded: bool,
114+
pub cumulative_gas_used: u64,
115+
pub bloom: Bloom,
116+
pub logs: Vec<Log>,
117+
}
118+
119+
impl ReceiptWithBloom {
25120
pub fn new(tx_type: TxType, succeeded: bool, cumulative_gas_used: u64, logs: Vec<Log>) -> Self {
26121
Self {
27122
tx_type,
@@ -31,6 +126,7 @@ impl Receipt {
31126
logs,
32127
}
33128
}
129+
34130
// By reading the typed transactions EIP, and some geth code:
35131
// - https://eips.ethereum.org/EIPS/eip-2718
36132
// - https://github.com/ethereum/go-ethereum/blob/330190e476e2a2de4aac712551629a4134f802d5/core/types/receipt.go#L143
@@ -69,7 +165,7 @@ impl Receipt {
69165
/// Decodes Receipts in the following formats:
70166
/// A) Legacy receipts: rlp(receipt)
71167
/// B) Non legacy receipts: tx_type | rlp(receipt).
72-
pub fn decode_inner(rlp: &[u8]) -> Result<Receipt, RLPDecodeError> {
168+
pub fn decode_inner(rlp: &[u8]) -> Result<Self, RLPDecodeError> {
73169
// Obtain TxType
74170
let (tx_type, rlp) = match rlp.first() {
75171
Some(tx_type) if *tx_type < 0x7f => {
@@ -97,7 +193,7 @@ impl Receipt {
97193
let (logs, decoder) = decoder.decode_field("logs")?;
98194
decoder.finish()?;
99195

100-
Ok(Receipt {
196+
Ok(Self {
101197
tx_type,
102198
succeeded,
103199
cumulative_gas_used,
@@ -107,18 +203,7 @@ impl Receipt {
107203
}
108204
}
109205

110-
fn bloom_from_logs(logs: &[Log]) -> Bloom {
111-
let mut bloom = Bloom::zero();
112-
for log in logs {
113-
bloom.accrue(BloomInput::Raw(log.address.as_ref()));
114-
for topic in log.topics.iter() {
115-
bloom.accrue(BloomInput::Raw(topic.as_ref()));
116-
}
117-
}
118-
bloom
119-
}
120-
121-
impl RLPEncode for Receipt {
206+
impl RLPEncode for ReceiptWithBloom {
122207
/// Receipts can be encoded in the following formats:
123208
/// A) Legacy receipts: rlp(receipt)
124209
/// B) Non legacy receipts: rlp(Bytes(tx_type | rlp(receipt))).
@@ -137,7 +222,7 @@ impl RLPEncode for Receipt {
137222
}
138223
}
139224

140-
impl RLPDecode for Receipt {
225+
impl RLPDecode for ReceiptWithBloom {
141226
/// Receipts can be encoded in the following formats:
142227
/// A) Legacy receipts: rlp(receipt)
143228
/// B) Non legacy receipts: rlp(Bytes(tx_type | rlp(receipt))).
@@ -169,7 +254,7 @@ impl RLPDecode for Receipt {
169254
let (logs, decoder) = decoder.decode_field("logs")?;
170255

171256
Ok((
172-
Receipt {
257+
ReceiptWithBloom {
173258
tx_type,
174259
succeeded,
175260
cumulative_gas_used,
@@ -181,6 +266,29 @@ impl RLPDecode for Receipt {
181266
}
182267
}
183268

269+
impl From<&Receipt> for ReceiptWithBloom {
270+
fn from(receipt: &Receipt) -> Self {
271+
Self {
272+
tx_type: receipt.tx_type,
273+
succeeded: receipt.succeeded,
274+
cumulative_gas_used: receipt.cumulative_gas_used,
275+
bloom: bloom_from_logs(&receipt.logs),
276+
logs: receipt.logs.clone(),
277+
}
278+
}
279+
}
280+
281+
impl From<&ReceiptWithBloom> for Receipt {
282+
fn from(receipt: &ReceiptWithBloom) -> Self {
283+
Self {
284+
tx_type: receipt.tx_type,
285+
succeeded: receipt.succeeded,
286+
cumulative_gas_used: receipt.cumulative_gas_used,
287+
logs: receipt.logs.clone(),
288+
}
289+
}
290+
}
291+
184292
/// Data record produced during the execution of a transaction.
185293
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
186294
pub struct Log {
@@ -224,7 +332,6 @@ mod test {
224332
tx_type: TxType::Legacy,
225333
succeeded: true,
226334
cumulative_gas_used: 1200,
227-
bloom: Bloom::random(),
228335
logs: vec![Log {
229336
address: Address::random(),
230337
topics: vec![],
@@ -241,7 +348,6 @@ mod test {
241348
tx_type: TxType::EIP4844,
242349
succeeded: true,
243350
cumulative_gas_used: 1500,
244-
bloom: Bloom::random(),
245351
logs: vec![Log {
246352
address: Address::random(),
247353
topics: vec![],
@@ -251,10 +357,9 @@ mod test {
251357
let encoded_receipt = receipt.encode_to_vec();
252358
assert_eq!(receipt, Receipt::decode(&encoded_receipt).unwrap())
253359
}
254-
255360
#[test]
256361
fn test_encode_decode_inner_receipt_legacy() {
257-
let receipt = Receipt {
362+
let receipt = ReceiptWithBloom {
258363
tx_type: TxType::Legacy,
259364
succeeded: true,
260365
cumulative_gas_used: 1200,
@@ -266,12 +371,15 @@ mod test {
266371
}],
267372
};
268373
let encoded_receipt = receipt.encode_inner();
269-
assert_eq!(receipt, Receipt::decode_inner(&encoded_receipt).unwrap())
374+
assert_eq!(
375+
receipt,
376+
ReceiptWithBloom::decode_inner(&encoded_receipt).unwrap()
377+
)
270378
}
271379

272380
#[test]
273381
fn test_encode_decode_receipt_inner_non_legacy() {
274-
let receipt = Receipt {
382+
let receipt = ReceiptWithBloom {
275383
tx_type: TxType::EIP4844,
276384
succeeded: true,
277385
cumulative_gas_used: 1500,
@@ -283,6 +391,9 @@ mod test {
283391
}],
284392
};
285393
let encoded_receipt = receipt.encode_inner();
286-
assert_eq!(receipt, Receipt::decode_inner(&encoded_receipt).unwrap())
394+
assert_eq!(
395+
receipt,
396+
ReceiptWithBloom::decode_inner(&encoded_receipt).unwrap()
397+
)
287398
}
288399
}

crates/networking/p2p/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ ethrex-blockchain.workspace = true
1111
ethrex-rlp.workspace = true
1212
ethrex-storage.workspace = true
1313
ethrex-trie.workspace = true
14-
14+
ethereum-types.workspace = true
1515
tracing.workspace = true
1616
tokio.workspace = true
1717
tokio-util.workspace = true

crates/networking/p2p/peer_handler.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
blocks::{
1818
BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders, BLOCK_HEADER_LIMIT,
1919
},
20-
receipts::{GetReceipts, Receipts},
20+
receipts::GetReceipts,
2121
},
2222
message::Message as RLPxMessage,
2323
p2p::{Capability, SUPPORTED_ETH_CAPABILITIES, SUPPORTED_SNAP_CAPABILITIES},
@@ -280,10 +280,11 @@ impl PeerHandler {
280280
if let Some(receipts) = tokio::time::timeout(PEER_REPLY_TIMEOUT, async move {
281281
loop {
282282
match receiver.recv().await {
283-
Some(RLPxMessage::Receipts(Receipts { id, receipts }))
284-
if id == request_id =>
285-
{
286-
return Some(receipts)
283+
Some(RLPxMessage::Receipts(receipts)) => {
284+
if receipts.get_id() == request_id {
285+
return Some(receipts.get_receipts());
286+
}
287+
return None;
287288
}
288289
// Ignore replies that don't match the expected id (such as late responses)
289290
Some(_) => continue,

0 commit comments

Comments
 (0)