Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c8874f8
Move DISCONNECT_ORACLE_QUERY_ID query to basic_system
antoniolocascio Nov 21, 2025
96e2ad3
Define ReadWitnessSource as IOOracle to capture read u32s
antoniolocascio Nov 21, 2025
2c66c55
Define ProverInputBootloader and entrypoint to compute prover input
antoniolocascio Nov 21, 2025
9edc6aa
Generalize modexp oracle to support native execution
antoniolocascio Nov 21, 2025
c445297
Use delegated modexp in prover input run
antoniolocascio Nov 21, 2025
6bb32c6
Add ProverInputResultKeeper
antoniolocascio Nov 21, 2025
a3e3416
Use native run for generate_proof_input
antoniolocascio Nov 21, 2025
124fa65
Fix fuzzer
antoniolocascio Nov 21, 2025
56ed936
Native blob oracle
antoniolocascio Nov 21, 2025
8e1c2ff
Add pubdata header in proving run
antoniolocascio Nov 21, 2025
261e5bf
Actually fix cycle cycle_marker
antoniolocascio Nov 26, 2025
7101370
Return batch output in proving run
antoniolocascio Nov 26, 2025
f4b4639
Add comments to native processors
antoniolocascio Nov 27, 2025
cb5bfc4
Remove pubdata field from BlockOutput
antoniolocascio Nov 27, 2025
feaf0c4
Rename modexp "delegation" to "advice"
antoniolocascio Nov 27, 2025
5b152a6
Fix eth_runner
AntonD3 Dec 3, 2025
1a1146c
Add comment
antoniolocascio Dec 8, 2025
c93a348
Fixes
antoniolocascio Dec 8, 2025
b018680
Fix crypto imports
antoniolocascio Dec 9, 2025
ae20853
Add interop roots storage
antoniolocascio Nov 25, 2025
c8f664b
Add interop roots rolling hash to pi, changes to finish io
antoniolocascio Nov 25, 2025
918163e
Add initial implementation of interop root reporter hook
antoniolocascio Nov 26, 2025
6ce0dd0
Add tests
antoniolocascio Nov 26, 2025
484f01b
Add resource accounting for interop root reporter
antoniolocascio Nov 27, 2025
54e675c
Initial event hooks work
antoniolocascio Dec 2, 2025
1cac1a6
Implement interop roots reporter as an event hook
antoniolocascio Dec 2, 2025
98e5a67
Introduce service transactions
antoniolocascio Dec 9, 2025
9662424
Add helper to encode service txs to api
antoniolocascio Dec 9, 2025
ee46b9c
Add basic tests for service txs
antoniolocascio Dec 9, 2025
169b149
Adapt interop tests to use service tx
antoniolocascio Dec 9, 2025
ab86572
Move interop root importing calldata encoding to api helpers
antoniolocascio Dec 9, 2025
ba17609
fixes
antoniolocascio Dec 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ members = [
"tests/instances/multiblock_batch",
"tests/instances/header",
"tests/instances/evm",
"tests/instances/interop",
"cycle_marker",
"tests/binary_checker",
"supporting_crates/delegated_u256",
Expand Down Expand Up @@ -57,8 +58,8 @@ keywords = ["blockchain", "zksync", "zk", "risc-v"]
categories = ["cryptography"]

[workspace.dependencies]
zksync_os_evm_errors = { version = "0.0.10", default-features = false }
zksync_os_interface = { version = "0.0.10"}
zksync_os_evm_errors = { git = "https://github.com/matter-labs/zksync-os-interface", branch = "alocascio-prover-input-gen", default-features = false }
zksync_os_interface = { git = "https://github.com/matter-labs/zksync-os-interface", branch = "alocascio-prover-input-gen", version = "0.0.10"}

risc_v_simulator = { git = "https://github.com/matter-labs/zksync-airbender", tag = "v0.4.3"}
blake2s_u32 = { git = "https://github.com/matter-labs/zksync-airbender", tag = "v0.4.3"}
Expand Down
3 changes: 3 additions & 0 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ risc_v_simulator = { workspace = true }
zk_ee = { path = "../zk_ee", default-features = false }
crypto = { path = "../crypto", default-features = false }
basic_system = { path = "../basic_system", default-features = false }
basic_bootloader = { path = "../basic_bootloader", default-features = false }
evm_interpreter = { path = "../evm_interpreter", default-features = false }
ruint = { version = "1.12.3", default-features = false, features = ["alloc"] }
alloy = { version = "=1", default-features = false, features = ["eip712", "consensus", "rpc-types", "signer-local", "dyn-abi", "network"] }
zksync_os_interface = { workspace = true }
alloy-sol-types = "1"


[features]
# By default, use the risc_v_simulator with delegation (which is faster).
Expand Down
106 changes: 106 additions & 0 deletions api/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ use alloy::consensus::{EthereumTxEnvelope, SignableTransaction};
use alloy::consensus::{Signed, TxEnvelope, TypedTransaction};
use alloy::dyn_abi::DynSolValue;
use alloy::network::TxSignerSync;
use alloy::primitives::Address;
use alloy::primitives::Signature;
use alloy::rlp::{encode, BufMut, Encodable};
use alloy::rpc::types::TransactionRequest;
use alloy::signers::local::PrivateKeySigner;
use alloy_sol_types::sol;
use alloy_sol_types::SolCall;
use basic_bootloader::bootloader::constants::BOOTLOADER_FORMAL_ADDRESS;
use basic_bootloader::bootloader::transaction::rlp_encoded::transaction_types::service_tx::SERVICE_TX_TYPE;
use basic_system::system_implementation::flat_storage_model::bytecode_padding_len;
use basic_system::system_implementation::flat_storage_model::AccountProperties;
use forward_system::run::PreimageSource;
use ruint::aliases::U256;
use std::alloc::Global;
use std::ops::Add;
use zk_ee::common_structs::interop_root_storage::InteropRoot as StoredInteropRoot;
use zk_ee::execution_environment_type::ExecutionEnvironmentType;
use zk_ee::system::EIP7702_DELEGATION_MARKER;
use zk_ee::utils::Bytes32;
Expand Down Expand Up @@ -235,3 +242,102 @@ pub fn sign_and_encode_transaction_request(
TypedTransaction::Eip4844(_) => panic!("Unsupported tx type"),
}
}

