Skip to content
This repository was archived by the owner on Aug 3, 2026. It is now read-only.

Commit 7d06d41

Browse files
smtmfftCopilot
andauthored
feat: align PROPOSAL_MAX_BLOCKS to 192 (#660)
* feat: align PROPOSAL_MAX_BLOCKS to 192 * fix: fix CI * chore: update gaiko * feat(shasta): enhance validation of L2 block header `extra_data` in batch processing - Added early validation for Shasta-specific `extra_data` in the `batch_preflight` function to improve error reporting. - Implemented a new utility function to decode and validate `extra_data` for Shasta blocks, ensuring consistency with proposal data. - Updated the `calculate_batch_blocks_final_header` function to assert `extra_data` validity post-execution for simplicity. * chore: fix compile warning * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * refactor(shasta): simplify extra_data decoding and validation - Removed the endOfProposalFlag from the decode_shasta_extra_data function, reducing the expected length of extra_data from 8 to 7 bytes. - Updated validation logic in validate_shasta_extra_data_for_batch to reflect the changes in extra_data structure, improving clarity and maintainability. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ec8a925 commit 7d06d41

7 files changed

Lines changed: 145 additions & 26 deletions

File tree

core/src/preflight/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,20 @@ pub async fn batch_preflight<BDP: BlockDataProvider>(
294294
taiko: taiko_guest_batch_input.clone(),
295295
};
296296

297+
// Shasta-only: verify the L2 block header `extra_data` matches the proposal decoding and the request.
298+
//
299+
// Note: the same rule is enforced inside the guests (via `calculate_batch_blocks_final_header`),
300+
// but we also fail early here for better error reporting and less wasted work.
301+
if let BlockProposedFork::Shasta(_) = &mock_guest_batch_input.taiko.batch_proposed {
302+
if !raiko_lib::utils::shasta_rules::validate_shasta_extra_data_for_batch(
303+
&mock_guest_batch_input,
304+
) {
305+
return Err(RaikoError::Preflight(
306+
"invalid shasta extra_data in batch input".to_string(),
307+
));
308+
}
309+
}
310+
297311
// distribute txs to each block
298312
let pool_txs_list: Vec<(Vec<TransactionSigned>, bool)> =
299313
generate_transactions_for_batch_blocks(&mock_guest_batch_input);

host/config/chain_spec_list_devnet.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"RISC0": null
3232
}
3333
},
34-
"genesis_time": 1768276900,
34+
"genesis_time": 1769395300,
3535
"seconds_per_slot": 12,
3636
"is_taiko": false
3737
},
@@ -50,7 +50,7 @@
5050
"Block": 0
5151
},
5252
"SHASTA": {
53-
"Timestamp": 0
53+
"Timestamp": 1769396301
5454
},
5555
"CANCUN": "TBD"
5656
},
@@ -69,16 +69,16 @@
6969
"beacon_rpc": null,
7070
"verifier_address_forks": {
7171
"PACAYA": {
72-
"SGX": "0xf0ebc67dB56A5687649fF2216e3826a52F8DBd7c",
72+
"SGX": "0x9D351F6E72e3095F24dD854c9B8CA69F99A2C538",
7373
"SP1": "0xCb2D625BF8C2187B180646aF4738AF5F1413Fb70",
7474
"RISC0": "0x48940F7ab2C674Aa66C09eDa71614aD704E9d007",
75-
"SGXGETH": "0xfE3EdF3e778a647c0955f2a5f79565E272E6AfdB"
75+
"SGXGETH": "0x6B455442C8C4cAC2e09c40409E8bf7FfcdB1Fc50"
7676
},
7777
"SHASTA": {
78-
"SGX": "0xf0ebc67dB56A5687649fF2216e3826a52F8DBd7c",
78+
"SGX": "0x9D351F6E72e3095F24dD854c9B8CA69F99A2C538",
7979
"SP1": "0xCb2D625BF8C2187B180646aF4738AF5F1413Fb70",
8080
"RISC0": "0x48940F7ab2C674Aa66C09eDa71614aD704E9d007",
81-
"SGXGETH": "0xfE3EdF3e778a647c0955f2a5f79565E272E6AfdB"
81+
"SGXGETH": "0x6B455442C8C4cAC2e09c40409E8bf7FfcdB1Fc50"
8282
}
8383
},
8484
"genesis_time": 0,

