Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(contract-verifier): Adapt contract verifier API for EVM bytecodes #3234

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ google-cloud-storage = "0.20.0"
governor = "0.4.2"
hex = "0.4"
http = "1.1"
http-body-util = "0.1.2"
httpmock = "0.7.0"
hyper = "1.3"
insta = "1.29.0"
Expand Down
5 changes: 1 addition & 4 deletions core/bin/contract-verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ publish = false

[dependencies]
zksync_dal.workspace = true
zksync_env_config.workspace = true
zksync_config = { workspace = true, features = ["observability_ext"] }
zksync_contract_verifier_lib.workspace = true
zksync_queued_job_processor.workspace = true
Expand All @@ -21,8 +20,6 @@ zksync_vlog.workspace = true
zksync_core_leftovers.workspace = true

anyhow.workspace = true
clap = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["full"] }
futures.workspace = true
ctrlc.workspace = true
structopt.workspace = true
tracing.workspace = true
53 changes: 18 additions & 35 deletions core/bin/contract-verifier/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::{cell::RefCell, time::Duration};
use std::{path::PathBuf, time::Duration};

use anyhow::Context;
use futures::{channel::mpsc, executor::block_on, SinkExt, StreamExt};
use structopt::StructOpt;
use anyhow::Context as _;
use clap::Parser;
use tokio::sync::watch;
use zksync_config::configs::PrometheusConfig;
use zksync_contract_verifier_lib::ContractVerifier;
Expand All @@ -12,27 +11,31 @@ use zksync_queued_job_processor::JobProcessor;
use zksync_utils::wait_for_tasks::ManagedTasks;
use zksync_vlog::prometheus::PrometheusExporterConfig;