/// Helper wrapper representing the RLP *body* of a service tx:
/// [nonce, gas_limit, to, data]
struct ServiceTxBody<'a> {
nonce: u64,
gas_limit: u64,
to: &'a [u8; 20],
data: &'a [u8],
}

enum ServiceTxField<'b> {
U64(u64),
Bytes(&'b [u8]),
}

impl<'b> Encodable for ServiceTxField<'b> {
fn encode(&self, out: &mut dyn BufMut) {
match self {
ServiceTxField::U64(v) => v.encode(out),
ServiceTxField::Bytes(b) => (*b).encode(out),
}
}
}

impl<'a> Encodable for ServiceTxBody<'a> {
fn encode(&self, out: &mut dyn BufMut) {
let fields = vec![
ServiceTxField::U64(self.nonce),
ServiceTxField::U64(self.gas_limit),
ServiceTxField::Bytes(self.to.as_slice()),
ServiceTxField::Bytes(self.data),
];

fields.encode(out);
}
}

///
/// Encode a service transaction
///
pub fn encode_service_tx(nonce: u64, gas_limit: u64, to: &[u8; 20], data: &[u8]) -> EncodedTx {
let body = ServiceTxBody {
nonce,
gas_limit,
to,
data,
};
let rlp_body = encode(&body);
let mut out = Vec::with_capacity(1 + rlp_body.len());
out.push(SERVICE_TX_TYPE);
out.extend_from_slice(&rlp_body);
let from = Address::from_slice(&BOOTLOADER_FORMAL_ADDRESS.to_be_bytes::<20>());
EncodedTx::Rlp(out, from)
}

///
/// Calldata used by service transactions that import interop roots.
///
/// Constructs the calldata for:
///
/// function addInteropRootsInBatch(InteropRoot[] calldata interopRootsInput);
///
/// where
///
/// struct InteropRoot {
/// uint256 chainId;
/// uint256 blockOrBatchNumber;
/// bytes32[] sides;
/// }
///
pub fn encode_interop_root_import_calldata(interop_roots: Vec<StoredInteropRoot>) -> Vec<u8> {
// Declare sol interface
sol! {
struct InteropRoot {
uint256 chainId;
uint256 blockOrBatchNumber;
bytes32[] sides;
}

function addInteropRootsInBatch(InteropRoot[] calldata interopRootsInput);
}

// Construct calldata
let interop_roots: Vec<InteropRoot> = interop_roots
.into_iter()
.map(|r: StoredInteropRoot| {
let root_b256 = alloy::primitives::B256::from_slice(r.root.as_u8_ref());
InteropRoot {
chainId: r.chain_id,
blockOrBatchNumber: r.block_or_batch_number,
sides: vec![root_b256],
}
})
.collect();
addInteropRootsInBatchCall {
interopRootsInput: interop_roots,
}
.abi_encode()
}
53 changes: 2 additions & 51 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,5 @@
#![feature(allocator_api)]

