Skip to content
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

feat: Evm Equivalence #2929

Closed
wants to merge 45 commits into from

Conversation

jrchatruc
Copy link
Contributor

@jrchatruc jrchatruc commented Sep 19, 2024

What ❔

This PR contains the rest of the changes required for EVM Equivalence. Changes have been made to make sure it can be merged to main currently without having to point to an era-contracts branch containing the EVM interpreter and the EVM Gas Manager. The changes required for this are all here. Also, the evm simulator hash and bytecode are currently set to be the same as the default AA contract as otherwise things will not work until the evm interpreter is present in the era-contracts branch.

The EVM equivalence changes are:

  • A use_evm_simulator (defaulted to false) config value to set whether the zk stack is initialized with the evm equivalence feature or not. Most changes below are conditional on whether this flag is set or not.
  • A new Postgres migration is added with an evm_simulator_code_hash column added to the following tables:
    • protocol_versions
    • l1_batches
    • miniblocks
      The corresponding dal modules were updated to account for these new columns.
  • The EvmInterpreter is added as a base system contract.
  • The decommiter present in the vm_latest now has separate hash calculations for each version, ZKEvm and EVM. In particular the length of the pre-image differs (EVM is in bytes, zkEVM in words, i.e. 32 bytes)
  • An EvmDeployTracer is added as another default tracer. Its purpose is to add EVM contracts to the decommiter once an evm deployment ends succesfully.
  • For the vm_latest, the TransactionData now checks if the address is absent and in the afirmative case it writes the corresponding flag on the reserved bit to signal an EVM contract deployment.
  • The decommitment processor for the vm_latest now has the hash and code of the EVM interpreter.
  • The EthTxAggregator now adds the call to the getters of the hash and code of the evm interpreter to the multicall.
  • The main_executor calculates a new_known_factory_deps with all the new known EVM bytecodes with the help of the decommiter. These are then added to the regular factory deps.

Why ❔

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • Code has been formatted via zk fmt and zk lint.

IAvecilla and others added 29 commits September 11, 2024 12:48
* Add evm simulator as optional for the server

* Changes to make it work against era-contracts `main`

* fmt

* Calculate bytecode of evm gas manager only if evm simulator flag is on

* Add multicall for get evm simulator bytecode hash

* Use bytecode of default account for the evm simulator if not present

* Remove unnecessary comments

* Use bytecode of default account for evm simulator in gas test sc

* Remove tests for the evm simulator

---------

Co-authored-by: Javier Chatruc <[email protected]>
@@ -92,7 +93,7 @@ fn test_nonce_holder() {
run_nonce_test(
1u32,
NonceHolderTestMode::SetValueUnderNonce,
Some("Previous nonce has not been used".to_string()),
Some("Error function_selector = 0x13595475, data = 0x13595475".to_string()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need these changes?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not really. These changes originate from this PR, which includes protocol defense updates. We needed them for the CI to pass before, but we can remove them now and check if they're still necessary.

@@ -62,6 +62,12 @@ impl From<Transaction> for TransactionData {
U256::zero()
};

let should_deploy_contract = if execute_tx.execute.contract_address.is_none() {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should do it only if EVM-eq is enabled

Copy link
Contributor

Choose a reason for hiding this comment

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

We can add the check to ensure it only happens with EVM equivalence enabled, but it might not be necessary due to the restriction added here. Given this, no transaction should have a None contract address unless the EVM is enabled.

Copy link
Contributor

@slowli slowli left a comment

Choose a reason for hiding this comment

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

I'd consider this way too dirty for even the local deployment, but that may be just me.

hash_bytecode(&evm_simulator_bytecode),
)
} else {
(bytecode.clone(), hash)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is blatantly incorrect; this is not an EVM simulator bytecode / hash. Why can't the EVM bytecode be defined unconditionally correctly as above?

Comment on lines +18 to +19
zksync_config.workspace = true
zksync_env_config.workspace = true
Copy link
Contributor

Choose a reason for hiding this comment

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

This breaks abstraction boundaries; node configuration is downstream of system constants, not the other way around. If the EVM simulator is loaded unconditionally (which I don't see any harm in, although I'm not a domain expert), it wouldn't be necessary. If this doesn't work for some reason, it would make sense to pass the "EVM simulator enabled" flag directly instead of parsing it from env.

Comment on lines +141 to +142
#[deprecated(note = "Use GenesisConfig::evm_simulator_hash instead")]
pub evm_simulator_hash: Option<H256>,
Copy link
Contributor

Choose a reason for hiding this comment

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

??? Why create an immediately deprecated field?

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to be deprecated for other libraries to access the hashes of the base system contracts, but it is still used to create the GenesisConfig, which is loaded when running a zk server.

@@ -0,0 +1,7 @@
use serde::Deserialize;

/// Configuration for the use evm simulator
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I'm (obviously) not a native English speaker, but this doesn't look parseable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed

Comment on lines +15 to +16
zksync_config.workspace = true
zksync_env_config.workspace = true
Copy link
Contributor

Choose a reason for hiding this comment

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

Same concern as for the system constants generator: this breaks abstraction boundaries and doesn't work in the general case (there's no guarantee that code from this crate will necessarily execute in the node context).

.unwrap()
.decode_input(data)
else {
// Not interested
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is ignoring an error OK here?

Comment on lines 822 to 824
let use_evm_simulator = use_evm_simulator::UseEvmSimulator::from_env()
.unwrap()
.use_evm_simulator;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use a dedicated argument provided to this method instead.

Comment on lines 132 to 136
new_known_factory_deps
.into_iter()
.for_each(|(hash, bytecode)| {
tx_factory_deps.insert(hash, bytecode);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: This is not JavaScript; please use a for cycle, or tx_factory_deps.extend(_).

@@ -227,6 +230,7 @@ impl GlueFrom<crate::vm_1_3_2::vm_instance::VmBlockResult>
circuit_statistic: Default::default(),
},
refunds: Refunds::default(),
new_known_factory_deps: Default::default(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't use Default::default() instead of None; this is overly verbose and unnecessarily opaque.

@@ -118,6 +118,7 @@ pub struct VmExecutionResultAndLogs {
pub logs: VmExecutionLogs,
pub statistics: VmExecutionStatistics,
pub refunds: Refunds,
pub new_known_factory_deps: Option<Vec<(H256, Vec<u8>)>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Dumb question: What does this field mean? Could you explain in a doc comment for this field what it's necessary for?

@IAvecilla IAvecilla closed this Sep 25, 2024
@IAvecilla
Copy link
Contributor

Close in favor of #2966

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants