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

Add lookup-methods post-action for solidity verifier #682

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ use crate::{
},
settings::{Extensions, FetcherSettings, S3FetcherSettings, SoliditySettings},
types::{
LookupMethodsRequestWrapper, LookupMethodsResponseWrapper, StandardJsonParseError,
VerifyResponseWrapper, VerifySolidityMultiPartRequestWrapper,
VerifySolidityStandardJsonRequestWrapper,
parse_post_actions, LookupMethodsRequestWrapper, LookupMethodsResponseWrapper,
StandardJsonParseError, VerifyPostAction, VerifyResponseWrapper,
VerifySolidityMultiPartRequestWrapper, VerifySolidityStandardJsonRequestWrapper,
},
};
use s3::{creds::Credentials, Bucket, Region};
use smart_contract_verifier::{
find_methods, solidity, Compilers, Fetcher, ListFetcher, S3Fetcher, SolcValidator,
SolidityClient, SolidityCompiler, VerificationError,
find_methods, find_methods_from_compiler_output, solidity, Compilers, Fetcher, ListFetcher,
S3Fetcher, SolcValidator, SolidityClient, SolidityCompiler, SoliditySuccess, VerificationError,
};
use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{
BytecodeType, LookupMethodsRequest, LookupMethodsResponse,
verify_response::PostActionResponses, BytecodeType, LookupMethodsRequest, LookupMethodsResponse,
};
use std::{str::FromStr, sync::Arc};
use std::{collections::HashSet, str::FromStr, sync::Arc};
use tokio::sync::Semaphore;
use tonic::{Request, Response, Status};
use uuid::Uuid;
Expand Down Expand Up @@ -90,6 +90,10 @@ impl SolidityVerifier for SolidityVerifierService {
request: Request<VerifySolidityMultiPartRequest>,
) -> Result<Response<VerifyResponse>, Status> {
let request: VerifySolidityMultiPartRequestWrapper = request.into_inner().into();

let post_actions = parse_post_actions(&request.post_actions)
.map_err(|err| Status::invalid_argument(err.to_string()))?;

let chain_id = request
.metadata
.as_ref()
Expand Down Expand Up @@ -124,43 +128,26 @@ impl SolidityVerifier for SolidityVerifierService {

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

let response = if let Ok(verification_success) = result {
tracing::info!(request_id=request_id.to_string(), match_type=?verification_success.match_type, "Request processed successfully");
VerifyResponseWrapper::ok(verification_success)
} else {
let err = result.unwrap_err();
tracing::info!(request_id=request_id.to_string(), 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!(
request_id = request_id.to_string(),
"internal error: {err:#?}"
);
return Err(Status::internal(err.to_string()));
}
}
};
let response = process_verify_result(result, post_actions, request_id)?;

metrics::count_verify_contract(
chain_id.as_ref(),
"solidity",
response.status().as_str_name(),
"multi-part",
);
Ok(Response::new(response.into_inner()))
Ok(Response::new(response))
}

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

let post_actions = parse_post_actions(&request.post_actions)
.map_err(|err| Status::invalid_argument(err.to_string()))?;

let chain_id = request
.metadata
.as_ref()
Expand Down Expand Up @@ -210,36 +197,15 @@ impl SolidityVerifier for SolidityVerifierService {
let result =
solidity::standard_json::verify(self.client.clone(), verification_request).await;

let response = if let Ok(verification_success) = result {
tracing::info!(request_id=request_id.to_string(), match_type=?verification_success.match_type, "Request processed successfully");
VerifyResponseWrapper::ok(verification_success)
} else {
let err = result.unwrap_err();
tracing::info!(request_id=request_id.to_string(), 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!(
request_id = request_id.to_string(),
"internal error: {err:#?}"
);
return Err(Status::internal(err.to_string()));
}
}
};
let response = process_verify_result(result, post_actions, request_id)?;

metrics::count_verify_contract(
chain_id.as_ref(),
"solidity",
response.status().as_str_name(),
"standard-json",
);
Ok(Response::new(response.into_inner()))
Ok(Response::new(response))
}

async fn list_compiler_versions(
Expand Down Expand Up @@ -296,3 +262,60 @@ fn new_bucket(settings: &S3FetcherSettings) -> anyhow::Result<Arc<Bucket>> {
)?);
Ok(bucket)
}

