Skip to content

Commit 5a1e6d2

Browse files
authored
feat: Adapt server for new EVM bytecode hash encoding (#3396)
## What ❔ After latest EVM emulator contract changes, EVM bytecode doesn't include any king of prefix with bytecode length. Actual unpadded raw EVM bytecode length is encoded directly in versioned bytecode hash. ## Checklist <!-- Check your PR fulfills the following items. --> <!-- For draft PRs check the boxes as you complete them. --> - [X] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [X] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [X] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`.
1 parent 3ce7e8a commit 5a1e6d2

File tree

11 files changed

+163
-96
lines changed

11 files changed

+163
-96
lines changed

core/lib/basic_types/src/bytecode.rs

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//! Both bytecode kinds are right-padded to consist of an integer, odd number of 32-byte words. All methods
1111
//! in this module operate on padded bytecodes unless explicitly specified otherwise.
1212
13+
use std::iter;
14+
1315
use anyhow::Context as _;
1416
use sha2::{Digest, Sha256};
1517

@@ -68,21 +70,31 @@ pub struct BytecodeHash(H256);
6870
impl BytecodeHash {
6971
/// Hashes the provided EraVM bytecode.
7072
pub fn for_bytecode(bytecode: &[u8]) -> Self {
71-
Self::for_generic_bytecode(BytecodeMarker::EraVm, bytecode)
73+
Self::for_generic_bytecode(BytecodeMarker::EraVm, bytecode, bytecode.len())
7274
}
7375

7476
/// Hashes the provided padded EVM bytecode.
75-
pub fn for_evm_bytecode(bytecode: &[u8]) -> Self {
76-
Self::for_generic_bytecode(BytecodeMarker::Evm, bytecode)
77+
pub fn for_evm_bytecode(raw_bytecode_len: usize, bytecode: &[u8]) -> Self {
78+
Self::for_generic_bytecode(BytecodeMarker::Evm, bytecode, raw_bytecode_len)
79+
}
80+
81+
/// Hashes the provided raw EVM bytecode.
82+
pub fn for_raw_evm_bytecode(bytecode: &[u8]) -> Self {
83+
let padded_evm_bytecode = pad_evm_bytecode(bytecode);
84+
Self::for_evm_bytecode(bytecode.len(), &padded_evm_bytecode)
7785
}
7886

79-
fn for_generic_bytecode(kind: BytecodeMarker, bytecode: &[u8]) -> Self {
87+
fn for_generic_bytecode(
88+
kind: BytecodeMarker,
89+
bytecode: &[u8],
90+
bytecode_len_in_bytes: usize,
91+
) -> Self {
8092
validate_bytecode(bytecode).expect("invalid bytecode");
8193

8294
let mut hasher = Sha256::new();
8395
let len = match kind {
84-
BytecodeMarker::EraVm => (bytecode.len() / 32) as u16,
85-
BytecodeMarker::Evm => bytecode.len() as u16,
96+
BytecodeMarker::EraVm => (bytecode_len_in_bytes / 32) as u16,
97+
BytecodeMarker::Evm => bytecode_len_in_bytes as u16,
8698
};
8799
hasher.update(bytecode);
88100
let result = hasher.finalize();
@@ -157,46 +169,65 @@ impl BytecodeMarker {
157169
}
158170

159171
/// Removes padding from an EVM bytecode, returning the original EVM bytecode.
160-
pub fn trim_padded_evm_bytecode(raw: &[u8]) -> anyhow::Result<&[u8]> {
172+
pub fn trim_padded_evm_bytecode(bytecode_hash: BytecodeHash, raw: &[u8]) -> anyhow::Result<&[u8]> {
173+
if bytecode_hash.marker() != BytecodeMarker::Evm {
174+
anyhow::bail!("only EVM bytecode hashes allowed")
175+
}
161176
validate_bytecode(raw).context("bytecode fails basic validity checks")?;
162177

163-
// EVM bytecodes are prefixed with a big-endian `U256` bytecode length.
164-
let bytecode_len_bytes = raw.get(..32).context("length < 32")?;
165-
let bytecode_len = U256::from_big_endian(bytecode_len_bytes);
166-
let bytecode_len: usize = bytecode_len
167-
.try_into()
168-
.map_err(|_| anyhow::anyhow!("length ({bytecode_len}) overflow"))?;
169-
let bytecode = raw.get(32..(32 + bytecode_len)).with_context(|| {
178+
// Actual raw unpadded EVM bytecode length is encoded in bytecode hash
179+
let bytecode_len: usize = bytecode_hash.len_in_bytes();
180+
let bytecode = raw.get(0..bytecode_len).with_context(|| {
170181
format!(
171-
"prefixed length ({bytecode_len}) exceeds real length ({})",
172-
raw.len() - 32
182+
"encoded length ({bytecode_len}) exceeds real length ({})",
183+
raw.len()
173184
)
174185
})?;
175186
// Since slicing above succeeded, this one is safe.
176-
let padding = &raw[(32 + bytecode_len)..];
187+
let padding = &raw[bytecode_len..];
177188
anyhow::ensure!(
178189
padding.iter().all(|&b| b == 0),
179190
"bytecode padding contains non-zero bytes"
180191
);
181192
Ok(bytecode)
182193
}
183194

195+
/// Pads an EVM bytecode in the same ways it's done by system contracts.
196+
pub fn pad_evm_bytecode(deployed_bytecode: &[u8]) -> Vec<u8> {
197+
let mut padded = Vec::with_capacity(deployed_bytecode.len());
198+
padded.extend_from_slice(deployed_bytecode);
199+
200+
// Pad to the 32-byte word boundary.
201+
if padded.len() % 32 != 0 {
202+
padded.extend(iter::repeat(0).take(32 - padded.len() % 32));
203+
}
204+
assert_eq!(padded.len() % 32, 0);
205+
206+
// Pad to contain the odd number of words.
207+
if (padded.len() / 32) % 2 != 1 {
208+
padded.extend_from_slice(&[0; 32]);
209+
}
210+
assert_eq!((padded.len() / 32) % 2, 1);
211+
padded
212+
}
213+
184214
#[doc(hidden)] // only useful for tests
185215
pub mod testonly {
186216
use const_decoder::Decoder;
187217

188-
pub const RAW_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
218+
pub const PADDED_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
189219
Decoder::Hex,
190-
b"00000000000000000000000000000000000000000000000000000000000001266080604052348015\
191-
600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063fb5343f314604c57\
192-
5b5f80fd5b604a60048036038101906046919060a6565b6066565b005b6052606f565b604051605d\
193-
919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd5b5f819050919050\
194-
565b6088816078565b81146091575f80fd5b50565b5f8135905060a0816081565b92915050565b5f\
195-
6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092915050565b60d381\
196-
6078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056fea2646970667358\
197-
221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9ce4d0964736f6c63\
198-
4300081a00330000000000000000000000000000000000000000000000000000"
220+
b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\
221+
fb5343f314604c575b5f80fd5b604a60048036038101906046919060a6565b6066565b005b605260\
222+
6f565b604051605d919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd\
223+
5b5f819050919050565b6088816078565b81146091575f80fd5b50565b5f8135905060a081608156\
224+
5b92915050565b5f6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092\
225+
915050565b60d3816078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056\
226+
fea2646970667358221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9\
227+
ce4d0964736f6c634300081a00330000000000000000000000000000000000000000000000000000\
228+
0000000000000000000000000000000000000000000000000000000000000000"
199229
);
230+
200231
pub const PROCESSED_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
201232
Decoder::Hex,
202233
b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\
@@ -213,7 +244,7 @@ pub mod testonly {
213244
#[cfg(test)]
214245
mod tests {
215246
use super::{
216-
testonly::{PROCESSED_EVM_BYTECODE, RAW_EVM_BYTECODE},
247+
testonly::{PADDED_EVM_BYTECODE, PROCESSED_EVM_BYTECODE},
217248
*,
218249
};
219250

@@ -223,14 +254,20 @@ mod tests {
223254
assert_eq!(bytecode_hash.marker(), BytecodeMarker::EraVm);
224255
assert_eq!(bytecode_hash.len_in_bytes(), 32);
225256

226-
let bytecode_hash = BytecodeHash::for_evm_bytecode(&[0; 32]);
257+
let bytecode_hash = BytecodeHash::for_raw_evm_bytecode(&[0; 32]);
258+
assert_eq!(bytecode_hash.marker(), BytecodeMarker::Evm);
259+
assert_eq!(bytecode_hash.len_in_bytes(), 32);
260+
261+
let bytecode_hash = BytecodeHash::for_evm_bytecode(32, &[0; 96]);
227262
assert_eq!(bytecode_hash.marker(), BytecodeMarker::Evm);
228263
assert_eq!(bytecode_hash.len_in_bytes(), 32);
229264
}
230265

231266
#[test]
232267
fn preparing_evm_bytecode() {
233-
let prepared = trim_padded_evm_bytecode(RAW_EVM_BYTECODE).unwrap();
268+
let bytecode_hash =
269+
BytecodeHash::for_evm_bytecode(PROCESSED_EVM_BYTECODE.len(), &PADDED_EVM_BYTECODE);
270+
let prepared = trim_padded_evm_bytecode(bytecode_hash, PADDED_EVM_BYTECODE).unwrap();
234271
assert_eq!(prepared, PROCESSED_EVM_BYTECODE);
235272
}
236273
}

core/lib/contract_verifier/src/lib.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use tokio::time;
1414
use zksync_dal::{contract_verification_dal::DeployedContractData, ConnectionPool, Core, CoreDal};
1515
use zksync_queued_job_processor::{async_trait, JobProcessor};
1616
use zksync_types::{
17-
bytecode::{trim_padded_evm_bytecode, BytecodeMarker},
17+
bytecode::{trim_padded_evm_bytecode, BytecodeHash, BytecodeMarker},
1818
contract_verification_api::{
1919
self as api, CompilationArtifacts, VerificationIncomingRequest, VerificationInfo,
2020
VerificationRequest,
@@ -257,8 +257,12 @@ impl ContractVerifier {
257257

258258
let deployed_bytecode = match bytecode_marker {
259259
BytecodeMarker::EraVm => deployed_contract.bytecode.as_slice(),
260-
BytecodeMarker::Evm => trim_padded_evm_bytecode(&deployed_contract.bytecode)
261-
.context("invalid stored EVM bytecode")?,
260+
BytecodeMarker::Evm => trim_padded_evm_bytecode(
261+
BytecodeHash::try_from(deployed_contract.bytecode_hash)
262+
.context("Invalid bytecode hash")?,
263+
&deployed_contract.bytecode,
264+
)
265+
.context("invalid stored EVM bytecode")?,
262266
};
263267

264268
if artifacts.deployed_bytecode() != deployed_bytecode {

core/lib/contract_verifier/src/tests/mod.rs

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
//! Tests for the contract verifier.
22
3-
use std::{
4-
collections::{HashMap, HashSet},
5-
iter,
6-
};
3+
use std::collections::{HashMap, HashSet};
74

85
use test_casing::{test_casing, Product};
96
use tokio::sync::watch;
107
use zksync_dal::Connection;
118
use zksync_node_test_utils::{create_l1_batch, create_l2_block};
129
use zksync_types::{
1310
address_to_h256,
14-
bytecode::BytecodeHash,
11+
bytecode::{pad_evm_bytecode, BytecodeHash},
1512
contract_verification_api::{CompilerVersions, SourceCodeData, VerificationIncomingRequest},
1613
get_code_key, get_known_code_key,
1714
l2::L2Tx,
@@ -114,28 +111,6 @@ impl TestContract {
114111
}
115112
}
116113

117-
/// Pads an EVM bytecode in the same ways it's done by system contracts.
118-
fn pad_evm_bytecode(deployed_bytecode: &[u8]) -> Vec<u8> {
119-
let mut padded = Vec::with_capacity(deployed_bytecode.len() + 32);
120-
let len = U256::from(deployed_bytecode.len());
121-
padded.extend_from_slice(&[0; 32]);
122-
len.to_big_endian(&mut padded);
123-
padded.extend_from_slice(deployed_bytecode);
124-
125-
// Pad to the 32-byte word boundary.
126-
if padded.len() % 32 != 0 {
127-
padded.extend(iter::repeat(0).take(32 - padded.len() % 32));
128-
}
129-
assert_eq!(padded.len() % 32, 0);
130-
131-
// Pad to contain the odd number of words.
132-
if (padded.len() / 32) % 2 != 1 {
133-
padded.extend_from_slice(&[0; 32]);
134-
}
135-
assert_eq!((padded.len() / 32) % 2, 1);
136-
padded
137-
}
138-
139114
async fn mock_deployment(
140115
storage: &mut Connection<'_, Core>,
141116
address: Address,
@@ -163,7 +138,7 @@ async fn mock_evm_deployment(
163138
factory_deps: vec![],
164139
};
165140
let bytecode = pad_evm_bytecode(deployed_bytecode);
166-
let bytecode_hash = BytecodeHash::for_evm_bytecode(&bytecode).value();
141+
let bytecode_hash = BytecodeHash::for_evm_bytecode(deployed_bytecode.len(), &bytecode).value();
167142
mock_deployment_inner(storage, address, bytecode_hash, bytecode, deployment).await;
168143
}
169144

core/lib/multivm/src/versions/testonly/evm_emulator.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ impl EvmTestBuilder {
7676
let mut system_env = default_system_env();
7777
if self.deploy_emulator {
7878
let evm_bytecode: Vec<_> = (0..32).collect();
79-
let evm_bytecode_hash = BytecodeHash::for_evm_bytecode(&evm_bytecode).value();
79+
let evm_bytecode_hash =
80+
BytecodeHash::for_evm_bytecode(evm_bytecode.len(), &evm_bytecode).value();
8081
storage.set_value(
8182
get_known_code_key(&evm_bytecode_hash),
8283
H256::from_low_u64_be(1),
@@ -131,7 +132,8 @@ pub(crate) fn test_tracing_evm_contract_deployment<VM: TestedVm>() {
131132

132133
let args = [Token::Bytes((0..32).collect())];
133134
let evm_bytecode = ethabi::encode(&args);
134-
let expected_bytecode_hash = BytecodeHash::for_evm_bytecode(&evm_bytecode).value();
135+
let expected_bytecode_hash =
136+
BytecodeHash::for_evm_bytecode(evm_bytecode.len(), &evm_bytecode).value();
135137
let execute = Execute::for_deploy(expected_bytecode_hash, vec![0; 32], &args);
136138
let deploy_tx = account.get_l2_tx_for_execute(execute, None);
137139
let (_, vm_result) = vm
@@ -148,7 +150,8 @@ pub(crate) fn test_tracing_evm_contract_deployment<VM: TestedVm>() {
148150
// "Deploy" a bytecode in another transaction and check that the first tx doesn't interfere with the returned `dynamic_factory_deps`.
149151
let args = [Token::Bytes((0..32).rev().collect())];
150152
let evm_bytecode = ethabi::encode(&args);
151-
let expected_bytecode_hash = BytecodeHash::for_evm_bytecode(&evm_bytecode).value();
153+
let expected_bytecode_hash =
154+
BytecodeHash::for_evm_bytecode(evm_bytecode.len(), &evm_bytecode).value();
152155
let execute = Execute::for_deploy(expected_bytecode_hash, vec![0; 32], &args);
153156
let deploy_tx = account.get_l2_tx_for_execute(execute, None);
154157
let (_, vm_result) = vm
@@ -324,7 +327,8 @@ pub(crate) fn test_mock_emulator_with_deployment<VM: TestedVm>(revert: bool) {
324327

325328
let mock_emulator_abi = &TestContract::mock_evm_emulator().abi;
326329
let new_evm_bytecode = vec![0xfe; 96];
327-
let new_evm_bytecode_hash = BytecodeHash::for_evm_bytecode(&new_evm_bytecode).value();
330+
let new_evm_bytecode_hash =
331+
BytecodeHash::for_evm_bytecode(new_evm_bytecode.len(), &new_evm_bytecode).value();
328332

329333
let test_fn = mock_emulator_abi.function("testDeploymentAndCall").unwrap();
330334
let test_tx = account.get_l2_tx_for_execute(
@@ -402,7 +406,10 @@ pub(crate) fn test_mock_emulator_with_recursive_deployment<VM: TestedVm>() {
402406
let bytecodes: HashMap<_, _> = (0_u8..10)
403407
.map(|byte| {
404408
let bytecode = vec![byte; 32];
405-
(BytecodeHash::for_evm_bytecode(&bytecode).value(), bytecode)
409+
(
410+
BytecodeHash::for_evm_bytecode(bytecode.len(), &bytecode).value(),
411+
bytecode,
412+
)
406413
})
407414
.collect();
408415
let test_fn = mock_emulator_abi
@@ -448,7 +455,10 @@ fn test_mock_emulator_with_partial_reverts_and_rng<VM: TestedVm>(rng: &mut impl
448455
let all_bytecodes: HashMap<_, _> = (0_u8..10)
449456
.map(|_| {
450457
let bytecode = vec![rng.gen(); 32];
451-
(BytecodeHash::for_evm_bytecode(&bytecode).value(), bytecode)
458+
(
459+
BytecodeHash::for_evm_bytecode(bytecode.len(), &bytecode).value(),
460+
bytecode,
461+
)
452462
})
453463
.collect();
454464
let should_revert: Vec<_> = (0..10).map(|_| rng.gen::<bool>()).collect();

core/lib/multivm/src/versions/vm_fast/tracers/evm_deploy.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ pub(super) struct EvmDeployTracer {
3737

3838
impl EvmDeployTracer {
3939
pub(super) fn new(bytecodes: DynamicBytecodes) -> Self {
40-
let tracked_signature =
41-
ethabi::short_signature("publishEVMBytecode", &[ethabi::ParamType::Bytes]);
40+
let tracked_signature = ethabi::short_signature(
41+
"publishEVMBytecode",
42+
&[ethabi::ParamType::Uint(256), ethabi::ParamType::Bytes],
43+
);
4244
Self {
4345
tracked_signature,
4446
bytecodes,
@@ -61,13 +63,26 @@ impl EvmDeployTracer {
6163
return;
6264
}
6365

64-
match ethabi::decode(&[ethabi::ParamType::Bytes], data) {
66+
match ethabi::decode(
67+
&[ethabi::ParamType::Uint(256), ethabi::ParamType::Bytes],
68+
data,
69+
) {
6570
Ok(decoded) => {
6671
// `unwrap`s should be safe since the function signature is checked above.
67-
let published_bytecode = decoded.into_iter().next().unwrap().into_bytes().unwrap();
68-
let bytecode_hash =
69-
BytecodeHash::for_evm_bytecode(&published_bytecode).value_u256();
70-
self.bytecodes.insert(bytecode_hash, published_bytecode);
72+
let mut decoded_iter = decoded.into_iter();
73+
let raw_bytecode_len = decoded_iter.next().unwrap().into_uint().unwrap().try_into();
74+
match raw_bytecode_len {
75+
Ok(raw_bytecode_len) => {
76+
let published_bytecode = decoded_iter.next().unwrap().into_bytes().unwrap();
77+
let bytecode_hash =
78+
BytecodeHash::for_evm_bytecode(raw_bytecode_len, &published_bytecode)
79+
.value_u256();
80+
self.bytecodes.insert(bytecode_hash, published_bytecode);
81+
}
82+
Err(err) => {
83+
tracing::error!("Invalid bytecode len in `publishEVMBytecode` call: {err}")
84+
}
85+
}
7186
}
7287
Err(err) => tracing::error!("Unable to decode `publishEVMBytecode` call: {err}"),
7388
}

0 commit comments

Comments
 (0)