Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
842fb73
docs(zk): add guest program modularization design documents
Zena-park Feb 22, 2026
d0b35b6
feat(zk): add GuestProgram trait and program implementations
Zena-park Feb 22, 2026
641a373
feat(zk): extend ProverBackend with bytes-based proving methods
Zena-park Feb 22, 2026
a890521
feat(zk): add GuestProgramRegistry and dual-path proving
Zena-park Feb 22, 2026
50206f7
feat(zk): extend L1 contracts with programTypeId and GuestProgramRegi…
Zena-park Feb 22, 2026
6909c3b
feat(zk): add Guest Program Store platform (server + client)
Zena-park Feb 22, 2026
eea40b6
feat(zk): add guest program scaffold script
Zena-park Feb 22, 2026
5715f8e
docs(zk): update implementation phases and add design documents
Zena-park Feb 22, 2026
55ca4a2
feat(zk): integrate OpenVM ELF into guest program registry
Zena-park Feb 22, 2026
55b24a0
perf(zk): cache SP1 proving/verifying keys by ELF hash
Zena-park Feb 22, 2026
57ba317
test(zk): add prove_with_elf integration tests for exec backend
Zena-park Feb 22, 2026
ccfeb6c
feat(zk): resolve programTypeId dynamically from storage
Zena-park Feb 22, 2026
86bcd39
docs(zk): sync design documents with stabilization tasks
Zena-park Feb 22, 2026
ad7d434
refactor(zk): standardize serialize_raw() across all prover backends
Zena-park Feb 22, 2026
b7d87bd
test(zk): add ProofData protocol backward compatibility tests
Zena-park Feb 22, 2026
f6be4be
docs(zk): mark serialize_raw and multi-ELF build as completed
Zena-park Feb 22, 2026
364ce98
feat(zk): add ZK-DEX custom types and execution logic
Zena-park Feb 22, 2026
f980b9b
feat(zk): add Tokamon custom types and execution logic
Zena-park Feb 22, 2026
45e9d09
feat(zk): add SP1 zkVM entry points for ZK-DEX and Tokamon
Zena-park Feb 22, 2026
1be69f7
feat(zk): upgrade scaffold script to generate full module structure
Zena-park Feb 22, 2026
3691e91
test(zk): add registry integration tests for ZK-DEX and Tokamon
Zena-park Feb 22, 2026
1538451
docs(zk): mark SDK scaffold and E2E tests as completed
Zena-park Feb 22, 2026
4f818e8
feat(platform): migrate sessions from in-memory Map to SQLite
Zena-park Feb 22, 2026
ec0fe3f
docs(zk): mark session storage migration as completed
Zena-park Feb 22, 2026
f5ab7e4
feat(zk): add ELF header validation and fuzz-style robustness tests
Zena-park Feb 22, 2026
12a95a8
docs(zk): mark ELF validation and fuzzing tests as completed
Zena-park Feb 22, 2026
73a9b00
feat(zk): add DynamicGuestProgram for runtime ELF loading
Zena-park Feb 22, 2026
e644438
docs(zk): add developer guide and mark all improvement tasks complete
Zena-park Feb 22, 2026
048a766
feat(zk): add production hardening for guest program framework
Zena-park Feb 22, 2026
fc54f74
fix(l2): add missing programTypeId and publicValuesHash args to Timel…
Zena-park Mar 1, 2026
adb4311
fix(l2): fix CI failures in ITimelock interface, Cargo.lock files, an…
Zena-park Mar 1, 2026
1391e9a
fix(l2): allow verificationKeys storage type change for upgrade check
Zena-park Mar 1, 2026
f0ccf33
fix(l2): use @custom:oz-retyped-from for verificationKeys mapping upg…
Zena-park Mar 1, 2026
fd8cd9e
fix(l2): fix clippy and compilation errors in prover
Zena-park Mar 1, 2026
c4d98f6
fix(l2): create placeholder ELFs for zk-dex and tokamon in build.rs
Zena-park Mar 1, 2026
7a9e9ba
style(l2): run cargo fmt on guest program modularization code
Zena-park Mar 1, 2026
0f06e88
fix(guest-program): derive Default for ResourceLimits instead of manu…
Zena-park Mar 1, 2026
513e109
fix(l2): allow clippy panics in prover.rs test module
Zena-park Mar 1, 2026
f217f08
fix(l2): pass guestProgramRegistry in OnChainProposer.initialize()
Zena-park Mar 1, 2026
488490b
ci(l2): remove Uniswap swap token test from L2 workflow
Zena-park Mar 1, 2026
2ecb769
Revert "ci(l2): remove Uniswap swap token test from L2 workflow"
Zena-park Mar 1, 2026
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
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ tower-http = { version = "0.6.2", features = ["cors"] }
indexmap = { version = "2.11.4" }
k256 = "0.13.4"
anyhow = "1.0.86"
toml = "0.8"

