Skip to content

Commit d51c0fb

Browse files
committed
Add more block validations, separate validation from actual execution
1 parent a0157a6 commit d51c0fb

File tree

6 files changed

+185
-76
lines changed

6 files changed

+185
-76
lines changed

crates/asm/common/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ where
1818
pub actual: T,
1919
}
2020

21+
impl<T: Debug + Display> Mismatched<T> {
22+
pub fn new(expected: T, actual: T) -> Self {
23+
Self { expected, actual }
24+
}
25+
}
26+
2127
/// Errors that can occur while working with ASM subprotocols.
2228
#[derive(Debug, Error)]
2329
pub enum AsmError {

crates/chainexec/src/executor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Chain executor.
22
33
use strata_chaintsn::{
4-
context::{BlockHeaderContext, L2HeaderAndParent, StateAccessor},
4+
context::{BlockHeaderContext, StateAccessor},
55
transition::process_block,
66
};
77
use strata_primitives::prelude::*;

crates/services/stf-runner/src/block.rs

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use sha2::{Digest, Sha256};
22
use strata_asm_common::AsmLogEntry;
33
use 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)]
4144
pub 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)]
7981
pub 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

233253
impl 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

259291
impl Transaction {
@@ -307,14 +339,14 @@ impl TransactionExtra {
307339
impl 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
}

crates/services/stf-runner/src/ledger.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,14 @@ impl LedgerProvider for InMemoryVectorLedger {
117117
// For now, recompute every time (inefficient but correct)
118118
let mut hasher = Sha256::new();
119119

120-
let mut sorted_accounts: Vec<_> = self.account_states.keys().collect();
121-
sorted_accounts.sort();
122-
123-
for account_id in sorted_accounts {
124-
hasher.update(account_id.as_ref());
125-
if let Some(state) = self.account_states.get(account_id) {
126-
hasher.update(state.serial.to_be_bytes());
127-
hasher.update(state.ty.to_be_bytes());
128-
hasher.update(state.balance.to_be_bytes());
129-
}
120+
let mut sorted_accounts: Vec<_> = self.account_states.iter().collect();
121+
sorted_accounts.sort_by_key(|(k, _)| **k);
122+
123+
for (acct_id, state) in sorted_accounts {
124+
hasher.update(acct_id.as_slice());
125+
hasher.update(state.serial.to_be_bytes());
126+
hasher.update(state.ty.to_be_bytes());
127+
hasher.update(state.balance.to_be_bytes());
130128
}
131129

132130
Ok(Buf32::new(hasher.finalize().into()))

0 commit comments

Comments
 (0)