From 9b2df9392d205349707a1c5f5131305be733bfe1 Mon Sep 17 00:00:00 2001 From: romsters Date: Fri, 13 Dec 2024 02:01:49 +0200 Subject: [PATCH] chore: add e2e tests --- e2e-tests-rust/src/lib.rs | 2 +- e2e-tests-rust/src/provider/mod.rs | 2 +- e2e-tests-rust/src/provider/testing.rs | 100 +++++++++++++++++++++---- e2e-tests-rust/src/utils.rs | 5 ++ e2e-tests-rust/tests/lib.rs | 37 ++++++++- 5 files changed, 127 insertions(+), 19 deletions(-) diff --git a/e2e-tests-rust/src/lib.rs b/e2e-tests-rust/src/lib.rs index 59a34eb4..599f42f9 100644 --- a/e2e-tests-rust/src/lib.rs +++ b/e2e-tests-rust/src/lib.rs @@ -5,4 +5,4 @@ mod provider; mod utils; pub use ext::{ReceiptExt, ZksyncWalletProviderExt}; -pub use provider::{init_testing_provider, AnvilZKsyncApi, TestingProvider, DEFAULT_TX_VALUE}; +pub use provider::{init_testing_provider, init_testing_provider_with_http_headers, AnvilZKsyncApi, TestingProvider, DEFAULT_TX_VALUE}; diff --git a/e2e-tests-rust/src/provider/mod.rs b/e2e-tests-rust/src/provider/mod.rs index cf6e0b6d..1dbed218 100644 --- a/e2e-tests-rust/src/provider/mod.rs +++ b/e2e-tests-rust/src/provider/mod.rs @@ -2,4 +2,4 @@ mod anvil_zksync; mod testing; pub use anvil_zksync::AnvilZKsyncApi; -pub use testing::{init_testing_provider, TestingProvider, DEFAULT_TX_VALUE}; +pub use testing::{init_testing_provider, init_testing_provider_with_http_headers, TestingProvider, DEFAULT_TX_VALUE}; diff --git a/e2e-tests-rust/src/provider/testing.rs b/e2e-tests-rust/src/provider/testing.rs index 48e713b6..897d60e4 100644 --- a/e2e-tests-rust/src/provider/testing.rs +++ b/e2e-tests-rust/src/provider/testing.rs @@ -1,21 +1,32 @@ -use crate::utils::LockedPort; +use crate::utils::{LockedPort,get_node_binary_path}; use crate::ReceiptExt; use alloy::network::primitives::{BlockTransactionsKind, HeaderResponse as _}; use alloy::network::{Network, ReceiptResponse as _, TransactionBuilder}; use alloy::primitives::{Address, U256}; +use alloy::signers::local::LocalSigner; use alloy::providers::{ PendingTransaction, PendingTransactionBuilder, PendingTransactionError, Provider, RootProvider, SendableTx, WalletProvider, }; -use alloy::rpc::types::{Block, TransactionRequest}; -use alloy::transports::http::{reqwest, Http}; +use alloy::rpc::{ + types::{Block, TransactionRequest}, + client::RpcClient, +}; +use alloy::transports::http::{ + reqwest, + reqwest::{ + header::HeaderMap, + Client, + }, + Http +}; use alloy::transports::{RpcError, Transport, TransportErrorKind, TransportResult}; use alloy_zksync::network::header_response::HeaderResponse; use alloy_zksync::network::receipt_response::ReceiptResponse; use alloy_zksync::network::transaction_response::TransactionResponse; use alloy_zksync::network::Zksync; -use alloy_zksync::node_bindings::EraTestNode; -use alloy_zksync::provider::{zksync_provider, ProviderBuilderExt}; +use alloy_zksync::node_bindings::{EraTestNode,EraTestNodeError::NoKeysAvailable}; +use alloy_zksync::provider::{zksync_provider, ProviderBuilderExt, layers::era_test_node::EraTestNodeLayer}; use alloy_zksync::wallet::ZksyncWallet; use anyhow::Context as _; use itertools::Itertools; @@ -71,10 +82,7 @@ pub async fn init_testing_provider( .with_recommended_fillers() .on_era_test_node_with_wallet_and_config(|node| { f(node - .path( - std::env::var("ANVIL_ZKSYNC_BINARY_PATH") - .unwrap_or("../target/release/anvil-zksync".to_string()), - ) + .path(get_node_binary_path()) .port(locked_port.port)) }); @@ -93,6 +101,66 @@ pub async fn init_testing_provider( }) } +// Init testing provider which sends specified HTTP headers e.g. for authentication +// Outside of `TestingProvider` to avoid specifying `P` +pub async fn init_testing_provider_with_http_headers( + headers: HeaderMap, + f: impl FnOnce(EraTestNode) -> EraTestNode, +) -> anyhow::Result< + TestingProvider>, Http>, +> { + use alloy::signers::Signer; + + let locked_port = LockedPort::acquire_unused().await?; + let node_layer = EraTestNodeLayer::from( + f( + EraTestNode::new() + .path(get_node_binary_path()) + .port(locked_port.port) + ) + ); + + let client_with_headers = Client::builder().default_headers(headers).build()?; + let rpc_url = node_layer.endpoint_url(); + let http = Http::with_client(client_with_headers, rpc_url); + let rpc_client = RpcClient::new(http, true); + + let default_keys = node_layer.instance().keys().to_vec(); + let (default_key, remaining_keys) = default_keys + .split_first() + .ok_or(NoKeysAvailable)?; + + let default_signer = LocalSigner::from(default_key.clone()) + .with_chain_id(Some(node_layer.instance().chain_id())); + let mut wallet = ZksyncWallet::from(default_signer); + + for key in remaining_keys { + let signer = LocalSigner::from(key.clone()); + wallet.register_signer(signer) + } + + let provider = zksync_provider() + .with_recommended_fillers() + .wallet(wallet) + .layer(node_layer) + .on_client(rpc_client); + + // Grab default rich accounts right after init. Note that subsequent calls to this method + // might return different value as wallet's signers are dynamic and can be changed by the user. + let rich_accounts = provider.signer_addresses().collect::>(); + // Wait for anvil-zksync to get up and be able to respond + // Ignore error response (should not fail here if provider is used with intentionally wrong origin for testing purposes) + let _ = provider.get_chain_id().await; + // Explicitly unlock the port to showcase why we waited above + drop(locked_port); + + Ok(TestingProvider { + inner: provider, + rich_accounts, + _pd: Default::default(), + }) +} + impl TestingProvider where P: FullZksyncProvider, @@ -383,13 +451,7 @@ where self } - /// Builder-pattern method for setting the chain id. - pub fn with_chain_id(mut self, id: u64) -> Self { - self.inner = self.inner.with_chain_id(id); - self - } - - /// Builder-pattern method for setting the recipient. + /// Builder-pattern method for setting the receiver. pub fn with_to(mut self, to: Address) -> Self { self.inner = self.inner.with_to(to); self @@ -401,6 +463,12 @@ where self } + /// Builder-pattern method for setting the chain id. + pub fn with_chain_id(mut self, id: u64) -> Self { + self.inner = self.inner.with_chain_id(id); + self + } + /// Submits transaction to the node. /// /// This does not wait for the transaction to be confirmed, but returns a [`PendingTransactionFinalizable`] diff --git a/e2e-tests-rust/src/utils.rs b/e2e-tests-rust/src/utils.rs index d7c641f7..2a62bd53 100644 --- a/e2e-tests-rust/src/utils.rs +++ b/e2e-tests-rust/src/utils.rs @@ -71,3 +71,8 @@ impl Drop for LockedPort { .unwrap(); } } + +pub fn get_node_binary_path() -> String { + std::env::var("ANVIL_ZKSYNC_BINARY_PATH") + .unwrap_or("../target/release/anvil-zksync".to_string()) +} diff --git a/e2e-tests-rust/tests/lib.rs b/e2e-tests-rust/tests/lib.rs index 05a4f300..42b5ccdc 100644 --- a/e2e-tests-rust/tests/lib.rs +++ b/e2e-tests-rust/tests/lib.rs @@ -2,12 +2,13 @@ use alloy::network::ReceiptResponse; use alloy::providers::ext::AnvilApi; use alloy::providers::Provider; use anvil_zksync_e2e_tests::{ - init_testing_provider, AnvilZKsyncApi, ReceiptExt, ZksyncWalletProviderExt, DEFAULT_TX_VALUE, + init_testing_provider, init_testing_provider_with_http_headers, AnvilZKsyncApi, ReceiptExt, ZksyncWalletProviderExt, DEFAULT_TX_VALUE, }; use alloy::{ primitives::U256, signers::local::PrivateKeySigner, }; +use alloy::transports::http::reqwest::header::{HeaderMap, HeaderValue, ORIGIN}; use std::convert::identity; use std::time::Duration; @@ -363,3 +364,37 @@ async fn set_chain_id() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test] +async fn cli_no_cors() -> anyhow::Result<()> { + let mut headers = HeaderMap::new(); + headers.insert(ORIGIN, HeaderValue::from_static("http://some.origin")); + + // Verify all origins are allowed by default + let provider = init_testing_provider_with_http_headers(headers.clone(), identity).await?; + provider.get_chain_id().await?; + + // Verify no origins are allowed with --no-cors + let provider_with_no_cors = init_testing_provider_with_http_headers(headers.clone(), |node| node.arg("--no-cors=true")).await?; + let error_resp = provider_with_no_cors.get_chain_id().await.unwrap_err(); + assert_eq!(error_resp.to_string().contains("Origin of the request is not whitelisted"), true); + + Ok(()) +} + +#[tokio::test] +async fn cli_allow_origin() -> anyhow::Result<()> { + let mut headers = HeaderMap::new(); + headers.insert(ORIGIN, HeaderValue::from_static("http://some.origin")); + + // Verify allowed origin can make requests + let provider_with_allowed_origin = init_testing_provider_with_http_headers(headers.clone(), |node| node.arg("--allow-origin=http://some.origin")).await?; + provider_with_allowed_origin.get_chain_id().await?; + + // Verify different origin is not allowed + let provider_with_not_allowed_origin = init_testing_provider_with_http_headers(headers.clone(), |node| node.arg("--allow-origin=http://other.origin")).await?; + let error_resp = provider_with_not_allowed_origin.get_chain_id().await.unwrap_err(); + assert_eq!(error_resp.to_string().contains("Origin of the request is not whitelisted"), true); + + Ok(()) +}