merge master into try-it-out-eth-rpc

pull/2433/head
saneery 5 years ago
commit 20dc50741b
  1. 2
      .credo.exs
  2. 3
      CHANGELOG.md
  3. 44
      apps/block_scout_web/lib/block_scout_web/chain.ex
  4. 40
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  5. 156
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  6. 4
      apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex
  8. 13
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  9. 2
      apps/block_scout_web/mix.exs
  10. 29
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex
  11. 2
      apps/ethereum_jsonrpc/mix.exs
  12. 129
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs
  13. 20
      apps/explorer/lib/explorer/chain.ex
  14. 2
      apps/explorer/lib/explorer/chain/hash/address.ex
  15. 24
      apps/explorer/lib/explorer/chain/wei.ex
  16. 2
      apps/explorer/mix.exs
  17. 102
      apps/indexer/lib/indexer/fetcher/internal_transaction.ex
  18. 4
      mix.lock

@ -89,7 +89,7 @@
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`. # set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
# #
{Credo.Check.Design.DuplicatedCode, excluded_macros: []}, {Credo.Check.Design.DuplicatedCode, false},
# You can also customize the exit_status of each check. # You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just # If you don't want TODO comments to cause `mix credo` to fail, just

@ -2,11 +2,14 @@
### Features ### Features
- [#2433](https://github.com/poanetwork/blockscout/pull/2433) - Add a functionality to try Eth RPC methods in the documentation - [#2433](https://github.com/poanetwork/blockscout/pull/2433) - Add a functionality to try Eth RPC methods in the documentation
- [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth
### Fixes ### Fixes
- [#2459](https://github.com/poanetwork/blockscout/pull/2459) - fix top addresses query
- [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB - [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB
### Chore ### Chore
- [#2432](https://github.com/poanetwork/blockscout/pull/2432) - bump credo version
- [#2457](https://github.com/poanetwork/blockscout/pull/2457) - update mix.lock - [#2457](https://github.com/poanetwork/blockscout/pull/2457) - update mix.lock
- [#2435](https://github.com/poanetwork/blockscout/pull/2435) - Replace deprecated extract-text-webpack-plugin with mini-css-extract-plugin - [#2435](https://github.com/poanetwork/blockscout/pull/2435) - Replace deprecated extract-text-webpack-plugin with mini-css-extract-plugin
- [#2450](https://github.com/poanetwork/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script - [#2450](https://github.com/poanetwork/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script

@ -124,18 +124,20 @@ defmodule BlockScoutWeb.Chain do
end end
def paging_options(%{"block_number" => block_number_string}) do def paging_options(%{"block_number" => block_number_string}) do
with {block_number, ""} <- Integer.parse(block_number_string) do case Integer.parse(block_number_string) do
[paging_options: %{@default_paging_options | key: {block_number}}] {block_number, ""} ->
else [paging_options: %{@default_paging_options | key: {block_number}}]
_ -> _ ->
[paging_options: @default_paging_options] [paging_options: @default_paging_options]
end end
end end
def paging_options(%{"index" => index_string}) when is_binary(index_string) do def paging_options(%{"index" => index_string}) when is_binary(index_string) do
with {index, ""} <- Integer.parse(index_string) do case Integer.parse(index_string) do
[paging_options: %{@default_paging_options | key: {index}}] {index, ""} ->
else [paging_options: %{@default_paging_options | key: {index}}]
_ -> _ ->
[paging_options: @default_paging_options] [paging_options: @default_paging_options]
end end
@ -174,10 +176,12 @@ defmodule BlockScoutWeb.Chain do
def split_list_by_page(list_plus_one), do: Enum.split(list_plus_one, @page_size) def split_list_by_page(list_plus_one), do: Enum.split(list_plus_one, @page_size)
defp address_from_param(param) do defp address_from_param(param) do
with {:ok, hash} <- string_to_address_hash(param) do case string_to_address_hash(param) do
find_or_insert_address_from_hash(hash) {:ok, hash} ->
else find_or_insert_address_from_hash(hash)
:error -> {:error, :not_found}
:error ->
{:error, :not_found}
end end
end end
@ -246,18 +250,22 @@ defmodule BlockScoutWeb.Chain do
end end
defp transaction_from_param(param) do defp transaction_from_param(param) do
with {:ok, hash} <- string_to_transaction_hash(param) do case string_to_transaction_hash(param) do
hash_to_transaction(hash) {:ok, hash} ->
else hash_to_transaction(hash)
:error -> {:error, :not_found}
:error ->
{:error, :not_found}
end end
end end
defp hash_string_to_block(hash_string) do defp hash_string_to_block(hash_string) do
with {:ok, hash} <- string_to_block_hash(hash_string) do case string_to_block_hash(hash_string) do
hash_to_block(hash) {:ok, hash} ->
else hash_to_block(hash)
:error -> {:error, :not_found}
:error ->
{:error, :not_found}
end end
end end
end end

@ -83,10 +83,12 @@ defmodule BlockScoutWeb.AddressTransactionController do
unprocessable_entity(conn) unprocessable_entity(conn)
{:error, :not_found} -> {:error, :not_found} ->
with {:ok, _} <- Chain.Hash.Address.validate(address_hash_string) do case Chain.Hash.Address.validate(address_hash_string) do
json(conn, %{items: [], next_page_path: ""}) {:ok, _} ->
else json(conn, %{items: [], next_page_path: ""})
{:error, _} -> not_found(conn)
_ ->
not_found(conn)
end end
end end
end end
@ -113,20 +115,22 @@ defmodule BlockScoutWeb.AddressTransactionController do
{:ok, address_hash} = Chain.string_to_address_hash(address_hash_string) {:ok, address_hash} = Chain.string_to_address_hash(address_hash_string)
address = %Chain.Address{hash: address_hash, smart_contract: nil, token: nil} address = %Chain.Address{hash: address_hash, smart_contract: nil, token: nil}
with {:ok, _} <- Chain.Hash.Address.validate(address_hash_string) do case Chain.Hash.Address.validate(address_hash_string) do
render( {:ok, _} ->
conn, render(
"index.html", conn,
address: address, "index.html",
coin_balance_status: nil, address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), coin_balance_status: nil,
filter: params["filter"], exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: 0, filter: params["filter"],
validation_count: 0, transaction_count: 0,
current_path: current_path(conn) validation_count: 0,
) current_path: current_path(conn)
else )
{:error, _} -> not_found(conn)
_ ->
not_found(conn)
end end
end end
end end

@ -11,57 +11,57 @@ defmodule BlockScoutWeb.BlockTransactionController do
alias Phoenix.View alias Phoenix.View
def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number, "type" => "JSON"} = params) do def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number, "type" => "JSON"} = params) do
with {:ok, block} <- case param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do
param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do {:ok, block} ->
full_options = full_options =
Keyword.merge( Keyword.merge(
[ [
necessity_by_association: %{ necessity_by_association: %{
:block => :optional, :block => :optional,
[created_contract_address: :names] => :optional, [created_contract_address: :names] => :optional,
[from_address: :names] => :required, [from_address: :names] => :required,
[to_address: :names] => :optional [to_address: :names] => :optional
} }
], ],
paging_options(params) paging_options(params)
) )
transactions_plus_one = Chain.block_to_transactions(block.hash, full_options)
{transactions, next_page} = split_list_by_page(transactions_plus_one)
next_page_path =
case next_page_params(next_page, transactions, params) do
nil ->
nil
next_page_params -> transactions_plus_one = Chain.block_to_transactions(block.hash, full_options)
block_transaction_path(
conn, {transactions, next_page} = split_list_by_page(transactions_plus_one)
:index,
block, next_page_path =
Map.delete(next_page_params, "type") case next_page_params(next_page, transactions, params) do
nil ->
nil
next_page_params ->
block_transaction_path(
conn,
:index,
block,
Map.delete(next_page_params, "type")
)
end
items =
transactions
|> Enum.map(fn transaction ->
View.render_to_string(
TransactionView,
"_tile.html",
transaction: transaction
) )
end end)
items = json(
transactions conn,
|> Enum.map(fn transaction -> %{
View.render_to_string( items: items,
TransactionView, next_page_path: next_page_path
"_tile.html", }
transaction: transaction )
)
end)
json(
conn,
%{
items: items,
next_page_path: next_page_path
}
)
else
{:error, {:invalid, :hash}} -> {:error, {:invalid, :hash}} ->
not_found(conn) not_found(conn)
@ -80,25 +80,25 @@ defmodule BlockScoutWeb.BlockTransactionController do
end end
def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number}) do def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number}) do
with {:ok, block} <- case param_block_hash_or_number_to_block(formatted_block_hash_or_number,
param_block_hash_or_number_to_block(formatted_block_hash_or_number, necessity_by_association: %{
necessity_by_association: %{ [miner: :names] => :required,
[miner: :names] => :required, :uncles => :optional,
:uncles => :optional, :nephews => :optional,
:nephews => :optional, :rewards => :optional
:rewards => :optional }
} ) do
) do {:ok, block} ->
block_transaction_count = Chain.block_to_transaction_count(block.hash) block_transaction_count = Chain.block_to_transaction_count(block.hash)
render( render(
conn, conn,
"index.html", "index.html",
block: block, block: block,
block_transaction_count: block_transaction_count, block_transaction_count: block_transaction_count,
current_path: current_path(conn) current_path: current_path(conn)
) )
else
{:error, {:invalid, :hash}} -> {:error, {:invalid, :hash}} ->
not_found(conn) not_found(conn)
@ -117,19 +117,23 @@ defmodule BlockScoutWeb.BlockTransactionController do
end end
defp param_block_hash_or_number_to_block("0x" <> _ = param, options) do defp param_block_hash_or_number_to_block("0x" <> _ = param, options) do
with {:ok, hash} <- string_to_block_hash(param) do case string_to_block_hash(param) do
hash_to_block(hash, options) {:ok, hash} ->
else hash_to_block(hash, options)
:error -> {:error, {:invalid, :hash}}
:error ->
{:error, {:invalid, :hash}}
end end
end end
defp param_block_hash_or_number_to_block(number_string, options) defp param_block_hash_or_number_to_block(number_string, options)
when is_binary(number_string) do when is_binary(number_string) do
with {:ok, number} <- BlockScoutWeb.Chain.param_to_block_number(number_string) do case BlockScoutWeb.Chain.param_to_block_number(number_string) do
number_to_block(number, options) {:ok, number} ->
else number_to_block(number, options)
{:error, :invalid} -> {:error, {:invalid, :number}}
{:error, :invalid} ->
{:error, {:invalid, :number}}
end end
end end

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
def show(conn, _params) do def show(conn, _params) do
with true <- ajax?(conn) do if ajax?(conn) do
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
recent_market_history = Market.fetch_recent_history() recent_market_history = Market.fetch_recent_history()
@ -24,7 +24,7 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do
supply_data: available_supply(Chain.supply_for_days(), exchange_rate) supply_data: available_supply(Chain.supply_for_days(), exchange_rate)
}) })
else else
_ -> unprocessable_entity(conn) unprocessable_entity(conn)
end end
end end

@ -6,7 +6,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do
alias Phoenix.View alias Phoenix.View
def index(conn, _params) do def index(conn, _params) do
with true <- ajax?(conn) do if ajax?(conn) do
recent_transactions = recent_transactions =
Chain.recent_collated_transactions( Chain.recent_collated_transactions(
necessity_by_association: %{ necessity_by_association: %{
@ -29,7 +29,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do
json(conn, %{transactions: transactions}) json(conn, %{transactions: transactions})
else else
_ -> unprocessable_entity(conn) unprocessable_entity(conn)
end end
end end
end end

@ -35,12 +35,13 @@ defmodule BlockScoutWeb.SmartContractController do
def show(conn, params) do def show(conn, params) do
with true <- ajax?(conn), with true <- ajax?(conn),
{:ok, address_hash} <- Chain.string_to_address_hash(params["id"]), {:ok, address_hash} <- Chain.string_to_address_hash(params["id"]),
:ok <- Chain.check_contract_address_exists(address_hash), :ok <- Chain.check_contract_address_exists(address_hash) do
outputs = outputs =
Reader.query_function( Reader.query_function(
address_hash, address_hash,
%{name: params["function_name"], args: params["args"]} %{name: params["function_name"], args: params["args"]}
) do )
conn conn
|> put_status(200) |> put_status(200)
|> put_layout(false) |> put_layout(false)

@ -71,7 +71,7 @@ defmodule BlockScoutWeb.Mixfile do
{:bypass, "~> 1.0", only: :test}, {:bypass, "~> 1.0", only: :test},
# To add (CORS)(https://www.w3.org/TR/cors/) # To add (CORS)(https://www.w3.org/TR/cors/)
{:cors_plug, "~> 2.0"}, {:cors_plug, "~> 2.0"},
{:credo, "1.0.0", only: :test, runtime: false}, {:credo, "~> 1.1", only: :test, runtime: false},
# For Absinthe to load data in batches # For Absinthe to load data in batches
{:dataloader, "~> 1.0.0"}, {:dataloader, "~> 1.0.0"},
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},

@ -5,7 +5,7 @@ defmodule EthereumJSONRPC.Geth do
import EthereumJSONRPC, only: [id_to_params: 1, integer_to_quantity: 1, json_rpc: 2, request: 1] import EthereumJSONRPC, only: [id_to_params: 1, integer_to_quantity: 1, json_rpc: 2, request: 1]
alias EthereumJSONRPC.{FetchedBalance, FetchedCode} alias EthereumJSONRPC.{FetchedBalance, FetchedCode, Transactions}
alias EthereumJSONRPC.Geth.{Calls, Tracer} alias EthereumJSONRPC.Geth.{Calls, Tracer}
@behaviour EthereumJSONRPC.Variant @behaviour EthereumJSONRPC.Variant
@ -46,12 +46,31 @@ defmodule EthereumJSONRPC.Geth do
def fetch_block_internal_transactions(_block_range, _json_rpc_named_arguments), do: :ignore def fetch_block_internal_transactions(_block_range, _json_rpc_named_arguments), do: :ignore
@doc """ @doc """
Pending transaction fetching is not supported currently for Geth. Fetches the pending transactions from the Geth node.
To signal to the caller that fetching is not supported, `:ignore` is returned.
""" """
@impl EthereumJSONRPC.Variant @impl EthereumJSONRPC.Variant
def fetch_pending_transactions(_json_rpc_named_arguments), do: :ignore def fetch_pending_transactions(json_rpc_named_arguments) do
with {:ok, transaction_data} <-
%{id: 1, method: "txpool_content", params: []} |> request() |> json_rpc(json_rpc_named_arguments) do
transactions_params =
transaction_data["pending"]
|> Enum.flat_map(fn {_address, nonce_transactions_map} ->
nonce_transactions_map
|> Enum.map(fn {_nonce, transaction} ->
transaction
end)
end)
|> Transactions.to_elixir()
|> Transactions.elixir_to_params()
|> Enum.map(fn params ->
# txpool_content always returns transaction with 0x0000000000000000000000000000000000000000000000000000000000000000 value in block hash and index is null.
# https://github.com/ethereum/go-ethereum/issues/19897
%{params | block_hash: nil, index: nil}
end)
{:ok, transactions_params}
end
end
defp debug_trace_transaction_requests(id_to_params) when is_map(id_to_params) do defp debug_trace_transaction_requests(id_to_params) when is_map(id_to_params) do
Enum.map(id_to_params, fn {id, %{hash_data: hash_data}} -> Enum.map(id_to_params, fn {id, %{hash_data: hash_data}} ->

@ -64,7 +64,7 @@ defmodule EthereumJsonrpc.MixProject do
# WebSocket-server for testing `EthereumJSONRPC.WebSocket.WebSocketClient`. # WebSocket-server for testing `EthereumJSONRPC.WebSocket.WebSocketClient`.
{:cowboy, "~> 2.0", only: [:dev, :test]}, {:cowboy, "~> 2.0", only: [:dev, :test]},
# Style Checking # Style Checking
{:credo, "1.0.0", only: :test, runtime: false}, {:credo, "~> 1.1", only: :test, runtime: false},
# Static Type Checking # Static Type Checking
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
# Code coverage # Code coverage

@ -84,8 +84,133 @@ defmodule EthereumJSONRPC.GethTest do
end end
describe "fetch_pending_transactions/1" do describe "fetch_pending_transactions/1" do
test "is not supported", %{json_rpc_named_arguments: json_rpc_named_arguments} do @tag :no_geth
EthereumJSONRPC.Geth.fetch_pending_transactions(json_rpc_named_arguments) test "fetches pending transactions", %{json_rpc_named_arguments: json_rpc_named_arguments} do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ ->
{:ok,
%{
"pending" => %{
"0xC99f4e9cFf697ca6717ad9cE8bA4A138e0e55109" => %{
"4656" => %{
"blockHash" => "0x0000000000000000000000000000000000000000000000000000000000000000",
"blockNumber" => nil,
"from" => "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
"gas" => "0x3d0900",
"gasPrice" => "0x3b9aca00",
"hash" => "0x2b8cfd76a31b942e51b6265c791c860e2840b11f8c2fcfa1c9dfe53dea4c3102",
"input" =>
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030af6932dec7c4eaf4b966059e74cc7a1767ba93e62f2d83a7dba5bb785b6efd25e8ab7d2e8798e7ecc27df96380d77a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000b29e5229b86fbb3a8e45e406b64226c3d49389804a6f7449325fae232d6623000000000000000000000000000000000000000000000000000000000000006097e4c1ed443f430b1d8ad66e565a960fade76e3e177b4120186bdad2fcfa43e134de3abdc0272c9433af94833fec73260c261cf41422e83d958787b62144478bc44ab84d1ddba7a462d355057f3be8ab914a195ac1a637c4fb8503c441dadb45",
"nonce" => "0x1230",
"r" => "0x81345ae149171f4cb4ab868f0ad637d033c96c4659b190b86a39725c8299c947",
"s" => "0x31450678841d7206fa02b564a641420262cc98c8ea0e32c4cb0e97208d3f9feb",
"to" => "0xf003a84d6890202663c0fd80954e836fcf21e004",
"transactionIndex" => "0x0",
"v" => "0x1b",
"value" => "0xb5e620f480000"
},
"4657" => %{
"blockHash" => "0x0000000000000000000000000000000000000000000000000000000000000000",
"blockNumber" => nil,
"from" => "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
"gas" => "0x3d0900",
"gasPrice" => "0x3b9aca00",
"hash" => "0x7c3ea924740e996bf552a8dded903ba4258b69d30bf5e6dca6ec86ebc60b8151",
"input" =>
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030a25723bca32f88a73abc7eb153cee248effd563d87efe12e08e8a33f74047afc28c30ab9c74bddeb6f0558628b8bf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020009c56025b2796cdc72f91836278a471590b774462adddd1c87a0b6f84b787990000000000000000000000000000000000000000000000000000000000000060aa53b46c8b57aed7c4c0fdf3f650ec3bb330591929bc813610656882e3203157c22b50d0d0b0316a8712c00fe4f0e0c509613114f5d24c0419a4e8188f2489678b05dccf72a67957785e8e250092c8787f049f7e20b1414a633595a56c98ff82",
"nonce" => "0x1231",
"r" => "0xee1eb895262d12ef5c4ee3cbf9b36de3903bc3a1343f0a312bd19edacc4bb877",
"s" => "0xfcb87efe4c3984a3e1d3f4fb10ce41e59f65e21fbd9206a1648ec73fa0a2206",
"to" => "0xf003a84d6890202663c0fd80954e836fcf21e004",
"transactionIndex" => "0x0",
"v" => "0x1b",
"value" => "0xb5e620f480000"
},
"4658" => %{
"blockHash" => "0x0000000000000000000000000000000000000000000000000000000000000000",
"blockNumber" => nil,
"from" => "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
"gas" => "0x3d0900",
"gasPrice" => "0x3b9aca00",
"hash" => "0xe699a58ef4986f2dbdc102acf73b35392aff9ce43fd226000526955e19c0b06e",
"input" =>
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000308eb3ed9e686f6bc1fe2d8ce3fea37fb3a66a9c67b91ef15ba6bd7da0eed73288f72577edea2b7ded5855ca8a56b1e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000051afe6c51e2175a62afbd66d293e8a7509943d5cd6f851f59923a61a186e80000000000000000000000000000000000000000000000000000000000000060a063498e8db2e75e0a193de89ad2947111d677c9501e75c34a64fcee8fe5a7c7607929fc6bce943d64f1039e1d1f325f02d1e5d71f86ca976c9ab79d19f0fd0e530a5210fbe131087ba1f1b3c92abc4a0dd7c8a47c3c276fac3e09bca964fd74",
"nonce" => "0x1232",
"r" => "0xe95bc86fc32cc591677c7ec9ca49f1dc33a31427235c1c41dbb7a3a957b55599",
"s" => "0xe8b41a6440d0fe6d0ec1f40982394a2d641b19b983aad49e45614e5f3a1abc9",
"to" => "0xf003a84d6890202663c0fd80954e836fcf21e004",
"transactionIndex" => "0x0",
"v" => "0x1c",
"value" => "0xb5e620f480000"
}
}
},
"queued" => %{}
}}
end)
assert {:ok,
[
%{
block_hash: nil,
block_number: nil,
from_address_hash: "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
gas: 4_000_000,
gas_price: 1_000_000_000,
hash: "0x2b8cfd76a31b942e51b6265c791c860e2840b11f8c2fcfa1c9dfe53dea4c3102",
index: 0,
input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030af6932dec7c4eaf4b966059e74cc7a1767ba93e62f2d83a7dba5bb785b6efd25e8ab7d2e8798e7ecc27df96380d77a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000b29e5229b86fbb3a8e45e406b64226c3d49389804a6f7449325fae232d6623000000000000000000000000000000000000000000000000000000000000006097e4c1ed443f430b1d8ad66e565a960fade76e3e177b4120186bdad2fcfa43e134de3abdc0272c9433af94833fec73260c261cf41422e83d958787b62144478bc44ab84d1ddba7a462d355057f3be8ab914a195ac1a637c4fb8503c441dadb45",
nonce: 4656,
r:
58_440_860_745_466_360_584_510_362_592_650_991_653_332_571_230_597_223_185_413_246_840_900_756_818_247,
s:
22_285_286_687_634_777_993_513_656_263_235_057_426_117_768_584_265_280_722_872_863_042_386_096_267_243,
to_address_hash: "0xf003a84d6890202663c0fd80954e836fcf21e004",
transaction_index: 0,
v: 27,
value: 3_200_000_000_000_000
},
%{
block_hash: nil,
block_number: nil,
from_address_hash: "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
gas: 4_000_000,
gas_price: 1_000_000_000,
hash: "0x7c3ea924740e996bf552a8dded903ba4258b69d30bf5e6dca6ec86ebc60b8151",
index: 0,
input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030a25723bca32f88a73abc7eb153cee248effd563d87efe12e08e8a33f74047afc28c30ab9c74bddeb6f0558628b8bf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020009c56025b2796cdc72f91836278a471590b774462adddd1c87a0b6f84b787990000000000000000000000000000000000000000000000000000000000000060aa53b46c8b57aed7c4c0fdf3f650ec3bb330591929bc813610656882e3203157c22b50d0d0b0316a8712c00fe4f0e0c509613114f5d24c0419a4e8188f2489678b05dccf72a67957785e8e250092c8787f049f7e20b1414a633595a56c98ff82",
nonce: 4657,
r:
107_704_737_317_141_024_268_971_404_113_297_355_261_066_880_504_936_960_891_977_784_149_226_505_877_623,
s:
7_144_300_886_174_743_587_831_226_472_052_852_957_529_607_874_128_062_849_708_955_356_153_894_281_734,
to_address_hash: "0xf003a84d6890202663c0fd80954e836fcf21e004",
transaction_index: 0,
v: 27,
value: 3_200_000_000_000_000
},
%{
block_hash: nil,
block_number: nil,
from_address_hash: "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
gas: 4_000_000,
gas_price: 1_000_000_000,
hash: "0xe699a58ef4986f2dbdc102acf73b35392aff9ce43fd226000526955e19c0b06e",
index: 0,
input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000308eb3ed9e686f6bc1fe2d8ce3fea37fb3a66a9c67b91ef15ba6bd7da0eed73288f72577edea2b7ded5855ca8a56b1e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000051afe6c51e2175a62afbd66d293e8a7509943d5cd6f851f59923a61a186e80000000000000000000000000000000000000000000000000000000000000060a063498e8db2e75e0a193de89ad2947111d677c9501e75c34a64fcee8fe5a7c7607929fc6bce943d64f1039e1d1f325f02d1e5d71f86ca976c9ab79d19f0fd0e530a5210fbe131087ba1f1b3c92abc4a0dd7c8a47c3c276fac3e09bca964fd74",
nonce: 4658,
r:
105_551_060_165_173_654_536_466_245_809_705_255_348_773_503_447_188_823_324_699_103_004_494_755_354_009,
s:
6_578_424_718_200_222_268_891_012_570_118_685_130_111_416_504_340_507_122_286_266_818_507_627_932_617,
to_address_hash: "0xf003a84d6890202663c0fd80954e836fcf21e004",
transaction_index: 0,
v: 28,
value: 3_200_000_000_000_000
}
]} = Geth.fetch_pending_transactions(json_rpc_named_arguments)
end end
end end
end end

@ -2464,9 +2464,10 @@ defmodule Explorer.Chain do
|> Multi.run(:set_address_verified, &set_address_verified/2) |> Multi.run(:set_address_verified, &set_address_verified/2)
|> Repo.transaction() |> Repo.transaction()
with {:ok, %{smart_contract: smart_contract}} <- insert_result do case insert_result do
{:ok, smart_contract} {:ok, %{smart_contract: smart_contract}} ->
else {:ok, smart_contract}
{:error, :smart_contract, changeset, _} -> {:error, :smart_contract, changeset, _} ->
{:error, changeset} {:error, changeset}
@ -2601,7 +2602,11 @@ defmodule Explorer.Chain do
defp page_addresses(query, %PagingOptions{key: nil}), do: query defp page_addresses(query, %PagingOptions{key: nil}), do: query
defp page_addresses(query, %PagingOptions{key: {coin_balance, hash}}) do defp page_addresses(query, %PagingOptions{key: {coin_balance, hash}}) do
where(query, [address], address.fetched_coin_balance <= ^coin_balance and address.hash > ^hash) from(address in query,
where:
(address.fetched_coin_balance == ^coin_balance and address.hash > ^hash) or
address.fetched_coin_balance < ^coin_balance
)
end end
defp page_blocks(query, %PagingOptions{key: nil}), do: query defp page_blocks(query, %PagingOptions{key: nil}), do: query
@ -2936,9 +2941,10 @@ defmodule Explorer.Chain do
) )
|> Repo.transaction() |> Repo.transaction()
with {:ok, %{token: token}} <- insert_result do case insert_result do
{:ok, token} {:ok, %{token: token}} ->
else {:ok, token}
{:error, :token, changeset, _} -> {:error, :token, changeset, _} ->
{:error, changeset} {:error, changeset}
end end

@ -166,7 +166,7 @@ defmodule Explorer.Chain.Hash.Address do
iex> Explorer.Chain.Hash.Address.validate("0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232H") iex> Explorer.Chain.Hash.Address.validate("0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232H")
{:error, :invalid_characters} {:error, :invalid_characters}
""" """
@spec validate(String.t()) :: {:ok, String.t()} | {:error, :invalid_length | :invalid_characters, :invalid_checksum} @spec validate(String.t()) :: {:ok, String.t()} | {:error, :invalid_length | :invalid_characters | :invalid_checksum}
def validate("0x" <> hash) do def validate("0x" <> hash) do
with {:length, true} <- {:length, String.length(hash) == 40}, with {:length, true} <- {:length, String.length(hash) == 40},
{:hex, true} <- {:hex, is_hex?(hash)}, {:hex, true} <- {:hex, is_hex?(hash)},

@ -31,21 +31,25 @@ defmodule Explorer.Chain.Wei do
@impl Ecto.Type @impl Ecto.Type
def cast("0x" <> hex_wei) do def cast("0x" <> hex_wei) do
with {int_wei, ""} <- Integer.parse(hex_wei, 16) do case Integer.parse(hex_wei, 16) do
decimal = Decimal.new(int_wei) {int_wei, ""} ->
{:ok, %__MODULE__{value: decimal}} decimal = Decimal.new(int_wei)
else {:ok, %__MODULE__{value: decimal}}
_ -> :error
_ ->
:error
end end
end end
@impl Ecto.Type @impl Ecto.Type
def cast(string_wei) when is_binary(string_wei) do def cast(string_wei) when is_binary(string_wei) do
with {int_wei, ""} <- Integer.parse(string_wei) do case Integer.parse(string_wei) do
decimal = Decimal.new(int_wei) {int_wei, ""} ->
{:ok, %__MODULE__{value: decimal}} decimal = Decimal.new(int_wei)
else {:ok, %__MODULE__{value: decimal}}
_ -> :error
_ ->
:error
end end
end end

@ -68,7 +68,7 @@ defmodule Explorer.Mixfile do
{:bypass, "~> 1.0", only: :test}, {:bypass, "~> 1.0", only: :test},
{:briefly, "~> 0.4", github: "CargoSense/briefly"}, {:briefly, "~> 0.4", github: "CargoSense/briefly"},
{:comeonin, "~> 4.0"}, {:comeonin, "~> 4.0"},
{:credo, "1.0.0", only: :test, runtime: false}, {:credo, "~> 1.1", only: :test, runtime: false},
# For Absinthe to load data in batches # For Absinthe to load data in batches
{:dataloader, "~> 1.0.0"}, {:dataloader, "~> 1.0.0"},
{:decimal, "~> 1.0"}, {:decimal, "~> 1.0"},

@ -152,12 +152,6 @@ defmodule Indexer.Fetcher.InternalTransaction do
unique_entries = unique_entries(entries, variant) unique_entries = unique_entries(entries, variant)
internal_transactions_indexed_at_blocks =
case variant do
EthereumJSONRPC.Parity -> Enum.map(unique_entries, &block_params/1)
_ -> []
end
unique_entries_count = Enum.count(unique_entries) unique_entries_count = Enum.count(unique_entries)
Logger.metadata(count: unique_entries_count) Logger.metadata(count: unique_entries_count)
@ -176,47 +170,7 @@ defmodule Indexer.Fetcher.InternalTransaction do
end end
|> case do |> case do
{:ok, internal_transactions_params} -> {:ok, internal_transactions_params} ->
internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params) import_internal_transaction(internal_transactions_params, json_rpc_named_arguments, unique_entries)
addresses_params =
Addresses.extract_addresses(%{
internal_transactions: internal_transactions_params_without_failed_creations
})
address_hash_to_block_number =
Enum.into(addresses_params, %{}, fn %{fetched_coin_balance_block_number: block_number, hash: hash} ->
{hash, block_number}
end)
with {:ok, imported} <-
Chain.import(%{
addresses: %{params: addresses_params},
internal_transactions: %{params: internal_transactions_params_without_failed_creations},
internal_transactions_indexed_at_blocks: %{
params: internal_transactions_indexed_at_blocks,
with: :number_only_changeset
},
timeout: :infinity
}) do
async_import_coin_balances(imported, %{
address_hash_to_fetched_balance_block_number: address_hash_to_block_number
})
else
{:error, step, reason, _changes_so_far} ->
Logger.error(
fn ->
[
"failed to import internal transactions for transactions: ",
inspect(reason)
]
end,
step: step,
error_count: unique_entries_count
)
# re-queue the de-duped entries
{:retry, unique_entries}
end
{:error, reason} -> {:error, reason} ->
Logger.error(fn -> ["failed to fetch internal transactions for transactions: ", inspect(reason)] end, Logger.error(fn -> ["failed to fetch internal transactions for transactions: ", inspect(reason)] end,
@ -231,6 +185,60 @@ defmodule Indexer.Fetcher.InternalTransaction do
end end
end end
defp import_internal_transaction(internal_transactions_params, json_rpc_named_arguments, unique_entries) do
internal_transactions_indexed_at_blocks =
case Keyword.fetch!(json_rpc_named_arguments, :variant) do
EthereumJSONRPC.Parity -> Enum.map(unique_entries, &block_params/1)
_ -> []
end
unique_entries_count = Enum.count(unique_entries)
internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params)
addresses_params =
Addresses.extract_addresses(%{
internal_transactions: internal_transactions_params_without_failed_creations
})
address_hash_to_block_number =
Enum.into(addresses_params, %{}, fn %{fetched_coin_balance_block_number: block_number, hash: hash} ->
{hash, block_number}
end)
imports =
Chain.import(%{
addresses: %{params: addresses_params},
internal_transactions: %{params: internal_transactions_params_without_failed_creations},
internal_transactions_indexed_at_blocks: %{
params: internal_transactions_indexed_at_blocks,
with: :number_only_changeset
},
timeout: :infinity
})
case imports do
{:ok, imported} ->
async_import_coin_balances(imported, %{
address_hash_to_fetched_balance_block_number: address_hash_to_block_number
})
{:error, step, reason, _changes_so_far} ->
Logger.error(
fn ->
[
"failed to import internal transactions for transactions: ",
inspect(reason)
]
end,
step: step,
error_count: unique_entries_count
)
# re-queue the de-duped entries
{:retry, unique_entries}
end
end
defp unique_entries(entries, EthereumJSONRPC.Parity), do: Enum.uniq(entries) defp unique_entries(entries, EthereumJSONRPC.Parity), do: Enum.uniq(entries)
# Protection and improved reporting for https://github.com/poanetwork/blockscout/issues/289 # Protection and improved reporting for https://github.com/poanetwork/blockscout/issues/289

@ -22,11 +22,11 @@
"cors_plug": {:hex, :cors_plug, "2.0.0", "238ddb479f92b38f6dc1ae44b8d81f0387f9519101a6da442d543ab70ee0e482", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "cors_plug": {:hex, :cors_plug, "2.0.0", "238ddb479f92b38f6dc1ae44b8d81f0387f9519101a6da442d543ab70ee0e482", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, "cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"}, "cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"},
"credo": {:hex, :credo, "1.0.0", "aaa40fdd0543a0cf8080e8c5949d8c25f0a24e4fc8c1d83d06c388f5e5e0ea42", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "credo": {:hex, :credo, "1.1.2", "02b6422f3e659eb74b05aca3c20c1d8da0119a05ee82577a82e6c2938bf29f81", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"csv": {:hex, :csv, "2.1.1", "a4c1a7c30d2151b6e4976cb2f52c0a1d49ec965afb737ed84a684bc4284d1627", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, optional: false]}]}, "csv": {:hex, :csv, "2.1.1", "a4c1a7c30d2151b6e4976cb2f52c0a1d49ec965afb737ed84a684bc4284d1627", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, optional: false]}]},
"dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"db_connection": {:hex, :db_connection, "2.0.3", "b4e8aa43c100e16f122ccd6798cd51c48c79fd391c39d411f42b3cd765daccb0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "db_connection": {:hex, :db_connection, "2.0.3", "b4e8aa43c100e16f122ccd6798cd51c48c79fd391c39d411f42b3cd765daccb0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"}, "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
"decorator": {:hex, :decorator, "1.3.0", "6203dbd6e4e519a21a079e2a74e50fddaf03e80be22711b92eb4cd410173abea", [:mix], [], "hexpm"}, "decorator": {:hex, :decorator, "1.3.0", "6203dbd6e4e519a21a079e2a74e50fddaf03e80be22711b92eb4cd410173abea", [:mix], [], "hexpm"},
"deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"}, "deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], []}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], []},

Loading…
Cancel
Save