diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c748863ff..a046a1a134 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,18 @@
### Features
- - [1654](https://github.com/poanetwork/blockscout/pull/1654) - add decompiled code tab
- - [1661](https://github.com/poanetwork/blockscout/pull/1661) - try to compile smart contract with the latest evm version
+ - [#1662](https://github.com/poanetwork/blockscout/pull/1662) - allow specifying number of optimization runs
+ - [#1654](https://github.com/poanetwork/blockscout/pull/1654) - add decompiled code tab
+ - [#1661](https://github.com/poanetwork/blockscout/pull/1661) - try to compile smart contract with the latest evm version
+ - [#1665](https://github.com/poanetwork/blockscout/pull/1665) - Add contract verification RPC endpoint.
### Fixes
- [#1669](https://github.com/poanetwork/blockscout/pull/1669) - do not fail if multiple matching tokens are found
+ - [#1691](https://github.com/poanetwork/blockscout/pull/1691) - decrease token metadata update interval
+ - [#1688](https://github.com/poanetwork/blockscout/pull/1688) - do not fail if failure reason is atom
+ - [#1692](https://github.com/poanetwork/blockscout/pull/1692) - exclude decompiled smart contract from encoding
+ - [#1684](https://github.com/poanetwork/blockscout/pull/1684) - Discard child block with parent_hash not matching hash of imported block
### Chore
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
index 75cda8c965..8f35dcaa86 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
@@ -26,10 +26,14 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
"address_id" => address_hash_string,
"smart_contract" => smart_contract,
"external_libraries" => external_libraries,
- "evm_version" => evm_version
+ "evm_version" => evm_version,
+ "optimization" => optimization
}
) do
- smart_sontact_with_evm_version = Map.put(smart_contract, "evm_version", evm_version["evm_version"])
+ smart_sontact_with_evm_version =
+ smart_contract
+ |> Map.put("evm_version", evm_version["evm_version"])
+ |> Map.put("optimization_runs", parse_optimization_runs(optimization))
case Publisher.publish(address_hash_string, smart_sontact_with_evm_version, external_libraries) do
{:ok, _smart_contract} ->
@@ -45,4 +49,11 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
)
end
end
+
+ def parse_optimization_runs(%{"runs" => runs}) do
+ case Integer.parse(runs) do
+ {integer, ""} -> integer
+ _ -> 200
+ end
+ end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
index 8a8036f2b4..146626cce5 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
@@ -4,10 +4,28 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
alias BlockScoutWeb.API.RPC.Helpers
alias Explorer.Chain
alias Explorer.Chain.SmartContract
+ alias Explorer.SmartContract.Publisher
+
+ def verify(conn, %{"addressHash" => address_hash} = params) do
+ with {:params, {:ok, fetched_params}} <- {:params, fetch_verify_params(params)},
+ {:params, external_libraries} <-
+ {:params, fetch_external_libraries(params)},
+ {:publish, {:ok, smart_contract}} <-
+ {:publish, Publisher.publish(address_hash, fetched_params, external_libraries)},
+ preloaded_smart_contract <- SmartContract.preload_decompiled_smart_contract(smart_contract) do
+ render(conn, :verify, %{contract: preloaded_smart_contract, address_hash: address_hash})
+ else
+ {:publish, _} ->
+ render(conn, :error, error: "Something went wrong while publishing the contract.")
+
+ {:params, {:error, error}} ->
+ render(conn, :error, error: error)
+ end
+ end
def listcontracts(conn, params) do
with pagination_options <- Helpers.put_pagination_options(%{}, params),
- {:params, {:ok, options}} <- {:params, add_filter(pagination_options, params)} do
+ {:params, {:ok, options}} <- {:params, add_filters(pagination_options, params)} do
options_with_defaults =
options
|> Map.put_new(:page_number, 0)
@@ -71,7 +89,8 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
Chain.list_verified_contracts(page_size, offset)
:decompiled ->
- Chain.list_decompiled_contracts(page_size, offset)
+ not_decompiled_with_version = Map.get(opts, :not_decompiled_with_version)
+ Chain.list_decompiled_contracts(page_size, offset, not_decompiled_with_version)
:unverified ->
Chain.list_unverified_contracts(page_size, offset)
@@ -84,6 +103,12 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
end
+ defp add_filters(options, params) do
+ options
+ |> add_filter(params)
+ |> add_not_decompiled_with_version(params)
+ end
+
defp add_filter(options, params) do
with {:param, {:ok, value}} <- {:param, Map.fetch(params, "filter")},
{:validation, {:ok, filter}} <- {:validation, contracts_filter(value)} do
@@ -94,6 +119,17 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
end
+ defp add_not_decompiled_with_version({:ok, options}, params) do
+ case Map.fetch(params, "not_decompiled_with_version") do
+ {:ok, value} -> {:ok, Map.put(options, :not_decompiled_with_version, value)}
+ :error -> {:ok, options}
+ end
+ end
+
+ defp add_not_decompiled_with_version(options, _params) do
+ options
+ end
+
defp contracts_filter(nil), do: {:ok, nil}
defp contracts_filter(1), do: {:ok, :verified}
defp contracts_filter(2), do: {:ok, :decompiled}
@@ -137,4 +173,71 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
{:contract, result}
end
+
+ defp fetch_verify_params(params) do
+ {:ok, %{}}
+ |> required_param(params, "addressHash", "address_hash")
+ |> required_param(params, "name", "name")
+ |> required_param(params, "compilerVersion", "compiler_version")
+ |> required_param(params, "optimization", "optimization")
+ |> required_param(params, "contractSourceCode", "contract_source_code")
+ |> optional_param(params, "evmVersion", "evm_version")
+ |> optional_param(params, "constructorArguments", "constructor_arguments")
+ |> optional_param(params, "optimizationRuns", "optimization_runs")
+ |> parse_optimization_runs()
+ end
+
+ defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_bitstring(runs) do
+ {:ok, Map.put(opts, "optimization_runs", 200)}
+ end
+
+ defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_integer(runs) do
+ {:ok, opts}
+ end
+
+ defp parse_optimization_runs({:ok, opts}) do
+ {:ok, Map.put(opts, "optimization_runs", 200)}
+ end
+
+ defp parse_optimization_runs(other), do: other
+
+ defp fetch_external_libraries(params) do
+ Enum.reduce(1..5, %{}, fn number, acc ->
+ case Map.fetch(params, "library#{number}Name") do
+ {:ok, library_name} ->
+ library_address = Map.get(params, "library#{number}Address")
+
+ acc
+ |> Map.put("library#{number}_name", library_name)
+ |> Map.put("library#{number}_address", library_address)
+
+ :error ->
+ acc
+ end
+ end)
+ end
+
+ defp required_param({:error, _} = error, _, _, _), do: error
+
+ defp required_param({:ok, map}, params, key, new_key) do
+ case Map.fetch(params, key) do
+ {:ok, value} ->
+ {:ok, Map.put(map, new_key, value)}
+
+ :error ->
+ {:error, "#{key} is required."}
+ end
+ end
+
+ defp optional_param({:error, _} = error, _, _, _), do: error
+
+ defp optional_param({:ok, map}, params, key, new_key) do
+ case Map.fetch(params, key) do
+ {:ok, value} ->
+ {:ok, Map.put(map, new_key, value)}
+
+ :error ->
+ {:ok, map}
+ end
+ end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
index 75bb51fa50..b1db31a8f3 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
@@ -66,6 +66,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
def call_controller(conn, controller, action) do
{:ok, controller.call(conn, action)}
rescue
- Conn.WrapperError -> :error
+ Conn.WrapperError ->
+ :error
end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex
index 4162a77e70..d68f0c95c7 100644
--- a/apps/block_scout_web/lib/block_scout_web/etherscan.ex
+++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex
@@ -328,6 +328,49 @@ defmodule BlockScoutWeb.Etherscan do
"result" => nil
}
+ @contract_verify_example_value %{
+ "status" => "1",
+ "message" => "OK",
+ "result" => %{
+ "SourceCode" => """
+ pragma solidity >0.4.24;
+
+ contract Test {
+ constructor() public { b = hex"12345678901234567890123456789012"; }
+ event Event(uint indexed a, bytes32 b);
+ event Event2(uint indexed a, bytes32 b);
+ function foo(uint a) public { emit Event(a, b); }
+ bytes32 b;
+ }
+ """,
+ "ABI" => """
+ [{
+ "type":"event",
+ "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
+ "name":"Event"
+ }, {
+ "type":"event",
+ "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
+ "name":"Event2"
+ }, {
+ "type":"function",
+ "inputs": [{"name":"a","type":"uint256"}],
+ "name":"foo",
+ "outputs": []
+ }]
+ """,
+ "ContractName" => "Test",
+ "CompilerVersion" => "v0.2.1-2016-01-30-91a6b35",
+ "OptimizationUsed" => "1"
+ }
+ }
+
+ @contract_verify_example_value_error %{
+ "status" => "0",
+ "message" => "There was an error verifying the contract.",
+ "result" => nil
+ }
+
@contract_getsourcecode_example_value %{
"status" => "1",
"message" => "OK",
@@ -1719,6 +1762,12 @@ defmodule BlockScoutWeb.Etherscan do
type: "string",
description:
"verified|decompiled|unverified|not_decompiled, or 1|2|3|4 respectively. This requests only contracts with that status."
+ },
+ %{
+ key: "not_decompiled_with_version",
+ type: "string",
+ description:
+ "Ensures that none of the returned contracts were decompiled with the provided version. Ignored unless filtering for decompiled contracts."
}
],
responses: [
@@ -1741,6 +1790,126 @@ defmodule BlockScoutWeb.Etherscan do
]
}
+ @contract_verify_action %{
+ name: "verify",
+ description: "Verify a contract with its source code and contract creation information.",
+ required_params: [
+ %{
+ key: "addressHash",
+ placeholder: "addressHash",
+ type: "string",
+ description: "The address of the contract."
+ },
+ %{
+ key: "name",
+ placeholder: "name",
+ type: "string",
+ description: "The name of the contract."
+ },
+ %{
+ key: "compilerVersion",
+ placeholder: "compilerVersion",
+ type: "string",
+ description: "The compiler version for the contract."
+ },
+ %{
+ key: "optimization",
+ placeholder: false,
+ type: "boolean",
+ description: "Whether or not compiler optimizations were enabled."
+ },
+ %{
+ key: "contractSourceCode",
+ placeholder: "contractSourceCode",
+ type: "string",
+ description: "The source code of the contract."
+ }
+ ],
+ optional_params: [
+ %{
+ key: "constructorArguments",
+ type: "string",
+ description: "The constructor argument data provided."
+ },
+ %{
+ key: "evmVersion",
+ placeholder: "evmVersion",
+ type: "string",
+ description: "The EVM version for the contract."
+ },
+ %{
+ key: "optimizationRuns",
+ placeholder: "optimizationRuns",
+ type: "integer",
+ description: "The number of optimization runs used during compilation"
+ },
+ %{
+ key: "library1Name",
+ type: "string",
+ description: "The name of the first library used."
+ },
+ %{
+ key: "library1Address",
+ type: "string",
+ description: "The address of the first library used."
+ },
+ %{
+ key: "library2Name",
+ type: "string",
+ description: "The name of the second library used."
+ },
+ %{
+ key: "library2Address",
+ type: "string",
+ description: "The address of the second library used."
+ },
+ %{
+ key: "library3Name",
+ type: "string",
+ description: "The name of the third library used."
+ },
+ %{
+ key: "library3Address",
+ type: "string",
+ description: "The address of the third library used."
+ },
+ %{
+ key: "library4Name",
+ type: "string",
+ description: "The name of the fourth library used."
+ },
+ %{
+ key: "library4Address",
+ type: "string",
+ description: "The address of the fourth library used."
+ },
+ %{
+ key: "library5Name",
+ type: "string",
+ description: "The name of the fourth library used."
+ },
+ %{
+ key: "library5Address",
+ type: "string",
+ description: "The address of the fourth library used."
+ }
+ ],
+ responses: [
+ %{
+ code: "200",
+ description: "successful operation",
+ example_value: Jason.encode!(@contract_verify_example_value),
+ type: "model",
+ model: @contract_model
+ },
+ %{
+ code: "200",
+ description: "error",
+ example_value: Jason.encode!(@contract_verify_example_value_error)
+ }
+ ]
+ }
+
@contract_getabi_action %{
name: "getabi",
description: "Get ABI for verified contract. Also available through a GraphQL 'addresses' query.",
@@ -1970,7 +2139,8 @@ defmodule BlockScoutWeb.Etherscan do
actions: [
@contract_listcontracts_action,
@contract_getabi_action,
- @contract_getsourcecode_action
+ @contract_getsourcecode_action,
+ @contract_verify_action
]
}
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
index ff0aec3423..e2969905cc 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
@@ -48,6 +48,11 @@
<%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger" %>
+
+ <%= label f, :name, gettext("Optimization runs") %>
+ <%= text_input :optimization, :runs, value: 200, class: "form-control", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %>
+
+
<%= label f, :contract_source_code, gettext("Enter the Solidity Contract Code below") %>
<%= textarea f, :contract_source_code, class: "form-control monospace", rows: 3, "aria-describedby": "contract-source-code-help-block" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
index fbe1813dce..27e049dfc3 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
@@ -22,6 +22,10 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
RPCView.render("error.json", assigns)
end
+ def render("verify.json", %{contract: contract, address_hash: address_hash}) do
+ RPCView.render("show.json", data: prepare_source_code_contract(contract, address_hash))
+ end
+
defp prepare_source_code_contract(nil, address_hash) do
%{
"Address" => to_string(address_hash),
@@ -36,19 +40,27 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
end
defp prepare_source_code_contract(contract, _) do
+ decompiled_smart_contract = latest_decompiled_smart_contract(contract.decompiled_smart_contracts)
+
%{
"Address" => to_string(contract.address_hash),
"SourceCode" => contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
- "DecompiledSourceCode" => decompiled_source_code(contract.decompiled_smart_contract),
- "DecompilerVersion" => decompiler_version(contract.decompiled_smart_contract),
+ "DecompiledSourceCode" => decompiled_source_code(decompiled_smart_contract),
+ "DecompilerVersion" => decompiler_version(decompiled_smart_contract),
"CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => if(contract.optimization, do: "1", else: "0")
}
end
- defp prepare_contract(%Address{hash: hash, smart_contract: nil, decompiled_smart_contract: decompiled_smart_contract}) do
+ defp prepare_contract(%Address{
+ hash: hash,
+ smart_contract: nil,
+ decompiled_smart_contracts: decompiled_smart_contracts
+ }) do
+ decompiled_smart_contract = latest_decompiled_smart_contract(decompiled_smart_contracts)
+
%{
"Address" => to_string(hash),
"SourceCode" => "",
@@ -64,8 +76,10 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
defp prepare_contract(%Address{
hash: hash,
smart_contract: %SmartContract{} = contract,
- decompiled_smart_contract: decompiled_smart_contract
+ decompiled_smart_contracts: decompiled_smart_contracts
}) do
+ decompiled_smart_contract = latest_decompiled_smart_contract(decompiled_smart_contracts)
+
%{
"Address" => to_string(hash),
"SourceCode" => contract.contract_source_code,
@@ -78,6 +92,12 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
}
end
+ defp latest_decompiled_smart_contract([]), do: nil
+
+ defp latest_decompiled_smart_contract(contracts) do
+ Enum.max_by(contracts, fn contract -> DateTime.to_unix(contract.inserted_at) end)
+ end
+
defp decompiled_source_code(nil), do: "Contract source code not decompiled."
defp decompiled_source_code(%DecompiledSmartContract{decompiled_source_code: decompiled_source_code}) do
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index 79c8a939f8..28d3db09d8 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -197,7 +197,7 @@ msgid "Blocks Validated"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:130
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:135
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:24
msgid "Cancel"
msgstr ""
@@ -367,7 +367,7 @@ msgid "ETH"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:52
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:57
msgid "Enter the Solidity Contract Code below"
msgstr ""
@@ -713,7 +713,7 @@ msgid "Request URL"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:128
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:133
msgid "Reset"
msgstr ""
@@ -1024,7 +1024,7 @@ msgid "Verify & Publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:127
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:132
msgid "Verify & publish"
msgstr ""
@@ -1165,7 +1165,7 @@ msgid "Loading..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:125
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:130
msgid "Loading...."
msgstr ""
@@ -1582,57 +1582,57 @@ msgid "Genesis Block"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:71
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:76
msgid "1 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:66
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:71
msgid "1 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:86
msgid "2 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:76
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
msgid "2 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:96
msgid "3 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:86
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
msgid "3 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:101
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:106
msgid "4 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:96
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:101
msgid "4 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:111
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:116
msgid "5 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:106
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:111
msgid "5 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:63
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:68
msgid "Contract Libraries"
msgstr ""
@@ -1698,16 +1698,10 @@ msgid "EVM Version"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:58
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:63
msgid "Enter constructor arguments if the contract had any"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address/_tabs.html.eex:67
-#: lib/block_scout_web/templates/address/_tabs.html.eex:133
-msgid "Decompiled code"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:20
msgid "Copy Decompiled Contract Code"
@@ -1718,6 +1712,12 @@ msgstr ""
msgid "Decompiled Code"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/_tabs.html.eex:67
+#: lib/block_scout_web/templates/address/_tabs.html.eex:133
+msgid "Decompiled code"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:18
msgid "Decompiled contract code"
@@ -1727,3 +1727,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:11
msgid "Decompiler version"
msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:52
+msgid "Optimization runs"
+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 aaa75f2b34..6dbdb2d676 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
@@ -197,7 +197,7 @@ msgid "Blocks Validated"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:130
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:135
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:24
msgid "Cancel"
msgstr ""
@@ -367,7 +367,7 @@ msgid "ETH"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:52
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:57
msgid "Enter the Solidity Contract Code below"
msgstr ""
@@ -713,7 +713,7 @@ msgid "Request URL"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:128
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:133
msgid "Reset"
msgstr ""
@@ -1024,7 +1024,7 @@ msgid "Verify & Publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:127
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:132
msgid "Verify & publish"
msgstr ""
@@ -1165,7 +1165,7 @@ msgid "Loading..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:125
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:130
msgid "Loading...."
msgstr ""
@@ -1582,57 +1582,57 @@ msgid "Genesis Block"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:71
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:76
msgid "1 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:66
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:71
msgid "1 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:86
msgid "2 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:76
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
msgid "2 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:96
msgid "3 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:86
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
msgid "3 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:101
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:106
msgid "4 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:96
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:101
msgid "4 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:111
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:116
msgid "5 Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:106
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:111
msgid "5 Library Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:63
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:68
msgid "Contract Libraries"
msgstr ""
@@ -1698,16 +1698,10 @@ msgid "EVM Version"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:58
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:63
msgid "Enter constructor arguments if the contract had any"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address/_tabs.html.eex:67
-#: lib/block_scout_web/templates/address/_tabs.html.eex:133
-msgid "Decompiled code"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:20
msgid "Copy Decompiled Contract Code"
@@ -1718,12 +1712,23 @@ msgstr ""
msgid "Decompiled Code"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/_tabs.html.eex:67
+#: lib/block_scout_web/templates/address/_tabs.html.eex:133
+msgid "Decompiled code"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:18
msgid "Decompiled contract code"
msgstr ""
-#, elixir-format
+#, elixir-format, fuzzy
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:11
msgid "Decompiler version"
msgstr ""
+
+#, elixir-format, fuzzy
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:52
+msgid "Optimization runs"
+msgstr ""
diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
index 766a691a62..632809863f 100644
--- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
+++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
@@ -1,5 +1,6 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase
+ alias Explorer.Factory
describe "listcontracts" do
setup do
@@ -157,6 +158,60 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
]
end
+ test "filtering for only decompiled contracts, with a decompiled with version filter", %{params: params, conn: conn} do
+ insert(:decompiled_smart_contract, decompiler_version: "foobar")
+ smart_contract = insert(:decompiled_smart_contract, decompiler_version: "bizbuz")
+
+ response =
+ conn
+ |> get("/api", Map.merge(params, %{"filter" => "decompiled", "not_decompiled_with_version" => "foobar"}))
+ |> json_response(200)
+
+ assert response["message"] == "OK"
+ assert response["status"] == "1"
+
+ assert response["result"] == [
+ %{
+ "ABI" => "Contract source code not verified",
+ "Address" => to_string(smart_contract.address_hash),
+ "CompilerVersion" => "",
+ "ContractName" => "",
+ "DecompiledSourceCode" => smart_contract.decompiled_source_code,
+ "DecompilerVersion" => "bizbuz",
+ "OptimizationUsed" => "",
+ "SourceCode" => ""
+ }
+ ]
+ end
+
+ test "filtering for only decompiled contracts, with a decompiled with version filter, where another decompiled version exists",
+ %{params: params, conn: conn} do
+ non_match = insert(:decompiled_smart_contract, decompiler_version: "foobar")
+ insert(:decompiled_smart_contract, decompiler_version: "bizbuz", address_hash: non_match.address_hash)
+ smart_contract = insert(:decompiled_smart_contract, decompiler_version: "bizbuz")
+
+ response =
+ conn
+ |> get("/api", Map.merge(params, %{"filter" => "decompiled", "not_decompiled_with_version" => "foobar"}))
+ |> json_response(200)
+
+ assert response["message"] == "OK"
+ assert response["status"] == "1"
+
+ assert response["result"] == [
+ %{
+ "ABI" => "Contract source code not verified",
+ "Address" => to_string(smart_contract.address_hash),
+ "CompilerVersion" => "",
+ "ContractName" => "",
+ "DecompiledSourceCode" => smart_contract.decompiled_source_code,
+ "DecompilerVersion" => "bizbuz",
+ "OptimizationUsed" => "",
+ "SourceCode" => ""
+ }
+ ]
+ end
+
test "filtering for only not_decompiled (and by extension not verified contracts)", %{params: params, conn: conn} do
insert(:decompiled_smart_contract)
insert(:smart_contract)
@@ -359,4 +414,100 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK"
end
end
+
+ describe "verify" do
+ test "with an address that doesn't exist", %{conn: conn} do
+ contract_code_info = Factory.contract_code_info()
+
+ contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
+
+ params = %{
+ "module" => "contract",
+ "action" => "verify",
+ "addressHash" => to_string(contract_address.hash),
+ "name" => contract_code_info.name,
+ "compilerVersion" => contract_code_info.version,
+ "optimization" => contract_code_info.optimized,
+ "contractSourceCode" => contract_code_info.source_code
+ }
+
+ response =
+ conn
+ |> get("/api", params)
+ |> json_response(200)
+
+ expected_result = %{
+ "Address" => to_string(contract_address.hash),
+ "SourceCode" => contract_code_info.source_code,
+ "ABI" => Jason.encode!(contract_code_info.abi),
+ "ContractName" => contract_code_info.name,
+ "CompilerVersion" => contract_code_info.version,
+ "DecompiledSourceCode" => "Contract source code not decompiled.",
+ "DecompilerVersion" => "",
+ "OptimizationUsed" => "0"
+ }
+
+ assert response["status"] == "1"
+ assert response["result"] == expected_result
+ assert response["message"] == "OK"
+ end
+
+ test "with external libraries", %{conn: conn} do
+ contract_data =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/compiler_tests.json"
+ |> File.read!()
+ |> Jason.decode!()
+ |> List.first()
+
+ %{
+ "compiler_version" => compiler_version,
+ "external_libraries" => external_libraries,
+ "name" => name,
+ "optimize" => optimize,
+ "contract" => contract_source_code,
+ "expected_bytecode" => expected_bytecode
+ } = contract_data
+
+ contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode)
+
+ params = %{
+ "module" => "contract",
+ "action" => "verify",
+ "addressHash" => to_string(contract_address.hash),
+ "name" => name,
+ "compilerVersion" => compiler_version,
+ "optimization" => optimize,
+ "contractSourceCode" => contract_source_code
+ }
+
+ params_with_external_libraries =
+ external_libraries
+ |> Enum.with_index()
+ |> Enum.reduce(params, fn {{name, address}, index}, acc ->
+ name_key = "library#{index + 1}Name"
+ address_key = "library#{index + 1}Address"
+
+ acc
+ |> Map.put(name_key, name)
+ |> Map.put(address_key, address)
+ end)
+
+ response =
+ conn
+ |> get("/api", params_with_external_libraries)
+ |> json_response(200)
+
+ assert response["status"] == "1"
+ assert response["message"] == "OK"
+
+ result = response["result"]
+
+ assert result["Address"] == to_string(contract_address.hash)
+ assert result["SourceCode"] == contract_source_code
+ assert result["ContractName"] == name
+ assert result["DecompiledSourceCode"] == "Contract source code not decompiled."
+ assert result["DecompilerVersion"] == ""
+ assert result["OptimizationUsed"] == "1"
+ end
+ end
end
diff --git a/apps/block_scout_web/test/support/fixture/smart_contract/compiler_tests.json b/apps/block_scout_web/test/support/fixture/smart_contract/compiler_tests.json
new file mode 100644
index 0000000000..f74cd3d707
--- /dev/null
+++ b/apps/block_scout_web/test/support/fixture/smart_contract/compiler_tests.json
@@ -0,0 +1,13 @@
+[
+ {
+ "compiler_version": "v0.4.11+commit.68ef5810",
+ "contract": "pragma solidity ^0.4.11;\n/**\n * @title ERC20Basic\n * @dev Simpler version of ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/179\n */\ncontract ERC20Basic {\n uint256 public totalSupply;\n function balanceOf(address who) public constant returns (uint256);\n function transfer(address to, uint256 value) public returns (bool);\n event Transfer(address indexed from, address indexed to, uint256 value);\n}\n/**\n * @title Ownable\n * @dev The Ownable contract has an owner address, and provides basic authorization control\n * functions, this simplifies the implementation of \"user permissions\".\n */\ncontract Ownable {\n address public owner;\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n /**\n * @dev The Ownable constructor sets the original `owner` of the contract to the sender\n * account.\n */\n function Ownable() {\n owner = msg.sender;\n }\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n /**\n * @dev Allows the current owner to transfer control of the contract to a newOwner.\n * @param newOwner The address to transfer ownership to.\n */\n function transferOwnership(address newOwner) onlyOwner public {\n require(newOwner != address(0));\n OwnershipTransferred(owner, newOwner);\n owner = newOwner;\n }\n}\n// Temporarily have SafeMath here until all contracts have been migrated to SafeMathLib version from OpenZeppelin\n/**\n * Math operations with safety checks\n */\ncontract SafeMath {\n function safeMul(uint a, uint b) internal returns (uint) {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n function safeDiv(uint a, uint b) internal returns (uint) {\n assert(b > 0);\n uint c = a / b;\n assert(a == b * c + a % b);\n return c;\n }\n function safeSub(uint a, uint b) internal returns (uint) {\n assert(b <= a);\n return a - b;\n }\n function safeAdd(uint a, uint b) internal returns (uint) {\n uint c = a + b;\n assert(c>=a && c>=b);\n return c;\n }\n function max64(uint64 a, uint64 b) internal constant returns (uint64) {\n return a >= b ? a : b;\n }\n function min64(uint64 a, uint64 b) internal constant returns (uint64) {\n return a < b ? a : b;\n }\n function max256(uint256 a, uint256 b) internal constant returns (uint256) {\n return a >= b ? a : b;\n }\n function min256(uint256 a, uint256 b) internal constant returns (uint256) {\n return a < b ? a : b;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * @title ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/20\n */\ncontract ERC20 is ERC20Basic {\n function allowance(address owner, address spender) public constant returns (uint256);\n function transferFrom(address from, address to, uint256 value) public returns (bool);\n function approve(address spender, uint256 value) public returns (bool);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n/**\n * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation.\n *\n * Based on code by FirstBlood:\n * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol\n */\ncontract StandardToken is ERC20, SafeMath {\n /* Token supply got increased and a new owner received these tokens */\n event Minted(address receiver, uint amount);\n /* Actual balances of token holders */\n mapping(address => uint) balances;\n /* approve() allowances */\n mapping (address => mapping (address => uint)) allowed;\n /* Interface declaration */\n function isToken() public constant returns (bool weAre) {\n return true;\n }\n function transfer(address _to, uint _value) returns (bool success) {\n balances[msg.sender] = safeSub(balances[msg.sender], _value);\n balances[_to] = safeAdd(balances[_to], _value);\n Transfer(msg.sender, _to, _value);\n return true;\n }\n function transferFrom(address _from, address _to, uint _value) returns (bool success) {\n uint _allowance = allowed[_from][msg.sender];\n balances[_to] = safeAdd(balances[_to], _value);\n balances[_from] = safeSub(balances[_from], _value);\n allowed[_from][msg.sender] = safeSub(_allowance, _value);\n Transfer(_from, _to, _value);\n return true;\n }\n function balanceOf(address _owner) constant returns (uint balance) {\n return balances[_owner];\n }\n function approve(address _spender, uint _value) returns (bool success) {\n // To change the approve amount you first have to reduce the addresses`\n // allowance to zero by calling `approve(_spender, 0)` if it is not\n // already 0 to mitigate the race condition described here:\n // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n function allowance(address _owner, address _spender) constant returns (uint remaining) {\n return allowed[_owner][_spender];\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Upgrade agent interface inspired by Lunyr.\n *\n * Upgrade agent transfers tokens to a new contract.\n * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting.\n */\ncontract UpgradeAgent {\n uint public originalSupply;\n /** Interface marker */\n function isUpgradeAgent() public constant returns (bool) {\n return true;\n }\n function upgradeFrom(address _from, uint256 _value) public;\n}\n/**\n * A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision.\n *\n * First envisioned by Golem and Lunyr projects.\n */\ncontract UpgradeableToken is StandardToken {\n /** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */\n address public upgradeMaster;\n /** The next contract where the tokens will be migrated. */\n UpgradeAgent public upgradeAgent;\n /** How many tokens we have upgraded by now. */\n uint256 public totalUpgraded;\n /**\n * Upgrade states.\n *\n * - NotAllowed: The child contract has not reached a condition where the upgrade can bgun\n * - WaitingForAgent: Token allows upgrade, but we don't have a new agent yet\n * - ReadyToUpgrade: The agent is set, but not a single token has been upgraded yet\n * - Upgrading: Upgrade agent is set and the balance holders can upgrade their tokens\n *\n */\n enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading}\n /**\n * Somebody has upgraded some of his tokens.\n */\n event Upgrade(address indexed _from, address indexed _to, uint256 _value);\n /**\n * New upgrade agent available.\n */\n event UpgradeAgentSet(address agent);\n /**\n * Do not allow construction without upgrade master set.\n */\n function UpgradeableToken(address _upgradeMaster) {\n upgradeMaster = _upgradeMaster;\n }\n /**\n * Allow the token holder to upgrade some of their tokens to a new contract.\n */\n function upgrade(uint256 value) public {\n UpgradeState state = getUpgradeState();\n if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) {\n // Called in a bad state\n throw;\n }\n // Validate input value.\n if (value == 0) throw;\n balances[msg.sender] = safeSub(balances[msg.sender], value);\n // Take tokens out from circulation\n totalSupply = safeSub(totalSupply, value);\n totalUpgraded = safeAdd(totalUpgraded, value);\n // Upgrade agent reissues the tokens\n upgradeAgent.upgradeFrom(msg.sender, value);\n Upgrade(msg.sender, upgradeAgent, value);\n }\n /**\n * Set an upgrade agent that handles\n */\n function setUpgradeAgent(address agent) external {\n if(!canUpgrade()) {\n // The token is not yet in a state that we could think upgrading\n throw;\n }\n if (agent == 0x0) throw;\n // Only a master can designate the next agent\n if (msg.sender != upgradeMaster) throw;\n // Upgrade has already begun for an agent\n if (getUpgradeState() == UpgradeState.Upgrading) throw;\n upgradeAgent = UpgradeAgent(agent);\n // Bad interface\n if(!upgradeAgent.isUpgradeAgent()) throw;\n // Make sure that token supplies match in source and target\n if (upgradeAgent.originalSupply() != totalSupply) throw;\n UpgradeAgentSet(upgradeAgent);\n }\n /**\n * Get the state of the token upgrade.\n */\n function getUpgradeState() public constant returns(UpgradeState) {\n if(!canUpgrade()) return UpgradeState.NotAllowed;\n else if(address(upgradeAgent) == 0x00) return UpgradeState.WaitingForAgent;\n else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade;\n else return UpgradeState.Upgrading;\n }\n /**\n * Change the upgrade master.\n *\n * This allows us to set a new owner for the upgrade mechanism.\n */\n function setUpgradeMaster(address master) public {\n if (master == 0x0) throw;\n if (msg.sender != upgradeMaster) throw;\n upgradeMaster = master;\n }\n /**\n * Child contract can enable to provide the condition when the upgrade can begun.\n */\n function canUpgrade() public constant returns(bool) {\n return true;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Define interface for releasing the token transfer after a successful crowdsale.\n */\ncontract ReleasableToken is ERC20, Ownable {\n /* The finalizer contract that allows unlift the transfer limits on this token */\n address public releaseAgent;\n /** A crowdsale contract can release us to the wild if ICO success. If false we are are in transfer lock up period.*/\n bool public released = false;\n /** Map of agents that are allowed to transfer tokens regardless of the lock down period. These are crowdsale contracts and possible the team multisig itself. */\n mapping (address => bool) public transferAgents;\n /**\n * Limit token transfer until the crowdsale is over.\n *\n */\n modifier canTransfer(address _sender) {\n if(!released) {\n if(!transferAgents[_sender]) {\n throw;\n }\n }\n _;\n }\n /**\n * Set the contract that can call release and make the token transferable.\n *\n * Design choice. Allow reset the release agent to fix fat finger mistakes.\n */\n function setReleaseAgent(address addr) onlyOwner inReleaseState(false) public {\n // We don't do interface check here as we might want to a normal wallet address to act as a release agent\n releaseAgent = addr;\n }\n /**\n * Owner can allow a particular address (a crowdsale contract) to transfer tokens despite the lock up period.\n */\n function setTransferAgent(address addr, bool state) onlyOwner inReleaseState(false) public {\n transferAgents[addr] = state;\n }\n /**\n * One way function to release the tokens to the wild.\n *\n * Can be called only from the release agent that is the final ICO contract. It is only called if the crowdsale has been success (first milestone reached).\n */\n function releaseTokenTransfer() public onlyReleaseAgent {\n released = true;\n }\n /** The function can be called only before or after the tokens have been releasesd */\n modifier inReleaseState(bool releaseState) {\n if(releaseState != released) {\n throw;\n }\n _;\n }\n /** The function can be called only by a whitelisted release agent. */\n modifier onlyReleaseAgent() {\n if(msg.sender != releaseAgent) {\n throw;\n }\n _;\n }\n function transfer(address _to, uint _value) canTransfer(msg.sender) returns (bool success) {\n // Call StandardToken.transfer()\n return super.transfer(_to, _value);\n }\n function transferFrom(address _from, address _to, uint _value) canTransfer(_from) returns (bool success) {\n // Call StandardToken.transferForm()\n return super.transferFrom(_from, _to, _value);\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Safe unsigned safe math.\n *\n * https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736#.750gwtwli\n *\n * Originally from https://raw.githubusercontent.com/AragonOne/zeppelin-solidity/master/contracts/SafeMathLib.sol\n *\n * Maintained here until merged to mainline zeppelin-solidity.\n *\n */\nlibrary SafeMathLibExt {\n function times(uint a, uint b) returns (uint) {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n function divides(uint a, uint b) returns (uint) {\n assert(b > 0);\n uint c = a / b;\n assert(a == b * c + a % b);\n return c;\n }\n function minus(uint a, uint b) returns (uint) {\n assert(b <= a);\n return a - b;\n }\n function plus(uint a, uint b) returns (uint) {\n uint c = a + b;\n assert(c>=a);\n return c;\n }\n}\n/**\n * A token that can increase its supply by another contract.\n *\n * This allows uncapped crowdsale by dynamically increasing the supply when money pours in.\n * Only mint agents, contracts whitelisted by owner, can mint new tokens.\n *\n */\ncontract MintableTokenExt is StandardToken, Ownable {\n using SafeMathLibExt for uint;\n bool public mintingFinished = false;\n /** List of agents that are allowed to create new tokens */\n mapping (address => bool) public mintAgents;\n event MintingAgentChanged(address addr, bool state );\n struct ReservedTokensData {\n uint inTokens;\n uint inPercentage;\n }\n mapping (address => ReservedTokensData) public reservedTokensList;\n address[] public reservedTokensDestinations;\n uint public reservedTokensDestinationsLen = 0;\n function setReservedTokensList(address addr, uint inTokens, uint inPercentage) onlyOwner {\n reservedTokensDestinations.push(addr);\n reservedTokensDestinationsLen++;\n reservedTokensList[addr] = ReservedTokensData({inTokens:inTokens, inPercentage:inPercentage});\n }\n function getReservedTokensListValInTokens(address addr) constant returns (uint inTokens) {\n return reservedTokensList[addr].inTokens;\n }\n function getReservedTokensListValInPercentage(address addr) constant returns (uint inPercentage) {\n return reservedTokensList[addr].inPercentage;\n }\n function setReservedTokensListMultiple(address[] addrs, uint[] inTokens, uint[] inPercentage) onlyOwner {\n for (uint iterator = 0; iterator < addrs.length; iterator++) {\n setReservedTokensList(addrs[iterator], inTokens[iterator], inPercentage[iterator]);\n }\n }\n /**\n * Create new tokens and allocate them to an address..\n *\n * Only callably by a crowdsale contract (mint agent).\n */\n function mint(address receiver, uint amount) onlyMintAgent canMint public {\n totalSupply = totalSupply.plus(amount);\n balances[receiver] = balances[receiver].plus(amount);\n // This will make the mint transaction apper in EtherScan.io\n // We can remove this after there is a standardized minting event\n Transfer(0, receiver, amount);\n }\n /**\n * Owner can allow a crowdsale contract to mint new tokens.\n */\n function setMintAgent(address addr, bool state) onlyOwner canMint public {\n mintAgents[addr] = state;\n MintingAgentChanged(addr, state);\n }\n modifier onlyMintAgent() {\n // Only crowdsale contracts are allowed to mint new tokens\n if(!mintAgents[msg.sender]) {\n throw;\n }\n _;\n }\n /** Make sure we are not done yet. */\n modifier canMint() {\n if(mintingFinished) throw;\n _;\n }\n}\n/**\n * A crowdsaled token.\n *\n * An ERC-20 token designed specifically for crowdsales with investor protection and further development path.\n *\n * - The token transfer() is disabled until the crowdsale is over\n * - The token contract gives an opt-in upgrade path to a new contract\n * - The same token can be part of several crowdsales through approve() mechanism\n * - The token can be capped (supply set in the constructor) or uncapped (crowdsale contract can mint new tokens)\n *\n */\ncontract CrowdsaleTokenExt is ReleasableToken, MintableTokenExt, UpgradeableToken {\n /** Name and symbol were updated. */\n event UpdatedTokenInformation(string newName, string newSymbol);\n string public name;\n string public symbol;\n uint public decimals;\n /* Minimum ammount of tokens every buyer can buy. */\n uint public minCap;\n /**\n * Construct the token.\n *\n * This token must be created through a team multisig wallet, so that it is owned by that wallet.\n *\n * @param _name Token name\n * @param _symbol Token symbol - should be all caps\n * @param _initialSupply How many tokens we start with\n * @param _decimals Number of decimal places\n * @param _mintable Are new tokens created over the crowdsale or do we distribute only the initial supply? Note that when the token becomes transferable the minting always ends.\n */\n function CrowdsaleTokenExt(string _name, string _symbol, uint _initialSupply, uint _decimals, bool _mintable, uint _globalMinCap)\n UpgradeableToken(msg.sender) {\n // Create any address, can be transferred\n // to team multisig via changeOwner(),\n // also remember to call setUpgradeMaster()\n owner = msg.sender;\n name = _name;\n symbol = _symbol;\n totalSupply = _initialSupply;\n decimals = _decimals;\n minCap = _globalMinCap;\n // Create initially all balance on the team multisig\n balances[owner] = totalSupply;\n if(totalSupply > 0) {\n Minted(owner, totalSupply);\n }\n // No more new supply allowed after the token creation\n if(!_mintable) {\n mintingFinished = true;\n if(totalSupply == 0) {\n throw; // Cannot create a token without supply and no minting\n }\n }\n }\n /**\n * When token is released to be transferable, enforce no new tokens can be created.\n */\n function releaseTokenTransfer() public onlyReleaseAgent {\n mintingFinished = true;\n super.releaseTokenTransfer();\n }\n /**\n * Allow upgrade agent functionality kick in only if the crowdsale was success.\n */\n function canUpgrade() public constant returns(bool) {\n return released && super.canUpgrade();\n }\n /**\n * Owner can update token information here.\n *\n * It is often useful to conceal the actual token association, until\n * the token operations, like central issuance or reissuance have been completed.\n *\n * This function allows the token owner to rename the token after the operations\n * have been completed and then point the audience to use the token contract.\n */\n function setTokenInformation(string _name, string _symbol) onlyOwner {\n name = _name;\n symbol = _symbol;\n UpdatedTokenInformation(name, symbol);\n }\n}",
+ "expected_bytecode": "606060405236156101e05763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166302f652a381146101e257806305d2035b1461020557806306fdde0314610229578063095ea7b3146102b957806318160ddd146102ec57806323b872dd1461030e57806329ff4f5314610347578063313ce56714610365578063338f43a0146103875780633fa615b0146103b557806340c10f19146103d757806342c1867b146103f8578063432146751461042857806345977d031461044b5780634eee966f1461046057806351ed17a4146104f55780635de4ccb01461052a5780635f412d4f14610556578063600440cb1461056857806370a08231146105945780637386f0a7146105c257806380c190cf146105f15780638444b3911461061f578063867c2857146106535780638da5cb5b1461068357806395d89b41146106af578063961325211461073f5780639738968c14610763578063a9059cbb14610787578063b4ffaece146107ba578063c33105171461087f578063c752ff62146108a1578063d1f276d3146108c3578063d7e7088a146108ef578063dd62ed3e1461090d578063eefa597b14610941578063f2fde38b14610965578063f30f850814610983578063ffeb7d75146109a7575bfe5b34156101ea57fe5b610203600160a060020a036004351660243515156109c5565b005b341561020d57fe5b610215610a28565b604080519115158252519081900360200190f35b341561023157fe5b610239610a31565b60408051602080825283518183015283519192839290830191850190808383821561027f575b80518252602083111561027f57601f19909201916020918201910161025f565b505050905090810190601f1680156102ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156102c157fe5b610215600160a060020a0360043516602435610abf565b604080519115158252519081900360200190f35b34156102f457fe5b6102fc610b66565b60408051918252519081900360200190f35b341561031657fe5b610215600160a060020a0360043581169060243516604435610b6c565b604080519115158252519081900360200190f35b341561034f57fe5b610203600160a060020a0360043516610bc4565b005b341561036d57fe5b6102fc610c2a565b60408051918252519081900360200190f35b341561038f57fe5b6102fc600160a060020a0360043516610c30565b60408051918252519081900360200190f35b34156103bd57fe5b6102fc610c4f565b60408051918252519081900360200190f35b34156103df57fe5b610203600160a060020a0360043516602435610c55565b005b341561040057fe5b610215600160a060020a0360043516610e1b565b604080519115158252519081900360200190f35b341561043057fe5b610203600160a060020a03600435166024351515610e30565b005b341561045357fe5b610203600435610ec4565b005b341561046857fe5b610203600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f89358b0180359182018390048302840183019094528083529799988101979196509182019450925082915084018382808284375094965061103495505050505050565b005b34156104fd57fe5b610511600160a060020a03600435166111a8565b6040805192835260208301919091528051918290030190f35b341561053257fe5b61053a6111c1565b60408051600160a060020a039092168252519081900360200190f35b341561055e57fe5b6102036111d0565b005b341561057057fe5b61053a611205565b60408051600160a060020a039092168252519081900360200190f35b341561059c57fe5b6102fc600160a060020a0360043516611214565b60408051918252519081900360200190f35b34156105ca57fe5b61053a600435611233565b60408051600160a060020a039092168252519081900360200190f35b34156105f957fe5b6102fc600160a060020a0360043516611265565b60408051918252519081900360200190f35b341561062757fe5b61062f611287565b6040518082600481111561063f57fe5b60ff16815260200191505060405180910390f35b341561065b57fe5b610215600160a060020a03600435166112d4565b604080519115158252519081900360200190f35b341561068b57fe5b61053a6112e9565b60408051600160a060020a039092168252519081900360200190f35b34156106b757fe5b6102396112f8565b60408051602080825283518183015283519192839290830191850190808383821561027f575b80518252602083111561027f57601f19909201916020918201910161025f565b505050905090810190601f1680156102ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561074757fe5b610215611386565b604080519115158252519081900360200190f35b341561076b57fe5b610215611396565b604080519115158252519081900360200190f35b341561078f57fe5b610215600160a060020a03600435166024356113bc565b604080519115158252519081900360200190f35b34156107c257fe5b610203600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a99890198929750908201955093508392508501908490808284375094965061141295505050505050565b005b341561088757fe5b6102fc61149f565b60408051918252519081900360200190f35b34156108a957fe5b6102fc6114a5565b60408051918252519081900360200190f35b34156108cb57fe5b61053a6114ab565b60408051600160a060020a039092168252519081900360200190f35b34156108f757fe5b610203600160a060020a03600435166114ba565b005b341561091557fe5b6102fc600160a060020a0360043581169060243516611697565b60408051918252519081900360200190f35b341561094957fe5b6102156116c4565b604080519115158252519081900360200190f35b341561096d57fe5b610203600160a060020a03600435166116ca565b005b341561098b57fe5b610203600160a060020a0360043516602435604435611763565b005b34156109af57fe5b610203600160a060020a03600435166117fe565b005b60035433600160a060020a039081169116146109e15760006000fd5b60045460009060a060020a900460ff16156109fc5760006000fd5b600160a060020a0383166000908152600560205260409020805460ff19168315151790555b5b505b5050565b60065460ff1681565b600e805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610ab75780601f10610a8c57610100808354040283529160200191610ab7565b820191906000526020600020905b815481529060010190602001808311610a9a57829003601f168201915b505050505081565b60008115801590610af45750600160a060020a0333811660009081526002602090815260408083209387168352929052205415155b15610aff5760006000fd5b600160a060020a03338116600081815260026020908152604080832094881680845294825291829020869055815186815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a35060015b92915050565b60005481565b600454600090849060a060020a900460ff161515610bac57600160a060020a03811660009081526005602052604090205460ff161515610bac5760006000fd5b5b610bb885858561185c565b91505b5b509392505050565b60035433600160a060020a03908116911614610be05760006000fd5b60045460009060a060020a900460ff1615610bfb5760006000fd5b6004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0384161790555b5b505b50565b60105481565b600160a060020a0381166000908152600860205260409020545b919050565b60115481565b600160a060020a03331660009081526007602052604090205460ff161515610c7d5760006000fd5b60065460ff1615610c8e5760006000fd5b6000547354ca5a7c536dbed5897b78d30a93dcd0e46fbdac6366098d4f9091836000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018281526020019250505060206040518083038186803b1515610d0a57fe5b6102c65a03f41515610d1857fe5b50506040805180516000908155600160a060020a038616815260016020908152838220549281019190915282517f66098d4f00000000000000000000000000000000000000000000000000000000815260048101929092526024820185905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b1515610daf57fe5b6102c65a03f41515610dbd57fe5b5050604080518051600160a060020a0386166000818152600160209081528582209390935586845293519094507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35b5b5b5050565b60076020526000908152604090205460ff1681565b60035433600160a060020a03908116911614610e4c5760006000fd5b60065460ff1615610e5d5760006000fd5b600160a060020a038216600081815260076020908152604091829020805460ff191685151590811790915582519384529083015280517f4b0adf6c802794c7dde28a08a4e07131abcff3bf9603cd71f14f90bec7865efa9281900390910190a15b5b5b5050565b6000610ece611287565b905060035b816004811115610edf57fe5b1480610ef7575060045b816004811115610ef557fe5b145b1515610f035760006000fd5b811515610f105760006000fd5b600160a060020a033316600090815260016020526040902054610f33908361195f565b600160a060020a03331660009081526001602052604081209190915554610f5a908361195f565b600055600d54610f6a9083611976565b600d55600c54604080517f753e88e5000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152602482018690529151919092169163753e88e591604480830192600092919082900301818387803b1515610fd757fe5b6102c65a03f11515610fe557fe5b5050600c54604080518581529051600160a060020a03928316935033909216917f7e5c344a8141a805725cb476f76c6953b842222b967edd1f78ddb6e8b3f397ac9181900360200190a35b5050565b60035433600160a060020a039081169116146110505760006000fd5b815161106390600e906020850190611a9c565b50805161107790600f906020840190611a9c565b5060408051818152600e8054600260001961010060018416150201909116049282018390527fd131ab1e6f279deea74e13a18477e13e2107deb6dc8ae955648948be5841fb46929091600f918190602082019060608301908690801561111e5780601f106110f35761010080835404028352916020019161111e565b820191906000526020600020905b81548152906001019060200180831161110157829003601f168201915b50508381038252845460026000196101006001841615020190911604808252602090910190859080156111925780601f1061116757610100808354040283529160200191611192565b820191906000526020600020905b81548152906001019060200180831161117557829003601f168201915b505094505050505060405180910390a15b5b5050565b6008602052600090815260409020805460019091015482565b600c54600160a060020a031681565b60045433600160a060020a039081169116146111ec5760006000fd5b6006805460ff1916600117905561120161199e565b5b5b565b600b54600160a060020a031681565b600160a060020a0381166000908152600160205260409020545b919050565b600980548290811061124157fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600160a060020a0381166000908152600860205260409020600101545b919050565b6000611291611396565b151561129f575060016112ce565b600c54600160a060020a031615156112b9575060026112ce565b600d5415156112ca575060036112ce565b5060045b5b5b5b90565b60056020526000908152604090205460ff1681565b600354600160a060020a031681565b600f805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610ab75780601f10610a8c57610100808354040283529160200191610ab7565b820191906000526020600020905b815481529060010190602001808311610a9a57829003601f168201915b505050505081565b60045460a060020a900460ff1681565b60045460009060a060020a900460ff1680156113b557506113b56116c4565b5b90505b90565b600454600090339060a060020a900460ff1615156113fc57600160a060020a03811660009081526005602052604090205460ff1615156113fc5760006000fd5b5b61140784846119e8565b91505b5b5092915050565b60035460009033600160a060020a039081169116146114315760006000fd5b5060005b83518110156114975761148e848281518110151561144f57fe5b90602001906020020151848381518110151561146757fe5b90602001906020020151848481518110151561147f57fe5b90602001906020020151611763565b5b600101611435565b5b5b50505050565b600a5481565b600d5481565b600454600160a060020a031681565b6114c2611396565b15156114ce5760006000fd5b600160a060020a03811615156114e45760006000fd5b600b5433600160a060020a039081169116146115005760006000fd5b60045b61150b611287565b600481111561151657fe5b14156115225760006000fd5b600c805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038381169190911791829055604080516000602091820181905282517f61d3d7a6000000000000000000000000000000000000000000000000000000008152925194909316936361d3d7a6936004808501948390030190829087803b15156115a857fe5b6102c65a03f115156115b657fe5b505060405151151590506115ca5760006000fd5b60008054600c5460408051602090810185905281517f4b2ba0dd00000000000000000000000000000000000000000000000000000000815291519394600160a060020a0390931693634b2ba0dd936004808501948390030190829087803b151561163057fe5b6102c65a03f1151561163e57fe5b5050604051519190911490506116545760006000fd5b600c5460408051600160a060020a039092168252517f7845d5aa74cc410e35571258d954f23b82276e160fe8c188fa80566580f279cc9181900360200190a15b50565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b60015b90565b60035433600160a060020a039081169116146116e65760006000fd5b600160a060020a03811615156116fc5760006000fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b50565b60035433600160a060020a0390811691161461177f5760006000fd5b60098054600181016117918382611b1b565b916000526020600020900160005b8154600160a060020a038088166101009390930a83810291021990911617909155600a80546001908101909155604080518082018252868152602080820187815260009586526008909152919093209251835551910155505b5b505050565b600160a060020a03811615156118145760006000fd5b600b5433600160a060020a039081169116146118305760006000fd5b600b805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b600160a060020a03808416600090815260026020908152604080832033851684528252808320549386168352600190915281205490919061189d9084611976565b600160a060020a0380861660009081526001602052604080822093909355908716815220546118cc908461195f565b600160a060020a0386166000908152600160205260409020556118ef818461195f565b600160a060020a038087166000818152600260209081526040808320338616845282529182902094909455805187815290519288169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600191505b509392505050565b60008282111561196b57fe5b508082035b92915050565b600082820183811080159061198b5750828110155b151561199357fe5b8091505b5092915050565b60045433600160a060020a039081169116146119ba5760006000fd5b6004805474ff0000000000000000000000000000000000000000191660a060020a1790555b5b565b60015b90565b600160a060020a033316600090815260016020526040812054611a0b908361195f565b600160a060020a033381166000908152600160205260408082209390935590851681522054611a3a9083611976565b600160a060020a038085166000818152600160209081526040918290209490945580518681529051919333909316927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611add57805160ff1916838001178555611b0a565b82800160010185558215611b0a579182015b82811115611b0a578251825591602001919060010190611aef565b5b50611b17929150611b45565b5090565b815481835581811511610a2157600083815260209020610a21918101908301611b45565b5b505050565b6112ce91905b80821115611b175760008155600101611b4b565b5090565b905600a165627a7a72305820c38ce552da1339b41e837cc46418755767ab24eb6b08726516f1e03f7b96fca9002900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085570666972696e6700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035546520000000000000000000000000000000000000000000000000000000000",
+ "external_libraries": {
+ "SafeMathLibExt": "0x54ca5a7c536dbed5897b78d30a93dcd0e46fbdac"
+ },
+ "name": "CrowdsaleTokenExt",
+ "optimize": true
+ }
+ ]
+
\ No newline at end of file
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
index 57010769c0..c372c0c979 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
@@ -46,6 +46,7 @@ defmodule EthereumJSONRPC.Contract do
|> case do
{:ok, responses} -> responses
{:error, {:bad_gateway, _request_url}} -> raise "Bad gateway"
+ {:error, reason} when is_atom(reason) -> raise Atom.to_string(reason)
{:error, error} -> raise error
end
|> Enum.into(%{}, &{&1.id, &1})
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index 0c65227931..d882e56b51 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -20,6 +20,7 @@ defmodule Explorer.Chain do
import EthereumJSONRPC, only: [integer_to_quantity: 1]
+ alias Ecto.Adapters.SQL
alias Ecto.{Changeset, Multi}
alias Explorer.Chain.{
@@ -1898,7 +1899,16 @@ defmodule Explorer.Chain do
"""
@spec transaction_estimated_count() :: non_neg_integer()
def transaction_estimated_count do
- TransactionCountCache.value()
+ cached_value = TransactionCountCache.value()
+
+ if is_nil(cached_value) do
+ %Postgrex.Result{rows: [[rows]]} =
+ SQL.query!(Repo, "SELECT reltuples::BIGINT AS estimate FROM pg_class WHERE relname='transactions'")
+
+ rows
+ else
+ cached_value
+ end
end
@doc """
@@ -2638,37 +2648,38 @@ defmodule Explorer.Chain do
@spec data() :: Dataloader.Ecto.t()
def data, do: DataloaderEcto.new(Repo)
- @doc """
- Returns a list of block numbers with invalid consensus.
- """
- @spec list_block_numbers_with_invalid_consensus :: [integer()]
- def list_block_numbers_with_invalid_consensus do
- query =
- from(
- block in Block,
- join: parent in Block,
- on: parent.hash == block.parent_hash,
- where: block.consensus == true,
- where: parent.consensus == false,
- select: parent.number
- )
-
- Repo.all(query, timeout: :infinity)
- end
-
- def list_decompiled_contracts(limit, offset) do
+ def list_decompiled_contracts(limit, offset, not_decompiled_with_version \\ nil) do
query =
from(
address in Address,
- join: decompiled_smart_contract in DecompiledSmartContract,
- on: decompiled_smart_contract.address_hash == address.hash,
- preload: [{:decompiled_smart_contract, decompiled_smart_contract}, :smart_contract],
+ where:
+ fragment(
+ "EXISTS (SELECT 1 FROM decompiled_smart_contracts WHERE decompiled_smart_contracts.address_hash = ?)",
+ address.hash
+ ),
+ preload: [:decompiled_smart_contracts, :smart_contract],
order_by: [asc: address.inserted_at],
limit: ^limit,
offset: ^offset
)
- Repo.all(query)
+ query
+ |> filter_decompiled_with_version(not_decompiled_with_version)
+ |> Repo.all()
+ end
+
+ defp filter_decompiled_with_version(query, nil) do
+ query
+ end
+
+ defp filter_decompiled_with_version(query, not_decompiled_with_version) do
+ from(address in query,
+ left_join: decompiled_smart_contract in DecompiledSmartContract,
+ on: decompiled_smart_contract.decompiler_version == ^not_decompiled_with_version,
+ on: decompiled_smart_contract.address_hash == address.hash,
+ where: is_nil(decompiled_smart_contract.id),
+ distinct: [address.hash]
+ )
end
def list_verified_contracts(limit, offset) do
@@ -2678,7 +2689,7 @@ defmodule Explorer.Chain do
where: not is_nil(address.contract_code),
join: smart_contract in SmartContract,
on: smart_contract.address_hash == address.hash,
- preload: [{:smart_contract, smart_contract}, :decompiled_smart_contract],
+ preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts],
order_by: [asc: address.inserted_at],
limit: ^limit,
offset: ^offset
@@ -2692,7 +2703,7 @@ defmodule Explorer.Chain do
from(
address in Address,
where: not is_nil(address.contract_code),
- preload: [:smart_contract, :decompiled_smart_contract],
+ preload: [:smart_contract, :decompiled_smart_contracts],
order_by: [asc: address.inserted_at],
limit: ^limit,
offset: ^offset
@@ -2709,7 +2720,7 @@ defmodule Explorer.Chain do
on: smart_contract.address_hash == address.hash,
where: not is_nil(address.contract_code),
where: is_nil(smart_contract.address_hash),
- preload: [{:smart_contract, smart_contract}, :decompiled_smart_contract],
+ preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts],
order_by: [asc: address.inserted_at],
limit: ^limit,
offset: ^offset
@@ -2722,11 +2733,16 @@ defmodule Explorer.Chain do
query =
from(
address in Address,
+ where:
+ fragment(
+ "NOT EXISTS (SELECT 1 FROM decompiled_smart_contracts WHERE decompiled_smart_contracts.address_hash = ?)",
+ address.hash
+ ),
left_join: smart_contract in SmartContract,
on: smart_contract.address_hash == address.hash,
left_join: decompiled_smart_contract in DecompiledSmartContract,
on: decompiled_smart_contract.address_hash == address.hash,
- preload: [smart_contract: smart_contract, decompiled_smart_contract: decompiled_smart_contract],
+ preload: [:smart_contract, :decompiled_smart_contracts],
where: not is_nil(address.contract_code),
where: is_nil(smart_contract.address_hash),
where: is_nil(decompiled_smart_contract.address_hash),
diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex
index d4c341c7db..d4b5bc8174 100644
--- a/apps/explorer/lib/explorer/chain/address.ex
+++ b/apps/explorer/lib/explorer/chain/address.ex
@@ -62,6 +62,7 @@ defmodule Explorer.Chain.Address do
except: [
:__meta__,
:smart_contract,
+ :decompiled_smart_contract,
:token,
:contracts_creation_internal_transaction,
:contracts_creation_transaction,
@@ -77,7 +78,6 @@ defmodule Explorer.Chain.Address do
field(:has_decompiled_code?, :boolean, virtual: true)
has_one(:smart_contract, SmartContract)
- has_one(:decompiled_smart_contract, DecompiledSmartContract)
has_one(:token, Token, foreign_key: :contract_address_hash)
has_one(
diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
index 100f120290..209d5d2fa6 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
@@ -46,7 +46,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
|> Map.put(:timestamps, timestamps)
ordered_consensus_block_numbers = ordered_consensus_block_numbers(changes_list)
- where_invalid_parent = where_invalid_parent(changes_list)
+ where_invalid_neighbour = where_invalid_neighbour(changes_list)
where_forked = where_forked(changes_list)
multi
@@ -70,8 +70,8 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
|> Multi.run(:lose_consensus, fn repo, _ ->
lose_consensus(repo, ordered_consensus_block_numbers, insert_options)
end)
- |> Multi.run(:lose_invalid_parent_consensus, fn repo, _ ->
- lose_invalid_parent_consensus(repo, where_invalid_parent, insert_options)
+ |> Multi.run(:lose_invalid_neighbour_consensus, fn repo, _ ->
+ lose_invalid_neighbour_consensus(repo, where_invalid_neighbour, insert_options)
end)
|> Multi.run(:delete_address_token_balances, fn repo, _ ->
delete_address_token_balances(repo, ordered_consensus_block_numbers, insert_options)
@@ -316,13 +316,13 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
end
end
- defp lose_invalid_parent_consensus(repo, where_invalid_parent, %{
+ defp lose_invalid_neighbour_consensus(repo, where_invalid_neighbour, %{
timeout: timeout,
timestamps: %{updated_at: updated_at}
}) do
query =
from(
- block in where_invalid_parent,
+ block in where_invalid_neighbour,
update: [
set: [
consensus: false,
@@ -338,7 +338,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
{:ok, result}
rescue
postgrex_error in Postgrex.Error ->
- {:error, %{exception: postgrex_error, where_invalid_parent: where_invalid_parent}}
+ {:error, %{exception: postgrex_error, where_invalid_neighbour: where_invalid_neighbour}}
end
end
@@ -581,12 +581,22 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
end)
end
- defp where_invalid_parent(blocks_changes) when is_list(blocks_changes) do
+ defp where_invalid_neighbour(blocks_changes) when is_list(blocks_changes) do
initial = from(b in Block, where: false)
- Enum.reduce(blocks_changes, initial, fn %{consensus: consensus, parent_hash: parent_hash, number: number}, acc ->
+ Enum.reduce(blocks_changes, initial, fn %{
+ consensus: consensus,
+ hash: hash,
+ parent_hash: parent_hash,
+ number: number
+ },
+ acc ->
if consensus do
- from(block in acc, or_where: block.number == ^(number - 1) and block.hash != ^parent_hash)
+ from(
+ block in acc,
+ or_where: block.number == ^(number - 1) and block.hash != ^parent_hash,
+ or_where: block.number == ^(number + 1) and block.parent_hash != ^hash
+ )
else
acc
end
diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex
index a117a77353..937c1d422f 100644
--- a/apps/explorer/lib/explorer/chain/smart_contract.ex
+++ b/apps/explorer/lib/explorer/chain/smart_contract.ex
@@ -209,8 +209,8 @@ defmodule Explorer.Chain.SmartContract do
field(:constructor_arguments, :string)
field(:abi, {:array, :map})
- has_one(
- :decompiled_smart_contract,
+ has_many(
+ :decompiled_smart_contracts,
DecompiledSmartContract,
foreign_key: :address_hash
)
@@ -227,7 +227,7 @@ defmodule Explorer.Chain.SmartContract do
end
def preload_decompiled_smart_contract(contract) do
- Repo.preload(contract, :decompiled_smart_contract)
+ Repo.preload(contract, :decompiled_smart_contracts)
end
def changeset(%__MODULE__{} = smart_contract, attrs) do
diff --git a/apps/explorer/lib/explorer/chain/transaction_count_cache.ex b/apps/explorer/lib/explorer/chain/transaction_count_cache.ex
index 60ecc05285..5e441734c2 100644
--- a/apps/explorer/lib/explorer/chain/transaction_count_cache.ex
+++ b/apps/explorer/lib/explorer/chain/transaction_count_cache.ex
@@ -12,7 +12,7 @@ defmodule Explorer.Chain.TransactionCountCache do
# 2 hours
@cache_period 1_000 * 60 * 60 * 2
- @default_value 0
+ @default_value nil
@key "count"
@name __MODULE__
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 2eab371445..e7f6090707 100644
--- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
@@ -13,10 +13,10 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
## Examples
- iex(1)> Explorer.SmartContract.Solidity.CodeCompiler.run(
- ...> "SimpleStorage",
- ...> "v0.4.24+commit.e67f0147",
- ...> \"""
+ iex(1)> Explorer.SmartContract.Solidity.CodeCompiler.run([
+ ...> name: "SimpleStorage",
+ ...> compiler_version: "v0.4.24+commit.e67f0147",
+ ...> code: \"""
...> pragma solidity ^0.4.24;
...>
...> contract SimpleStorage {
@@ -31,8 +31,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
...> }
...> }
...> \""",
- ...> false
- ...> )
+ ...> optimize: false, evm_version: "byzantium"
+ ...> ])
{
:ok,
%{
@@ -61,10 +61,18 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
}
}
"""
- def run(name, compiler_version, code, optimize, evm_version \\ "byzantium", external_libs \\ %{}) do
+ def run(params) do
+ name = Keyword.fetch!(params, :name)
+ compiler_version = Keyword.fetch!(params, :compiler_version)
+ code = Keyword.fetch!(params, :code)
+ optimize = Keyword.fetch!(params, :optimize)
+ optimization_runs = params |> Keyword.get(:optimization_runs, 200) |> Integer.to_string()
+ evm_version = Keyword.get(params, :evm_version, List.last(@allowed_evm_versions))
+ external_libs = Keyword.get(params, :external_libs, %{})
+
external_libs_string = Jason.encode!(external_libs)
- evm_version =
+ checked_evm_version =
if evm_version in @allowed_evm_versions do
evm_version
else
@@ -79,9 +87,10 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
code,
compiler_version,
optimize_value(optimize),
+ optimization_runs,
@new_contract_name,
external_libs_string,
- evm_version
+ checked_evm_version
]
)
diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex
index ae2dae5cbe..576b1fcef1 100644
--- a/apps/explorer/lib/explorer/smart_contract/verifier.ex
+++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex
@@ -24,9 +24,18 @@ defmodule Explorer.SmartContract.Verifier do
external_libraries = Map.get(params, "external_libraries", %{})
constructor_arguments = Map.get(params, "constructor_arguments", "")
evm_version = Map.get(params, "evm_version", "byzantium")
+ optimization_runs = Map.get(params, "optimization_runs", 200)
solc_output =
- CodeCompiler.run(name, compiler_version, contract_source_code, optimization, evm_version, external_libraries)
+ CodeCompiler.run(
+ name: name,
+ compiler_version: compiler_version,
+ code: contract_source_code,
+ optimize: optimization,
+ optimization_runs: optimization_runs,
+ evm_version: evm_version,
+ external_libs: external_libraries
+ )
case compare_bytecodes(solc_output, address_hash, constructor_arguments) do
{:error, :generated_bytecode} ->
@@ -34,12 +43,12 @@ defmodule Explorer.SmartContract.Verifier do
second_solc_output =
CodeCompiler.run(
- name,
- compiler_version,
- contract_source_code,
- optimization,
- next_evm_version,
- external_libraries
+ name: name,
+ compiler_version: compiler_version,
+ code: contract_source_code,
+ optimize: optimization,
+ evm_version: next_evm_version,
+ external_libs: external_libraries
)
compare_bytecodes(second_solc_output, address_hash, constructor_arguments)
diff --git a/apps/explorer/priv/compile_solc.js b/apps/explorer/priv/compile_solc.js
index 2ed0ff2664..6e9489d433 100755
--- a/apps/explorer/priv/compile_solc.js
+++ b/apps/explorer/priv/compile_solc.js
@@ -5,9 +5,10 @@ const solc = require('solc');
var sourceCode = process.argv[2];
var version = process.argv[3];
var optimize = process.argv[4];
-var newContractName = process.argv[5];
-var externalLibraries = JSON.parse(process.argv[6])
-var evmVersion = process.argv[7]
+var optimizationRuns = parseInt(process.argv[5], 10);
+var newContractName = process.argv[6];
+var externalLibraries = JSON.parse(process.argv[7])
+var evmVersion = process.argv[8];
var compiled_code = solc.loadRemoteVersion(version, function (err, solcSnapshot) {
if (err) {
@@ -24,7 +25,7 @@ var compiled_code = solc.loadRemoteVersion(version, function (err, solcSnapshot)
evmVersion: evmVersion,
optimizer: {
enabled: optimize == '1',
- runs: 200
+ runs: optimizationRuns
},
libraries: {
[newContractName]: externalLibraries
diff --git a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs
index 3d6fd912cc..ecf1d71dfb 100644
--- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs
+++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs
@@ -261,34 +261,36 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
# Regression test for https://github.com/poanetwork/blockscout/issues/1644
- test "discards parent block if it isn't related to the current one because of reorg",
+ test "discards neighbouring blocks if they aren't related to the current one because of reorg and/or import timeout",
%{consensus_block: %Block{number: block_number, hash: block_hash, miner_hash: miner_hash}, options: options} do
- old_block = insert(:block, parent_hash: block_hash, number: block_number + 1)
- insert(:block, parent_hash: old_block.hash, number: old_block.number + 1)
+ old_block1 = params_for(:block, miner_hash: miner_hash, parent_hash: block_hash, number: block_number + 1)
- new_block1 = params_for(:block, parent_hash: block_hash, number: block_number + 1, miner_hash: miner_hash)
+ new_block1 = params_for(:block, miner_hash: miner_hash, parent_hash: block_hash, number: block_number + 1)
+ new_block2 = params_for(:block, miner_hash: miner_hash, parent_hash: new_block1.hash, number: block_number + 2)
- new_block2 =
- params_for(:block, parent_hash: new_block1.hash, number: new_block1.number + 1, miner_hash: miner_hash)
+ range = block_number..(block_number + 2)
- %Ecto.Changeset{valid?: true, changes: block_changes} = Block.changeset(%Block{}, new_block2)
- changes_list = [block_changes]
+ insert_block(new_block1, options)
+ insert_block(new_block2, options)
+ assert Chain.missing_block_number_ranges(range) == []
- Multi.new()
- |> Blocks.run(changes_list, options)
- |> Repo.transaction()
+ insert_block(old_block1, options)
+ assert Chain.missing_block_number_ranges(range) == [(block_number + 2)..(block_number + 2)]
- assert Chain.missing_block_number_ranges(block_number..new_block2.number) == [old_block.number..old_block.number]
+ insert_block(new_block2, options)
+ assert Chain.missing_block_number_ranges(range) == [(block_number + 1)..(block_number + 1)]
- %Ecto.Changeset{valid?: true, changes: block_changes} = Block.changeset(%Block{}, new_block1)
- changes_list = [block_changes]
+ insert_block(new_block1, options)
+ assert Chain.missing_block_number_ranges(range) == []
+ end
+ end
- Multi.new()
- |> Blocks.run(changes_list, options)
- |> Repo.transaction()
+ defp insert_block(block_params, options) do
+ %Ecto.Changeset{valid?: true, changes: block_changes} = Block.changeset(%Block{}, block_params)
- assert Chain.missing_block_number_ranges(block_number..new_block2.number) == []
- end
+ Multi.new()
+ |> Blocks.run([block_changes], options)
+ |> Repo.transaction()
end
defp count(schema) do
diff --git a/apps/explorer/test/explorer/chain/transaction_count_cache_test.exs b/apps/explorer/test/explorer/chain/transaction_count_cache_test.exs
index 8ddcda2f04..a1b0d72485 100644
--- a/apps/explorer/test/explorer/chain/transaction_count_cache_test.exs
+++ b/apps/explorer/test/explorer/chain/transaction_count_cache_test.exs
@@ -8,7 +8,7 @@ defmodule Explorer.Chain.TransactionCountCacheTest do
result = TransactionCountCache.value(TestCache)
- assert result == 0
+ assert is_nil(result)
end
test "updates cache if initial value is zero" do
diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs
index c408716afd..ebb4b880dd 100644
--- a/apps/explorer/test/explorer/chain_test.exs
+++ b/apps/explorer/test/explorer/chain_test.exs
@@ -3667,27 +3667,6 @@ defmodule Explorer.ChainTest do
end
end
- describe "list_block_numbers_with_invalid_consensus/0" do
- test "returns a list of block numbers with invalid consensus" do
- block1 = insert(:block)
- block2_with_invalid_consensus = insert(:block, parent_hash: block1.hash, consensus: false)
- _block2 = insert(:block, parent_hash: block1.hash, number: block2_with_invalid_consensus.number)
- block3 = insert(:block, parent_hash: block2_with_invalid_consensus.hash)
- block4 = insert(:block, parent_hash: block3.hash)
- block5 = insert(:block, parent_hash: block4.hash)
- block6_without_consensus = insert(:block, parent_hash: block5.hash, consensus: false)
- block6 = insert(:block, parent_hash: block5.hash, number: block6_without_consensus.number)
- block7 = insert(:block, parent_hash: block6.hash)
- block8_with_invalid_consensus = insert(:block, parent_hash: block7.hash, consensus: false)
- _block8 = insert(:block, parent_hash: block7.hash, number: block8_with_invalid_consensus.number)
- block9 = insert(:block, parent_hash: block8_with_invalid_consensus.hash)
- _block10 = insert(:block, parent_hash: block9.hash)
-
- assert Chain.list_block_numbers_with_invalid_consensus() ==
- [block2_with_invalid_consensus.number, block8_with_invalid_consensus.number]
- end
- end
-
describe "block_combined_rewards/1" do
test "sums the block_rewards values" do
block = insert(:block)
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 ebf13a4d41..79bbb62f70 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
@@ -18,10 +18,11 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
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
+ name: contract_code_info.name,
+ compiler_version: contract_code_info.version,
+ code: contract_code_info.source_code,
+ optimize: contract_code_info.optimized,
+ evm_version: "byzantium"
)
assert {:ok,
@@ -37,10 +38,11 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
response =
CodeCompiler.run(
- contract_code_info.name,
- contract_code_info.version,
- contract_code_info.source_code,
- optimize
+ name: contract_code_info.name,
+ compiler_version: contract_code_info.version,
+ code: contract_code_info.source_code,
+ optimize: optimize,
+ evm_version: "byzantium"
)
assert {:ok,
@@ -61,12 +63,12 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
{:ok, result} =
CodeCompiler.run(
- name,
- compiler_version,
- contract,
- optimize,
- "byzantium",
- external_libraries
+ name: name,
+ compiler_version: compiler_version,
+ code: contract,
+ optimize: optimize,
+ evm_version: "byzantium",
+ external_libs: external_libraries
)
clean_result = remove_init_data_and_whisper_data(result["bytecode"])
@@ -109,7 +111,14 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
evm_version = "constantinople"
- response = CodeCompiler.run(name, version, code, optimize, evm_version)
+ response =
+ CodeCompiler.run(
+ name: name,
+ compiler_version: version,
+ code: code,
+ optimize: optimize,
+ evm_version: evm_version
+ )
assert {:ok,
%{
@@ -139,7 +148,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
version = "v0.1.3-nightly.2015.9.25+commit.4457170"
- response = CodeCompiler.run(name, version, code, optimize)
+ response = CodeCompiler.run(name: name, compiler_version: version, code: code, optimize: optimize)
assert {:ok,
%{
@@ -156,10 +165,10 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
response =
CodeCompiler.run(
- contract_code_info.name,
- contract_code_info.version,
- wrong_code,
- contract_code_info.optimized
+ name: contract_code_info.name,
+ compiler_version: contract_code_info.version,
+ code: wrong_code,
+ optimize: contract_code_info.optimized
)
assert {:error, :compilation} = response
diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs
index 9f9a34c18e..e95f3f421c 100644
--- a/apps/indexer/config/config.exs
+++ b/apps/indexer/config/config.exs
@@ -31,7 +31,7 @@ block_transformer =
config :indexer,
block_transformer: block_transformer,
ecto_repos: [Explorer.Repo],
- metadata_updater_days_interval: 7,
+ metadata_updater_days_interval: 2,
# bytes
memory_limit: 1 <<< 30,
first_block: System.get_env("FIRST_BLOCK") || 0
diff --git a/apps/indexer/lib/indexer/block/invalid_consensus/supervisor.ex b/apps/indexer/lib/indexer/block/invalid_consensus/supervisor.ex
deleted file mode 100644
index cca692a76c..0000000000
--- a/apps/indexer/lib/indexer/block/invalid_consensus/supervisor.ex
+++ /dev/null
@@ -1,45 +0,0 @@
-defmodule Indexer.Block.InvalidConsensus.Supervisor do
- @moduledoc """
- Supervises process for ensuring blocks with invalid consensus get queued for
- indexing.
- """
-
- use Supervisor
-
- alias Indexer.Block.InvalidConsensus.Worker
-
- def child_spec([]) do
- child_spec([[]])
- end
-
- def child_spec([init_arguments]) do
- child_spec([init_arguments, [name: __MODULE__]])
- end
-
- def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
- spec = %{
- id: __MODULE__,
- start: {__MODULE__, :start_link, start_link_arguments},
- restart: :transient,
- type: :supervisor
- }
-
- Supervisor.child_spec(spec, [])
- end
-
- def start_link(init_arguments, gen_server_options \\ []) do
- Supervisor.start_link(__MODULE__, init_arguments, gen_server_options)
- end
-
- @impl Supervisor
- def init(_) do
- children = [
- {Worker, [[supervisor: self()], [name: Worker]]},
- {Task.Supervisor, name: Indexer.Block.InvalidConsensus.TaskSupervisor}
- ]
-
- opts = [strategy: :one_for_all]
-
- Supervisor.init(children, opts)
- end
-end
diff --git a/apps/indexer/lib/indexer/block/invalid_consensus/worker.ex b/apps/indexer/lib/indexer/block/invalid_consensus/worker.ex
deleted file mode 100644
index cbe93c604b..0000000000
--- a/apps/indexer/lib/indexer/block/invalid_consensus/worker.ex
+++ /dev/null
@@ -1,99 +0,0 @@
-defmodule Indexer.Block.InvalidConsensus.Worker do
- @moduledoc """
- Finds blocks with invalid consensus and queues them up to be refetched. This
- process does this once, after the application starts up.
-
- A block has invalid consensus when it is referenced as the parent hash of a
- block with consensus while not having consensus (consensus=false). Only one
- block can have consensus at a given height (block number).
- """
-
- use GenServer
-
- require Logger
-
- alias Explorer.Chain
- alias Indexer.Block.Catchup.Fetcher
- alias Indexer.Block.InvalidConsensus.TaskSupervisor
-
- def child_spec([init_arguments]) do
- child_spec([init_arguments, []])
- end
-
- def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
- spec = %{
- id: __MODULE__,
- start: {__MODULE__, :start_link, start_link_arguments},
- restart: :transient,
- type: :worker
- }
-
- Supervisor.child_spec(spec, [])
- end
-
- def start_link(init_arguments, gen_server_options \\ []) do
- GenServer.start_link(__MODULE__, init_arguments, gen_server_options)
- end
-
- def init(opts) do
- sup_pid = Keyword.fetch!(opts, :supervisor)
- retry_interval = Keyword.get(opts, :retry_interval, 10_000)
-
- send(self(), :scan)
-
- state = %{
- block_numbers: [],
- retry_interval: retry_interval,
- sup_pid: sup_pid,
- task_ref: nil
- }
-
- {:ok, state}
- end
-
- def handle_info(:scan, state) do
- block_numbers = Chain.list_block_numbers_with_invalid_consensus()
-
- case block_numbers do
- [] ->
- Supervisor.stop(state.sup_pid, :normal)
- {:noreply, state}
-
- block_numbers ->
- Process.send_after(self(), :push_front_blocks, state.retry_interval)
- {:noreply, %{state | block_numbers: block_numbers}}
- end
- end
-
- def handle_info(:push_front_blocks, %{block_numbers: block_numbers} = state) do
- %Task{ref: ref} = async_push_front(block_numbers)
- {:noreply, %{state | task_ref: ref}}
- end
-
- def handle_info({ref, :ok}, %{task_ref: ref, sup_pid: sup_pid}) do
- Process.demonitor(ref, [:flush])
- Supervisor.stop(sup_pid, :normal)
- {:stop, :shutdown}
- end
-
- def handle_info({ref, {:error, reason}}, %{task_ref: ref, retry_interval: millis} = state) do
- case reason do
- :queue_unavailable -> :ok
- _ -> Logger.error(fn -> inspect(reason) end)
- end
-
- Process.demonitor(ref, [:flush])
- Process.send_after(self(), :push_front_blocks, millis)
-
- {:noreply, %{state | task_ref: nil}}
- end
-
- def handle_info({:DOWN, ref, :process, _, _}, %{task_ref: ref, retry_interval: millis} = state) do
- Process.send_after(self(), :push_front_blocks, millis)
- {:noreply, %{state | task_ref: nil}}
- end
-
- defp async_push_front(block_numbers) do
- Task.Supervisor.async_nolink(TaskSupervisor, Fetcher, :push_front, [block_numbers])
- end
-end
diff --git a/apps/indexer/lib/indexer/block/realtime/consensus_ensurer.ex b/apps/indexer/lib/indexer/block/realtime/consensus_ensurer.ex
deleted file mode 100644
index 816f1d76bf..0000000000
--- a/apps/indexer/lib/indexer/block/realtime/consensus_ensurer.ex
+++ /dev/null
@@ -1,34 +0,0 @@
-defmodule Indexer.Block.Realtime.ConsensusEnsurer do
- @moduledoc """
- Triggers a refetch if a given block doesn't have consensus.
-
- """
- require Logger
-
- alias Explorer.Chain
- alias Explorer.Chain.Hash
- alias Indexer.Block.Realtime.Fetcher
-
- def perform(_, number, _) when not is_integer(number) or number < 0, do: :ok
-
- def perform(%Hash{byte_count: unquote(Hash.Full.byte_count())} = block_hash, number, block_fetcher) do
- case Chain.hash_to_block(block_hash) do
- {:ok, %{consensus: true} = _block} ->
- :ignore
-
- _ ->
- Logger.info(fn ->
- [
- "refetch from consensus was found on block (",
- to_string(number),
- "). A reorg initiated."
- ]
- end)
-
- # trigger refetch if consensus=false or block was not found
- Fetcher.fetch_and_import_block(number, block_fetcher, true)
- end
-
- :ok
- end
-end
diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex
index 49787ad675..fe486f62c2 100644
--- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex
+++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex
@@ -27,7 +27,7 @@ defmodule Indexer.Block.Realtime.Fetcher do
alias Explorer.Chain.TokenTransfer
alias Explorer.Counters.AverageBlockTime
alias Indexer.{AddressExtraction, Block, TokenBalances, Tracer}
- alias Indexer.Block.Realtime.{ConsensusEnsurer, TaskSupervisor}
+ alias Indexer.Block.Realtime.TaskSupervisor
alias Timex.Duration
@behaviour Block.Fetcher
@@ -269,12 +269,7 @@ defmodule Indexer.Block.Realtime.Fetcher do
@decorate span(tracer: Tracer)
defp do_fetch_and_import_block(block_number_to_fetch, block_fetcher, retry) do
case fetch_and_import_range(block_fetcher, block_number_to_fetch..block_number_to_fetch) do
- {:ok, %{inserted: inserted, errors: []}} ->
- for block <- Map.get(inserted, :blocks, []) do
- args = [block.parent_hash, block.number - 1, block_fetcher]
- Task.Supervisor.start_child(TaskSupervisor, ConsensusEnsurer, :perform, args)
- end
-
+ {:ok, %{inserted: _, errors: []}} ->
Logger.debug("Fetched and imported.")
{:ok, %{inserted: _, errors: [_ | _] = errors}} ->
diff --git a/apps/indexer/lib/indexer/block/supervisor.ex b/apps/indexer/lib/indexer/block/supervisor.ex
index 2bab786f18..50b24a6dd5 100644
--- a/apps/indexer/lib/indexer/block/supervisor.ex
+++ b/apps/indexer/lib/indexer/block/supervisor.ex
@@ -4,7 +4,7 @@ defmodule Indexer.Block.Supervisor do
"""
alias Indexer.Block
- alias Indexer.Block.{Catchup, InvalidConsensus, Realtime, Reward, Uncle}
+ alias Indexer.Block.{Catchup, Realtime, Reward, Uncle}
alias Indexer.Temporary.{AddressesWithoutCode, FailedCreatedAddresses}
use Supervisor
@@ -50,7 +50,6 @@ defmodule Indexer.Block.Supervisor do
%{block_fetcher: block_fetcher, block_interval: block_interval, memory_monitor: memory_monitor},
[name: Catchup.Supervisor]
]},
- {InvalidConsensus.Supervisor, [[], [name: InvalidConsensus.Supervisor]]},
{Realtime.Supervisor,
[
%{block_fetcher: realtime_block_fetcher, subscribe_named_arguments: realtime_subscribe_named_arguments},
diff --git a/apps/indexer/lib/indexer/token/fetcher.ex b/apps/indexer/lib/indexer/token/fetcher.ex
index c18f1d6488..818f27f138 100644
--- a/apps/indexer/lib/indexer/token/fetcher.ex
+++ b/apps/indexer/lib/indexer/token/fetcher.ex
@@ -52,11 +52,8 @@ defmodule Indexer.Token.Fetcher do
@decorate trace(name: "fetch", resource: "Indexer.Token.Fetcher.run/2", service: :indexer, tracer: Tracer)
def run([token_contract_address], _json_rpc_named_arguments) do
case Chain.token_from_address_hash(token_contract_address) do
- {:ok, %Token{cataloged: false} = token} ->
+ {:ok, %Token{} = token} ->
catalog_token(token)
-
- {:ok, _} ->
- :ok
end
end
@@ -74,7 +71,7 @@ defmodule Indexer.Token.Fetcher do
|> MetadataRetriever.get_functions_of()
|> Map.put(:cataloged, true)
- {:ok, _} = Chain.update_token(token, token_params)
+ {:ok, _} = Chain.update_token(%{token | updated_at: DateTime.utc_now()}, token_params)
:ok
end
end
diff --git a/apps/indexer/test/indexer/block/invalid_consensus/worker_test.exs b/apps/indexer/test/indexer/block/invalid_consensus/worker_test.exs
deleted file mode 100644
index a6fa07f0d3..0000000000
--- a/apps/indexer/test/indexer/block/invalid_consensus/worker_test.exs
+++ /dev/null
@@ -1,87 +0,0 @@
-defmodule Indexer.Block.InvalidConsensus.WorkerTest do
- use Explorer.DataCase
-
- alias Indexer.Sequence
- alias Indexer.Block.InvalidConsensus.{Worker, TaskSupervisor}
-
- @moduletag :capture_log
-
- describe "start_link/1" do
- test "starts the worker" do
- assert {:ok, _pid} = Worker.start_link(supervisor: self())
- end
- end
-
- describe "init/1" do
- test "sends message to self" do
- pid = self()
- assert {:ok, %{task_ref: nil, block_numbers: [], sup_pid: ^pid}} = Worker.init(supervisor: self())
- assert_received :scan
- end
- end
-
- describe "handle_info with :scan" do
- test "sends shutdown to supervisor" do
- state = %{task_ref: nil, block_numbers: [], sup_pid: self()}
- Task.async(fn -> Worker.handle_info(:scan, state) end)
- assert_receive {_, _, {:terminate, :normal}}
- end
-
- test "sends message to self when blocks with invalid consensus are found" do
- block1 = insert(:block)
- block2_with_invalid_consensus = insert(:block, parent_hash: block1.hash, consensus: false)
- _block2 = insert(:block, parent_hash: block1.hash, number: block2_with_invalid_consensus.number)
- _block3 = insert(:block, parent_hash: block2_with_invalid_consensus.hash)
-
- block_number = block2_with_invalid_consensus.number
-
- expected_state = %{task_ref: nil, block_numbers: [block_number], retry_interval: 1}
- state = %{task_ref: nil, block_numbers: [], retry_interval: 1}
-
- assert {:noreply, ^expected_state} = Worker.handle_info(:scan, state)
- assert_receive :push_front_blocks
- end
- end
-
- describe "handle_info with :push_front_blocks" do
- test "starts a task" do
- task_sup_pid = start_supervised!({Task.Supervisor, name: TaskSupervisor})
- start_supervised!({Sequence, [[ranges: [], step: -1], [name: :block_catchup_sequencer]]})
-
- state = %{task_ref: nil, block_numbers: [1]}
- assert {:noreply, %{task_ref: task_ref}} = Worker.handle_info(:push_front_blocks, state)
- assert is_reference(task_ref)
-
- refute_receive {^task_ref, {:error, :queue_unavailable}}
- assert_receive {^task_ref, :ok}
-
- stop_supervised(task_sup_pid)
- end
- end
-
- describe "handle_info with task ref tuple" do
- test "sends shutdown to supervisor on success" do
- ref = Process.monitor(self())
- state = %{task_ref: ref, block_numbers: [], sup_pid: self()}
- Task.async(fn -> assert Worker.handle_info({ref, :ok}, state) end)
- assert_receive {_, _, {:terminate, :normal}}
- end
-
- test "sends message to self to try again on failure" do
- ref = Process.monitor(self())
- state = %{task_ref: ref, block_numbers: [1], sup_pid: self(), retry_interval: 1}
- expected_state = %{state | task_ref: nil}
- assert {:noreply, ^expected_state} = Worker.handle_info({ref, {:error, :queue_unavailable}}, state)
- assert_receive :push_front_blocks
- end
- end
-
- describe "handle_info with failed task" do
- test "sends message to self to try again" do
- ref = Process.monitor(self())
- state = %{task_ref: ref, block_numbers: [1], sup_pid: self(), retry_interval: 1}
- assert {:noreply, %{task_ref: nil}} = Worker.handle_info({:DOWN, ref, :process, self(), :EXIT}, state)
- assert_receive :push_front_blocks
- end
- end
-end
diff --git a/apps/indexer/test/indexer/token/fetcher_test.exs b/apps/indexer/test/indexer/token/fetcher_test.exs
index 85872a70c5..d3e56d7712 100644
--- a/apps/indexer/test/indexer/token/fetcher_test.exs
+++ b/apps/indexer/test/indexer/token/fetcher_test.exs
@@ -20,12 +20,6 @@ defmodule Indexer.Token.FetcherTest do
end
describe "run/3" do
- test "skips tokens that have already been cataloged", %{json_rpc_named_arguments: json_rpc_named_arguments} do
- expect(EthereumJSONRPC.Mox, :json_rpc, 0, fn _, _ -> :ok end)
- %Token{contract_address_hash: contract_address_hash} = insert(:token, cataloged: true)
- assert Fetcher.run([contract_address_hash], json_rpc_named_arguments) == :ok
- end
-
test "catalogs tokens that haven't been cataloged", %{json_rpc_named_arguments: json_rpc_named_arguments} do
token = insert(:token, name: nil, symbol: nil, total_supply: nil, decimals: nil, cataloged: false)
contract_address_hash = token.contract_address_hash