Skip to content

Commit f74d130

Browse files
author
Naohiro Yoshida
committed
add misbehaviour / remove todo
Signed-off-by: Naohiro Yoshida <naohiro.yoshida@datachain.jp>
1 parent c751cb1 commit f74d130

22 files changed

+2057
-51
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

derivation/src/derivation.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl Derivation {
4949
chain_id: u64,
5050
rollup_config: &RollupConfig,
5151
oracle: MemoryOracleClient,
52-
) -> Result<Header, Error> {
52+
) -> Result<(Header, u64), Error> {
5353
kona_proof::block_on(self.run(chain_id, rollup_config, oracle))
5454
}
5555

@@ -60,7 +60,7 @@ impl Derivation {
6060
chain_id: u64,
6161
rollup_config: &RollupConfig,
6262
oracle: MemoryOracleClient,
63-
) -> Result<Header, Error> {
63+
) -> Result<(Header, u64), Error> {
6464
let boot = &BootInfo {
6565
l1_head: self.l1_head_hash,
6666
agreed_l2_output_root: self.agreed_l2_output_root,
@@ -131,7 +131,8 @@ impl Derivation {
131131
}
132132

133133
let read = driver.cursor.read();
134+
let l1_origin_number = read.l2_safe_head().l1_origin.number;
134135
let header = read.l2_safe_head_header().clone().unseal();
135-
Ok(header)
136+
Ok((header, l1_origin_number))
136137
}
137138
}

light-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ethereum-light-client-verifier = { workspace = true }
2323

2424
# Kona
2525
kona-genesis = { workspace = true}
26+
kona-protocol = { workspace = true}
2627

2728
# Alloy
2829
alloy-consensus = { workspace = true}

light-client/src/client.rs

Lines changed: 400 additions & 16 deletions
Large diffs are not rendered by default.

