Merge branch 'master' into feature/#1476-add-styles-for-POSDAO-network

* master: (28 commits)
  feat: verify contracts via an RPC endpoint
  Remove obsolete InvalidConsensus.Worker
  Discard child block with parent_hash not matching hash of imported block
  Expand non-consensus block regression test to test for race conditions
  Remove obsolete ConsensusEnsurer
  feat: add not_decompiled_with_version filter
  Update fetcher_test.exs
  mix format
  fix test
  update gettext
  fix build
  remove fetching token 2 times
  update CHANGELOG
  update tokens in fetcher
  add CHANGELOG entry
  exclude decompiled smart contract from encoding
  add CHANGELOG entry
  update metadata in controller
  add CHANGELOG entry
  decrease token metadata update interval
  ...
pull/1704/head
Gabriel Rodriguez Alsina 6 years ago
commit ee635fa42a
  1. 10
      CHANGELOG.md
  2. 15
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  3. 107
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
  4. 3
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
  5. 172
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  6. 5
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  7. 28
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
  8. 51
      apps/block_scout_web/priv/gettext/default.pot
  9. 53
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  10. 151
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  11. 13
      apps/block_scout_web/test/support/fixture/smart_contract/compiler_tests.json
  12. 1
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
  13. 72
      apps/explorer/lib/explorer/chain.ex
  14. 2
      apps/explorer/lib/explorer/chain/address.ex
  15. 28
      apps/explorer/lib/explorer/chain/import/runner/blocks.ex
  16. 6
      apps/explorer/lib/explorer/chain/smart_contract.ex
  17. 2
      apps/explorer/lib/explorer/chain/transaction_count_cache.ex
  18. 27
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  19. 23
      apps/explorer/lib/explorer/smart_contract/verifier.ex
  20. 9
      apps/explorer/priv/compile_solc.js
  21. 40
      apps/explorer/test/explorer/chain/import/runner/blocks_test.exs
  22. 2
      apps/explorer/test/explorer/chain/transaction_count_cache_test.exs
  23. 21
      apps/explorer/test/explorer/chain_test.exs
  24. 49
      apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
  25. 2
      apps/indexer/config/config.exs
  26. 45
      apps/indexer/lib/indexer/block/invalid_consensus/supervisor.ex
  27. 99
      apps/indexer/lib/indexer/block/invalid_consensus/worker.ex
  28. 34
      apps/indexer/lib/indexer/block/realtime/consensus_ensurer.ex
  29. 9
      apps/indexer/lib/indexer/block/realtime/fetcher.ex
  30. 3
      apps/indexer/lib/indexer/block/supervisor.ex
  31. 7
      apps/indexer/lib/indexer/token/fetcher.ex
  32. 87
      apps/indexer/test/indexer/block/invalid_consensus/worker_test.exs
  33. 6
      apps/indexer/test/indexer/token/fetcher_test.exs

@ -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

@ -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

@ -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

@ -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

@ -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
]
}

@ -48,6 +48,11 @@
<%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger" %>
</div>
<div class="form-group">
<%= 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" %>
</div>
<div class="form-group mb-4">
<%= 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" %>

@ -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

@ -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 ""

@ -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 ""

@ -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

File diff suppressed because one or more lines are too long

@ -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})

@ -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),

@ -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(

@ -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

@ -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

@ -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__

@ -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
]
)

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

@ -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

@ -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

@ -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

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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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}} ->

@ -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},

@ -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

@ -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

@ -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

Loading…
Cancel
Save