Skip to content

Commit

Permalink
Merge branch 'main' into jrigada-ct-setup-fork-failure
Browse files Browse the repository at this point in the history
  • Loading branch information
Jrigada authored Aug 14, 2024
2 parents bd9a438 + 7f38caa commit b473eb5
Show file tree
Hide file tree
Showing 12 changed files with 342 additions and 45 deletions.
8 changes: 5 additions & 3 deletions Cargo.lock

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

9 changes: 2 additions & 7 deletions crates/forge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ opener = "0.7"
soldeer.workspace = true

# zk
zksync-web3-rs = { workspace = true }
zksync_types = { workspace = true }
zksync-web3-rs.workspace = true
zksync_types.workspace = true

[target.'cfg(unix)'.dependencies]
tikv-jemallocator = { workspace = true, optional = true }
Expand All @@ -124,11 +124,6 @@ tikv-jemallocator = { workspace = true, optional = true }
anvil.workspace = true
foundry-test-utils.workspace = true

# zk
era_test_node.workspace = true
jsonrpc-core = { git = "https://github.com/matter-labs/jsonrpc.git", branch = "master" }
jsonrpc-http-server = { git = "https://github.com/matter-labs/jsonrpc.git", branch = "master" }

mockall = "0.12"
criterion = "0.5"
paste = "1.0"
Expand Down
2 changes: 0 additions & 2 deletions crates/forge/tests/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,4 @@ mod svm;
mod test_cmd;
mod verify;

mod zksync_node;

