Merge pull request #1658 from poanetwork/add-with-version-filter

Add with version filter
pull/1665/head
Zach Daniel 6 years ago committed by GitHub
commit bc61808344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
  2. 3
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
  3. 6
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  4. 24
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
  5. 54
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  6. 42
      apps/explorer/lib/explorer/chain.ex
  7. 1
      apps/explorer/lib/explorer/chain/address.ex
  8. 6
      apps/explorer/lib/explorer/chain/smart_contract.ex

@ -7,7 +7,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
def listcontracts(conn, params) do def listcontracts(conn, params) do
with pagination_options <- Helpers.put_pagination_options(%{}, params), with pagination_options <- Helpers.put_pagination_options(%{}, params),
{:params, {:ok, options}} <- {:params, add_filter(pagination_options, params)} do {:params, {:ok, options}} <- {:params, add_filters(pagination_options, params)} do
options_with_defaults = options_with_defaults =
options options
|> Map.put_new(:page_number, 0) |> Map.put_new(:page_number, 0)
@ -71,7 +71,8 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
Chain.list_verified_contracts(page_size, offset) Chain.list_verified_contracts(page_size, offset)
:decompiled -> :decompiled ->
Chain.list_decompiled_contracts(page_size, offset) not_decompiled_with_version = Map.get(opts, :not_decompiled_with_version)
Chain.list_decompiled_contracts(page_size, offset, not_decompiled_with_version)
:unverified -> :unverified ->
Chain.list_unverified_contracts(page_size, offset) Chain.list_unverified_contracts(page_size, offset)
@ -84,6 +85,12 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end end
end end
defp add_filters(options, params) do
options
|> add_filter(params)
|> add_not_decompiled_with_version(params)
end
defp add_filter(options, params) do defp add_filter(options, params) do
with {:param, {:ok, value}} <- {:param, Map.fetch(params, "filter")}, with {:param, {:ok, value}} <- {:param, Map.fetch(params, "filter")},
{:validation, {:ok, filter}} <- {:validation, contracts_filter(value)} do {:validation, {:ok, filter}} <- {:validation, contracts_filter(value)} do
@ -94,6 +101,17 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end end
end end
defp add_not_decompiled_with_version({:ok, options}, params) do
case Map.fetch(params, "not_decompiled_with_version") do
{:ok, value} -> {:ok, Map.put(options, :not_decompiled_with_version, value)}
:error -> {:ok, options}
end
end
defp add_not_decompiled_with_version(options, _params) do
options
end
defp contracts_filter(nil), do: {:ok, nil} defp contracts_filter(nil), do: {:ok, nil}
defp contracts_filter(1), do: {:ok, :verified} defp contracts_filter(1), do: {:ok, :verified}
defp contracts_filter(2), do: {:ok, :decompiled} defp contracts_filter(2), do: {:ok, :decompiled}

@ -66,6 +66,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
def call_controller(conn, controller, action) do def call_controller(conn, controller, action) do
{:ok, controller.call(conn, action)} {:ok, controller.call(conn, action)}
rescue rescue
Conn.WrapperError -> :error Conn.WrapperError ->
:error
end end
end end

