Merge branch 'master' into ab-fix-rsk-total-supply

pull/2237/head
Victor Baranov 5 years ago committed by GitHub
commit 5ff8063e7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      CHANGELOG.md
  2. 11
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  3. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/read_contract_controller.ex
  6. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex
  7. 31
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  8. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  9. 66
      apps/block_scout_web/priv/gettext/default.pot
  10. 68
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  11. 4
      apps/explorer/config/config.exs
  12. 11
      apps/explorer/lib/explorer/chain.ex
  13. 22
      apps/explorer/lib/explorer/chain/smart_contract.ex
  14. 105
      apps/explorer/lib/explorer/chain/transaction.ex
  15. 11
      apps/explorer/lib/explorer/counters/average_block_time.ex
  16. 3
      apps/explorer/lib/explorer/smart_contract/publisher.ex
  17. 12
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  18. 1
      apps/explorer/mix.exs
  19. 132
      apps/explorer/priv/repo/migrations/20190619154943_reduce_transaction_status_constraint.exs
  20. 11
      apps/explorer/priv/repo/migrations/20190625085852_add_additional_contract_fields.exs
  21. 2
      apps/explorer/test/explorer/chain/import_test.exs
  22. 11
      apps/explorer/test/explorer/chain_test.exs
  23. 31
      apps/explorer/test/explorer/counters/average_block_time_test.exs
  24. 2
      apps/indexer/lib/indexer/fetcher/token.ex
  25. 5
      apps/indexer/lib/indexer/fetcher/token_balance.ex
  26. 2
      apps/indexer/lib/indexer/fetcher/token_updater.ex
  27. 35
      apps/indexer/test/indexer/fetcher/token_balance_test.exs

