mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-10 04:01:51 +00:00
Verify Versioned Hashes During Optimistic Sync (#4832)
* Convert NewPayloadRequest to use Reference * Refactor for Clarity * Verify Versioned Hashes * Added Tests for Version Hash Verification * Added Moar Tests * Fix Problems Caused By Merge * Update to use Alloy Instead of Reth Crates (#14) * Update beacon_node/execution_layer/src/engine_api/new_payload_request.rs Co-authored-by: realbigsean <seananderson33@GMAIL.com> * Faster Versioned Hash Extraction * Update to rust 1.75 & Pin alloy-consensus
This commit is contained in:
135
beacon_node/execution_layer/src/versioned_hashes.rs
Normal file
135
beacon_node/execution_layer/src/versioned_hashes.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
extern crate alloy_consensus;
|
||||
extern crate alloy_rlp;
|
||||
use alloy_consensus::TxEnvelope;
|
||||
use alloy_rlp::Decodable;
|
||||
use types::{EthSpec, ExecutionPayloadRef, Hash256, Unsigned, VersionedHash};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
DecodingTransaction(String),
|
||||
LengthMismatch { expected: usize, found: usize },
|
||||
VersionHashMismatch { expected: Hash256, found: Hash256 },
|
||||
}
|
||||
|
||||
pub fn verify_versioned_hashes<E: EthSpec>(
|
||||
execution_payload: ExecutionPayloadRef<E>,
|
||||
expected_versioned_hashes: &[VersionedHash],
|
||||
) -> Result<(), Error> {
|
||||
let versioned_hashes =
|
||||
extract_versioned_hashes_from_transactions::<E>(execution_payload.transactions())?;
|
||||
if versioned_hashes.len() != expected_versioned_hashes.len() {
|
||||
return Err(Error::LengthMismatch {
|
||||
expected: expected_versioned_hashes.len(),
|
||||
found: versioned_hashes.len(),
|
||||
});
|
||||
}
|
||||
for (found, expected) in versioned_hashes
|
||||
.iter()
|
||||
.zip(expected_versioned_hashes.iter())
|
||||
{
|
||||
if found != expected {
|
||||
return Err(Error::VersionHashMismatch {
|
||||
expected: *expected,
|
||||
found: *found,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extract_versioned_hashes_from_transactions<E: EthSpec>(
|
||||
transactions: &types::Transactions<E>,
|
||||
) -> Result<Vec<VersionedHash>, Error> {
|
||||
let mut versioned_hashes = Vec::new();
|
||||
|
||||
for tx in transactions {
|
||||
match beacon_tx_to_tx_envelope(tx)? {
|
||||
TxEnvelope::Eip4844(signed_tx_eip4844) => {
|
||||
versioned_hashes.extend(
|
||||
signed_tx_eip4844
|
||||
.tx()
|
||||
.blob_versioned_hashes
|
||||
.iter()
|
||||
.map(|fb| Hash256::from(fb.0)),
|
||||
);
|
||||
}
|
||||
// enumerating all variants explicitly to make pattern irrefutable
|
||||
// in case new types are added in the future which also have blobs
|
||||
TxEnvelope::Legacy(_)
|
||||
| TxEnvelope::TaggedLegacy(_)
|
||||
| TxEnvelope::Eip2930(_)
|
||||
| TxEnvelope::Eip1559(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(versioned_hashes)
|
||||
}
|
||||
|
||||
pub fn beacon_tx_to_tx_envelope<N: Unsigned>(
|
||||
tx: &types::Transaction<N>,
|
||||
) -> Result<TxEnvelope, Error> {
|
||||
let tx_bytes = Vec::from(tx.clone());
|
||||
TxEnvelope::decode(&mut tx_bytes.as_slice())
|
||||
.map_err(|e| Error::DecodingTransaction(e.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::test_utils::static_valid_tx;
|
||||
use alloy_consensus::{TxKind, TxLegacy};
|
||||
|
||||
type E = types::MainnetEthSpec;
|
||||
|
||||
#[test]
|
||||
fn test_decode_static_transaction() {
|
||||
let valid_tx = static_valid_tx::<E>().expect("should give me known valid transaction");
|
||||
let tx_envelope = beacon_tx_to_tx_envelope(&valid_tx).expect("should decode tx");
|
||||
let TxEnvelope::Legacy(signed_tx) = tx_envelope else {
|
||||
panic!("should decode to legacy transaction");
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
signed_tx.tx(),
|
||||
TxLegacy {
|
||||
chain_id: Some(0x01),
|
||||
nonce: 0x15,
|
||||
gas_price: 0x4a817c800,
|
||||
to: TxKind::Call(..),
|
||||
..
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_versioned_hashes() {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct TestTransactions<E: EthSpec>(
|
||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] types::Transactions<E>,
|
||||
);
|
||||
|
||||
let TestTransactions(raw_transactions): TestTransactions<E> = serde_json::from_str(r#"[
|
||||
"0x03f901388501a1f0ff430f843b9aca00843b9aca0082520894e7249813d8ccf6fa95a2203f46a64166073d58878080c002f8c6a0012e98362c814f1724262c0d211a1463418a5f6382a8d457b37a2698afbe7b5ea00100ef985761395dfa8ed5ce91f3f2180b612401909e4cb8f33b90c8a454d9baa0013d45411623b90d90f916e4025ada74b453dd4ca093c017c838367c9de0f801a001753e2af0b1e70e7ef80541355b2a035cc9b2c177418bb2a4402a9b346cf84da0011789b520a8068094a92aa0b04db8d8ef1c6c9818947c5210821732b8744049a0011c4c4f95597305daa5f62bf5f690e37fa11f5de05a95d05cac4e2119e394db80a0ccd86a742af0e042d08cbb35d910ddc24bbc6538f9e53be6620d4b6e1bb77662a01a8bacbc614940ac2f5c23ffc00a122c9f085046883de65c88ab0edb859acb99",
|
||||
"0x02f9017a8501a1f0ff4382363485012a05f2008512a05f2000830249f094c1b0bc605e2c808aa0867bfc98e51a1fe3e9867f80b901040cc7326300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000009445a285baa43e00000000000000000000000000c500931f24edb821cef6e28f7adb33b38578c82000000000000000000000000fc7360b3b28cf4204268a8354dbec60720d155d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000009a054a063f0fe7b9c68de8df91aaa5e96c15ab540000000000000000000000000c8d41b8fcc066cdabaf074d78e5153e8ce018a9c080a008e14475c1173cd9f5740c24c08b793f9e16c36c08fa73769db95050e31e3396a019767dcdda26c4a774ca28c9df15d0c20e43bd07bd33ee0f84d6096cb5a1ebed"
|
||||
]"#).expect("should get raw transactions");
|
||||
let expected_versioned_hashes = vec![
|
||||
"0x012e98362c814f1724262c0d211a1463418a5f6382a8d457b37a2698afbe7b5e",
|
||||
"0x0100ef985761395dfa8ed5ce91f3f2180b612401909e4cb8f33b90c8a454d9ba",
|
||||
"0x013d45411623b90d90f916e4025ada74b453dd4ca093c017c838367c9de0f801",
|
||||
"0x01753e2af0b1e70e7ef80541355b2a035cc9b2c177418bb2a4402a9b346cf84d",
|
||||
"0x011789b520a8068094a92aa0b04db8d8ef1c6c9818947c5210821732b8744049",
|
||||
"0x011c4c4f95597305daa5f62bf5f690e37fa11f5de05a95d05cac4e2119e394db",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|tx| Hash256::from_slice(&hex::decode(&tx[2..]).expect("should decode hex")))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let versioned_hashes = extract_versioned_hashes_from_transactions::<E>(&raw_transactions)
|
||||
.expect("should get versioned hashes");
|
||||
assert_eq!(versioned_hashes, expected_versioned_hashes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user