@@ -3,8 +3,13 @@ use serde::{Deserialize, Serialize};
3
3
use sha2:: { Digest as Sha256Digest , Sha256 } ;
4
4
5
5
use crate :: Digest ;
6
- use crate :: { error:: ProofError , keccak:: keccak256_combine, vkey_hash:: HashU32 } ;
7
-
6
+ use crate :: {
7
+ error:: ProofError ,
8
+ keccak:: keccak256_combine,
9
+ local_exit_tree:: { hasher:: Keccak256Hasher , proof:: LETMerkleProof } ,
10
+ vkey_hash:: HashU32 ,
11
+ L1InfoTreeLeaf ,
12
+ } ;
8
13
/// Hardcoded hash of the "aggregation vkey".
9
14
/// NOTE: Format being `hash_u32()` of the `SP1StarkVerifyingKey`.
10
15
pub const AGGREGATION_VKEY_HASH : HashU32 = [ 0u32 ; 8 ] ; // TODO: to put the right value
@@ -18,6 +23,7 @@ pub const OUTPUT_ROOT_VERSION: [u8; 32] = [0u8; 32];
18
23
/// Public values to verify the FEP.
19
24
#[ derive( Serialize , Deserialize , Clone , Debug ) ]
20
25
pub struct FepPublicValues {
26
+ /// OP succint values.
21
27
pub l1_head : Digest ,
22
28
pub claim_block_num : u32 ,
23
29
pub rollup_config_hash : Digest ,
@@ -28,6 +34,10 @@ pub struct FepPublicValues {
28
34
pub new_withdrawal_storage_root : Digest ,
29
35
pub new_block_hash : Digest ,
30
36
37
+ /// L1 info tree leaf and index containing the `l1Head` as block hash.
38
+ pub l1_info_tree_leaf : ( u32 , L1InfoTreeLeaf ) ,
39
+ /// Inclusion proof of the leaf to the l1 info root.
40
+ pub l1_head_inclusion_proof : LETMerkleProof < Keccak256Hasher > ,
31
41
/// Trusted sequencer address.
32
42
pub trusted_sequencer : Address ,
33
43
/// Signature in the "OptimisticMode" case.
@@ -81,11 +91,22 @@ impl FepPublicValues {
81
91
}
82
92
83
93
/// Verify one ECDSA or the sp1 proof.
84
- pub fn verify ( & self ) -> Result < ( ) , ProofError > {
94
+ pub fn verify (
95
+ & self ,
96
+ l1_info_root : Digest ,
97
+ new_local_exit_root : Digest ,
98
+ commit_imported_bridge_exits : Digest ,
99
+ ) -> Result < ( ) , ProofError > {
85
100
if let Some ( signature) = self . signature_optimistic_mode {
86
101
// Verify only one ECDSA on the public inputs
102
+ let signature_commitment = keccak256_combine ( [
103
+ self . hash ( ) ,
104
+ new_local_exit_root. 0 ,
105
+ commit_imported_bridge_exits. 0 ,
106
+ ] ) ;
107
+
87
108
let recovered_signer = signature
88
- . recover_address_from_prehash ( & B256 :: new ( self . hash ( ) ) )
109
+ . recover_address_from_prehash ( & B256 :: new ( signature_commitment . 0 ) )
89
110
. map_err ( |_| ProofError :: InvalidSignature ) ?;
90
111
91
112
if recovered_signer != self . trusted_sequencer {
@@ -97,6 +118,9 @@ impl FepPublicValues {
97
118
98
119
Ok ( ( ) )
99
120
} else {
121
+ // Verify l1 head
122
+ self . verify_l1_head ( l1_info_root) ?;
123
+
100
124
// Verify the FEP stark proof.
101
125
#[ cfg( not( target_os = "zkvm" ) ) ]
102
126
unreachable ! ( "verify_sp1_proof is not callable outside of SP1" ) ;
@@ -115,6 +139,32 @@ impl FepPublicValues {
115
139
}
116
140
117
141
impl FepPublicValues {
142
+ // Verify that the `l1Head` considered by the FEP exists in the L1 Info Tree
143
+ pub fn verify_l1_head ( & self , l1_info_root : Digest ) -> Result < ( ) , ProofError > {
144
+ if self . l1_head != self . l1_info_tree_leaf . 1 . block_hash {
145
+ return Err ( ProofError :: MismatchL1Head {
146
+ from_l1_info_tree_leaf : self . l1_info_tree_leaf . 1 . block_hash ,
147
+ from_fep_public_values : self . l1_head ,
148
+ } ) ;
149
+ }
150
+
151
+ let inclusion_proof_valid = self . l1_head_inclusion_proof . verify (
152
+ self . l1_info_tree_leaf . 1 . hash ( ) ,
153
+ self . l1_info_tree_leaf . 0 ,
154
+ l1_info_root,
155
+ ) ;
156
+
157
+ if !inclusion_proof_valid {
158
+ return Err ( ProofError :: InvalidInclusionProofL1Head {
159
+ index : self . l1_info_tree_leaf . 0 ,
160
+ l1_leaf_hash : self . l1_info_tree_leaf . 1 . hash ( ) ,
161
+ l1_info_root : l1_info_root,
162
+ } ) ;
163
+ }
164
+
165
+ Ok ( ( ) )
166
+ }
167
+
118
168
/// Compute l2 pre root.
119
169
pub fn compute_l2_pre_root ( & self ) -> Digest {
120
170
compute_output_root (
0 commit comments