@ -6,8 +6,11 @@
- [#2151](https://github.com/poanetwork/blockscout/pull/2151) - hide dropdown menu then other networks list is empty
- [#2191](https://github.com/poanetwork/blockscout/pull/2191) - allow to configure token metadata update interval
- [#2146](https://github.com/poanetwork/blockscout/pull/2146) - feat: add eth_getLogs rpc endpoint
- [#2216](https://github.com/poanetwork/blockscout/pull/2216) - Improve token's controllers by avoiding unnecessary preloads
- [#2235](https://github.com/poanetwork/blockscout/pull/2235) - save and show additional validation fields to smart contract
- [#2190](https://github.com/poanetwork/blockscout/pull/2190) - show all token transfers
- [#2193](https://github.com/poanetwork/blockscout/pull/2193) - feat: add BLOCKSCOUT_HOST, and use it in API docs
- [#2266](https://github.com/poanetwork/blockscout/pull/2266) - allow excluding uncles from average block time calculation
### Fixes
- [#2263](https://github.com/poanetwork/blockscout/pull/2263) - added an ability to close network selector on outside click
@ -52,13 +55,14 @@
- [#2173](https://github.com/poanetwork/blockscout/pull/2173) - handle correctly empty transactions
- [#2174](https://github.com/poanetwork/blockscout/pull/2174) - fix reward channel joining
- [#2186](https://github.com/poanetwork/blockscout/pull/2186) - fix net version test
- [#2167](https://github.com/poanetwork/blockscout/pull/2168) - feat: document eth rpc api mimicking endpoints
- [#2237](https://github.com/poanetwork/blockscout/pull/2237) - fix rsk total_supply
- [#2198](https://github.com/poanetwork/blockscout/pull/2198) - reduce transaction status and error constraint
- [#2167](https://github.com/poanetwork/blockscout/pull/2167) - feat: document eth rpc api mimicking endpoints
- [#2225](https://github.com/poanetwork/blockscout/pull/2225) - fix metadata decoding in Solidity 0.5.9 smart contract verification
- [#2204](https://github.com/poanetwork/blockscout/pull/2204) - fix large contract verification
- [#2247](https://github.com/poanetwork/blockscout/pull/2247) - hide logs search if there are no logs
- [#2248](https://github.com/poanetwork/blockscout/pull/2248) - sort block after query execution for average block time
- [#2270](https://github.com/poanetwork/blockscout/pull/2270) - Remove duplicate params in `Indexer.Fetcher.TokenBalance`
- [#2268](https://github.com/poanetwork/blockscout/pull/2268) - remove not existing assigns in html code
### Chore

@ -25,17 +25,10 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
%{
"address_id" => address_hash_string,
"smart_contract" => smart_contract,
"external_libraries" => external_libraries,
"evm_version" => evm_version,
"optimization" => optimization
"external_libraries" => external_libraries
}
) do
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
case Publisher.publish(address_hash_string, smart_contract, external_libraries) do
{:ok, _smart_contract} ->
redirect(conn, to: address_contract_path(conn, :index, address_hash_string))

@ -44,7 +44,7 @@ defmodule BlockScoutWeb.Tokens.HolderController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -61,7 +61,7 @@ defmodule BlockScoutWeb.Tokens.InventoryController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.Tokens.ReadContractController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -45,7 +45,7 @@ defmodule BlockScoutWeb.Tokens.TransferController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -30,10 +30,39 @@
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Compiler version" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.compiler_version %></dd>
</dl>
<%= if @address.smart_contract.evm_version do %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "EVM Version" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.evm_version %></dd>
</dl>
<% end %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Optimization enabled" %></dt>
<dd class="col-sm-8 col-md-10"><%= format_optimization_text(@address.smart_contract.optimization) %></dd>
</dl>
<%= if @address.smart_contract.optimization && @address.smart_contract.optimization_runs do %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Optimization runs" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.optimization_runs %></dd>
</dl>
<% end %>
<%= if @address.smart_contract.constructor_arguments do %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Constructor arguments" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.constructor_arguments %></dd>
</dl>
<% end %>
<%= if @address.smart_contract.external_libraries do %>
<section>
<div class="d-flex justify-content-between align-items-baseline">
<h3><%= gettext "External libraries" %></h3>
</div>
<div class="tile tile-muted mb-4">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= format_smart_contract_abi(@address.smart_contract.abi) %></code>
</pre>
</div>
</section>
<% end %>
</div>
<hr/>
<section>
@ -64,7 +93,7 @@
<% end %>
<section>
<%= case contract_creation_code do %>
<% {:selfdestructed, transaction_init} -> %>
<% {:selfdestructed, transaction_init} -> %>
<div class="d-flex justify-content-between align-items-baseline">
<h3><%= gettext "Contract Creation Code" %></h3>
<button type="button" class="button button-secondary button-sm" id="button" data-clipboard-text="<%= transaction_init %>" aria-label="copy contract creation code">

@ -44,7 +44,7 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label :evm_version, :evm_version, gettext("EVM Version") %>
<div class="center-column">
<%= select :evm_version, :evm_version, @evm_versions, class: "form-control border-rounded", selected: "petersburg", "aria-describedby": "evm-version-help-block" %>
<%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", selected: "petersburg", "aria-describedby": "evm-version-help-block" %>
</div>
<div class="smart-contract-form-group-tooltip">The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version. <a href="https://forum.poa.network/t/smart-contract-verification-evm-version-details/2318" target="_blank">EVM version details</a>.</div>
</div>
@ -76,7 +76,7 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :name, gettext("Optimization runs") %>
<div class="center-column">
<%= text_input :optimization, :runs, value: 200, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %>
<%= text_input f, :optimization_runs, value: 200, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %>
</div>
<div class="smart-contract-form-group-tooltip"></div>
</div>

@ -258,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Contract ABI"
msgstr ""
@ -296,7 +296,7 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:41
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
msgid "Contract source code"
msgstr ""
@ -577,7 +577,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_contract/index.html.eex:40
msgid "Optimization enabled"
msgstr ""
@ -1405,17 +1405,17 @@ msgid "Support"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_contract/index.html.eex:84
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71
#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_contract/index.html.eex:72
msgid "Copy Source Code"
msgstr ""
@ -1487,6 +1487,7 @@ msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:45
msgid "EVM Version"
msgstr ""
@ -1517,6 +1518,7 @@ msgid "Decompiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
msgid "Optimization runs"
msgstr ""
@ -1583,27 +1585,27 @@ msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:112
msgid "Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:104
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:85
#: lib/block_scout_web/templates/address_contract/index.html.eex:114
msgid "Copy Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@ -1740,11 +1742,37 @@ msgstr ""
msgid "here."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Change Network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:58
msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
@ -1769,19 +1797,3 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""

@ -258,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Contract ABI"
msgstr ""
@ -296,7 +296,7 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:41
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
msgid "Contract source code"
msgstr ""
@ -577,7 +577,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_contract/index.html.eex:40
msgid "Optimization enabled"
msgstr ""
@ -1405,17 +1405,17 @@ msgid "Support"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_contract/index.html.eex:84
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71
#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_contract/index.html.eex:72
msgid "Copy Source Code"
msgstr ""
@ -1487,6 +1487,7 @@ msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:45
msgid "EVM Version"
msgstr ""
@ -1517,6 +1518,7 @@ msgid "Decompiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
msgid "Optimization runs"
msgstr ""
@ -1583,27 +1585,27 @@ msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:112
msgid "Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:104
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:85
#: lib/block_scout_web/templates/address_contract/index.html.eex:114
msgid "Copy Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@ -1740,48 +1742,58 @@ msgstr ""
msgid "here."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Change Network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore."
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Mainnet"
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
#: lib/block_scout_web/templates/address_contract/index.html.eex:58
msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore."
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Mainnet"
msgstr ""
#, elixir-format, fuzzy
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
msgstr ""

@ -12,7 +12,9 @@ config :explorer,
token_functions_reader_max_retries: 3,
allowed_evm_versions:
System.get_env("ALLOWED_EVM_VERSIONS") ||
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg"
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg",
include_uncles_in_average_block_time:
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "false", do: false, else: true)
config :explorer, Explorer.Counters.AverageBlockTime, enabled: true

@ -2671,14 +2671,19 @@ defmodule Explorer.Chain do
@doc """
Fetches a `t:Token.t/0` by an address hash.
Optionally accepts a list of bindings to preload, just like `Ecto.Query.preload/3`
"""
@spec token_from_address_hash(Hash.Address.t()) :: {:ok, Token.t()} | {:error, :not_found}
def token_from_address_hash(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do
@spec token_from_address_hash(Hash.Address.t(), [Macro.t()]) :: {:ok, Token.t()} | {:error, :not_found}
def token_from_address_hash(
%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash,
preloads \\ []
) do
query =
from(
token in Token,
where: token.contract_address_hash == ^hash,
preload: [{:contract_address, :smart_contract}]
preload: ^preloads
)
case Repo.one(query) do

@ -198,6 +198,9 @@ defmodule Explorer.Chain.SmartContract do
compiler_version: String.t(),
optimization: boolean,
contract_source_code: String.t(),
constructor_arguments: String.t() | nil,
evm_version: String.t() | nil,
optimization_runs: non_neg_integer() | nil,
abi: [function_description]
}
@ -207,6 +210,9 @@ defmodule Explorer.Chain.SmartContract do
field(:optimization, :boolean)
field(:contract_source_code, :string)
field(:constructor_arguments, :string)
field(:evm_version, :string)
field(:optimization_runs, :integer)
field(:external_libraries, :map)
field(:abi, {:array, :map})
has_many(
@ -239,7 +245,10 @@ defmodule Explorer.Chain.SmartContract do
:contract_source_code,
:address_hash,
:abi,
:constructor_arguments
:constructor_arguments,
:evm_version,
:optimization_runs,
:external_libraries
])
|> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash])
|> unique_constraint(:address_hash)
@ -248,7 +257,16 @@ defmodule Explorer.Chain.SmartContract do
def invalid_contract_changeset(%__MODULE__{} = smart_contract, attrs, error) do
smart_contract
|> cast(attrs, [:name, :compiler_version, :optimization, :contract_source_code, :address_hash])
|> cast(attrs, [
:name,
:compiler_version,
:optimization,
:contract_source_code,
:address_hash,
:evm_version,
:optimization_runs,
:constructor_arguments
])
|> validate_required([:name, :compiler_version, :optimization, :address_hash])
|> add_error(:contract_source_code, error_message(error))
end

@ -247,7 +247,7 @@ defmodule Explorer.Chain.Transaction do
end
@doc """
A pending transaction has neither `block_hash` nor an `index`
A pending transaction does not have a `block_hash`
iex> changeset = Explorer.Chain.Transaction.changeset(
...> %Transaction{},
@ -267,42 +267,6 @@ defmodule Explorer.Chain.Transaction do
iex> changeset.valid?
true
A pending transaction (which is indicated by not having a `block_hash`) can't have `block_number`,
`cumulative_gas_used`, `gas_used`, or `index`.
iex> changeset = Explorer.Chain.Transaction.changeset(
...> %Transaction{},
...> %{
...> from_address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
...> block_number: 34,
...> cumulative_gas_used: 0,
...> gas: 4700000,
...> gas_price: 100000000000,
...> gas_used: 4600000,
...> hash: "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6",
...> index: 0,
...> input: "0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102db8061005e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100a05780638da5cb5b146100c9578063fdacd5761461011e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610141565b005b34156100ab57600080fd5b6100b3610224565b6040518082815260200191505060405180910390f35b34156100d457600080fd5b6100dc61022a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561012957600080fd5b61013f600480803590602001909190505061024f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610220578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561020b57600080fd5b6102c65a03f1151561021c57600080fd5b5050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102ac57806001819055505b505600a165627a7a72305820a9c628775efbfbc17477a472413c01ee9b33881f550c59d21bee9928835c854b0029",
...> nonce: 0,
...> r: 0xAD3733DF250C87556335FFE46C23E34DBAFFDE93097EF92F52C88632A40F0C75,
...> s: 0x72caddc0371451a58de2ca6ab64e0f586ccdb9465ff54e1c82564940e89291e3,
...> status: :ok,
...> v: 0x8d,
...> value: 0
...> }
...> )
iex> changeset.valid?
false
iex> Keyword.get_values(changeset.errors, :block_number)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :cumulative_gas_used)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :gas_used)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :index)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :status)
[{"can't be set when the transaction is pending", []}]
A collated transaction MUST have an `index` so its position in the `block` is known and the `cumulative_gas_used` ane
`gas_used` to know its fees.
@ -359,35 +323,6 @@ defmodule Explorer.Chain.Transaction do
iex> changeset.valid?
true
Once the `internal_transactions_indexed_at` is set, both pre- and post-Byzantium transactions will be able to know
their status, so if `internal_transaction_indexed_at` is set, `status` is required.
iex> changeset = Explorer.Chain.Transaction.changeset(
...> %Transaction{},
...> %{
...> block_hash: "0xe52d77084cab13a4e724162bcd8c6028e5ecfaa04d091ee476e96b9958ed6b47",
...> block_number: 34,
...> cumulative_gas_used: 0,
...> from_address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
...> gas: 4700000,
...> gas_price: 100000000000,
...> gas_used: 4600000,
...> hash: "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6",
...> index: 0,
...> input: "0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102db8061005e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100a05780638da5cb5b146100c9578063fdacd5761461011e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610141565b005b34156100ab57600080fd5b6100b3610224565b6040518082815260200191505060405180910390f35b34156100d457600080fd5b6100dc61022a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561012957600080fd5b61013f600480803590602001909190505061024f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610220578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561020b57600080fd5b6102c65a03f1151561021c57600080fd5b5050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102ac57806001819055505b505600a165627a7a72305820a9c628775efbfbc17477a472413c01ee9b33881f550c59d21bee9928835c854b0029",
...> internal_transactions_indexed_at: DateTime.utc_now(),
...> nonce: 0,
...> r: 0xAD3733DF250C87556335FFE46C23E34DBAFFDE93097EF92F52C88632A40F0C75,
...> s: 0x72caddc0371451a58de2ca6ab64e0f586ccdb9465ff54e1c82564940e89291e3,
...> v: 0x8d,
...> value: 0
...> }
...> )
iex> changeset.valid?
false
iex> Keyword.get_values(changeset.errors, :status)
[{"can't be blank when the internal transactions have been fetched", []}]
The `error` can only be set with a specific error message when `status` is `:error`
iex> changeset = Explorer.Chain.Transaction.changeset(
@ -445,10 +380,9 @@ defmodule Explorer.Chain.Transaction do
transaction
|> cast(attrs, @required_attrs ++ @optional_attrs)
|> validate_required(@required_attrs)
|> validate_collated_or_pending()
|> validate_collated()
|> validate_error()
|> validate_status()
|> check_pending()
|> check_collated()
|> check_error()
|> check_status()
@ -592,29 +526,17 @@ defmodule Explorer.Chain.Transaction do
{collated_field, :"collated_#{collated_field}}"}
end)
@pending_fields_with_check @collated_fields
@pending_fields_with_validation @collated_fields ++ ~w(internal_transaction_indexed_at status)a
@pending_message "can't be set when the transaction is pending"
@pending_field_to_check Enum.into(@pending_fields_with_check, %{}, fn pending_field ->
{pending_field, :"pending_#{pending_field}}"}
end)
defp check_collated(%Changeset{} = changeset) do
check_constraints(changeset, @collated_field_to_check, @collated_message)
end
defp check_pending(%Changeset{} = changeset) do
check_constraints(changeset, @pending_field_to_check, @pending_message)
end
@error_message "can't be set when status is not :error"
defp check_error(%Changeset{} = changeset) do
check_constraint(changeset, :error, message: @error_message, name: :error)
changeset
end
@status_message "can't be blank when the internal transactions have been fetched"
@status_message "can't be set when the block_hash is unknown"
defp check_status(%Changeset{} = changeset) do
check_constraint(changeset, :status, message: @status_message, name: :status)
@ -632,22 +554,10 @@ defmodule Explorer.Chain.Transaction do
end)
end
defp validate_collated_or_pending(%Changeset{} = changeset) do
defp validate_collated(%Changeset{} = changeset) do
case Changeset.get_field(changeset, :block_hash) do
nil -> validate_collated_or_pending(changeset, @pending_fields_with_validation, &validate_pending/2)
%Hash{} -> validate_collated_or_pending(changeset, @collated_fields, &validate_collated/2)
end
end
defp validate_collated_or_pending(%Changeset{} = changeset, fields, field_validator)
when is_list(fields) and is_function(field_validator, 2) do
Enum.reduce(fields, changeset, field_validator)
end
defp validate_pending(field, %Changeset{} = changeset) when is_atom(field) do
case Changeset.get_field(changeset, field) do
%Hash{} -> Enum.reduce(@collated_fields, changeset, &validate_collated/2)
nil -> changeset
_ -> Changeset.add_error(changeset, field, @pending_message)
end
end
@ -667,9 +577,8 @@ defmodule Explorer.Chain.Transaction do
end
defp validate_status(%Changeset{} = changeset) do
# all other errors on status are handled by validate_pending
if Changeset.get_field(changeset, :internal_transactions_indexed_at) != nil and
Changeset.get_field(changeset, :status) == nil do
if Changeset.get_field(changeset, :block_hash) == nil and
Changeset.get_field(changeset, :status) != nil do
Changeset.add_error(changeset, :status, @status_message)
else
changeset

@ -70,8 +70,17 @@ defmodule Explorer.Counters.AverageBlockTime do
select: {block.number, block.timestamp}
)
query =
if Application.get_env(:explorer, :include_uncles_in_average_block_time) do
timestamps_query
else
from(block in timestamps_query,
where: block.consensus == true
)
end
timestamps =
timestamps_query
query
|> Repo.all()
|> Enum.sort_by(fn {_, timestamp} -> timestamp end, &>=/2)
|> Enum.map(fn {number, timestamp} ->

@ -68,9 +68,12 @@ defmodule Explorer.SmartContract.Publisher do
address_hash: address_hash,
name: params["name"],
compiler_version: params["compiler_version"],
evm_version: params["evm_version"],
optimization_runs: params["optimization_runs"],
optimization: params["optimization"],
contract_source_code: params["contract_source_code"],
constructor_arguments: clean_constructor_arguments,
external_libaries: params["external_libraries"],
abi: abi
}
end

@ -70,7 +70,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
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()
optimization_runs = optimization_runs(params)
evm_version = Keyword.get(params, :evm_version, List.last(allowed_evm_versions()))
external_libs = Keyword.get(params, :external_libs, %{})
@ -163,6 +163,16 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
defp optimize_value(true), do: "1"
defp optimize_value("true"), do: "1"
defp optimization_runs(params) do
value = params |> Keyword.get(:optimization_runs, "200")
if is_binary(value) do
value
else
"#{value}"
end
end
defp create_source_file(source) do
{:ok, path} = Briefly.create()

@ -92,7 +92,6 @@ defmodule Explorer.Mixfile do
{:math, "~> 0.3.0"},
{:mock, "~> 0.3.0", only: [:test], runtime: false},
{:mox, "~> 0.4", only: [:test]},
{:nimble_csv, "~> 0.6.0"},
{:poison, "~> 3.1"},
{:nimble_csv, "~> 0.6.0"},
{:postgrex, ">= 0.0.0"},

@ -0,0 +1,132 @@
defmodule Explorer.Repo.Migrations.ReduceTransactionStatusConstraint do
use Ecto.Migration
def up do
drop(
constraint(
:transactions,
:status
)
)
create(
constraint(
:transactions,
:status,
# NOTE: all checks on status are lifted except those regarding block_hash
# This is because of block invalidation, that causes transactions to be
# refetched while previous internal transactions still exist
check: """
(block_hash IS NULL AND status IS NULL) OR
(block_hash IS NOT NULL) OR
(status = 0 and error = 'dropped/replaced')
"""
)
)
drop(
constraint(
:transactions,
:error
)
)
create(
constraint(
:transactions,
:error,
# NOTE: all checks on error are lifted except when status is not 0, for
# the same reasons as above
check: """
(status = 0) OR
(status != 0 AND error IS NULL)
"""
)
)
end
def down do
drop(
constraint(
:transactions,
:status
)
)
create(
constraint(
:transactions,
:status,
# 0 - NULL
# 1 - NOT NULL
#
# | block_hash | internal_transactions_indexed_at | status | OK | description
# |------------|----------------------------------|--------|----|------------
# | 0 | 0 | 0 | 1 | pending
# | 0 | 0 | 1 | 0 | pending with status
# | 0 | 1 | 0 | 0 | pending with internal transactions
# | 0 | 1 | 1 | 0 | pending with internal transactions and status
# | 1 | 0 | 0 | 1 | pre-byzantium collated transaction without internal transactions
# | 1 | 0 | 1 | 1 | post-byzantium collated transaction without internal transactions
# | 1 | 1 | 0 | 0 | pre-byzantium collated transaction with internal transaction without status
# | 1 | 1 | 1 | 1 | pre- or post-byzantium collated transaction with internal transactions and status
#
# [Karnaugh map](https://en.wikipedia.org/wiki/Karnaugh_map)
# b \ is | 00 | 01 | 11 | 10 |
# -------|----|----|----|----|
# 0 | 1 | 0 | 0 | 0 |
# 1 | 1 | 1 | 1 | 0 |
#
# Simplification: ¬i·¬s + b·¬i + b·s
check: """
(internal_transactions_indexed_at IS NULL AND status IS NULL) OR
(block_hash IS NOT NULL AND internal_transactions_indexed_at IS NULL) OR
(block_hash IS NOT NULL AND status IS NOT NULL) OR
(status = 0 and error = 'dropped/replaced')
"""
)
)
drop(
constraint(
:transactions,
:error
)
)
create(
constraint(
:transactions,
:error,
# | status | internal_transactions_indexed_at | error | OK | description
# |--------|----------------------------------|----------|------------|------------
# | NULL | NULL | NULL | TRUE | pending or pre-byzantium collated
# | NULL | NULL | NOT NULL | FALSE | error cannot be known before internal transactions are indexed
# | NULL | NOT NULL | NULL | DON'T CARE | handled by `status` check
# | NULL | NOT NULL | NOT NULL | FALSE | error cannot be set unless status is known to be error (`0`)
# | 0 | NULL | NULL | TRUE | post-byzantium before internal transactions indexed
# | 0 | NULL | NOT NULL | FALSE | error cannot be set unless internal transactions are indexed
# | 0 | NOT NULL | NULL | FALSE | error MUST be set when status is error
# | 0 | NOT NULL | NOT NULL | TRUE | error is set when status is error
# | 1 | NULL | NULL | TRUE | post-byzantium before internal transactions indexed
# | 1 | NULL | NOT NULL | FALSE | error cannot be set when status is ok
# | 1 | NOT NULL | NULL | TRUE | error is not set when status is ok
# | 1 | NOT NULL | NOT NULL | FALSE | error cannot be set when status is ok
#
# Karnaugh map
# s \ ie | NULL, NULL | NULL, NOT NULL | NOT NULL, NOT NULL | NOT NULL, NULL |
# -------|------------|----------------|--------------------|----------------|
# NULL | TRUE | FALSE | FALSE | DON'T CARE |
# 0 | TRUE | FALSE | TRUE | FALSE |
# 1 | TRUE | FALSE | FALSE | TRUE |
#
check: """
(internal_transactions_indexed_at IS NULL AND error IS NULL) OR
(status = 0 AND internal_transactions_indexed_at IS NOT NULL AND error IS NOT NULL) OR
(status != 0 AND internal_transactions_indexed_at IS NOT NULL AND error IS NULL) OR
(status = 0 and error = 'dropped/replaced')
"""
)
)
end
end

@ -0,0 +1,11 @@
defmodule Explorer.Repo.Migrations.AddAdditionalContractFields do
use Ecto.Migration
def change do
alter table(:smart_contracts) do
add(:optimization_runs, :integer, null: true)
add(:evm_version, :string, null: true)
add(:external_libraries, :jsonb, null: true)
end
end
end

@ -573,7 +573,7 @@ defmodule Explorer.Chain.ImportTest do
transaction =
:transaction
|> insert(error: nil, internal_transactions_indexed_at: nil, status: nil, from_address: from_address, status: 0)
|> insert(error: nil, internal_transactions_indexed_at: nil, status: nil, from_address: from_address)
|> with_block(block, status: :error)
internal_transacton =

@ -3430,6 +3430,17 @@ defmodule Explorer.ChainTest do
token = build(:token)
assert {:error, :not_found} = Chain.token_from_address_hash(token.contract_address.hash)
end
test "with contract_address' smart_contract preloaded" do
smart_contract = build(:smart_contract)
address = insert(:address, smart_contract: smart_contract)
token = insert(:token, contract_address: address)
assert {:ok, result} =
Chain.token_from_address_hash(token.contract_address_hash, [{:contract_address, :smart_contract}])
assert smart_contract = result.contract_address.smart_contract
end
end
test "stream_uncataloged_token_contract_address_hashes/2 reduces with given reducer and accumulator" do

@ -11,6 +11,8 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
start_supervised!(AverageBlockTime)
Application.put_env(:explorer, AverageBlockTime, enabled: true)
Application.put_env(:explorer, :include_uncles_in_average_block_time, true)
on_exit(fn ->
Application.put_env(:explorer, AverageBlockTime, enabled: false)
end)
@ -43,6 +45,35 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT3S")
end
test "excludes uncles if include_uncles_in_average_block_time is set to false" do
block_number = 99_999_999
Application.put_env(:explorer, :include_uncles_in_average_block_time, false)
first_timestamp = Timex.now()
insert(:block, number: block_number, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 3))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 4))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 5))
AverageBlockTime.refresh()
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT2S")
end
test "excludes uncles if include_uncles_in_average_block_time is set to true" do
block_number = 99_999_999
first_timestamp = Timex.now()
insert(:block, number: block_number, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 3))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 4))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 5))
AverageBlockTime.refresh()
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT1S")
end
test "when there are no uncles sorts by block number" do
block_number = 99_999_999

@ -52,7 +52,7 @@ defmodule Indexer.Fetcher.Token do
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.Token.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
case Chain.token_from_address_hash(token_contract_address, [{:contract_address, :smart_contract}]) do
{:ok, %Token{} = token} ->
catalog_token(token)
end

@ -93,7 +93,10 @@ defmodule Indexer.Fetcher.TokenBalance do
end
def fetch_from_blockchain(params_list) do
retryable_params_list = Enum.filter(params_list, &(&1.retries_count <= @max_retries))
retryable_params_list =
params_list
|> Enum.filter(&(&1.retries_count <= @max_retries))
|> Enum.uniq_by(&Map.take(&1, [:token_contract_address_hash, :address_hash, :block_number]))
Logger.metadata(count: Enum.count(retryable_params_list))

@ -37,7 +37,7 @@ defmodule Indexer.Fetcher.TokenUpdater do
@doc false
def update_metadata(token_addresses) when is_list(token_addresses) do
Enum.each(token_addresses, fn address ->
case Chain.token_from_address_hash(address) do
case Chain.token_from_address_hash(address, [{:contract_address, :smart_contract}]) do
{:ok, %Token{cataloged: true} = token} ->
update_metadata(token)
end

@ -111,6 +111,41 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
assert TokenBalance.run(token_balances, nil) == :ok
end
test "fetches duplicate params only once" do
%Address.TokenBalance{
address_hash: %Hash{bytes: address_hash_bytes} = address_hash,
token_contract_address_hash: %Hash{bytes: token_contract_address_hash_bytes},
block_number: block_number
} = insert(:token_balance, value_fetched_at: nil, value: nil)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000"
}
]}
end
)
assert TokenBalance.run(
[
{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0},
{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}
],
nil
) == :ok
assert 1 =
from(tb in Address.TokenBalance, where: tb.address_hash == ^address_hash)
|> Explorer.Repo.aggregate(:count, :id)
end
end
describe "import_token_balances/1" do

Loading…
Cancel
Save