11use sha2:: { Digest , Sha256 } ;
22use strata_asm_common:: AsmLogEntry ;
33use strata_primitives:: {
4+ block_credential:: CredRule ,
45 buf:: { Buf32 , Buf64 } ,
6+ crypto:: verify_schnorr_sig,
57 params:: RollupParams ,
68} ;
79
@@ -28,6 +30,7 @@ pub struct OLBlockHeader {
2830 slot : Slot ,
2931 epoch : Epoch ,
3032 parent_blockid : OLBlockId ,
33+ logs_root : Buf32 ,
3134 body_root : Buf32 ,
3235 state_root : Buf32 ,
3336}
@@ -39,7 +42,6 @@ type Epoch = u64;
3942/// The body portion of an OL block containing the actual data
4043#[ derive( Debug , Clone ) ]
4144pub struct OLBlockBody {
42- logs : Vec < OLLog > ,
4345 txs : Option < Vec < Transaction > > ,
4446 l1update : Option < L1Update > ,
4547}
@@ -78,7 +80,7 @@ pub struct TransactionExtra {
7880#[ derive( Debug , Clone ) ]
7981pub struct L1Update {
8082 /// The state root before applying updates from L1
81- inner_state_root : Buf32 ,
83+ unsealed_state_root : Buf32 ,
8284 /// L1 height the manifests are read upto
8385 new_l1_height : u32 ,
8486 /// Manifests from last l1_height to the new_l1_height
@@ -114,46 +116,57 @@ impl OLBlock {
114116 & self . body
115117 }
116118
117- pub fn validate_block_header (
119+ /// Check the continuity of block header
120+ pub fn validate_block_header_continuity (
118121 & self ,
119- _params : & RollupParams ,
122+ params : & RollupParams ,
120123 prev_header : & OLBlockHeader ,
121124 ) -> Result < ( ) , String > {
122- let current_header = self . signed_header . header ( ) ;
125+ self . validate_block_signature ( params ) ? ;
123126
124- if current_header. slot ( ) > 0 {
125- if current_header. slot ( ) != prev_header. slot ( ) + 1 {
126- return Err ( format ! ( "Invalid block slot {}" , current_header. slot( ) ) ) ;
127+ let cur_header = self . signed_header . header ( ) ;
128+
129+ if cur_header. slot ( ) > 0 {
130+ if cur_header. slot ( ) != prev_header. slot ( ) + 1 {
131+ return Err ( format ! ( "Invalid block slot {}" , cur_header. slot( ) ) ) ;
127132 }
128- if * current_header . parent_blockid ( ) != prev_header. compute_header_root ( ) {
133+ if * cur_header . parent_blockid ( ) != prev_header. compute_header_root ( ) {
129134 return Err ( "Invalid parent block ID" . to_string ( ) ) ;
130135 }
131136 }
132137
133138 // Check epoch progression - epoch should not decrease and increase only by 1 at max
134- let same_epoch = current_header . epoch ( ) == prev_header. epoch ( ) ;
135- let valid_increment = current_header . epoch ( ) == prev_header . epoch ( ) + 1 ;
136- if current_header . epoch ( ) != 0 && ( same_epoch || valid_increment) {
139+ let epoch_diff = cur_header . epoch ( ) as i64 - prev_header. epoch ( ) as i64 ;
140+ let valid_increment = epoch_diff == 0 || epoch_diff == 1 ;
141+ if cur_header . epoch ( ) != 0 && ! valid_increment {
137142 return Err ( format ! (
138143 "Epoch regression: current {} < previous {}" ,
139- current_header . epoch, prev_header. epoch
144+ cur_header . epoch, prev_header. epoch
140145 ) ) ;
141146 }
142147
143148 // Check timestamp progression - should not go backwards.
144149 // FIXME: might need to use some threshold like bitcoin.
145- if current_header . timestamp < prev_header. timestamp {
150+ if cur_header . timestamp < prev_header. timestamp {
146151 return Err ( format ! (
147152 "Timestamp regression: current {} < previous {}" ,
148- current_header . timestamp, prev_header. timestamp
153+ cur_header . timestamp, prev_header. timestamp
149154 ) ) ;
150155 }
151156
152- // Basic sanity checks
153- if current_header. body_root == Buf32 :: zero ( ) {
154- return Err ( "Invalid body root (zero hash)" . to_string ( ) ) ;
155- }
157+ Ok ( ( ) )
158+ }
156159
160+ pub fn validate_block_signature ( & self , params : & RollupParams ) -> Result < ( ) , String > {
161+ let seq_pubkey = match params. cred_rule {
162+ CredRule :: SchnorrKey ( key) => key,
163+ CredRule :: Unchecked => return Ok ( ( ) ) ,
164+ } ;
165+ let digest = self . signed_header ( ) . header ( ) . compute_header_root ( ) ;
166+
167+ if !verify_schnorr_sig ( self . signed_header ( ) . signature ( ) , & digest, & seq_pubkey) {
168+ return Err ( "Invalid block signature" . to_string ( ) ) ;
169+ }
157170 Ok ( ( ) )
158171 }
159172}
@@ -178,6 +191,7 @@ impl OLBlockHeader {
178191 slot : Slot ,
179192 epoch : Epoch ,
180193 parent_blockid : OLBlockId ,
194+ logs_root : Buf32 ,
181195 body_root : Buf32 ,
182196 state_root : Buf32 ,
183197 ) -> Self {
@@ -186,6 +200,7 @@ impl OLBlockHeader {
186200 slot,
187201 epoch,
188202 parent_blockid,
203+ logs_root,
189204 body_root,
190205 state_root,
191206 }
@@ -207,6 +222,10 @@ impl OLBlockHeader {
207222 & self . parent_blockid
208223 }
209224
225+ pub fn logs_root ( & self ) -> & Buf32 {
226+ & self . logs_root
227+ }
228+
210229 pub fn body_root ( & self ) -> & Buf32 {
211230 & self . body_root
212231 }
@@ -215,6 +234,7 @@ impl OLBlockHeader {
215234 & self . state_root
216235 }
217236
237+ // NOTE: this will possibly be redundant once we have SSZ
218238 pub fn compute_header_root ( & self ) -> Buf32 {
219239 let mut hasher = Sha256 :: new ( ) ;
220240
@@ -231,20 +251,8 @@ impl OLBlockHeader {
231251}
232252
233253impl OLBlockBody {
234- pub fn new (
235- logs : Vec < OLLog > ,
236- txs : Option < Vec < Transaction > > ,
237- l1update : Option < L1Update > ,
238- ) -> Self {
239- Self {
240- logs,
241- txs,
242- l1update,
243- }
244- }
245-
246- pub fn logs ( & self ) -> & [ OLLog ] {
247- & self . logs
254+ pub fn new ( txs : Option < Vec < Transaction > > , l1update : Option < L1Update > ) -> Self {
255+ Self { txs, l1update }
248256 }
249257
250258 pub fn txs ( & self ) -> & Option < Vec < Transaction > > {
@@ -254,6 +262,30 @@ impl OLBlockBody {
254262 pub fn l1update ( & self ) -> & Option < L1Update > {
255263 & self . l1update
256264 }
265+
266+ // NOTE: this will be redundant after ssz
267+ pub fn compute_root ( & self ) -> Buf32 {
268+ let mut hasher = Sha256 :: new ( ) ;
269+ if let Some ( txs) = self . txs ( ) {
270+ for tx in txs {
271+ hasher. update ( tx. type_id ( ) . to_be_bytes ( ) ) ;
272+ match & tx. payload {
273+ TransactionPayload :: GenericAccountMessage { target, payload } => {
274+ hasher. update ( target. as_slice ( ) ) ;
275+ hasher. update ( payload) ;
276+ }
277+ TransactionPayload :: SnarkAccountUpdate { target, update } => {
278+ hasher. update ( target. as_slice ( ) ) ;
279+ hasher. update ( & update. witness ) ;
280+ hasher. update ( update. data . seq_no . to_be_bytes ( ) ) ;
281+ // TODO: other fields, maybe wait for ssz?
282+ todo ! ( )
283+ }
284+ }
285+ }
286+ }
287+ Buf32 :: new ( hasher. finalize ( ) . into ( ) )
288+ }
257289}
258290
259291impl Transaction {
@@ -307,14 +339,14 @@ impl TransactionExtra {
307339impl L1Update {
308340 pub fn new ( inner_state_root : Buf32 , new_l1_height : u32 , manifests : Vec < AsmManifest > ) -> Self {
309341 Self {
310- inner_state_root,
342+ unsealed_state_root : inner_state_root,
311343 new_l1_height,
312344 manifests,
313345 }
314346 }
315347
316348 pub fn inner_state_root ( & self ) -> & Buf32 {
317- & self . inner_state_root
349+ & self . unsealed_state_root
318350 }
319351
320352 pub fn new_l1_height ( & self ) -> u32 {
@@ -355,4 +387,14 @@ impl OLLog {
355387 pub fn payload ( & self ) -> & [ u8 ] {
356388 & self . payload
357389 }
390+
391+ // NOTE: This will also be redundant after SSZ
392+ pub ( crate ) fn compute_root ( logs : & [ Self ] ) -> Buf32 {
393+ let mut hasher = Sha256 :: new ( ) ;
394+ for log in logs {
395+ hasher. update ( log. account_serial ( ) . to_be_bytes ( ) ) ;
396+ hasher. update ( log. payload ( ) ) ;
397+ }
398+ Buf32 :: new ( hasher. finalize ( ) . into ( ) )
399+ }
358400}
0 commit comments