Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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.

7 changes: 4 additions & 3 deletions derivation/src/derivation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,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 @@ -60,7 +60,7 @@ impl Derivation {
chain_id: u64,
rollup_config: &RollupConfig,
oracle: MemoryOracleClient,
) -> Result<Header, Error> {
) -> Result<(Header, u64), Error> {
let boot = &BootInfo {
l1_head: self.l1_head_hash,
agreed_l2_output_root: self.agreed_l2_output_root,
Expand Down Expand Up @@ -131,7 +131,8 @@ 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))
}
}
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
416 changes: 400 additions & 16 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 @@ -69,6 +71,8 @@ pub enum Error {
// Update
#[error("MissingL1Config")]
MissingL1Config,
#[error("MissingFaultDisputeGameConfig")]
MissingFaultDisputeGameConfig,
#[error("MissingForkSpec")]
MissingForkSpec,
#[error("MissingL1Head")]
Expand Down Expand Up @@ -147,6 +151,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