Skip to content

Commit 09e7db7

Browse files
JereSaloilitteri
andauthored
feat(l2): add validium mode (#2365)
Validium is a [scaling solution](https://ethereum.org/en/developers/docs/scaling/) that enforces integrity of transactions using validity proofs like [ZK-rollups](https://ethereum.org/en/developers/docs/scaling/zk-rollups/), but doesn’t store transaction data on the Ethereum Mainnet. **Description** - Replace EIP 4844 transactions for EIP 1559 - Modify OnChainProposer contract so that it supports validium. It is not the most efficient way of doing it but the simplest. - Now the config.toml has a validium field. Note: I'm not 100% sure about the changes that I made to the OnChainProposer, there may be a mistake in the additions that I made. I will review it though but I still consider worth opening this PR. <!-- Link to issues: Resolves #111, Resolves #222 --> Closes #2313 --------- Co-authored-by: ilitteri <[email protected]> Co-authored-by: Ivan Litteri <[email protected]>
1 parent dab0e88 commit 09e7db7

File tree

8 files changed

+182
-66
lines changed

8 files changed

+182
-66
lines changed

.github/workflows/pr-main_l2.yaml

+32-3
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,15 @@ jobs:
4646
path: /tmp/buildx-cache
4747

4848
integration-test:
49-
# "Integration Test" is a required check, don't change the name
50-
name: Integration Test
49+
name: Integration Test - ${{ matrix.name }}
5150
runs-on: ubuntu-latest
51+
strategy:
52+
matrix:
53+
include:
54+
- name: "Validium"
55+
validium: true
56+
- name: "Vanilla"
57+
validium: false
5258
needs: [docker-bake]
5359
steps:
5460
- name: Checkout sources
@@ -95,7 +101,9 @@ jobs:
95101
cp configs/prover_client_config_example.toml configs/prover_client_config.toml
96102
cp configs/sequencer_config_example.toml configs/sequencer_config.toml
97103
sed -i 's/listen_ip = "127.0.0.1"/listen_ip = "0.0.0.0"/' configs/sequencer_config.toml
98-
104+
if [ "${{ matrix.validium }}" = "true" ]; then
105+
sed -i.bak 's/^validium = false$/validium = true/' configs/sequencer_config.toml && rm configs/sequencer_config.toml.bak
106+
fi
99107
make integration-test
100108
101109
state-diff-test:
@@ -146,3 +154,24 @@ jobs:
146154
sed -i 's/listen_ip = "127.0.0.1"/listen_ip = "0.0.0.0"/' configs/sequencer_config.toml
147155
148156
make state-diff-test
157+
158+
# The purpose of this job is to add it as a required check in GitHub so that we don't have to add every individual job as a required check
159+
all-tests:
160+
# "Integration Test" is a required check, don't change the name
161+
name: Integration Test
162+
runs-on: ubuntu-latest
163+
needs: [integration-test, state-diff-test]
164+
# Make sure this job runs even if the previous jobs failed or were skipped
165+
if: ${{ always() && needs.integration-test.result != 'skipped' && needs.state-diff-test.result != 'skipped' }}
166+
steps:
167+
- name: Check if any job failed
168+
run: |
169+
if [ "${{ needs.integration-test.result }}" != "success" ]; then
170+
echo "Job Integration Tests failed"
171+
exit 1
172+
fi
173+
174+
if [ "${{ needs.state-diff-test.result }}" != "success" ]; then
175+
echo "Job State Reconstruction Tests failed"
176+
exit 1
177+
fi

crates/l2/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ test: ## 🚧 Runs the L2's integration test, run `make init` and in a new termi
202202

203203
integration-test: rm-db-l2 rm-db-l1
204204
# We create an empty .env file simply because if the file
205-
# does not exists, the container fails to write to it.
205+
# does not exist, the container fails to write to it.
206206
touch .env
207207
CI_ETHREX_WORKDIR=${CI_ETHREX_WORKDIR} docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} down
208208
CI_ETHREX_WORKDIR=${CI_ETHREX_WORKDIR} docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} up --detach --build

crates/l2/configs/sequencer_config_example.toml

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ on_chain_proposer_address = "0xea6d04861106c1fb69176d49eeb8de6dd14a9cfe"
3939
commit_time_ms = 5000
4040
# 1 Gwei
4141
arbitrary_base_blob_gas_price = 1000000000
42+
# If set to true, initializes the committer in validium mode
43+
validium = false
4244

4345
[prover_server]
4446
# Address of a funded account that the sequencer will use to send verify txs to the L1.

crates/l2/contracts/deployer.rs

+48-11
Original file line numberDiff line numberDiff line change
@@ -350,13 +350,14 @@ async fn deploy_contracts(
350350
Color::Cyan,
351351
);
352352

353-
let (on_chain_proposer_deployment_tx_hash, on_chain_proposer_address) = deploy_contract(
354-
deployer,
355-
deployer_private_key,
356-
eth_client,
357-
&contracts_path.join("solc_out/OnChainProposer.bin"),
358-
)
359-
.await?;
353+
let (on_chain_proposer_deployment_tx_hash, on_chain_proposer_address) =
354+
deploy_on_chain_proposer(
355+
deployer,
356+
deployer_private_key,
357+
eth_client,
358+
&contracts_path.join("solc_out/OnChainProposer.bin"),
359+
)
360+
.await?;
360361

