Skip to content

Commit 474f1a0

Browse files
committed
fix blob processing code
1 parent eb70845 commit 474f1a0

File tree

5 files changed

+111
-42
lines changed

5 files changed

+111
-42
lines changed
Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,71 @@
11
use crate::BlockProcessingError;
22
use eth2_hashing::{hash, hash_fixed};
3-
use itertools::Itertools;
3+
use itertools::{EitherOrBoth, Itertools};
44
use ssz::Decode;
55
use ssz_types::VariableList;
6+
use std::slice::Iter;
67
use std::vec::IntoIter;
78
use types::consts::eip4844::{BLOB_TX_TYPE, VERSIONED_HASH_VERSION_KZG};
8-
use types::{EthSpec, ExecPayload, KzgCommitment, Transaction, Transactions, VersionedHash};
9+
use types::{
10+
AbstractExecPayload, BeaconBlockBodyRef, EthSpec, ExecPayload, FullPayload, FullPayloadRef,
11+
KzgCommitment, Transaction, Transactions, VersionedHash,
12+
};
913

10-
pub fn process_blob_kzg_commitments<T: EthSpec, Payload: ExecPayload<T>>(
14+
pub fn process_blob_kzg_commitments<T: EthSpec, Payload: AbstractExecPayload<T>>(
1115
block_body: BeaconBlockBodyRef<T, Payload>,
1216
) -> Result<(), BlockProcessingError> {
13-
if let (Ok(transactions), Ok(kzg_commitments)) = (
14-
block_body.execution_payload_header().transactions(),
17+
if let (Ok(payload), Ok(kzg_commitments)) = (
18+
block_body.execution_payload(),
1519
block_body.blob_kzg_commitments(),
1620
) {
17-
if !verify_kzg_commitments_against_transactions(transactions, kzg_commitments) {
18-
return Err(BlockProcessingError::BlobVersionHashMismatch);
21+
if let Some(transactions) = payload.transactions() {
22+
if !verify_kzg_commitments_against_transactions::<T>(transactions, kzg_commitments)? {
23+
return Err(BlockProcessingError::BlobVersionHashMismatch);
24+
}
1925
}
2026
}
2127

2228
Ok(())
2329
}
2430

2531
pub fn verify_kzg_commitments_against_transactions<T: EthSpec>(
26-
transactions: Transactions<T>,
32+
transactions: &Transactions<T>,
2733
kzg_commitments: &VariableList<KzgCommitment, T::MaxBlobsPerBlock>,
2834
) -> Result<bool, BlockProcessingError> {
29-
let tx_versioned_hashes = transactions
35+
let nested_iter = transactions
3036
.into_iter()
3137
.filter(|tx| {
3238
tx.get(0)
33-
.map(|tx_type| tx_type == BLOB_TX_TYPE)
39+
.map(|tx_type| *tx_type == BLOB_TX_TYPE)
3440
.unwrap_or(false)
3541
})
36-
.map(|tx| tx_peek_blob_versioned_hashes(tx))
37-
.flatten()
38-
.collect();
42+
.map(|tx| tx_peek_blob_versioned_hashes::<T>(tx));
3943

40-
//FIXME(sean) Need to check lengths are equal here, just zipping first hides if one iter is shorter
41-
// and `itertools::zip_eq` panics
42-
if tx_versioned_hashes.len() != kzg_commitments.len() {
43-
return Err(BlockProcessingError::BlobVersionHashMismatch);
44-
}
44+
itertools::process_results(nested_iter, |mut iter| {
45+
let zipped_iter = iter
46+
.flatten()
47+
// Need to use `itertools::zip_longest` here because just zipping hides if one iter is shorter
48+
// and `itertools::zip_eq` panics.
49+
.zip_longest(kzg_commitments.into_iter())
50+
.map(|next| match next {
51+
EitherOrBoth::Both(hash, commitmnet) => Ok((hash?, commitmnet)),
52+
_ => Err(BlockProcessingError::BlobVersionHashMismatch),
53+
});
4554

46-
tx_versioned_hashes
47-
.into_iter()
48-
.zip(kzg_commitments.into_iter())
49-
.all(|(tx_versioned_hash, commitment)| {
50-
tx_versioned_hash == kzg_commitment_to_versioned_hash(committment)
55+
itertools::process_results(zipped_iter, |mut iter| {
56+
iter.all(|(tx_versioned_hash, commitment)| {
57+
tx_versioned_hash == kzg_commitment_to_versioned_hash(commitment)
58+
})
5159
})
60+
})?
5261
}
5362

5463
fn tx_peek_blob_versioned_hashes<T: EthSpec>(
55-
opaque_tx: &Transaction<T>,
56-
) -> IntoIter<VersionedHash> {
57-
//FIXME(sean) there was a first byte check for blob tx type but I think it's redundant, will raise a spec PR
58-
64+
opaque_tx: &Transaction<T::MaxBytesPerTransaction>,
65+
) -> Result<
66+
impl IntoIterator<Item = Result<VersionedHash, BlockProcessingError>> + '_,
67+
BlockProcessingError,
68+
> {
5969
let tx_len = opaque_tx.len();
6070
let message_offset = 1 + u32::from_ssz_bytes(opaque_tx.get(1..5).ok_or(
6171
BlockProcessingError::BlobVersionHashIndexOutOfBounds {
@@ -64,30 +74,34 @@ fn tx_peek_blob_versioned_hashes<T: EthSpec>(
6474
},
6575
)?)?;
6676

77+
let message_offset_usize = message_offset as usize;
78+
6779
// field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 = 156
6880
let blob_versioned_hashes_offset = message_offset
6981
+ u32::from_ssz_bytes(
7082
opaque_tx
71-
.get((message_offset + 156)..(message_offset + 160))
83+
.get((message_offset_usize + 156)..(message_offset_usize + 160))
7284
.ok_or(BlockProcessingError::BlobVersionHashIndexOutOfBounds {
7385
length: tx_len,
7486
index: 160,
7587
})?,
76-
);
88+
)?;
7789

78-
opaque_tx
79-
.get(blob_versioned_hashes_offset..tx_len)
80-
.ok_or(BlockProcessingError::BlobVersionHashIndexOutOfBounds {
81-
length: tx_len,
82-
index: 160,
83-
})?
84-
.into_iter()
85-
.chunks(32)
86-
.map(|chunk| VersionedHash::from(chunk))
90+
let num_hashes = (tx_len - blob_versioned_hashes_offset as usize) / 32;
91+
92+
Ok((0..num_hashes).into_iter().map(move |i| {
93+
let bytes = opaque_tx.get(i..i + 32).ok_or(
94+
BlockProcessingError::BlobVersionHashIndexOutOfBounds {
95+
length: tx_len,
96+
index: i + 32,
97+
},
98+
)?;
99+
Ok(VersionedHash::from_slice(bytes))
100+
}))
87101
}
88102

89-
fn kzg_commitment_to_versioned_hash(kzg_commitment: &KZGCommitment) -> VersionedHash {
90-
let mut hashed_commitment = hash_fixed(kzg_commitment);
103+
fn kzg_commitment_to_versioned_hash(kzg_commitment: &KzgCommitment) -> VersionedHash {
104+
let mut hashed_commitment = hash_fixed(&kzg_commitment.0);
91105
hashed_commitment[0] = VERSIONED_HASH_VERSION_KZG;
92106
VersionedHash::from(hashed_commitment)
93107
}

consensus/state_processing/src/per_block_processing/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::signature_sets::Error as SignatureSetError;
22
use merkle_proof::MerkleTreeError;
33
use safe_arith::ArithError;
4+
use ssz::DecodeError;
45
use types::*;
56

67
/// The error returned from the `per_block_processing` function. Indicates that a block is either
@@ -53,6 +54,7 @@ pub enum BlockProcessingError {
5354
BeaconStateError(BeaconStateError),
5455
SignatureSetError(SignatureSetError),
5556
SszTypesError(ssz_types::Error),
57+
SszDecodeError(DecodeError),
5658
MerkleTreeError(MerkleTreeError),
5759
ArithError(ArithError),
5860
InconsistentBlockFork(InconsistentFork),
@@ -95,6 +97,12 @@ impl From<ssz_types::Error> for BlockProcessingError {
9597
}
9698
}
9799

100+
impl From<DecodeError> for BlockProcessingError {
101+
fn from(error: DecodeError) -> Self {
102+
BlockProcessingError::SszDecodeError(error)
103+
}
104+
}
105+
98106
impl From<ArithError> for BlockProcessingError {
99107
fn from(e: ArithError) -> Self {
100108
BlockProcessingError::ArithError(e)

consensus/types/src/kzg_commitment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tree_hash::{PackedEncoding, TreeHash};
99

1010
#[derive(Derivative, Debug, Clone, Serialize, Deserialize)]
1111
#[derivative(PartialEq, Eq, Hash)]
12-
pub struct KzgCommitment(#[serde(with = "BigArray")] [u8; 48]);
12+
pub struct KzgCommitment(#[serde(with = "BigArray")] pub [u8; 48]);
1313

1414
impl Display for KzgCommitment {
1515
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {

consensus/types/src/kzg_proof.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::test_utils::{RngCore, TestRandom};
22
use serde::{Deserialize, Serialize};
3+
use serde_big_array::BigArray;
34
use ssz::{Decode, DecodeError, Encode};
45
use std::fmt;
56
use tree_hash::{PackedEncoding, TreeHash};
6-
use serde_big_array::BigArray;
77

88
const KZG_PROOF_BYTES_LEN: usize = 48;
99

consensus/types/src/payload.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub trait ExecPayload<T: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
3434
fn block_hash(&self) -> ExecutionBlockHash;
3535
fn fee_recipient(&self) -> Address;
3636
fn gas_limit(&self) -> u64;
37+
fn transactions(&self) -> Option<&Transactions<T>>;
3738

3839
// Is this a default payload? (pre-merge)
3940
fn is_default(&self) -> bool;
@@ -191,6 +192,10 @@ impl<T: EthSpec> ExecPayload<T> for FullPayloadMerge<T> {
191192
self.execution_payload.gas_limit
192193
}
193194

195+
fn transactions(&self) -> Option<&Transactions<T>> {
196+
Some(&self.execution_payload.transactions)
197+
}
198+
194199
// TODO: can this function be optimized?
195200
fn is_default(&self) -> bool {
196201
self.execution_payload == ExecutionPayloadMerge::default()
@@ -235,6 +240,10 @@ impl<T: EthSpec> ExecPayload<T> for FullPayloadCapella<T> {
235240
self.execution_payload.gas_limit
236241
}
237242

243+
fn transactions(&self) -> Option<&Transactions<T>> {
244+
Some(&self.execution_payload.transactions)
245+
}
246+
238247
// TODO: can this function be optimized?
239248
fn is_default(&self) -> bool {
240249
self.execution_payload == ExecutionPayloadCapella::default()
@@ -279,6 +288,10 @@ impl<T: EthSpec> ExecPayload<T> for FullPayloadEip4844<T> {
279288
self.execution_payload.gas_limit
280289
}
281290

291+
fn transactions(&self) -> Option<&Transactions<T>> {
292+
Some(&self.execution_payload.transactions)
293+
}
294+
282295
// TODO: can this function be optimized?
283296
fn is_default(&self) -> bool {
284297
self.execution_payload == ExecutionPayloadEip4844::default()
@@ -347,6 +360,13 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
347360
})
348361
}
349362

363+
fn transactions<'a>(&'a self) -> Option<&'a Transactions<T>> {
364+
map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
365+
cons(payload);
366+
Some(&payload.execution_payload.transactions)
367+
})
368+
}
369+
350370
fn is_default(&self) -> bool {
351371
match self {
352372
Self::Merge(payload) => payload.is_default(),
@@ -428,6 +448,13 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
428448
})
429449
}
430450

451+
fn transactions<'a>(&'a self) -> Option<&'a Transactions<T>> {
452+
map_full_payload_ref!(&'a _, self, move |payload, cons| {
453+
cons(payload);
454+
Some(&payload.execution_payload.transactions)
455+
})
456+
}
457+
431458
// TODO: can this function be optimized?
432459
fn is_default<'a>(&'a self) -> bool {
433460
match self {
@@ -687,6 +714,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
687714
}
688715
}
689716

717+
fn transactions(&self) -> Option<&Transactions<T>> {
718+
None
719+
}
720+
690721
// TODO: can this function be optimized?
691722
fn is_default(&self) -> bool {
692723
match self {
@@ -773,6 +804,10 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
773804
}
774805
}
775806

807+
fn transactions(&self) -> Option<&Transactions<T>> {
808+
None
809+
}
810+
776811
// TODO: can this function be optimized?
777812
fn is_default<'a>(&'a self) -> bool {
778813
match self {
@@ -828,6 +863,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayloadMerge<T> {
828863
self.execution_payload_header.gas_limit
829864
}
830865

866+
fn transactions(&self) -> Option<&Transactions<T>> {
867+
None
868+
}
869+
831870
fn is_default(&self) -> bool {
832871
self.execution_payload_header == ExecutionPayloadHeaderMerge::default()
833872
}
@@ -871,6 +910,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayloadCapella<T> {
871910
self.execution_payload_header.gas_limit
872911
}
873912

913+
fn transactions(&self) -> Option<&Transactions<T>> {
914+
None
915+
}
916+
874917
fn is_default(&self) -> bool {
875918
self.execution_payload_header == ExecutionPayloadHeaderCapella::default()
876919
}
@@ -914,6 +957,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayloadEip4844<T> {
914957
self.execution_payload_header.gas_limit
915958
}
916959

960+
fn transactions(&self) -> Option<&Transactions<T>> {
961+
None
962+
}
963+
917964
fn is_default(&self) -> bool {
918965
self.execution_payload_header == ExecutionPayloadHeaderEip4844::default()
919966
}

0 commit comments

Comments
 (0)