Skip to content

Commit

Permalink
fix(api): Adapt eth_getCode to EVM emulator (#3073)
Browse files Browse the repository at this point in the history
## What ❔

Fixes returned bytecodes with EVM bytecodes by removing the length
prefix and padding suffix.

## Why ❔

Callers are interested in the original EVM bytecode rather than its
transformed version.

## Checklist

- [x] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [x] Tests for the changes have been added / updated.
- [x] Documentation comments have been added / updated.
- [x] Code has been formatted via `zk_supervisor fmt` and `zk_supervisor
lint`.
  • Loading branch information
slowli authored Oct 14, 2024
1 parent 6dd4865 commit 15fe5a6
Show file tree
Hide file tree
Showing 14 changed files with 338 additions and 64 deletions.
10 changes: 10 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 @@ -114,6 +114,7 @@ blake2 = "0.10"
chrono = "0.4"
clap = "4.2.2"
codegen = "0.2.0"
const-decoder = "0.4.0"
criterion = "0.4.0"
ctrlc = "3.1"
dashmap = "5.5.3"
Expand Down

This file was deleted.

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

20 changes: 16 additions & 4 deletions core/lib/dal/src/storage_web3_dal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ use zksync_utils::h256_to_u256;

use crate::{models::storage_block::ResolvedL1BatchForL2Block, Core, CoreDal};

/// Raw bytecode information returned by [`StorageWeb3Dal::get_contract_code_unchecked()`].
#[derive(Debug)]
pub struct RawBytecode {
pub bytecode_hash: H256,
pub bytecode: Vec<u8>,
}

#[derive(Debug)]
pub struct StorageWeb3Dal<'a, 'c> {
pub(crate) storage: &'a mut Connection<'c, Core>,
Expand Down Expand Up @@ -234,16 +241,17 @@ impl StorageWeb3Dal<'_, '_> {
&mut self,
address: Address,
block_number: L2BlockNumber,
) -> DalResult<Option<Vec<u8>>> {
) -> DalResult<Option<RawBytecode>> {
let hashed_key = get_code_key(&address).hashed_key();
let row = sqlx::query!(
r#"
SELECT
bytecode_hash,
bytecode
FROM
(
SELECT
*
value
FROM
storage_logs
WHERE
Expand All @@ -254,7 +262,7 @@ impl StorageWeb3Dal<'_, '_> {
storage_logs.operation_number DESC
LIMIT
1
) t
) deploy_log
JOIN factory_deps ON value = factory_deps.bytecode_hash
WHERE
value != $3
Expand All @@ -268,7 +276,11 @@ impl StorageWeb3Dal<'_, '_> {
.with_arg("block_number", &block_number)
.fetch_optional(self.storage)
.await?;
Ok(row.map(|row| row.bytecode))

Ok(row.map(|row| RawBytecode {
bytecode_hash: H256::from_slice(&row.bytecode_hash),
bytecode: row.bytecode,
}))
}

/// Given bytecode hash, returns bytecode and L2 block number at which it was inserted.
Expand Down
11 changes: 7 additions & 4 deletions core/lib/multivm/src/versions/vm_latest/tests/evm_emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ use zksync_types::{
utils::{key_for_eth_balance, storage_key_for_eth_balance},
AccountTreeId, Address, Execute, StorageKey, H256, U256,
};
use zksync_utils::{be_words_to_bytes, bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256};
use zksync_utils::{
be_words_to_bytes,
bytecode::{hash_bytecode, hash_evm_bytecode},
bytes_to_be_words, h256_to_u256,
};

use crate::{
interface::{
Expand All @@ -21,7 +25,6 @@ use crate::{
versions::testonly::default_system_env,
vm_latest::{
tests::tester::{VmTester, VmTesterBuilder},
utils::hash_evm_bytecode,
HistoryEnabled,
},
};
Expand Down Expand Up @@ -87,7 +90,7 @@ impl EvmTestBuilder {
let mut storage = self.storage;
let mut system_env = default_system_env();
if self.deploy_emulator {
let evm_bytecode: Vec<_> = (0..=u8::MAX).collect();
let evm_bytecode: Vec<_> = (0..32).collect();
let evm_bytecode_hash = hash_evm_bytecode(&evm_bytecode);
storage.set_value(
get_known_code_key(&evm_bytecode_hash),
Expand Down Expand Up @@ -142,7 +145,7 @@ fn tracing_evm_contract_deployment() {
.build();
let account = &mut vm.rich_accounts[0];

let args = [Token::Bytes((0..=u8::MAX).collect())];
let args = [Token::Bytes((0..32).collect())];
let evm_bytecode = ethabi::encode(&args);
let expected_bytecode_hash = hash_evm_bytecode(&evm_bytecode);
let execute = Execute::for_deploy(expected_bytecode_hash, vec![0; 32], &args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ use zk_evm_1_5_0::{
},
};
use zksync_types::{CONTRACT_DEPLOYER_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS};
use zksync_utils::{bytes_to_be_words, h256_to_u256};
use zksync_utils::{bytecode::hash_evm_bytecode, bytes_to_be_words, h256_to_u256};
use zksync_vm_interface::storage::StoragePtr;

use super::{traits::VmTracer, utils::read_pointer};
use crate::{
interface::{storage::WriteStorage, tracer::TracerExecutionStatus},
tracers::dynamic::vm_1_5_0::DynTracer,
vm_latest::{
utils::hash_evm_bytecode, BootloaderState, HistoryMode, SimpleMemory, ZkSyncVmState,
},
vm_latest::{BootloaderState, HistoryMode, SimpleMemory, ZkSyncVmState},
};

/// Tracer responsible for collecting information about EVM deploys and providing those
Expand Down
22 changes: 1 addition & 21 deletions core/lib/multivm/src/versions/vm_latest/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
//! Utility functions for the VM.
use once_cell::sync::Lazy;
use zk_evm_1_5_0::{
aux_structures::MemoryPage,
sha2,
zkevm_opcode_defs::{BlobSha256Format, VersionedHashLen32},
};
use zk_evm_1_5_0::aux_structures::MemoryPage;
use zksync_types::{H256, KNOWN_CODES_STORAGE_ADDRESS};
use zksync_vm_interface::VmEvent;

Expand All @@ -15,22 +11,6 @@ pub(crate) mod logs;
pub mod overhead;
pub mod transaction_encoding;

pub(crate) fn hash_evm_bytecode(bytecode: &[u8]) -> H256 {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
let len = bytecode.len() as u16;
hasher.update(bytecode);
let result = hasher.finalize();

let mut output = [0u8; 32];
output[..].copy_from_slice(result.as_slice());
output[0] = BlobSha256Format::VERSION_BYTE;
output[1] = 0;
output[2..4].copy_from_slice(&len.to_be_bytes());

H256(output)
}

pub const fn heap_page_from_base(base: MemoryPage) -> MemoryPage {
MemoryPage(base.0 + 2)
}
Expand Down
61 changes: 61 additions & 0 deletions core/lib/utils/src/bytecode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FIXME: move to basic_types?

use zk_evm::k256::sha2::{Digest, Sha256};
use zksync_basic_types::H256;

use crate::bytes_to_chunks;
Expand Down Expand Up @@ -40,6 +41,7 @@ pub fn validate_bytecode(code: &[u8]) -> Result<(), InvalidBytecodeError> {
Ok(())
}

/// Hashes the provided EraVM bytecode.
pub fn hash_bytecode(code: &[u8]) -> H256 {
let chunked_code = bytes_to_chunks(code);
let hash = zk_evm::zkevm_opcode_defs::utils::bytecode_to_code_hash(&chunked_code)
Expand All @@ -55,3 +57,62 @@ pub fn bytecode_len_in_words(bytecodehash: &H256) -> u16 {
pub fn bytecode_len_in_bytes(bytecodehash: H256) -> usize {
bytecode_len_in_words(&bytecodehash) as usize * 32
}

/// Bytecode marker encoded in the first byte of the bytecode hash.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum BytecodeMarker {
/// EraVM bytecode marker (1).
EraVm = 1,
/// EVM bytecode marker (2).
Evm = 2,
}

impl BytecodeMarker {
/// Parses a marker from the bytecode hash.
pub fn new(bytecode_hash: H256) -> Option<Self> {
Some(match bytecode_hash.as_bytes()[0] {
val if val == Self::EraVm as u8 => Self::EraVm,
val if val == Self::Evm as u8 => Self::Evm,
_ => return None,
})
}
}

/// Hashes the provided EVM bytecode. The bytecode must be padded to an odd number of 32-byte words;
/// bytecodes stored in the known codes storage satisfy this requirement automatically.
pub fn hash_evm_bytecode(bytecode: &[u8]) -> H256 {
validate_bytecode(bytecode).expect("invalid EVM bytecode");

let mut hasher = Sha256::new();
let len = bytecode.len() as u16;
hasher.update(bytecode);
let result = hasher.finalize();

let mut output = [0u8; 32];
output[..].copy_from_slice(result.as_slice());
output[0] = BytecodeMarker::Evm as u8;
output[1] = 0;
output[2..4].copy_from_slice(&len.to_be_bytes());

H256(output)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn bytecode_markers_are_valid() {
let bytecode_hash = hash_bytecode(&[0; 32]);
assert_eq!(
BytecodeMarker::new(bytecode_hash),
Some(BytecodeMarker::EraVm)
);
let bytecode_hash = hash_evm_bytecode(&[0; 32]);
assert_eq!(
BytecodeMarker::new(bytecode_hash),
Some(BytecodeMarker::Evm)
);
}
}
1 change: 1 addition & 0 deletions core/node/api_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@ zksync_node_genesis.workspace = true
zksync_node_test_utils.workspace = true

assert_matches.workspace = true
const-decoder.workspace = true
test-casing.workspace = true
25 changes: 25 additions & 0 deletions core/node/api_server/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use std::{collections::HashMap, iter};

use const_decoder::Decoder;
use zk_evm_1_5_0::zkevm_opcode_defs::decoding::{EncodingModeProduction, VmEncodingMode};
use zksync_contracts::{
eth_contract, get_loadnext_contract, load_contract, read_bytecode,
Expand All @@ -26,6 +27,30 @@ use zksync_types::{
};
use zksync_utils::{address_to_u256, u256_to_h256};

pub(crate) const RAW_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
Decoder::Hex,
b"00000000000000000000000000000000000000000000000000000000000001266080604052348015\
600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063fb5343f314604c57\
5b5f80fd5b604a60048036038101906046919060a6565b6066565b005b6052606f565b604051605d\
919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd5b5f819050919050\
565b6088816078565b81146091575f80fd5b50565b5f8135905060a0816081565b92915050565b5f\
6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092915050565b60d381\
6078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056fea2646970667358\
221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9ce4d0964736f6c63\
4300081a00330000000000000000000000000000000000000000000000000000"
);
pub(crate) const PROCESSED_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
Decoder::Hex,
b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\
fb5343f314604c575b5f80fd5b604a60048036038101906046919060a6565b6066565b005b605260\
6f565b604051605d919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd\
5b5f819050919050565b6088816078565b81146091575f80fd5b50565b5f8135905060a081608156\
5b92915050565b5f6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092\
915050565b60d3816078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056\
fea2646970667358221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9\
ce4d0964736f6c634300081a0033"
);

const EXPENSIVE_CONTRACT_PATH: &str =
"etc/contracts-test-data/artifacts-zk/contracts/expensive/expensive.sol/Expensive.json";
const PRECOMPILES_CONTRACT_PATH: &str =
Expand Down
Loading

0 comments on commit 15fe5a6

Please sign in to comment.