diff --git a/Cargo.lock b/Cargo.lock index 76ca212a..9e7520b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1810,7 +1810,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era_test_node" -version = "0.1.0-alpha.27" +version = "0.1.0-alpha.28" dependencies = [ "anyhow", "bigdecimal 0.3.1", diff --git a/Cargo.toml b/Cargo.toml index 65fdca76..4af27db2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "era_test_node" -version = "0.1.0-alpha.27" +version = "0.1.0-alpha.28" edition = "2018" authors = ["The Matter Labs Team "] homepage = "https://zksync.io/" diff --git a/e2e-tests/contracts/Fib.sol b/e2e-tests/contracts/Fib.sol new file mode 100644 index 00000000..d6053f1b --- /dev/null +++ b/e2e-tests/contracts/Fib.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract Fib { + function fib(uint256 n) public pure returns (uint256) { + return n <= 1 ? 1 : fib(n - 1) + fib(n - 2); + } +} diff --git a/e2e-tests/test/fib.test.ts b/e2e-tests/test/fib.test.ts new file mode 100644 index 00000000..0673d065 --- /dev/null +++ b/e2e-tests/test/fib.test.ts @@ -0,0 +1,24 @@ +import { Wallet } from "zksync-web3"; +import * as hre from "hardhat"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; +import { RichAccounts } from "../helpers/constants"; +import { deployContract, expectThrowsAsync, getTestProvider } from "../helpers/utils"; + +const provider = getTestProvider(); + +describe("Test Fib error flags", function () { + it("Should print to the console NOT ENOUGH ERGS", async function () { + const action = async () => { + const wallet = new Wallet(RichAccounts[0].PrivateKey, provider); + + const deployer = new Deployer(hre, wallet); + const fib = await deployContract(deployer, "Fib"); + await fib.fib(100); + }; + + // This is expected to throw and the console is expected to show: + // XX:YY:ZZ ERROR !! Got error flags: + // XX:YY:ZZ ERROR NOT ENOUGH ERGS + await expectThrowsAsync(action, "call revert exception"); + }); +}); diff --git a/src/node/call_error_tracer.rs b/src/node/call_error_tracer.rs new file mode 100644 index 00000000..8af50257 --- /dev/null +++ b/src/node/call_error_tracer.rs @@ -0,0 +1,62 @@ +use zksync_multivm::{ + tracers::dynamic::vm_1_5_0::DynTracer, + vm_latest::{HistoryMode, SimpleMemory, VmTracer}, + zk_evm_latest::{ + tracing::{AfterDecodingData, VmLocalStateData}, + vm_state::ErrorFlags, + }, +}; +use zksync_state::interface::WriteStorage; + +pub struct CallErrorTracer {} + +impl CallErrorTracer { + pub fn new() -> Self { + Self {} + } +} + +impl DynTracer> for CallErrorTracer { + fn after_decoding( + &mut self, + _state: VmLocalStateData<'_>, + data: AfterDecodingData, + _memory: &SimpleMemory, + ) { + if !data.error_flags_accumulated.is_empty() { + tracing::error!("!! Got error flags: "); + if data + .error_flags_accumulated + .contains(ErrorFlags::INVALID_OPCODE) + { + tracing::error!("INVALID OPCODE"); + } + if data + .error_flags_accumulated + .contains(ErrorFlags::NOT_ENOUGH_ERGS) + { + tracing::error!("NOT ENOUGH ERGS"); + } + if data + .error_flags_accumulated + .contains(ErrorFlags::PRIVILAGED_ACCESS_NOT_FROM_KERNEL) + { + tracing::error!("PRIVILEGED ACCESS"); + } + if data + .error_flags_accumulated + .contains(ErrorFlags::WRITE_IN_STATIC_CONTEXT) + { + tracing::error!("WRITE IN STATIC"); + } + if data + .error_flags_accumulated + .contains(ErrorFlags::CALLSTACK_IS_FULL) + { + tracing::error!("CALLSTACK IS FULL"); + } + } + } +} + +impl VmTracer for CallErrorTracer {} diff --git a/src/node/in_memory.rs b/src/node/in_memory.rs index 653f2111..5e6b70f4 100644 --- a/src/node/in_memory.rs +++ b/src/node/in_memory.rs @@ -12,7 +12,10 @@ use crate::{ filters::EthFilters, fork::{block_on, ForkDetails, ForkSource, ForkStorage}, formatter, - node::{fee_model::TestNodeFeeInputProvider, storage_logs::print_storage_logs_details}, + node::{ + call_error_tracer::CallErrorTracer, fee_model::TestNodeFeeInputProvider, + storage_logs::print_storage_logs_details, + }, observability::Observability, system_contracts::{self, SystemContracts}, utils::{bytecode_to_factory_dep, create_debug_output, into_jsrpc_error, to_human_size}, @@ -1036,9 +1039,11 @@ impl InMemoryNode { let call_tracer_result = Arc::new(OnceCell::default()); - let custom_tracer = CallTracer::new(call_tracer_result.clone()).into_tracer_pointer(); - - let tx_result = vm.inspect(&mut custom_tracer.into(), VmExecutionMode::OneTx); + let tracers = vec![ + CallErrorTracer::new().into_tracer_pointer(), + CallTracer::new(call_tracer_result.clone()).into_tracer_pointer(), + ]; + let tx_result = vm.inspect(&mut tracers.into(), VmExecutionMode::OneTx); let call_traces = Arc::try_unwrap(call_tracer_result) .unwrap() @@ -1345,6 +1350,7 @@ impl InMemoryNode { let call_tracer_result = Arc::new(OnceCell::default()); let bootloader_debug_result = Arc::new(OnceCell::default()); + tracers.push(CallErrorTracer::new().into_tracer_pointer()); tracers.push(CallTracer::new(call_tracer_result.clone()).into_tracer_pointer()); tracers.push( BootloaderDebugTracer { diff --git a/src/node/mod.rs b/src/node/mod.rs index 463bb87e..9dccd2dd 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -1,6 +1,7 @@ //! In-memory node, that supports forking other networks. mod anvil; +mod call_error_tracer; mod config_api; mod debug; mod eth;