Merge pull request #3061 from poanetwork/vb-fix-contract-verification

Fix verification of contracts with error messages in require in parent contract
pull/3064/head
Victor Baranov 5 years ago committed by GitHub
commit fcb160cba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 15
      apps/explorer/lib/explorer/smart_contract/verifier.ex
  3. 87
      apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
  4. 33
      apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs

@ -4,6 +4,8 @@
### Fixes ### Fixes
- [#3061](https://github.com/poanetwork/blockscout/pull/3061) - Fix verification of contracts with error messages in require in parent contract
### Chore ### Chore

@ -59,12 +59,13 @@ defmodule Explorer.SmartContract.Verifier do
address_hash, address_hash,
constructor_arguments, constructor_arguments,
autodetect_contructor_arguments, autodetect_contructor_arguments,
contract_source_code contract_source_code,
name
) )
end end
defp compare_bytecodes({:error, :name}, _, _, _, _), do: {:error, :name} defp compare_bytecodes({:error, :name}, _, _, _, _, _), do: {:error, :name}
defp compare_bytecodes({:error, _}, _, _, _, _), do: {:error, :compilation} defp compare_bytecodes({:error, _}, _, _, _, _, _), do: {:error, :compilation}
# credo:disable-for-next-line /Complexity/ # credo:disable-for-next-line /Complexity/
defp compare_bytecodes( defp compare_bytecodes(
@ -72,7 +73,8 @@ defmodule Explorer.SmartContract.Verifier do
address_hash, address_hash,
arguments_data, arguments_data,
autodetect_contructor_arguments, autodetect_contructor_arguments,
contract_source_code contract_source_code,
contract_name
) do ) do
generated_bytecode = extract_bytecode(bytecode) generated_bytecode = extract_bytecode(bytecode)
@ -89,7 +91,7 @@ defmodule Explorer.SmartContract.Verifier do
{:error, :generated_bytecode} {:error, :generated_bytecode}
has_constructor_with_params?(abi) && autodetect_contructor_arguments -> has_constructor_with_params?(abi) && autodetect_contructor_arguments ->
result = ConstructorArguments.find_constructor_arguments(address_hash, abi, contract_source_code) result = ConstructorArguments.find_constructor_arguments(address_hash, abi, contract_source_code, contract_name)
if result do if result do
{:ok, %{abi: abi, contructor_arguments: result}} {:ok, %{abi: abi, contructor_arguments: result}}
@ -105,7 +107,8 @@ defmodule Explorer.SmartContract.Verifier do
address_hash, address_hash,
blockchain_bytecode_without_whisper, blockchain_bytecode_without_whisper,
arguments_data, arguments_data,
contract_source_code contract_source_code,
contract_name
) -> ) ->
{:error, :constructor_arguments} {:error, :constructor_arguments}

