diff --git a/.dialyzer-ignore b/.dialyzer-ignore index 9a1636eee1..271ef7d322 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -23,8 +23,8 @@ lib/indexer/fetcher/token_total_supply_on_demand.ex:16 lib/explorer/exchange_rates/source.ex:110 lib/explorer/exchange_rates/source.ex:113 lib/explorer/smart_contract/solidity/verifier.ex:162 -lib/block_scout_web/templates/address_contract/index.html.eex:159 -lib/block_scout_web/templates/address_contract/index.html.eex:196 +lib/block_scout_web/templates/address_contract/index.html.eex:162 +lib/block_scout_web/templates/address_contract/index.html.eex:199 lib/explorer/staking/stake_snapshotting.ex:15: Function do_snapshotting/7 has no local return lib/explorer/staking/stake_snapshotting.ex:147 lib/explorer/third_party_integrations/sourcify.ex:70 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c2748c95f..01f64b844a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Current ### Features +- [#4924](https://github.com/blockscout/blockscout/pull/4924) - Add daily bytecode verifcation to prevent metamorphic contracts vulnerablity - [#4908](https://github.com/blockscout/blockscout/pull/4908) - Add verification via standard JSON input - [#5004](https://github.com/blockscout/blockscout/pull/5004) - Add ability to set up a separate DB endpoint for the API endpoints - [#4989](https://github.com/blockscout/blockscout/pull/4989), [#4991](https://github.com/blockscout/blockscout/pull/4991) - Bridged tokens list API endpoint diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index 5b24e21d97..2a371a92f4 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -31,6 +31,9 @@ <% end %> <% end %> <% end %> + <%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %> + <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %> + <% end %> <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> <%= if @address.smart_contract.partially_verified && smart_contract_verified do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex new file mode 100644 index 0000000000..665733f715 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex @@ -0,0 +1,4 @@ +
+ <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> + <%= gettext("Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.") %> +
\ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex index 21125a712b..4dd73b65f9 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex @@ -1,6 +1,7 @@ <% minimal_proxy_template = Chain.get_minimal_proxy_template(@address.hash) %> <% metadata_for_verification = minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %> -<%= unless BlockScoutWeb.AddressView.smart_contract_verified?(@address) do %> +<% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> +<%= unless smart_contract_verified do %> <%= if metadata_for_verification do %> <%= if minimal_proxy_template do %> <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %> @@ -17,6 +18,9 @@ <% end %> <% end %> <% end %> +<%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %> + <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %> +<% end %> <%= if @action == "write" or (@read_functions_required_wallet && @read_functions_required_wallet != []) do %> <%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %> <% end %> diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index a8c608569d..df65d5a225 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -215,7 +215,7 @@ msgid "All" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13 msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with" msgstr "" @@ -582,7 +582,7 @@ msgid "Compiler" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:62 +#: lib/block_scout_web/templates/address_contract/index.html.eex:65 msgid "Compiler version" msgstr "" @@ -633,7 +633,7 @@ msgid "Connection Lost, click to load newer validations" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:87 +#: lib/block_scout_web/templates/address_contract/index.html.eex:90 msgid "Constructor Arguments" msgstr "" @@ -643,7 +643,7 @@ msgid "Contract" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:123 +#: lib/block_scout_web/templates/address_contract/index.html.eex:126 msgid "Contract ABI" msgstr "" @@ -673,8 +673,8 @@ msgid "Contract Creation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:139 -#: lib/block_scout_web/templates/address_contract/index.html.eex:154 +#: lib/block_scout_web/templates/address_contract/index.html.eex:142 +#: lib/block_scout_web/templates/address_contract/index.html.eex:157 msgid "Contract Creation Code" msgstr "" @@ -692,22 +692,22 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:25 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:10 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11 msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:54 +#: lib/block_scout_web/templates/address_contract/index.html.eex:57 msgid "Contract name:" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:97 +#: lib/block_scout_web/templates/address_contract/index.html.eex:100 msgid "Contract source code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:145 +#: lib/block_scout_web/templates/address_contract/index.html.eex:148 msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgstr "" @@ -717,7 +717,7 @@ msgid "Contribute" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:125 +#: lib/block_scout_web/templates/address_contract/index.html.eex:128 msgid "Copy ABI" msgstr "" @@ -730,8 +730,8 @@ msgid "Copy Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:141 -#: lib/block_scout_web/templates/address_contract/index.html.eex:157 +#: lib/block_scout_web/templates/address_contract/index.html.eex:144 +#: lib/block_scout_web/templates/address_contract/index.html.eex:160 msgid "Copy Contract Creation Code" msgstr "" @@ -741,8 +741,8 @@ msgid "Copy Decompiled Contract Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:184 -#: lib/block_scout_web/templates/address_contract/index.html.eex:194 +#: lib/block_scout_web/templates/address_contract/index.html.eex:187 +#: lib/block_scout_web/templates/address_contract/index.html.eex:197 msgid "Copy Deployed ByteCode" msgstr "" @@ -776,8 +776,8 @@ msgid "Copy Raw Trace" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:99 -#: lib/block_scout_web/templates/address_contract/index.html.eex:112 +#: lib/block_scout_web/templates/address_contract/index.html.eex:102 +#: lib/block_scout_web/templates/address_contract/index.html.eex:115 msgid "Copy Source Code" msgstr "" @@ -947,8 +947,8 @@ msgid "Delegators’ Staked Amount" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:182 -#: lib/block_scout_web/templates/address_contract/index.html.eex:190 +#: lib/block_scout_web/templates/address_contract/index.html.eex:185 +#: lib/block_scout_web/templates/address_contract/index.html.eex:193 msgid "Deployed ByteCode" msgstr "" @@ -971,7 +971,7 @@ msgid "Difficulty" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:146 +#: lib/block_scout_web/templates/address_contract/index.html.eex:149 msgid "Displaying the init data provided of the creating transaction." msgstr "" @@ -1017,8 +1017,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_token/overview.html.eex:1 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:128 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93 lib/block_scout_web/templates/smart_contract/_functions.html.eex:93 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:132 msgid "ETH" msgstr "" @@ -1028,7 +1028,7 @@ msgid "ETH RPC API Documentation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:73 +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 #: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:40 msgid "EVM Version" msgstr "" @@ -1138,7 +1138,7 @@ msgid "Export Data" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:225 +#: lib/block_scout_web/templates/address_contract/index.html.eex:228 msgid "External libraries" msgstr "" @@ -1706,12 +1706,12 @@ msgid "OUT" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:58 +#: lib/block_scout_web/templates/address_contract/index.html.eex:61 msgid "Optimization enabled" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:67 +#: lib/block_scout_web/templates/address_contract/index.html.eex:70 #: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:72 msgid "Optimization runs" msgstr "" @@ -1864,7 +1864,7 @@ msgid "QR Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:94 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98 msgid "Query" msgstr "" @@ -2265,7 +2265,7 @@ msgid "The block height of a particular block is defined as the number of blocks msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41 msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable." msgstr "" @@ -2315,7 +2315,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43 msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception." msgstr "" @@ -2467,12 +2467,12 @@ msgid "This block has not been processed yet." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:38 +#: lib/block_scout_web/templates/address_contract/index.html.eex:41 msgid "This contract has been partially verified via Sourcify." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:42 +#: lib/block_scout_web/templates/address_contract/index.html.eex:45 msgid "This contract has been verified via Sourcify." msgstr "" @@ -2911,15 +2911,15 @@ msgid "Value sent in the native token (and USD) if applicable." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:79 +#: lib/block_scout_web/templates/address_contract/index.html.eex:82 msgid "Verified at" msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:27 -#: lib/block_scout_web/templates/address_contract/index.html.eex:29 lib/block_scout_web/templates/address_contract/index.html.eex:161 -#: lib/block_scout_web/templates/address_contract/index.html.eex:167 lib/block_scout_web/templates/address_contract/index.html.eex:198 -#: lib/block_scout_web/templates/address_contract/index.html.eex:204 lib/block_scout_web/templates/smart_contract/_functions.html.eex:13 +#: lib/block_scout_web/templates/address_contract/index.html.eex:29 lib/block_scout_web/templates/address_contract/index.html.eex:164 +#: lib/block_scout_web/templates/address_contract/index.html.eex:170 lib/block_scout_web/templates/address_contract/index.html.eex:201 +#: lib/block_scout_web/templates/address_contract/index.html.eex:207 lib/block_scout_web/templates/smart_contract/_functions.html.eex:14 msgid "Verify & Publish" msgstr "" @@ -3004,7 +3004,7 @@ msgid "Vyper contract" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:127 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:131 msgid "WEI" msgstr "" @@ -3018,6 +3018,11 @@ msgstr "" msgid "Wallet addresses" msgstr "" +#, elixir-format +#: lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex:3 +msgid "Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky." +msgstr "" + #, elixir-format #: lib/block_scout_web/templates/stakes/_stakes_modal_claim_reward_content.html.eex:2 msgid "We found the following pools you can claim reward from:" @@ -3054,7 +3059,7 @@ msgid "Working Stake Amount" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:94 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98 msgid "Write" msgstr "" @@ -3184,7 +3189,7 @@ msgid "custom RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41 msgid "fallback" msgstr "" @@ -3220,7 +3225,7 @@ msgid "of" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:15 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16 msgid "page" msgstr "" @@ -3230,7 +3235,7 @@ msgid "pool owner" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43 msgid "receive" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index a8c608569d..df65d5a225 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -215,7 +215,7 @@ msgid "All" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13 msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with" msgstr "" @@ -582,7 +582,7 @@ msgid "Compiler" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:62 +#: lib/block_scout_web/templates/address_contract/index.html.eex:65 msgid "Compiler version" msgstr "" @@ -633,7 +633,7 @@ msgid "Connection Lost, click to load newer validations" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:87 +#: lib/block_scout_web/templates/address_contract/index.html.eex:90 msgid "Constructor Arguments" msgstr "" @@ -643,7 +643,7 @@ msgid "Contract" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:123 +#: lib/block_scout_web/templates/address_contract/index.html.eex:126 msgid "Contract ABI" msgstr "" @@ -673,8 +673,8 @@ msgid "Contract Creation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:139 -#: lib/block_scout_web/templates/address_contract/index.html.eex:154 +#: lib/block_scout_web/templates/address_contract/index.html.eex:142 +#: lib/block_scout_web/templates/address_contract/index.html.eex:157 msgid "Contract Creation Code" msgstr "" @@ -692,22 +692,22 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:25 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:10 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11 msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:54 +#: lib/block_scout_web/templates/address_contract/index.html.eex:57 msgid "Contract name:" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:97 +#: lib/block_scout_web/templates/address_contract/index.html.eex:100 msgid "Contract source code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:145 +#: lib/block_scout_web/templates/address_contract/index.html.eex:148 msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgstr "" @@ -717,7 +717,7 @@ msgid "Contribute" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:125 +#: lib/block_scout_web/templates/address_contract/index.html.eex:128 msgid "Copy ABI" msgstr "" @@ -730,8 +730,8 @@ msgid "Copy Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:141 -#: lib/block_scout_web/templates/address_contract/index.html.eex:157 +#: lib/block_scout_web/templates/address_contract/index.html.eex:144 +#: lib/block_scout_web/templates/address_contract/index.html.eex:160 msgid "Copy Contract Creation Code" msgstr "" @@ -741,8 +741,8 @@ msgid "Copy Decompiled Contract Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:184 -#: lib/block_scout_web/templates/address_contract/index.html.eex:194 +#: lib/block_scout_web/templates/address_contract/index.html.eex:187 +#: lib/block_scout_web/templates/address_contract/index.html.eex:197 msgid "Copy Deployed ByteCode" msgstr "" @@ -776,8 +776,8 @@ msgid "Copy Raw Trace" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:99 -#: lib/block_scout_web/templates/address_contract/index.html.eex:112 +#: lib/block_scout_web/templates/address_contract/index.html.eex:102 +#: lib/block_scout_web/templates/address_contract/index.html.eex:115 msgid "Copy Source Code" msgstr "" @@ -947,8 +947,8 @@ msgid "Delegators’ Staked Amount" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:182 -#: lib/block_scout_web/templates/address_contract/index.html.eex:190 +#: lib/block_scout_web/templates/address_contract/index.html.eex:185 +#: lib/block_scout_web/templates/address_contract/index.html.eex:193 msgid "Deployed ByteCode" msgstr "" @@ -971,7 +971,7 @@ msgid "Difficulty" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:146 +#: lib/block_scout_web/templates/address_contract/index.html.eex:149 msgid "Displaying the init data provided of the creating transaction." msgstr "" @@ -1017,8 +1017,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_token/overview.html.eex:1 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:128 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93 lib/block_scout_web/templates/smart_contract/_functions.html.eex:93 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:132 msgid "ETH" msgstr "" @@ -1028,7 +1028,7 @@ msgid "ETH RPC API Documentation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:73 +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 #: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:40 msgid "EVM Version" msgstr "" @@ -1138,7 +1138,7 @@ msgid "Export Data" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:225 +#: lib/block_scout_web/templates/address_contract/index.html.eex:228 msgid "External libraries" msgstr "" @@ -1706,12 +1706,12 @@ msgid "OUT" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:58 +#: lib/block_scout_web/templates/address_contract/index.html.eex:61 msgid "Optimization enabled" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:67 +#: lib/block_scout_web/templates/address_contract/index.html.eex:70 #: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:72 msgid "Optimization runs" msgstr "" @@ -1864,7 +1864,7 @@ msgid "QR Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:94 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98 msgid "Query" msgstr "" @@ -2265,7 +2265,7 @@ msgid "The block height of a particular block is defined as the number of blocks msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41 msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable." msgstr "" @@ -2315,7 +2315,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43 msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception." msgstr "" @@ -2467,12 +2467,12 @@ msgid "This block has not been processed yet." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:38 +#: lib/block_scout_web/templates/address_contract/index.html.eex:41 msgid "This contract has been partially verified via Sourcify." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:42 +#: lib/block_scout_web/templates/address_contract/index.html.eex:45 msgid "This contract has been verified via Sourcify." msgstr "" @@ -2911,15 +2911,15 @@ msgid "Value sent in the native token (and USD) if applicable." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:79 +#: lib/block_scout_web/templates/address_contract/index.html.eex:82 msgid "Verified at" msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:27 -#: lib/block_scout_web/templates/address_contract/index.html.eex:29 lib/block_scout_web/templates/address_contract/index.html.eex:161 -#: lib/block_scout_web/templates/address_contract/index.html.eex:167 lib/block_scout_web/templates/address_contract/index.html.eex:198 -#: lib/block_scout_web/templates/address_contract/index.html.eex:204 lib/block_scout_web/templates/smart_contract/_functions.html.eex:13 +#: lib/block_scout_web/templates/address_contract/index.html.eex:29 lib/block_scout_web/templates/address_contract/index.html.eex:164 +#: lib/block_scout_web/templates/address_contract/index.html.eex:170 lib/block_scout_web/templates/address_contract/index.html.eex:201 +#: lib/block_scout_web/templates/address_contract/index.html.eex:207 lib/block_scout_web/templates/smart_contract/_functions.html.eex:14 msgid "Verify & Publish" msgstr "" @@ -3004,7 +3004,7 @@ msgid "Vyper contract" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:127 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:131 msgid "WEI" msgstr "" @@ -3018,6 +3018,11 @@ msgstr "" msgid "Wallet addresses" msgstr "" +#, elixir-format +#: lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex:3 +msgid "Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky." +msgstr "" + #, elixir-format #: lib/block_scout_web/templates/stakes/_stakes_modal_claim_reward_content.html.eex:2 msgid "We found the following pools you can claim reward from:" @@ -3054,7 +3059,7 @@ msgid "Working Stake Amount" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:94 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98 msgid "Write" msgstr "" @@ -3184,7 +3189,7 @@ msgid "custom RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41 msgid "fallback" msgstr "" @@ -3220,7 +3225,7 @@ msgid "of" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:15 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16 msgid "page" msgstr "" @@ -3230,7 +3235,7 @@ msgid "pool owner" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43 msgid "receive" msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs index 102df87ebe..e13af34d94 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs @@ -85,6 +85,12 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do def get_eip1967_implementation do EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) |> expect(:json_rpc, fn %{ id: 0, method: "eth_getStorageAt", diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs index 2c2075461a..dd8781f993 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs @@ -82,6 +82,12 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do def get_eip1967_implementation do EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) |> expect(:json_rpc, fn %{ id: 0, method: "eth_getStorageAt", diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs index 62a3b4d0a6..06a0c070f1 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs @@ -86,6 +86,12 @@ defmodule BlockScoutWeb.AddressWriteContractControllerTest do def get_eip1967_implementation do EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) |> expect(:json_rpc, fn %{ id: 0, method: "eth_getStorageAt", diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs index 481ecfcadb..17dfcdd982 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs @@ -84,6 +84,12 @@ defmodule BlockScoutWeb.AddressWriteProxyControllerTest do def get_eip1967_implementation do EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) |> expect(:json_rpc, fn %{ id: 0, method: "eth_getStorageAt", diff --git a/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs index 2dc0ac8a39..07e1124311 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs @@ -47,6 +47,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do insert(:smart_contract, address_hash: token_contract_address.hash) + blockchain_get_code_mock() blockchain_get_function_mock() path = @@ -83,6 +84,8 @@ defmodule BlockScoutWeb.SmartContractControllerTest do ] ) + blockchain_get_code_mock() + path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: token_contract_address.hash, @@ -117,6 +120,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do ] ) + blockchain_get_code_mock() blockchain_get_implementation_mock() path = @@ -153,6 +157,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do ] ) + blockchain_get_code_mock() blockchain_get_implementation_mock_2() path = @@ -232,6 +237,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do address = insert(:contract_address) smart_contract = insert(:smart_contract, address_hash: address.hash) + blockchain_get_code_mock() blockchain_get_function_mock() path = @@ -269,6 +275,16 @@ defmodule BlockScoutWeb.SmartContractControllerTest do ) end + defp blockchain_get_code_mock do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + end + defp blockchain_get_implementation_mock do expect( EthereumJSONRPC.Mox, diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 3c3a8893f7..cd74312f35 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -101,6 +101,9 @@ defmodule Explorer.Chain do @burn_address_hash_str "0x0000000000000000000000000000000000000000" + # seconds + @check_bytecode_interval 86_400 + @typedoc """ The name of an association on the `t:Ecto.Schema.t/0` """ @@ -1700,7 +1703,7 @@ defmodule Explorer.Chain do case address_result do %{smart_contract: smart_contract} -> if smart_contract do - address_result + check_bytecode_matching(address_result) else address_verified_twin_contract = Chain.get_minimal_proxy_template(hash) || @@ -1730,6 +1733,46 @@ defmodule Explorer.Chain do end end + defp check_bytecode_matching(address) do + now = DateTime.utc_now() + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + if !address.smart_contract.is_changed_bytecode and + address.smart_contract.bytecode_checked_at + |> DateTime.add(@check_bytecode_interval, :second) + |> DateTime.compare(now) != :gt do + case EthereumJSONRPC.fetch_codes( + [%{block_quantity: "latest", address: address.smart_contract.address_hash}], + json_rpc_named_arguments + ) do + {:ok, %EthereumJSONRPC.FetchedCodes{params_list: fetched_codes}} -> + bytecode_from_node = fetched_codes |> List.first() |> Map.get(:code) + bytecode_from_db = "0x" <> (address.contract_code.bytes |> Base.encode16(case: :lower)) + + if bytecode_from_node == bytecode_from_db do + {:ok, smart_contract} = + address.smart_contract + |> Changeset.change(%{bytecode_checked_at: now}) + |> Repo.update() + + %{address | smart_contract: smart_contract} + else + {:ok, smart_contract} = + address.smart_contract + |> Changeset.change(%{bytecode_checked_at: now, is_changed_bytecode: true}) + |> Repo.update() + + %{address | smart_contract: smart_contract} + end + + _ -> + address + end + else + address + end + end + @spec find_decompiled_contract_address(Hash.Address.t()) :: {:ok, Address.t()} | {:error, :not_found} def find_decompiled_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do query = diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 5c52bd5a06..502a7e8088 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -195,6 +195,9 @@ defmodule Explorer.Chain.SmartContract do * `verified_via_sourcify` - whether contract verified through Sourcify utility or not. * `partially_verified` - whether contract verified using partial matched source code or not. * `is_vyper_contract` - boolean flag, determines if contract is Vyper or not + * `file_path` - show the filename or path to the file of the contract source file + * `is_changed_bytecode` - boolean flag, determines if contract's bytecode was modified + * `bytecode_checked_at` - timestamp of the last check of contract's bytecode matching (DB and BlockChain) """ @type t :: %Explorer.Chain.SmartContract{ @@ -209,7 +212,9 @@ defmodule Explorer.Chain.SmartContract do verified_via_sourcify: boolean | nil, partially_verified: boolean | nil, file_path: String.t(), - is_vyper_contract: boolean | nil + is_vyper_contract: boolean | nil, + is_changed_bytecode: boolean, + bytecode_checked_at: DateTime.t() } schema "smart_contracts" do @@ -226,6 +231,8 @@ defmodule Explorer.Chain.SmartContract do field(:partially_verified, :boolean) field(:file_path, :string) field(:is_vyper_contract, :boolean) + field(:is_changed_bytecode, :boolean, default: false) + field(:bytecode_checked_at, :utc_datetime_usec, default: DateTime.add(DateTime.utc_now(), -86400, :second)) has_many( :decompiled_smart_contracts, @@ -263,7 +270,9 @@ defmodule Explorer.Chain.SmartContract do :verified_via_sourcify, :partially_verified, :file_path, - :is_vyper_contract + :is_vyper_contract, + :is_changed_bytecode, + :bytecode_checked_at ]) |> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash]) |> unique_constraint(:address_hash) @@ -291,7 +300,9 @@ defmodule Explorer.Chain.SmartContract do :verified_via_sourcify, :partially_verified, :file_path, - :is_vyper_contract + :is_vyper_contract, + :is_changed_bytecode, + :bytecode_checked_at ]) |> (&if(json_verification, do: &1, diff --git a/apps/explorer/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs b/apps/explorer/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs new file mode 100644 index 0000000000..dfb86074be --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs @@ -0,0 +1,13 @@ +defmodule Explorer.Repo.Migrations.AddCheckForBytecodeActuality do + use Ecto.Migration + + def change do + alter table(:smart_contracts) do + add(:is_changed_bytecode, :boolean, default: false) + # subtracting 1 day to perform first check + add(:bytecode_checked_at, :"timestamp without time zone", + default: fragment("(NOW() AT TIME ZONE 'utc') - INTERVAL '1 DAY'") + ) + end + end +end diff --git a/apps/explorer/test/explorer/chain/log_test.exs b/apps/explorer/test/explorer/chain/log_test.exs index f670c83873..8a6fecb492 100644 --- a/apps/explorer/test/explorer/chain/log_test.exs +++ b/apps/explorer/test/explorer/chain/log_test.exs @@ -1,6 +1,8 @@ defmodule Explorer.Chain.LogTest do use Explorer.DataCase + import Mox + alias Ecto.Changeset alias Explorer.Chain.{Log, SmartContract} alias Explorer.Repo @@ -99,6 +101,8 @@ defmodule Explorer.Chain.LogTest do data: data ) + blockchain_get_code_mock() + assert Log.decode(log, transaction) == {:ok, "eb9b3c4c", "WantsPets(string indexed _from_human, uint256 _number, bool indexed _belly)", [ @@ -167,4 +171,14 @@ defmodule Explorer.Chain.LogTest do ]} end end + + defp blockchain_get_code_mock do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + end end