Skip to content

Commit

Permalink
chore: fix tests after the gas changes (#341)
Browse files Browse the repository at this point in the history
* fix: changed evm_setNextBlockTimestamp (#321)

* changed evm_setNextBlockTimestamp argument type to U64

* fixed caller of evm_setNextBlockTimestamp

* setting the timestamp of the next block in evm_setNextBlockTimestamp

* formatted

* updated comment

* fix: returning errors from fork handling (#335)

* not panicking in is_write_initial_internal

* not panicking in fork_network_and_client

* not panicking in get_earlier_transactions_in_same_block

* not panicking in get_block_gas_details

* Update src/fork.rs

Co-authored-by: AnastasiiaVashchuk <[email protected]>

* more specific error message

* more specific error message

* added miniblock number to error message

---------

Co-authored-by: AnastasiiaVashchuk <[email protected]>

* feat: anvil API (#313)

* added evm_setAccountNonce alias

* added anvil_setNonce alias

* added anvil_impersonateAccount & anvil_stopImpersonatingAccount aliases

* added anvil_mine alias

* added anvil_reset alias

* added anvil_setBalance alias

* added anvil_setCode alias

* added anvil_setStorageAt alias

* added anvil_* aliases

* added anvil_mine test

* modified hardhat_impersonateAccount & hardhat_stopImpersonatingAccount
to actually call hardhat_stopImpersonatingAccount, interfere less
with other tests

* added anvil_impersonateAccount & anvil_stopImpersonatingAccount test

* added anvil_setCode test

* added hardhat_setStorageAt and anvil_setStorageAt tests

* formatted

* removed links to anvil namespace

* split tables

* Revert "split tables"

This reverts commit 4a54fe8.

* renamed AnvilNamespaceT::hardhat_mine to anvil_mine

* Update SUPPORTED_APIS.md

Co-authored-by: AnastasiiaVashchuk <[email protected]>

* added section for evm_setAccountNonce

* changed var to let to satisfy linter

* using random address in anvil_setCode test

* added semicolon

* named test account address

* changed var to let to satisfy linter

* Update src/namespaces/anvil.rs

Co-authored-by: AnastasiiaVashchuk <[email protected]>

* removed to.be.within checks

* supressing TypeScript errors

* supressing TypeScript errors

* changed anvil_setCode code type

* using find instead of an explicit loop

---------

Co-authored-by: AnastasiiaVashchuk <[email protected]>

* test: Add test for hardhat_reset (#333)

* add test and do follow up fixes

* run lint

* fix nits

* fix: Update gas estimation logic for forks (#339)

* fix: fork gas estimation

* Additional fixes

* chore: Update release version (#340)

* chore: fix tests after the gas changes

* test: fix unit tests

---------

Co-authored-by: Vaclav Barta <[email protected]>
Co-authored-by: AnastasiiaVashchuk <[email protected]>
Co-authored-by: Nicolas Villanueva <[email protected]>
  • Loading branch information
4 people authored Sep 9, 2024
1 parent b5536a3 commit dfd9aa1
Show file tree
Hide file tree
Showing 24 changed files with 812 additions and 212 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "era_test_node"
version = "0.1.0-alpha.25"
version = "0.1.0-alpha.26"
edition = "2018"
authors = ["The Matter Labs Team <[email protected]>"]
homepage = "https://zksync.io/"
Expand Down
39 changes: 38 additions & 1 deletion SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ The `status` options are:

| Namespace | API | <div style="width:130px">Status</div> | Description |
| --- | --- | --- | --- |
| `ANVIL` | `anvil_setNonce` | `SUPPORTED` | Sets the nonce of an address.|
| `ANVIL` | `anvil_impersonateAccount` | `SUPPORTED` | Impersonate an account |
| `ANVIL` | `anvil_stopImpersonatingAccount` | `SUPPORTED` | Stop impersonating an account after having previously used `anvil_impersonateAccount` |
| `ANVIL` | `anvil_reset` | `PARTIALLY` | Resets the state of the network; cannot revert to past block numbers, unless they're in a fork |
| `ANVIL` | `anvil_mine` | `SUPPORTED` | Mine any number of blocks at once, in constant time |
| `ANVIL` | `anvil_setBalance` | `SUPPORTED` | Modifies the balance of an account |
| `ANVIL` | `anvil_setCode` | `SUPPORTED` | Sets the bytecode of a given account |
| `ANVIL` | `anvil_setStorageAt` | `SUPPORTED` | Sets the storage value at a given key for a given account |
| [`CONFIG`](#config-namespace) | [`config_getShowCalls`](#config_getshowcalls) | `SUPPORTED` | Gets the current value of `show_calls` that's originally set with `--show-calls` option |
| [`CONFIG`](#config-namespace) | [`config_getShowOutputs`](#config_getshowoutputs) | `SUPPORTED` | Gets the current value of `show_outputs` that's originally set with `--show-outputs` option |
| [`CONFIG`](#config-namespace) | [`config_getCurrentTimestamp`](#config_getcurrenttimestamp) | `SUPPORTED` | Gets the value of `current_timestamp` for the node |
Expand Down Expand Up @@ -85,7 +93,7 @@ The `status` options are:
| [`EVM`](#evm-namespace) | [`evm_revert`](#evm_revert) | `SUPPORTED` | Revert the state of the blockchain to a previous snapshot |
| `EVM` | `evm_setAccountBalance` | `NOT IMPLEMENTED` | Sets the given account's balance to the specified WEI value |
| `EVM` | `evm_setAccountCode` | `NOT IMPLEMENTED` | Sets the given account's code to the specified data |
| `EVM` | `evm_setAccountNonce` | `NOT IMPLEMENTED` | Sets the given account's nonce to the specified value |
| [`EVM`](#evm-namespace) | [`evm_setAccountNonce`](#evm_setaccountnonce) | `SUPPORTED` | Sets the given account's nonce to the specified value |
| `EVM` | `evm_setAccountStorageAt` | `NOT IMPLEMENTED` | Sets the given account's storage slot to the specified data |
| `EVM` | `evm_setAutomine` | `NOT IMPLEMENTED` | Enables or disables the automatic mining of new blocks with each new transaction submitted to the network |
| `EVM` | `evm_setBlockGasLimit` | `NOT IMPLEMENTED` | Sets the Block Gas Limit of the network |
Expand Down Expand Up @@ -1669,6 +1677,35 @@ curl --request POST \
}'
```

### `evm_setAccountNonce`

[source](src/node/evm.rs)

Modifies an account's nonce by overwriting it.
The new nonce must be greater than the existing nonce.

#### Arguments

+ `address: Address` - The `Address` whose nonce is to be changed
+ `nonce: U256` - The new nonce

#### Example

```bash
curl --request POST \
--url http://localhost:8011/ \
--header 'content-type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": "1",
"method": "evm_setAccountNonce",
"params": [
"0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
"0x1337"
]
}'
```

### `evm_increaseTime`

[source](src/node/evm.rs)
Expand Down
191 changes: 191 additions & 0 deletions e2e-tests/test/anvil-apis.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { expect } from "chai";
import { Wallet } from "zksync-web3";
import { deployContract, expectThrowsAsync, getTestProvider } from "../helpers/utils";
import { RichAccounts } from "../helpers/constants";
import { ethers } from "hardhat";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import * as hre from "hardhat";
import { keccak256 } from "ethers/lib/utils";
import { BigNumber } from "ethers";

const provider = getTestProvider();

describe("anvil_setBalance", function () {
it("Should update the balance of an account", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const newBalance = ethers.utils.parseEther("42");

// Act
await provider.send("anvil_setBalance", [userWallet.address, newBalance._hex]);

// Assert
const balance = await userWallet.getBalance();
expect(balance.eq(newBalance)).to.true;
});
});

describe("anvil_setNonce", function () {
it("Should update the nonce of an account", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const newNonce = 42;

// Act
await provider.send("anvil_setNonce", [userWallet.address, ethers.utils.hexlify(newNonce)]);

// Assert
const nonce = await userWallet.getNonce();
expect(nonce).to.equal(newNonce);
});
});

describe("anvil_mine", function () {
it("Should mine multiple blocks with a given interval", async function () {
// Arrange
const numberOfBlocks = 100;
const intervalInSeconds = 60;
const startingBlock = await provider.getBlock("latest");
const startingTimestamp: number = await provider.send("config_getCurrentTimestamp", []);

// Act
await provider.send("anvil_mine", [ethers.utils.hexlify(numberOfBlocks), ethers.utils.hexlify(intervalInSeconds)]);

// Assert
const latestBlock = await provider.getBlock("latest");
expect(latestBlock.number).to.equal(startingBlock.number + numberOfBlocks, "Block number mismatch");
expect(latestBlock.timestamp).to.equal(
startingTimestamp + (numberOfBlocks - 1) * intervalInSeconds * 1000 + 1,
"Timestamp mismatch"
);
});
});

describe("anvil_impersonateAccount & anvil_stopImpersonatingAccount", function () {
it("Should allow transfers of funds without knowing the Private Key", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const richAccount = RichAccounts[5].Account;
const beforeBalance = await provider.getBalance(richAccount);

// Act
await provider.send("anvil_impersonateAccount", [richAccount]);

const signer = await ethers.getSigner(richAccount);
const tx = {
to: userWallet.address,
value: ethers.utils.parseEther("0.42"),
};

const recieptTx = await signer.sendTransaction(tx);
await recieptTx.wait();

await provider.send("anvil_stopImpersonatingAccount", [richAccount]);

// Assert
expect((await userWallet.getBalance()).eq(ethers.utils.parseEther("0.42"))).to.true;
expect((await provider.getBalance(richAccount)).eq(beforeBalance.sub(ethers.utils.parseEther("0.42")))).to.true;
});
});

describe("anvil_setCode", function () {
it("Should set code at an address", async function () {
// Arrange
const wallet = new Wallet(RichAccounts[0].PrivateKey);
const deployer = new Deployer(hre, wallet);

const randomWallet = Wallet.createRandom();
const address = randomWallet.address;
const artifact = await deployer.loadArtifact("Return5");
const contractCode = artifact.deployedBytecode;

// Act
await provider.send("anvil_setCode", [address, contractCode]);

// Assert
const result = await provider.send("eth_call", [
{
to: address,
data: keccak256(ethers.utils.toUtf8Bytes("value()")).substring(0, 10),
from: wallet.address,
gas: "0x1000",
gasPrice: "0x0ee6b280",
value: "0x0",
nonce: "0x1",
},
"latest",
]);
expect(BigNumber.from(result).toNumber()).to.eq(5);
});

it("Should reject invalid code", async function () {
const action = async () => {
// Arrange
const wallet = new Wallet(RichAccounts[0].PrivateKey);
const deployer = new Deployer(hre, wallet);

const address = "0x1000000000000000000000000000000000001111";
const artifact = await deployer.loadArtifact("Return5");
const contractCode = artifact.deployedBytecode;
const shortCode = contractCode.slice(0, contractCode.length - 2);

// Act
await provider.send("anvil_setCode", [address, shortCode]);
};

await expectThrowsAsync(action, "bytes must be divisible by 32");
});

it("Should update code with a different smart contract", async function () {
// Arrange
const wallet = new Wallet(RichAccounts[0].PrivateKey);
const deployer = new Deployer(hre, wallet);

const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
expect(await greeter.greet()).to.eq("Hi");
const artifact = await deployer.loadArtifact("Return5");
const newContractCode = artifact.deployedBytecode;

// Act
await provider.send("anvil_setCode", [greeter.address, newContractCode]);

// Assert
const result = await provider.send("eth_call", [
{
to: greeter.address,
data: keccak256(ethers.utils.toUtf8Bytes("value()")).substring(0, 10),
from: wallet.address,
gas: "0x1000",
gasPrice: "0x0ee6b280",
value: "0x0",
nonce: "0x1",
},
"latest",
]);
expect(BigNumber.from(result).toNumber()).to.eq(5);
});
});

describe("anvil_setStorageAt", function () {
it("Should set storage at an address", async function () {
const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
const userWallet = Wallet.createRandom().connect(provider);
await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("3"),
});

const deployer = new Deployer(hre, userWallet);
const artifact = await deployer.loadArtifact("MyERC20");
const token = await deployer.deploy(artifact, ["MyToken", "MyToken", 18]);

const before = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
expect(BigNumber.from(before).toNumber()).to.eq(0);

const value = ethers.utils.hexlify(ethers.utils.zeroPad("0x10", 32));
await provider.send("anvil_setStorageAt", [token.address, "0x0", value]);

const after = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
expect(BigNumber.from(after).toNumber()).to.eq(16);
});
});
19 changes: 17 additions & 2 deletions e2e-tests/test/evm-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ import { Deployer } from "@matterlabs/hardhat-zksync-deploy";

const provider = getTestProvider();

describe("evm_setAccountNonce", function () {
it("Should update the nonce of an account", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const newNonce = 42;

// Act
await provider.send("evm_setAccountNonce", [userWallet.address, ethers.utils.hexlify(newNonce)]);

// Assert
const nonce = await userWallet.getNonce();
expect(nonce).to.equal(newNonce);
});
});

describe("evm_mine", function () {
it("Should mine one block", async function () {
// Arrange
Expand Down Expand Up @@ -56,13 +71,13 @@ describe("evm_setNextBlockTimestamp", function () {
const userWallet = Wallet.createRandom().connect(provider);

// Act
await provider.send("evm_setNextBlockTimestamp", [expectedTimestamp]);
await provider.send("evm_setNextBlockTimestamp", [expectedTimestamp.toString(16)]);

await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("0.1"),
});
expectedTimestamp += 2; // New transaction will add two blocks
expectedTimestamp += 1; // After executing a transaction, the node puts it into a block and increases its current timestamp

// Assert
const newBlockTimestamp = (await provider.getBlock("latest")).timestamp;
Expand Down
50 changes: 46 additions & 4 deletions e2e-tests/test/hardhat-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ describe("hardhat_impersonateAccount & hardhat_stopImpersonatingAccount", functi
it("Should allow transfers of funds without knowing the Private Key", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const beforeBalance = await provider.getBalance(RichAccounts[0].Account);
const beforeBalance = await provider.getBalance(RichAccounts[5].Account);

// Act
await provider.send("hardhat_impersonateAccount", [RichAccounts[0].Account]);
await provider.send("hardhat_impersonateAccount", [RichAccounts[5].Account]);

const signer = await ethers.getSigner(RichAccounts[0].Account);
const signer = await ethers.getSigner(RichAccounts[5].Account);
const tx = {
to: userWallet.address,
value: ethers.utils.parseEther("0.42"),
Expand All @@ -82,9 +82,11 @@ describe("hardhat_impersonateAccount & hardhat_stopImpersonatingAccount", functi
const recieptTx = await signer.sendTransaction(tx);
await recieptTx.wait();

await provider.send("hardhat_stopImpersonatingAccount", [RichAccounts[5].Account]);

// Assert
expect((await userWallet.getBalance()).eq(ethers.utils.parseEther("0.42"))).to.true;
expect((await provider.getBalance(RichAccounts[0].Account)).eq(beforeBalance.sub(ethers.utils.parseEther("0.42"))))
expect((await provider.getBalance(RichAccounts[5].Account)).eq(beforeBalance.sub(ethers.utils.parseEther("0.42"))))
.to.true;
});
});
Expand Down Expand Up @@ -165,3 +167,43 @@ describe("hardhat_setCode", function () {
expect(BigNumber.from(result).toNumber()).to.eq(5);
});
});

describe("hardhat_reset", function () {
it("should return the correct block number after a hardhat_reset", async function () {
const oldBlockNumber = await provider.send("eth_blockNumber", []);

await provider.send("evm_mine", []);
await provider.send("evm_mine", []);

const blockNumber = await provider.send("eth_blockNumber", []);
expect(BigNumber.from(blockNumber).toNumber()).to.be.eq(BigNumber.from(oldBlockNumber).toNumber() + 2);

await provider.send("hardhat_reset", []);
const newBlockNumber = await provider.send("eth_blockNumber", []);
expect(BigNumber.from(newBlockNumber).toNumber()).to.be.eq(0);
});
});

describe("hardhat_setStorageAt", function () {
it("Should set storage at an address", async function () {
const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
const userWallet = Wallet.createRandom().connect(provider);
await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("3"),
});

const deployer = new Deployer(hre, userWallet);
const artifact = await deployer.loadArtifact("MyERC20");
const token = await deployer.deploy(artifact, ["MyToken", "MyToken", 18]);

const before = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
expect(BigNumber.from(before).toNumber()).to.eq(0);

const value = ethers.utils.hexlify(ethers.utils.zeroPad("0x10", 32));
await provider.send("hardhat_setStorageAt", [token.address, "0x0", value]);

const after = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
expect(BigNumber.from(after).toNumber()).to.eq(16);
});
});
Loading

0 comments on commit dfd9aa1

Please sign in to comment.