diff --git a/.dialyzer-ignore b/.dialyzer-ignore index 0e9541ca7a..98a9e6069c 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -26,3 +26,4 @@ lib/indexer/fetcher/token_total_supply_on_demand.ex:16 lib/explorer/exchange_rates/source.ex:41 lib/explorer/exchange_rates/source/coin_gecko.ex:115 lib/explorer/exchange_rates/source/coin_gecko.ex:136 +lib/explorer/smart_contract/verifier.ex:89 diff --git a/CHANGELOG.md b/CHANGELOG.md index d4c8a94dda..fb6148b775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [#3261](https://github.com/poanetwork/blockscout/pull/3261) - Bridged tokens table ### Fixes +- [#3307](https://github.com/poanetwork/blockscout/pull/3307) - Replace "latest" compiler version with the actual one - [#3303](https://github.com/poanetwork/blockscout/pull/3303) - Address contract twins feature performance - [#3295](https://github.com/poanetwork/blockscout/pull/3295) - Token instance: check if external_url is not null before trimming - [#3291](https://github.com/poanetwork/blockscout/pull/3291) - Support unlimited number of external rewards in block diff --git a/apps/explorer/lib/explorer/smart_contract/publisher.ex b/apps/explorer/lib/explorer/smart_contract/publisher.ex index 24c51b2d24..bb9a925e42 100644 --- a/apps/explorer/lib/explorer/smart_contract/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/publisher.ex @@ -5,6 +5,7 @@ defmodule Explorer.SmartContract.Publisher do alias Explorer.Chain alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.Solidity.CompilerVersion alias Explorer.SmartContract.Verifier @doc """ @@ -76,10 +77,12 @@ defmodule Explorer.SmartContract.Publisher do prepared_external_libraries = prepare_external_libraies(params["external_libraries"]) + compiler_version = CompilerVersion.get_strict_compiler_version(params["compiler_version"]) + %{ address_hash: address_hash, name: params["name"], - compiler_version: params["compiler_version"], + compiler_version: compiler_version, evm_version: params["evm_version"], optimization_runs: params["optimization_runs"], optimization: params["optimization"], diff --git a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex index 4c4fea4109..f1658cd518 100644 --- a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex +++ b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex @@ -12,7 +12,7 @@ defmodule Explorer.SmartContract.SolcDownloader do def ensure_exists(version) do path = file_path(version) - if File.exists?(path) do + if File.exists?(path) && version !== "latest" do path else {:ok, compiler_versions} = CompilerVersion.fetch_versions() diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex index 9096a405de..d679cfd635 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex @@ -60,4 +60,29 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersion do "#{solc_bin_api_url}/bin/list.json" end + + def get_strict_compiler_version(compiler_version) do + if compiler_version == "latest" do + {:ok, compiler_versions} = fetch_versions() + + if Enum.count(compiler_versions) > 1 do + latest_stable_version = + compiler_versions + |> Enum.drop(1) + |> Enum.reduce_while("", fn version, acc -> + if String.contains?(version, "-nightly") do + {:cont, acc} + else + {:halt, version} + end + end) + + latest_stable_version + else + "latest" + end + else + compiler_version + end + end end diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index 86f5b40a71..8491d126f8 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -33,14 +33,20 @@ defmodule Explorer.SmartContract.Verifier do all_versions_extra = all_versions ++ [evm_version] - Enum.reduce(all_versions_extra, false, fn version, acc -> + Enum.reduce_while(all_versions_extra, false, fn version, acc -> case acc do {:ok, _} = result -> - result + {:cont, result} + + {:error, :compiler_version} -> + {:halt, acc} + + {:error, :name} -> + {:halt, acc} _ -> cur_params = Map.put(params, "evm_version", version) - verify(address_hash, cur_params) + {:cont, verify(address_hash, cur_params)} end end) end @@ -112,13 +118,13 @@ defmodule Explorer.SmartContract.Verifier do %{ "metadata_hash" => _metadata_hash, "bytecode" => blockchain_bytecode_without_whisper, - "compiler_version" => compiler_version + "compiler_version" => compiler_version_from_input } = extract_bytecode_and_metadata_hash(blockchain_created_tx_input) empty_constructor_arguments = arguments_data == "" or arguments_data == nil cond do - generated_compiler_version != compiler_version -> + compiler_version_from_input != generated_compiler_version -> {:error, :compiler_version} generated_bytecode != blockchain_bytecode_without_whisper && @@ -226,7 +232,9 @@ defmodule Explorer.SmartContract.Verifier do @metadata_hash_prefix_0_5_family_2 <> <> <> @metadata_hash_common_suffix <> - "7826" <> <> <> "0057" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) <<_::binary-size(2)>> <> @@ -235,7 +243,9 @@ defmodule Explorer.SmartContract.Verifier do @metadata_hash_prefix_0_5_family_2 <> <> <> @metadata_hash_common_suffix <> - "7827" <> <> <> "0057" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) <<_::binary-size(2)>> <> @@ -244,7 +254,9 @@ defmodule Explorer.SmartContract.Verifier do @metadata_hash_prefix_0_5_family_2 <> <> <> @metadata_hash_common_suffix <> - "7828" <> <> <> "0058" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) <<_::binary-size(2)>> <> @@ -253,7 +265,9 @@ defmodule Explorer.SmartContract.Verifier do @metadata_hash_prefix_0_5_family_2 <> <> <> @metadata_hash_common_suffix <> - "7829" <> <> <> "0059" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) # Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17 @@ -275,25 +289,33 @@ defmodule Explorer.SmartContract.Verifier do @metadata_hash_prefix_0_6_0 <> <> <> @metadata_hash_common_suffix <> - "7826" <> <> <> "0057" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) @metadata_hash_prefix_0_6_0 <> <> <> @metadata_hash_common_suffix <> - "7827" <> <> <> "0057" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) @metadata_hash_prefix_0_6_0 <> <> <> @metadata_hash_common_suffix <> - "7828" <> <> <> "0058" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) @metadata_hash_prefix_0_6_0 <> <> <> @metadata_hash_common_suffix <> - "7829" <> <> <> "0059" <> _constructor_arguments -> + "78" <> + <<_::binary-size(2)>> <> + <> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments -> do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) <> <> rest -> diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index b9cbc69a6e..ac170a6cf5 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -757,33 +757,32 @@ defmodule Explorer.SmartContract.VerifierTest do assert abi != nil end - # flaky test - # test "verification is failed if wrong nightly version of compiler ~0.5.11" do - # bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = - # "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" + test "verification is failed if wrong nightly version of compiler ~0.5.11" do + bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = + "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" - # constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - # contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) - # bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) + bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" - # :transaction - # |> insert( - # created_contract_address_hash: contract_address.hash, - # input: bytecode_construtor_arguments - # ) - # |> with_block() + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() - # params = %{ - # "contract_source_code" => @code_0_5, - # "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2", - # "evm_version" => "homestead", - # "name" => "Incrementer", - # "optimization" => false, - # "constructor_arguments" => constructor_arguments - # } + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } - # response = Verifier.evaluate_authenticity(contract_address.hash, params) - # assert {:error, :compiler_version} = response - # end + response = Verifier.evaluate_authenticity(contract_address.hash, params) + assert {:error, :compiler_version} = response + end end end