mod ext_integration;
4 changes: 2 additions & 2 deletions crates/forge/tests/cli/script.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contains various tests related to `forge script`.
use crate::{constants::TEMPLATE_CONTRACT, zksync_node};
use crate::constants::TEMPLATE_CONTRACT;
use alloy_primitives::{hex, Address, Bytes};
use anvil::{spawn, NodeConfig};
use foundry_test_utils::{rpc, util::OutputExt, ScriptOutcome, ScriptTester};
Expand Down Expand Up @@ -1472,7 +1472,7 @@ forgetest_async!(test_zk_can_execute_script_with_arguments, |prj, cmd| {
factory_deps: Vec<Vec<u8>>,
}

let node = zksync_node::ZkSyncNode::start();
let node = foundry_test_utils::ZkSyncNode::start();

cmd.args(["init", "--force"]).arg(prj.root());
cmd.assert_non_empty_stdout();
Expand Down
67 changes: 67 additions & 0 deletions crates/forge/tests/fixtures/zk/Factory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import "forge-std/Script.sol";
import "../src/Factory.sol";

contract ZkClassicFactoryScript is Script {
function run() external {
vm.startBroadcast();
MyClassicFactory factory = new MyClassicFactory();
factory.create(42);

vm.stopBroadcast();
assert(factory.getNumber() == 42);
}
}

contract ZkConstructorFactoryScript is Script {
function run() external {
vm.startBroadcast();
MyConstructorFactory factory = new MyConstructorFactory(42);

vm.stopBroadcast();
assert(factory.getNumber() == 42);
}
}

contract ZkNestedFactoryScript is Script{
function run() external {
vm.startBroadcast();
MyNestedFactory factory = new MyNestedFactory();
factory.create(42);

vm.stopBroadcast();
assert(factory.getNumber() == 42);
}
}

contract ZkNestedConstructorFactoryScript is Script{
function run() external {
vm.startBroadcast();
MyNestedConstructorFactory factory = new MyNestedConstructorFactory(42);

vm.stopBroadcast();
assert(factory.getNumber() == 42);
}
}

contract ZkUserFactoryScript is Script {
function run() external {
vm.startBroadcast();
MyClassicFactory factory = new MyClassicFactory();
MyUserFactory user = new MyUserFactory();
user.create(address(factory), 42);

vm.stopBroadcast();
assert(user.getNumber(address(factory)) == 42);
}
}

contract ZkUserConstructorFactoryScript is Script{
function run() external {
vm.startBroadcast();
MyConstructorFactory factory = new MyConstructorFactory(42);
MyUserFactory user = new MyUserFactory();

vm.stopBroadcast();
assert(user.getNumber(address(factory)) == 42);
}
}
102 changes: 102 additions & 0 deletions crates/forge/tests/it/zk/factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Forge tests for zksync factory contracts.
use forge::revm::primitives::SpecId;
use foundry_test_utils::{forgetest_async, util, Filter, TestCommand, TestProject, ZkSyncNode};

use crate::{config::TestConfig, test_helpers::TEST_DATA_DEFAULT};

#[tokio::test(flavor = "multi_thread")]
async fn test_zk_can_deploy_in_method() {
let runner = TEST_DATA_DEFAULT.runner_zksync();
{
let filter = Filter::new("testClassicFactory|testNestedFactory", "ZkFactoryTest", ".*");
TestConfig::with_filter(runner, filter).evm_spec(SpecId::SHANGHAI).run().await;
}
}

#[tokio::test(flavor = "multi_thread")]
async fn test_zk_can_deploy_in_constructor() {
let runner = TEST_DATA_DEFAULT.runner_zksync();
{
let filter = Filter::new(
"testConstructorFactory|testNestedConstructorFactory",
"ZkFactoryTest",
".*",
);
TestConfig::with_filter(runner, filter).evm_spec(SpecId::SHANGHAI).run().await;
}
}

#[tokio::test(flavor = "multi_thread")]
async fn test_zk_can_use_predeployed_factory() {
let runner = TEST_DATA_DEFAULT.runner_zksync();
{
let filter = Filter::new("testUser.*", "ZkFactoryTest", ".*");
TestConfig::with_filter(runner, filter).evm_spec(SpecId::SHANGHAI).run().await;
}
}

forgetest_async!(script_zk_can_deploy_in_method, |prj, cmd| {
setup_factory_prj(&mut prj);
run_factory_script_test(prj.root(), &mut cmd, "ZkClassicFactoryScript", 2);
run_factory_script_test(prj.root(), &mut cmd, "ZkNestedFactoryScript", 2);
});

forgetest_async!(script_zk_can_deploy_in_constructor, |prj, cmd| {
setup_factory_prj(&mut prj);
run_factory_script_test(prj.root(), &mut cmd, "ZkConstructorFactoryScript", 1);
run_factory_script_test(prj.root(), &mut cmd, "ZkNestedConstructorFactoryScript", 1);
});

forgetest_async!(script_zk_can_use_predeployed_factory, |prj, cmd| {
setup_factory_prj(&mut prj);
run_factory_script_test(prj.root(), &mut cmd, "ZkUserFactoryScript", 3);
run_factory_script_test(prj.root(), &mut cmd, "ZkUserConstructorFactoryScript", 2);
});

fn setup_factory_prj(prj: &mut TestProject) {
util::initialize(prj.root());
prj.add_source("Factory.sol", include_str!("../../../../../testdata/zk/Factory.sol")).unwrap();
prj.add_script("Factory.s.sol", include_str!("../../fixtures/zk/Factory.s.sol")).unwrap();
}

fn run_factory_script_test(
root: impl AsRef<std::path::Path>,
cmd: &mut TestCommand,
name: &str,
expected_broadcastable_txs: usize,
) {
let node = ZkSyncNode::start();

cmd.arg("script").args([
"--zk-startup",
&format!("./script/Factory.s.sol:{name}"),
"--broadcast",
"--private-key",
"0x3d3cbc973389cb26f657686445bcc75662b415b656078503592ac8c1abb8810e",
"--chain",
"260",
"--gas-estimate-multiplier",
"310",
"--rpc-url",
node.url().as_str(),
"--slow",
"--evm-version",
"shanghai",
]);

assert!(cmd.stdout_lossy().contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL"));

let run_latest = foundry_common::fs::json_files(root.as_ref().join("broadcast").as_path())
.find(|file| file.ends_with("run-latest.json"))
.expect("No broadcast artifacts");

let content = foundry_common::fs::read_to_string(run_latest).unwrap();

let json: serde_json::Value = serde_json::from_str(&content).unwrap();
assert_eq!(
json["transactions"].as_array().expect("broadcastable txs").len(),
expected_broadcastable_txs
);
cmd.forge_fuse();
}
1 change: 1 addition & 0 deletions crates/forge/tests/it/zk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod basic;
mod cheats;
mod contracts;
mod factory;
mod fork;
mod fuzz;
mod invariant;
Expand Down
7 changes: 7 additions & 0 deletions crates/test-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] }
walkdir.workspace = true
rand.workspace = true
snapbox = { version = "0.6.9", features = ["json"] }
tokio.workspace = true

