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 1 commit
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 @@ -14,16 +14,31 @@ use crate::{
};
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::{
LookupMethodsRequest, LookupMethodsResponse,
verify_response::PostActionResponses, 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};

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),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add this option to comment on this protobuf field. It will make this field appear in comment in swagger

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, why kebab-case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// Additional actions the client wants the result to be returned.
/// Currently supports only: "lookup-methods" for Solidity contracts.
repeated string post_actions = 9;

It was added in the previous PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sevenzing What is your suggestion instead of kebab-case?

_ => Err(anyhow::anyhow!("invalid post action")),
}
}
}

pub struct SolidityVerifierService {
client: Arc<SolidityClient>,
}
Expand Down Expand Up @@ -82,53 +97,102 @@ impl SolidityVerifierService {
}
}

fn process_verify_result(
lok52 marked this conversation as resolved.
Show resolved Hide resolved
result: Result<SoliditySuccess, VerificationError>,
post_actions: Vec<VerifyPostAction>,
) -> Result<VerifyResponse, Status> {
match result {
Ok(res) => {
// Process requested post actions
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.contract_name,
&res.compiler_output,
);
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:#?}");
}
}
}
}
}
lok52 marked this conversation as resolved.
Show resolved Hide resolved
Ok(VerifyResponseWrapper::ok(res, Some(post_actions_responses)))
}
Err(err) => match err {
VerificationError::Compilation(_)
| VerificationError::NoMatchingContracts
| VerificationError::CompilerVersionMismatch(_) => Ok(VerifyResponseWrapper::err(err)),
VerificationError::Initialization(_) | VerificationError::VersionNotFound(_) => {
tracing::debug!("invalid argument: {err:#?}");
Err(Status::invalid_argument(err.to_string()))
}
VerificationError::Internal(err) => {
tracing::error!("internal error: {err:#?}");
Err(Status::internal(err.to_string()))
}
},
}
.map(|r| r.into_inner())
}