use std::{path::PathBuf, str::FromStr};

use forward_system::run::{
test_impl::{InMemoryPreimageSource, InMemoryTree},
BlockContext, StorageCommitment,
};
use oracle_provider::ReadWitnessSource;
use zksync_os_interface::traits::TxListSource;
pub mod helpers;

/// Runs the batch, and returns the output (that contains gas usage, transaction status etc.).
pub use forward_system::run::run_block;
use zk_ee::common_structs::da_commitment_scheme::DACommitmentScheme;
use zk_ee::common_structs::ProofData;

/// Runs a block in RISC-V - using zksync_os binary - and returns the
/// witness that can be passed to the prover subsystem.
pub fn run_block_generate_witness(
block_context: BlockContext,
tree: InMemoryTree,
preimage_source: InMemoryPreimageSource,
tx_source: TxListSource,
proof_data: ProofData<StorageCommitment>,
da_commitment_scheme: DACommitmentScheme,
zksync_os_bin_path: &str,
) -> Vec<u32> {
use forward_system::run::*;

let oracle = make_oracle_for_proofs_and_dumps_for_init_data(
block_context,
tree,
preimage_source,
tx_source,
Some(proof_data),
Some(da_commitment_scheme),
false,
);

// We'll wrap the source, to collect all the reads.
let copy_source = ReadWitnessSource::new(oracle);

let items = copy_source.get_read_items();
// By default - enable diagnostics is false (which makes the test run faster).
let path = PathBuf::from_str(zksync_os_bin_path).unwrap();
let output = zksync_os_runner::run(path, None, 1 << 36, copy_source);

// We return 0s in case of failure.
assert_ne!(output, [0u32; 8]);

let result = items.borrow().clone();
result
}
pub use forward_system::run::{generate_proof_input, run_block};
pub mod helpers;
3 changes: 3 additions & 0 deletions basic_bootloader/src/bootloader/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use ruint::aliases::{B160, U256};
pub const SPECIAL_ADDRESS_SPACE_BOUND: u64 = 0x010000;
pub const SPECIAL_ADDRESS_TO_WASM_DEPLOY: B160 = B160::from_limbs([0x9000, 0, 0]);

/// Bootloader's formal address for system-level operations
pub const BOOTLOADER_FORMAL_ADDRESS: B160 = B160::from_limbs([0x8001, 0, 0]);

pub const MAX_TX_LEN_BYTES: usize = 1 << 23;
pub const MAX_TX_LEN_WORDS: usize = MAX_TX_LEN_BYTES / core::mem::size_of::<u32>();

Expand Down
50 changes: 45 additions & 5 deletions basic_bootloader/src/bootloader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use result_keeper::ResultKeeperExt;
use ruint::aliases::*;
use zk_ee::common_structs::MAX_NUMBER_OF_LOGS;
use zk_ee::execution_environment_type::ExecutionEnvironmentType;
use zk_ee::system::errors::internal::InternalError;
use zk_ee::system::tracer::Tracer;
use zk_ee::system::{EthereumLikeTypes, System, SystemTypes};

Expand Down Expand Up @@ -35,7 +36,7 @@ use crate::bootloader::runner::RunnerMemoryBuffers;
use crate::bootloader::transaction_flow::{
BasicTransactionFlow, ExecutionOutput, ExecutionResult, TxProcessingResult,
};
use system_hooks::HooksStorage;
use zk_ee::common_structs::system_hooks::HooksStorage;
use zk_ee::system::*;
use zk_ee::utils::*;

Expand Down Expand Up @@ -125,19 +126,23 @@ where

let mut system_functions = HooksStorage::new_in(system.get_allocator());

system_functions.add_precompiles();
system_hooks::add_precompiles(&mut system_functions);

#[cfg(not(feature = "disable_system_contracts"))]
{
system_functions.add_l1_messenger();
system_functions.add_l2_base_token();
system_functions.add_contract_deployer();
system_hooks::add_l1_messenger(&mut system_functions);
system_hooks::add_l2_base_token(&mut system_functions);
system_hooks::add_contract_deployer(&mut system_functions);
system_hooks::add_interop_root_reporter(&mut system_functions);
}

let mut tx_rolling_hash = [0u8; 32];
let mut l1_to_l2_txs_hasher = crypto::blake2s::Blake2s256::new();

let mut first_tx = true;
// Service blocks are blocks that only contain service transactions.
// Service transactions can only be included in service blocks.
let mut is_service_block = false;
let mut upgrade_tx_hash = Bytes32::zero();
let mut block_gas_used = 0;
let mut block_computational_native_used = 0;
Expand Down Expand Up @@ -217,6 +222,14 @@ where
"Tx execution result = {:?}\n",
&tx_processing_result,
));

