-
Notifications
You must be signed in to change notification settings - Fork 138
feat(l2): add timelock to provide exit window #5666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ac5726d
5e341dd
6138e29
a2b631c
06429f5
75968ed
ea206cb
4e2ab15
d390d97
6516788
3545409
6a41d67
ad29f8a
af1f5d7
8a8df36
a5c12c7
9e740c4
f697e23
e4dd641
43589a8
1e6c0b2
89bc0ba
1369951
a16cd29
7edc1e0
f68872f
90d7add
21a4d5d
f8bce2a
fa3c723
3e6b14b
4442465
f3b40dd
da63fe6
1c33ced
2681bb2
a56ece8
5d8747b
97f99c0
0bde3eb
d35c56b
790111f
f6e02d7
0b63415
a02e8e5
44344bc
73b8c1c
c6fab46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -533,17 +533,24 @@ const SEQUENCER_REGISTRY_BYTECODE: &[u8] = include_bytes!(concat!( | |
| "/contracts/solc_out/SequencerRegistry.bytecode" | ||
| )); | ||
|
|
||
| /// Bytecode of the Timelock contract. | ||
| /// This is generated by the [build script](./build.rs). | ||
| const TIMELOCK_BYTECODE: &[u8] = include_bytes!(concat!( | ||
| env!("OUT_DIR"), | ||
| "/contracts/solc_out/Timelock.bytecode" | ||
| )); | ||
|
|
||
| /// Bytecode of the SP1Verifier contract. | ||
| /// This is generated by the [build script](./build.rs). | ||
| const SP1_VERIFIER_BYTECODE: &[u8] = include_bytes!(concat!( | ||
| env!("OUT_DIR"), | ||
| "/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)"; | ||
| const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE: &str = "initialize(bool,address,bool,bool,bool,bool,address,address,address,address,bytes32,bytes32,bytes32,bytes32,address[],uint256)"; | ||
| 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)"; | ||
| const INITIALIZE_TIMELOCK_SIGNATURE: &str = "initialize(uint256,address[],address,address,address)"; | ||
|
|
||
| const INITIALIZE_BRIDGE_ADDRESS_SIGNATURE: &str = "initializeBridgeAddress(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)"; | ||
|
|
@@ -565,6 +572,7 @@ pub struct ContractAddresses { | |
| pub sequencer_registry_address: Address, | ||
| pub aligned_aggregator_address: Address, | ||
| pub router: Option<Address>, | ||
| pub timelock_address: Option<Address>, | ||
| } | ||
|
|
||
| pub async fn deploy_l1_contracts( | ||
|
|
@@ -716,6 +724,41 @@ async fn deploy_contracts( | |
| Default::default() | ||
| }; | ||
|
|
||
| let (timelock_deployment, timelock_address) = if !opts.deploy_based_contracts { | ||
| info!("Deploying Timelock"); | ||
|
|
||
| let timelock_deployment = deploy_with_proxy_from_bytecode_no_wait( | ||
| deployer, | ||
| eth_client, | ||
| TIMELOCK_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?; | ||
|
|
||
| nonce += 2; | ||
|
|
||
| info!( | ||
| "Timelock deployed:\n Proxy -> address={:#x}, tx_hash={:#x}\n Impl -> address={:#x}, tx_hash={:#x}", | ||
| timelock_deployment.proxy_address, | ||
| timelock_deployment.proxy_tx_hash, | ||
| timelock_deployment.implementation_address, | ||
| timelock_deployment.implementation_tx_hash, | ||
| ); | ||
| ( | ||
| Some(timelock_deployment.clone()), | ||
| Some(timelock_deployment.proxy_address), | ||
| ) | ||
| } else { | ||
| (None, None) | ||
| }; | ||
|
|
||
| info!("Deploying OnChainProposer"); | ||
|
|
||
| trace!("Attempting to deploy OnChainProposer contract"); | ||
|
|
@@ -854,13 +897,15 @@ async fn deploy_contracts( | |
| _ => Address::zero(), | ||
| }; | ||
|
|
||
| let tdx_controller_address = | ||
| timelock_address.unwrap_or(on_chain_proposer_deployment.proxy_address); | ||
|
|
||
| // if it's a required proof type, but no address has been specified, deploy it. | ||
| let tdx_verifier_address = match opts.tdx_verifier_address { | ||
| Some(addr) if opts.tdx => addr, | ||
| None if opts.tdx => { | ||
| info!("Deploying TDXVerifier (if tdx_deploy_verifier is true)"); | ||
| let tdx_verifier_address = | ||
| deploy_tdx_contracts(opts, on_chain_proposer_deployment.proxy_address)?; | ||
| let tdx_verifier_address = deploy_tdx_contracts(opts, tdx_controller_address)?; | ||
|
|
||
| info!(address = %format!("{tdx_verifier_address:#x}"), "TDXVerifier deployed"); | ||
| tdx_verifier_address | ||
|
|
@@ -888,7 +933,7 @@ async fn deploy_contracts( | |
| tdx_verifier_address = ?tdx_verifier_address, | ||
| "Contracts deployed" | ||
| ); | ||
| let receipts = vec![ | ||
| let mut receipts = vec![ | ||
| on_chain_proposer_deployment.implementation_tx_hash, | ||
| on_chain_proposer_deployment.proxy_tx_hash, | ||
| bridge_deployment.implementation_tx_hash, | ||
|
|
@@ -900,6 +945,11 @@ async fn deploy_contracts( | |
| router_deployment.proxy_tx_hash, | ||
| ]; | ||
|
|
||
| if let Some(timelock_deployment) = timelock_deployment { | ||
| receipts.push(timelock_deployment.implementation_tx_hash); | ||
| receipts.push(timelock_deployment.proxy_tx_hash); | ||
| } | ||
|
|
||
| Ok(( | ||
| ContractAddresses { | ||
| on_chain_proposer_address: on_chain_proposer_deployment.proxy_address, | ||
|
|
@@ -910,6 +960,7 @@ async fn deploy_contracts( | |
| sequencer_registry_address: sequencer_registry_deployment.proxy_address, | ||
| aligned_aggregator_address, | ||
| router: opts.router.or(Some(router_deployment.proxy_address)), | ||
| timelock_address, | ||
| }, | ||
| receipts, | ||
| )) | ||
|
|
@@ -1033,13 +1084,55 @@ async fn initialize_contracts( | |
| let deployer_address = get_address_from_secret_key(&opts.private_key.secret_bytes()) | ||
| .map_err(DeployerError::InternalError)?; | ||
|
|
||
| if let Some(timelock_address) = contract_addresses.timelock_address { | ||
| info!("Initializing Timelock"); | ||
| let initialize_tx_hash = { | ||
| let deployer = Signer::Local(LocalSigner::new(opts.private_key)); | ||
| let deployer_nonce = eth_client | ||
| .get_nonce(deployer_address, BlockIdentifier::Tag(BlockTag::Pending)) | ||
| .await?; | ||
| let calldata_values = vec![ | ||
| Value::Uint(U256::from(30)), // TODO: Make minDelay parametrizable. For now this is for testing purposes. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we file an issue for this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, it's important. Created #5703 |
||
| Value::Array(vec![ | ||
| // sequencers | ||
| Value::Address(opts.committer_l1_address), | ||
| Value::Address(opts.proof_sender_l1_address), | ||
| ]), | ||
| Value::Address(opts.on_chain_proposer_owner), // owner | ||
| Value::Address(opts.on_chain_proposer_owner), // securityCouncil | ||
| Value::Address(contract_addresses.on_chain_proposer_address), // onChainProposer | ||
| ]; | ||
| let timelock_initialization_calldata = | ||
| encode_calldata(INITIALIZE_TIMELOCK_SIGNATURE, &calldata_values)?; | ||
|
|
||
| initialize_contract_no_wait( | ||
| timelock_address, | ||
| timelock_initialization_calldata, | ||
| &deployer, | ||
| eth_client, | ||
| Overrides { | ||
| nonce: Some(deployer_nonce), | ||
ManuelBilbao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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}"), "Timelock initialized"); | ||
| tx_hashes.push(initialize_tx_hash); | ||
| } else { | ||
| info!("Skipping Timelock initialization (based enabled)"); | ||
| } | ||
|
|
||
| info!("Initializing OnChainProposer"); | ||
|
|
||
| if opts.deploy_based_contracts { | ||
| // Initialize OnChainProposer with Based config and SequencerRegistry | ||
| let calldata_values = vec![ | ||
| Value::Bool(opts.validium), | ||
| Value::Address(deployer_address), | ||
| Value::Address(opts.on_chain_proposer_owner), | ||
| Value::Bool(opts.risc0), | ||
| Value::Bool(opts.sp1), | ||
| Value::Bool(opts.tdx), | ||
|
|
@@ -1054,6 +1147,7 @@ async fn initialize_contracts( | |
| Value::FixedBytes(genesis.compute_state_root().0.to_vec().into()), | ||
| Value::Address(contract_addresses.sequencer_registry_address), | ||
| Value::Uint(genesis.config.chain_id.into()), | ||
| Value::Address(contract_addresses.bridge_address), | ||
| ]; | ||
|
|
||
| trace!(calldata_values = ?calldata_values, "OnChainProposer initialization calldata values"); | ||
|
|
@@ -1149,7 +1243,9 @@ async fn initialize_contracts( | |
| // Initialize only OnChainProposer without Based config | ||
| let calldata_values = vec![ | ||
| Value::Bool(opts.validium), | ||
| Value::Address(deployer_address), | ||
| Value::Address(contract_addresses.timelock_address.ok_or( | ||
| DeployerError::InternalError("Timelock address missing".to_string()), | ||
| )?), | ||
| Value::Bool(opts.risc0), | ||
| Value::Bool(opts.sp1), | ||
| Value::Bool(opts.tdx), | ||
|
|
@@ -1162,11 +1258,8 @@ async fn initialize_contracts( | |
| Value::FixedBytes(risc0_vk), | ||
| Value::FixedBytes(commit_hash.0.to_vec().into()), | ||
| Value::FixedBytes(genesis.compute_state_root().0.to_vec().into()), | ||
| Value::Array(vec![ | ||
| Value::Address(opts.committer_l1_address), | ||
| Value::Address(opts.proof_sender_l1_address), | ||
| ]), | ||
| Value::Uint(genesis.config.chain_id.into()), | ||
| Value::Address(contract_addresses.bridge_address), | ||
| ]; | ||
| trace!(calldata_values = ?calldata_values, "OnChainProposer initialization calldata values"); | ||
| let on_chain_proposer_initialization_calldata = | ||
|
|
@@ -1196,108 +1289,6 @@ async fn initialize_contracts( | |
| tx_hashes.push(initialize_tx_hash); | ||
| } | ||
|
|
||
| let initialize_bridge_address_tx_hash = { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now initialize the bridge in the |
||
| let initializer_nonce = eth_client | ||
| .get_nonce( | ||
| initializer.address(), | ||
| BlockIdentifier::Tag(BlockTag::Pending), | ||
| ) | ||
| .await?; | ||
| let calldata_values = vec![Value::Address(contract_addresses.bridge_address)]; | ||
| let on_chain_proposer_initialization_calldata = | ||
| encode_calldata(INITIALIZE_BRIDGE_ADDRESS_SIGNATURE, &calldata_values)?; | ||
|
|
||
| initialize_contract_no_wait( | ||
| contract_addresses.on_chain_proposer_address, | ||
| on_chain_proposer_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_bridge_address_tx_hash:#x}"), | ||
| "OnChainProposer bridge address initialized" | ||
| ); | ||
|
|
||
| tx_hashes.push(initialize_bridge_address_tx_hash); | ||
|
|
||
| if opts.on_chain_proposer_owner != initializer.address() { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to transfer ownership anymore, we now initialize the ownership correctly in |
||
| let initializer_nonce = eth_client | ||
| .get_nonce( | ||
| initializer.address(), | ||
| BlockIdentifier::Tag(BlockTag::Pending), | ||
| ) | ||
| .await?; | ||
| let transfer_ownership_tx_hash = { | ||
| let owner_transfer_calldata = encode_calldata( | ||
| TRANSFER_OWNERSHIP_SIGNATURE, | ||
| &[Value::Address(opts.on_chain_proposer_owner)], | ||
| )?; | ||
|
|
||
| initialize_contract_no_wait( | ||
| contract_addresses.on_chain_proposer_address, | ||
| owner_transfer_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? | ||
| }; | ||
|
|
||
| tx_hashes.push(transfer_ownership_tx_hash); | ||
|
|
||
| if let Some(owner_pk) = opts.on_chain_proposer_owner_pk { | ||
| let signer = Signer::Local(LocalSigner::new(owner_pk)); | ||
| let owner_nonce = eth_client | ||
| .get_nonce(signer.address(), BlockIdentifier::Tag(BlockTag::Pending)) | ||
| .await?; | ||
| let accept_ownership_calldata = encode_calldata(ACCEPT_OWNERSHIP_SIGNATURE, &[])?; | ||
| let accept_tx = build_generic_tx( | ||
| eth_client, | ||
| TxType::EIP1559, | ||
| contract_addresses.on_chain_proposer_address, | ||
| opts.on_chain_proposer_owner, | ||
| accept_ownership_calldata.into(), | ||
| Overrides { | ||
| nonce: Some(owner_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?; | ||
| let accept_tx_hash = send_generic_transaction(eth_client, accept_tx, &signer).await?; | ||
| tx_hashes.push(accept_tx_hash); | ||
|
|
||
| info!( | ||
| transfer_tx_hash = %format!("{transfer_ownership_tx_hash:#x}"), | ||
| accept_tx_hash = %format!("{accept_tx_hash:#x}"), | ||
| "OnChainProposer ownership transfered" | ||
| ); | ||
| } else { | ||
| info!( | ||
| transfer_tx_hash = %format!("{transfer_ownership_tx_hash:#x}"), | ||
| "OnChainProposer ownership transfered but not accepted yet" | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| info!("Initializing CommonBridge"); | ||
| let initialize_tx_hash = { | ||
| let initializer_nonce = eth_client | ||
|
|
@@ -1581,6 +1572,9 @@ fn write_contract_addresses_to_env( | |
| "ETHREX_COMMITTER_ON_CHAIN_PROPOSER_ADDRESS={:#x}", | ||
| contract_addresses.on_chain_proposer_address | ||
| )?; | ||
| if let Some(timelock_address) = contract_addresses.timelock_address { | ||
| writeln!(writer, "ETHREX_TIMELOCK_ADDRESS={:#x}", timelock_address)?; | ||
| } | ||
| writeln!( | ||
| writer, | ||
| "ETHREX_WATCHER_BRIDGE_ADDRESS={:#x}", | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.