Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions derivation/src/derivation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl Derivation {
chain_id: u64,
rollup_config: &RollupConfig,
oracle: MemoryOracleClient,
) -> Result<Header, Error> {
) -> Result<(Header, u64), Error> {
kona_proof::block_on(self.run(chain_id, rollup_config, oracle))
}

Expand All @@ -62,9 +62,8 @@ impl Derivation {
chain_id: u64,
rollup_config: &RollupConfig,
oracle: MemoryOracleClient,
) -> Result<Header, Error> {
) -> Result<(Header, u64), Error> {
verify_config(rollup_config, &oracle).await?;

let boot = &BootInfo {
l1_head: self.l1_head_hash,
agreed_l2_output_root: self.agreed_l2_output_root,
Expand Down Expand Up @@ -135,8 +134,9 @@ impl Derivation {
}

let read = driver.cursor.read();
let l1_origin_number = read.l2_safe_head().l1_origin.number;
let header = read.l2_safe_head_header().clone().unseal();
Ok(header)
Ok((header, l1_origin_number))
}
}

Expand Down
1 change: 1 addition & 0 deletions light-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ethereum-light-client-verifier = { workspace = true }

# Kona
kona-genesis = { workspace = true}
kona-protocol = { workspace = true}

# Alloy
alloy-consensus = { workspace = true}
Expand Down
419 changes: 401 additions & 18 deletions light-client/src/client.rs

Large diffs are not rendered by default.