// Check for service block invariants
Self::check_for_service_block_invariants(
&mut is_service_block,
first_tx,
tx_processing_result.is_service_tx,
)?;

// Do not update the accumulators yet, we may need to revert the transaction
let next_block_gas_used =
block_gas_used + tx_processing_result.gas_used;
Expand Down Expand Up @@ -400,4 +413,31 @@ where
}
}
}

/// Check the service block invariants:
/// 1. If the first tx is a service tx, then the block is a service block
/// 2. Service transactions can only be processed in service blocks
/// 3. Non-service transactions cannot be processed in service blocks
fn check_for_service_block_invariants(
is_service_block: &mut bool,
is_first_tx: bool,
is_service_tx: bool,
) -> Result<(), InternalError> {
// 1. If the first tx is a service tx, then the block is a service block
if is_first_tx && is_service_tx {
*is_service_block = true;
}
if *is_service_block {
if !is_service_tx {
// 3. Non-service transactions cannot be processed in service blocks
return Err(internal_error!("Non-service tx in service block"));
}
} else {
// 2. Service transactions can only be processed in service blocks
if is_service_tx {
return Err(internal_error!("Service tx in non-service block"));
}
}
Ok(())
}
}
6 changes: 5 additions & 1 deletion basic_bootloader/src/bootloader/process_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use gas_helpers::check_enough_resources_for_pubdata;
use gas_helpers::get_resources_to_charge_for_pubdata;
use gas_helpers::ResourcesForTx;
use metadata::zk_metadata::TxLevelMetadata;
use system_hooks::HooksStorage;
use transaction::charge_keccak;
use zk_ee::common_structs::system_hooks::HooksStorage;
use zk_ee::interface_error;
use zk_ee::internal_error;
use zk_ee::system::errors::cascade::CascadedError;
Expand Down Expand Up @@ -396,6 +396,7 @@ where
tx_hash,
is_l1_tx: is_priority_op,
is_upgrade_tx: !is_priority_op,
is_service_tx: false,
gas_used,
gas_refunded: evm_refund,
computational_native_used,
Expand Down Expand Up @@ -767,11 +768,14 @@ where
.as_str(),
);

let is_service_tx = transaction.is_service();

Ok(TxProcessingResult {
result: execution_result,
tx_hash,
is_l1_tx: false,
is_upgrade_tx: false,
is_service_tx,
gas_used,
gas_refunded: evm_refund,
computational_native_used,
Expand Down
2 changes: 1 addition & 1 deletion basic_bootloader/src/bootloader/run_single_interaction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::bootloader::errors::BootloaderInterfaceError;
use crate::bootloader::runner::{run_till_completion, RunnerMemoryBuffers};
use errors::BootloaderSubsystemError;
use system_hooks::HooksStorage;
use zk_ee::common_structs::system_hooks::HooksStorage;
use zk_ee::system::errors::subsystem::SubsystemError;
use zk_ee::system::errors::{runtime::RuntimeError, system::SystemError};
use zk_ee::system::CallModifier;
Expand Down
18 changes: 15 additions & 3 deletions basic_bootloader/src/bootloader/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::fmt::Write;
use core::mem::MaybeUninit;
use errors::internal::InternalError;
use ruint::aliases::B160;
use system_hooks::*;
use zk_ee::common_structs::system_hooks::HooksStorage;
use zk_ee::common_structs::CalleeAccountProperties;
use zk_ee::error_ctx;
use zk_ee::execution_environment_type::ExecutionEnvironmentType;
Expand Down Expand Up @@ -414,7 +414,13 @@ impl<'external, S: EthereumLikeTypes> ExecutionContext<'_, 'external, S> {
let new_ee_type = new_vm.ee_type();

let mut preemption = new_vm
.start_executing_frame(self.system, external_call_launch_params, heap, tracer)
.start_executing_frame(
self.system,
self.hooks,
external_call_launch_params,
heap,
tracer,
)
.map_err(wrap_error!())?;

// Execute until we get `End` preemption point
Expand Down Expand Up @@ -446,7 +452,13 @@ impl<'external, S: EthereumLikeTypes> ExecutionContext<'_, 'external, S> {
self.callstack_height -= 1;

preemption = new_vm
.continue_after_preemption(self.system, resources_returned, result, tracer)
.continue_after_preemption(
self.system,
self.hooks,
resources_returned,
result,
tracer,
)
.map_err(wrap_error!())?;
}
ExecutionEnvironmentPreemptionPoint::End(CompletedExecution {
Expand Down
Loading
Loading