From 44430af6f961f379ef09b0c4eae95a9aac882e8a Mon Sep 17 00:00:00 2001 From: Igor Florian Date: Wed, 13 Jun 2018 14:51:21 -0300 Subject: [PATCH] Support multiple compile versions This will allow that the versions selected in the page have real impact on how the Contract will be verified. --- .../smart_contract/solidity/code_compiler.ex | 122 ++++++++++------- .../solidity/compiler_version.ex | 8 +- .../lib/explorer/smart_contract/verifier.ex | 14 +- apps/explorer/priv/compile_solc.js | 3 +- .../smart_contract/publisher_test.exs | 40 ++---- .../solidity/code_compiler_test.exs | 123 ++++++++++++++---- .../solidity/compiler_version_test.exs | 2 +- .../explorer/smart_contract/verifier_test.exs | 85 +++++------- apps/explorer/test/support/factory.ex | 32 +++++ .../address_contract_verification_test.exs | 47 ++----- 10 files changed, 272 insertions(+), 204 deletions(-) diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex index 9c190b669c..4b282e9d70 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex @@ -3,14 +3,36 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do Module responsible to compile the Solidity code of a given Smart Contract. """ - @doc ~S""" + @doc """ Compiles a code in the solidity command line. Returns a `Map`. ## Examples - iex(1)> Explorer.SmartContract.Solidity.CodeCompiler.run("SimpleStorage", "pragma solidity ^0.4.23; contract SimpleStorage {uint storedData; function set(uint x) public {storedData = x; } function get() public constant returns (uint) {return storedData; } }", false) - {:ok, %{ + + iex(1)> Explorer.SmartContract.Solidity.CodeCompiler.run( + ...> "SimpleStorage", + ...> "v0.4.24+commit.e67f0147", + ...> \""" + ...> pragma solidity ^0.4.24; + ...> + ...> contract SimpleStorage { + ...> uint storedData; + ...> + ...> function set(uint x) public { + ...> storedData = x; + ...> } + ...> + ...> function get() public constant returns (uint) { + ...> return storedData; + ...> } + ...> } + ...> \""", + ...> false + ...> ) + { + :ok, + %{ "abi" => [ %{ "constant" => false, @@ -31,66 +53,66 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do "type" => "function" } ], - "bytecode" => "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820017172d01c000255d5c74c0efce764adf7c4ae444d7f7e2ed852f6fb9b73df5d0029", - "name" => "SimpleStorage" - }} + "bytecode" => "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820235fceab083d33bf112b473c85551306a29f32dcdc7e95b4dfdd697c1db188ec0029", + "name" => "SimpleStorage", + "opcodes" => "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0xDF DUP1 PUSH2 0x1F PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x49 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x60FE47B1 EQ PUSH1 0x4E JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH1 0x78 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x76 PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0xA0 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x83 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x8A PUSH1 0xAA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0x23 0x5f 0xce 0xab ADDMOD RETURNDATASIZE CALLER 0xbf GT 0x2b 0x47 EXTCODECOPY DUP6 SSTORE SGT MOD LOG2 SWAP16 ORIGIN 0xdc 0xdc PUSH31 0x95B4DFDD697C1DB188EC002900000000000000000000000000000000000000 " + } + } """ - def run(name, code, optimization) do + def run(name, compiler_version, code, optimize) do {response, _status} = System.cmd( "node", [ Application.app_dir(:explorer, "priv/compile_solc.js"), - generate_settings(name, code, optimization), - "v0.4.24+commit.e67f0147" + code, + compiler_version, + optimize_value(optimize) ] ) - case Jason.decode!(response) do - %{ - "contracts" => %{ - ^name => %{ - ^name => %{ - "abi" => abi, - "evm" => %{ - "bytecode" => %{"object" => bytecode} - } - } - } + with %{"contracts" => contracts} <- Jason.decode!(response), + %{"interface" => abi, "runtimeBytecode" => bytecode, "opcodes" => opcodes} <- + get_contract_info(contracts, name) do + { + :ok, + %{ + "abi" => Jason.decode!(abi), + "bytecode" => bytecode, + "name" => name, + "opcodes" => opcodes } - } -> - {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}} + } + else + error -> + parse_error(error) + end + end + + def get_contract_info(contracts, _) when contracts == %{}, do: %{"errors" => []} + + def get_contract_info(contracts, name) do + new_versions_name = ":" <> name + + case contracts do + %{^new_versions_name => response} -> + response + + %{^name => response} -> + response _ -> - {:error, :compilation} + {:error, :name} end end - @doc """ - For more output options check the documentation. - https://solidity.readthedocs.io/en/v0.4.24/using-the-compiler.html#compiler-input-and-output-json-description - """ - def generate_settings(name, code, optimization) do - """ - { - "language": "Solidity", - "sources": { - "#{name}": - { - "content": "#{code}" - } - }, - "settings": { - "optimizer": { - "enabled": #{optimization} - }, - "outputSelection": { - "*": { - "*": [ "evm.bytecode", "evm.deployedBytecode", "evm.gasEstimates", "abi", "metadata" ] - } - } - } - } - """ - end + def parse_error({:error, :name} = error), do: error + def parse_error(%{"error" => error}), do: {:error, [error]} + def parse_error(%{"errors" => errors}), do: {:error, errors} + + defp optimize_value(false), do: "0" + defp optimize_value("false"), do: "0" + + defp optimize_value(true), do: "1" + defp optimize_value("true"), do: "1" end 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 20c404785a..43d402c567 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex @@ -30,11 +30,17 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersion do releases |> Map.to_list() - |> Enum.map(fn {key, _value} -> {key, key} end) + |> Enum.map(fn {key, value} -> {key, extract_version(value)} end) |> Enum.sort() |> Enum.reverse() end + defp extract_version(version) do + version + |> String.replace_prefix("soljson-", "") + |> String.replace_suffix(".js", "") + end + defp decode_json(json) do Jason.decode!(json) end diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index 3a6fc6b6d8..de5d8b802c 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -11,18 +11,24 @@ defmodule Explorer.SmartContract.Verifier do alias Explorer.SmartContract.Solidity.CodeCompiler def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name} - def evaluate_authenticity(_, %{"contract_source_code" => ""}), do: {:error, :contract_source_code} + + def evaluate_authenticity(_, %{"contract_source_code" => ""}), + do: {:error, :contract_source_code} def evaluate_authenticity(address_hash, %{ "name" => name, "contract_source_code" => contract_source_code, - "optimization" => optimization + "optimization" => optimization, + "compiler" => compiler_version }) do - solc_output = CodeCompiler.run(name, contract_source_code, optimization) + + solc_output = CodeCompiler.run(name, compiler_version, contract_source_code, optimization) + compare_bytecodes(solc_output, address_hash) end - defp compare_bytecodes(compilation_error = {:error, _}, _), do: compilation_error + defp compare_bytecodes({:error, :name}, _), do: {:error, :name} + defp compare_bytecodes({:error, _}, _), do: {:error, :compilation} defp compare_bytecodes({:ok, %{"abi" => abi, "bytecode" => bytecode}}, address_hash) do generated_bytecode = extract_bytecode(bytecode) diff --git a/apps/explorer/priv/compile_solc.js b/apps/explorer/priv/compile_solc.js index c4b336d990..55c62194e9 100755 --- a/apps/explorer/priv/compile_solc.js +++ b/apps/explorer/priv/compile_solc.js @@ -4,11 +4,12 @@ const solc = require('solc-js'); var sourceCode = process.argv[2]; var version = process.argv[3]; +var optimize = process.argv[4]; var compiled_code = solc.loadRemoteVersion(version, function (err, solcSnapshot) { if (err) { console.log(JSON.stringify(err)); } else { - console.log(solcSnapshot.compileStandardWrapper(sourceCode)); + console.log(JSON.stringify(solcSnapshot.compile(sourceCode, optimize))); } }); diff --git a/apps/explorer/test/explorer/smart_contract/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/publisher_test.exs index b81bfd1ccd..83be93bc0c 100644 --- a/apps/explorer/test/explorer/smart_contract/publisher_test.exs +++ b/apps/explorer/test/explorer/smart_contract/publisher_test.exs @@ -5,43 +5,29 @@ defmodule Explorer.SmartContract.PublisherTest do doctest Explorer.SmartContract.Publisher - alias Explorer.Chain.{SmartContract, Hash} + alias Explorer.Chain.SmartContract alias Explorer.SmartContract.Publisher + alias Explorer.Factory describe "publish/2" do test "with valid data creates a smart_contract" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + contract_code_info = Factory.contract_code_info() - smart_contract_bytecode = - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() - - insert( - :internal_transaction_create, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: smart_contract_bytecode - ) + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) valid_attrs = %{ - "contract_source_code" => - "pragma solidity ^0.4.23;\r\n\r\ncontract SimpleStorage {\r\n uint storedData;\r\n\r\n function set(uint x) public {\r\n storedData = x;\r\n }\r\n\r\n function get() public constant returns (uint) {\r\n return storedData;\r\n }\r\n}", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:ok, %SmartContract{} = smart_contract} = Publisher.publish(address_hash, valid_attrs) + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = smart_contract} = response + + assert smart_contract.address_hash == contract_address.hash assert smart_contract.name == valid_attrs["name"] - assert Hash.to_string(smart_contract.address_hash) == address_hash - assert smart_contract.compiler_version == valid_attrs["compiler"] + assert smart_contract.compiler_version == valid_attrs["compiler_version"] assert smart_contract.optimization == valid_attrs["optimization"] assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] assert smart_contract.abi != nil diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 9d759351e2..9bd7862f47 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -4,15 +4,56 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do doctest Explorer.SmartContract.Solidity.CodeCompiler alias Explorer.SmartContract.Solidity.CodeCompiler + alias Explorer.Factory describe "run/2" do - test "compiles a smart contract using the solidity command line" do + setup do + {:ok, contract_code_info: Factory.contract_code_info()} + end + + test "compiles the latest solidity version", %{contract_code_info: contract_code_info} do + response = + CodeCompiler.run( + contract_code_info.name, + contract_code_info.version, + contract_code_info.source_code, + contract_code_info.optimized + ) + + assert {:ok, + %{ + "abi" => _, + "bytecode" => _, + "name" => _, + "opcodes" => _ + }} = response + end + + test "compiles a optimized smart contract", %{contract_code_info: contract_code_info} do + optimize = true + + response = + CodeCompiler.run( + contract_code_info.name, + contract_code_info.version, + contract_code_info.source_code, + optimize + ) + + assert {:ok, + %{ + "abi" => _, + "bytecode" => _, + "name" => _, + "opcodes" => _ + }} = response + end + + test "compile in an older solidity version" do + optimize = false name = "SimpleStorage" - optimization = false code = """ - pragma solidity ^0.4.24; - contract SimpleStorage { uint storedData; @@ -26,42 +67,74 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do } """ - response = CodeCompiler.run(name, code, optimization) + version = "v0.1.3-nightly.2015.9.25+commit.4457170" + + response = CodeCompiler.run(name, version, code, optimize) assert {:ok, %{ "abi" => _, "bytecode" => _, - "name" => _ + "name" => _, + "opcodes" => _ }} = response end + + test "returns a list of errors the compilation isn't possible", %{ + contract_code_info: contract_code_info + } do + wrong_code = "pragma solidity ^0.4.24; cont SimpleStorage { " + + response = + CodeCompiler.run( + contract_code_info.name, + contract_code_info.version, + wrong_code, + contract_code_info.optimized + ) + + assert {:error, errors} = response + assert is_list(errors) + end end - describe "generate_settings/2" do - test "creates a json file with the solidity compiler expected settings" do - name = "SimpleStorage" - optimization = false + describe "get_contract_info/1" do + test "return name error when the Contract name doesn't match" do + name = "Name" + different_name = "diff_name" - code = """ - pragma solidity ^0.4.24; + response = CodeCompiler.get_contract_info(%{name => %{}}, different_name) - contract SimpleStorage { - uint storedData; + assert {:error, :name} == response + end - function set(uint x) public { - storedData = x; - } + test "returns an empty list of errors for empty info" do + name = "Name" - function get() public constant returns (uint) { - return storedData; - } - } - """ + response = CodeCompiler.get_contract_info(%{}, name) + + assert %{"errors" => []} == response + end + + test "the contract info is returned when the name matches" do + contract_inner_info = %{"abi" => %{}, "bytecode" => "", "opcodes" => ""} + name = "Name" + contract_info = %{name => contract_inner_info} + + response = CodeCompiler.get_contract_info(contract_info, name) + + assert contract_inner_info == response + end + + test "the contract info is returned when the name matches with a `:` sufix" do + name = "Name" + name_with_sufix = ":Name" + contract_inner_info = %{"abi" => %{}, "bytecode" => "", "opcodes" => ""} + contract_info = %{name_with_sufix => contract_inner_info} - generated = CodeCompiler.generate_settings(name, code, optimization) + response = CodeCompiler.get_contract_info(contract_info, name) - assert String.contains?(generated, "contract SimpleStorage") == true - assert String.contains?(generated, "settings") == true + assert contract_inner_info == response end end end diff --git a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs index 5e28f7aa6e..0ceb1dc61b 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs @@ -24,7 +24,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do end) assert {:ok, versions} = CompilerVersion.fetch_versions() - assert Enum.any?(versions, fn item -> item == {"0.4.9", "0.4.9"} end) == true + assert Enum.any?(versions, fn item -> item == {"0.4.9", "v0.4.9+commit.364da425"} end) == true end test "returns error when list of versions is not available", %{bypass: bypass} do diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index 1c7066d7fc..bb5d2ba5c5 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -5,84 +5,57 @@ defmodule Explorer.SmartContract.VerifierTest do doctest Explorer.SmartContract.Verifier alias Explorer.SmartContract.Verifier + alias Explorer.Factory describe "evaluate_authenticity/2" do - test "verifies the generated bytecode against bytecode retrieved from the blockchain" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" - - smart_contract_bytecode = - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() + setup do + {:ok, contract_code_info: Factory.contract_code_info()} + end - insert( - :internal_transaction_create, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: smart_contract_bytecode - ) + test "verifies the generated bytecode against bytecode retrieved from the blockchain", %{ + contract_code_info: contract_code_info + } do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) params = %{ - "contract_source_code" => - "pragma solidity ^0.4.24; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public constant returns (uint) { return storedData; } }", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(address_hash, params) + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) assert abi != nil end - test "returns error when bytecoed doesn't match" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - wrong_smart_contract_bytecode = - "0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058207722b6ddfe522b31e50b878ced2f22d051e8e2cd19be7b4fba9686602b90ba2b0029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: wrong_smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() - - insert( - :internal_transaction_create, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: wrong_smart_contract_bytecode - ) + different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}" params = %{ - "contract_source_code" => - "pragma solidity ^0.4.24; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public constant returns (uint) { return storedData; } }", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "contract_source_code" => different_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:error, :generated_bytecode} = Verifier.evaluate_authenticity(address_hash, params) + response = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert {:error, :generated_bytecode} = response end - test "returns error when there is a compilation problem" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + test "returns error when there is a compilation problem", %{contract_code_info: contract_code_info} do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) params = %{ "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { ", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:error, :compilation} = Verifier.evaluate_authenticity(address_hash, params) + assert {:error, :compilation} = Verifier.evaluate_authenticity(contract_address.hash, params) end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 0972f87db1..a5b20cb046 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -29,6 +29,38 @@ defmodule Explorer.Factory do } end + def contract_address_factory do + %Address{ + hash: address_hash(), + contract_code: Map.fetch!(contract_code_info(), :bytecode) + } + end + + def contract_code_info do + %{ + bytecode: + "0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820f65a3adc1cfb055013d1dc37d0fe98676e2a5963677fa7541a10386d163446680029", + name: "SimpleStorage", + source_code: """ + pragma solidity ^0.4.24; + + contract SimpleStorage { + uint storedData; + + function set(uint x) public { + storedData = x; + } + + function get() public constant returns (uint) { + return storedData; + } + } + """, + version: "v0.4.24+commit.e67f0147", + optimized: false + } + end + def address_hash do {:ok, address_hash} = "address_hash" diff --git a/apps/explorer_web/test/explorer_web/features/address_contract_verification_test.exs b/apps/explorer_web/test/explorer_web/features/address_contract_verification_test.exs index 4102913b98..e859112c02 100644 --- a/apps/explorer_web/test/explorer_web/features/address_contract_verification_test.exs +++ b/apps/explorer_web/test/explorer_web/features/address_contract_verification_test.exs @@ -4,6 +4,7 @@ defmodule ExplorerWeb.AddressContractVerificationTest do import Wallaby.Query alias Plug.Conn + alias Explorer.Factory setup do bypass = Bypass.open() @@ -16,51 +17,19 @@ defmodule ExplorerWeb.AddressContractVerificationTest do test "users validates smart contract", %{session: session, bypass: bypass} do Bypass.expect(bypass, fn conn -> Conn.resp(conn, 200, solc_bin_versions()) end) - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + %{name: name, source_code: source_code, bytecode: bytecode, version: version} = Factory.contract_code_info() - smart_contract_bytecode = - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() - - insert( - :internal_transaction, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: smart_contract_bytecode - ) - - code = """ - pragma solidity ^0.4.24; - - contract SimpleStorage { - uint storedData; - - function set(uint x) public { - storedData = x; - } - - function get() public constant returns (uint) { - return storedData; - } - } - """ + contract_address = insert(:contract_address, contract_code: bytecode) session - |> visit("/en/addresses/#{address_hash}/contract_verifications/new") - |> fill_in(text_field("Contract Name"), with: "SimpleStorage") - |> click(option("0.4.24")) + |> visit("/en/addresses/#{contract_address.hash}/contract_verifications/new") + |> fill_in(text_field("Contract Name"), with: name) + |> click(option(version)) |> click(radio_button("No")) - |> fill_in(text_field("Enter the Solidity Contract Code below"), with: code) + |> fill_in(text_field("Enter the Solidity Contract Code below"), with: source_code) |> click(button("Verify and publish")) - assert current_path(session) =~ ~r/\/en\/addresses\/#{address_hash}\/contracts/ + assert current_path(session) =~ ~r/\/en\/addresses\/#{contract_address.hash}\/contracts/ end test "with invalid data shows error messages", %{session: session, bypass: bypass} do