diff --git a/eth-bytecode-db/Cargo.lock b/eth-bytecode-db/Cargo.lock index 1d97f02a6..343083361 100644 --- a/eth-bytecode-db/Cargo.lock +++ b/eth-bytecode-db/Cargo.lock @@ -1387,6 +1387,7 @@ dependencies = [ "rstest", "sea-orm", "serde", + "serde_json", "serde_with", "smart-contract-verifier-proto", "tokio", @@ -3902,7 +3903,7 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smart-contract-verifier-proto" version = "0.1.0" -source = "git+https://github.com/blockscout/blockscout-rs?rev=ee52afad#ee52afad47b5032c79492f4bef20f99d999c50bb" +source = "git+https://github.com/blockscout/blockscout-rs?rev=52c41ec#52c41ecdf4dca82580893cd4dd026079f784d560" dependencies = [ "actix-prost", "actix-prost-build", diff --git a/eth-bytecode-db/eth-bytecode-db-proto/proto/v2/eth-bytecode-db.proto b/eth-bytecode-db/eth-bytecode-db-proto/proto/v2/eth-bytecode-db.proto index 63b590ae4..469ec150c 100644 --- a/eth-bytecode-db/eth-bytecode-db-proto/proto/v2/eth-bytecode-db.proto +++ b/eth-bytecode-db/eth-bytecode-db-proto/proto/v2/eth-bytecode-db.proto @@ -79,9 +79,9 @@ enum BytecodeType { message VerificationMetadata { /// Id of the chain the contract is verified on - string chain_id = 1; + optional string chain_id = 1; /// The address of the contract to be verified - string contract_address = 2; + optional string contract_address = 2; } message VerifySolidityMultiPartRequest { diff --git a/eth-bytecode-db/eth-bytecode-db-server/Cargo.toml b/eth-bytecode-db/eth-bytecode-db-server/Cargo.toml index 240a44d31..5e06890f9 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/Cargo.toml +++ b/eth-bytecode-db/eth-bytecode-db-server/Cargo.toml @@ -24,7 +24,7 @@ tokio = { version = "1.23", features = [ "rt-multi-thread", "macros" ] } tonic = "0.8" [dev-dependencies] -smart-contract-verifier-proto = { git = "https://github.com/blockscout/blockscout-rs", rev = "ee52afad" } +smart-contract-verifier-proto = { git = "https://github.com/blockscout/blockscout-rs", rev = "52c41ec" } hex = "0.4.3" mockall = "0.11" @@ -33,5 +33,6 @@ 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" \ No newline at end of file diff --git a/eth-bytecode-db/eth-bytecode-db-server/src/types/verification_metadata.rs b/eth-bytecode-db/eth-bytecode-db-server/src/types/verification_metadata.rs index f00d043c3..263012fee 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/src/types/verification_metadata.rs +++ b/eth-bytecode-db/eth-bytecode-db-server/src/types/verification_metadata.rs @@ -12,12 +12,29 @@ impl TryFrom for verification::VerificationMetadata fn try_from(value: VerificationMetadataWrapper) -> Result { let value = value.0; + + let chain_id = if let Some(chain_id) = &value.chain_id { + Some( + i64::from_str(chain_id) + .map_err(|_err| tonic::Status::invalid_argument("Invalid metadata.chain_id"))?, + ) + } else { + None + }; + + let contract_address = if let Some(contract_address) = &value.contract_address { + Some( + DisplayBytes::from_str(contract_address) + .map_err(|_err| tonic::Status::invalid_argument("Invalid contract address"))? + .0, + ) + } else { + None + }; + Ok(verification::VerificationMetadata { - chain_id: i64::from_str(&value.chain_id) - .map_err(|_err| tonic::Status::invalid_argument("Invalid metadata.chain_id"))?, - contract_address: DisplayBytes::from_str(&value.contract_address) - .map_err(|_err| tonic::Status::invalid_argument("Invalid contract address"))? - .0, + chain_id, + contract_address, }) } } @@ -29,15 +46,17 @@ mod tests { #[test] fn from_proto_to_verification_metadata() { let proto_type = proto::VerificationMetadata { - chain_id: "1".into(), - contract_address: "0xcafecafecafecafecafecafecafecafecafecafe".into(), + chain_id: Some("1".into()), + contract_address: Some("0xcafecafecafecafecafecafecafecafecafecafe".into()), }; let expected = verification::VerificationMetadata { - chain_id: 1, - contract_address: DisplayBytes::from_str("0xcafecafecafecafecafecafecafecafecafecafe") - .unwrap() - .0, + chain_id: Some(1), + contract_address: Some( + DisplayBytes::from_str("0xcafecafecafecafecafecafecafecafecafecafe") + .unwrap() + .0, + ), }; let wrapper: VerificationMetadataWrapper = proto_type.into(); diff --git a/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_multi_part.rs b/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_multi_part.rs index d05a74feb..b99bd0a0f 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_multi_part.rs +++ b/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_multi_part.rs @@ -136,3 +136,28 @@ async fn test_search_returns_full_matches_only_if_any() { ) .await; } + +#[rstest] +#[tokio::test] +#[timeout(std::time::Duration::from_secs(60))] +#[ignore = "Needs database to run"] +async fn test_accepts_partial_verification_metadata_in_input() { + let default_request = VerifySolidityMultiPartRequest { + bytecode: "".to_string(), + bytecode_type: BytecodeType::CreationInput.into(), + compiler_version: "".to_string(), + evm_version: None, + optimization_runs: None, + source_files: Default::default(), + libraries: Default::default(), + metadata: None, + }; + let source_type = verification::SourceType::Solidity; + test_cases::test_accepts_partial_verification_metadata_in_input::( + TEST_SUITE_NAME, + ROUTE, + default_request, + source_type, + ) + .await; +} diff --git a/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_standard_json.rs b/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_standard_json.rs index dbaf60841..89fbbea3b 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_standard_json.rs +++ b/eth-bytecode-db/eth-bytecode-db-server/tests/solidity_standard_json.rs @@ -124,3 +124,25 @@ async fn test_search_returns_full_matches_only_if_any() { ) .await; } + +#[rstest] +#[tokio::test] +#[timeout(std::time::Duration::from_secs(60))] +#[ignore = "Needs database to run"] +async fn test_accepts_partial_verification_metadata_in_input() { + let default_request = VerifySolidityStandardJsonRequest { + bytecode: "".to_string(), + bytecode_type: BytecodeType::CreationInput.into(), + compiler_version: "".to_string(), + input: "".to_string(), + metadata: None, + }; + let source_type = verification::SourceType::Solidity; + test_cases::test_accepts_partial_verification_metadata_in_input::( + TEST_SUITE_NAME, + ROUTE, + default_request, + source_type, + ) + .await; +} diff --git a/eth-bytecode-db/eth-bytecode-db-server/tests/verification_test_helpers/mod.rs b/eth-bytecode-db/eth-bytecode-db-server/tests/verification_test_helpers/mod.rs index 8c79b8cd4..2e374b366 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/tests/verification_test_helpers/mod.rs +++ b/eth-bytecode-db/eth-bytecode-db-server/tests/verification_test_helpers/mod.rs @@ -434,4 +434,65 @@ pub mod test_cases { "Sources returned on verification and search differ" ); } + + pub async fn test_accepts_partial_verification_metadata_in_input( + test_suite_name: &str, + route: &str, + verification_request: Request, + source_type: SourceType, + ) where + Service: Default + VerifierService, + Request: Serialize + Clone, + { + let db = init_db( + test_suite_name, + "test_accepts_partial_verification_metadata_in_input", + ) + .await; + + let test_data = test_input_data::basic(source_type, MatchType::Partial); + + let db_url = db.db_url(); + let verifier_addr = + init_verifier_server(Service::default(), test_data.verifier_response).await; + + let eth_bytecode_db_base = init_eth_bytecode_db_server(db_url, verifier_addr).await; + + let validate = |metadata: serde_json::Value| async { + let metadata_to_print = metadata.clone(); + let mut request = serde_json::to_value(verification_request.clone()).unwrap(); + if let Some(value) = request.as_object_mut() { + value.insert("metadata".to_string(), metadata) + } else { + panic!("Request value is not an object") + }; + + let response = reqwest::Client::new() + .post(eth_bytecode_db_base.join(route).unwrap()) + .json(&request) + .send() + .await + .expect("Failed to send request"); + + // Assert that status code is success + if !response.status().is_success() { + let status = response.status(); + let message = response.text().await.expect("Read body as text"); + panic!( + "Invalid status code (success expected). \ + Status: {status}. Message: {message}.\ + Metadata: {metadata_to_print}" + ) + } + }; + + // `chain_id` is provided, but `contract_address` is missed from the verification metadata + let metadata = serde_json::json!({ "chainId": "5" }); + validate(metadata).await; + + // `chain_id` is provided, but `contract_address` is missed from the verification metadata + let metadata = + serde_json::json!({ "contractAddress": "0x0123456789012345678901234567890123456789" }); + validate(metadata).await; + } } diff --git a/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_multi_part.rs b/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_multi_part.rs index ff1859240..38b231d12 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_multi_part.rs +++ b/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_multi_part.rs @@ -132,3 +132,27 @@ async fn test_search_returns_full_matches_only_if_any() { ) .await; } + +#[rstest] +#[tokio::test] +#[timeout(std::time::Duration::from_secs(60))] +#[ignore = "Needs database to run"] +async fn test_accepts_partial_verification_metadata_in_input() { + let default_request = VerifyVyperMultiPartRequest { + bytecode: "".to_string(), + bytecode_type: BytecodeType::CreationInput.into(), + compiler_version: "".to_string(), + evm_version: None, + source_files: Default::default(), + interfaces: Default::default(), + metadata: None, + }; + let source_type = verification::SourceType::Vyper; + test_cases::test_accepts_partial_verification_metadata_in_input::( + TEST_SUITE_NAME, + ROUTE, + default_request, + source_type, + ) + .await; +} diff --git a/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_standard_json.rs b/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_standard_json.rs index 727646ad0..409b8b7ab 100644 --- a/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_standard_json.rs +++ b/eth-bytecode-db/eth-bytecode-db-server/tests/vyper_standard_json.rs @@ -124,3 +124,25 @@ async fn test_search_returns_full_matches_only_if_any() { ) .await; } + +#[rstest] +#[tokio::test] +#[timeout(std::time::Duration::from_secs(60))] +#[ignore = "Needs database to run"] +async fn test_accepts_partial_verification_metadata_in_input() { + let default_request = VerifyVyperStandardJsonRequest { + bytecode: "".to_string(), + bytecode_type: BytecodeType::CreationInput.into(), + compiler_version: "".to_string(), + input: "".to_string(), + metadata: None, + }; + let source_type = verification::SourceType::Vyper; + test_cases::test_accepts_partial_verification_metadata_in_input::( + TEST_SUITE_NAME, + ROUTE, + default_request, + source_type, + ) + .await; +} diff --git a/eth-bytecode-db/eth-bytecode-db/Cargo.toml b/eth-bytecode-db/eth-bytecode-db/Cargo.toml index 5a6d0b260..5ba461b9a 100644 --- a/eth-bytecode-db/eth-bytecode-db/Cargo.toml +++ b/eth-bytecode-db/eth-bytecode-db/Cargo.toml @@ -27,7 +27,7 @@ prometheus = "0.13" semver = "1.0" serde = "1.0" serde_json = "1.0" -smart-contract-verifier-proto = { git = "https://github.com/blockscout/blockscout-rs", rev = "ee52afad" } +smart-contract-verifier-proto = { git = "https://github.com/blockscout/blockscout-rs", rev = "52c41ec" } solidity-metadata = "1.0" thiserror = "1.0" tokio = "1.22" diff --git a/eth-bytecode-db/eth-bytecode-db/src/verification/db.rs b/eth-bytecode-db/eth-bytecode-db/src/verification/db.rs index eb68c9503..0630ecf59 100644 --- a/eth-bytecode-db/eth-bytecode-db/src/verification/db.rs +++ b/eth-bytecode-db/eth-bytecode-db/src/verification/db.rs @@ -118,8 +118,8 @@ pub(crate) async fn insert_verified_contract_data( let (chain_id, contract_address) = match verification_metadata { None => (None, None), Some(metadata) => ( - Some(metadata.chain_id), - Some(metadata.contract_address.to_vec()), + metadata.chain_id, + metadata.contract_address.map(|address| address.to_vec()), ), }; verified_contracts::ActiveModel { diff --git a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_multi_part.rs b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_multi_part.rs index 13c78ba70..fcb5a1e8a 100644 --- a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_multi_part.rs +++ b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_multi_part.rs @@ -89,8 +89,8 @@ mod tests { libraries: BTreeMap::from([("lib1".into(), "0xcafe".into())]), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifySolidityMultiPartRequest { @@ -105,8 +105,8 @@ mod tests { optimization_runs: Some(200), libraries: BTreeMap::from([("lib1".into(), "0xcafe".into())]), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( @@ -132,8 +132,8 @@ mod tests { libraries: BTreeMap::from([("lib1".into(), "0xcafe".into())]), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifySolidityMultiPartRequest { @@ -148,8 +148,8 @@ mod tests { optimization_runs: Some(200), libraries: BTreeMap::from([("lib1".into(), "0xcafe".into())]), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( diff --git a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_standard_json.rs b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_standard_json.rs index e07542092..a1ba3bd72 100644 --- a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_standard_json.rs +++ b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/solidity_standard_json.rs @@ -76,8 +76,8 @@ mod tests { input: "standard_json_input".to_string(), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifySolidityStandardJsonRequest { @@ -86,8 +86,8 @@ mod tests { compiler_version: "compiler_version".to_string(), input: "standard_json_input".to_string(), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( @@ -107,8 +107,8 @@ mod tests { input: "standard_json_input".to_string(), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifySolidityStandardJsonRequest { @@ -117,8 +117,8 @@ mod tests { compiler_version: "compiler_version".to_string(), input: "standard_json_input".to_string(), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( diff --git a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_multi_part.rs b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_multi_part.rs index 9358ca760..603841dea 100644 --- a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_multi_part.rs +++ b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_multi_part.rs @@ -89,8 +89,8 @@ mod tests { ]), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifyVyperMultiPartRequest { @@ -107,8 +107,8 @@ mod tests { ]), evm_version: Some("istanbul".to_string()), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( @@ -136,8 +136,8 @@ mod tests { ]), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifyVyperMultiPartRequest { @@ -154,8 +154,8 @@ mod tests { ]), evm_version: Some("istanbul".to_string()), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( diff --git a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_standard_json.rs b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_standard_json.rs index 8a7193838..1dc8ef0c0 100644 --- a/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_standard_json.rs +++ b/eth-bytecode-db/eth-bytecode-db/src/verification/handlers/vyper_standard_json.rs @@ -75,8 +75,8 @@ mod tests { input: "standard_json_input".to_string(), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifyVyperStandardJsonRequest { @@ -85,8 +85,8 @@ mod tests { compiler_version: "compiler_version".to_string(), input: "standard_json_input".to_string(), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( @@ -106,8 +106,8 @@ mod tests { input: "standard_json_input".to_string(), }, metadata: Some(types::VerificationMetadata { - chain_id: 1, - contract_address: bytes::Bytes::from_static(&[1u8; 20]), + chain_id: Some(1), + contract_address: Some(bytes::Bytes::from_static(&[1u8; 20])), }), }; let expected = VerifyVyperStandardJsonRequest { @@ -116,8 +116,8 @@ mod tests { compiler_version: "compiler_version".to_string(), input: "standard_json_input".to_string(), metadata: Some(smart_contract_verifier::VerificationMetadata { - chain_id: "1".to_string(), - contract_address: "0x0101010101010101010101010101010101010101".to_string(), + chain_id: Some("1".to_string()), + contract_address: Some("0x0101010101010101010101010101010101010101".to_string()), }), }; assert_eq!( diff --git a/eth-bytecode-db/eth-bytecode-db/src/verification/types.rs b/eth-bytecode-db/eth-bytecode-db/src/verification/types.rs index 315d81b54..29aaf9d4e 100644 --- a/eth-bytecode-db/eth-bytecode-db/src/verification/types.rs +++ b/eth-bytecode-db/eth-bytecode-db/src/verification/types.rs @@ -174,16 +174,19 @@ pub struct Source { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct VerificationMetadata { - pub chain_id: i64, - pub contract_address: bytes::Bytes, + pub chain_id: Option, + pub contract_address: Option, } impl From for smart_contract_verifier::VerificationMetadata { fn from(value: VerificationMetadata) -> Self { + let chain_id = value.chain_id.map(|id| format!("{}", id)); + let contract_address = value + .contract_address + .map(|address| blockscout_display_bytes::Bytes::from(address).to_string()); Self { - chain_id: format!("{}", value.chain_id), - contract_address: blockscout_display_bytes::Bytes::from(value.contract_address) - .to_string(), + chain_id, + contract_address, } } } diff --git a/eth-bytecode-db/eth-bytecode-db/tests/verification_test_helpers/mod.rs b/eth-bytecode-db/eth-bytecode-db/tests/verification_test_helpers/mod.rs index ae2b16b9f..193c2f696 100644 --- a/eth-bytecode-db/eth-bytecode-db/tests/verification_test_helpers/mod.rs +++ b/eth-bytecode-db/eth-bytecode-db/tests/verification_test_helpers/mod.rs @@ -467,8 +467,8 @@ pub async fn test_historical_data_saves_chain_id_and_contract_address