361362
let msg = format!(
362363
"OnChainProposer:\n\tDeployed at address {}\n\tWith tx hash {}",
@@ -438,12 +439,10 @@ async fn deploy_contract(
438439
contract_path: &Path,
439440
) -> Result<(H256, Address), DeployError> {
440441
let init_code = hex::decode(std::fs::read_to_string(contract_path).map_err(|err| {
441-
DeployError::DecodingError(format!("Failed to read on_chain_proposer_init_code: {err}"))
442+
DeployError::DecodingError(format!("Failed to read contract init code: {err}"))
442443
})?)
443444
.map_err(|err| {
444-
DeployError::DecodingError(format!(
445-
"Failed to decode on_chain_proposer_init_code: {err}"
446-
))
445+
DeployError::DecodingError(format!("Failed to decode contract init code: {err}"))
447446
})?
448447
.into();
449448

@@ -455,6 +454,44 @@ async fn deploy_contract(
455454
Ok((deploy_tx_hash, contract_address))
456455
}
457456

457+
async fn deploy_on_chain_proposer(
458+
deployer: Address,
459+
deployer_private_key: SecretKey,
460+
eth_client: &EthClient,
461+
contract_path: &Path,
462+
) -> Result<(H256, Address), DeployError> {
463+
let mut init_code = hex::decode(std::fs::read_to_string(contract_path).map_err(|err| {
464+
DeployError::DecodingError(format!("Failed to read on_chain_proposer_init_code: {err}"))
465+
})?)
466+
.map_err(|err| {
467+
DeployError::DecodingError(format!(
468+
"Failed to decode on_chain_proposer_init_code: {err}"
469+
))
470+
})?;
471+
472+
let validium: bool = read_env_var("COMMITTER_VALIDIUM")?
473+
.trim()
474+
.parse()
475+
.map_err(|err| DeployError::ParseError(format!("Malformed COMMITTER_VALIDIUM: {err}")))?;
476+
477+
let validium_value = if validium { 1u8 } else { 0u8 };
478+
let encoded_validium = vec![0; 31]
479+
.into_iter()
480+
.chain(std::iter::once(validium_value));
481+
init_code.extend(encoded_validium);
482+
483+
let (deploy_tx_hash, contract_address) = create2_deploy(
484+
deployer,
485+
deployer_private_key,
486+
&init_code.into(),
487+
eth_client,
488+
)
489+
.await
490+
.map_err(DeployError::from)?;
491+
492+
Ok((deploy_tx_hash, contract_address))
493+
}
494+
458495
async fn deploy_bridge(
459496
deployer: Address,
460497
deployer_private_key: SecretKey,

crates/l2/contracts/src/l1/OnChainProposer.sol

+19-7
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
6060
/// @dev Used only in dev mode.
6161
address public constant DEV_MODE = address(0xAA);
6262

63+
/// @notice Indicates whether the contract operates in validium mode.
64+
/// @dev This value is immutable and can only be set during contract deployment.
65+
bool public immutable VALIDIUM;
66+
67+
/// @notice Constructor to initialize the immutable validium value.
68+
/// @param _validium A boolean indicating if the contract operates in validium mode.
69+
constructor(bool _validium) {
70+
VALIDIUM = _validium;
71+
}
72+
6373
modifier onlySequencer() {
6474
require(
6575
authorizedSequencerAddresses[msg.sender],
@@ -149,13 +159,14 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
149159
bytes32 withdrawalsLogsMerkleRoot,
150160
bytes32 processedDepositLogsRollingHash
151161
) external override onlySequencer {
162+
// TODO: Refactor validation
152163
require(
153164
blockNumber == lastCommittedBlock + 1,
154165
"OnChainProposer: blockNumber is not the immediate successor of lastCommittedBlock"
155166
);
156167
require(
157168
blockCommitments[blockNumber].newStateRoot == bytes32(0),
158-
"OnChainProposer: block already committed"
169+
"OnChainProposer: tried to commit an already committed block"
159170
);
160171

161172
// Check if commitment is equivalent to blob's KZG commitment.
@@ -176,13 +187,15 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
176187
withdrawalsLogsMerkleRoot
177188
);
178189
}
190+
179191
blockCommitments[blockNumber] = BlockCommitmentInfo(
180192
newStateRoot,
181193
stateDiffKZGVersionedHash,
182194
processedDepositLogsRollingHash
183195
);
184-
lastCommittedBlock = blockNumber;
185196
emit BlockCommitted(newStateRoot);
197+
198+
lastCommittedBlock = blockNumber;
186199
}
187200

188201
/// @inheritdoc IOnChainProposer
@@ -206,17 +219,16 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
206219
bytes calldata picoPublicValues,
207220
uint256[8] calldata picoProof
208221
) external override onlySequencer {
222+
// TODO: Refactor validation
209223
// TODO: imageid, programvkey and riscvvkey should be constants
210224
// TODO: organize each zkvm proof arguments in their own structs
211225
require(
212226
blockNumber == lastVerifiedBlock + 1,
213227
"OnChainProposer: block already verified"
214228
);
215-
216229
require(
217-
blockCommitments[blockNumber].stateDiffKZGVersionedHash !=
218-
bytes32(0),
219-
"OnChainProposer: block not committed"
230+
blockCommitments[blockNumber].newStateRoot != bytes32(0),
231+
"OnChainProposer: cannot verify an uncommitted block"
220232
);
221233

222234
if (PICOVERIFIER != DEV_MODE) {
@@ -247,6 +259,7 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
247259
}
248260

249261
lastVerifiedBlock = blockNumber;
262+
250263
// The first 2 bytes are the number of deposits.
251264
uint16 deposits_amount = uint16(
252265
bytes2(
@@ -256,7 +269,6 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
256269
if (deposits_amount > 0) {
257270
ICommonBridge(BRIDGE).removePendingDepositLogs(deposits_amount);
258271
}
259-
260272
// Remove previous block commitment as it is no longer needed.
261273
delete blockCommitments[blockNumber - 1];
262274

0 commit comments

Comments
 (0)