From c2ec7c529ae324be8c40cc550aeb978588c94529 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Thu, 16 Nov 2023 14:05:09 +0400 Subject: [PATCH 1/4] Add lookup-methods post-action for solidity verifier --- .../src/services/solidity_verifier.rs | 131 ++++++++++++------ .../src/services/sourcify_verifier.rs | 2 +- .../src/services/vyper_verifier.rs | 4 +- .../src/types/verify_response.rs | 11 +- .../tests/solidity.rs | 18 +++ .../tests/solidity_types.rs | 12 +- .../test_cases_solidity/simple_storage.json | 16 +++ .../test_cases_solidity/two_cbor_auxdata.json | 21 +++ .../smart-contract-verifier/src/lib.rs | 4 +- .../src/lookup_methods/find_methods.rs | 88 ++++++++++-- .../src/lookup_methods/mod.rs | 4 +- 11 files changed, 247 insertions(+), 64 deletions(-) diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs index 8f0520923..a0d1d66dd 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs @@ -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 { + match s { + "lookup-methods" => Ok(VerifyPostAction::LookupMethods), + _ => Err(anyhow::anyhow!("invalid post action")), + } + } +} + pub struct SolidityVerifierService { client: Arc, } @@ -82,6 +97,52 @@ impl SolidityVerifierService { } } +fn process_verify_result( + result: Result, + post_actions: Vec, +) -> Result { + 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:#?}"); + } + } + } + } + } + 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( @@ -89,6 +150,16 @@ impl SolidityVerifier for SolidityVerifierService { request: Request, ) -> Result, Status> { let request: VerifySolidityMultiPartRequestWrapper = request.into_inner().into(); + + let post_actions = request + .post_actions + .iter() + .collect::>() + .into_iter() + .map(|action| action.parse()) + .collect::>>() + .map_err(|err| Status::invalid_argument(err.to_string()))?; + let chain_id = request .metadata .as_ref() @@ -96,24 +167,7 @@ impl SolidityVerifier for SolidityVerifierService { .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(), @@ -121,7 +175,7 @@ impl SolidityVerifier for SolidityVerifierService { response.status().as_str_name(), "multi-part", ); - Ok(Response::new(response.into_inner())) + Ok(Response::new(response)) } async fn verify_standard_json( @@ -129,6 +183,16 @@ impl SolidityVerifier for SolidityVerifierService { request: Request, ) -> Result, Status> { let request: VerifySolidityStandardJsonRequestWrapper = request.into_inner().into(); + + let post_actions = request + .post_actions + .iter() + .collect::>() + .into_iter() + .map(|action| action.parse()) + .collect::>>() + .map_err(|err| Status::invalid_argument(err.to_string()))?; + let chain_id = request .metadata .as_ref() @@ -151,24 +215,7 @@ 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(), @@ -176,7 +223,7 @@ impl SolidityVerifier for SolidityVerifierService { response.status().as_str_name(), "standard-json", ); - Ok(Response::new(response.into_inner())) + Ok(Response::new(response)) } async fn list_compiler_versions( diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs index a9a30d0d5..1850cfd77 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs @@ -96,7 +96,7 @@ fn process_verification_result( response: Result, ) -> Result { 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:#?}"); diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs index 2ce13fe31..7f5f92767 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs @@ -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 { @@ -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 { diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs index a4627fd3e..5f0b00e7d 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs @@ -84,16 +84,17 @@ impl VerifyResponseOk for SourcifySuccess { } impl VerifyResponseWrapper { - pub fn ok(success: T) -> Self { + pub fn ok( + success: T, + post_action_responses: Option, + ) -> 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, } .into() } @@ -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(), diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/solidity.rs b/smart-contract-verifier/smart-contract-verifier-server/tests/solidity.rs index a1a148d30..22f0615cc 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/tests/solidity.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/solidity.rs @@ -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) { diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs b/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs index b2ccaf344..19d7116c4 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs @@ -53,6 +53,10 @@ pub trait TestCase { fn deployed_bytecode_artifacts(&self) -> Option { None } + + fn lookup_methods(&self) -> Option { + None + } } pub fn from_file(test_case: &str) -> T { @@ -79,6 +83,7 @@ pub struct Flattened { pub expected_compiler_artifacts: Option, pub expected_creation_input_artifacts: Option, pub expected_deployed_bytecode_artifacts: Option, + pub expected_lookup_methods: Option, // Verification metadata related values pub chain_id: Option, @@ -105,7 +110,8 @@ impl TestCase for Flattened { "metadata": { "chainId": self.chain_id, "contractAddress": self.contract_address - } + }, + "postActions": ["lookup-methods"] }) } @@ -172,6 +178,10 @@ impl TestCase for Flattened { fn deployed_bytecode_artifacts(&self) -> Option { self.expected_deployed_bytecode_artifacts.clone() } + + fn lookup_methods(&self) -> Option { + self.expected_lookup_methods.clone() + } } #[derive(Debug, Clone, Deserialize)] diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/simple_storage.json b/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/simple_storage.json index 05778f10a..fb706d7e8 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/simple_storage.json +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/simple_storage.json @@ -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}, diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/two_cbor_auxdata.json b/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/two_cbor_auxdata.json index b50e9cc18..dc1b4b1a4 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/two_cbor_auxdata.json +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_solidity/two_cbor_auxdata.json @@ -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}, diff --git a/smart-contract-verifier/smart-contract-verifier/src/lib.rs b/smart-contract-verifier/smart-contract-verifier/src/lib.rs index e93a9862f..8c428f809 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lib.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lib.rs @@ -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, }; diff --git a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs index 96ed5a62b..9539260c2 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs @@ -4,7 +4,7 @@ use super::{ }; use bytes::Bytes; use ethers_core::abi::Abi; -use ethers_solc::sourcemap::SourceMap; +use ethers_solc::{sourcemap::SourceMap, CompilerOutput}; use std::{collections::BTreeMap, iter::repeat}; pub struct LookupMethodsRequest { @@ -18,9 +18,68 @@ pub struct LookupMethodsResponse { pub methods: BTreeMap, } +pub fn find_methods_from_compiler_output( + contract_name: &String, + output: &CompilerOutput, +) -> anyhow::Result { + let file_ids = output + .sources + .iter() + .map(|(name, file)| (file.id, name.clone())) + .collect(); + + let (_, contract) = output + .contracts_iter() + .find(|(name, _)| *name == contract_name) + .ok_or_else(|| anyhow::anyhow!("contract {contract_name} not found"))?; + + let evm = contract + .evm + .as_ref() + .ok_or_else(|| anyhow::anyhow!("evm missing"))?; + let deployed_bytecode = evm + .deployed_bytecode + .as_ref() + .ok_or_else(|| anyhow::anyhow!("deployed bytecode missing"))?; + let bytecode = deployed_bytecode + .bytecode + .as_ref() + .ok_or_else(|| anyhow::anyhow!("bytecode missing"))?; + let source_map = bytecode + .source_map() + .ok_or_else(|| anyhow::anyhow!("source map missing"))??; + let bytecode_raw = &bytecode + .object + .as_bytes() + .ok_or_else(|| anyhow::anyhow!("invalid bytecode"))? + .0; + let methods = parse_selectors_from_method_ids(&evm.method_identifiers)?; + + Ok(find_methods_internal( + methods, + bytecode_raw, + &source_map, + &file_ids, + )) +} + pub fn find_methods(request: LookupMethodsRequest) -> LookupMethodsResponse { - let methods = parse_selectors(request.abi); - let opcodes = disassemble_bytecode(&request.bytecode); + let methods = parse_selectors_from_abi(&request.abi); + find_methods_internal( + methods, + &request.bytecode, + &request.source_map, + &request.file_ids, + ) +} + +fn find_methods_internal( + methods: BTreeMap, + bytecode: &Bytes, + source_map: &SourceMap, + file_ids: &BTreeMap, +) -> LookupMethodsResponse { + let opcodes = disassemble_bytecode(bytecode); let methods = methods .into_iter() @@ -33,13 +92,7 @@ pub fn find_methods(request: LookupMethodsRequest) -> LookupMethodsResponse { } }; - tracing::debug!(func_sig, func_index, "found function"); - let method = match Method::from_source_map( - selector, - &request.source_map, - func_index, - &request.file_ids, - ) { + let method = match Method::from_source_map(selector, source_map, func_index, file_ids) { Ok(m) => m, Err(err) => { tracing::warn!(func_sig, err = err.to_string(), "failed to parse method"); @@ -105,7 +158,20 @@ fn find_src_map_index(selector: &[u8; 4], opcodes: &[DisassembledOpcode]) -> Opt None } -fn parse_selectors(abi: Abi) -> BTreeMap { +fn parse_selectors_from_method_ids( + methods: &BTreeMap, +) -> anyhow::Result> { + let methods: BTreeMap = methods + .iter() + .map(|(name, selector)| { + let mut result = [0u8; 4]; + hex::decode_to_slice(selector, &mut result).map(|_| (name.to_owned(), result)) + }) + .collect::>()?; + Ok(methods) +} + +fn parse_selectors_from_abi(abi: &Abi) -> BTreeMap { abi.functions() .map(|f| (f.signature(), f.short_signature())) .collect() diff --git a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/mod.rs index 8d61a5698..4f3405c07 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/mod.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/mod.rs @@ -3,4 +3,6 @@ mod find_methods; mod method; mod opcodes; -pub use find_methods::{find_methods, LookupMethodsRequest, LookupMethodsResponse}; +pub use find_methods::{ + find_methods, find_methods_from_compiler_output, LookupMethodsRequest, LookupMethodsResponse, +}; From 92f7c198186c6ce20d6da57eab5501ec37369191 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Thu, 16 Nov 2023 15:38:33 +0400 Subject: [PATCH 2/4] Refactor --- .../src/services/solidity_verifier.rs | 136 ++++++++---------- .../src/types/mod.rs | 5 +- .../src/types/verify_post_actions.rs | 24 ++++ .../src/types/verify_response.rs | 4 +- .../tests/solidity_types.rs | 6 +- 5 files changed, 92 insertions(+), 83 deletions(-) create mode 100644 smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs index a0d1d66dd..9f0dbd758 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs @@ -7,9 +7,9 @@ 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}; @@ -20,25 +20,10 @@ use smart_contract_verifier::{ use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{ verify_response::PostActionResponses, LookupMethodsRequest, LookupMethodsResponse, }; -use std::{collections::HashSet, str::FromStr, sync::Arc}; +use std::{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 { - match s { - "lookup-methods" => Ok(VerifyPostAction::LookupMethods), - _ => Err(anyhow::anyhow!("invalid post action")), - } - } -} - pub struct SolidityVerifierService { client: Arc, } @@ -97,52 +82,6 @@ impl SolidityVerifierService { } } -fn process_verify_result( - result: Result, - post_actions: Vec, -) -> Result { - 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:#?}"); - } - } - } - } - } - 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( @@ -151,13 +90,7 @@ impl SolidityVerifier for SolidityVerifierService { ) -> Result, Status> { let request: VerifySolidityMultiPartRequestWrapper = request.into_inner().into(); - let post_actions = request - .post_actions - .iter() - .collect::>() - .into_iter() - .map(|action| action.parse()) - .collect::>>() + let post_actions = parse_post_actions(&request.post_actions) .map_err(|err| Status::invalid_argument(err.to_string()))?; let chain_id = request @@ -184,13 +117,7 @@ impl SolidityVerifier for SolidityVerifierService { ) -> Result, Status> { let request: VerifySolidityStandardJsonRequestWrapper = request.into_inner().into(); - let post_actions = request - .post_actions - .iter() - .collect::>() - .into_iter() - .map(|action| action.parse()) - .collect::>>() + let post_actions = parse_post_actions(&request.post_actions) .map_err(|err| Status::invalid_argument(err.to_string()))?; let chain_id = request @@ -280,3 +207,54 @@ fn new_bucket(settings: &S3FetcherSettings) -> anyhow::Result> { )?); Ok(bucket) } + +fn process_verify_result( + result: Result, + post_actions: Vec, +) -> Result { + match result { + Ok(res) => { + let post_actions_responses = process_post_actions(&res, &post_actions); + 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()) +} + +fn process_post_actions( + res: &SoliditySuccess, + post_actions: &Vec, +) -> 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.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:#?}"); + } + } + } + } + } + post_actions_responses +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs index 644d1fba2..a466c6e1b 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs @@ -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; diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs new file mode 100644 index 000000000..15e8dfe77 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs @@ -0,0 +1,24 @@ +use std::{collections::HashSet, str::FromStr}; + +pub enum VerifyPostAction { + LookupMethods, +} + +impl FromStr for VerifyPostAction { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "lookup-methods" => Ok(VerifyPostAction::LookupMethods), + _ => Err(anyhow::anyhow!("invalid post action {s}")), + } + } +} +pub fn parse_post_actions(actions: &[String]) -> anyhow::Result> { + actions + .iter() + .collect::>() + .into_iter() + .map(|action| action.parse()) + .collect() +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs index 5f0b00e7d..29dd55910 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs @@ -191,7 +191,9 @@ mod tests { deployed_bytecode_artifacts: Default::default(), }; - let response = VerifyResponseWrapper::ok(verification_success.clone(), None).into_inner(); + let response = + VerifyResponseWrapper::ok(verification_success.clone(), Some(Default::default())) + .into_inner(); let expected = VerifyResponse { message: "OK".to_string(), diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs b/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs index 19d7116c4..64074e730 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/solidity_types.rs @@ -97,6 +97,10 @@ impl TestCase for Flattened { fn to_request(&self) -> 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"); + } serde_json::json!({ "bytecode": self.creation_bytecode, "bytecodeType": BytecodeType::CreationInput.as_str_name(), @@ -111,7 +115,7 @@ impl TestCase for Flattened { "chainId": self.chain_id, "contractAddress": self.contract_address }, - "postActions": ["lookup-methods"] + "postActions": post_actions }) } From dad40359a51f3e585e62a433a1fa76cbc771ca77 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Wed, 29 Nov 2023 13:24:52 +0400 Subject: [PATCH 3/4] Adjust default post actions response --- .../src/services/solidity_verifier.rs | 2 +- .../src/services/sourcify_verifier.rs | 5 +++- .../src/services/vyper_verifier.rs | 4 ++-- .../src/types/verify_response.rs | 12 ++++------ .../src/lookup_methods/find_methods.rs | 24 +++++++------------ 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs index 9f0dbd758..b003546e5 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs @@ -215,7 +215,7 @@ fn process_verify_result( match result { Ok(res) => { let post_actions_responses = process_post_actions(&res, &post_actions); - Ok(VerifyResponseWrapper::ok(res, Some(post_actions_responses))) + Ok(VerifyResponseWrapper::ok(res, post_actions_responses)) } Err(err) => match err { VerificationError::Compilation(_) diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs index 1850cfd77..0d8b58075 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/sourcify_verifier.rs @@ -96,7 +96,10 @@ fn process_verification_result( response: Result, ) -> Result { match response { - Ok(verification_success) => Ok(VerifyResponseWrapper::ok(verification_success, None)), + Ok(verification_success) => Ok(VerifyResponseWrapper::ok( + verification_success, + Default::default(), + )), Err(err) => match err { Error::Internal(err) => { tracing::error!("internal error: {err:#?}"); diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs index 7f5f92767..a3cfd9ad3 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs @@ -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, None) + VerifyResponseWrapper::ok(verification_success, Default::default()) } else { let err = result.unwrap_err(); match err { @@ -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, None) + VerifyResponseWrapper::ok(verification_success, Default::default()) } else { let err = result.unwrap_err(); match err { diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs index 29dd55910..32104e945 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs @@ -84,17 +84,14 @@ impl VerifyResponseOk for SourcifySuccess { } impl VerifyResponseWrapper { - pub fn ok( - success: T, - post_action_responses: Option, - ) -> Self { + pub fn ok(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, + post_action_responses: Some(post_action_responses), } .into() } @@ -191,9 +188,8 @@ mod tests { deployed_bytecode_artifacts: Default::default(), }; - let response = - VerifyResponseWrapper::ok(verification_success.clone(), Some(Default::default())) - .into_inner(); + let response = VerifyResponseWrapper::ok(verification_success.clone(), Default::default()) + .into_inner(); let expected = VerifyResponse { message: "OK".to_string(), diff --git a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs index 9539260c2..4518aa48e 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs @@ -53,7 +53,12 @@ pub fn find_methods_from_compiler_output( .as_bytes() .ok_or_else(|| anyhow::anyhow!("invalid bytecode"))? .0; - let methods = parse_selectors_from_method_ids(&evm.method_identifiers)?; + let abi = &contract + .abi + .as_ref() + .ok_or_else(|| anyhow::anyhow!("abi missing"))? + .abi; + let methods = parse_selectors(abi); Ok(find_methods_internal( methods, @@ -64,7 +69,7 @@ pub fn find_methods_from_compiler_output( } pub fn find_methods(request: LookupMethodsRequest) -> LookupMethodsResponse { - let methods = parse_selectors_from_abi(&request.abi); + let methods = parse_selectors(&request.abi); find_methods_internal( methods, &request.bytecode, @@ -158,20 +163,7 @@ fn find_src_map_index(selector: &[u8; 4], opcodes: &[DisassembledOpcode]) -> Opt None } -fn parse_selectors_from_method_ids( - methods: &BTreeMap, -) -> anyhow::Result> { - let methods: BTreeMap = methods - .iter() - .map(|(name, selector)| { - let mut result = [0u8; 4]; - hex::decode_to_slice(selector, &mut result).map(|_| (name.to_owned(), result)) - }) - .collect::>()?; - Ok(methods) -} - -fn parse_selectors_from_abi(abi: &Abi) -> BTreeMap { +fn parse_selectors(abi: &Abi) -> BTreeMap { abi.functions() .map(|f| (f.signature(), f.short_signature())) .collect() From a8cf95ff564fcdcbd5b9e8cd197cea0a52782c64 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Wed, 29 Nov 2023 14:33:47 +0400 Subject: [PATCH 4/4] Fix same contract name issue --- .../src/services/solidity_verifier.rs | 9 +++-- .../src/types/verify_post_actions.rs | 10 ++---- .../src/lookup_methods/find_methods.rs | 35 ++++++++++++------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs index b003546e5..18e9edb73 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs @@ -20,7 +20,7 @@ use smart_contract_verifier::{ use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{ 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}; @@ -210,7 +210,7 @@ fn new_bucket(settings: &S3FetcherSettings) -> anyhow::Result> { fn process_verify_result( result: Result, - post_actions: Vec, + post_actions: HashSet, ) -> Result { match result { Ok(res) => { @@ -236,14 +236,13 @@ fn process_verify_result( fn process_post_actions( res: &SoliditySuccess, - post_actions: &Vec, + post_actions: &HashSet, ) -> 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.contract_name, &res.compiler_output); + let methods = find_methods_from_compiler_output(res); match methods { Ok(methods) => { let response = LookupMethodsResponseWrapper::from(methods); diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs index 15e8dfe77..bf6333fe5 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_post_actions.rs @@ -1,5 +1,6 @@ use std::{collections::HashSet, str::FromStr}; +#[derive(PartialEq, Eq, Hash)] pub enum VerifyPostAction { LookupMethods, } @@ -14,11 +15,6 @@ impl FromStr for VerifyPostAction { } } } -pub fn parse_post_actions(actions: &[String]) -> anyhow::Result> { - actions - .iter() - .collect::>() - .into_iter() - .map(|action| action.parse()) - .collect() +pub fn parse_post_actions(actions: &[String]) -> anyhow::Result> { + actions.iter().map(|action| action.parse()).collect() } diff --git a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs index 4518aa48e..eb3a1af65 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs @@ -2,9 +2,10 @@ use super::{ disassemble::{disassemble_bytecode, DisassembledOpcode}, method::Method, }; +use crate::SoliditySuccess; use bytes::Bytes; use ethers_core::abi::Abi; -use ethers_solc::{sourcemap::SourceMap, CompilerOutput}; +use ethers_solc::sourcemap::SourceMap; use std::{collections::BTreeMap, iter::repeat}; pub struct LookupMethodsRequest { @@ -19,24 +20,37 @@ pub struct LookupMethodsResponse { } pub fn find_methods_from_compiler_output( - contract_name: &String, - output: &CompilerOutput, + res: &SoliditySuccess, ) -> anyhow::Result { - let file_ids = output + let file_ids = res + .compiler_output .sources .iter() .map(|(name, file)| (file.id, name.clone())) .collect(); - let (_, contract) = output - .contracts_iter() - .find(|(name, _)| *name == contract_name) + let path = &res.file_path; + let file = res + .compiler_output + .contracts + .get(path) + .ok_or_else(|| anyhow::anyhow!("file {path} not found"))?; + let contract_name = &res.contract_name; + let contract = file + .get(&res.contract_name) .ok_or_else(|| anyhow::anyhow!("contract {contract_name} not found"))?; + let abi = &contract + .abi + .as_ref() + .ok_or_else(|| anyhow::anyhow!("abi missing"))? + .abi; + let evm = contract .evm .as_ref() .ok_or_else(|| anyhow::anyhow!("evm missing"))?; + let deployed_bytecode = evm .deployed_bytecode .as_ref() @@ -45,6 +59,7 @@ pub fn find_methods_from_compiler_output( .bytecode .as_ref() .ok_or_else(|| anyhow::anyhow!("bytecode missing"))?; + let source_map = bytecode .source_map() .ok_or_else(|| anyhow::anyhow!("source map missing"))??; @@ -53,11 +68,7 @@ pub fn find_methods_from_compiler_output( .as_bytes() .ok_or_else(|| anyhow::anyhow!("invalid bytecode"))? .0; - let abi = &contract - .abi - .as_ref() - .ok_or_else(|| anyhow::anyhow!("abi missing"))? - .abi; + let methods = parse_selectors(abi); Ok(find_methods_internal(