78 changes: 75 additions & 3 deletions light-client/src/client_state.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::consensus_state::ConsensusState;
use crate::errors::Error;
use crate::header::Header;
use crate::l1::L1Config;
use crate::l1::{L1Config, L1Consensus};
use crate::misbehaviour::{FaultDisputeGameConfig, Misbehaviour, Verifier};
use crate::misc::{
new_timestamp, validate_header_timestamp_not_future,
validate_state_timestamp_within_trusting_period,
Expand All @@ -15,7 +16,7 @@ use ethereum_consensus::types::{Address, H256, U64};
use ethereum_light_client_verifier::context::Fraction;
use ethereum_light_client_verifier::execution::ExecutionVerifier;
use kona_genesis::RollupConfig;
use light_client::types::{Any, Height, Time};
use light_client::types::{Any, ClientId, Height, Time};
use optimism_ibc_proto::google::protobuf::Any as IBCAny;
use optimism_ibc_proto::ibc::lightclients::ethereum::v1::{
Fork as ProtoFork, ForkParameters as ProtoForkParameters, ForkSpec as ProtoForkSpec,
Expand Down Expand Up @@ -44,6 +45,9 @@ pub struct ClientState {

/// L1 Config
pub l1_config: L1Config,

/// Fault Dispute Game Config
pub fault_dispute_game_config: FaultDisputeGameConfig,
}

impl ClientState {
Expand Down Expand Up @@ -74,7 +78,7 @@ impl ClientState {
)?;

// Ensure L2 header is valid
let (l2_header, l2_output_root) = header.verify_l2(
let (l2_header, l1_origin, l2_output_root) = header.verify_l2(
self.chain_id,
trusted_consensus_state.output_root,
&self.rollup_config,
Expand Down Expand Up @@ -113,10 +117,73 @@ impl ClientState {
l1_current_sync_committee: l1_consensus.current_sync_committee,
l1_next_sync_committee: l1_consensus.next_sync_committee,
l1_timestamp: l1_consensus.timestamp,
l1_origin,
};

Ok((new_client_state, new_consensus_state, header_height))
}
pub fn check_misbehaviour_and_update_state<const L1_SYNC_COMMITTEE_SIZE: usize>(
&self,
now: Time,
client_id: &ClientId,
trusted_consensus_state: &ConsensusState,
misbehaviour: Misbehaviour<L1_SYNC_COMMITTEE_SIZE>,
) -> Result<ClientState, Error> {
if self.frozen {
return Err(Error::ClientFrozen(client_id.clone()));
}

let misbehaviour_client_id = misbehaviour.client_id();
if misbehaviour_client_id != client_id {
return Err(Error::UnexpectedClientIdInMisbehaviour(
client_id.clone(),
misbehaviour_client_id.clone(),
));
}

let l1_cons_state = L1Consensus {
slot: trusted_consensus_state.l1_slot,
current_sync_committee: trusted_consensus_state.l1_current_sync_committee.clone(),
next_sync_committee: trusted_consensus_state.l1_next_sync_committee.clone(),
timestamp: trusted_consensus_state.l1_timestamp,
};

validate_state_timestamp_within_trusting_period(
now,
self.l1_config.trusting_period,
trusted_consensus_state.l1_timestamp,
)?;

match &misbehaviour {
Misbehaviour::L1(l1) => l1.verify(
now.as_unix_timestamp_secs(),
&self.l1_config,
&l1_cons_state,
),
Misbehaviour::L2(l2) => match l2.verifier() {
Verifier::Future(v) => v.verify(
now.as_unix_timestamp_secs(),
&self.l1_config,
&self.fault_dispute_game_config,
&l1_cons_state,
misbehaviour.trusted_height().revision_height(),
trusted_consensus_state.l1_origin,
),
Verifier::Past(v) => v.verify(
now.as_unix_timestamp_secs(),
&self.l1_config,
&self.fault_dispute_game_config,
&l1_cons_state,
trusted_consensus_state.output_root,
),
},
}?;

Ok(Self {
frozen: true,
..self.clone()
})
}

pub fn verify_membership(
&self,
Expand Down Expand Up @@ -292,6 +359,9 @@ impl TryFrom<RawClientState> for ClientState {

let l1_config = value.l1_config.ok_or(Error::MissingL1Config)?;
let l1_config = L1Config::try_from(l1_config)?;
let fault_dispute_game_config = value
.fault_dispute_game_config
.ok_or(Error::MissingFaultDisputeGameConfig)?;

Ok(Self {
chain_id: value.chain_id,
Expand All @@ -301,6 +371,7 @@ impl TryFrom<RawClientState> for ClientState {
frozen,
rollup_config,
l1_config,
fault_dispute_game_config: fault_dispute_game_config.try_into()?,
})
}
}
Expand All @@ -321,6 +392,7 @@ impl TryFrom<ClientState> for RawClientState {
rollup_config_json: serde_json::to_vec(&value.rollup_config)
.map_err(Error::UnexpectedRollupConfig)?,
l1_config: Some(value.l1_config.into()),
fault_dispute_game_config: Some(value.fault_dispute_game_config.into()),
})
}
}
Expand Down
5 changes: 5 additions & 0 deletions light-client/src/consensus_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ pub struct ConsensusState {
pub l1_next_sync_committee: PublicKey,
/// finalized header's timestamp
pub l1_timestamp: Time,

/// L1 origin block number for output_root
pub l1_origin: u64,
}

impl ConsensusState {
Expand Down Expand Up @@ -66,6 +69,7 @@ impl TryFrom<RawConsensusState> for ConsensusState {
.map_err(Error::L1ConsensusError)?,
l1_next_sync_committee: PublicKey::try_from(value.l1_next_sync_committee)
.map_err(Error::L1ConsensusError)?,
l1_origin: value.l1_origin,
})
}
}
Expand All @@ -81,6 +85,7 @@ impl From<ConsensusState> for RawConsensusState {
l1_current_sync_committee: value.l1_current_sync_committee.to_vec(),
l1_next_sync_committee: value.l1_next_sync_committee.to_vec(),
l1_timestamp: value.l1_timestamp.as_unix_timestamp_secs(),
l1_origin: value.l1_origin,
}
}
}
Expand Down
77 changes: 75 additions & 2 deletions light-client/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::l1::L1Consensus;
use crate::misbehaviour::FaultDisputeGameProof;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use alloy_primitives::private::alloy_rlp;
use alloy_primitives::B256;
use core::array::TryFromSliceError;
use ethereum_consensus::bls::PublicKey;
use ethereum_consensus::errors::{Error as L1ConsensusError, MerkleError};
use ethereum_consensus::sync_protocol::SyncCommitteePeriod;
use ethereum_consensus::types::H256;
use ethereum_consensus::types::{Address, H256};
use ethereum_light_client_verifier::errors::Error as L1VerifyError;
use light_client::types::{ClientId, Height, Time, TimeError};
use light_client::types::{ClientId, Height, Time, TimeError, TypeError};
use optimism_derivation::derivation::Derivation;

