diff --git a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex index d38c04a28d..34868a7592 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex @@ -2,7 +2,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do @moduledoc """ Smart contract contrstructor arguments verification logic. """ - + alias ABI.{FunctionSelector, TypeDecoder} alias Explorer.Chain def verify(address_hash, contract_code, arguments_data) do @@ -15,7 +15,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do check_func = fn assumed_arguments -> assumed_arguments == arguments_data end - if verify_older_version(creation_code, contract_code, arguments_data) do + if verify_older_version(creation_code, contract_code, check_func) do true else extract_constructor_arguments(creation_code, check_func) @@ -24,11 +24,11 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do # Earlier versions of Solidity didn't have whisper code. # constructor argument were directly appended to source code - defp verify_older_version(creation_code, contract_code, arguments_data) do + defp verify_older_version(creation_code, contract_code, check_func) do creation_code |> String.split(contract_code) |> List.last() - |> Kernel.==(arguments_data) + |> check_func.() end defp extract_constructor_arguments(code, check_func) do @@ -74,18 +74,30 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do end end - # def find_contructor_arguments(address_hash, contract_code, abi) do - # arguments_data = arguments_data |> String.trim_trailing() |> String.trim_leading("0x") + def find_contructor_arguments(address_hash, contract_code, abi) do + creation_code = + address_hash + |> Chain.contract_creation_input_data() + |> String.replace("0x", "") + + constructor_abi = Enum.find(abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end) - # creation_code = - # address_hash - # |> Chain.contract_creation_input_data() - # |> String.replace("0x", "") + input_types = Enum.map(constructor_abi["inputs"], &FunctionSelector.parse_specification_type/1) - # if verify_older_version(creation_code, contract_code, arguments_data) do - # true - # else - # extract_constructor_arguments(creation_code, arguments_data) - # end - # end + check_func = fn assumed_arguments -> + try do + _ = + assumed_arguments + |> Base.decode16!(case: :mixed) + |> TypeDecoder.decode_raw(input_types) + + assumed_arguments + rescue + _ -> false + end + end + + verify_older_version(creation_code, contract_code, check_func) || + extract_constructor_arguments(creation_code, check_func) + end end diff --git a/apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs b/apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs index 9c4f6c8f68..dbf81da78e 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs @@ -6,63 +6,86 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do alias Explorer.Chain.Data alias Explorer.SmartContract.Verifier.ConstructorArguments - test "veriies constructor constructor arguments with whisper data" do - constructor_arguments = Base.encode16(:crypto.strong_rand_bytes(64), case: :lower) - address = insert(:address) + describe "verify/3" do + test "veriies constructor constructor arguments with whisper data" do + constructor_arguments = Base.encode16(:crypto.strong_rand_bytes(64), case: :lower) + address = insert(:address) - input = - "a165627a7a72305820" <> - Base.encode16(:crypto.strong_rand_bytes(32), case: :lower) <> "0029" <> constructor_arguments + input = + "a165627a7a72305820" <> + Base.encode16(:crypto.strong_rand_bytes(32), case: :lower) <> "0029" <> constructor_arguments - input_data = %Data{ - bytes: Base.decode16!(input, case: :lower) - } + input_data = %Data{ + bytes: Base.decode16!(input, case: :lower) + } - :transaction - |> insert(created_contract_address_hash: address.hash, input: input_data) - |> with_block() + :transaction + |> insert(created_contract_address_hash: address.hash, input: input_data) + |> with_block() - assert ConstructorArguments.verify(address.hash, "", constructor_arguments) - end + assert ConstructorArguments.verify(address.hash, "", constructor_arguments) + end - test "verifies with multiple nested constructor arguments" do - address = insert(:address) + test "verifies with multiple nested constructor arguments" do + address = insert(:address) - constructor_arguments = - "000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" + constructor_arguments = + "000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" - input = - "a165627a7a72305820fbfa6f8a2024760ef0e0eb29a332c9a820526e92f8b4fbcce6f00c7643234b1400297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a723058203c2db82e7c80cd1e371fe349b03d49b812c324ba4a3fcd063b7bc2662353c5de0029000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" + input = + "a165627a7a72305820fbfa6f8a2024760ef0e0eb29a332c9a820526e92f8b4fbcce6f00c7643234b1400297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a723058203c2db82e7c80cd1e371fe349b03d49b812c324ba4a3fcd063b7bc2662353c5de0029000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" - input_data = %Data{ - bytes: Base.decode16!(input, case: :lower) - } + input_data = %Data{ + bytes: Base.decode16!(input, case: :lower) + } - :transaction - |> insert(created_contract_address_hash: address.hash, input: input_data) - |> with_block() + :transaction + |> insert(created_contract_address_hash: address.hash, input: input_data) + |> with_block() - assert ConstructorArguments.verify(address.hash, "", constructor_arguments) - end + assert ConstructorArguments.verify(address.hash, "", constructor_arguments) + end + + test "verifies older version of Solidity where constructor_arguments were directly appended to source code" do + address = insert(:address) + + constructor_arguments = + "000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" - test "verifies older version of Solidity where constructor_arguments were directly appended to source code" do - address = insert(:address) + source_code = "0001" - constructor_arguments = - "000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" + input = source_code <> constructor_arguments + + input_data = %Data{ + bytes: Base.decode16!(input, case: :lower) + } + + :transaction + |> insert(created_contract_address_hash: address.hash, input: input_data) + |> with_block() + + assert ConstructorArguments.verify(address.hash, source_code, constructor_arguments) + end + end - source_code = "0001" + describe "find_contructor_arguments/3" do + test "finds contructor arguments" do + address = insert(:address) + expected_contructor_arguments = + "000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" - input = source_code <> constructor_arguments + input = + "a165627a7a72305820fbfa6f8a2024760ef0e0eb29a332c9a820526e92f8b4fbcce6f00c7643234b1400297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a723058203c2db82e7c80cd1e371fe349b03d49b812c324ba4a3fcd063b7bc2662353c5de0029000000000000000000000000314159265dd8dbb310642f98f50c066173c1259b93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae00000000000000000000000000000000000000000000000000000000590b09b0" - input_data = %Data{ - bytes: Base.decode16!(input, case: :lower) - } + input_data = %Data{ + bytes: Base.decode16!(input, case: :lower) + } - :transaction - |> insert(created_contract_address_hash: address.hash, input: input_data) - |> with_block() + :transaction + |> insert(created_contract_address_hash: address.hash, input: input_data) + |> with_block() - assert ConstructorArguments.verify(address.hash, source_code, constructor_arguments) + assert ConstructorArguments.find_contructor_arguments(address.hash, "", %{}) + end end end