use std::collections::HashSet;
use anyhow::Context as _;
use zksync_dal::{Connection, ConnectionPool, Core, CoreDal};
use zksync_multivm::{
interface::{ExecutionResult, VmExecutionMode, VmInterface},
tracers::{
validator::{self, ValidationTracer, ValidationTracerParams},
StorageInvocations,
},
vm_latest::HistoryDisabled,
MultiVMTracer,
};
use zksync_types::{l2::L2Tx, Address, Transaction, TRUSTED_ADDRESS_SLOTS, TRUSTED_TOKEN_SLOTS};
use super::{
apply,
execute::TransactionExecutor,
vm_metrics::{SandboxStage, EXECUTION_METRICS, SANDBOX_METRICS},
BlockArgs, TxExecutionArgs, TxSharedArgs, VmPermit,
};
#[derive(Debug, thiserror::Error)]
pub(crate) enum ValidationError {
#[error("VM validation error: {0}")]
Vm(validator::ValidationError),
#[error("Internal error")]
Internal(#[from] anyhow::Error),
}
impl TransactionExecutor {
pub(crate) async fn validate_tx_in_sandbox(
&self,
connection_pool: ConnectionPool<Core>,
vm_permit: VmPermit,
tx: L2Tx,
shared_args: TxSharedArgs,
block_args: BlockArgs,
computational_gas_limit: u32,
) -> Result<(), ValidationError> {
if let Self::Mock(mock) = self {
return mock.validate_tx(tx, &block_args);
}
let stage_latency = SANDBOX_METRICS.sandbox[&SandboxStage::ValidateInSandbox].start();
let mut connection = connection_pool
.connection_tagged("api")
.await
.context("failed acquiring DB connection")?;
let validation_params = get_validation_params(
&mut connection,
&tx,
computational_gas_limit,
&shared_args.whitelisted_tokens_for_aa,
)
.await
.context("failed getting validation params")?;
drop(connection);
let execution_args = TxExecutionArgs::for_validation(&tx);
let tx: Transaction = tx.into();
let validation_result = tokio::task::spawn_blocking(move || {
let span = tracing::debug_span!("validate_in_sandbox").entered();
let result = apply::apply_vm_in_sandbox(
vm_permit,
shared_args,
true,
&execution_args,
&connection_pool,
tx,
block_args,
|vm, tx, protocol_version| {
let stage_latency = SANDBOX_METRICS.sandbox[&SandboxStage::Validation].start();
let span = tracing::debug_span!("validation").entered();
vm.push_transaction(tx);
let (tracer, validation_result) = ValidationTracer::<HistoryDisabled>::new(
validation_params,
protocol_version.into(),
);
let result = vm.inspect(
vec![
tracer.into_tracer_pointer(),
StorageInvocations::new(execution_args.missed_storage_invocation_limit)
.into_tracer_pointer(),
]
.into(),
VmExecutionMode::OneTx,
);
let result = match (result.result, validation_result.get()) {
(_, Some(err)) => {
Err(validator::ValidationError::ViolatedRule(err.clone()))
}
(ExecutionResult::Halt { reason }, _) => {
Err(validator::ValidationError::FailedTx(reason))
}
(_, None) => Ok(()),
};
stage_latency.observe();
span.exit();
result
},
);
span.exit();
result
})
.await
.context("transaction validation panicked")??;
stage_latency.observe();
validation_result.map_err(ValidationError::Vm)
}
}
async fn get_validation_params(
connection: &mut Connection<'_, Core>,
tx: &L2Tx,
computational_gas_limit: u32,
whitelisted_tokens_for_aa: &[Address],
) -> anyhow::Result<ValidationTracerParams> {
let method_latency = EXECUTION_METRICS.get_validation_params.start();
let user_address = tx.common_data.initiator_address;
let paymaster_address = tx.common_data.paymaster_params.paymaster;
let all_bridged_tokens = connection.tokens_dal().get_all_l2_token_addresses().await?;
let all_tokens: Vec<_> = all_bridged_tokens
.iter()
.chain(whitelisted_tokens_for_aa)
.collect();
EXECUTION_METRICS.tokens_amount.set(all_tokens.len());
let span = tracing::debug_span!("compute_trusted_slots_for_validation").entered();
let trusted_slots: HashSet<_> = all_tokens
.iter()
.flat_map(|&token| TRUSTED_TOKEN_SLOTS.iter().map(move |&slot| (*token, slot)))
.collect();
let trusted_addresses = HashSet::new();
let trusted_address_slots: HashSet<_> = all_tokens
.into_iter()
.flat_map(|token| {
TRUSTED_ADDRESS_SLOTS
.iter()
.map(move |&slot| (*token, slot))
})
.collect();
EXECUTION_METRICS
.trusted_address_slots_amount
.set(trusted_address_slots.len());
span.exit();
method_latency.observe();
Ok(ValidationTracerParams {
user_address,
paymaster_address,
trusted_slots,
trusted_addresses,
trusted_address_slots,
computational_gas_limit,
})
}