check if to_address_hash is contract address to fetch internal transactions

pull/1455/head
Ayrat Badykov 6 years ago
parent dc2a0e2829
commit 1616266194
No known key found for this signature in database
GPG Key ID: B44668E265E9396F
  1. 42
      apps/explorer/lib/explorer/chain.ex
  2. 70
      apps/explorer/test/explorer/chain_test.exs
  3. 9
      apps/indexer/lib/indexer/block/realtime/fetcher.ex

@ -17,6 +17,8 @@ defmodule Explorer.Chain do
where: 3 where: 3
] ]
import EthereumJSONRPC, only: [integer_to_quantity: 1]
alias Ecto.Adapters.SQL alias Ecto.Adapters.SQL
alias Ecto.{Changeset, Multi} alias Ecto.{Changeset, Multi}
@ -1868,6 +1870,46 @@ defmodule Explorer.Chain do
|> Data.to_string() |> Data.to_string()
end end
@doc """
Checks if an address is a contract
"""
@spec contract_address?(String.t(), non_neg_integer(), Keyword.t()) :: boolean() | :json_rpc_error
def contract_address?(address_hash, block_number, json_rpc_named_arguments \\ []) do
{:ok, binary_hash} = Explorer.Chain.Hash.Address.cast(address_hash)
query =
from(
address in Address,
where: address.hash == ^binary_hash
)
address = Repo.one(query)
cond do
is_nil(address) ->
block_quantity = integer_to_quantity(block_number)
case EthereumJSONRPC.fetch_codes(
[%{block_quantity: block_quantity, address: address_hash}],
json_rpc_named_arguments
) do
{:ok, %EthereumJSONRPC.FetchedCodes{params_list: fetched_codes}} ->
result = List.first(fetched_codes)
result && !(is_nil(result[:code]) || result[:code] == "" || result[:code] == "0x")
_ ->
:json_rpc_error
end
is_nil(address.contract_code) ->
false
true ->
true
end
end
@doc """ @doc """
Fetches contract creation input data. Fetches contract creation input data.
""" """

@ -1,10 +1,12 @@
defmodule Explorer.ChainTest do defmodule Explorer.ChainTest do
use Explorer.DataCase use Explorer.DataCase
use EthereumJSONRPC.Case, async: true
require Ecto.Query require Ecto.Query
import Ecto.Query import Ecto.Query
import Explorer.Factory import Explorer.Factory
import Mox
alias Explorer.{Chain, Factory, PagingOptions, Repo} alias Explorer.{Chain, Factory, PagingOptions, Repo}
@ -27,6 +29,10 @@ defmodule Explorer.ChainTest do
doctest Explorer.Chain doctest Explorer.Chain
setup :set_mox_global
setup :verify_on_exit!
describe "count_addresses_with_balance_from_cache/0" do describe "count_addresses_with_balance_from_cache/0" do
test "returns the number of addresses with fetched_coin_balance > 0" do test "returns the number of addresses with fetched_coin_balance > 0" do
insert(:address, fetched_coin_balance: 0) insert(:address, fetched_coin_balance: 0)
@ -3631,4 +3637,68 @@ defmodule Explorer.ChainTest do
assert found_creation_data == "" assert found_creation_data == ""
end end
end end
describe "contract_address?/2" do
test "returns true if address has contract code" do
code = %Data{
bytes: <<1, 2, 3, 4, 5>>
}
address = insert(:address, contract_code: code)
assert Chain.contract_address?(to_string(address.hash), 1)
end
test "returns false if address has not contract code" do
address = insert(:address)
refute Chain.contract_address?(to_string(address.hash), 1)
end
@tag :no_parity
@tag :no_geth
test "returns true if fetched code from json rpc", %{
json_rpc_named_arguments: json_rpc_named_arguments
} do
hash = "0x71300d93a8CdF93385Af9635388cF2D00b95a480"
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn _arguments, _options ->
{:ok,
[
%{
id: 0,
result: "0x0102030405"
}
]}
end)
end
assert Chain.contract_address?(to_string(hash), 1, json_rpc_named_arguments)
end
@tag :no_parity
@tag :no_geth
test "returns false if no fetched code from json rpc", %{
json_rpc_named_arguments: json_rpc_named_arguments
} do
hash = "0x71300d93a8CdF93385Af9635388cF2D00b95a480"
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn _arguments, _options ->
{:ok,
[
%{
id: 0,
result: "0x"
}
]}
end)
end
refute Chain.contract_address?(to_string(hash), 1, json_rpc_named_arguments)
end
end
end end

@ -435,8 +435,13 @@ defmodule Indexer.Block.Realtime.Fetcher do
end end
end end
# Input-less transactions are value-transfers only, so their internal transactions do not need to be indexed defp fetch_internal_transactions?(
defp fetch_internal_transactions?(%{status: :ok, created_contract_address_hash: nil, input: "0x"}, _), do: false %{status: :ok, created_contract_address_hash: nil, input: "0x", to_address_hash: from_address_hash},
_
) do
Chain.contract_address?(from_address_hash)
end
# Token transfers not transferred during contract creation don't need internal transactions as the token transfers # Token transfers not transferred during contract creation don't need internal transactions as the token transfers
# derive completely from the logs. # derive completely from the logs.
defp fetch_internal_transactions?(%{status: :ok, created_contract_address_hash: nil}, true), do: false defp fetch_internal_transactions?(%{status: :ok, created_contract_address_hash: nil}, true), do: false

Loading…
Cancel
Save