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(verifier): update vyper standard-json verification approach #1297

Merged
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use crate::settings::{FetcherSettings, S3FetcherSettings};
use cron::Schedule;
use s3::{creds::Credentials, Bucket, Region};
use smart_contract_verifier::{
DetailedVersion, Fetcher, FileValidator, ListFetcher, S3Fetcher, Version,
};
use smart_contract_verifier::{Fetcher, FileValidator, ListFetcher, S3Fetcher, Version};
use std::{path::PathBuf, str::FromStr, sync::Arc};

pub async fn initialize_fetcher<Ver: Version>(
Expand Down Expand Up @@ -73,30 +71,38 @@ fn new_bucket(settings: &S3FetcherSettings) -> anyhow::Result<Arc<Bucket>> {
Ok(bucket)
}

/// Normalizes the requested compiler version by matching it against a list of known compiler versions.
/// The function takes a [`DetailedVersion`] from the request and attempts to find a corresponding version
/// from the known list, allowing for cases where the requested commit hash is either a prefix or a longer
/// version than the known one. If a matching version is found, it is returned; otherwise, a
/// [`Status::invalid_argument`] error is returned.
pub fn normalize_request_compiler_version(
compilers: &[DetailedVersion],
request_compiler_version: &DetailedVersion,
) -> Result<DetailedVersion, tonic::Status> {
let corresponding_known_compiler_version = compilers.iter().find(|&version| {
version.version() == request_compiler_version.version()
&& version.date() == request_compiler_version.date()
&& (version
.commit()
.starts_with(request_compiler_version.commit())
|| request_compiler_version
.commit()
.starts_with(version.commit()))
});
if let Some(compiler_version) = corresponding_known_compiler_version {
Ok(compiler_version.clone())
} else {
Err(tonic::Status::invalid_argument(format!(
"Compiler version not found: {request_compiler_version}"
)))
}
macro_rules! process_solo_verification_request_conversion {
($maybe_verification_request:expr) => {
match $maybe_verification_request {
Ok(request) => request,
Err(err @ smart_contract_verifier::RequestParseError::InvalidContent(_)) => {
let response = $crate::types::VerifyResponseWrapper::err(err).into_inner();
tracing::info!(response=?response, "request processed");
return Ok(Response::new(response));
}
Err(err @ smart_contract_verifier::RequestParseError::BadRequest(_)) => {
tracing::info!(err=%err, "bad request");
return Err(tonic::Status::invalid_argument(err.to_string()));
}
}
};
}
pub(crate) use process_solo_verification_request_conversion;

macro_rules! process_batch_verification_request_conversion {
($maybe_verification_request:expr) => {
match $maybe_verification_request {
Ok(request) => request,
Err(err @ smart_contract_verifier::RequestParseError::InvalidContent(_)) => {
let response = $crate::types::batch_verification::compilation_error(err.to_string());
tracing::info!(response=?response, "request processed");
return Ok(Response::new(response));
}
Err(err @ smart_contract_verifier::RequestParseError::BadRequest(_)) => {
tracing::info!(err=%err, "bad request");
return Err(tonic::Status::invalid_argument(err.to_string()));
}
}
};
}
pub(crate) use process_batch_verification_request_conversion;
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ use crate::{
services::common,
settings::SoliditySettings,
types,
types::{LookupMethodsRequestWrapper, LookupMethodsResponseWrapper, VerifyResponseWrapper},
types::{LookupMethodsRequestWrapper, LookupMethodsResponseWrapper},
};
use anyhow::Context;
use smart_contract_verifier::{
find_methods, solidity, solidity::RequestParseError, Compilers, SolcValidator, SolidityClient,
SolidityCompiler,
find_methods, solidity, Compilers, SolcValidator, SolidityClient, SolidityCompiler,
};
use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{
LookupMethodsRequest, LookupMethodsResponse,
Expand Down Expand Up @@ -80,18 +79,8 @@ impl SolidityVerifier for SolidityVerifierService {

let maybe_verification_request =
solidity::multi_part::VerificationRequest::try_from(request);
let verification_request = match maybe_verification_request {
Ok(request) => request,
Err(err @ RequestParseError::InvalidContent(_)) => {
let response = VerifyResponseWrapper::err(err).into_inner();
tracing::info!(response=?response, "request processed");
return Ok(Response::new(response));
}
Err(err @ RequestParseError::BadRequest(_)) => {
tracing::info!(err=%err, "bad request");
return Err(Status::invalid_argument(err.to_string()));
}
};
let verification_request =
common::process_solo_verification_request_conversion!(maybe_verification_request);

let result = solidity::multi_part::verify(self.client.clone(), verification_request).await;

Expand Down Expand Up @@ -131,18 +120,8 @@ impl SolidityVerifier for SolidityVerifierService {

let maybe_verification_request =
solidity::standard_json::VerificationRequest::try_from(request);
let verification_request = match maybe_verification_request {
Ok(request) => request,
Err(err @ RequestParseError::InvalidContent(_)) => {
let response = VerifyResponseWrapper::err(err).into_inner();
tracing::info!(response=?response, "request processed");
return Ok(Response::new(response));
}
Err(err @ RequestParseError::BadRequest(_)) => {
tracing::info!(err=%err, "bad request");
return Err(Status::invalid_argument(err.to_string()));
}
};
let verification_request =
common::process_solo_verification_request_conversion!(maybe_verification_request);

let result =
solidity::standard_json::verify(self.client.clone(), verification_request).await;
Expand All @@ -169,18 +148,8 @@ impl SolidityVerifier for SolidityVerifierService {

let maybe_verification_request =
solidity::multi_part::BatchVerificationRequest::try_from(request);
let verification_request = match maybe_verification_request {
Ok(request) => request,
Err(err @ RequestParseError::InvalidContent(_)) => {
let response = types::batch_verification::compilation_error(err.to_string());
tracing::info!(response=?response, "request processed");
return Ok(Response::new(response));
}
Err(err @ RequestParseError::BadRequest(_)) => {
tracing::info!(err=%err, "bad request");
return Err(Status::invalid_argument(err.to_string()));
}
};
let verification_request =
common::process_batch_verification_request_conversion!(maybe_verification_request);

let result =
solidity::multi_part::batch_verify(self.client.clone(), verification_request).await;
Expand All @@ -201,18 +170,8 @@ impl SolidityVerifier for SolidityVerifierService {

let maybe_verification_request =
solidity::standard_json::BatchVerificationRequest::try_from(request);
let verification_request = match maybe_verification_request {
Ok(request) => request,
Err(err @ RequestParseError::InvalidContent(_)) => {
let response = types::batch_verification::compilation_error(err.to_string());
tracing::info!(response=?response, "request processed");
return Ok(Response::new(response));
}
Err(err @ RequestParseError::BadRequest(_)) => {
tracing::info!(err=%err, "bad request");
return Err(Status::invalid_argument(err.to_string()));
}
};
let verification_request =
common::process_batch_verification_request_conversion!(maybe_verification_request);

let result =
solidity::standard_json::batch_verify(self.client.clone(), verification_request).await;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use crate::{
metrics,
proto::{
vyper_verifier_server::VyperVerifier, BytecodeType, ListCompilerVersionsRequest,
vyper_verifier_server::VyperVerifier, ListCompilerVersionsRequest,
ListCompilerVersionsResponse, VerifyResponse, VerifyVyperMultiPartRequest,
VerifyVyperStandardJsonRequest,
},
services::common,
settings::VyperSettings,
types::{
StandardJsonParseError, VerifyResponseWrapper, VerifyVyperMultiPartRequestWrapper,
VerifyVyperStandardJsonRequestWrapper,
},
types,
};
use anyhow::Context;
use smart_contract_verifier::{vyper, Compilers, VerificationError, VyperClient, VyperCompiler};
use smart_contract_verifier::{vyper, Compilers, VyperClient, VyperCompiler};
use std::sync::Arc;
use tokio::sync::Semaphore;
use tonic::{Request, Response, Status};
Expand Down Expand Up @@ -52,153 +49,81 @@ impl VyperVerifier for VyperVerifierService {
&self,
request: Request<VerifyVyperMultiPartRequest>,
) -> Result<Response<VerifyResponse>, Status> {
let request: VerifyVyperMultiPartRequestWrapper = request.into_inner().into();
let request = request.into_inner();

let chain_id = request
.metadata
.as_ref()
.and_then(|metadata| metadata.chain_id.clone())
.unwrap_or_default();
.and_then(|metadata| metadata.chain_id.clone());
let contract_address = request
.metadata
.as_ref()
.and_then(|metadata| metadata.contract_address.clone())
.unwrap_or_default();
.and_then(|metadata| metadata.contract_address.clone());
tracing::info!(
chain_id = chain_id,
contract_address = contract_address,
"Vyper multi-part verification request received"
);

tracing::debug!(
bytecode = request.bytecode,
bytecode_type = BytecodeType::try_from(request.bytecode_type)
.unwrap()
.as_str_name(),
compiler_version = request.compiler_version,
evm_version = request.evm_version,
source_files = ?request.source_files,
interfaces = ?request.interfaces,
"Request details"
chain_id =? chain_id,
contract_address =? contract_address,
"vyper multi-part verification request received"
);

let mut verification_request: vyper::multi_part::VerificationRequest =
request.try_into()?;
verification_request.compiler_version = common::normalize_request_compiler_version(
&self.client.compilers().all_versions(),
&verification_request.compiler_version,
)?;
let maybe_verification_request = vyper::multi_part::VerificationRequest::try_from(request);
let verification_request =
common::process_solo_verification_request_conversion!(maybe_verification_request);

let result = vyper::multi_part::verify(self.client.clone(), verification_request).await;

let response = if let Ok(verification_success) = result {
tracing::info!(match_type=?verification_success.match_type, "Request processed successfully");
VerifyResponseWrapper::ok(verification_success)
} else {
let err = result.unwrap_err();
tracing::info!(err=%err, "Request processing failed");
match err {
VerificationError::Compilation(_)
| VerificationError::NoMatchingContracts
| VerificationError::CompilerVersionMismatch(_) => VerifyResponseWrapper::err(err),
VerificationError::Initialization(_) | VerificationError::VersionNotFound(_) => {
return Err(Status::invalid_argument(err.to_string()));
}
VerificationError::Internal(err) => {
tracing::error!("internal error: {err:#?}");
return Err(Status::internal(err.to_string()));
}
}
let verify_response = match result {
Ok(value) => types::verification_result::process_verification_result(value)?,
Err(error) => types::verification_result::process_error(error)?,
};

metrics::count_verify_contract(
chain_id.as_ref(),
&chain_id.unwrap_or_default(),
"vyper",
response.status().as_str_name(),
verify_response.status().as_str_name(),
"multi-part",
);
return Ok(Response::new(response.into_inner()));
Ok(Response::new(verify_response))
}

async fn verify_standard_json(
&self,
request: Request<VerifyVyperStandardJsonRequest>,
) -> Result<Response<VerifyResponse>, Status> {
let request: VerifyVyperStandardJsonRequestWrapper = request.into_inner().into();
let request = request.into_inner();

let chain_id = request
.metadata
.as_ref()
.and_then(|metadata| metadata.chain_id.clone())
.unwrap_or_default();
.and_then(|metadata| metadata.chain_id.clone());
let contract_address = request
.metadata
.as_ref()
.and_then(|metadata| metadata.contract_address.clone())
.unwrap_or_default();
.and_then(|metadata| metadata.contract_address.clone());
tracing::info!(
chain_id = chain_id,
contract_address = contract_address,
"Vyper standard-json verification request received"
);

tracing::debug!(
bytecode = request.bytecode,
bytecode_type = BytecodeType::try_from(request.bytecode_type)
.unwrap()
.as_str_name(),
compiler_version = request.compiler_version,
input = request.input,
"Request details"
chain_id =? chain_id,
contract_address =? contract_address,
"vyper standard-json verification request received"
);

let mut verification_request: vyper::standard_json::VerificationRequest = {
let request: Result<_, StandardJsonParseError> = request.try_into();
if let Err(err) = request {
match err {
StandardJsonParseError::InvalidContent(_) => {
let response = VerifyResponseWrapper::err(err).into_inner();
return Ok(Response::new(response));
}
StandardJsonParseError::BadRequest(_) => {
return Err(Status::invalid_argument(err.to_string()));
}
}
}
request.unwrap()
};
verification_request.compiler_version = common::normalize_request_compiler_version(
&self.client.compilers().all_versions(),
&verification_request.compiler_version,
)?;
let maybe_verification_request =
vyper::standard_json::VerificationRequest::try_from(request);
let verification_request =
common::process_solo_verification_request_conversion!(maybe_verification_request);

let result = vyper::standard_json::verify(self.client.clone(), verification_request).await;

let response = if let Ok(verification_success) = result {
tracing::info!(match_type=?verification_success.match_type, "Request processed successfully");
VerifyResponseWrapper::ok(verification_success)
} else {
let err = result.unwrap_err();
tracing::info!(err=%err, "Request processing failed");
match err {
VerificationError::Compilation(_)
| VerificationError::NoMatchingContracts
| VerificationError::CompilerVersionMismatch(_) => VerifyResponseWrapper::err(err),
VerificationError::Initialization(_) | VerificationError::VersionNotFound(_) => {
return Err(Status::invalid_argument(err.to_string()));
}
VerificationError::Internal(err) => {
tracing::error!("internal error: {err:#?}");
return Err(Status::internal(err.to_string()));
}
}
let verify_response = match result {
Ok(value) => types::verification_result::process_verification_result(value)?,
Err(error) => types::verification_result::process_error(error)?,
};

metrics::count_verify_contract(
chain_id.as_ref(),
&chain_id.unwrap_or_default(),
"vyper",
response.status().as_str_name(),
verify_response.status().as_str_name(),
"standard-json",
);
return Ok(Response::new(response.into_inner()));
Ok(Response::new(verify_response))
}

async fn list_compiler_versions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ mod source;
mod sourcify;
mod sourcify_from_etherscan;
mod verify_response;
mod vyper_multi_part;
mod vyper_standard_json;
pub mod zksolc_standard_json;

pub mod batch_verification;
Expand All @@ -16,5 +14,3 @@ pub use errors::StandardJsonParseError;
pub use lookup_methods::{LookupMethodsRequestWrapper, LookupMethodsResponseWrapper};
pub use sourcify_from_etherscan::VerifyFromEtherscanSourcifyRequestWrapper;
pub use verify_response::VerifyResponseWrapper;
pub use vyper_multi_part::VerifyVyperMultiPartRequestWrapper;
pub use vyper_standard_json::VerifyVyperStandardJsonRequestWrapper;
Loading
Loading