Merge pull request #8814 from blockscout/np-addresses-endpoint-performance

Do not preload smart contract info if address has no contract_code
pull/8832/head
Victor Baranov 1 year ago committed by GitHub
commit ffbbefd2c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 20
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  3. 6
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  4. 62
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs
  5. 11
      apps/explorer/lib/explorer/chain/address.ex

@ -13,6 +13,7 @@
### Fixes
- [#8814](https://github.com/blockscout/blockscout/pull/8814) - Improve performance for EOA addresses in `/api/v2/addresses/{address_hash}`
- [#8813](https://github.com/blockscout/blockscout/pull/8813) - Force verify twin contracts on `/api/v2/import/smart-contracts/{address_hash}`
- [#8784](https://github.com/blockscout/blockscout/pull/8784) - Fix Indexer.Transform.Addresses for non-Suave setup
- [#8770](https://github.com/blockscout/blockscout/pull/8770) - Fix for eth_getbalance API v1 endpoint when requesting latest tag

@ -17,6 +17,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
alias BlockScoutWeb.AccessHelper
alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView}
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Token.Instance
alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand}
@ -46,15 +47,18 @@ defmodule BlockScoutWeb.API.V2.AddressController do
@address_options [
necessity_by_association: %{
:contracts_creation_internal_transaction => :optional,
:names => :optional,
:smart_contract => :optional,
:token => :optional,
:contracts_creation_transaction => :optional
:token => :optional
},
api?: true
]
@contract_address_preloads [
:smart_contract,
:contracts_creation_internal_transaction,
:contracts_creation_transaction
]
@nft_necessity_by_association [
necessity_by_association: %{
:token => :optional
@ -66,12 +70,14 @@ defmodule BlockScoutWeb.API.V2.AddressController do
action_fallback(BlockScoutWeb.API.V2.FallbackController)
def address(conn, %{"address_hash_param" => address_hash_string} = params) do
with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options) do
CoinBalanceOnDemand.trigger_fetch(address)
with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options),
fully_preloaded_address <-
Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) do
CoinBalanceOnDemand.trigger_fetch(fully_preloaded_address)
conn
|> put_status(200)
|> render(:address, %{address: address})
|> render(:address, %{address: fully_preloaded_address})
end
end

@ -258,7 +258,7 @@ defmodule BlockScoutWeb.AddressView do
Enum.any?(address.smart_contract.abi || [], &is_read_function?(&1))
end
def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false
def smart_contract_with_read_only_functions?(%Address{smart_contract: _}), do: false
def is_read_function?(function), do: Helper.queriable_method?(function) || Helper.read_with_wallet_method?(function)
@ -268,7 +268,7 @@ defmodule BlockScoutWeb.AddressView do
SmartContract.proxy_contract?(smart_contract, options)
end
def smart_contract_is_proxy?(%Address{smart_contract: nil}, _), do: false
def smart_contract_is_proxy?(%Address{smart_contract: _}, _), do: false
def smart_contract_with_write_functions?(%Address{smart_contract: %SmartContract{}} = address) do
!contract_interaction_disabled?() &&
@ -278,7 +278,7 @@ defmodule BlockScoutWeb.AddressView do
)
end
def smart_contract_with_write_functions?(%Address{smart_contract: nil}), do: false
def smart_contract_with_write_functions?(%Address{smart_contract: _}), do: false
def has_decompiled_code?(address) do
address.has_decompiled_code? ||

@ -1,5 +1,6 @@
defmodule BlockScoutWeb.API.V2.AddressControllerTest do
use BlockScoutWeb.ConnCase
use EthereumJSONRPC.Case, async: false
alias BlockScoutWeb.Models.UserFromAuth
alias Explorer.{Chain, Repo}
@ -22,6 +23,9 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
alias Explorer.Chain.Address.CurrentTokenBalance
import Explorer.Chain, only: [hash_to_lower_case_string: 1]
import Mox
setup :set_mox_global
describe "/addresses/{address_hash}" do
test "get 404 on non existing address", %{conn: conn} do
@ -44,7 +48,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
correct_response = %{
"hash" => Address.checksum(address.hash),
"is_contract" => false,
"is_verified" => false,
"is_verified" => nil,
"name" => nil,
"private_tags" => [],
"public_tags" => [],
@ -79,6 +83,47 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
assert ^correct_response = json_response(request, 200)
end
test "get contract info", %{conn: conn} do
smart_contract = insert(:smart_contract)
tx =
insert(:transaction,
to_address_hash: nil,
to_address: nil,
created_contract_address_hash: smart_contract.address_hash,
created_contract_address: smart_contract.address
)
insert(:address_name,
address: smart_contract.address,
primary: true,
name: smart_contract.name,
address_hash: smart_contract.address_hash
)
name = smart_contract.name
from = Address.checksum(tx.from_address_hash)
tx_hash = to_string(tx.hash)
address_hash = Address.checksum(smart_contract.address_hash)
get_eip1967_implementation_non_zero_address()
request = get(conn, "/api/v2/addresses/#{Address.checksum(smart_contract.address_hash)}")
assert %{
"hash" => ^address_hash,
"is_contract" => true,
"is_verified" => true,
"name" => ^name,
"private_tags" => [],
"public_tags" => [],
"watchlist_names" => [],
"creator_address_hash" => ^from,
"creation_tx_hash" => ^tx_hash,
"implementation_address" => "0x0000000000000000000000000000000000000001"
} = json_response(request, 200)
end
test "get watchlist id", %{conn: conn} do
auth = build(:auth)
address = insert(:address)
@ -2622,4 +2667,19 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
end
def check_total(_, _, _), do: true
def get_eip1967_implementation_non_zero_address do
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x0000000000000000000000000000000000000000000000000000000000000001"}
end)
end
end

@ -8,6 +8,7 @@ defmodule Explorer.Chain.Address do
use Explorer.Schema
alias Ecto.Changeset
alias Explorer.Chain
alias Explorer.Chain.{
Address,
@ -252,6 +253,16 @@ defmodule Explorer.Chain.Address do
end)
end
@doc """
Preloads provided contracts associations if address has contract_code which is not nil
"""
@spec maybe_preload_smart_contract_associations(Address.t(), list, list) :: Address.t()
def maybe_preload_smart_contract_associations(%Address{contract_code: nil} = address, _associations, _options),
do: address
def maybe_preload_smart_contract_associations(%Address{contract_code: _} = address, associations, options),
do: Chain.select_repo(options).preload(address, associations)
@doc """
Counts all the addresses where the `fetched_coin_balance` is > 0.
"""

Loading…
Cancel
Save