From e5751213f1a0c41b7c6801d5c46ea3b078bfab4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9COgnyan?= Date: Mon, 22 Apr 2024 12:52:43 +0300 Subject: [PATCH] feat: add and refactor post-merge proofs --- .../src/types/consensus/beacon_state.rs | 2 +- ...eader_proof.rs => historical_summaries.rs} | 33 +-- ethportal-api/src/types/consensus/mod.rs | 2 +- .../src/types/content_value/beacon.rs | 2 +- .../src/types/content_value/history.rs | 7 +- ethportal-api/src/types/execution/header.rs | 170 +------------- .../src/types/execution/header_with_proof.rs | 213 ++++++++++++++++++ ethportal-api/src/types/execution/mod.rs | 1 + portal-bridge/src/api/execution.rs | 2 +- tests/rpc_server.rs | 2 +- trin-history/src/validation.rs | 6 +- trin-validation/src/accumulator.rs | 7 +- trin-validation/src/oracle.rs | 2 +- 13 files changed, 239 insertions(+), 210 deletions(-) rename ethportal-api/src/types/consensus/{header_proof.rs => historical_summaries.rs} (67%) create mode 100644 ethportal-api/src/types/execution/header_with_proof.rs diff --git a/ethportal-api/src/types/consensus/beacon_state.rs b/ethportal-api/src/types/consensus/beacon_state.rs index d9c60affb..f0404f882 100644 --- a/ethportal-api/src/types/consensus/beacon_state.rs +++ b/ethportal-api/src/types/consensus/beacon_state.rs @@ -5,7 +5,7 @@ use crate::consensus::{ }, fork::ForkName, header::BeaconBlockHeader, - header_proof::HistoricalSummary, + historical_summaries::HistoricalSummary, participation_flags::ParticipationFlags, pubkey::PubKey, sync_committee::SyncCommittee, diff --git a/ethportal-api/src/types/consensus/header_proof.rs b/ethportal-api/src/types/consensus/historical_summaries.rs similarity index 67% rename from ethportal-api/src/types/consensus/header_proof.rs rename to ethportal-api/src/types/consensus/historical_summaries.rs index 0a95655a5..291000ced 100644 --- a/ethportal-api/src/types/consensus/header_proof.rs +++ b/ethportal-api/src/types/consensus/historical_summaries.rs @@ -5,29 +5,6 @@ use ssz_derive::{Decode, Encode}; use ssz_types::{typenum, VariableList}; use tree_hash_derive::TreeHash; -/// Types sourced from Fluffy: -/// https://github.com/status-im/nimbus-eth1/blob/77135e70015de77d9ca46b196d99dc260ed3e364/fluffy/network/history/experimental/beacon_chain_block_proof.nim - -//BeaconBlockBodyProof* = array[8, Digest] -pub type BeaconBlockBodyProof = [B256; 8]; - -//BeaconBlockHeaderProof* = array[3, Digest] -pub type BeaconBlockHeaderProof = [B256; 3]; - -//HistoricalRootsProof* = array[14, Digest] -pub type BeaconBlockHistoricalRootsProof = [B256; 14]; - -//# Total size (8 + 1 + 3 + 1 + 14) * 32 bytes + 4 bytes = 868 bytes -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct BeaconChainBlockProof { - pub beacon_block_body_proof: BeaconBlockBodyProof, - pub beacon_block_body_root: B256, - pub beacon_block_header_proof: BeaconBlockHeaderProof, - pub beacon_block_header_root: B256, - pub historical_roots_proof: BeaconBlockHistoricalRootsProof, - pub slot: u64, -} - /// `HistoricalSummary` matches the components of the phase0 `HistoricalBatch` /// making the two hash_tree_root-compatible. This struct is introduced into the beacon state /// in the Capella hard fork. @@ -43,11 +20,11 @@ type HistoricalSummaries = VariableList; /// Proof against the beacon state root hash #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct HistoricalSummariesProof { +pub struct HistoricalSummariesStateProof { pub proof: [B256; 5], } -impl Default for HistoricalSummariesProof { +impl Default for HistoricalSummariesStateProof { fn default() -> Self { Self { proof: [B256::ZERO; 5], @@ -55,7 +32,7 @@ impl Default for HistoricalSummariesProof { } } -impl ssz::Decode for HistoricalSummariesProof { +impl ssz::Decode for HistoricalSummariesStateProof { fn is_ssz_fixed_len() -> bool { true } @@ -75,7 +52,7 @@ impl ssz::Decode for HistoricalSummariesProof { } } -impl ssz::Encode for HistoricalSummariesProof { +impl ssz::Encode for HistoricalSummariesStateProof { fn is_ssz_fixed_len() -> bool { true } @@ -104,7 +81,7 @@ impl ssz::Encode for HistoricalSummariesProof { pub struct HistoricalSummariesWithProof { pub epoch: u64, pub historical_summaries: HistoricalSummaries, - pub proof: HistoricalSummariesProof, + pub proof: HistoricalSummariesStateProof, } // TODO: Add test vectors for HistoricalSummariesWithProof diff --git a/ethportal-api/src/types/consensus/mod.rs b/ethportal-api/src/types/consensus/mod.rs index 6ff303f2e..acaa200fb 100644 --- a/ethportal-api/src/types/consensus/mod.rs +++ b/ethportal-api/src/types/consensus/mod.rs @@ -4,7 +4,7 @@ pub mod body; pub mod execution_payload; pub mod fork; pub mod header; -pub mod header_proof; +pub mod historical_summaries; pub mod kzg_commitment; pub mod light_client; pub mod participation_flags; diff --git a/ethportal-api/src/types/content_value/beacon.rs b/ethportal-api/src/types/content_value/beacon.rs index 17fc0d60a..135e489d7 100644 --- a/ethportal-api/src/types/content_value/beacon.rs +++ b/ethportal-api/src/types/content_value/beacon.rs @@ -6,7 +6,7 @@ use crate::{ types::{ consensus::{ fork::{ForkDigest, ForkName}, - header_proof::HistoricalSummariesWithProof, + historical_summaries::HistoricalSummariesWithProof, light_client::{ bootstrap::{ LightClientBootstrap, LightClientBootstrapBellatrix, diff --git a/ethportal-api/src/types/content_value/history.rs b/ethportal-api/src/types/content_value/history.rs index 574424cdb..5faa52347 100644 --- a/ethportal-api/src/types/content_value/history.rs +++ b/ethportal-api/src/types/content_value/history.rs @@ -1,7 +1,10 @@ use crate::{ - types::{content_value::ContentValue, execution::accumulator::EpochAccumulator}, + types::{ + content_value::ContentValue, + execution::{accumulator::EpochAccumulator, header_with_proof::HeaderWithProof}, + }, utils::bytes::hex_encode, - BlockBody, ContentValueError, HeaderWithProof, Receipts, + BlockBody, ContentValueError, Receipts, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use ssz::{Decode, Encode}; diff --git a/ethportal-api/src/types/execution/header.rs b/ethportal-api/src/types/execution/header.rs index 525f52e2d..1bc6599b4 100644 --- a/ethportal-api/src/types/execution/header.rs +++ b/ethportal-api/src/types/execution/header.rs @@ -2,13 +2,8 @@ use alloy_primitives::{keccak256, Address, Bloom, Bytes, B256, B64, U256, U64}; use alloy_rlp::{Decodable, Encodable, Header as RlpHeader}; use reth_rpc_types::Header as RpcHeader; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use ssz::{Encode, SszDecoderBuilder, SszEncoder}; -use ssz_derive::{Decode, Encode}; -use crate::{ - types::bytes::ByteList2048, - utils::bytes::{hex_decode, hex_encode}, -}; +use crate::utils::bytes::{hex_decode, hex_encode}; /// A block header. #[derive(Debug, Clone, Eq, Deserialize, Serialize)] @@ -307,156 +302,13 @@ impl<'de> Deserialize<'de> for TxHashes { } } -/// A block header with accumulator proof. -/// Type definition: -/// https://github.com/status-im/nimbus-eth1/blob/master/fluffy/network/history/history_content.nim#L136 -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -pub struct HeaderWithProof { - pub header: Header, - pub proof: BlockHeaderProof, -} - -impl ssz::Encode for HeaderWithProof { - fn is_ssz_fixed_len() -> bool { - false - } - - fn ssz_append(&self, buf: &mut Vec) { - let header = alloy_rlp::encode(&self.header); - let header = ByteList2048::from(header); - let offset = ::ssz_fixed_len() - + ::ssz_fixed_len(); - let mut encoder = SszEncoder::container(buf, offset); - encoder.append(&header); - encoder.append(&self.proof); - encoder.finalize(); - } - - fn ssz_bytes_len(&self) -> usize { - let header = alloy_rlp::encode(&self.header); - let header = ByteList2048::from(header); - header.len() + self.proof.ssz_bytes_len() - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize)] -#[ssz(enum_behaviour = "union")] -// Ignore clippy here, since "box"-ing the accumulator proof breaks the Decode trait -#[allow(clippy::large_enum_variant)] -pub enum BlockHeaderProof { - None(SszNone), - AccumulatorProof(AccumulatorProof), -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AccumulatorProof { - pub proof: [B256; 15], -} - -impl ssz::Decode for HeaderWithProof { - fn is_ssz_fixed_len() -> bool { - false - } - - fn from_ssz_bytes(bytes: &[u8]) -> Result { - let mut builder = SszDecoderBuilder::new(bytes); - - builder.register_type::()?; - builder.register_type::()?; - - let mut decoder = builder.build()?; - - let header_rlp: Vec = decoder.decode_next()?; - let proof = decoder.decode_next()?; - let header: Header = Decodable::decode(&mut header_rlp.as_slice()).map_err(|_| { - ssz::DecodeError::BytesInvalid("Unable to decode bytes into header.".to_string()) - })?; - - Ok(Self { header, proof }) - } -} - -impl ssz::Decode for AccumulatorProof { - fn is_ssz_fixed_len() -> bool { - true - } - - fn from_ssz_bytes(bytes: &[u8]) -> Result { - let vec: Vec<[u8; 32]> = Vec::from_ssz_bytes(bytes)?; - let mut proof: [B256; 15] = [B256::ZERO; 15]; - let raw_proof: [[u8; 32]; 15] = vec - .try_into() - .map_err(|_| ssz::DecodeError::BytesInvalid("Invalid proof length".to_string()))?; - for (idx, val) in raw_proof.iter().enumerate() { - proof[idx] = B256::from_slice(val); - } - Ok(Self { proof }) - } -} - -impl ssz::Encode for AccumulatorProof { - fn is_ssz_fixed_len() -> bool { - true - } - - fn ssz_append(&self, buf: &mut Vec) { - let offset = self.ssz_bytes_len(); - let mut encoder = SszEncoder::container(buf, offset); - - for proof in self.proof { - encoder.append(&proof); - } - encoder.finalize(); - } - - fn ssz_bytes_len(&self) -> usize { - ::ssz_fixed_len() * 15 - } -} - -/// Struct to represent encodable/decodable None value for an SSZ enum -#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize)] -pub struct SszNone { - // In rust, None is a variant not a type, - // so we must use Option here to represent a None value - pub value: Option<()>, -} - -impl ssz::Decode for SszNone { - fn is_ssz_fixed_len() -> bool { - true - } - - fn from_ssz_bytes(bytes: &[u8]) -> Result { - match bytes.len() { - 0 => Ok(Self { value: None }), - _ => Err(ssz::DecodeError::BytesInvalid( - "Expected None value to be empty, found bytes.".to_string(), - )), - } - } -} - -impl ssz::Encode for SszNone { - fn is_ssz_fixed_len() -> bool { - true - } - - fn ssz_append(&self, _buf: &mut Vec) {} - - fn ssz_bytes_len(&self) -> usize { - 0 - } -} - #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { use super::*; - use std::{fs, str::FromStr}; + use std::str::FromStr; use serde_json::{json, Value}; - use ssz::Decode; #[test_log::test] fn decode_and_encode_header() { @@ -538,24 +390,6 @@ mod tests { assert_eq!(header.base_fee_per_gas, None); } - #[test_log::test] - fn decode_encode_header_with_proofs() { - let file = - fs::read_to_string("../trin-validation/src/assets/fluffy/header_with_proofs.json") - .unwrap(); - let json: Value = serde_json::from_str(&file).unwrap(); - let hwps = json.as_object().unwrap(); - for (block_number, obj) in hwps { - let _content_key = obj.get("content_key").unwrap(); - let block_number: u64 = block_number.parse().unwrap(); - let proof = obj.get("value").unwrap().as_str().unwrap(); - let hwp = HeaderWithProof::from_ssz_bytes(&hex_decode(proof).unwrap()).unwrap(); - assert_eq!(block_number, hwp.header.number); - let encoded = hex_encode(hwp.as_ssz_bytes()); - assert_eq!(encoded, proof); - } - } - #[test_log::test] fn post_shanghai_header() { let body = diff --git a/ethportal-api/src/types/execution/header_with_proof.rs b/ethportal-api/src/types/execution/header_with_proof.rs new file mode 100644 index 000000000..c6e2282cf --- /dev/null +++ b/ethportal-api/src/types/execution/header_with_proof.rs @@ -0,0 +1,213 @@ +use crate::{types::bytes::ByteList2048, Header}; +use alloy_primitives::B256; +use alloy_rlp::Decodable; +use jsonrpsee::core::Serialize; +use serde::Deserialize; +use ssz::{Encode, SszDecoderBuilder, SszEncoder}; +use ssz_derive::{Decode, Encode}; + +/// A block header with accumulator proof. +/// Type definition: +/// https://github.com/status-im/nimbus-eth1/blob/master/fluffy/network/history/history_content.nim#L136 +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +pub struct HeaderWithProof { + pub header: Header, + pub proof: BlockHeaderProof, +} + +impl ssz::Encode for HeaderWithProof { + fn is_ssz_fixed_len() -> bool { + false + } + + fn ssz_append(&self, buf: &mut Vec) { + let header = alloy_rlp::encode(&self.header); + let header = ByteList2048::from(header); + let offset = ::ssz_fixed_len() + + ::ssz_fixed_len(); + let mut encoder = SszEncoder::container(buf, offset); + encoder.append(&header); + encoder.append(&self.proof); + encoder.finalize(); + } + + fn ssz_bytes_len(&self) -> usize { + let header = alloy_rlp::encode(&self.header); + let header = ByteList2048::from(header); + header.len() + self.proof.ssz_bytes_len() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize)] +#[ssz(enum_behaviour = "union")] +// Ignore clippy here, since "box"-ing the accumulator proof breaks the Decode trait +#[allow(clippy::large_enum_variant)] +pub enum BlockHeaderProof { + None(SszNone), + AccumulatorProof(AccumulatorProof), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AccumulatorProof { + pub proof: [B256; 15], +} + +impl ssz::Decode for HeaderWithProof { + fn is_ssz_fixed_len() -> bool { + false + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + let mut builder = SszDecoderBuilder::new(bytes); + + builder.register_type::()?; + builder.register_type::()?; + + let mut decoder = builder.build()?; + + let header_rlp: Vec = decoder.decode_next()?; + let proof = decoder.decode_next()?; + let header: Header = Decodable::decode(&mut header_rlp.as_slice()).map_err(|_| { + ssz::DecodeError::BytesInvalid("Unable to decode bytes into header.".to_string()) + })?; + + Ok(Self { header, proof }) + } +} + +impl ssz::Decode for AccumulatorProof { + fn is_ssz_fixed_len() -> bool { + true + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + let vec: Vec<[u8; 32]> = Vec::from_ssz_bytes(bytes)?; + let mut proof: [B256; 15] = [B256::ZERO; 15]; + let raw_proof: [[u8; 32]; 15] = vec + .try_into() + .map_err(|_| ssz::DecodeError::BytesInvalid("Invalid proof length".to_string()))?; + for (idx, val) in raw_proof.iter().enumerate() { + proof[idx] = B256::from_slice(val); + } + Ok(Self { proof }) + } +} + +impl ssz::Encode for AccumulatorProof { + fn is_ssz_fixed_len() -> bool { + true + } + + fn ssz_append(&self, buf: &mut Vec) { + let offset = self.ssz_bytes_len(); + let mut encoder = SszEncoder::container(buf, offset); + + for proof in self.proof { + encoder.append(&proof); + } + encoder.finalize(); + } + + fn ssz_bytes_len(&self) -> usize { + ::ssz_fixed_len() * 15 + } +} + +/// Proof that execution header root is part of BeaconBlockBody +pub type BeaconBlockBodyProof = [B256; 8]; +/// Proof that BeaconBlockBody root is part of BeaconBlockHeader +pub type BeaconBlockHeaderProof = [B256; 3]; +/// Proof that BeaconBlockHeader root is part of HistoricalRoots +pub type HistoricalRootsProof = [B256; 14]; +/// Proof that BeaconBlockHeader root is part of HistoricalSummaries +pub type HistoricalSummariesProof = [B256; 13]; + +/// The struct holds a chain of proofs. This chain of proofs allows for verifying that an EL +/// `BlockHeader` is part of the canonical chain. The only requirement is having access to the +/// beacon chain `historical_roots`. +// Total size (8 + 1 + 3 + 1 + 14) * 32 bytes + 4 bytes = 868 bytes +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct HistoricalRootsBlockProof { + pub beacon_block_body_proof: BeaconBlockBodyProof, + pub beacon_block_body_root: B256, + pub beacon_block_header_proof: BeaconBlockHeaderProof, + pub beacon_block_header_root: B256, + pub historical_roots_proof: HistoricalRootsProof, + pub slot: u64, +} + +/// The struct holds a chain of proofs. This chain of proofs allows for verifying that an EL +/// `BlockHeader` is part of the canonical chain. The only requirement is having access to the +/// beacon chain `historical_summaries`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct HistoricalSummariesBlockProof { + pub beacon_block_body_proof: BeaconBlockBodyProof, + pub beacon_block_body_root: B256, + pub beacon_block_header_proof: BeaconBlockHeaderProof, + pub beacon_block_header_root: B256, + pub historical_summaries_proof: HistoricalSummariesProof, + pub slot: u64, +} + +/// Struct to represent encodable/decodable None value for an SSZ enum +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize)] +pub struct SszNone { + // In rust, None is a variant not a type, + // so we must use Option here to represent a None value + pub value: Option<()>, +} + +impl ssz::Decode for SszNone { + fn is_ssz_fixed_len() -> bool { + true + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + match bytes.len() { + 0 => Ok(Self { value: None }), + _ => Err(ssz::DecodeError::BytesInvalid( + "Expected None value to be empty, found bytes.".to_string(), + )), + } + } +} + +impl ssz::Encode for SszNone { + fn is_ssz_fixed_len() -> bool { + true + } + + fn ssz_append(&self, _buf: &mut Vec) {} + + fn ssz_bytes_len(&self) -> usize { + 0 + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::*; + use crate::utils::bytes::{hex_decode, hex_encode}; + use serde_json::Value; + use ssz::Decode; + use std::fs; + + #[test_log::test] + fn decode_encode_header_with_proofs() { + let file = + fs::read_to_string("../trin-validation/src/assets/fluffy/header_with_proofs.json") + .unwrap(); + let json: Value = serde_json::from_str(&file).unwrap(); + let hwps = json.as_object().unwrap(); + for (block_number, obj) in hwps { + let _content_key = obj.get("content_key").unwrap(); + let block_number: u64 = block_number.parse().unwrap(); + let proof = obj.get("value").unwrap().as_str().unwrap(); + let hwp = HeaderWithProof::from_ssz_bytes(&hex_decode(proof).unwrap()).unwrap(); + assert_eq!(block_number, hwp.header.number); + let encoded = hex_encode(hwp.as_ssz_bytes()); + assert_eq!(encoded, proof); + } + } +} diff --git a/ethportal-api/src/types/execution/mod.rs b/ethportal-api/src/types/execution/mod.rs index 5cb988a91..7340d9e2e 100644 --- a/ethportal-api/src/types/execution/mod.rs +++ b/ethportal-api/src/types/execution/mod.rs @@ -1,5 +1,6 @@ pub mod accumulator; pub mod block_body; pub mod header; +pub mod header_with_proof; pub mod receipts; pub mod transaction; diff --git a/portal-bridge/src/api/execution.rs b/portal-bridge/src/api/execution.rs index 4945c4c9b..f4893f85a 100644 --- a/portal-bridge/src/api/execution.rs +++ b/portal-bridge/src/api/execution.rs @@ -16,7 +16,7 @@ use ethportal_api::{ BlockBody, BlockBodyLegacy, BlockBodyMerge, BlockBodyShanghai, MERGE_TIMESTAMP, SHANGHAI_TIMESTAMP, }, - header::{AccumulatorProof, BlockHeaderProof, HeaderWithProof, SszNone}, + header_with_proof::{AccumulatorProof, BlockHeaderProof, HeaderWithProof, SszNone}, }, jsonrpc::{params::Params, request::JsonRequest}, }, diff --git a/tests/rpc_server.rs b/tests/rpc_server.rs index 63b179969..04fdb48cf 100644 --- a/tests/rpc_server.rs +++ b/tests/rpc_server.rs @@ -14,7 +14,7 @@ use ssz::Decode; use ethportal_api::{ types::{ cli::{TrinConfig, DEFAULT_WEB3_IPC_PATH}, - execution::{block_body::BlockBody, header::HeaderWithProof}, + execution::{block_body::BlockBody, header_with_proof::HeaderWithProof}, }, utils::bytes::{hex_decode, hex_encode}, HistoryContentKey, HistoryContentValue, HistoryNetworkApiClient, diff --git a/trin-history/src/validation.rs b/trin-history/src/validation.rs index b62e896b2..111b265b1 100644 --- a/trin-history/src/validation.rs +++ b/trin-history/src/validation.rs @@ -8,10 +8,8 @@ use tree_hash::TreeHash; use ethportal_api::{ types::execution::{ - accumulator::EpochAccumulator, - block_body::BlockBody, - header::{Header, HeaderWithProof}, - receipts::Receipts, + accumulator::EpochAccumulator, block_body::BlockBody, header::Header, + header_with_proof::HeaderWithProof, receipts::Receipts, }, utils::bytes::hex_encode, HistoryContentKey, diff --git a/trin-validation/src/accumulator.rs b/trin-validation/src/accumulator.rs index 27e3745aa..fbd59c7a8 100644 --- a/trin-validation/src/accumulator.rs +++ b/trin-validation/src/accumulator.rs @@ -20,7 +20,8 @@ use ethportal_api::{ types::{ execution::{ accumulator::EpochAccumulator, - header::{BlockHeaderProof, Header, HeaderWithProof}, + header::Header, + header_with_proof::{BlockHeaderProof, HeaderWithProof}, }, jsonrpc::{endpoints::HistoryEndpoint, request::HistoryJsonRpcRequest}, }, @@ -252,7 +253,9 @@ mod test { use crate::constants::DEFAULT_MASTER_ACC_HASH; use ethportal_api::{ - types::execution::header::{AccumulatorProof, BlockHeaderProof, HeaderWithProof, SszNone}, + types::execution::header_with_proof::{ + AccumulatorProof, BlockHeaderProof, HeaderWithProof, SszNone, + }, utils::bytes::hex_encode, }; diff --git a/trin-validation/src/oracle.rs b/trin-validation/src/oracle.rs index 0f7ea051e..2951d06a1 100644 --- a/trin-validation/src/oracle.rs +++ b/trin-validation/src/oracle.rs @@ -6,7 +6,7 @@ use tokio::sync::mpsc; use crate::accumulator::MasterAccumulator; use ethportal_api::{ types::{ - execution::header::HeaderWithProof, + execution::header_with_proof::HeaderWithProof, history::ContentInfo, jsonrpc::{ endpoints::HistoryEndpoint,