Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4c526a0
start with implementation
JereSalo Mar 31, 2025
64965fe
add validium option to cli
JereSalo Mar 31, 2025
bf26fdf
add some things for validium to work
JereSalo Mar 31, 2025
f3c2e0f
make progress
JereSalo Mar 31, 2025
0e60588
nits
JereSalo Mar 31, 2025
b32f1f7
clippy lint and change comment
JereSalo Mar 31, 2025
85fd749
move state_diff call into if
JereSalo Apr 9, 2025
37e106d
remove validium wrong checks
JereSalo Apr 9, 2025
a9b8a9e
change l2 CI to test validium mode too
JereSalo Apr 9, 2025
63be86c
merge main
JereSalo Apr 11, 2025
a1cfadd
restore file from main
JereSalo Apr 11, 2025
1fad6a9
update sequencer configs with validium attribute and update integrati…
JereSalo Apr 11, 2025
e5d06c3
Merge branch 'main' into l2/add_validium_mode
JereSalo Apr 11, 2025
fefaf04
Merge branch 'main' of github.com:lambdaclass/ethrex into l2/add_vali…
ilitteri Apr 21, 2025
5718c72
Unify steps
ilitteri Apr 21, 2025
698e31d
Merge branch 'main' into l2/add_validium_mode
ilitteri Apr 22, 2025
06a31c1
Fix CI
ilitteri Apr 23, 2025
73d044d
Update .github/workflows/pr-main_l2.yaml
ilitteri Apr 23, 2025
501d964
Merge branch 'main' of github.com:lambdaclass/ethrex into l2/add_vali…
ilitteri Apr 24, 2025
c686fd1
Merge leftover
ilitteri Apr 24, 2025
ddddca3
Reject uncommitted blocks in Validium mode
ilitteri Apr 24, 2025
4a1fede
Add TODOs
ilitteri Apr 24, 2025
62df9bc
Update .github/workflows/pr-main_l2.yaml
ilitteri Apr 24, 2025
232c71d
Update .github/workflows/pr-main_l2.yaml
ilitteri Apr 24, 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
37 changes: 33 additions & 4 deletions .github/workflows/pr-main_l2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,16 @@ jobs:
name: buildx-cache
path: /tmp/buildx-cache

integration-test:
# "Integration Test" is a required check, don't change the name
name: Integration Test
test:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't have unit tests? Because generally we have "Test" for unit tests and "Integration Test" for integration tests. Both are required checks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we don't, I'll rollback the name change.

name: Integration Test - ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: "Validium"
validium: true
- name: "Vanilla"
validium: false
needs: [docker-bake]
steps:
- name: Checkout sources
Expand Down Expand Up @@ -95,7 +101,9 @@ jobs:
cp configs/prover_client_config_example.toml configs/prover_client_config.toml
cp configs/sequencer_config_example.toml configs/sequencer_config.toml
sed -i 's/listen_ip = "127.0.0.1"/listen_ip = "0.0.0.0"/' configs/sequencer_config.toml

if [ "${{ matrix.validium }}" = "true" ]; then
sed -i.bak 's/^validium = false$/validium = true/' configs/sequencer_config.toml && rm configs/sequencer_config.toml.bak
fi
make integration-test

state-diff-test:
Expand Down Expand Up @@ -146,3 +154,24 @@ jobs:
sed -i 's/listen_ip = "127.0.0.1"/listen_ip = "0.0.0.0"/' configs/sequencer_config.toml

make state-diff-test

# 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
all-tests:
# "Integration Test" is a required check, don't change the name
name: Integration Test
runs-on: ubuntu-latest
needs: [test, state-diff-test]
# Make sure this job runs even if the previous jobs failed or were skipped
if: ${{ always() && needs.test.result != 'skipped' && needs.state-diff-test.result != 'skipped' }}
steps:
- name: Check if any job failed
run: |
if [ "${{ needs.test.result }}" != "success" ]; then
echo "Job Integration Tests failed"
exit 1
fi

if [ "${{ needs.state-diff-test.result }}" != "success" ]; then
echo "Job State Reconstruction Tests failed"
exit 1
fi
2 changes: 1 addition & 1 deletion crates/l2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ test: ## 🚧 Runs the L2's integration test, run `make init` and in a new termi

integration-test: rm-db-l2 rm-db-l1
# We create an empty .env file simply because if the file
# does not exists, the container fails to write to it.
# does not exist, the container fails to write to it.
touch .env
CI_ETHREX_WORKDIR=${CI_ETHREX_WORKDIR} docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} down
CI_ETHREX_WORKDIR=${CI_ETHREX_WORKDIR} docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} up --detach --build
Expand Down
2 changes: 2 additions & 0 deletions crates/l2/configs/sequencer_config_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ on_chain_proposer_address = "0xea6d04861106c1fb69176d49eeb8de6dd14a9cfe"
commit_time_ms = 5000
# 1 Gwei
arbitrary_base_blob_gas_price = 1000000000
# If set to true, initializes the committer in validium mode
validium = false

[prover_server]
# Address of a funded account that the sequencer will use to send verify txs to the L1.
Expand Down
59 changes: 48 additions & 11 deletions crates/l2/contracts/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,14 @@ async fn deploy_contracts(
Color::Cyan,
);

