From 5862c6af10b9c8c9f91447e56a6bff78a2752b69 Mon Sep 17 00:00:00 2001 From: Vaclav Barta Date: Thu, 27 Jun 2024 13:29:07 +0200 Subject: [PATCH 1/3] feat: added hardhat_setStorageAt (#298) * feat: added hardhat_setStorageAt * reformatted * moved import to test module --- SUPPORTED_APIS.md | 2 +- src/namespaces/hardhat.rs | 14 ++++++++++++ src/node/hardhat.rs | 9 ++++++++ src/node/in_memory_ext.rs | 45 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md index 15db090c..f1d56666 100644 --- a/SUPPORTED_APIS.md +++ b/SUPPORTED_APIS.md @@ -108,7 +108,7 @@ The `status` options are: | `HARDHAT` | `hardhat_setNextBlockBaseFeePerGas` | `NOT IMPLEMENTED` | Sets the base fee per gas for the next block | | `HARDHAT` | `hardhat_setPrevRandao` | `NOT IMPLEMENTED` | Sets the PREVRANDAO value of the next block | | [`HARDHAT`](#hardhat-namespace) | [`hardhat_setNonce`](#hardhat_setnonce) | `SUPPORTED` | Sets the nonce of a given account | -| `HARDHAT` | `hardhat_setStorageAt` | `NOT IMPLEMENTED` | Sets the storage value at a given key for a given account | +| [`HARDHAT`](#hardhat-namespace) | [`hardhat_setStorageAt`](#hardhat_setstorageat) | `SUPPORTED` | Sets the storage value at a given key for a given account | | [`HARDHAT`](#hardhat-namespace) | [`hardhat_stopImpersonatingAccount`](#hardhat_stopimpersonatingaccount) | `SUPPORTED` | Stop impersonating an account after having previously used `hardhat_impersonateAccount` | | [`NETWORK`](#network-namespace) | [`net_version`](#net_version) | `SUPPORTED` | Returns the current network id
_(default is `260`)_ | | [`NETWORK`](#network-namespace) | [`net_peerCount`](#net_peercount) | `SUPPORTED` | Returns the number of peers currently connected to the client
_(hard-coded to `0`)_ | diff --git a/src/namespaces/hardhat.rs b/src/namespaces/hardhat.rs index 5a69f6b5..08d2a87c 100644 --- a/src/namespaces/hardhat.rs +++ b/src/namespaces/hardhat.rs @@ -86,4 +86,18 @@ pub trait HardhatNamespaceT { /// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation. #[rpc(name = "hardhat_setCode")] fn set_code(&self, address: Address, code: Vec) -> RpcResult<()>; + + /// Directly modifies the storage of a contract at a specified slot. + /// + /// # Arguments + /// + /// * `address` - The contract address whose storage is to be modified. + /// * `slot` - The storage slot to modify. + /// * `value` - The value to be set at the specified slot. + /// + /// # Returns + /// + /// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation. + #[rpc(name = "hardhat_setStorageAt")] + fn set_storage_at(&self, address: Address, slot: U256, value: U256) -> RpcResult; } diff --git a/src/node/hardhat.rs b/src/node/hardhat.rs index 619d8572..fd2a66f1 100644 --- a/src/node/hardhat.rs +++ b/src/node/hardhat.rs @@ -64,4 +64,13 @@ impl HardhatNam }) .into_boxed_future() } + + fn set_storage_at(&self, address: Address, slot: U256, value: U256) -> RpcResult { + self.set_storage_at(address, slot, value) + .map_err(|err| { + tracing::error!("failed setting storage: {:?}", err); + into_jsrpc_error(Web3Error::InternalError(err)) + }) + .into_boxed_future() + } } diff --git a/src/node/in_memory_ext.rs b/src/node/in_memory_ext.rs index a25e2b25..899d6d29 100644 --- a/src/node/in_memory_ext.rs +++ b/src/node/in_memory_ext.rs @@ -1,8 +1,9 @@ use anyhow::anyhow; -use zksync_basic_types::{Address, U256, U64}; +use zksync_basic_types::{AccountTreeId, Address, U256, U64}; use zksync_types::{ get_code_key, get_nonce_key, utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance}, + StorageKey, }; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -323,6 +324,17 @@ impl InMemoryNo Ok(()) }) } + + pub fn set_storage_at(&self, address: Address, slot: U256, value: U256) -> Result { + self.get_inner() + .write() + .map_err(|err| anyhow!("failed acquiring lock: {:?}", err)) + .map(|mut writer| { + let key = StorageKey::new(AccountTreeId::new(address), u256_to_h256(slot)); + writer.fork_storage.set_value(key, u256_to_h256(value)); + true + }) + } } #[cfg(test)] @@ -332,6 +344,7 @@ mod tests { use crate::{http_fork_source::HttpForkSource, node::InMemoryNode}; use std::str::FromStr; use zksync_basic_types::{Nonce, H256}; + use zksync_state::ReadStorage; use zksync_types::{api::BlockNumber, fee::Fee, l2::L2Tx, PackedEthSignature}; #[tokio::test] @@ -529,6 +542,36 @@ mod tests { assert_eq!(new_code, code_after); } + #[tokio::test] + async fn test_set_storage_at() { + let node = InMemoryNode::::default(); + let address = Address::repeat_byte(0x1); + let slot = U256::from(37); + let value = U256::from(42); + + let key = StorageKey::new(AccountTreeId::new(address), u256_to_h256(slot)); + let value_before = node + .get_inner() + .write() + .unwrap() + .fork_storage + .read_value(&key); + assert_eq!(H256::default(), value_before); + + let result = node + .set_storage_at(address, slot, value) + .expect("failed setting value"); + assert!(result); + + let value_after = node + .get_inner() + .write() + .unwrap() + .fork_storage + .read_value(&key); + assert_eq!(value, h256_to_u256(value_after)); + } + #[tokio::test] async fn test_increase_time_zero_value() { let node = InMemoryNode::::default(); From f8e2b97f884b1f3514ecbd4349307399410ecf77 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 27 Jun 2024 15:44:47 +0400 Subject: [PATCH 2/3] feat: Treat 'run' as default command (#305) --- .github/workflows/e2e.yml | 2 +- src/main.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 4db7e764..87a0f230 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -33,7 +33,7 @@ jobs: tar -xzf era_test_node-${{ matrix.os }}.tar.gz chmod +x era_test_node echo "Starting node in background" - ./era_test_node run 2>&1 | tee era_test_node_output.log & + ./era_test_node 2>&1 | tee era_test_node_output.log & echo "PID=$!" >> $GITHUB_ENV - name: Launch tests diff --git a/src/main.rs b/src/main.rs index d61f9177..9e1c56da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -212,7 +212,7 @@ enum DevSystemContracts { #[command(author = "Matter Labs", version, about = "Test Node", long_about = None)] struct Cli { #[command(subcommand)] - command: Command, + command: Option, #[arg(long, default_value = "8011")] /// Port to listen on - default: 8011 port: u16, @@ -335,7 +335,9 @@ async fn main() -> anyhow::Result<()> { }, }; - let fork_details = match &opt.command { + // Use `Command::Run` as default. + let command = opt.command.as_ref().unwrap_or(&Command::Run); + let fork_details = match &command { Command::Run => None, Command::Fork(fork) => { Some(ForkDetails::from_network(&fork.network, fork.fork_at, cache_config).await) @@ -347,7 +349,7 @@ async fn main() -> anyhow::Result<()> { // If we're replaying the transaction, we need to sync to the previous block // and then replay all the transactions that happened in - let transactions_to_replay = if let Command::ReplayTx(replay_tx) = &opt.command { + let transactions_to_replay = if let Command::ReplayTx(replay_tx) = command { fork_details .as_ref() .unwrap() From dd6d2f463eb9697dc2365899a72ae12dae3ec809 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 27 Jun 2024 15:56:00 +0400 Subject: [PATCH 3/3] fix: Include transaction type in the transaction receipt (#304) --- e2e-tests/test/eth-apis.test.ts | 28 ++++++++++++++++++++++++++++ src/node/in_memory.rs | 4 +++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/e2e-tests/test/eth-apis.test.ts b/e2e-tests/test/eth-apis.test.ts index 6a367e0e..fb6c6d19 100644 --- a/e2e-tests/test/eth-apis.test.ts +++ b/e2e-tests/test/eth-apis.test.ts @@ -1,4 +1,5 @@ import { expect } from "chai"; +import { Wallet } from "zksync-web3"; import { expectThrowsAsync, getTestProvider } from "../helpers/utils"; import { RichAccounts } from "../helpers/constants"; import { ethers } from "ethers"; @@ -17,6 +18,33 @@ describe("eth_accounts", function () { // Assert expect(accounts).to.deep.equal(richAccounts); }); + + it("Should have required fields in transaction receipt", async function () { + // Arrange + const wallet = new Wallet(RichAccounts[0].PrivateKey, provider); + const tx = await wallet.sendTransaction({ + to: wallet.address, + value: ethers.utils.parseEther("3"), + }); + const response = await tx.wait(); + const txHash = response.transactionHash; + + // Act + const receipt = await provider.send("eth_getTransactionReceipt", [txHash]); + + // Assert + expect(receipt).to.have.property("blockHash"); + expect(receipt).to.have.property("blockNumber"); + expect(receipt).to.have.property("transactionHash"); + expect(receipt).to.have.property("transactionIndex"); + expect(receipt).to.have.property("from"); + expect(receipt).to.have.property("to"); + expect(receipt).to.have.property("cumulativeGasUsed"); + expect(receipt).to.have.property("gasUsed"); + expect(receipt).to.have.property("logs"); + expect(receipt).to.have.property("logsBloom"); + expect(receipt).to.have.property("type"); + }); }); describe("eth_sendTransaction", function () { diff --git a/src/node/in_memory.rs b/src/node/in_memory.rs index 3f3a5717..d8b52887 100644 --- a/src/node/in_memory.rs +++ b/src/node/in_memory.rs @@ -1560,6 +1560,7 @@ impl InMemoryNode { /// Runs L2 transaction and commits it to a new block. pub fn run_l2_tx(&self, l2_tx: L2Tx, execution_mode: TxExecutionMode) -> Result<(), String> { let tx_hash = l2_tx.hash(); + let transaction_type = l2_tx.common_data.transaction_type; tracing::info!(""); tracing::info!("Validating {}", format!("{:?}", tx_hash).bold()); @@ -1672,7 +1673,8 @@ impl InMemoryNode { U64::from(1) }, effective_gas_price: Some(inner.fee_input_provider.l2_gas_price.into()), - ..Default::default() + transaction_type: Some((transaction_type as u32).into()), + logs_bloom: Default::default(), }; let debug = create_debug_output(&l2_tx, &result, call_traces).expect("create debug output"); // OK to unwrap here as Halt is handled above inner.tx_results.insert(