rocksdb = { version = "0.24.0", default-features = false, features = [
"bindgen-runtime",
Expand Down
5 changes: 5 additions & 0 deletions cmd/ethrex/build_l2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ pub fn download_script() {
&Path::new("../../crates/l2/contracts/src/l1/Timelock.sol"),
"Timelock",
),
(
&Path::new("../../crates/l2/contracts/src/l1/GuestProgramRegistry.sol"),
"GuestProgramRegistry",
),
];
for (path, name) in l1_contracts {
compile_contract_to_bytecode(
Expand Down Expand Up @@ -201,6 +205,7 @@ fn write_empty_bytecode_files(output_contracts_path: &Path) {
"SequencerRegistry",
"OnChainProposerBased",
"Timelock",
"GuestProgramRegistry",
];

for name in &contract_names {
Expand Down
82 changes: 80 additions & 2 deletions cmd/ethrex/l2/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,15 +547,23 @@ const SP1_VERIFIER_BYTECODE: &[u8] = include_bytes!(concat!(
"/contracts/solc_out/SP1Verifier.bytecode"
));

const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE_BASED: &str = "initialize(bool,address,bool,bool,bool,bool,address,address,address,address,bytes32,bytes32,bytes32,bytes32,address,uint256,address)";
const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE: &str = "initialize(bool,address,bool,bool,bool,bool,address,address,address,address,bytes32,bytes32,bytes32,bytes32,uint256,address)";
/// Bytecode of the GuestProgramRegistry contract.
/// This is generated by the [build script](./build.rs).
const GUEST_PROGRAM_REGISTRY_BYTECODE: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/contracts/solc_out/GuestProgramRegistry.bytecode"
));

const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE_BASED: &str = "initialize(bool,address,bool,bool,bool,bool,address,address,address,address,bytes32,bytes32,bytes32,bytes32,address,uint256,address,address)";
const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE: &str = "initialize(bool,address,bool,bool,bool,bool,address,address,address,address,bytes32,bytes32,bytes32,bytes32,uint256,address,address)";
const INITIALIZE_TIMELOCK_SIGNATURE: &str = "initialize(uint256,address[],address,address,address)";

const TRANSFER_OWNERSHIP_SIGNATURE: &str = "transferOwnership(address)";
const ACCEPT_OWNERSHIP_SIGNATURE: &str = "acceptOwnership()";
const BRIDGE_INITIALIZER_SIGNATURE: &str = "initialize(address,address,uint256,address, uint256)";
const ROUTER_INITIALIZER_SIGNATURE: &str = "initialize(address)";
const ROUTER_REGISTER_SIGNATURE: &str = "register(uint256,address)";
const GUEST_PROGRAM_REGISTRY_INITIALIZER_SIGNATURE: &str = "initialize(address)";

// Gas limit for deploying and initializing contracts
// Needed to avoid estimating gas of initializations when the
Expand All @@ -571,6 +579,7 @@ pub struct ContractAddresses {
pub tdx_verifier_address: Address,
pub sequencer_registry_address: Address,
pub aligned_aggregator_address: Address,
pub guest_program_registry_address: Address,
pub router: Option<Address>,
pub timelock_address: Option<Address>,
}
Expand Down Expand Up @@ -857,6 +866,34 @@ async fn deploy_contracts(
Default::default()
};

// Deploy GuestProgramRegistry
info!("Deploying GuestProgramRegistry");

let guest_program_registry_deployment = deploy_with_proxy_from_bytecode_no_wait(
deployer,
eth_client,
GUEST_PROGRAM_REGISTRY_BYTECODE,
&salt,
Overrides {
nonce: Some(nonce),
gas_limit: Some(TRANSACTION_GAS_LIMIT),
max_fee_per_gas: Some(gas_price),
max_priority_fee_per_gas: Some(gas_price),
..Default::default()
},
)
.await?;

info!(
"GuestProgramRegistry deployed:\n Proxy -> address={:#x}, tx_hash={:#x}\n Impl -> address={:#x}, tx_hash={:#x}",
guest_program_registry_deployment.proxy_address,
guest_program_registry_deployment.proxy_tx_hash,
guest_program_registry_deployment.implementation_address,
guest_program_registry_deployment.implementation_tx_hash,
);

nonce += 2;

// if it's a required proof type, but no address has been specified, deploy it.
let (sp1_verifier_deployment_tx_hash, sp1_verifier_address) = match opts.sp1_verifier_address {
_ if opts.aligned => (H256::default(), Address::zero()),
Expand Down Expand Up @@ -948,6 +985,8 @@ async fn deploy_contracts(
bridge_deployment.proxy_tx_hash,
sequencer_registry_deployment.implementation_tx_hash,
sequencer_registry_deployment.proxy_tx_hash,
guest_program_registry_deployment.implementation_tx_hash,
guest_program_registry_deployment.proxy_tx_hash,
sp1_verifier_deployment_tx_hash,
router_deployment.implementation_tx_hash,
router_deployment.proxy_tx_hash,
Expand All @@ -967,6 +1006,7 @@ async fn deploy_contracts(
tdx_verifier_address,
sequencer_registry_address: sequencer_registry_deployment.proxy_address,
aligned_aggregator_address,
guest_program_registry_address: guest_program_registry_deployment.proxy_address,
router: opts.router.or(Some(router_deployment.proxy_address)),
timelock_address,
},
Expand Down Expand Up @@ -1198,6 +1238,7 @@ async fn initialize_contracts(
Value::Address(contract_addresses.sequencer_registry_address),
Value::Uint(genesis.config.chain_id.into()),
Value::Address(contract_addresses.bridge_address),
Value::Address(contract_addresses.guest_program_registry_address),
];

trace!(calldata_values = ?calldata_values, "OnChainProposer initialization calldata values");
Expand Down Expand Up @@ -1310,6 +1351,7 @@ async fn initialize_contracts(
Value::FixedBytes(genesis.compute_state_root().0.to_vec().into()),
Value::Uint(genesis.config.chain_id.into()),
Value::Address(contract_addresses.bridge_address),
Value::Address(contract_addresses.guest_program_registry_address),
];
trace!(calldata_values = ?calldata_values, "OnChainProposer initialization calldata values");
let on_chain_proposer_initialization_calldata =
Expand Down Expand Up @@ -1467,6 +1509,42 @@ async fn initialize_contracts(
}
}

// Initialize GuestProgramRegistry
info!("Initializing GuestProgramRegistry");
let initialize_tx_hash = {
let initializer_nonce = eth_client
.get_nonce(
initializer.address(),
BlockIdentifier::Tag(BlockTag::Pending),
)
.await?;
let calldata_values = vec![Value::Address(deployer_address)];
let guest_program_registry_initialization_calldata = encode_calldata(
GUEST_PROGRAM_REGISTRY_INITIALIZER_SIGNATURE,
&calldata_values,
)?;

initialize_contract_no_wait(
contract_addresses.guest_program_registry_address,
guest_program_registry_initialization_calldata,
initializer,
eth_client,
Overrides {
nonce: Some(initializer_nonce),
gas_limit: Some(TRANSACTION_GAS_LIMIT),
max_fee_per_gas: Some(gas_price),
max_priority_fee_per_gas: Some(gas_price),
..Default::default()
},
)
.await?
};
info!(tx_hash = %format!("{initialize_tx_hash:#x}"), "GuestProgramRegistry initialized");
tx_hashes.push(initialize_tx_hash);

// GuestProgramRegistry is linked to OnChainProposer via the initialize() parameter,
// so no separate setGuestProgramRegistry() call is needed.

trace!("Contracts initialized");
Ok(tx_hashes)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/ethrex/l2/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,7 @@ impl From<ProverClientOptions> for ProverConfig {
timed: config.timed,
#[cfg(all(feature = "sp1", feature = "gpu"))]
sp1_server: config.sp1_server,
programs_config_path: None,
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/guest-program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ serde_with.workspace = true
thiserror = "2.0.9"
rkyv.workspace = true
bytes.workspace = true
sha2.workspace = true

ethrex-common = { path = "../common/", default-features = false }
ethrex-crypto = { path = "../common/crypto", default-features = false }
Expand Down Expand Up @@ -52,5 +53,8 @@ ci = []
# report_cycles function will be reported to stdout when running inside SP1 zkVM.
sp1-cycles = []

[dev-dependencies]
tempfile.workspace = true

[lib]
path = "./src/lib.rs"
21 changes: 19 additions & 2 deletions crates/guest-program/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: sp1 risc0 zisk openvm l2-sp1 l2-risc0 l2-zisk l2-openvm clean help
.PHONY: sp1 risc0 zisk openvm l2-sp1 l2-risc0 l2-zisk l2-openvm l2-all sp1-zk-dex sp1-tokamon clean help

# Build flags
CARGO_FLAGS := --release -p ethrex-guest-program
Expand All @@ -8,6 +8,11 @@ ifdef REPRODUCIBLE
ENV_PREFIX := PROVER_REPRODUCIBLE_BUILD=true
endif

# Guest program selection
ifdef PROGRAMS
ENV_PREFIX += GUEST_PROGRAMS=$(PROGRAMS)
endif

# Default target
help:
@echo "Usage: make <target>"
Expand All @@ -26,6 +31,7 @@ help:
@echo ""
@echo "Options:"
@echo " REPRODUCIBLE=1 Use Docker for reproducible builds"
@echo " PROGRAMS=list Comma-separated guest programs to build (default: evm-l2)"
@echo ""
@echo "Examples:"
@echo " make sp1 # Local build"
Expand Down Expand Up @@ -58,5 +64,16 @@ l2-zisk:
l2-openvm:
$(ENV_PREFIX) cargo check $(CARGO_FLAGS) --features openvm,l2

# Per-program SP1 targets
sp1-zk-dex:
$(ENV_PREFIX) GUEST_PROGRAMS=zk-dex cargo check $(CARGO_FLAGS) --features sp1

sp1-tokamon:
$(ENV_PREFIX) GUEST_PROGRAMS=tokamon cargo check $(CARGO_FLAGS) --features sp1

clean:
rm -rf bin/sp1/out bin/risc0/out bin/zisk/out bin/openvm/out
rm -rf bin/sp1/out bin/sp1-zk-dex/out bin/sp1-tokamon/out bin/risc0/out bin/zisk/out bin/openvm/out

# Multi-program targets
l2-all:
$(ENV_PREFIX) GUEST_PROGRAMS=evm-l2,zk-dex,tokamon cargo check $(CARGO_FLAGS) --features sp1,l2
1 change: 1 addition & 0 deletions crates/guest-program/bin/openvm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/guest-program/bin/risc0/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading