diff --git a/smart-contract-verifier/Cargo.lock b/smart-contract-verifier/Cargo.lock index a9e7cb478..f27ae6fb7 100644 --- a/smart-contract-verifier/Cargo.lock +++ b/smart-contract-verifier/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-sink", @@ -31,7 +31,7 @@ dependencies = [ "actix-utils", "ahash 0.8.3", "base64 0.21.2", - "bitflags", + "bitflags 1.3.2", "brotli", "bytes", "bytestring", @@ -299,6 +299,46 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "amplify" +version = "3.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ec14f4fb838e9ddace42fa5944bb1ee4dff8477494ba48c5f874e16caf27a" +dependencies = [ + "amplify_derive", + "amplify_num", + "wasm-bindgen", +] + +[[package]] +name = "amplify_derive" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c3de270e75f27a4468a7c344070109046656e85cb522141f7d40ab4b83803ac" +dependencies = [ + "amplify_syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "amplify_num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27d3d00d3d115395a7a8a4dc045feb7aa82b641e485f7e15f4e67ac16f4f56d" + +[[package]] +name = "amplify_syn" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da24db1445cc7bc3842fa072c2d51fe5b25b812b6a572d65842a4c72e87221ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -456,7 +496,7 @@ checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -538,6 +578,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "bitvec" version = "1.0.1" @@ -745,6 +791,19 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "const-hex" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.9.2" @@ -1221,17 +1280,17 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.7" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da5fa198af0d3be20c19192df2bd9590b92ce09a8421e793bec8851270f1b05" +checksum = "c0a17f0708692024db9956b31d7a20163607d2745953f5ae8125ab368ba280ad" dependencies = [ "arrayvec", "bytes", "chrono", + "const-hex", "elliptic-curve", "ethabi", "generic-array", - "hex", "k256", "num_enum", "open-fastrlp", @@ -1295,6 +1354,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "ff" version = "0.13.0" @@ -1448,7 +1513,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", @@ -1892,7 +1957,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", + "rustix 0.37.19", "windows-sys 0.48.0", ] @@ -2004,9 +2069,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linked-hash-map" @@ -2020,6 +2091,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + [[package]] name = "local-channel" version = "0.1.3" @@ -2281,6 +2358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2295,18 +2373,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2351,7 +2429,7 @@ version = "0.10.54" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b3f656a17a6cbc115b5c7a40c616947d213ba182135b014d6051b73ab6f019" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -2936,9 +3014,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -2958,6 +3036,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "proptest" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" +dependencies = [ + "bitflags 2.4.1", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax 0.7.2", + "unarray", +] + [[package]] name = "prost" version = "0.11.9" @@ -3114,6 +3208,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rayon" version = "1.7.0" @@ -3142,16 +3245,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3398,7 +3501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "serde", ] @@ -3515,11 +3618,24 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.10", "windows-sys 0.48.0", ] @@ -3661,7 +3777,7 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -3920,6 +4036,7 @@ dependencies = [ "const_format", "cron", "ethabi", + "ethers-core", "ethers-solc", "futures", "hex", @@ -4015,6 +4132,7 @@ version = "0.1.0" dependencies = [ "actix-web", "actix-web-prom", + "amplify", "anyhow", "async-trait", "blockscout-display-bytes", @@ -4023,6 +4141,7 @@ dependencies = [ "config", "cron", "ethabi", + "ethers-core", "ethers-solc", "futures", "lazy_static", @@ -4180,24 +4299,24 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.24.3" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -4284,15 +4403,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.21", + "windows-sys 0.48.0", ] [[package]] @@ -4783,6 +4902,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.6.0" diff --git a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml index 2fd46c29b..f27620b79 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml +++ b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml @@ -16,6 +16,11 @@ http: - selector: blockscout.smartContractVerifier.v2.SolidityVerifier.ListCompilerVersions get: /api/v2/verifier/solidity/versions + - selector: blockscout.smartContractVerifier.v2.SolidityVerifier.LookupMethods + post: /api/v2/verifier/solidity/lookup-methods + body: "*" + + #################### Vyper Verifier #################### - selector: blockscout.smartContractVerifier.v2.VyperVerifier.VerifyMultiPart diff --git a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/smart-contract-verifier.proto b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/smart-contract-verifier.proto index 6fd79adf7..7aefeb0f3 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/smart-contract-verifier.proto +++ b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/smart-contract-verifier.proto @@ -10,6 +10,8 @@ service SolidityVerifier { rpc VerifyStandardJson(VerifySolidityStandardJsonRequest) returns (VerifyResponse) {} rpc ListCompilerVersions(ListCompilerVersionsRequest) returns (ListCompilerVersionsResponse) {} + + rpc LookupMethods(LookupMethodsRequest) returns (LookupMethodsResponse) {} } service VyperVerifier { @@ -218,3 +220,19 @@ message ListCompilerVersionsResponse { /// Compiler versions available repeated string compiler_versions = 1; } + +message LookupMethodsRequest { + string bytecode = 1; + string abi = 2; + string source_map = 3; + map file_id_map = 4; +} + +message LookupMethodsResponse { + message Method { + string file_name = 1; + int32 file_offset = 2; + int32 length = 3; + } + map methods = 1; +} \ No newline at end of file diff --git a/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml b/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml index 73764d824..34d9d036d 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml +++ b/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml @@ -12,6 +12,26 @@ consumes: produces: - application/json paths: + /api/v2/verifier/solidity/lookup-methods: + post: + operationId: SolidityVerifier_LookupMethods + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/v2LookupMethodsResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: body + in: body + required: true + schema: + $ref: '#/definitions/v2LookupMethodsRequest' + tags: + - SolidityVerifier /api/v2/verifier/solidity/sources:verify-multi-part: post: operationId: SolidityVerifier_VerifyMultiPart @@ -186,10 +206,10 @@ definitions: ExtraDataBytecodePart: type: object properties: - data: - type: string type: type: string + data: + type: string HealthCheckResponseServingStatus: type: string enum: @@ -198,6 +218,18 @@ definitions: - NOT_SERVING - SERVICE_UNKNOWN default: UNKNOWN + description: ' - SERVICE_UNKNOWN: Used only by the Watch method.' + LookupMethodsResponseMethod: + type: object + properties: + fileName: + type: string + fileOffset: + type: integer + format: int32 + length: + type: integer + format: int32 SourceMatchType: type: string enum: @@ -238,12 +270,12 @@ definitions: code: type: integer format: int32 + message: + type: string details: type: array items: $ref: '#/definitions/protobufAny' - message: - type: string protobufAny: type: object properties: @@ -270,53 +302,73 @@ definitions: items: type: string title: / Compiler versions available - v2Source: + v2LookupMethodsRequest: type: object properties: + bytecode: + type: string abi: type: string - title: |- - / Contract abi (https://docs.soliditylang.org/en/latest/abi-spec.html?highlight=abi#json); - / (does not exist for Yul contracts) - compilationArtifacts: + sourceMap: type: string - description: |- - / General and compiler-specific artifacts (abi, userdoc, devdoc, licenses, ast, etc), - / encoded as a json. + fileIdMap: + type: object + additionalProperties: + type: string + v2LookupMethodsResponse: + type: object + properties: + methods: + type: object + additionalProperties: + $ref: '#/definitions/LookupMethodsResponseMethod' + v2Source: + type: object + properties: + fileName: + type: string + title: / The name of the file verified contract was located at + contractName: + type: string + title: / The name of the contract which was verified + compilerVersion: + type: string + title: Compiler version used to compile the contract compilerSettings: type: string title: |- / 'settings' key in Standard Input JSON / (https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description) - compilerVersion: + sourceType: + $ref: '#/definitions/SourceSourceType' + sourceFiles: + type: object + additionalProperties: + type: string + abi: type: string - title: Compiler version used to compile the contract + title: |- + / Contract abi (https://docs.soliditylang.org/en/latest/abi-spec.html?highlight=abi#json); + / (does not exist for Yul contracts) constructorArguments: type: string title: |- / Constructor arguments used for deploying verified contract / (exists only for creation inputs) - contractName: + matchType: + $ref: '#/definitions/SourceMatchType' + title: / Similar to Sourcify (see https://docs.sourcify.dev/docs/full-vs-partial-match/) + compilationArtifacts: type: string - title: / The name of the contract which was verified + description: |- + / General and compiler-specific artifacts (abi, userdoc, devdoc, licenses, ast, etc), + / encoded as a json. creationInputArtifacts: type: string description: / Info about the creation code (sourcemaps, linkreferences) encoded as a json. deployedBytecodeArtifacts: type: string description: / Info about the runtime code (sourcemaps, linkreferences, immutables) encoded as a json. - fileName: - type: string - title: / The name of the file verified contract was located at - matchType: - $ref: '#/definitions/SourceMatchType' - title: / Similar to Sourcify (see https://docs.sourcify.dev/docs/full-vs-partial-match/) - sourceFiles: - type: object - additionalProperties: - type: string - sourceType: - $ref: '#/definitions/SourceSourceType' v2VerificationMetadata: type: object properties: @@ -340,14 +392,14 @@ definitions: v2VerifyResponse: type: object properties: - extraData: - $ref: '#/definitions/VerifyResponseExtraData' message: type: string - source: - $ref: '#/definitions/v2Source' status: $ref: '#/definitions/v2VerifyResponseStatus' + source: + $ref: '#/definitions/v2Source' + extraData: + $ref: '#/definitions/VerifyResponseExtraData' v2VerifyResponseStatus: type: string enum: @@ -370,14 +422,6 @@ definitions: evmVersion: type: string title: / Version of the EVM to compile for. If absent results in default EVM version - libraries: - type: object - additionalProperties: - type: string - title: / Map from a library name to its address - metadata: - $ref: '#/definitions/v2VerificationMetadata' - title: / An optional field to be filled by explorers optimizationRuns: type: integer format: int32 @@ -389,6 +433,14 @@ definitions: additionalProperties: type: string title: / Map from a source file name to the actual source code + libraries: + type: object + additionalProperties: + type: string + title: / Map from a library name to its address + metadata: + $ref: '#/definitions/v2VerificationMetadata' + title: / An optional field to be filled by explorers v2VerifySolidityStandardJsonRequest: type: object properties: @@ -418,10 +470,6 @@ definitions: title: |- / The chain (network) the contract was deployed to / (https://docs.sourcify.dev/docs/api/chains/) - chosenContract: - type: integer - format: int32 - title: (optional) see Sourcify Api files: type: object additionalProperties: @@ -430,6 +478,10 @@ definitions: / Files required for verification (see Sourcify Api) Is named as `files` instead of `source_files` to correspond with Sourcify api + chosenContract: + type: integer + format: int32 + title: (optional) see Sourcify Api v2VerifyVyperMultiPartRequest: type: object properties: @@ -445,6 +497,11 @@ definitions: evmVersion: type: string title: / Version of the EVM to compile for. If absent results in default EVM version + sourceFiles: + type: object + additionalProperties: + type: string + title: / Map from a source file name to the actual source code interfaces: type: object additionalProperties: @@ -455,11 +512,6 @@ definitions: metadata: $ref: '#/definitions/v2VerificationMetadata' title: / An optional field to be filled by explorers - sourceFiles: - type: object - additionalProperties: - type: string - title: / Map from a source file name to the actual source code v2VerifyVyperStandardJsonRequest: type: object properties: diff --git a/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml b/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml index aa4694b0b..e1fa68f3a 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml +++ b/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml @@ -12,6 +12,7 @@ sig-provider-extension = { path = "../sig-provider-extension", optional = true } actix-web = "4" actix-web-prom = "0.6" +amplify = { version = "3.13.0", features = ["derive"] } anyhow = "1.0" async-trait = "0.1" blockscout-display-bytes = { version = "1.0" } @@ -20,6 +21,7 @@ bytes = "1.3" config = "0.13" cron = "0.11" ethers-solc = "2.0.6" +ethers-core = "2.0.10" futures = "0.3" lazy_static = "1" opentelemetry = { version = "0.18", features = ["rt-tokio"] } diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/proto.rs b/smart-contract-verifier/smart-contract-verifier-server/src/proto.rs index eb8bae983..e6b280b94 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/proto.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/proto.rs @@ -1,8 +1,9 @@ pub use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{ - health_actix, health_check_response, health_server, 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, Source, + health_actix, health_check_response, health_server, lookup_methods_response, + 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, LookupMethodsRequest, LookupMethodsResponse, Source, VerificationMetadata, VerifyFromEtherscanSourcifyRequest, VerifyResponse, VerifySolidityMultiPartRequest, VerifySolidityStandardJsonRequest, VerifySourcifyRequest, VerifyVyperMultiPartRequest, VerifyVyperStandardJsonRequest, 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 4db250e28..72a6c2242 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,14 +7,18 @@ use crate::{ }, settings::{Extensions, FetcherSettings, S3FetcherSettings, SoliditySettings}, types::{ - StandardJsonParseError, VerifyResponseWrapper, VerifySolidityMultiPartRequestWrapper, + LookupMethodsRequestWrapper, LookupMethodsResponseWrapper, StandardJsonParseError, + VerifyResponseWrapper, VerifySolidityMultiPartRequestWrapper, VerifySolidityStandardJsonRequestWrapper, }, }; use s3::{creds::Credentials, Bucket, Region}; use smart_contract_verifier::{ - solidity, Compilers, Fetcher, ListFetcher, S3Fetcher, SolcValidator, SolidityClient, - SolidityCompiler, VerificationError, + find_methods, solidity, Compilers, Fetcher, ListFetcher, S3Fetcher, SolcValidator, + SolidityClient, SolidityCompiler, VerificationError, +}; +use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{ + LookupMethodsRequest, LookupMethodsResponse, }; use std::{str::FromStr, sync::Arc}; use tokio::sync::Semaphore; @@ -184,6 +188,17 @@ impl SolidityVerifier for SolidityVerifierService { compiler_versions, })) } + + async fn lookup_methods( + &self, + request: Request, + ) -> Result, Status> { + let r: LookupMethodsRequestWrapper = request.into_inner().into(); + let methods = find_methods(r.try_into()?); + println!("methods: {:?}", methods.methods); + let response = LookupMethodsResponseWrapper::from(methods); + Ok(Response::new(response.into())) + } } fn new_region(region: Option, endpoint: Option) -> Option { diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs new file mode 100644 index 000000000..3661e7616 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs @@ -0,0 +1,54 @@ +use crate::proto; +use amplify::{From, Wrapper}; +use blockscout_display_bytes::Bytes as DisplayBytes; +use ethers_core::abi::Abi; +use ethers_solc::sourcemap; +use smart_contract_verifier::{LookupMethodsRequest, LookupMethodsResponse}; +use std::{collections::BTreeMap, str::FromStr}; + +#[derive(Wrapper, From, Clone, Debug, PartialEq)] +pub struct LookupMethodsRequestWrapper(proto::LookupMethodsRequest); + +impl TryFrom for LookupMethodsRequest { + type Error = tonic::Status; + + fn try_from(request: LookupMethodsRequestWrapper) -> Result { + let bytecode = DisplayBytes::from_str(&request.bytecode) + .map_err(|err| tonic::Status::invalid_argument(format!("Invalid bytecode: {err:?}")))? + .0; + let abi = Abi::load(request.abi.as_bytes()).unwrap(); // TODO why unwrap? + let source_map = sourcemap::parse(&request.source_map).unwrap(); + let file_id_map = request.file_id_map.clone(); // TODO: use Arc ? + + Ok(Self { + bytecode, + abi, + source_map, + file_id_map, + }) + } +} + +#[derive(Wrapper, From, Clone, Debug, PartialEq)] +pub struct LookupMethodsResponseWrapper(proto::LookupMethodsResponse); + +impl From for LookupMethodsResponseWrapper { + fn from(response: LookupMethodsResponse) -> Self { + Self(proto::LookupMethodsResponse { + methods: response + .methods + .iter() + .map(|(selector, method)| { + ( + selector.clone(), + proto::lookup_methods_response::Method { + file_name: method.filename.clone(), + file_offset: method.offset as i32, + length: method.length as i32, + }, + ) + }) + .collect::>(), + }) + } +} 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 e6adbddc0..644d1fba2 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 @@ -8,8 +8,11 @@ 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; diff --git a/smart-contract-verifier/smart-contract-verifier/Cargo.toml b/smart-contract-verifier/smart-contract-verifier/Cargo.toml index 656e58f50..4c4e4cbf5 100644 --- a/smart-contract-verifier/smart-contract-verifier/Cargo.toml +++ b/smart-contract-verifier/smart-contract-verifier/Cargo.toml @@ -14,6 +14,7 @@ bytes = "1.2" chrono = "0.4" cron = "0.11" ethabi = "18.0" +ethers-core = "2.0.10" ethers-solc = { version = "2.0.6", features = ["async"] } futures = "0.3" hex = "0.4" diff --git a/smart-contract-verifier/smart-contract-verifier/src/lib.rs b/smart-contract-verifier/smart-contract-verifier/src/lib.rs index 4efc2a76c..e93a9862f 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lib.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lib.rs @@ -7,6 +7,7 @@ pub mod middleware; mod common_types; mod compiler; mod consts; +mod lookup_methods; mod metrics; mod scheduler; mod verifier; @@ -28,6 +29,7 @@ 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 solidity::{ Client as SolidityClient, SolcValidator, SolidityCompiler, Success as SoliditySuccess, }; diff --git a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/disassemble.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/disassemble.rs new file mode 100644 index 000000000..245a8f701 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/disassemble.rs @@ -0,0 +1,67 @@ +use super::opcodes::{opcode, Opcode}; +use bytes::Bytes; +use std::fmt::{Debug, Display}; + +pub struct DisassembledOpcode { + pub operation: Opcode, + pub program_counter: usize, + pub args: Vec, +} + +impl Debug for DisassembledOpcode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.args.is_empty() { + writeln!(f, "{}", self.operation.name) + } else { + writeln!(f, "{} {:?}", self.operation.name, self.args) + } + } +} + +impl Display for DisassembledOpcode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.args.is_empty() { + write!(f, "{}", self.operation.name) + } else { + write!(f, "{} {:?}", self.operation.name, self.args) + } + } +} + +// Changed version of +// https://github.com/Jon-Becker/heimdall-rs/blob/6363d2fe02b68a4b03e0d5f726f605d1360250b7/common/src/ether/evm/ext/disassemble.rs#L34 +pub fn disassemble_bytecode(bytecode: &Bytes) -> Vec { + let mut program_counter = 0; + let mut output = Vec::new(); + + // Iterate over the bytecode, disassembling each instruction. + while program_counter < bytecode.len() { + let operation = opcode(bytecode[program_counter]); + let args = if operation.name.contains("PUSH") { + let byte_count_to_push: u8 = operation + .name + .replace("PUSH", "") + .parse() + .expect("PUSH should contain number"); + + match bytecode + .get(program_counter + 1..program_counter + 1 + byte_count_to_push as usize) + { + Some(bytes) => bytes.to_vec(), + None => vec![], + } + } else { + vec![] + }; + let opcode = DisassembledOpcode { + operation, + program_counter, + args, + }; + program_counter += opcode.args.len(); + program_counter += 1; + output.push(opcode); + } + + output +} 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 new file mode 100644 index 000000000..3202cab89 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/find_methods.rs @@ -0,0 +1,124 @@ +use super::{ + disassemble::{disassemble_bytecode, DisassembledOpcode}, + method::Method, +}; +use bytes::Bytes; +use ethers_core::abi::Abi; +use ethers_solc::sourcemap::SourceMap; +use std::{collections::BTreeMap, iter::repeat}; + +pub struct LookupMethodsRequest { + pub bytecode: Bytes, + pub abi: Abi, + pub source_map: SourceMap, + pub file_id_map: BTreeMap, +} + +pub struct LookupMethodsResponse { + pub methods: BTreeMap, +} + +pub fn find_methods(request: LookupMethodsRequest) -> LookupMethodsResponse { + let methods = parse_selectors(request.abi); + let opcodes = disassemble_bytecode(&request.bytecode); + let opcodes = opcodes.as_slice(); + + let methods = methods + .into_iter() + .filter_map(|(func_sig, selector)| { + let func_index = find_selector(&selector, opcodes).or_else(|| { + tracing::warn!( + "function {} with selector '{}' not found in bytecode", + func_sig, + hex::encode(selector) + ); + None + })?; + + tracing::info!("found function {} in {}", func_sig, func_index); + let method = Method::from_source_map( + selector, + &request.source_map, + func_index, + &request.file_id_map, + ) + .unwrap(); + Some((hex::encode(selector), method)) + }) + .collect::>(); + LookupMethodsResponse { methods } +} + +fn prepend_selector(partial_selector: &Vec) -> Option> { + if partial_selector.len() > 4 { + return None; + }; + + // prepend selector with 0s if it's shorter than 4 bytes + let mut selector = partial_selector.clone(); + selector.splice(..0, repeat(0).take(4 - partial_selector.len())); + Some(selector) +} + +fn find_selector(selector: &[u8; 4], opcodes: &[DisassembledOpcode]) -> Option { + for window in opcodes.windows(5) { + if window[0].operation.name.starts_with("PUSH") + && window[1].operation.name == "EQ" + && window[2].operation.name.starts_with("PUSH") + && window[3].operation.name == "JUMPI" + { + let push_selector = prepend_selector(&window[0].args).expect("valid selector"); + + if push_selector != selector { + continue; + } + + let jump_to = + usize::from_str_radix(&hex::encode(&window[2].args), 16).expect("valid hex string"); + + let maybe_target_opcode_index = opcodes + .iter() + .enumerate() + .find_map(|(index, opcode)| (opcode.program_counter == jump_to).then_some(index)); + + match maybe_target_opcode_index { + Some(index) => return Some(index), + None => tracing::warn!(selector =? selector, "target opcode not found"), + } + } + + if window[0].operation.name.starts_with("PUSH") + && window[1].operation.name == "DUP2" + && window[2].operation.name == "EQ" + && window[3].operation.name.starts_with("PUSH") + && window[4].operation.name == "JUMPI" + { + let push_selector = prepend_selector(&window[0].args).expect("valid selector"); + + if push_selector != selector { + continue; + } + + let jump_to = + usize::from_str_radix(&hex::encode(&window[3].args), 16).expect("valid hex string"); + + let maybe_target_opcode_index = opcodes + .iter() + .enumerate() + .find_map(|(index, opcode)| (opcode.program_counter == jump_to).then_some(index)); + + match maybe_target_opcode_index { + Some(index) => return Some(index), + None => tracing::warn!(selector =? selector, "target opcode not found"), + } + } + } + + None +} + +fn parse_selectors(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/method.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/method.rs new file mode 100644 index 000000000..6091ef2fe --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/method.rs @@ -0,0 +1,36 @@ +use ethers_solc::sourcemap::SourceMap; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Method { + pub selector: [u8; 4], + pub offset: usize, + pub length: usize, + pub filename: String, +} + +impl Method { + pub fn from_source_map( + selector: [u8; 4], + source_map: &SourceMap, + index: usize, + file_id_map: &BTreeMap, + ) -> anyhow::Result { + let src = source_map + .get(index) + .ok_or_else(|| anyhow::anyhow!("source map doesn't have function index"))?; + + let filename = src + .index + .and_then(|id| file_id_map.get(&id)) + .ok_or_else(|| anyhow::anyhow!("src {:?} not found in output sources", src.index))?; + + println!("src: {:?}", src); + Ok(Method { + selector, + offset: src.offset, + length: src.length, + filename: filename.clone(), + }) + } +} 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 new file mode 100644 index 000000000..8d61a5698 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/mod.rs @@ -0,0 +1,6 @@ +mod disassemble; +mod find_methods; +mod method; +mod opcodes; + +pub use find_methods::{find_methods, LookupMethodsRequest, LookupMethodsResponse}; diff --git a/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/opcodes.rs b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/opcodes.rs new file mode 100644 index 000000000..a82ccba3b --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/lookup_methods/opcodes.rs @@ -0,0 +1,160 @@ +// Changed version of +// https://github.com/Jon-Becker/heimdall-rs/blob/6363d2fe02b68a4b03e0d5f726f605d1360250b7/common/src/ether/evm/core/opcodes.rs + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Opcode { + pub code: u8, + pub name: &'static str, +} + +// Returns the opcode for the given hexcode, fetched from the hashmap. +pub fn opcode(code: u8) -> Opcode { + let name = match code { + 0x00 => "STOP", + 0x01 => "ADD", + 0x02 => "MUL", + 0x03 => "SUB", + 0x04 => "DIV", + 0x05 => "SDIV", + 0x06 => "MOD", + 0x07 => "SMOD", + 0x08 => "ADDMOD", + 0x09 => "MULMOD", + 0x0a => "EXP", + 0x0b => "SIGNEXTEND", + 0x10 => "LT", + 0x11 => "GT", + 0x12 => "SLT", + 0x13 => "SGT", + 0x14 => "EQ", + 0x15 => "ISZERO", + 0x16 => "AND", + 0x17 => "OR", + 0x18 => "XOR", + 0x19 => "NOT", + 0x1a => "BYTE", + 0x1b => "SHL", + 0x1c => "SHR", + 0x1d => "SAR", + 0x20 => "SHA3", + 0x30 => "ADDRESS", + 0x31 => "BALANCE", + 0x32 => "ORIGIN", + 0x33 => "CALLER", + 0x34 => "CALLVALUE", + 0x35 => "CALLDATALOAD", + 0x36 => "CALLDATASIZE", + 0x37 => "CALLDATACOPY", + 0x38 => "CODESIZE", + 0x39 => "CODECOPY", + 0x3a => "GASPRICE", + 0x3b => "EXTCODESIZE", + 0x3c => "EXTCODECOPY", + 0x3d => "RETURNDATASIZE", + 0x3e => "RETURNDATACOPY", + 0x3f => "EXTCODEHASH", + 0x40 => "BLOCKHASH", + 0x41 => "COINBASE", + 0x42 => "TIMESTAMP", + 0x43 => "NUMBER", + 0x44 => "DIFFICULTY", + 0x45 => "GASLIMIT", + 0x46 => "CHAINID", + 0x47 => "SELFBALANCE", + 0x48 => "BASEFEE", + 0x50 => "POP", + 0x51 => "MLOAD", + 0x52 => "MSTORE", + 0x53 => "MSTORE8", + 0x54 => "SLOAD", + 0x55 => "SSTORE", + 0x56 => "JUMP", + 0x57 => "JUMPI", + 0x58 => "PC", + 0x59 => "MSIZE", + 0x5a => "GAS", + 0x5b => "JUMPDEST", + 0x5f => "PUSH0", + 0x60 => "PUSH1", + 0x61 => "PUSH2", + 0x62 => "PUSH3", + 0x63 => "PUSH4", + 0x64 => "PUSH5", + 0x65 => "PUSH6", + 0x66 => "PUSH7", + 0x67 => "PUSH8", + 0x68 => "PUSH9", + 0x69 => "PUSH10", + 0x6a => "PUSH11", + 0x6b => "PUSH12", + 0x6c => "PUSH13", + 0x6d => "PUSH14", + 0x6e => "PUSH15", + 0x6f => "PUSH16", + 0x70 => "PUSH17", + 0x71 => "PUSH18", + 0x72 => "PUSH19", + 0x73 => "PUSH20", + 0x74 => "PUSH21", + 0x75 => "PUSH22", + 0x76 => "PUSH23", + 0x77 => "PUSH24", + 0x78 => "PUSH25", + 0x79 => "PUSH26", + 0x7a => "PUSH27", + 0x7b => "PUSH28", + 0x7c => "PUSH29", + 0x7d => "PUSH30", + 0x7e => "PUSH31", + 0x7f => "PUSH32", + 0x80 => "DUP1", + 0x81 => "DUP2", + 0x82 => "DUP3", + 0x83 => "DUP4", + 0x84 => "DUP5", + 0x85 => "DUP6", + 0x86 => "DUP7", + 0x87 => "DUP8", + 0x88 => "DUP9", + 0x89 => "DUP10", + 0x8a => "DUP11", + 0x8b => "DUP12", + 0x8c => "DUP13", + 0x8d => "DUP14", + 0x8e => "DUP15", + 0x8f => "DUP16", + 0x90 => "SWAP1", + 0x91 => "SWAP2", + 0x92 => "SWAP3", + 0x93 => "SWAP4", + 0x94 => "SWAP5", + 0x95 => "SWAP6", + 0x96 => "SWAP7", + 0x97 => "SWAP8", + 0x98 => "SWAP9", + 0x99 => "SWAP10", + 0x9a => "SWAP11", + 0x9b => "SWAP12", + 0x9c => "SWAP13", + 0x9d => "SWAP14", + 0x9e => "SWAP15", + 0x9f => "SWAP16", + 0xa0 => "LOG0", + 0xa1 => "LOG1", + 0xa2 => "LOG2", + 0xa3 => "LOG3", + 0xa4 => "LOG4", + 0xf0 => "CREATE", + 0xf1 => "CALL", + 0xf2 => "CALLCODE", + 0xf3 => "RETURN", + 0xf4 => "DELEGATECALL", + 0xf5 => "CREATE2", + 0xfa => "STATICCALL", + 0xfd => "REVERT", + 0xfe => "INVALID", + 0xff => "SELFDESTRUCT", + _ => "unknown", + }; + Opcode { code, name } +}