Skip to content

Commit

Permalink
Initialize Sourcify client (#569)
Browse files Browse the repository at this point in the history
* Initialize 'sourcify' crate. Implement 'get_source_files_any' request

* Implement 'search_sourcify_sources' route

* Add sourcify database search settings and client initialization
  • Loading branch information
rimrakhimov authored Aug 8, 2023
1 parent 2516b11 commit 392cbe5
Show file tree
Hide file tree
Showing 20 changed files with 2,687 additions and 132 deletions.
1,646 changes: 1,528 additions & 118 deletions eth-bytecode-db/Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion eth-bytecode-db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ members = [
"eth-bytecode-db/entity",
"eth-bytecode-db/migration",
"eth-bytecode-db-server",
"eth-bytecode-db-proto"
"eth-bytecode-db-proto",
]
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ http:
post: /api/v2/bytecodes/sources:search
body: "*"

- selector: blockscout.ethBytecodeDb.v2.Database.SearchSourcifySources
post: /api/v2/bytecodes/sources:search-sourcify
body: "*"

#################### SolidityVerifier ####################

- selector: blockscout.ethBytecodeDb.v2.SolidityVerifier.VerifyMultiPart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ option go_package = "github.com/blockscout/blockscout-rs/eth-bytecode-db";

service Database {
rpc SearchSources(SearchSourcesRequest) returns (SearchSourcesResponse) {}

rpc SearchSourcifySources(SearchSourcifySourcesRequest) returns (SearchSourcesResponse) {}
}

service SolidityVerifier {
Expand Down Expand Up @@ -175,6 +177,13 @@ message SearchSourcesRequest {
BytecodeType bytecode_type = 2;
}

message SearchSourcifySourcesRequest {
/// Id of the chain the contract is verified on
string chain_id = 1;
/// The address of the contract to be verified
string contract_address = 2;
}

message SearchSourcesResponse {
repeated Source sources = 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ paths:
$ref: '#/definitions/v2SearchSourcesRequest'
tags:
- Database
/api/v2/bytecodes/sources:search-sourcify:
post:
operationId: Database_SearchSourcifySources
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v2SearchSourcesResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/v2SearchSourcifySourcesRequest'
tags:
- Database
/api/v2/verifier/solidity/sources:verify-multi-part:
post:
operationId: SolidityVerifier_VerifyMultiPart
Expand Down Expand Up @@ -263,6 +283,15 @@ definitions:
type: array
items:
$ref: '#/definitions/v2Source'
v2SearchSourcifySourcesRequest:
type: object
properties:
chainId:
type: string
title: / Id of the chain the contract is verified on
contractAddress:
type: string
title: / The address of the contract to be verified
v2Source:
type: object
properties:
Expand Down
8 changes: 5 additions & 3 deletions eth-bytecode-db/eth-bytecode-db-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ async-trait = "0.1"
blockscout-display-bytes = "1.0"
blockscout-service-launcher = { version = "0.7.1", features = [ "database-0_11" ] }
config = "0.13"
ethers = "2.0.0"
sea-orm = "0.11"
serde = "1.0"
serde_json = "1.0.96"
serde_with = "2.1"
sourcify = { git = "https://github.com/blockscout/blockscout-rs", branch = "rimrakhimov/libs/sourcify" }
tokio = { version = "1.23", features = [ "rt-multi-thread", "macros" ] }
tonic = "0.8"
tracing = "0.1"

[dev-dependencies]
smart-contract-verifier-proto = { git = "https://github.com/blockscout/blockscout-rs", rev = "52c41ec" }
Expand All @@ -33,6 +37,4 @@ reqwest = { version = "0.11", features = ["json"]}
rand = "0.8"
rstest = "0.16"
sea-orm = { version = "*", features = [ "sqlx-sqlite" ]}
serde_json = "1.0.96"
tokio-stream = { version = "0.1", features = ["net"] }
tracing = "0.1"
tokio-stream = { version = "0.1", features = ["net"] }
8 changes: 4 additions & 4 deletions eth-bytecode-db/eth-bytecode-db-server/src/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pub use eth_bytecode_db_proto::blockscout::eth_bytecode_db::v2::{
solidity_verifier_actix, solidity_verifier_server, source, sourcify_verifier_actix,
sourcify_verifier_server, verify_response, vyper_verifier_actix, vyper_verifier_server,
BytecodeType, HealthCheckRequest, HealthCheckResponse, ListCompilerVersionsRequest,
ListCompilerVersionsResponse, SearchSourcesRequest, SearchSourcesResponse, Source,
VerificationMetadata, VerifyResponse, VerifySolidityMultiPartRequest,
VerifySolidityStandardJsonRequest, VerifySourcifyRequest, VerifyVyperMultiPartRequest,
VerifyVyperStandardJsonRequest,
ListCompilerVersionsResponse, SearchSourcesRequest, SearchSourcesResponse,
SearchSourcifySourcesRequest, Source, VerificationMetadata, VerifyResponse,
VerifySolidityMultiPartRequest, VerifySolidityStandardJsonRequest, VerifySourcifyRequest,
VerifyVyperMultiPartRequest, VerifyVyperStandardJsonRequest,
};
14 changes: 13 additions & 1 deletion eth-bytecode-db/eth-bytecode-db-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,19 @@ pub async fn run(settings: Settings) -> Result<(), anyhow::Error> {
let db_connection = Arc::new(sea_orm::Database::connect(settings.database.url).await?);
let client = Client::new_arc(db_connection.clone(), settings.verifier.uri).await?;

let database = Arc::new(DatabaseService::new_arc(db_connection));
let sourcify_client = settings
.database
.sourcify
.enabled
.then(|| {
sourcify::ClientBuilder::default()
.base_url(&settings.database.sourcify.base_url)
.total_duration(settings.database.sourcify.total_request_duration)
.build()
})
.transpose()?;
let database = Arc::new(DatabaseService::new_arc(db_connection, sourcify_client));

let solidity_verifier = Arc::new(SolidityVerifierService::new(client.clone()));
let vyper_verifier = Arc::new(VyperVerifierService::new(client.clone()));
let sourcify_verifier = Arc::new(SourcifyVerifierService::new(client.clone()));
Expand Down
92 changes: 89 additions & 3 deletions eth-bytecode-db/eth-bytecode-db-server/src/services/database.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::{
proto::{database_server::Database, SearchSourcesRequest, SearchSourcesResponse},
proto::{
database_server::Database, SearchSourcesRequest, SearchSourcesResponse,
SearchSourcifySourcesRequest,
},
types::{BytecodeTypeWrapper, SourceWrapper},
};
use amplify::Wrapper;
Expand All @@ -11,11 +14,18 @@ use std::{str::FromStr, sync::Arc};

pub struct DatabaseService {
pub db_client: Arc<DatabaseConnection>,
pub sourcify_client: Option<sourcify::Client>,
}

impl DatabaseService {
pub fn new_arc(db_client: Arc<DatabaseConnection>) -> Self {
Self { db_client }
pub fn new_arc(
db_client: Arc<DatabaseConnection>,
sourcify_client: Option<sourcify::Client>,
) -> Self {
Self {
db_client,
sourcify_client,
}
}
}

Expand Down Expand Up @@ -47,4 +57,80 @@ impl Database for DatabaseService {
let response = SearchSourcesResponse { sources };
Ok(tonic::Response::new(response))
}

async fn search_sourcify_sources(
&self,
request: tonic::Request<SearchSourcifySourcesRequest>,
) -> Result<tonic::Response<SearchSourcesResponse>, tonic::Status> {
let request = request.into_inner();

let chain_id = request.chain_id;
let contract_address = DisplayBytes::from_str(&request.contract_address)
.map_err(|err| {
tonic::Status::invalid_argument(format!("Invalid contract address: {err}"))
})?
.0;

let sourcify_client = self
.sourcify_client
.as_ref()
.ok_or(tonic::Status::unimplemented(
"sourcify search is not enabled",
))?;

let sourcify_result = sourcify_client
.get_source_files_any(&chain_id, contract_address)
.await
.map_err(process_sourcify_error);

let result = match sourcify_result {
Ok(response) => {
let source = SourceWrapper::try_from(response)?.into_inner();
SearchSourcesResponse {
sources: vec![source],
}
}
Err(None) => SearchSourcesResponse { sources: vec![] },
Err(Some(err)) => return Err(err),
};

Ok(tonic::Response::new(result))
}
}

fn process_sourcify_error(error: sourcify::Error) -> Option<tonic::Status> {
match error {
sourcify::Error::InvalidArgument { .. }
| sourcify::Error::Reqwest(_)
| sourcify::Error::ReqwestMiddleware(_) => {
tracing::error!(target: "sourcify", "{error}");
Some(tonic::Status::internal(
"sending request to sourcify failed",
))
}
sourcify::Error::Sourcify(sourcify::SourcifyError::TooManyRequests(_)) => {
tracing::error!(target: "sourcify", "{error}");
Some(tonic::Status::resource_exhausted(error.to_string()))
}
sourcify::Error::Sourcify(sourcify::SourcifyError::InternalServerError(_)) => {
tracing::error!(target: "sourcify", "{error}");
Some(tonic::Status::internal("sourcify responded with error"))
}
sourcify::Error::Sourcify(sourcify::SourcifyError::NotFound(_)) => {
tracing::trace!(target: "sourcify", "{error}");
None
}
sourcify::Error::Sourcify(sourcify::SourcifyError::ChainNotSupported(_)) => {
tracing::error!(target: "sourcify", "{error}");
None
}
sourcify::Error::Sourcify(sourcify::SourcifyError::BadRequest(_)) => {
tracing::error!(target: "sourcify", "{error}");
Some(tonic::Status::internal("sourcify responded with error"))
}
sourcify::Error::Sourcify(sourcify::SourcifyError::UnexpectedStatusCode { .. }) => {
tracing::error!(target: "sourcify", "{error}");
Some(tonic::Status::internal("sourcify responded with error"))
}
}
}
23 changes: 23 additions & 0 deletions eth-bytecode-db/eth-bytecode-db-server/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use blockscout_service_launcher::{
use config::{Config, File};
use serde::{de, Deserialize};
use serde_with::{serde_as, DisplayFromStr};
use std::time::Duration;

/// Wrapper under [`serde::de::IgnoredAny`] which implements
/// [`PartialEq`] and [`Eq`] for fields to be ignored.
Expand Down Expand Up @@ -47,6 +48,8 @@ pub struct DatabaseSettings {
pub url: String,
pub create_database: bool,
pub run_migrations: bool,
#[serde(default)]
pub sourcify: SourcifySettings,
}

#[serde_as]
Expand All @@ -57,6 +60,25 @@ pub struct VerifierSettings {
pub uri: tonic::transport::Uri,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct SourcifySettings {
pub enabled: bool,
pub base_url: String,
/// The maximum duration for which attempts to send a request will be processed.
pub total_request_duration: Duration,
}

impl Default for SourcifySettings {
fn default() -> Self {
Self {
enabled: false,
base_url: "https://sourcify.dev/server/".to_string(),
total_request_duration: Duration::from_secs(60),
}
}
}

impl Settings {
pub fn new() -> anyhow::Result<Self> {
let config_path = std::env::var("ETH_BYTECODE_DB__CONFIG");
Expand Down Expand Up @@ -84,6 +106,7 @@ impl Settings {
url: database_url,
create_database: false,
run_migrations: false,
sourcify: Default::default(),
},
verifier: VerifierSettings { uri: verifier_uri },
config_path: Default::default(),
Expand Down
22 changes: 22 additions & 0 deletions eth-bytecode-db/eth-bytecode-db-server/src/types/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ impl From<verification::MatchType> for MatchTypeWrapper {
}
}

impl From<sourcify::MatchType> for MatchTypeWrapper {
fn from(value: sourcify::MatchType) -> Self {
match value {
sourcify::MatchType::Full => MatchTypeWrapper::from(proto::source::MatchType::Full),
sourcify::MatchType::Partial => {
MatchTypeWrapper::from(proto::source::MatchType::Partial)
}
}
}
}

/********** Tests **********/

#[cfg(test)]
Expand Down Expand Up @@ -163,4 +174,15 @@ mod match_type_tests {
let result = MatchTypeWrapper::from(verification_type).into_inner();
assert_eq!(proto_type, result);
}

#[rstest]
#[case(sourcify::MatchType::Partial, proto::source::MatchType::Partial)]
#[case(sourcify::MatchType::Full, proto::source::MatchType::Full)]
fn from_sourcify_to_proto(
#[case] verification_type: sourcify::MatchType,
#[case] proto_type: proto::source::MatchType,
) {
let result = MatchTypeWrapper::from(verification_type).into_inner();
assert_eq!(proto_type, result);
}
}
Loading

0 comments on commit 392cbe5

Please sign in to comment.