Skip to content

Commit

Permalink
feat: Switch contract verification API to axum and get rid of actix-w…
Browse files Browse the repository at this point in the history
…eb usage (#1467)

## What ❔

- Switches contract verification API to axum.
- Switches an eth-signer test to use axum.
- Adds support of contract verification API to the node framework.

## Why ❔

We use axum for web servers nowadays.

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk fmt` and `zk lint`.
- [ ] Spellcheck has been run via `zk spellcheck`.
- [ ] Linkcheck has been run via `zk linkcheck`.
  • Loading branch information
popzxc committed Mar 22, 2024
1 parent 14a8b78 commit e7a9d61
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 462 deletions.
486 changes: 184 additions & 302 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions core/lib/eth_signer/Cargo.toml
Expand Up @@ -27,7 +27,6 @@ jsonrpc-core = "18.0.0"
async-trait = "0.1"

[dev-dependencies]
actix-rt = "2"
tokio = { version = "1", features = ["full"] }
actix-web = "4.0.0-beta.8"
axum = "0.7"
futures = "0.3"
47 changes: 24 additions & 23 deletions core/lib/eth_signer/src/json_rpc_signer.rs
Expand Up @@ -323,10 +323,12 @@ mod messages {

#[cfg(test)]
mod tests {
use actix_web::{
post,
web::{self, Data},
App, HttpResponse, HttpServer, Responder,
use std::{future::IntoFuture, sync::Arc};

use axum::{
extract::{Json, State},
routing::post,
Router,
};
use futures::future::{AbortHandle, Abortable};
use jsonrpc_core::{Failure, Id, Output, Success, Version};
Expand All @@ -336,8 +338,10 @@ mod tests {
use super::messages::JsonRpcRequest;
use crate::{raw_ethereum_tx::TransactionParameters, EthereumSigner, JsonRpcSigner};

#[post("/")]
async fn index(req: web::Json<JsonRpcRequest>, state: web::Data<State>) -> impl Responder {
async fn index(
State(state): State<Arc<ServerState>>,
Json(req): Json<JsonRpcRequest>,
) -> Json<serde_json::Value> {
let resp = match req.method.as_str() {
"eth_accounts" => {
let mut addresses = vec![];
Expand All @@ -357,7 +361,7 @@ mod tests {
}
_ => create_fail(req.method.clone()),
};
HttpResponse::Ok().json(json!(resp))
Json(json!(resp))
}

fn create_fail(method: String) -> Output {
Expand All @@ -380,43 +384,40 @@ mod tests {
})
}
#[derive(Clone)]
struct State {
struct ServerState {
private_keys: Vec<H256>,
}

fn run_server(state: State) -> (String, AbortHandle) {
async fn run_server(state: ServerState) -> (String, AbortHandle) {
let mut url = None;
let mut server = None;
let app = Router::new()
.route("/", post(index))
.with_state(Arc::new(state));

for i in 9000..9999 {
let new_url = format!("127.0.0.1:{}", i);
// Try to bind to some port, hope that 999 variants will be enough
let tmp_state = state.clone();
if let Ok(ser) = HttpServer::new(move || {
App::new()
.app_data(Data::new(tmp_state.clone()))
.service(index)
})
.bind(new_url.clone())
{
server = Some(ser);
if let Ok(listener) = tokio::net::TcpListener::bind(&new_url).await {
server = Some(axum::serve(listener, app));
url = Some(new_url);
break;
}
}

let server = server.expect("Could not bind to port from 9000 to 9999");
let (abort_handle, abort_registration) = AbortHandle::new_pair();
let future = Abortable::new(server.run(), abort_registration);
let future = Abortable::new(server.into_future(), abort_registration);
tokio::spawn(future);
let address = format!("http://{}/", &url.unwrap());
(address, abort_handle)
}

#[actix_rt::test]
#[tokio::test]
async fn run_client() {
let (address, abort_handle) = run_server(State {
let (address, abort_handle) = run_server(ServerState {
private_keys: vec![H256::repeat_byte(0x17)],
});
})
.await;
// Get address is ok, unlock address is ok, recover address from signature is also ok
let client = JsonRpcSigner::new(address, None, None).await.unwrap();

Expand Down
1 change: 0 additions & 1 deletion core/lib/utils/src/lib.rs
Expand Up @@ -5,7 +5,6 @@ mod convert;
pub mod http_with_retries;
pub mod misc;
pub mod panic_extractor;
pub mod panic_notify;
mod serde_wrappers;
pub mod time;
pub mod wait_for_tasks;
Expand Down
26 changes: 0 additions & 26 deletions core/lib/utils/src/panic_notify.rs

This file was deleted.

4 changes: 0 additions & 4 deletions core/lib/zksync_core/Cargo.toml
Expand Up @@ -90,10 +90,6 @@ axum = { version = "0.6.19", default-features = false, features = [
] }
once_cell = "1.7"

actix-rt = "2.2.0"
actix-cors = "0.6.0-beta.2"
actix-web = "4.0.0-beta.8"

tracing = "0.1.26"

[dev-dependencies]
Expand Down
@@ -1,4 +1,5 @@
use actix_web::web;
use std::sync::Arc;

use zksync_dal::{ConnectionPool, Core};

#[derive(Debug, Clone)]
Expand All @@ -18,34 +19,36 @@ impl RestApi {
}
}

/// Creates an actix-web `Scope`, which can be mounted to the HTTP server.
pub fn into_scope(self) -> actix_web::Scope {
web::scope("")
.app_data(web::Data::new(self))
.route("/contract_verification", web::post().to(Self::verification))
pub fn into_router(self) -> axum::Router<()> {
axum::Router::new()
.route(
"/contract_verification",
axum::routing::post(Self::verification),
)
.route(
"/contract_verification/zksolc_versions",
web::get().to(Self::zksolc_versions),
axum::routing::get(Self::zksolc_versions),
)
.route(
"/contract_verification/solc_versions",
web::get().to(Self::solc_versions),
axum::routing::get(Self::solc_versions),
)
.route(
"/contract_verification/zkvyper_versions",
web::get().to(Self::zkvyper_versions),
axum::routing::get(Self::zkvyper_versions),
)
.route(
"/contract_verification/vyper_versions",
web::get().to(Self::vyper_versions),
axum::routing::get(Self::vyper_versions),
)
.route(
"/contract_verification/{id}",
web::get().to(Self::verification_request_status),
"/contract_verification/:id",
axum::routing::get(Self::verification_request_status),
)
.route(
"/contract_verification/info/{address}",
web::get().to(Self::verification_info),
"/contract_verification/info/:address",
axum::routing::get(Self::verification_info),
)
.with_state(Arc::new(self))
}
}
@@ -1,24 +1,44 @@
use actix_web::{
web::{self, Json},
HttpResponse, Result as ActixResult,
use std::sync::Arc;

use axum::{
extract::{Path, State},
response::Response,
Json,
};
use serde::Serialize;
use zksync_dal::CoreDal;
use zksync_types::{contract_verification_api::VerificationIncomingRequest, Address};

use super::{api_decl::RestApi, metrics::METRICS};

fn ok_json(data: impl Serialize) -> ActixResult<HttpResponse> {
Ok(HttpResponse::Ok().json(data))
fn ok_json(data: impl Serialize) -> Response<String> {
Response::builder()
.status(axum::http::StatusCode::OK)
.body(serde_json::to_string(&data).expect("Failed to serialize"))
.unwrap()
}

fn bad_request(message: &str) -> Response<String> {
Response::builder()
.status(axum::http::StatusCode::BAD_REQUEST)
.body(message.to_string())
.unwrap()
}

fn not_found() -> Response<String> {
Response::builder()
.status(axum::http::StatusCode::NOT_FOUND)
.body(String::new())
.unwrap()
}

impl RestApi {
#[tracing::instrument(skip(query))]
fn validate_contract_verification_query(
query: &VerificationIncomingRequest,
) -> Result<(), HttpResponse> {
) -> Result<(), Response<String>> {
if query.source_code_data.compiler_type() != query.compiler_versions.compiler_type() {
return Err(HttpResponse::BadRequest().body("incorrect compiler versions"));
return Err(bad_request("incorrect compiler versions"));
}

Ok(())
Expand All @@ -27,12 +47,12 @@ impl RestApi {
/// Add a contract verification job to the queue if the requested contract wasn't previously verified.
#[tracing::instrument(skip(self_, request))]
pub async fn verification(
self_: web::Data<Self>,
State(self_): State<Arc<Self>>,
Json(request): Json<VerificationIncomingRequest>,
) -> ActixResult<HttpResponse> {
) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification"].start();
if let Err(res) = Self::validate_contract_verification_query(&request) {
return Ok(res);
return res;
}
let mut storage = self_
.master_connection_pool
Expand All @@ -45,9 +65,7 @@ impl RestApi {
.is_contract_deployed_at_address(request.contract_address)
.await
{
return Ok(
HttpResponse::BadRequest().body("There is no deployed contract on this address")
);
return bad_request("There is no deployed contract on this address");
}

let request_id = storage
Expand All @@ -62,9 +80,9 @@ impl RestApi {

#[tracing::instrument(skip(self_))]
pub async fn verification_request_status(
self_: web::Data<Self>,
id: web::Path<usize>,
) -> ActixResult<HttpResponse> {
State(self_): State<Arc<Self>>,
id: Path<usize>,
) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification_request_status"].start();
let status = self_
.replica_connection_pool
Expand All @@ -79,12 +97,12 @@ impl RestApi {
method_latency.observe();
match status {
Some(status) => ok_json(status),
None => Ok(HttpResponse::NotFound().finish()),
None => not_found(),
}
}

#[tracing::instrument(skip(self_))]
pub async fn zksolc_versions(self_: web::Data<Self>) -> ActixResult<HttpResponse> {
pub async fn zksolc_versions(State(self_): State<Arc<Self>>) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification_zksolc_versions"].start();
let versions = self_
.replica_connection_pool
Expand All @@ -101,7 +119,7 @@ impl RestApi {
}

#[tracing::instrument(skip(self_))]
pub async fn solc_versions(self_: web::Data<Self>) -> ActixResult<HttpResponse> {
pub async fn solc_versions(State(self_): State<Arc<Self>>) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification_solc_versions"].start();
let versions = self_
.replica_connection_pool
Expand All @@ -118,7 +136,7 @@ impl RestApi {
}

#[tracing::instrument(skip(self_))]
pub async fn zkvyper_versions(self_: web::Data<Self>) -> ActixResult<HttpResponse> {
pub async fn zkvyper_versions(State(self_): State<Arc<Self>>) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification_zkvyper_versions"].start();
let versions = self_
.replica_connection_pool
Expand All @@ -135,7 +153,7 @@ impl RestApi {
}

#[tracing::instrument(skip(self_))]
pub async fn vyper_versions(self_: web::Data<Self>) -> ActixResult<HttpResponse> {
pub async fn vyper_versions(State(self_): State<Arc<Self>>) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification_vyper_versions"].start();
let versions = self_
.replica_connection_pool
Expand All @@ -153,9 +171,9 @@ impl RestApi {

#[tracing::instrument(skip(self_))]
pub async fn verification_info(
self_: web::Data<Self>,
address: web::Path<Address>,
) -> ActixResult<HttpResponse> {
State(self_): State<Arc<Self>>,
address: Path<Address>,
) -> Response<String> {
let method_latency = METRICS.call[&"contract_verification_info"].start();

let info = self_
Expand All @@ -171,7 +189,7 @@ impl RestApi {
method_latency.observe();
match info {
Some(info) => ok_json(info),
None => Ok(HttpResponse::NotFound().finish()),
None => not_found(),
}
}
}

0 comments on commit e7a9d61

Please sign in to comment.