#[async_trait::async_trait]
impl SolidityVerifier for SolidityVerifierService {
async fn verify_multi_part(
&self,
request: Request<VerifySolidityMultiPartRequest>,
) -> Result<Response<VerifyResponse>, Status> {
let request: VerifySolidityMultiPartRequestWrapper = request.into_inner().into();

let post_actions = request
.post_actions
.iter()
.collect::<HashSet<_>>()
.into_iter()
.map(|action| action.parse())
.collect::<anyhow::Result<Vec<VerifyPostAction>>>()
.map_err(|err| Status::invalid_argument(err.to_string()))?;
lok52 marked this conversation as resolved.
Show resolved Hide resolved

let chain_id = request
.metadata
.as_ref()
.and_then(|metadata| metadata.chain_id.clone())
.unwrap_or_default();
let result = solidity::multi_part::verify(self.client.clone(), request.try_into()?).await;

let response = if let Ok(verification_success) = result {
VerifyResponseWrapper::ok(verification_success)
} else {
let err = result.unwrap_err();
match err {
VerificationError::Compilation(_)
| VerificationError::NoMatchingContracts
| VerificationError::CompilerVersionMismatch(_) => VerifyResponseWrapper::err(err),
VerificationError::Initialization(_) | VerificationError::VersionNotFound(_) => {
tracing::debug!("invalid argument: {err:#?}");
return Err(Status::invalid_argument(err.to_string()));
}
VerificationError::Internal(err) => {
tracing::error!("internal error: {err:#?}");
return Err(Status::internal(err.to_string()));
}
}
};
let response = process_verify_result(result, post_actions)?;

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 = request
.post_actions
.iter()
.collect::<HashSet<_>>()
.into_iter()
.map(|action| action.parse())
.collect::<anyhow::Result<Vec<VerifyPostAction>>>()
.map_err(|err| Status::invalid_argument(err.to_string()))?;

let chain_id = request
.metadata
.as_ref()
Expand All @@ -151,32 +215,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 {
VerifyResponseWrapper::ok(verification_success)
} else {
let err = result.unwrap_err();
match err {
VerificationError::Compilation(_)
| VerificationError::NoMatchingContracts
| VerificationError::CompilerVersionMismatch(_) => VerifyResponseWrapper::err(err),
VerificationError::Initialization(_) | VerificationError::VersionNotFound(_) => {
tracing::debug!("invalid argument: {err:#?}");
return Err(Status::invalid_argument(err.to_string()));
}
VerificationError::Internal(err) => {
tracing::error!("internal error: {err:#?}");
return Err(Status::internal(err.to_string()));
}
}
};
let response = process_verify_result(result, post_actions)?;

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
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ 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, None)),
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 @@ -80,7 +80,7 @@ impl VyperVerifier for VyperVerifierService {
let result = vyper::multi_part::verify(self.client.clone(), request.try_into()?).await;

let response = if let Ok(verification_success) = result {
VerifyResponseWrapper::ok(verification_success)
VerifyResponseWrapper::ok(verification_success, None)
} else {
let err = result.unwrap_err();
match err {
Expand Down Expand Up @@ -134,7 +134,7 @@ impl VyperVerifier for VyperVerifierService {
let result = vyper::standard_json::verify(self.client.clone(), verification_request).await;

let response = if let Ok(verification_success) = result {
VerifyResponseWrapper::ok(verification_success)
VerifyResponseWrapper::ok(verification_success, None)
} else {
let err = result.unwrap_err();
match err {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,17 @@ impl VerifyResponseOk for SourcifySuccess {
}

impl VerifyResponseWrapper {
pub fn ok<T: VerifyResponseOk>(success: T) -> Self {
pub fn ok<T: VerifyResponseOk>(
success: T,
post_action_responses: Option<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,
lok52 marked this conversation as resolved.
Show resolved Hide resolved
}
.into()
}
Expand Down Expand Up @@ -190,7 +191,7 @@ mod tests {
deployed_bytecode_artifacts: Default::default(),
};

let response = VerifyResponseWrapper::ok(verification_success.clone()).into_inner();
let response = VerifyResponseWrapper::ok(verification_success.clone(), None).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(test_case: impl TestCase) {
"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(test_case: impl TestCase, expected_message: &str) {
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 @@ -105,7 +110,8 @@ impl TestCase for Flattened {
"metadata": {
"chainId": self.chain_id,
"contractAddress": self.contract_address
}
},
"postActions": ["lookup-methods"]
lok52 marked this conversation as resolved.
Show resolved Hide resolved
})
}

Expand Down Expand Up @@ -172,6 +178,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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@
"compiler_version": "v0.8.18+commit.87f61d96",
"contract_name": "CarFactory",
"source_code": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\ncontract Car {\n \n string public model;\n address public carAddr;\n\n constructor( string memory _model) {\n model = _model;\n carAddr = address(this);\n }\n}\n\ncontract CarFactory {\n Car[] public cars;\n\n function create( string memory _model) public {\n Car car = new Car( _model);\n cars.push(car);\n }\n\n\n function create2( string memory _model, bytes32 _salt) public {\n Car car = (new Car){salt: _salt}( _model);\n cars.push(car);\n }\n\n \n}\n",
"expected_lookup_methods": {
"lookupMethods": {
"methods":{
"973d5e44": {
"fileName": "source.sol",
"fileOffset": 410,
"length": 144
},
"b6a46b3b": {
"fileName": "source.sol",
"fileOffset": 290,
"length": 113
},
"f7746e36": {
"fileName": "source.sol",
"fileOffset": 266,
"length": 17
}
}
}
},
"expected_compiler_artifacts": {
"abi": [{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"cars","outputs":[{"internalType":"contract Car","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_model","type":"string"}],"name":"create","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_model","type":"string"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"create2","outputs":[],"stateMutability":"nonpayable","type":"function"}],
"userdoc": {"kind":"user","methods":{},"version":1},
Expand Down
4 changes: 3 additions & 1 deletion smart-contract-verifier/smart-contract-verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ pub use compiler::{Compilers, Fetcher, ListFetcher, S3Fetcher, Version};
pub use verifier::{BytecodePart, Error as VerificationError};

pub use crate::sourcify::{SourcifyApiClient, Success as SourcifySuccess};
pub use lookup_methods::{find_methods, LookupMethodsRequest, LookupMethodsResponse};
pub use lookup_methods::{
find_methods, find_methods_from_compiler_output, LookupMethodsRequest, LookupMethodsResponse,
};
pub use solidity::{
Client as SolidityClient, SolcValidator, SolidityCompiler, Success as SoliditySuccess,
};
Expand Down
Loading
Loading