@ -1719,6 +1719,12 @@ defmodule BlockScoutWeb.Etherscan do
type: "string", type: "string",
description: description:
"verified|decompiled|unverified|not_decompiled, or 1|2|3|4 respectively. This requests only contracts with that status." "verified|decompiled|unverified|not_decompiled, or 1|2|3|4 respectively. This requests only contracts with that status."
},
%{
key: "not_decompiled_with_version",
type: "string",
description:
"Ensures that none of the returned contracts were decompiled with the provided version. Ignored unless filtering for decompiled contracts."
} }
], ],
responses: [ responses: [

@ -36,19 +36,27 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
end end
defp prepare_source_code_contract(contract, _) do defp prepare_source_code_contract(contract, _) do
decompiled_smart_contract = latest_decompiled_smart_contract(contract.decompiled_smart_contracts)
%{ %{
"Address" => to_string(contract.address_hash), "Address" => to_string(contract.address_hash),
"SourceCode" => contract.contract_source_code, "SourceCode" => contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi), "ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name, "ContractName" => contract.name,
"DecompiledSourceCode" => decompiled_source_code(contract.decompiled_smart_contract), "DecompiledSourceCode" => decompiled_source_code(decompiled_smart_contract),
"DecompilerVersion" => decompiler_version(contract.decompiled_smart_contract), "DecompilerVersion" => decompiler_version(decompiled_smart_contract),
"CompilerVersion" => contract.compiler_version, "CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => if(contract.optimization, do: "1", else: "0") "OptimizationUsed" => if(contract.optimization, do: "1", else: "0")
} }
end end
defp prepare_contract(%Address{hash: hash, smart_contract: nil, decompiled_smart_contract: decompiled_smart_contract}) do defp prepare_contract(%Address{
hash: hash,
smart_contract: nil,
decompiled_smart_contracts: decompiled_smart_contracts
}) do
decompiled_smart_contract = latest_decompiled_smart_contract(decompiled_smart_contracts)
%{ %{
"Address" => to_string(hash), "Address" => to_string(hash),
"SourceCode" => "", "SourceCode" => "",
@ -64,8 +72,10 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
defp prepare_contract(%Address{ defp prepare_contract(%Address{
hash: hash, hash: hash,
smart_contract: %SmartContract{} = contract, smart_contract: %SmartContract{} = contract,
decompiled_smart_contract: decompiled_smart_contract decompiled_smart_contracts: decompiled_smart_contracts
}) do }) do
decompiled_smart_contract = latest_decompiled_smart_contract(decompiled_smart_contracts)
%{ %{
"Address" => to_string(hash), "Address" => to_string(hash),
"SourceCode" => contract.contract_source_code, "SourceCode" => contract.contract_source_code,
@ -78,6 +88,12 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
} }
end end
defp latest_decompiled_smart_contract([]), do: nil
defp latest_decompiled_smart_contract(contracts) do
Enum.max_by(contracts, fn contract -> DateTime.to_unix(contract.inserted_at) end)
end
defp decompiled_source_code(nil), do: "Contract source code not decompiled." defp decompiled_source_code(nil), do: "Contract source code not decompiled."
defp decompiled_source_code(%DecompiledSmartContract{decompiled_source_code: decompiled_source_code}) do defp decompiled_source_code(%DecompiledSmartContract{decompiled_source_code: decompiled_source_code}) do

@ -157,6 +157,60 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
] ]
end end
test "filtering for only decompiled contracts, with a decompiled with version filter", %{params: params, conn: conn} do
insert(:decompiled_smart_contract, decompiler_version: "foobar")
smart_contract = insert(:decompiled_smart_contract, decompiler_version: "bizbuz")
response =
conn
|> get("/api", Map.merge(params, %{"filter" => "decompiled", "not_decompiled_with_version" => "foobar"}))
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"DecompiledSourceCode" => smart_contract.decompiled_source_code,
"DecompilerVersion" => "bizbuz",
"OptimizationUsed" => "",
"SourceCode" => ""
}
]
end
test "filtering for only decompiled contracts, with a decompiled with version filter, where another decompiled version exists",
%{params: params, conn: conn} do
non_match = insert(:decompiled_smart_contract, decompiler_version: "foobar")
insert(:decompiled_smart_contract, decompiler_version: "bizbuz", address_hash: non_match.address_hash)
smart_contract = insert(:decompiled_smart_contract, decompiler_version: "bizbuz")
response =
conn
|> get("/api", Map.merge(params, %{"filter" => "decompiled", "not_decompiled_with_version" => "foobar"}))
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"DecompiledSourceCode" => smart_contract.decompiled_source_code,
"DecompilerVersion" => "bizbuz",
"OptimizationUsed" => "",
"SourceCode" => ""
}
]
end
test "filtering for only not_decompiled (and by extension not verified contracts)", %{params: params, conn: conn} do test "filtering for only not_decompiled (and by extension not verified contracts)", %{params: params, conn: conn} do
insert(:decompiled_smart_contract) insert(:decompiled_smart_contract)
insert(:smart_contract) insert(:smart_contract)