let (on_chain_proposer_deployment_tx_hash, on_chain_proposer_address) = deploy_contract(
deployer,
deployer_private_key,
eth_client,
&contracts_path.join("solc_out/OnChainProposer.bin"),
)
.await?;
let (on_chain_proposer_deployment_tx_hash, on_chain_proposer_address) =
deploy_on_chain_proposer(
deployer,
deployer_private_key,
eth_client,
&contracts_path.join("solc_out/OnChainProposer.bin"),
)
.await?;

let msg = format!(
"OnChainProposer:\n\tDeployed at address {}\n\tWith tx hash {}",
Expand Down Expand Up @@ -438,12 +439,10 @@ async fn deploy_contract(
contract_path: &Path,
) -> Result<(H256, Address), DeployError> {
let init_code = hex::decode(std::fs::read_to_string(contract_path).map_err(|err| {
DeployError::DecodingError(format!("Failed to read on_chain_proposer_init_code: {err}"))
DeployError::DecodingError(format!("Failed to read contract init code: {err}"))
})?)
.map_err(|err| {
DeployError::DecodingError(format!(
"Failed to decode on_chain_proposer_init_code: {err}"
))
DeployError::DecodingError(format!("Failed to decode contract init code: {err}"))
})?
.into();

Expand All @@ -455,6 +454,44 @@ async fn deploy_contract(
Ok((deploy_tx_hash, contract_address))
}

async fn deploy_on_chain_proposer(
deployer: Address,
deployer_private_key: SecretKey,
eth_client: &EthClient,
contract_path: &Path,
) -> Result<(H256, Address), DeployError> {
let mut init_code = hex::decode(std::fs::read_to_string(contract_path).map_err(|err| {
DeployError::DecodingError(format!("Failed to read on_chain_proposer_init_code: {err}"))
})?)
.map_err(|err| {
DeployError::DecodingError(format!(
"Failed to decode on_chain_proposer_init_code: {err}"
))
})?;

let validium: bool = read_env_var("COMMITTER_VALIDIUM")?
.trim()
.parse()
.map_err(|err| DeployError::ParseError(format!("Malformed COMMITTER_VALIDIUM: {err}")))?;

let validium_value = if validium { 1u8 } else { 0u8 };
let encoded_validium = vec![0; 31]
.into_iter()
.chain(std::iter::once(validium_value));
init_code.extend(encoded_validium);

let (deploy_tx_hash, contract_address) = create2_deploy(
deployer,
deployer_private_key,
&init_code.into(),
eth_client,
)
.await
.map_err(DeployError::from)?;

Ok((deploy_tx_hash, contract_address))
}

async fn deploy_bridge(
deployer: Address,
deployer_private_key: SecretKey,
Expand Down
26 changes: 19 additions & 7 deletions crates/l2/contracts/src/l1/OnChainProposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
/// @dev Used only in dev mode.
address public constant DEV_MODE = address(0xAA);

/// @notice Indicates whether the contract operates in validium mode.
/// @dev This value is immutable and can only be set during contract deployment.
bool public immutable VALIDIUM;

/// @notice Constructor to initialize the immutable validium value.
/// @param _validium A boolean indicating if the contract operates in validium mode.
constructor(bool _validium) {
VALIDIUM = _validium;
}

modifier onlySequencer() {
require(
authorizedSequencerAddresses[msg.sender],
Expand Down Expand Up @@ -149,13 +159,14 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
bytes32 withdrawalsLogsMerkleRoot,
bytes32 processedDepositLogsRollingHash
) external override onlySequencer {
// TODO: Refactor validation
require(
blockNumber == lastCommittedBlock + 1,
"OnChainProposer: blockNumber is not the immediate successor of lastCommittedBlock"
);
require(
blockCommitments[blockNumber].newStateRoot == bytes32(0),
"OnChainProposer: block already committed"
"OnChainProposer: tried to commit an already committed block"
);

// Check if commitment is equivalent to blob's KZG commitment.
Expand All @@ -176,13 +187,15 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
withdrawalsLogsMerkleRoot
);
}

blockCommitments[blockNumber] = BlockCommitmentInfo(
newStateRoot,
stateDiffKZGVersionedHash,
processedDepositLogsRollingHash
);
lastCommittedBlock = blockNumber;
emit BlockCommitted(newStateRoot);

lastCommittedBlock = blockNumber;
}

/// @inheritdoc IOnChainProposer
Expand All @@ -206,17 +219,16 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
bytes calldata picoPublicValues,
uint256[8] calldata picoProof
) external override onlySequencer {
// TODO: Refactor validation
// TODO: imageid, programvkey and riscvvkey should be constants
// TODO: organize each zkvm proof arguments in their own structs
require(
blockNumber == lastVerifiedBlock + 1,
"OnChainProposer: block already verified"
);

require(
blockCommitments[blockNumber].stateDiffKZGVersionedHash !=
bytes32(0),
"OnChainProposer: block not committed"
blockCommitments[blockNumber].newStateRoot != bytes32(0),
"OnChainProposer: cannot verify an uncommitted block"
);

if (PICOVERIFIER != DEV_MODE) {
Expand Down Expand Up @@ -247,6 +259,7 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
}

lastVerifiedBlock = blockNumber;

// The first 2 bytes are the number of deposits.
uint16 deposits_amount = uint16(
bytes2(
Expand All @@ -256,7 +269,6 @@ contract OnChainProposer is IOnChainProposer, ReentrancyGuard {
if (deposits_amount > 0) {
ICommonBridge(BRIDGE).removePendingDepositLogs(deposits_amount);
}

// Remove previous block commitment as it is no longer needed.
delete blockCommitments[blockNumber - 1];

Expand Down
Loading
Loading