diff --git a/.credo.exs b/.credo.exs index ab3359abb1..9f69aecd10 100644 --- a/.credo.exs +++ b/.credo.exs @@ -89,7 +89,7 @@ # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just # 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. # If you don't want TODO comments to cause `mix credo` to fail, just diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8924b70f..37d0e0a573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,14 @@ ### Features - [#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 +- [#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 ### Chore +- [#2432](https://github.com/poanetwork/blockscout/pull/2432) - bump credo version - [#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 - [#2450](https://github.com/poanetwork/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index 9e82c30023..b1a3c3ec19 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -124,18 +124,20 @@ defmodule BlockScoutWeb.Chain do end def paging_options(%{"block_number" => block_number_string}) do - with {block_number, ""} <- Integer.parse(block_number_string) do - [paging_options: %{@default_paging_options | key: {block_number}}] - else + case Integer.parse(block_number_string) do + {block_number, ""} -> + [paging_options: %{@default_paging_options | key: {block_number}}] + _ -> [paging_options: @default_paging_options] end end def paging_options(%{"index" => index_string}) when is_binary(index_string) do - with {index, ""} <- Integer.parse(index_string) do - [paging_options: %{@default_paging_options | key: {index}}] - else + case Integer.parse(index_string) do + {index, ""} -> + [paging_options: %{@default_paging_options | key: {index}}] + _ -> [paging_options: @default_paging_options] 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) defp address_from_param(param) do - with {:ok, hash} <- string_to_address_hash(param) do - find_or_insert_address_from_hash(hash) - else - :error -> {:error, :not_found} + case string_to_address_hash(param) do + {:ok, hash} -> + find_or_insert_address_from_hash(hash) + + :error -> + {:error, :not_found} end end @@ -246,18 +250,22 @@ defmodule BlockScoutWeb.Chain do end defp transaction_from_param(param) do - with {:ok, hash} <- string_to_transaction_hash(param) do - hash_to_transaction(hash) - else - :error -> {:error, :not_found} + case string_to_transaction_hash(param) do + {:ok, hash} -> + hash_to_transaction(hash) + + :error -> + {:error, :not_found} end end defp hash_string_to_block(hash_string) do - with {:ok, hash} <- string_to_block_hash(hash_string) do - hash_to_block(hash) - else - :error -> {:error, :not_found} + case string_to_block_hash(hash_string) do + {:ok, hash} -> + hash_to_block(hash) + + :error -> + {:error, :not_found} end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex index 47100b972e..f30c3cb0cd 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -83,10 +83,12 @@ defmodule BlockScoutWeb.AddressTransactionController do unprocessable_entity(conn) {:error, :not_found} -> - with {:ok, _} <- Chain.Hash.Address.validate(address_hash_string) do - json(conn, %{items: [], next_page_path: ""}) - else - {:error, _} -> not_found(conn) + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + json(conn, %{items: [], next_page_path: ""}) + + _ -> + not_found(conn) end end end @@ -113,20 +115,22 @@ defmodule BlockScoutWeb.AddressTransactionController do {:ok, address_hash} = Chain.string_to_address_hash(address_hash_string) address = %Chain.Address{hash: address_hash, smart_contract: nil, token: nil} - with {:ok, _} <- Chain.Hash.Address.validate(address_hash_string) do - render( - conn, - "index.html", - address: address, - coin_balance_status: nil, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), - filter: params["filter"], - transaction_count: 0, - validation_count: 0, - current_path: current_path(conn) - ) - else - {:error, _} -> not_found(conn) + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + render( + conn, + "index.html", + address: address, + coin_balance_status: nil, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + transaction_count: 0, + validation_count: 0, + current_path: current_path(conn) + ) + + _ -> + not_found(conn) end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex index a1e4cbeb09..dd362cd065 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex @@ -11,57 +11,57 @@ defmodule BlockScoutWeb.BlockTransactionController do alias Phoenix.View def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number, "type" => "JSON"} = params) do - with {:ok, block} <- - param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do - full_options = - Keyword.merge( - [ - necessity_by_association: %{ - :block => :optional, - [created_contract_address: :names] => :optional, - [from_address: :names] => :required, - [to_address: :names] => :optional - } - ], - 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 + case param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do + {:ok, block} -> + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :required, + [to_address: :names] => :optional + } + ], + paging_options(params) + ) - next_page_params -> - block_transaction_path( - conn, - :index, - block, - Map.delete(next_page_params, "type") + 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 -> + 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 - - items = - transactions - |> Enum.map(fn transaction -> - View.render_to_string( - TransactionView, - "_tile.html", - transaction: transaction - ) - end) - - json( - conn, - %{ - items: items, - next_page_path: next_page_path - } - ) - else + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + {:error, {:invalid, :hash}} -> not_found(conn) @@ -80,25 +80,25 @@ defmodule BlockScoutWeb.BlockTransactionController do end def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number}) do - with {:ok, block} <- - param_block_hash_or_number_to_block(formatted_block_hash_or_number, - necessity_by_association: %{ - [miner: :names] => :required, - :uncles => :optional, - :nephews => :optional, - :rewards => :optional - } - ) do - block_transaction_count = Chain.block_to_transaction_count(block.hash) - - render( - conn, - "index.html", - block: block, - block_transaction_count: block_transaction_count, - current_path: current_path(conn) - ) - else + case param_block_hash_or_number_to_block(formatted_block_hash_or_number, + necessity_by_association: %{ + [miner: :names] => :required, + :uncles => :optional, + :nephews => :optional, + :rewards => :optional + } + ) do + {:ok, block} -> + block_transaction_count = Chain.block_to_transaction_count(block.hash) + + render( + conn, + "index.html", + block: block, + block_transaction_count: block_transaction_count, + current_path: current_path(conn) + ) + {:error, {:invalid, :hash}} -> not_found(conn) @@ -117,19 +117,23 @@ defmodule BlockScoutWeb.BlockTransactionController do end defp param_block_hash_or_number_to_block("0x" <> _ = param, options) do - with {:ok, hash} <- string_to_block_hash(param) do - hash_to_block(hash, options) - else - :error -> {:error, {:invalid, :hash}} + case string_to_block_hash(param) do + {:ok, hash} -> + hash_to_block(hash, options) + + :error -> + {:error, {:invalid, :hash}} end end defp param_block_hash_or_number_to_block(number_string, options) when is_binary(number_string) do - with {:ok, number} <- BlockScoutWeb.Chain.param_to_block_number(number_string) do - number_to_block(number, options) - else - {:error, :invalid} -> {:error, {:invalid, :number}} + case BlockScoutWeb.Chain.param_to_block_number(number_string) do + {:ok, number} -> + number_to_block(number, options) + + {:error, :invalid} -> + {:error, {:invalid, :number}} end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex index a89728949c..238f858769 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex @@ -5,7 +5,7 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do alias Explorer.ExchangeRates.Token def show(conn, _params) do - with true <- ajax?(conn) do + if ajax?(conn) do exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() 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) }) else - _ -> unprocessable_entity(conn) + unprocessable_entity(conn) end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex index b001093c6e..a98e380e67 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex @@ -6,7 +6,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do alias Phoenix.View def index(conn, _params) do - with true <- ajax?(conn) do + if ajax?(conn) do recent_transactions = Chain.recent_collated_transactions( necessity_by_association: %{ @@ -29,7 +29,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do json(conn, %{transactions: transactions}) else - _ -> unprocessable_entity(conn) + unprocessable_entity(conn) end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex index ff0a4e67aa..16b0122c5b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -35,12 +35,13 @@ defmodule BlockScoutWeb.SmartContractController do def show(conn, params) do with true <- ajax?(conn), {:ok, address_hash} <- Chain.string_to_address_hash(params["id"]), - :ok <- Chain.check_contract_address_exists(address_hash), - outputs = - Reader.query_function( - address_hash, - %{name: params["function_name"], args: params["args"]} - ) do + :ok <- Chain.check_contract_address_exists(address_hash) do + outputs = + Reader.query_function( + address_hash, + %{name: params["function_name"], args: params["args"]} + ) + conn |> put_status(200) |> put_layout(false) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 623aba2089..819a9d7adb 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -71,7 +71,7 @@ defmodule BlockScoutWeb.Mixfile do {:bypass, "~> 1.0", only: :test}, # To add (CORS)(https://www.w3.org/TR/cors/) {: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 {:dataloader, "~> 1.0.0"}, {:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false}, diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex index ab725c9c73..e0a6672be8 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex @@ -5,7 +5,7 @@ defmodule EthereumJSONRPC.Geth do 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} @behaviour EthereumJSONRPC.Variant @@ -46,12 +46,31 @@ defmodule EthereumJSONRPC.Geth do def fetch_block_internal_transactions(_block_range, _json_rpc_named_arguments), do: :ignore @doc """ - Pending transaction fetching is not supported currently for Geth. - - To signal to the caller that fetching is not supported, `:ignore` is returned. + Fetches the pending transactions from the Geth node. """ @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 Enum.map(id_to_params, fn {id, %{hash_data: hash_data}} -> diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 58dcff8fd5..e3949457e9 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -64,7 +64,7 @@ defmodule EthereumJsonrpc.MixProject do # WebSocket-server for testing `EthereumJSONRPC.WebSocket.WebSocketClient`. {:cowboy, "~> 2.0", only: [:dev, :test]}, # Style Checking - {:credo, "1.0.0", only: :test, runtime: false}, + {:credo, "~> 1.1", only: :test, runtime: false}, # Static Type Checking {:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false}, # Code coverage diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs index 626583fa01..32a7f84df6 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs @@ -84,8 +84,133 @@ defmodule EthereumJSONRPC.GethTest do end describe "fetch_pending_transactions/1" do - test "is not supported", %{json_rpc_named_arguments: json_rpc_named_arguments} do - EthereumJSONRPC.Geth.fetch_pending_transactions(json_rpc_named_arguments) + @tag :no_geth + 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 diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index c2e480957d..536cd80577 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2464,9 +2464,10 @@ defmodule Explorer.Chain do |> Multi.run(:set_address_verified, &set_address_verified/2) |> Repo.transaction() - with {:ok, %{smart_contract: smart_contract}} <- insert_result do - {:ok, smart_contract} - else + case insert_result do + {:ok, %{smart_contract: smart_contract}} -> + {:ok, smart_contract} + {:error, :smart_contract, 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: {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 defp page_blocks(query, %PagingOptions{key: nil}), do: query @@ -2936,9 +2941,10 @@ defmodule Explorer.Chain do ) |> Repo.transaction() - with {:ok, %{token: token}} <- insert_result do - {:ok, token} - else + case insert_result do + {:ok, %{token: token}} -> + {:ok, token} + {:error, :token, changeset, _} -> {:error, changeset} end diff --git a/apps/explorer/lib/explorer/chain/hash/address.ex b/apps/explorer/lib/explorer/chain/hash/address.ex index fefdf8743c..4cb1ba751f 100644 --- a/apps/explorer/lib/explorer/chain/hash/address.ex +++ b/apps/explorer/lib/explorer/chain/hash/address.ex @@ -166,7 +166,7 @@ defmodule Explorer.Chain.Hash.Address do iex> Explorer.Chain.Hash.Address.validate("0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232H") {: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 with {:length, true} <- {:length, String.length(hash) == 40}, {:hex, true} <- {:hex, is_hex?(hash)}, diff --git a/apps/explorer/lib/explorer/chain/wei.ex b/apps/explorer/lib/explorer/chain/wei.ex index c1c434aa44..78483632a7 100644 --- a/apps/explorer/lib/explorer/chain/wei.ex +++ b/apps/explorer/lib/explorer/chain/wei.ex @@ -31,21 +31,25 @@ defmodule Explorer.Chain.Wei do @impl Ecto.Type def cast("0x" <> hex_wei) do - with {int_wei, ""} <- Integer.parse(hex_wei, 16) do - decimal = Decimal.new(int_wei) - {:ok, %__MODULE__{value: decimal}} - else - _ -> :error + case Integer.parse(hex_wei, 16) do + {int_wei, ""} -> + decimal = Decimal.new(int_wei) + {:ok, %__MODULE__{value: decimal}} + + _ -> + :error end end @impl Ecto.Type def cast(string_wei) when is_binary(string_wei) do - with {int_wei, ""} <- Integer.parse(string_wei) do - decimal = Decimal.new(int_wei) - {:ok, %__MODULE__{value: decimal}} - else - _ -> :error + case Integer.parse(string_wei) do + {int_wei, ""} -> + decimal = Decimal.new(int_wei) + {:ok, %__MODULE__{value: decimal}} + + _ -> + :error end end diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index b9b887f838..b261d96d28 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -68,7 +68,7 @@ defmodule Explorer.Mixfile do {:bypass, "~> 1.0", only: :test}, {:briefly, "~> 0.4", github: "CargoSense/briefly"}, {: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 {:dataloader, "~> 1.0.0"}, {:decimal, "~> 1.0"}, diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex index 3b9746b838..7d93bbf64f 100644 --- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex +++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex @@ -152,12 +152,6 @@ defmodule Indexer.Fetcher.InternalTransaction do 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) Logger.metadata(count: unique_entries_count) @@ -176,47 +170,7 @@ defmodule Indexer.Fetcher.InternalTransaction do end |> case do {:ok, internal_transactions_params} -> - 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) - - 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 + import_internal_transaction(internal_transactions_params, json_rpc_named_arguments, unique_entries) {:error, reason} -> Logger.error(fn -> ["failed to fetch internal transactions for transactions: ", inspect(reason)] end, @@ -231,6 +185,60 @@ defmodule Indexer.Fetcher.InternalTransaction do 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) # Protection and improved reporting for https://github.com/poanetwork/blockscout/issues/289 diff --git a/mix.lock b/mix.lock index 09f449b928..111a006f53 100644 --- a/mix.lock +++ b/mix.lock @@ -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"}, "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"}, - "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]}]}, "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"}, - "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"}, "deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], []},