# zk
zksync_types.workspace = true
era_test_node.workspace = true
jsonrpc-core = { git = "https://github.com/matter-labs/jsonrpc.git", branch = "master" }
jsonrpc-http-server = { git = "https://github.com/matter-labs/jsonrpc.git", branch = "master" }

[features]
# feature for integration tests that test external projects
Expand Down
4 changes: 4 additions & 0 deletions crates/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub use util::{TestCommand, TestProject};
mod script;
pub use script::{ScriptOutcome, ScriptTester};

// TODO: remove once anvil supports zksync node
mod zksync;
pub use zksync::ZkSyncNode;

// re-exports for convenience
pub use foundry_compilers;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ use era_test_node::{
},
node::InMemoryNode,
};
use futures::{SinkExt, StreamExt};
use jsonrpc_core::IoHandler;
use zksync_types::H160;

const DEFAULT_PORT: u16 = 18011;

/// List of legacy wallets (address, private key) that we seed with tokens at start.
const LEGACY_RICH_WALLETS: [(&str, &str); 10] = [
(
Expand Down Expand Up @@ -118,26 +115,22 @@ const RICH_WALLETS: [(&str, &str, &str); 10] = [

/// In-memory era-test-node that is stopped when dropped.
pub struct ZkSyncNode {
close_tx: futures::channel::mpsc::Sender<()>,
}

impl Drop for ZkSyncNode {
fn drop(&mut self) {
self.stop();
}
port: u16,
_guard: tokio::sync::oneshot::Sender<()>,
}

impl ZkSyncNode {
/// Returns the server url.
#[inline]
pub fn url(&self) -> String {
format!("http://127.0.0.1:{DEFAULT_PORT}")
format!("http://127.0.0.1:{}", self.port)
}

/// Start era-test-node in memory at the [DEFAULT_PORT]. The server is automatically stopped
/// when the instance is dropped.
/// Start era-test-node in memory, binding a random available port
///
/// The server is automatically stopped when the instance is dropped.
pub fn start() -> Self {
let (tx, mut rx) = futures::channel::mpsc::channel::<()>(1);
let (_guard, _guard_rx) = tokio::sync::oneshot::channel::<()>();

let io_handler = {
let node: InMemoryNode<HttpForkSource> =
Expand Down Expand Up @@ -165,37 +158,36 @@ impl ZkSyncNode {
io.extend_with(ZksNamespaceT::to_delegate(node));
io
};
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.worker_threads(1)
.build()
.unwrap();
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
let (port_tx, port) = tokio::sync::oneshot::channel();

let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), DEFAULT_PORT);
std::thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.worker_threads(2)
.build()
.unwrap();

let server = jsonrpc_http_server::ServerBuilder::new(io_handler)
.threads(1)
.event_loop_executor(runtime.handle().clone())
.start_http(&addr)
.unwrap();

futures::executor::block_on(async {
let _ = rx.next().await;
});
// if no receiver was ready to receive the spawning thread died
_ = port_tx.send(server.address().port());
// we only care that the channel is alive
_ = tokio::task::block_in_place(move || runtime.block_on(_guard_rx));

server.close();
});

// wait for server to start
std::thread::sleep(std::time::Duration::from_millis(600));
let port =
tokio::task::block_in_place(move || tokio::runtime::Handle::current().block_on(port))
.expect("failed to start server");

Self { close_tx: tx }
}

/// Stop the running era-test-node.
pub fn stop(&mut self) {
futures::executor::block_on(async {
let _ = self.close_tx.send(()).await;
});
Self { _guard, port }
}
}
Loading

0 comments on commit b473eb5

Please sign in to comment.