@ -5,7 +5,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
alias ABI.{FunctionSelector, TypeDecoder} alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain alias Explorer.Chain
def verify(address_hash, contract_code, arguments_data, contract_source_code) do def verify(address_hash, contract_code, arguments_data, contract_source_code, contract_name) do
arguments_data = arguments_data |> String.trim_trailing() |> String.trim_leading("0x") arguments_data = arguments_data |> String.trim_trailing() |> String.trim_leading("0x")
creation_code = creation_code =
@ -18,7 +18,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
if verify_older_version(creation_code, contract_code, check_func) do if verify_older_version(creation_code, contract_code, check_func) do
true true
else else
extract_constructor_arguments(creation_code, check_func, contract_source_code) extract_constructor_arguments(creation_code, check_func, contract_source_code, contract_name)
end end
end end
@ -31,22 +31,22 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
|> check_func.() |> check_func.()
end end
defp extract_constructor_arguments(code, check_func, contract_source_code) do defp extract_constructor_arguments(code, check_func, contract_source_code, contract_name) do
case code do case code do
# Solidity ~ 4.23 # https://solidity.readthedocs.io/en/v0.4.23/metadata.html # Solidity ~ 4.23 # https://solidity.readthedocs.io/en/v0.4.23/metadata.html
"a165627a7a72305820" <> <<_::binary-size(64)>> <> "0029" <> constructor_arguments -> "a165627a7a72305820" <> <<_::binary-size(64)>> <> "0029" <> constructor_arguments ->
extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code) extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name)
# Solidity >= 0.5.10 https://solidity.readthedocs.io/en/v0.5.10/metadata.html # Solidity >= 0.5.10 https://solidity.readthedocs.io/en/v0.5.10/metadata.html
"a265627a7a72305820" <> "a265627a7a72305820" <>
<<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> <<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments ->
extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code) extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name)
# Solidity >= 0.5.11 https://github.com/ethereum/solidity/blob/develop/Changelog.md#0511-2019-08-12 # Solidity >= 0.5.11 https://github.com/ethereum/solidity/blob/develop/Changelog.md#0511-2019-08-12
# Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw:// # Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw://
"a265627a7a72315820" <> "a265627a7a72315820" <>
<<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> <<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments ->
extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code) extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name)
# Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17 # Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17
# https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode # https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode
@ -60,33 +60,40 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
# Fixing PR has been created https://github.com/ethereum/solidity/pull/8174 # Fixing PR has been created https://github.com/ethereum/solidity/pull/8174
"a264697066735822" <> "a264697066735822" <>
<<_::binary-size(68)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments -> <<_::binary-size(68)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments ->
extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code) extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name)
<<>> -> <<>> ->
check_func.("") check_func.("")
<<_::binary-size(2)>> <> rest -> <<_::binary-size(2)>> <> rest ->
extract_constructor_arguments(rest, check_func, contract_source_code) extract_constructor_arguments(rest, check_func, contract_source_code, contract_name)
end end
end end
defp extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code) do defp extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) do
constructor_arguments = constructor_arguments =
remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments) remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name)
check_func_result = check_func.(constructor_arguments) check_func_result = check_func.(constructor_arguments)
if check_func_result do if check_func_result do
check_func_result check_func_result
else else
extract_constructor_arguments(constructor_arguments, check_func, contract_source_code) extract_constructor_arguments(constructor_arguments, check_func, contract_source_code, contract_name)
end end
end end
def remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments) do def remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name) do
msgs_list = all_msgs =
contract_source_code contract_source_code
|> extract_require_messages_from_constructor() |> extract_require_messages_from_constructor(contract_name)
filtered_msgs =
all_msgs
|> Enum.filter(fn require_msg -> require_msg != nil end)
msgs_list =
filtered_msgs
|> Enum.reverse() |> Enum.reverse()
Enum.reduce(msgs_list, constructor_arguments, fn msg, pure_constructor_arguments -> Enum.reduce(msgs_list, constructor_arguments, fn msg, pure_constructor_arguments ->
@ -100,7 +107,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
end) end)
end end
def find_constructor_arguments(address_hash, abi, contract_source_code) do def find_constructor_arguments(address_hash, abi, contract_source_code, contract_name) do
creation_code = creation_code =
address_hash address_hash
|> Chain.contract_creation_input_data() |> Chain.contract_creation_input_data()
@ -123,17 +130,17 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
end end
end end
extract_constructor_arguments(creation_code, check_func, contract_source_code) extract_constructor_arguments(creation_code, check_func, contract_source_code, contract_name)
end end
def extract_require_messages_from_constructor(contract_source_code) do def extract_require_messages_from_constructor(contract_source_code, _contract_name) do
constructor = find_constructor_content(contract_source_code) # todo: _contract_name is for parsing of actually used constructor for concrete contract
require_contents = find_require_in_constructor(constructor) require_contents = find_all_requires(contract_source_code)
messages_list = messages_list =
Enum.reduce(require_contents, [], fn require_content, msgs -> Enum.reduce(require_contents, [], fn require_content, msgs ->
msg = get_require_message_hex(require_content) msg = get_require_message_hex(require_content)
if msg, do: [msg | msgs] if msg, do: [msg | msgs], else: msgs
end) end)
if messages_list, do: messages_list, else: [] if messages_list, do: messages_list, else: []
@ -165,19 +172,35 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
end end
end end
def get_require_message_hex(require_content) do def find_all_requires(contract_source_code) do
case String.split(require_content, ",", parts: 2) do if contract_source_code do
[_ | [msg]] -> [_ | requires] = String.split(contract_source_code, "require")
msg
|> String.trim()
|> String.trim_leading("\"")
|> String.trim_trailing("\"")
|> String.trim_leading("'")
|> String.trim_trailing("'")
|> Base.encode16(case: :lower)
[_] -> Enum.reduce(requires, [], fn right_from_require, requires_list ->
nil [_ | [right_from_require_inside]] = String.split(right_from_require, "(", parts: 2)
[require_content | _] = String.split(right_from_require_inside, ");", parts: 2)
[require_content | requires_list]
end)
else
[]
end
end
def get_require_message_hex(require_content) do
parts = String.split(require_content, ",")
if Enum.count(parts) > 1 do
[msg] = Enum.take(parts, -1)
msg
|> String.trim()
|> String.trim_leading("\"")
|> String.trim_trailing("\"")
|> String.trim_leading("'")
|> String.trim_trailing("'")
|> Base.encode16(case: :lower)
else
nil
end end
end end
end end

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save