fn process_verify_result(
result: Result<SoliditySuccess, VerificationError>,
post_actions: HashSet<VerifyPostAction>,
request_id: blockscout_display_bytes::Bytes,
) -> Result<VerifyResponse, Status> {
match result {
Ok(res) => {
tracing::info!(request_id=request_id.to_string(), match_type=?res.match_type, "Request processed successfully");
let post_actions_responses = process_post_actions(&res, &post_actions);
Ok(VerifyResponseWrapper::ok(res, post_actions_responses))
}
Err(err) => {
tracing::info!(request_id=request_id.to_string(), err=%err, "Request processing failed");
match err {
VerificationError::Compilation(_)
| VerificationError::NoMatchingContracts
| VerificationError::CompilerVersionMismatch(_) => Ok(VerifyResponseWrapper::err(err)),
VerificationError::Initialization(_) | VerificationError::VersionNotFound(_) => {
Err(Status::invalid_argument(err.to_string()))
}
VerificationError::Internal(err) => {
tracing::error!(
request_id = request_id.to_string(),
"internal error: {err:#?}"
);
Err(Status::internal(err.to_string()))
}
}
},
}
.map(|r| r.into_inner())
}

fn process_post_actions(
res: &SoliditySuccess,
post_actions: &HashSet<VerifyPostAction>,
) -> PostActionResponses {
let mut post_actions_responses: PostActionResponses = Default::default();
for action in post_actions {
match action {
VerifyPostAction::LookupMethods => {
let methods = find_methods_from_compiler_output(res);
match methods {
Ok(methods) => {
let response = LookupMethodsResponseWrapper::from(methods);
post_actions_responses.lookup_methods = Some(response.into());
}
Err(err) => {
tracing::error!("lookup-methods error: {err:#?}");
}
}
}
}
}
post_actions_responses
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ fn process_verification_result(
response: Result<sc_sourcify::Success, Error>,
) -> Result<VerifyResponseWrapper, Status> {
match response {
Ok(verification_success) => Ok(VerifyResponseWrapper::ok(verification_success)),
Ok(verification_success) => Ok(VerifyResponseWrapper::ok(
verification_success,
Default::default(),
)),
Err(err) => match err {
Error::Internal(err) => {
tracing::error!("internal error: {err:#?}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl VyperVerifier for VyperVerifierService {

let response = if let Ok(verification_success) = result {
tracing::info!(request_id=request_id.to_string(), match_type=?verification_success.match_type, "Request processed successfully");
VerifyResponseWrapper::ok(verification_success)
VerifyResponseWrapper::ok(verification_success, Default::default())
} else {
let err = result.unwrap_err();
tracing::info!(request_id=request_id.to_string(), err=%err, "Request processing failed");
Expand Down Expand Up @@ -193,7 +193,7 @@ impl VyperVerifier for VyperVerifierService {

let response = if let Ok(verification_success) = result {
tracing::info!(request_id=request_id.to_string(), match_type=?verification_success.match_type, "Request processed successfully");
VerifyResponseWrapper::ok(verification_success)
VerifyResponseWrapper::ok(verification_success, Default::default())
} else {
let err = result.unwrap_err();
tracing::info!(request_id=request_id.to_string(), err=%err, "Request processing failed");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
mod errors;
mod lookup_methods;
mod solidity_multi_part;
mod solidity_standard_json;
mod source;
mod sourcify;
mod sourcify_from_etherscan;
mod verify_post_actions;
mod verify_response;
mod vyper_multi_part;
mod vyper_standard_json;

mod lookup_methods;

pub use self::sourcify::VerifySourcifyRequestWrapper;
pub use errors::StandardJsonParseError;
pub use lookup_methods::{LookupMethodsRequestWrapper, LookupMethodsResponseWrapper};
pub use solidity_multi_part::VerifySolidityMultiPartRequestWrapper;
pub use solidity_standard_json::VerifySolidityStandardJsonRequestWrapper;
pub use sourcify_from_etherscan::VerifyFromEtherscanSourcifyRequestWrapper;
pub use verify_post_actions::{parse_post_actions, VerifyPostAction};
pub use verify_response::VerifyResponseWrapper;
pub use vyper_multi_part::VerifyVyperMultiPartRequestWrapper;
pub use vyper_standard_json::VerifyVyperStandardJsonRequestWrapper;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::{collections::HashSet, str::FromStr};

#[derive(PartialEq, Eq, Hash)]
pub enum VerifyPostAction {
LookupMethods,
}

impl FromStr for VerifyPostAction {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"lookup-methods" => Ok(VerifyPostAction::LookupMethods),
_ => Err(anyhow::anyhow!("invalid post action {s}")),
}
}
}
pub fn parse_post_actions(actions: &[String]) -> anyhow::Result<HashSet<VerifyPostAction>> {
actions.iter().map(|action| action.parse()).collect()
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,14 @@ impl VerifyResponseOk for SourcifySuccess {
}

impl VerifyResponseWrapper {
pub fn ok<T: VerifyResponseOk>(success: T) -> Self {
pub fn ok<T: VerifyResponseOk>(success: T, post_action_responses: PostActionResponses) -> Self {
let (source, extra_data) = success.result();
VerifyResponse {
message: "OK".to_string(),
status: Status::Success.into(),
source: Some(source),
extra_data: Some(extra_data),
post_action_responses: Some(PostActionResponses {
lookup_methods: None,
}),
post_action_responses: Some(post_action_responses),
}
.into()
}
Expand Down Expand Up @@ -190,7 +188,8 @@ mod tests {
deployed_bytecode_artifacts: Default::default(),
};

let response = VerifyResponseWrapper::ok(verification_success.clone()).into_inner();
let response = VerifyResponseWrapper::ok(verification_success.clone(), Default::default())
.into_inner();

let expected = VerifyResponse {
message: "OK".to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,24 @@ async fn test_success<T: TestCase>(test_case: &T, bytecode_type: BytecodeType) {
"Invalid deployed bytecode artifacts"
)
}

if let Some(expected_lookup_methods) = test_case.lookup_methods() {
let lookup_methods = verification_response
.post_action_responses
.map(|value| {
serde_json::to_value(&value).unwrap_or_else(|err| {
panic!(
"Lookup methods serialization failed: {:?}; err: {}",
value, err
)
})
})
.expect("Lookup methods are missing");
assert_eq!(
lookup_methods, expected_lookup_methods,
"Invalid lookup methods"
)
}
}

async fn _test_failure<T: TestCase>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub trait TestCase {
fn deployed_bytecode_artifacts(&self) -> Option<serde_json::Value> {
None
}

fn lookup_methods(&self) -> Option<serde_json::Value> {
None
}
}

pub fn from_file<T: TestCase + DeserializeOwned>(test_case: &str) -> T {
Expand All @@ -79,6 +83,7 @@ pub struct Flattened {
pub expected_compiler_artifacts: Option<serde_json::Value>,
pub expected_creation_input_artifacts: Option<serde_json::Value>,
pub expected_deployed_bytecode_artifacts: Option<serde_json::Value>,
pub expected_lookup_methods: Option<serde_json::Value>,

// Verification metadata related values
pub chain_id: Option<String>,
Expand All @@ -92,6 +97,10 @@ impl TestCase for Flattened {

fn to_request(&self, bytecode_type: BytecodeType) -> serde_json::Value {
let extension = if self.is_yul() { "yul" } else { "sol" };
let mut post_actions = vec![];
if self.expected_lookup_methods.is_some() {
post_actions.push("lookup-methods");
}
let bytecode = match bytecode_type {
BytecodeType::Unspecified | BytecodeType::CreationInput => {
self.creation_bytecode.as_str()
Expand All @@ -111,7 +120,8 @@ impl TestCase for Flattened {
"metadata": {
"chainId": self.chain_id,
"contractAddress": self.contract_address
}
},
"postActions": post_actions
})
}

Expand Down Expand Up @@ -178,6 +188,10 @@ impl TestCase for Flattened {
fn deployed_bytecode_artifacts(&self) -> Option<serde_json::Value> {
self.expected_deployed_bytecode_artifacts.clone()
}

fn lookup_methods(&self) -> Option<serde_json::Value> {
self.expected_lookup_methods.clone()
}
}

#[derive(Debug, Clone, Deserialize)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
"compiler_version": "v0.8.18+commit.87f61d96",
"contract_name": "Storage",
"source_code": "// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.7.0 <0.9.0;\n\n/**\n * @title Storage\n * @dev Store & retrieve value in a variable\n */\ncontract Storage {\n\n uint256 number;\n\n /**\n * Some user related comment.\n * @dev Store value in variable\n * @param num value to store\n */\n function store(uint256 num) public {\n number = num;\n }\n\n /**\n * @dev Return value \n * @return value of 'number'\n */\n function retrieve() public view returns (uint256){\n return number;\n }\n}",
"expected_lookup_methods": {
"lookupMethods": {
"methods":{
"2e64cec1": {
"fileName": "source.sol",
"fileOffset": 450,
"length": 79
},
"6057361d": {
"fileName": "source.sol",
"fileOffset": 305,
"length": 64
}
}
}
},
"expected_compiler_artifacts": {
"abi": [{"inputs":[],"name":"retrieve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"store","outputs":[],"stateMutability":"nonpayable","type":"function"}],
"userdoc": {"kind":"user","methods":{"store(uint256)":{"notice":"Some user related comment."}},"version":1},
Expand Down
Loading
Loading