#[derive(thiserror::Error, Debug)]
Expand Down Expand Up @@ -73,6 +75,8 @@ pub enum Error {
// Update
#[error("MissingL1Config")]
MissingL1Config,
#[error("MissingFaultDisputeGameConfig")]
MissingFaultDisputeGameConfig,
#[error("MissingForkSpec")]
MissingForkSpec,
#[error("MissingL1Head")]
Expand Down Expand Up @@ -153,6 +157,75 @@ pub enum Error {
#[error("SyncCommitteeValidateError: err={0:?}")]
SyncCommitteeValidateError(L1ConsensusError),

// Misbehaviour
#[error("NoHeaderFound")]
NoHeaderFound,
#[error("MissingL2History")]
MissingL2History,
#[error("UnexpectedResolvedL2Number: expected={0} actual={1}")]
UnexpectedResolvedL2Number(u64, u64),
#[error("UnexpectedHeaderRelation: expected_parent_hash={expected_parent_hash:?} actual_parent_hash={actual_parent_hash:?} header_number={header_number} parent_number={parent_number}")]
UnexpectedHeaderRelation {
expected_parent_hash: B256,
actual_parent_hash: B256,
header_number: u64,
parent_number: u64,
},
#[error("UnexpectedHeaderRLPError err={0:?}")]
UnexpectedHeaderRLPError(alloy_rlp::Error),
#[error("UnexpectedDisputeGameFactoryProxyProof: proof={proof:?} output_root={output_root:?} l2_block_number={l2_block_number} err={err:?}")]
UnexpectedDisputeGameFactoryProxyProof {
proof: FaultDisputeGameProof,
output_root: B256,
l2_block_number: u64,
err: Option<L1VerifyError>,
},
#[error("UnexpectedFaultDisputeGameProof: proof={proof:?} address={address:?} err={err:?}")]
UnexpectedFaultDisputeGameProof {
proof: FaultDisputeGameProof,
address: Address,
err: Option<L1VerifyError>,
},
#[error("UnexpectedGameID: game_id={0:?}")]
UnexpectedGameID(Vec<u8>),
#[error("UnexpectedResolvedStatus: proof={proof:?} status={status} address={address:?} packing_slot_value={packing_slot_value:?}")]
UnexpectedResolvedStatus {
proof: FaultDisputeGameProof,
status: u8,
address: Address,
packing_slot_value: [u8; 32],
},
#[error("L1VerifyMisbehaviourError: err={0:?}")]
L1VerifyMisbehaviourError(L1VerifyError),
#[error("UnknownMisbehaviourType: type={0:?}")]
UnknownMisbehaviourType(String),
#[error("UnexpectedDisputeGameFactoryAddress: err={0:?}")]
UnexpectedDisputeGameFactoryAddress(L1ConsensusError),
#[error("UnexpectedClientId: err={0:?}")]
UnexpectedClientId(TypeError),
#[error("UnexpectedClientIdInMisbehaviour: request={0:?} misbehaviour={1:?}")]
UnexpectedClientIdInMisbehaviour(ClientId, ClientId),
#[error("UnexpectedMisbehaviourOutput: resolved_output_root={0:?}")]
UnexpectedMisbehaviourOutput(B256),
#[error("UnexpectedMisbehaviourHeight: trusted={0} requested={1}")]
UnexpectedMisbehaviourHeight(u64, u64),
#[error("UnexpectedPastL1Header: trusted_l1_origin={0} requested={1}")]
UnexpectedPastL1Header(u64, u64),
#[error("UnexpectedSealedL1Number: expected={0} actual={1}")]
UnexpectedL1HeaderNumber(u64, u64),
#[error("UnexpectedL1HeaderStateRoot: expected={0:?} actual={1:?}")]
UnexpectedL1HeaderStateRoot(B256, B256),
#[error("UnexpectedSubmittedL1HeaderStateRoot: expected={0:?} actual={1:?}")]
UnexpectedSubmittedL1HeaderStateRoot(B256, B256),
#[error("UnexpectedGameExists: game_id={0:?}")]
UnexpectedGameExists(Vec<u8>),
#[error("UnexpectedStateRoot: state_root={0:?}")]
UnexpectedStateRoot(Vec<u8>),
#[error("UnexpectedGameCreatedAt: created_at={0} l1_timestamp={1}")]
UnexpectedGameCreatedAt(u64, u64),
#[error("UnexpectedCreatedAt: data={0}")]
UnexpectedCreatedAt(TryFromSliceError),

// Framework
#[error("LCPError: err={0:?}")]
LCPError(light_client::Error),
Expand Down
6 changes: 3 additions & 3 deletions light-client/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl<const L1_SYNC_COMMITTEE_SIZE: usize> Header<L1_SYNC_COMMITTEE_SIZE> {
chain_id: u64,
trusted_output_root: B256,
rollup_config: &RollupConfig,
) -> Result<(alloy_consensus::Header, B256), Error> {
) -> Result<(alloy_consensus::Header, u64, B256), Error> {
// Ensure trusted
if self.derivation.agreed_l2_output_root != trusted_output_root {
return Err(Error::UnexpectedTrustedOutputRoot(
Expand All @@ -119,11 +119,11 @@ impl<const L1_SYNC_COMMITTEE_SIZE: usize> Header<L1_SYNC_COMMITTEE_SIZE> {
}

// Ensure honest derivation
let header = self
let result = self
.derivation
.verify(chain_id, rollup_config, self.oracle.clone())
.map_err(|e| Error::DerivationError(self.derivation.clone(), self.oracle.len(), e))?;
Ok((header, self.derivation.l2_output_root))
Ok((result.0, result.1, self.derivation.l2_output_root))
}
}

Expand Down
Loading
Loading