light-client/src/client_state.rs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::consensus_state::ConsensusState;
22
use crate::errors::Error;
33
use crate::header::Header;
4-
use crate::l1::L1Config;
4+
use crate::l1::{L1Config, L1Consensus};
5+
use crate::misbehaviour::{FaultDisputeGameConfig, Misbehaviour, Verifier};
56
use crate::misc::{
67
new_timestamp, validate_header_timestamp_not_future,
78
validate_state_timestamp_within_trusting_period,
@@ -15,7 +16,7 @@ use ethereum_consensus::types::{Address, H256, U64};
1516
use ethereum_light_client_verifier::context::Fraction;
1617
use ethereum_light_client_verifier::execution::ExecutionVerifier;
1718
use kona_genesis::RollupConfig;
18-
use light_client::types::{Any, Height, Time};
19+
use light_client::types::{Any, ClientId, Height, Time};
1920
use optimism_ibc_proto::google::protobuf::Any as IBCAny;
2021
use optimism_ibc_proto::ibc::lightclients::ethereum::v1::{
2122
Fork as ProtoFork, ForkParameters as ProtoForkParameters, ForkSpec as ProtoForkSpec,
@@ -44,6 +45,9 @@ pub struct ClientState {
4445

4546
/// L1 Config
4647
pub l1_config: L1Config,
48+
49+
/// Fault Dispute Game Config
50+
pub fault_dispute_game_config: FaultDisputeGameConfig,
4751
}
4852

4953
impl ClientState {
@@ -74,7 +78,7 @@ impl ClientState {
7478
)?;
7579

7680
// Ensure L2 header is valid
77-
let (l2_header, l2_output_root) = header.verify_l2(
81+
let (l2_header, l1_origin, l2_output_root) = header.verify_l2(
7882
self.chain_id,
7983
trusted_consensus_state.output_root,
8084
&self.rollup_config,
@@ -113,10 +117,73 @@ impl ClientState {
113117
l1_current_sync_committee: l1_consensus.current_sync_committee,
114118
l1_next_sync_committee: l1_consensus.next_sync_committee,
115119
l1_timestamp: l1_consensus.timestamp,
120+
l1_origin,
116121
};
117122

118123
Ok((new_client_state, new_consensus_state, header_height))
119124
}
125+
pub fn check_misbehaviour_and_update_state<const L1_SYNC_COMMITTEE_SIZE: usize>(
126+
&self,
127+
now: Time,
128+
client_id: &ClientId,
129+
trusted_consensus_state: &ConsensusState,
130+
misbehaviour: Misbehaviour<L1_SYNC_COMMITTEE_SIZE>,
131+
) -> Result<ClientState, Error> {
132+
if self.frozen {
133+
return Err(Error::ClientFrozen(client_id.clone()));
134+
}
135+
136+
let misbehaviour_client_id = misbehaviour.client_id();
137+
if misbehaviour_client_id != client_id {
138+
return Err(Error::UnexpectedClientIdInMisbehaviour(
139+
client_id.clone(),
140+
misbehaviour_client_id.clone(),
141+
));
142+
}
143+
144+
let l1_cons_state = L1Consensus {
145+
slot: trusted_consensus_state.l1_slot,
146+
current_sync_committee: trusted_consensus_state.l1_current_sync_committee.clone(),
147+
next_sync_committee: trusted_consensus_state.l1_next_sync_committee.clone(),
148+
timestamp: trusted_consensus_state.l1_timestamp,
149+
};
150+
151+
validate_state_timestamp_within_trusting_period(
152+
now,
153+
self.l1_config.trusting_period,
154+
trusted_consensus_state.l1_timestamp,
155+
)?;
156+
157+
match &misbehaviour {
158+
Misbehaviour::L1(l1) => l1.verify(
159+
now.as_unix_timestamp_secs(),
160+
&self.l1_config,
161+
&l1_cons_state,
162+
),
163+
Misbehaviour::L2(l2) => match l2.verifier() {
164+
Verifier::Future(v) => v.verify(
165+
now.as_unix_timestamp_secs(),
166+
&self.l1_config,
167+
&self.fault_dispute_game_config,
168+
&l1_cons_state,
169+
misbehaviour.trusted_height().revision_height(),
170+
trusted_consensus_state.l1_origin,
171+
),
172+
Verifier::Past(v) => v.verify(
173+
now.as_unix_timestamp_secs(),
174+
&self.l1_config,
175+
&self.fault_dispute_game_config,
176+
&l1_cons_state,
177+
trusted_consensus_state.output_root,
178+
),
179+
},
180+
}?;
181+
182+
Ok(Self {
183+
frozen: true,
184+
..self.clone()
185+
})
186+
}
120187

121188
pub fn verify_membership(
122189
&self,
@@ -292,6 +359,9 @@ impl TryFrom<RawClientState> for ClientState {
292359

293360
let l1_config = value.l1_config.ok_or(Error::MissingL1Config)?;
294361
let l1_config = L1Config::try_from(l1_config)?;
362+
let fault_dispute_game_config = value
363+
.fault_dispute_game_config
364+
.ok_or(Error::MissingFaultDisputeGameConfig)?;
295365

296366
Ok(Self {
297367
chain_id: value.chain_id,
@@ -301,6 +371,7 @@ impl TryFrom<RawClientState> for ClientState {
301371
frozen,
302372
rollup_config,
303373
l1_config,
374+
fault_dispute_game_config: fault_dispute_game_config.try_into()?,
304375
})
305376
}
306377
}
@@ -321,6 +392,7 @@ impl TryFrom<ClientState> for RawClientState {
321392
rollup_config_json: serde_json::to_vec(&value.rollup_config)
322393
.map_err(Error::UnexpectedRollupConfig)?,
323394
l1_config: Some(value.l1_config.into()),
395+
fault_dispute_game_config: Some(value.fault_dispute_game_config.into()),
324396
})
325397
}
326398
}

light-client/src/consensus_state.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ pub struct ConsensusState {
3434
pub l1_next_sync_committee: PublicKey,
3535
/// finalized header's timestamp
3636
pub l1_timestamp: Time,
37+
38+
/// L1 origin block number for output_root
39+
pub l1_origin: u64,
3740
}
3841

3942
impl ConsensusState {
@@ -66,6 +69,7 @@ impl TryFrom<RawConsensusState> for ConsensusState {
6669
.map_err(Error::L1ConsensusError)?,
6770
l1_next_sync_committee: PublicKey::try_from(value.l1_next_sync_committee)
6871
.map_err(Error::L1ConsensusError)?,
72+
l1_origin: value.l1_origin,
6973
})
7074
}
7175
}
@@ -81,6 +85,7 @@ impl From<ConsensusState> for RawConsensusState {
8185
l1_current_sync_committee: value.l1_current_sync_committee.to_vec(),
8286
l1_next_sync_committee: value.l1_next_sync_committee.to_vec(),
8387
l1_timestamp: value.l1_timestamp.as_unix_timestamp_secs(),
88+
l1_origin: value.l1_origin,
8489
}
8590
}
8691
}

