Skip to content

Commit

Permalink
feat(state-keeper): Reject transactions that fail to publish bytecodes (
Browse files Browse the repository at this point in the history
#832)

## What ❔

State keeper and API reject transactions that fail to publish bytecodes

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] 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`.
- [ ] Spellcheck has been run via `cargo spellcheck
--cfg=./spellcheck/era.cfg --code 1`.
  • Loading branch information
perekopskiy authored Jan 8, 2024
1 parent 1b57984 commit 0a010f0
Show file tree
Hide file tree
Showing 21 changed files with 192 additions and 46 deletions.
1 change: 1 addition & 0 deletions core/bin/external_node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ async fn build_state_keeper(
save_call_traces,
false,
config.optional.enum_index_migration_chunk_size,
true,
));

let main_node_url = config.required.main_node_url().unwrap();
Expand Down
10 changes: 8 additions & 2 deletions core/lib/multivm/src/interface/traits/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ pub trait VmInterface<S, H: HistoryMode> {
&mut self,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.inspect_transaction_with_bytecode_compression(
Self::TracerDispatcher::default(),
tx,
Expand All @@ -118,7 +121,10 @@ pub trait VmInterface<S, H: HistoryMode> {
tracer: Self::TracerDispatcher,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError>;
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
);

/// Record VM memory metrics.
fn record_vm_memory_metrics(&self) -> VmMemoryMetrics;
Expand Down
4 changes: 4 additions & 0 deletions core/lib/multivm/src/interface/types/errors/halt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum Halt {
FailedToAppendTransactionToL2Block(String),
VMPanic,
TracerCustom(String),
FailedToPublishCompressedBytecodes,
}

impl Display for Halt {
Expand Down Expand Up @@ -112,6 +113,9 @@ impl Display for Halt {
Halt::ValidationOutOfGas => {
write!(f, "Validation run out of gas")
}
Halt::FailedToPublishCompressedBytecodes => {
write!(f, "Failed to publish compressed bytecodes")
}
}
}
}
12 changes: 9 additions & 3 deletions core/lib/multivm/src/versions/vm_1_3_2/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
_tracer: Self::TracerDispatcher,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.last_tx_compressed_bytecodes = vec![];
let bytecodes = if with_compression {
let deps = tx.execute.factory_deps.as_deref().unwrap_or_default();
Expand Down Expand Up @@ -209,9 +212,12 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
.iter()
.any(|info| !self.vm.is_bytecode_known(info))
{
Err(crate::interface::BytecodeCompressionError::BytecodeCompressionFailed)
(
Err(BytecodeCompressionError::BytecodeCompressionFailed),
result.glue_into(),
)
} else {
Ok(result.glue_into())
(Ok(()), result.glue_into())
}
}

Expand Down
12 changes: 9 additions & 3 deletions core/lib/multivm/src/versions/vm_boojum_integration/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,19 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
tracer: Self::TracerDispatcher,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.push_transaction_with_compression(tx, with_compression);
let result = self.inspect_inner(tracer, VmExecutionMode::OneTx);
if self.has_unpublished_bytecodes() {
Err(BytecodeCompressionError::BytecodeCompressionFailed)
(
Err(BytecodeCompressionError::BytecodeCompressionFailed),
result,
)
} else {
Ok(result)
(Ok(()), result)
}
}

Expand Down
12 changes: 9 additions & 3 deletions core/lib/multivm/src/versions/vm_latest/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,19 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
tracer: Self::TracerDispatcher,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.push_transaction_with_compression(tx, with_compression);
let result = self.inspect_inner(tracer, VmExecutionMode::OneTx);
if self.has_unpublished_bytecodes() {
Err(BytecodeCompressionError::BytecodeCompressionFailed)
(
Err(BytecodeCompressionError::BytecodeCompressionFailed),
result,
)
} else {
Ok(result)
(Ok(()), result)
}
}

Expand Down
7 changes: 5 additions & 2 deletions core/lib/multivm/src/versions/vm_m5/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,16 @@ impl<S: Storage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
_tracer: Self::TracerDispatcher,
tx: Transaction,
_with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
crate::vm_m5::vm_with_bootloader::push_transaction_to_bootloader_memory(
&mut self.vm,
&tx,
self.system_env.execution_mode.glue_into(),
);
Ok(self.execute(VmExecutionMode::OneTx))
(Ok(()), self.execute(VmExecutionMode::OneTx))
}

fn record_vm_memory_metrics(&self) -> VmMemoryMetrics {
Expand Down
12 changes: 9 additions & 3 deletions core/lib/multivm/src/versions/vm_m6/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ impl<S: Storage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
_tracer: Self::TracerDispatcher,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.last_tx_compressed_bytecodes = vec![];
let bytecodes = if with_compression {
let deps = tx.execute.factory_deps.as_deref().unwrap_or_default();
Expand Down Expand Up @@ -226,9 +229,12 @@ impl<S: Storage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
.iter()
.any(|info| !self.vm.is_bytecode_exists(info))
{
Err(crate::interface::BytecodeCompressionError::BytecodeCompressionFailed)
(
Err(BytecodeCompressionError::BytecodeCompressionFailed),
result.glue_into(),
)
} else {
Ok(result.glue_into())
(Ok(()), result.glue_into())
}
}

Expand Down
12 changes: 9 additions & 3 deletions core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,19 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
dispatcher: Self::TracerDispatcher,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.push_transaction_with_compression(tx, with_compression);
let result = self.inspect(dispatcher, VmExecutionMode::OneTx);
if self.has_unpublished_bytecodes() {
Err(BytecodeCompressionError::BytecodeCompressionFailed)
(
Err(BytecodeCompressionError::BytecodeCompressionFailed),
result,
)
} else {
Ok(result)
(Ok(()), result)
}
}

Expand Down
12 changes: 9 additions & 3 deletions core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,19 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for Vm<S, H> {
tracer: TracerDispatcher<S, H::VmVirtualBlocksMode>,
tx: Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
self.push_transaction_with_compression(tx, with_compression);
let result = self.inspect_inner(tracer, VmExecutionMode::OneTx);
if self.has_unpublished_bytecodes() {
Err(BytecodeCompressionError::BytecodeCompressionFailed)
(
Err(BytecodeCompressionError::BytecodeCompressionFailed),
result,
)
} else {
Ok(result)
(Ok(()), result)
}
}

Expand Down
14 changes: 10 additions & 4 deletions core/lib/multivm/src/vm_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use zksync_utils::bytecode::CompressedBytecodeInfo;
use crate::{
glue::history_mode::HistoryMode,
interface::{
BootloaderMemory, CurrentExecutionState, FinishedL1Batch, L1BatchEnv, L2BlockEnv,
SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface,
BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch,
L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface,
VmInterfaceHistoryEnabled, VmMemoryMetrics,
},
tracers::TracerDispatcher,
Expand Down Expand Up @@ -86,7 +86,10 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for VmInstance<S, H> {
&mut self,
tx: zksync_types::Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, crate::interface::BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
dispatch_vm!(self.execute_transaction_with_bytecode_compression(tx, with_compression))
}

Expand All @@ -96,7 +99,10 @@ impl<S: WriteStorage, H: HistoryMode> VmInterface<S, H> for VmInstance<S, H> {
dispatcher: Self::TracerDispatcher,
tx: zksync_types::Transaction,
with_compression: bool,
) -> Result<VmExecutionResultAndLogs, crate::interface::BytecodeCompressionError> {
) -> (
Result<(), BytecodeCompressionError>,
VmExecutionResultAndLogs,
) {
dispatch_vm!(self.inspect_transaction_with_bytecode_compression(
dispatcher.into(),
tx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ impl From<Halt> for SandboxExecutionError {
Halt::ValidationOutOfGas => Self::AccountValidationFailed(
"The validation of the transaction ran out of gas".to_string(),
),
Halt::FailedToPublishCompressedBytecodes => {
Self::UnexpectedVMBehavior("Failed to publish compressed bytecodes".to_string())
}
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Implementation of "executing" methods, e.g. `eth_call`.
use multivm::{
interface::{TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface},
interface::{TxExecutionMode, VmExecutionResultAndLogs, VmInterface},
tracers::StorageInvocations,
vm_latest::constants::ETH_CALL_GAS_LIMIT,
MultiVMTracer,
Expand Down Expand Up @@ -94,7 +94,7 @@ pub(crate) async fn execute_tx_eth_call(
// limiting the amount of gas the call can use.
// We can't use `BLOCK_ERGS_LIMIT` here since the VM itself has some overhead.
tx.common_data.fee.gas_limit = ETH_CALL_GAS_LIMIT.into();
let (vm_result, _) = execute_tx_in_sandbox(
let (vm_result, _, _) = execute_tx_in_sandbox(
vm_permit,
shared_args,
false,
Expand Down Expand Up @@ -125,14 +125,14 @@ pub(crate) async fn execute_tx_in_sandbox(
tx: Transaction,
block_args: BlockArgs,
custom_tracers: Vec<ApiTracer>,
) -> (VmExecutionResultAndLogs, TransactionExecutionMetrics) {
) -> (VmExecutionResultAndLogs, TransactionExecutionMetrics, bool) {
let total_factory_deps = tx
.execute
.factory_deps
.as_ref()
.map_or(0, |deps| deps.len() as u16);

let execution_result = tokio::task::spawn_blocking(move || {
let (published_bytecodes, execution_result) = tokio::task::spawn_blocking(move || {
let span = span!(Level::DEBUG, "execute_in_sandbox").entered();
let result = apply::apply_vm_in_sandbox(
vm_permit,
Expand All @@ -143,15 +143,14 @@ pub(crate) async fn execute_tx_in_sandbox(
tx,
block_args,
|vm, tx| {
vm.push_transaction(tx);
let storage_invocation_tracer =
StorageInvocations::new(execution_args.missed_storage_invocation_limit);
let custom_tracers: Vec<_> = custom_tracers
.into_iter()
.map(|tracer| tracer.into_boxed())
.chain(vec![storage_invocation_tracer.into_tracer_pointer()])
.collect();
vm.inspect(custom_tracers.into(), VmExecutionMode::OneTx)
vm.inspect_transaction_with_bytecode_compression(custom_tracers.into(), tx, true)
},
);
span.exit();
Expand All @@ -162,5 +161,9 @@ pub(crate) async fn execute_tx_in_sandbox(

let tx_execution_metrics =
vm_metrics::collect_tx_execution_metrics(total_factory_deps, &execution_result);
(execution_result, tx_execution_metrics)
(
execution_result,
tx_execution_metrics,
published_bytecodes.is_ok(),
)
}
8 changes: 6 additions & 2 deletions core/lib/zksync_core/src/api_server/tx_sender/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ impl TxSender {
let block_args = BlockArgs::pending(&mut connection).await;
drop(connection);

let (_, tx_metrics) = execute_tx_in_sandbox(
let (_, tx_metrics, published_bytecodes) = execute_tx_in_sandbox(
vm_permit.clone(),
shared_args.clone(),
true,
Expand Down Expand Up @@ -319,6 +319,10 @@ impl TxSender {
return Err(err.into());
}

if !published_bytecodes {
return Err(SubmitTxError::FailedToPublishCompressedBytecodes);
}

let stage_started_at = Instant::now();
self.ensure_tx_executable(tx.clone().into(), &tx_metrics, true)?;

Expand Down Expand Up @@ -588,7 +592,7 @@ impl TxSender {
let vm_execution_cache_misses_limit = self.0.sender_config.vm_execution_cache_misses_limit;
let execution_args =
TxExecutionArgs::for_gas_estimate(vm_execution_cache_misses_limit, &tx, base_fee);
let (exec_result, tx_metrics) = execute_tx_in_sandbox(
let (exec_result, tx_metrics, _) = execute_tx_in_sandbox(
vm_permit,
shared_args,
true,
Expand Down
3 changes: 3 additions & 0 deletions core/lib/zksync_core/src/api_server/tx_sender/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ pub enum SubmitTxError {
/// Error returned from main node
#[error("{0}")]
ProxyError(#[from] zksync_web3_decl::jsonrpsee::core::ClientError),
#[error("not enough gas to publish compressed bytecodes")]
FailedToPublishCompressedBytecodes,
}

impl SubmitTxError {
Expand Down Expand Up @@ -99,6 +101,7 @@ impl SubmitTxError {
Self::InsufficientFundsForTransfer => "insufficient-funds-for-transfer",
Self::IntrinsicGas => "intrinsic-gas",
Self::ProxyError(_) => "proxy-error",
Self::FailedToPublishCompressedBytecodes => "failed-to-publish-compressed-bytecodes",
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub(super) fn execute_tx<S: WriteStorage>(
vm.make_snapshot();
if vm
.execute_transaction_with_bytecode_compression(tx.clone(), true)
.0
.is_ok()
{
vm.pop_snapshot_no_rollback();
Expand All @@ -84,6 +85,7 @@ pub(super) fn execute_tx<S: WriteStorage>(
vm.rollback_to_the_latest_snapshot();
if vm
.execute_transaction_with_bytecode_compression(tx.clone(), false)
.0
.is_err()
{
return Err(anyhow!("compression can't fail if we don't apply it"));
Expand Down
Loading

0 comments on commit 0a010f0

Please sign in to comment.