Skip to content

Commit 926247b

Browse files
ilitteriilitteritomip01
authored
refactor(l2): decouple guest program IO (#5314)
**Motivation** Our guest program library is used for both L1 and L2 block proving, and the logic for it is very coupled. A first step towards decoupling this is separating the guest program inputs for L1 and L2. This also improves the UX for users that only care about proving L1 blocks. **Description** Guest program inputs for L1 ```rust pub struct ProgramInput { /// Block to execute pub block: Block, /// database containing all the data necessary to execute pub execution_witness: ExecutionWitness, } ``` Guest program inputs for L2 ```rust pub struct ProgramInput { /// blocks to execute pub blocks: Vec<Block>, /// database containing all the data necessary to execute pub execution_witness: ExecutionWitness, /// value used to calculate base fee pub elasticity_multiplier: u64, /// Configuration for L2 fees used for each block pub fee_configs: Vec<ethrex_common::types::fee_config::FeeConfig>, /// KZG commitment to the blob data #[serde_as(as = "[_; 48]")] pub blob_commitment: ethrex_common::types::blobs_bundle::Commitment, /// KZG opening for a challenge over the blob commitment #[serde_as(as = "[_; 48]")] pub blob_proof: ethrex_common::types::blobs_bundle::Proof, } ``` --------- Co-authored-by: ilitteri <[email protected]> Co-authored-by: Tomás Paradelo <[email protected]>
1 parent 82d4ead commit 926247b

File tree

5 files changed

+121
-64
lines changed

5 files changed

+121
-64
lines changed

crates/l2/prover/src/guest_program/src/execution.rs

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
use crate::input::ProgramInput;
22
use crate::output::ProgramOutput;
3-
use crate::report_cycles;
43

54
use ethrex_blockchain::error::ChainError;
6-
use ethrex_blockchain::{
7-
validate_block, validate_gas_used, validate_receipts_root, validate_requests_hash,
8-
validate_state_root,
9-
};
105
use ethrex_common::types::AccountUpdate;
6+
#[cfg(not(feature = "l2"))]
7+
use ethrex_common::types::ELASTICITY_MULTIPLIER;
118
use ethrex_common::types::block_execution_witness::ExecutionWitness;
129
use ethrex_common::types::fee_config::FeeConfig;
1310
use ethrex_common::types::{
@@ -19,7 +16,7 @@ use ethrex_common::{H256, types::Block};
1916
use ethrex_l2_common::l1_messages::L1Message;
2017
use ethrex_rlp::encode::RLPEncode;
2118
use ethrex_vm::{Evm, EvmError, GuestProgramStateWrapper, VmDatabase};
22-
use std::collections::{BTreeMap, HashMap};
19+
use std::collections::HashMap;
2320

2421
#[cfg(feature = "l2")]
2522
use ethrex_common::types::{
@@ -91,42 +88,52 @@ pub enum StatelessExecutionError {
9188
TryIntoError(#[from] std::num::TryFromIntError),
9289
}
9390

91+
#[cfg(feature = "l2")]
9492
pub fn execution_program(input: ProgramInput) -> Result<ProgramOutput, StatelessExecutionError> {
9593
let ProgramInput {
9694
blocks,
9795
execution_witness,
9896
elasticity_multiplier,
99-
fee_configs: _fee_configs,
100-
#[cfg(feature = "l2")]
97+
fee_configs,
10198
blob_commitment,
102-
#[cfg(feature = "l2")]
10399
blob_proof,
104100
} = input;
105101

106102
let chain_id = execution_witness.chain_config.chain_id;
107103

108-
if cfg!(feature = "l2") {
109-
#[cfg(feature = "l2")]
110-
return stateless_validation_l2(
111-
&blocks,
112-
execution_witness,
113-
elasticity_multiplier,
114-
_fee_configs,
115-
blob_commitment,
116-
blob_proof,
117-
chain_id,
118-
);
119-
}
104+
stateless_validation_l2(
105+
&blocks,
106+
execution_witness,
107+
elasticity_multiplier,
108+
fee_configs,
109+
blob_commitment,
110+
blob_proof,
111+
chain_id,
112+
)
113+
}
114+
115+
#[cfg(not(feature = "l2"))]
116+
pub fn execution_program(input: ProgramInput) -> Result<ProgramOutput, StatelessExecutionError> {
117+
let ProgramInput {
118+
block,
119+
execution_witness,
120+
} = input;
120121

121-
stateless_validation_l1(blocks, execution_witness, elasticity_multiplier, chain_id)
122+
let chain_id = execution_witness.chain_config.chain_id;
123+
124+
stateless_validation_l1(&[block], execution_witness, chain_id)
122125
}
123126

127+
#[cfg(not(feature = "l2"))]
124128
pub fn stateless_validation_l1(
125129
blocks: Vec<Block>,
126130
execution_witness: ExecutionWitness,
127-
elasticity_multiplier: u64,
128131
chain_id: u64,
129132
) -> Result<ProgramOutput, StatelessExecutionError> {
133+
use std::collections::BTreeMap;
134+
135+
use crate::report_cycles;
136+
130137
let guest_program_state: GuestProgramState =
131138
report_cycles("guest_program_state_initialization", || {
132139
execution_witness
@@ -186,7 +193,7 @@ pub fn stateless_validation_l1(
186193
for block in blocks.iter() {
187194
// Validate the block
188195
report_cycles("validate_block", || {
189-
validate_block(
196+
ethrex_blockchain::validate_block(
190197
block,
191198
parent_block_header,
192199
&chain_config,
@@ -227,19 +234,23 @@ pub fn stateless_validation_l1(
227234
}
228235

229236
report_cycles("validate_gas_and_receipts", || {
230-
validate_gas_used(&result.receipts, &block.header)
237+
ethrex_blockchain::validate_gas_used(&result.receipts, &block.header)
231238
.map_err(StatelessExecutionError::GasValidationError)
232239
})?;
233240

234241
report_cycles("validate_receipts_root", || {
235-
validate_receipts_root(&block.header, &result.receipts)
242+
ethrex_blockchain::validate_receipts_root(&block.header, &result.receipts)
236243
.map_err(StatelessExecutionError::ReceiptsRootValidationError)
237244
})?;
238245

239246
// validate_requests_hash doesn't do anything for l2 blocks as this verifies l1 requests (messages, privileged transactions and consolidations)
240247
report_cycles("validate_requests_hash", || {
241-
validate_requests_hash(&block.header, &chain_config, &result.requests)
242-
.map_err(StatelessExecutionError::RequestsRootValidationError)
248+
ethrex_blockchain::validate_requests_hash(
249+
&block.header,
250+
&chain_config,
251+
&result.requests,
252+
)
253+
.map_err(StatelessExecutionError::RequestsRootValidationError)
243254
})?;
244255

245256
non_privileged_count += block.body.transactions.len();
@@ -258,7 +269,7 @@ pub fn stateless_validation_l1(
258269
.ok_or(StatelessExecutionError::EmptyBatchError)?;
259270

260271
report_cycles("validate_state_root", || {
261-
validate_state_root(&last_block.header, final_state_root)
272+
ethrex_blockchain::validate_state_root(&last_block.header, final_state_root)
262273
.map_err(|_chain_err| StatelessExecutionError::InvalidFinalStateTrie)
263274
})?;
264275

@@ -282,7 +293,7 @@ pub fn stateless_validation_l2(
282293
blocks: &[Block],
283294
execution_witness: ExecutionWitness,
284295
elasticity_multiplier: u64,
285-
fee_configs: Option<Vec<FeeConfig>>,
296+
fee_configs: Vec<FeeConfig>,
286297
blob_commitment: Commitment,
287298
blob_proof: Proof,
288299
chain_id: u64,
@@ -314,7 +325,9 @@ pub fn stateless_validation_l2(
314325

315326
// Check blobs are valid
316327
let blob_versioned_hash = if !validium {
317-
let fee_configs = fee_configs.ok_or_else(|| StatelessExecutionError::FeeConfigNotFound)?;
328+
if fee_configs.is_empty() {
329+
return Err(StatelessExecutionError::FeeConfigNotFound);
330+
}
318331
verify_blob(blocks, &fee_configs, blob_commitment, blob_proof)?
319332
} else {
320333
H256::zero()
@@ -344,14 +357,16 @@ fn execute_stateless(
344357
blocks: &[Block],
345358
execution_witness: ExecutionWitness,
346359
elasticity_multiplier: u64,
347-
fee_configs: Option<Vec<FeeConfig>>,
360+
fee_configs: Vec<FeeConfig>,
348361
) -> Result<StatelessResult, StatelessExecutionError> {
349362
let guest_program_state: GuestProgramState = execution_witness
350363
.try_into()
351364
.map_err(StatelessExecutionError::GuestProgramState)?;
352365

353366
#[cfg(feature = "l2")]
354-
let fee_configs = fee_configs.ok_or_else(|| StatelessExecutionError::FeeConfigNotFound)?;
367+
if fee_configs.is_empty() {
368+
return Err(StatelessExecutionError::FeeConfigNotFound);
369+
}
355370

356371
let mut wrapped_db = GuestProgramStateWrapper::new(guest_program_state);
357372
let chain_config = wrapped_db.get_chain_config().map_err(|_| {
@@ -394,7 +409,7 @@ fn execute_stateless(
394409

395410
for (i, block) in blocks.iter().enumerate() {
396411
// Validate the block
397-
validate_block(
412+
ethrex_blockchain::validate_block(
398413
block,
399414
parent_block_header,
400415
&chain_config,
@@ -439,12 +454,12 @@ fn execute_stateless(
439454
non_privileged_count += block.body.transactions.len()
440455
- get_block_privileged_transactions(&block.body.transactions).len();
441456

442-
validate_gas_used(&receipts, &block.header)
457+
ethrex_blockchain::validate_gas_used(&receipts, &block.header)
443458
.map_err(StatelessExecutionError::GasValidationError)?;
444-
validate_receipts_root(&block.header, &receipts)
459+
ethrex_blockchain::validate_receipts_root(&block.header, &receipts)
445460
.map_err(StatelessExecutionError::ReceiptsRootValidationError)?;
446461
// validate_requests_hash doesn't do anything for l2 blocks as this verifies l1 requests (messages, privileged transactions and consolidations)
447-
validate_requests_hash(&block.header, &chain_config, &result.requests)
462+
ethrex_blockchain::validate_requests_hash(&block.header, &chain_config, &result.requests)
448463
.map_err(StatelessExecutionError::RequestsRootValidationError)?;
449464
acc_receipts.push(receipts);
450465

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
use ethrex_common::types::{
2-
Block, block_execution_witness::ExecutionWitness, fee_config::FeeConfig,
3-
};
4-
use rkyv::{Archive, Deserialize as RDeserialize, Serialize as RSerialize};
5-
use serde::{Deserialize, Serialize};
1+
use ethrex_common::types::{Block, block_execution_witness::ExecutionWitness};
62
use serde_with::serde_as;
73

8-
#[cfg(feature = "l2")]
9-
use ethrex_common::types::blobs_bundle;
4+
/// Private input variables passed into the zkVM execution program.
5+
#[cfg(not(feature = "l2"))]
6+
#[serde_as]
7+
#[derive(
8+
serde::Serialize, serde::Deserialize, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Default,
9+
)]
10+
pub struct ProgramInput {
11+
/// Block to execute
12+
pub block: Block,
13+
/// database containing all the data necessary to execute
14+
pub execution_witness: ExecutionWitness,
15+
}
1016

1117
/// Private input variables passed into the zkVM execution program.
18+
#[cfg(feature = "l2")]
1219
#[serde_as]
13-
#[derive(Serialize, Deserialize, RDeserialize, RSerialize, Archive)]
20+
#[derive(
21+
serde::Serialize, serde::Deserialize, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive,
22+
)]
1423
pub struct ProgramInput {
1524
/// blocks to execute
1625
pub blocks: Vec<Block>,
@@ -19,28 +28,25 @@ pub struct ProgramInput {
1928
/// value used to calculate base fee
2029
pub elasticity_multiplier: u64,
2130
/// Configuration for L2 fees used for each block
22-
pub fee_configs: Option<Vec<FeeConfig>>,
23-
#[cfg(feature = "l2")]
31+
pub fee_configs: Vec<ethrex_common::types::fee_config::FeeConfig>,
2432
/// KZG commitment to the blob data
2533
#[serde_as(as = "[_; 48]")]
26-
pub blob_commitment: blobs_bundle::Commitment,
27-
#[cfg(feature = "l2")]
34+
pub blob_commitment: ethrex_common::types::blobs_bundle::Commitment,
2835
/// KZG opening for a challenge over the blob commitment
2936
#[serde_as(as = "[_; 48]")]
30-
pub blob_proof: blobs_bundle::Proof,
37+
pub blob_proof: ethrex_common::types::blobs_bundle::Proof,
3138
}
3239

40+
#[cfg(feature = "l2")]
3341
impl Default for ProgramInput {
3442
fn default() -> Self {
3543
Self {
36-
blocks: Default::default(),
44+
blocks: Vec::default(),
3745
execution_witness: ExecutionWitness::default(),
38-
elasticity_multiplier: Default::default(),
39-
fee_configs: None,
40-
#[cfg(feature = "l2")]
46+
elasticity_multiplier: u64::default(),
47+
fee_configs: Vec::default(),
4148
blob_commitment: [0; 48],
42-
#[cfg(feature = "l2")]
43-
blob_proof: [0u8; 48],
49+
blob_proof: [0; 48],
4450
}
4551
}
4652
}

crates/l2/prover/src/guest_program/src/output.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@ use serde::{Deserialize, Serialize};
33

44
/// Public output variables exposed by the zkVM execution program. Some of these are part of
55
/// the program input.
6+
#[cfg(feature = "l2")]
67
#[derive(Serialize, Deserialize)]
78
pub struct ProgramOutput {
89
/// initial state trie root hash
910
pub initial_state_hash: H256,
1011
/// final state trie root hash
1112
pub final_state_hash: H256,
12-
#[cfg(feature = "l2")]
1313
/// merkle root of all messages in a batch
1414
pub l1messages_merkle_root: H256,
15-
#[cfg(feature = "l2")]
1615
/// hash of all the privileged transactions made in a batch
1716
pub privileged_transactions_hash: H256,
18-
#[cfg(feature = "l2")]
1917
/// blob commitment versioned hash
2018
pub blob_versioned_hash: H256,
2119
/// hash of the last block in a batch
@@ -26,16 +24,14 @@ pub struct ProgramOutput {
2624
pub non_privileged_count: U256,
2725
}
2826

27+
#[cfg(feature = "l2")]
2928
impl ProgramOutput {
3029
pub fn encode(&self) -> Vec<u8> {
3130
[
3231
self.initial_state_hash.to_fixed_bytes(),
3332
self.final_state_hash.to_fixed_bytes(),
34-
#[cfg(feature = "l2")]
3533
self.l1messages_merkle_root.to_fixed_bytes(),
36-
#[cfg(feature = "l2")]
3734
self.privileged_transactions_hash.to_fixed_bytes(),
38-
#[cfg(feature = "l2")]
3935
self.blob_versioned_hash.to_fixed_bytes(),
4036
self.last_block_hash.to_fixed_bytes(),
4137
self.chain_id.to_big_endian(),
@@ -44,3 +40,34 @@ impl ProgramOutput {
4440
.concat()
4541
}
4642
}
43+
44+
/// Public output variables exposed by the zkVM execution program. Some of these are part of
45+
/// the program input.
46+
#[cfg(not(feature = "l2"))]
47+
#[derive(Serialize, Deserialize)]
48+
pub struct ProgramOutput {
49+
/// initial state trie root hash
50+
pub initial_state_hash: H256,
51+
/// final state trie root hash
52+
pub final_state_hash: H256,
53+
/// hash of the last block in a batch
54+
pub last_block_hash: H256,
55+
/// chain_id of the network
56+
pub chain_id: U256,
57+
/// amount of non-privileged transactions
58+
pub non_privileged_count: U256,
59+
}
60+
61+
#[cfg(not(feature = "l2"))]
62+
impl ProgramOutput {
63+
pub fn encode(&self) -> Vec<u8> {
64+
[
65+
self.initial_state_hash.to_fixed_bytes(),
66+
self.final_state_hash.to_fixed_bytes(),
67+
self.last_block_hash.to_fixed_bytes(),
68+
self.chain_id.to_big_endian(),
69+
self.non_privileged_count.to_big_endian(),
70+
]
71+
.concat()
72+
}
73+
}

crates/l2/prover/src/prover.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,23 @@ impl Prover {
123123
Ok(Some(ProverData {
124124
batch_number,
125125
input: ProgramInput {
126+
#[cfg(not(feature = "l2"))]
127+
block: input
128+
.blocks
129+
.first()
130+
.cloned()
131+
.ok_or("Input blocks is empty")?,
132+
#[cfg(feature = "l2")]
126133
blocks: input.blocks,
127134
execution_witness: input.execution_witness,
135+
#[cfg(feature = "l2")]
128136
elasticity_multiplier: input.elasticity_multiplier,
129137
#[cfg(feature = "l2")]
130138
blob_commitment: input.blob_commitment,
131139
#[cfg(feature = "l2")]
132140
blob_proof: input.blob_proof,
133-
fee_configs: Some(input.fee_configs),
141+
#[cfg(feature = "l2")]
142+
fee_configs: input.fee_configs,
134143
},
135144
format,
136145
}))

crates/l2/tee/quote-gen/src/sender.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub async fn get_batch(commit_hash: String) -> Result<(u64, ProgramInput), Strin
3434
blob_commitment: input.blob_commitment,
3535
#[cfg(feature = "l2")]
3636
blob_proof: input.blob_proof,
37-
fee_configs: Some(input.fee_configs),
37+
fee_configs: input.fee_configs,
3838
},
3939
)),
4040
_ => Err("No blocks to prove.".to_owned()),

0 commit comments

Comments
 (0)