@ -2666,19 +2666,38 @@ defmodule Explorer.Chain do
Repo.all(query, timeout: :infinity) Repo.all(query, timeout: :infinity)
end end
def list_decompiled_contracts(limit, offset) do def list_decompiled_contracts(limit, offset, not_decompiled_with_version \\ nil) do
query = query =
from( from(
address in Address, address in Address,
join: decompiled_smart_contract in DecompiledSmartContract, where:
on: decompiled_smart_contract.address_hash == address.hash, fragment(
preload: [{:decompiled_smart_contract, decompiled_smart_contract}, :smart_contract], "EXISTS (SELECT 1 FROM decompiled_smart_contracts WHERE decompiled_smart_contracts.address_hash = ?)",
address.hash
),
preload: [:decompiled_smart_contracts, :smart_contract],
order_by: [asc: address.inserted_at], order_by: [asc: address.inserted_at],
limit: ^limit, limit: ^limit,
offset: ^offset offset: ^offset
) )
Repo.all(query) query
|> filter_decompiled_with_version(not_decompiled_with_version)
|> Repo.all()
end
defp filter_decompiled_with_version(query, nil) do
query
end
defp filter_decompiled_with_version(query, not_decompiled_with_version) do
from(address in query,
left_join: decompiled_smart_contract in DecompiledSmartContract,
on: decompiled_smart_contract.decompiler_version == ^not_decompiled_with_version,
on: decompiled_smart_contract.address_hash == address.hash,
where: is_nil(decompiled_smart_contract.id),
distinct: [address.hash]
)
end end
def list_verified_contracts(limit, offset) do def list_verified_contracts(limit, offset) do
@ -2688,7 +2707,7 @@ defmodule Explorer.Chain do
where: not is_nil(address.contract_code), where: not is_nil(address.contract_code),
join: smart_contract in SmartContract, join: smart_contract in SmartContract,
on: smart_contract.address_hash == address.hash, on: smart_contract.address_hash == address.hash,
preload: [{:smart_contract, smart_contract}, :decompiled_smart_contract], preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts],
order_by: [asc: address.inserted_at], order_by: [asc: address.inserted_at],
limit: ^limit, limit: ^limit,
offset: ^offset offset: ^offset
@ -2702,7 +2721,7 @@ defmodule Explorer.Chain do
from( from(
address in Address, address in Address,
where: not is_nil(address.contract_code), where: not is_nil(address.contract_code),
preload: [:smart_contract, :decompiled_smart_contract], preload: [:smart_contract, :decompiled_smart_contracts],
order_by: [asc: address.inserted_at], order_by: [asc: address.inserted_at],
limit: ^limit, limit: ^limit,
offset: ^offset offset: ^offset
@ -2719,7 +2738,7 @@ defmodule Explorer.Chain do
on: smart_contract.address_hash == address.hash, on: smart_contract.address_hash == address.hash,
where: not is_nil(address.contract_code), where: not is_nil(address.contract_code),
where: is_nil(smart_contract.address_hash), where: is_nil(smart_contract.address_hash),
preload: [{:smart_contract, smart_contract}, :decompiled_smart_contract], preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts],
order_by: [asc: address.inserted_at], order_by: [asc: address.inserted_at],
limit: ^limit, limit: ^limit,
offset: ^offset offset: ^offset
@ -2732,11 +2751,16 @@ defmodule Explorer.Chain do
query = query =
from( from(
address in Address, address in Address,
where:
fragment(
"NOT EXISTS (SELECT 1 FROM decompiled_smart_contracts WHERE decompiled_smart_contracts.address_hash = ?)",
address.hash
),
left_join: smart_contract in SmartContract, left_join: smart_contract in SmartContract,
on: smart_contract.address_hash == address.hash, on: smart_contract.address_hash == address.hash,
left_join: decompiled_smart_contract in DecompiledSmartContract, left_join: decompiled_smart_contract in DecompiledSmartContract,
on: decompiled_smart_contract.address_hash == address.hash, on: decompiled_smart_contract.address_hash == address.hash,
preload: [smart_contract: smart_contract, decompiled_smart_contract: decompiled_smart_contract], preload: [:smart_contract, :decompiled_smart_contracts],
where: not is_nil(address.contract_code), where: not is_nil(address.contract_code),
where: is_nil(smart_contract.address_hash), where: is_nil(smart_contract.address_hash),
where: is_nil(decompiled_smart_contract.address_hash), where: is_nil(decompiled_smart_contract.address_hash),

@ -78,7 +78,6 @@ defmodule Explorer.Chain.Address do
field(:has_decompiled_code?, :boolean, virtual: true) field(:has_decompiled_code?, :boolean, virtual: true)
has_one(:smart_contract, SmartContract) has_one(:smart_contract, SmartContract)
has_one(:decompiled_smart_contract, DecompiledSmartContract)
has_one(:token, Token, foreign_key: :contract_address_hash) has_one(:token, Token, foreign_key: :contract_address_hash)
has_one( has_one(

@ -209,8 +209,8 @@ defmodule Explorer.Chain.SmartContract do
field(:constructor_arguments, :string) field(:constructor_arguments, :string)
field(:abi, {:array, :map}) field(:abi, {:array, :map})
has_one( has_many(
:decompiled_smart_contract, :decompiled_smart_contracts,
DecompiledSmartContract, DecompiledSmartContract,
foreign_key: :address_hash foreign_key: :address_hash
) )
@ -227,7 +227,7 @@ defmodule Explorer.Chain.SmartContract do
end end
def preload_decompiled_smart_contract(contract) do def preload_decompiled_smart_contract(contract) do
Repo.preload(contract, :decompiled_smart_contract) Repo.preload(contract, :decompiled_smart_contracts)
end end
def changeset(%__MODULE__{} = smart_contract, attrs) do def changeset(%__MODULE__{} = smart_contract, attrs) do

Loading…
Cancel
Save