Skip to content

Commit

Permalink
Find all require messages in the source code
Browse files Browse the repository at this point in the history
  • Loading branch information
vbaranov committed Mar 26, 2020
1 parent df67fa7 commit 2b7fc92
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 39 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Fixes

- [#3061](https://github.com/poanetwork/blockscout/pull/3061) - Fix verification of contracts with error messages in require in parent contract

### Chore


Expand Down
15 changes: 9 additions & 6 deletions apps/explorer/lib/explorer/smart_contract/verifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,22 @@ defmodule Explorer.SmartContract.Verifier do
address_hash,
constructor_arguments,
autodetect_contructor_arguments,
contract_source_code
contract_source_code,
name
)
end

defp compare_bytecodes({:error, :name}, _, _, _, _), do: {:error, :name}
defp compare_bytecodes({:error, _}, _, _, _, _), do: {:error, :compilation}
defp compare_bytecodes({:error, :name}, _, _, _, _, _), do: {:error, :name}
defp compare_bytecodes({:error, _}, _, _, _, _, _), do: {:error, :compilation}

# credo:disable-for-next-line /Complexity/
defp compare_bytecodes(
{:ok, %{"abi" => abi, "bytecode" => bytecode}},
address_hash,
arguments_data,
autodetect_contructor_arguments,
contract_source_code
contract_source_code,
contract_name
) do
generated_bytecode = extract_bytecode(bytecode)

Expand All @@ -89,7 +91,7 @@ defmodule Explorer.SmartContract.Verifier do
{:error, :generated_bytecode}

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
{:ok, %{abi: abi, contructor_arguments: result}}
Expand All @@ -105,7 +107,8 @@ defmodule Explorer.SmartContract.Verifier do
address_hash,
blockchain_bytecode_without_whisper,
arguments_data,
contract_source_code
contract_source_code,
contract_name
) ->
{:error, :constructor_arguments}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
alias ABI.{FunctionSelector, TypeDecoder}
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")

creation_code =
Expand All @@ -18,7 +18,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
if verify_older_version(creation_code, contract_code, check_func) do
true
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

Expand All @@ -31,22 +31,22 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
|> check_func.()
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
# Solidity ~ 4.23 # https://solidity.readthedocs.io/en/v0.4.23/metadata.html
"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
"a265627a7a72305820" <>
<<_::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
# Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw://
"a265627a7a72315820" <>
<<_::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
# https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode
Expand All @@ -60,33 +60,40 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
# Fixing PR has been created https://github.com/ethereum/solidity/pull/8174
"a264697066735822" <>
<<_::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.("")

<<_::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

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 =
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)

if check_func_result do
check_func_result
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

def remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments) do
msgs_list =
def remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name) do
all_msgs =
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.reduce(msgs_list, constructor_arguments, fn msg, pure_constructor_arguments ->
Expand All @@ -100,7 +107,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
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 =
address_hash
|> Chain.contract_creation_input_data()
Expand All @@ -123,17 +130,17 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
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

def extract_require_messages_from_constructor(contract_source_code) do
constructor = find_constructor_content(contract_source_code)
require_contents = find_require_in_constructor(constructor)
def extract_require_messages_from_constructor(contract_source_code, _contract_name) do
# todo: _contract_name is for parsing of actually used constructor for concrete contract
require_contents = find_all_requires(contract_source_code)

messages_list =
Enum.reduce(require_contents, [], fn require_content, msgs ->
msg = get_require_message_hex(require_content)
if msg, do: [msg | msgs]
if msg, do: [msg | msgs], else: msgs
end)

if messages_list, do: messages_list, else: []
Expand Down Expand Up @@ -165,6 +172,20 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
end
end

def find_all_requires(contract_source_code) do
if contract_source_code do
[_ | requires] = String.split(contract_source_code, "require")

Enum.reduce(requires, [], fn right_from_require, requires_list ->
[_ | [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
case String.split(require_content, ",", parts: 2) do
[_ | [msg]] ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
|> insert(created_contract_address_hash: address.hash, input: input_data)
|> with_block()

assert ConstructorArguments.verify(address.hash, "", constructor_arguments, "")
assert ConstructorArguments.verify(address.hash, "", constructor_arguments, "", "")
end

test "verifies with multiple nested constructor arguments" do
Expand All @@ -43,7 +43,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
|> insert(created_contract_address_hash: address.hash, input: input_data)
|> with_block()

assert ConstructorArguments.verify(address.hash, "", constructor_arguments, "")
assert ConstructorArguments.verify(address.hash, "", constructor_arguments, "", "")
end

test "verifies older version of Solidity where constructor_arguments were directly appended to source code" do
Expand All @@ -64,7 +64,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
|> insert(created_contract_address_hash: address.hash, input: input_data)
|> with_block()

assert ConstructorArguments.verify(address.hash, source_code, constructor_arguments, "")
assert ConstructorArguments.verify(address.hash, source_code, constructor_arguments, "", "")
end

test "verifies with require messages" do
Expand Down Expand Up @@ -346,7 +346,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
|> insert(created_contract_address_hash: address.hash, input: input_data)
|> with_block()

assert ConstructorArguments.verify(address.hash, input, constructor_arguments, source_code)
assert ConstructorArguments.verify(address.hash, input, constructor_arguments, source_code, "ValidatorProxy")
end

test "get require messages from constructor" do
Expand Down Expand Up @@ -377,7 +377,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
}
"""

result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code)
result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code, "HomeBridge")

assert result == [
"70726f7879206d757374206e6f7420626520746865207a65726f206164647265737321",
Expand Down Expand Up @@ -406,7 +406,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
}
"""

result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code)
result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code, "HomeBridge")

assert result == [
"5f76616c696461746f7273526571756972656450657263656e74206d757374206265206265747765656e203020616e6420313030"
Expand Down Expand Up @@ -434,7 +434,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
}
"""

result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code)
result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code, "HomeBridge")

assert result == [
"5f76616c22696461746f727352657127756972656450657263656e74206d7573742062652027206265747765656e2022203020616e6420313030"
Expand All @@ -461,7 +461,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
}
"""

result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code)
result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code, "HomeBridge")

assert result == []
end
Expand All @@ -480,7 +480,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
}
"""

result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code)
result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code, "HomeBridge")

assert result == []
end
Expand All @@ -497,7 +497,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
}
"""

result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code)
result = ConstructorArguments.extract_require_messages_from_constructor(contract_source_code, "HomeBridge")

assert result == []
end
Expand Down Expand Up @@ -536,7 +536,8 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
result =
ConstructorArguments.remove_require_messages_from_constructor_arguments(
contract_source_code,
dirty_constructor_arguments
dirty_constructor_arguments,
"HomeBridge"
)

assert result ==
Expand Down Expand Up @@ -571,7 +572,8 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
result =
ConstructorArguments.remove_require_messages_from_constructor_arguments(
contract_source_code,
dirty_constructor_arguments
dirty_constructor_arguments,
"HomeBridge"
)

# Arg [0] (string) : 70726f7879206d757374206e6f7420626520746865207a65726f206164647265737321
Expand Down Expand Up @@ -614,7 +616,8 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
result =
ConstructorArguments.remove_require_messages_from_constructor_arguments(
contract_source_code,
dirty_constructor_arguments
dirty_constructor_arguments,
"HomeBridge"
)

assert result ==
Expand Down

0 comments on commit 2b7fc92

Please sign in to comment.