#[derive(StructOpt)]
#[structopt(name = "ZKsync contract code verifier", author = "Matter Labs")]
#[derive(Debug, Parser)]
#[command(name = "ZKsync contract code verifier", author = "Matter Labs")]
struct Opt {
/// Number of jobs to process. If None, runs indefinitely.
#[structopt(long)]
#[arg(long)]
jobs_number: Option<usize>,
/// Path to the configuration file.
#[structopt(long)]
config_path: Option<std::path::PathBuf>,
#[arg(long)]
config_path: Option<PathBuf>,
/// Path to the secrets file.
#[structopt(long)]
secrets_path: Option<std::path::PathBuf>,
#[arg(long)]
secrets_path: Option<PathBuf>,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let opt = Opt::from_args();
let opt = Opt::parse();

let general_config = load_general_config(opt.config_path).context("general config")?;
let database_secrets = load_database_secrets(opt.secrets_path).context("database secrets")?;
let observability_config = general_config
.observability
.context("ObservabilityConfig")?;
let _observability_guard = observability_config.install()?;

let database_secrets = load_database_secrets(opt.secrets_path).context("database secrets")?;
let verifier_config = general_config
.contract_verifier
.context("ContractVerifierConfig")?;
Expand All @@ -46,33 +49,13 @@ async fn main() -> anyhow::Result<()> {
.context("Master DB URL is absent")?,
)
.build()
.await
.unwrap();

let observability_config = general_config
.observability
.context("ObservabilityConfig")?;

let _observability_guard = observability_config.install()?;
.await?;

let (stop_sender, stop_receiver) = watch::channel(false);
let (stop_signal_sender, mut stop_signal_receiver) = mpsc::channel(256);
{
let stop_signal_sender = RefCell::new(stop_signal_sender.clone());
ctrlc::set_handler(move || {
let mut sender = stop_signal_sender.borrow_mut();
block_on(sender.send(true)).expect("Ctrl+C signal send");
})
.expect("Error setting Ctrl+C handler");
}

let contract_verifier = ContractVerifier::new(verifier_config.compilation_timeout(), pool)
.await
.context("failed initializing contract verifier")?;
let tasks = vec![
// TODO PLA-335: Leftovers after the prover DB split.
// The prover connection pool is not used by the contract verifier, but we need to pass it
// since `JobProcessor` trait requires it.
tokio::spawn(contract_verifier.run(stop_receiver.clone(), opt.jobs_number)),
tokio::spawn(
PrometheusExporterConfig::pull(prometheus_config.listener_port).run(stop_receiver),
Expand All @@ -82,7 +65,7 @@ async fn main() -> anyhow::Result<()> {
let mut tasks = ManagedTasks::new(tasks);
tokio::select! {
() = tasks.wait_single() => {},
_ = stop_signal_receiver.next() => {
_ = tokio::signal::ctrl_c() => {
tracing::info!("Stop signal received, shutting down");
},
};
Expand Down
112 changes: 87 additions & 25 deletions core/lib/contract_verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use zksync_dal::{contract_verification_dal::DeployedContractData, ConnectionPool
use zksync_queued_job_processor::{async_trait, JobProcessor};
use zksync_types::{
contract_verification_api::{
CompilationArtifacts, CompilerType, VerificationIncomingRequest, VerificationInfo,
self as api, CompilationArtifacts, VerificationIncomingRequest, VerificationInfo,
VerificationRequest,
},
Address, CONTRACT_DEPLOYER_ADDRESS,
Expand All @@ -35,6 +35,65 @@ mod resolver;
#[cfg(test)]
mod tests;

#[derive(Debug)]
struct ZkCompilerVersions {
/// Version of the base / non-ZK compiler.
pub base: String,
/// Version of the ZK compiler.
pub zk: String,
}

/// Internal counterpart of `ContractVersions` from API that encompasses all supported compilation modes.
#[derive(Debug)]
enum VersionedCompiler {
Solc(String),
#[allow(dead_code)] // TODO (EVM-864): add vyper support
Vyper(String),
ZkSolc(ZkCompilerVersions),
ZkVyper(ZkCompilerVersions),
}

impl From<api::CompilerVersions> for VersionedCompiler {
fn from(versions: api::CompilerVersions) -> Self {
match versions {
api::CompilerVersions::Solc {
compiler_solc_version,
compiler_zksolc_version: None,
} => Self::Solc(compiler_solc_version),

api::CompilerVersions::Solc {
compiler_solc_version,
compiler_zksolc_version: Some(zk),
} => Self::ZkSolc(ZkCompilerVersions {
base: compiler_solc_version,
zk,
}),

api::CompilerVersions::Vyper {
compiler_vyper_version,
compiler_zkvyper_version: None,
} => Self::Vyper(compiler_vyper_version),

api::CompilerVersions::Vyper {
compiler_vyper_version,
compiler_zkvyper_version: Some(zk),
} => Self::ZkVyper(ZkCompilerVersions {
base: compiler_vyper_version,
zk,
}),
}
}
}

impl VersionedCompiler {
fn expected_bytecode_kind(&self) -> BytecodeMarker {
match self {
Self::Solc(_) | Self::Vyper(_) => BytecodeMarker::Evm,
Self::ZkSolc(_) | Self::ZkVyper(_) => BytecodeMarker::EraVm,
}
}
}

enum ConstructorArgs {
Check(Vec<u8>),
Ignore,
Expand Down Expand Up @@ -112,19 +171,19 @@ impl ContractVerifier {
let mut transaction = storage.start_transaction().await?;
transaction
.contract_verification_dal()
.set_zksolc_versions(supported_versions.zksolc)
.set_zksolc_versions(&supported_versions.zksolc)
.await?;
transaction
.contract_verification_dal()
.set_solc_versions(supported_versions.solc)
.set_solc_versions(&supported_versions.solc)
.await?;
transaction
.contract_verification_dal()
.set_zkvyper_versions(supported_versions.zkvyper)
.set_zkvyper_versions(&supported_versions.zkvyper)
.await?;
transaction
.contract_verification_dal()
.set_vyper_versions(supported_versions.vyper)
.set_vyper_versions(&supported_versions.vyper)
.await?;
transaction.commit().await?;
Ok(())
Expand Down Expand Up @@ -214,13 +273,11 @@ impl ContractVerifier {

async fn compile_zksolc(
&self,
version: &ZkCompilerVersions,
req: VerificationIncomingRequest,
) -> Result<CompilationArtifacts, ContractVerifierError> {
let zksolc = self
.compiler_resolver
.resolve_zksolc(&req.compiler_versions)
.await?;
tracing::debug!(?zksolc, ?req.compiler_versions, "resolved compiler");
let zksolc = self.compiler_resolver.resolve_zksolc(version).await?;
tracing::debug!(?zksolc, ?version, "resolved compiler");
let input = ZkSolc::build_input(req)?;

time::timeout(self.compilation_timeout, zksolc.compile(input))
Expand All @@ -230,13 +287,11 @@ impl ContractVerifier {

async fn compile_zkvyper(
&self,
version: &ZkCompilerVersions,
req: VerificationIncomingRequest,
) -> Result<CompilationArtifacts, ContractVerifierError> {
let zkvyper = self
.compiler_resolver
.resolve_zkvyper(&req.compiler_versions)
.await?;
tracing::debug!(?zkvyper, ?req.compiler_versions, "resolved compiler");
let zkvyper = self.compiler_resolver.resolve_zkvyper(version).await?;
tracing::debug!(?zkvyper, ?version, "resolved compiler");
let input = ZkVyper::build_input(req)?;
time::timeout(self.compilation_timeout, zkvyper.compile(input))
.await
Expand All @@ -245,12 +300,10 @@ impl ContractVerifier {

async fn compile_solc(
&self,
version: &str,
req: VerificationIncomingRequest,
) -> Result<CompilationArtifacts, ContractVerifierError> {
let solc = self
.compiler_resolver
.resolve_solc(req.compiler_versions.compiler_version())
.await?;
let solc = self.compiler_resolver.resolve_solc(version).await?;
tracing::debug!(?solc, ?req.compiler_versions, "resolved compiler");
let input = Solc::build_input(req)?;

Expand All @@ -276,15 +329,24 @@ impl ContractVerifier {
return Err(err.into());
}

match (bytecode_marker, compiler_type) {
(BytecodeMarker::EraVm, CompilerType::Solc) => self.compile_zksolc(req).await,
(BytecodeMarker::EraVm, CompilerType::Vyper) => self.compile_zkvyper(req).await,
(BytecodeMarker::Evm, CompilerType::Solc) => self.compile_solc(req).await,
(BytecodeMarker::Evm, CompilerType::Vyper) => {
// TODO: add vyper support
let compiler = VersionedCompiler::from(req.compiler_versions.clone());
if compiler.expected_bytecode_kind() != bytecode_marker {
let err = anyhow::anyhow!(
"bytecode kind expected by compiler {compiler:?} differs from the actual bytecode kind \
of the verified contract ({bytecode_marker:?})",
);
return Err(err.into());
}

match &compiler {
VersionedCompiler::Solc(version) => self.compile_solc(version, req).await,
VersionedCompiler::Vyper(_) => {
// TODO (EVM-864): add vyper support
let err = anyhow::anyhow!("vyper toolchain is not yet supported for EVM contracts");
return Err(err.into());
}
VersionedCompiler::ZkSolc(version) => self.compile_zksolc(version, req).await,
VersionedCompiler::ZkVyper(version) => self.compile_zkvyper(version, req).await,
}
}

Expand Down
Loading
Loading