light-client/src/errors.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use crate::l1::L1Consensus;
2+
use crate::misbehaviour::FaultDisputeGameProof;
23
use alloc::boxed::Box;
34
use alloc::string::{String, ToString};
45
use alloc::vec::Vec;
6+
use alloy_primitives::private::alloy_rlp;
57
use alloy_primitives::B256;
68
use core::array::TryFromSliceError;
79
use ethereum_consensus::bls::PublicKey;
810
use ethereum_consensus::errors::{Error as L1ConsensusError, MerkleError};
911
use ethereum_consensus::sync_protocol::SyncCommitteePeriod;
10-
use ethereum_consensus::types::H256;
12+
use ethereum_consensus::types::{Address, H256};
1113
use ethereum_light_client_verifier::errors::Error as L1VerifyError;
12-
use light_client::types::{ClientId, Height, Time, TimeError};
14+
use light_client::types::{ClientId, Height, Time, TimeError, TypeError};
1315
use optimism_derivation::derivation::Derivation;
1416

1517
#[derive(thiserror::Error, Debug)]
@@ -69,6 +71,8 @@ pub enum Error {
6971
// Update
7072
#[error("MissingL1Config")]
7173
MissingL1Config,
74+
#[error("MissingFaultDisputeGameConfig")]
75+
MissingFaultDisputeGameConfig,
7276
#[error("MissingForkSpec")]
7377
MissingForkSpec,
7478
#[error("MissingL1Head")]
@@ -147,6 +151,75 @@ pub enum Error {
147151
#[error("SyncCommitteeValidateError: err={0:?}")]
148152
SyncCommitteeValidateError(L1ConsensusError),
149153

154+
// Misbehaviour
155+
#[error("NoHeaderFound")]
156+
NoHeaderFound,
157+
#[error("MissingL2History")]
158+
MissingL2History,
159+
#[error("UnexpectedResolvedL2Number: expected={0} actual={1}")]
160+
UnexpectedResolvedL2Number(u64, u64),
161+
#[error("UnexpectedHeaderRelation: expected_parent_hash={expected_parent_hash:?} actual_parent_hash={actual_parent_hash:?} header_number={header_number} parent_number={parent_number}")]
162+
UnexpectedHeaderRelation {
163+
expected_parent_hash: B256,
164+
actual_parent_hash: B256,
165+
header_number: u64,
166+
parent_number: u64,
167+
},
168+
#[error("UnexpectedHeaderRLPError err={0:?}")]
169+
UnexpectedHeaderRLPError(alloy_rlp::Error),
170+
#[error("UnexpectedDisputeGameFactoryProxyProof: proof={proof:?} output_root={output_root:?} l2_block_number={l2_block_number} err={err:?}")]
171+
UnexpectedDisputeGameFactoryProxyProof {
172+
proof: FaultDisputeGameProof,
173+
output_root: B256,
174+
l2_block_number: u64,
175+
err: Option<L1VerifyError>,
176+
},
177+
#[error("UnexpectedFaultDisputeGameProof: proof={proof:?} address={address:?} err={err:?}")]
178+
UnexpectedFaultDisputeGameProof {
179+
proof: FaultDisputeGameProof,
180+
address: Address,
181+
err: Option<L1VerifyError>,
182+
},
183+
#[error("UnexpectedGameID: game_id={0:?}")]
184+
UnexpectedGameID(Vec<u8>),
185+
#[error("UnexpectedResolvedStatus: proof={proof:?} status={status} address={address:?} packing_slot_value={packing_slot_value:?}")]
186+
UnexpectedResolvedStatus {
187+
proof: FaultDisputeGameProof,
188+
status: u8,
189+
address: Address,
190+
packing_slot_value: [u8; 32],
191+
},
192+
#[error("L1VerifyMisbehaviourError: err={0:?}")]
193+
L1VerifyMisbehaviourError(L1VerifyError),
194+
#[error("UnknownMisbehaviourType: type={0:?}")]
195+
UnknownMisbehaviourType(String),
196+
#[error("UnexpectedDisputeGameFactoryAddress: err={0:?}")]
197+
UnexpectedDisputeGameFactoryAddress(L1ConsensusError),
198+
#[error("UnexpectedClientId: err={0:?}")]
199+
UnexpectedClientId(TypeError),
200+
#[error("UnexpectedClientIdInMisbehaviour: request={0:?} misbehaviour={1:?}")]
201+
UnexpectedClientIdInMisbehaviour(ClientId, ClientId),
202+
#[error("UnexpectedMisbehaviourOutput: resolved_output_root={0:?}")]
203+
UnexpectedMisbehaviourOutput(B256),
204+
#[error("UnexpectedMisbehaviourHeight: trusted={0} requested={1}")]
205+
UnexpectedMisbehaviourHeight(u64, u64),
206+
#[error("UnexpectedPastL1Header: trusted_l1_origin={0} requested={1}")]
207+
UnexpectedPastL1Header(u64, u64),
208+
#[error("UnexpectedSealedL1Number: expected={0} actual={1}")]
209+
UnexpectedL1HeaderNumber(u64, u64),
210+
#[error("UnexpectedL1HeaderStateRoot: expected={0:?} actual={1:?}")]
211+
UnexpectedL1HeaderStateRoot(B256, B256),
212+
#[error("UnexpectedSubmittedL1HeaderStateRoot: expected={0:?} actual={1:?}")]
213+
UnexpectedSubmittedL1HeaderStateRoot(B256, B256),
214+
#[error("UnexpectedGameExists: game_id={0:?}")]
215+
UnexpectedGameExists(Vec<u8>),
216+
#[error("UnexpectedStateRoot: state_root={0:?}")]
217+
UnexpectedStateRoot(Vec<u8>),
218+
#[error("UnexpectedGameCreatedAt: created_at={0} l1_timestamp={1}")]
219+
UnexpectedGameCreatedAt(u64, u64),
220+
#[error("UnexpectedCreatedAt: data={0}")]
221+
UnexpectedCreatedAt(TryFromSliceError),
222+
150223
// Framework
151224
#[error("LCPError: err={0:?}")]
152225
LCPError(light_client::Error),

light-client/src/header.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ impl<const L1_SYNC_COMMITTEE_SIZE: usize> Header<L1_SYNC_COMMITTEE_SIZE> {
109109
chain_id: u64,
110110
trusted_output_root: B256,
111111
rollup_config: &RollupConfig,
112-
) -> Result<(alloy_consensus::Header, B256), Error> {
112+
) -> Result<(alloy_consensus::Header, u64, B256), Error> {
113113
// Ensure trusted
114114
if self.derivation.agreed_l2_output_root != trusted_output_root {
115115
return Err(Error::UnexpectedTrustedOutputRoot(
@@ -119,11 +119,11 @@ impl<const L1_SYNC_COMMITTEE_SIZE: usize> Header<L1_SYNC_COMMITTEE_SIZE> {
119119
}
120120

121121
// Ensure honest derivation
122-
let header = self
122+
let result = self
123123
.derivation
124124
.verify(chain_id, rollup_config, self.oracle.clone())
125125
.map_err(|e| Error::DerivationError(self.derivation.clone(), self.oracle.len(), e))?;
126-
Ok((header, self.derivation.l2_output_root))
126+
Ok((result.0, result.1, self.derivation.l2_output_root))
127127
}
128128
}
129129

0 commit comments

Comments
 (0)