lib/src/builder.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ pub fn calculate_batch_blocks_final_header(input: &GuestBatchInput) -> Vec<Block
8585
// 1. connect parent hash & state root
8686
// 2. block number should be in sequence
8787
fn validate_final_batch_blocks(input: &GuestBatchInput, final_blocks: &[Block]) {
88+
// Shasta-only: validate extra_data once, post-execution, to keep code simple.
89+
assert!(
90+
crate::utils::shasta_rules::validate_shasta_extra_data_for_batch(input),
91+
"invalid shasta extra_data in batch input"
92+
);
93+
8894
input
8995
.inputs
9096
.iter()

lib/src/manifest/codec.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use alloy_rlp::{Decodable, Encodable};
22
use anyhow::Result;
33

44
use super::types::{DerivationSourceManifest, ProtocolBlockManifest};
5-
use crate::utils::blobs::{zlib_compress_data, zlib_decompress_data};
5+
use crate::utils::blobs::zlib_compress_data;
66

77
/// Encode and compress a Shasta proposal manifest (equivalent to Go's EncodeAndCompressShastaProposal)
88
pub fn encode_and_compress_shasta_proposal(proposal: &DerivationSourceManifest) -> Result<Vec<u8>> {
@@ -15,20 +15,6 @@ pub fn encode_and_compress_shasta_proposal(proposal: &DerivationSourceManifest)
1515
Ok(compressed)
1616
}
1717

18-
/// Decode and decompress a Shasta proposal manifest
19-
pub fn decode_and_decompress_shasta_proposal(
20-
compressed_data: &[u8],
21-
) -> Result<DerivationSourceManifest> {
22-
// First, decompress the data
23-
let rlp_encoded = zlib_decompress_data(compressed_data)?;
24-
25-
// Then RLP decode
26-
let mut data = rlp_encoded.as_slice();
27-
let proposal = DerivationSourceManifest::decode(&mut data)?;
28-
29-
Ok(proposal)
30-
}
31-
3218
impl Encodable for ProtocolBlockManifest {
3319
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
3420
// Calculate the payload length first
@@ -107,7 +93,7 @@ impl Encodable for DerivationSourceManifest {
10793
}
10894
}
10995

110-
pub(crate) const PROPOSAL_MAX_BLOCKS: usize = 384;
96+
pub(crate) const PROPOSAL_MAX_BLOCKS: usize = 192;
11197

11298
impl Decodable for DerivationSourceManifest {
11399
fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
@@ -134,6 +120,7 @@ impl Decodable for DerivationSourceManifest {
134120
#[cfg(test)]
135121
mod tests {
136122
use super::*;
123+
use crate::utils::blobs::zlib_decompress_data;
137124
use alloy_primitives::Address;
138125

139126
fn create_test_proposal() -> DerivationSourceManifest {
@@ -202,4 +189,18 @@ mod tests {
202189
// Decode and decompress
203190
let _decoded = decode_and_decompress_shasta_proposal(&encoded).unwrap();
204191
}
192+
193+
/// Decode and decompress a Shasta proposal manifest
194+
fn decode_and_decompress_shasta_proposal(
195+
compressed_data: &[u8],
196+
) -> Result<DerivationSourceManifest> {
197+
// First, decompress the data
198+
let rlp_encoded = zlib_decompress_data(compressed_data)?;
199+
200+
// Then RLP decode
201+
let mut data = rlp_encoded.as_slice();
202+
let proposal = DerivationSourceManifest::decode(&mut data)?;
203+
204+
Ok(proposal)
205+
}
205206
}

lib/src/utils/shasta_rules.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::cmp::max as std_max;
66
use tracing::warn;
77

88
use crate::consts::ForkCondition;
9-
use crate::input::{GuestBatchInput, GuestInput};
9+
use crate::input::{BlockProposedFork, GuestBatchInput, GuestInput};
1010
use crate::manifest::{DerivationSourceManifest, ProtocolBlockManifest, PROPOSAL_MAX_BLOCKS};
1111
#[cfg(not(feature = "std"))]
1212
use crate::no_std::*;
@@ -15,6 +15,104 @@ pub const BOND_PROCESSING_DELAY: usize = 6;
1515

1616
pub const ANCHOR_MAX_OFFSET: usize = 128;
1717

18+
/// Decode Shasta `extra_data` layout (per `Derivation.md`):
19+
/// - byte[0] => basefeeSharingPctg (uint8)
20+
/// - byte[1..6] => proposalId (uint48, big-endian)
21+
fn decode_shasta_extra_data(extra_data: &[u8]) -> Option<(u8, u64)> {
22+
if extra_data.len() < 7 {
23+
warn!(
24+
"invalid shasta extra_data length: {} (need at least 7 bytes)",
25+
extra_data.len()
26+
);
27+
return None;
28+
}
29+
30+
let basefee_sharing_pctg = extra_data[0];
31+
let proposal_id = u64::from_be_bytes([
32+
0,
33+
0,
34+
extra_data[1],
35+
extra_data[2],
36+
extra_data[3],
37+
extra_data[4],
38+
extra_data[5],
39+
extra_data[6],
40+
]);
41+
42+
Some((basefee_sharing_pctg, proposal_id))
43+
}
44+
45+
/// Validate Shasta `extra_data` for all blocks in this proposal batch.
46+
///
47+
/// Enforced in guests (ZKVM programs) by calling from `calculate_batch_blocks_final_header`,
48+
/// so the proof itself rejects malformed `extra_data` even if host-side preflight is bypassed.
49+
pub fn validate_shasta_extra_data_for_batch(input: &GuestBatchInput) -> bool {
50+
let BlockProposedFork::Shasta(event_data) = &input.taiko.batch_proposed else {
51+
return true;
52+
};
53+
54+
if input.inputs.is_empty() {
55+
warn!("empty shasta batch input");
56+
return false;
57+
}
58+
59+
let expected_basefee_sharing_pctg = event_data.proposal.basefeeSharingPctg;
60+
let expected_proposal_id = input.taiko.batch_id;
61+
62+
for guest_input in input.inputs.iter() {
63+
let header = &guest_input.block.header;
64+
let Some((basefee_sharing_pctg, proposal_id)) =
65+
decode_shasta_extra_data(header.extra_data.as_ref())
66+
else {
67+
warn!(
68+
"failed to decode shasta extra_data for block {}",
69+
header.number
70+
);
71+
return false;
72+
};
73+
74+
if basefee_sharing_pctg != expected_basefee_sharing_pctg {
75+
warn!(
76+
"shasta extra_data basefeeSharingPctg mismatch for block {}: got {}, expected {}",
77+
header.number, basefee_sharing_pctg, expected_basefee_sharing_pctg
78+
);
79+
return false;
80+
}
81+
if proposal_id != expected_proposal_id {
82+
warn!(
83+
"shasta extra_data proposalId mismatch for block {}: got {}, expected {}",
84+
header.number, proposal_id, expected_proposal_id
85+
);
86+
return false;
87+
}
88+
}
89+
90+
// Optional: the parent of the 1st block should belong to the previous proposal.
91+
if expected_proposal_id > 0 {
92+
let parent_header = &input.inputs[0].parent_header;
93+
let Some((_parent_pctg, parent_proposal_id)) =
94+
decode_shasta_extra_data(parent_header.extra_data.as_ref())
95+
else {
96+
warn!(
97+
"failed to decode shasta extra_data for parent of first block {} (parent {})",
98+
input.inputs[0].block.header.number, parent_header.number
99+
);
100+
return false;
101+
};
102+
if parent_proposal_id != expected_proposal_id - 1 {
103+
warn!(
104+
"shasta parent extra_data proposalId mismatch for parent {}: got {}, expected {}",
105+
parent_header.number,
106+
parent_proposal_id,
107+
expected_proposal_id - 1
108+
);
109+
return false;
110+
}
111+
}
112+
113+
true
114+
}
115+
18116
pub(crate) fn valid_anchor_in_normal_proposal(
19117
blocks: &[ProtocolBlockManifest],
20118
last_anchor_block_number: u64,

provers/sgx/prover/src/remote_prover.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ mod tests {
478478
prove: false,
479479
};
480480
let spec_id = taiko_chain_spec
481-
.active_fork(1, 0)
481+
.active_fork(0, 1869396301)
482482
.map_err(|e| ProverError::GuestError(e.to_string()))
483483
.expect("ok");
484484
assert_eq!(spec_id, SpecId::SHASTA);
@@ -494,7 +494,7 @@ mod tests {
494494
assert_eq!(instance_id, 10);
495495

496496
let spec_id = taiko_chain_spec
497-
.active_fork(15, 0)
497+
.active_fork(15, 1869396301)
498498
.map_err(|e| ProverError::GuestError(e.to_string()))
499499
.expect("ok");
500500
assert_eq!(spec_id, SpecId::SHASTA);

0 commit comments

Comments
 (0)