Merge branch 'master' into ab-fix-empty-buffered-task

pull/2899/head
Victor Baranov 5 years ago committed by GitHub
commit 5c051d26dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      CHANGELOG.md
  2. 7
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  3. 19
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
  4. 102
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
  5. 18
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  6. 210
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  7. 2
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs
  8. 22
      apps/explorer/lib/explorer/chain.ex
  9. 2
      apps/explorer/lib/explorer/chain/cache/address_sum.ex
  10. 25
      apps/explorer/lib/explorer/counters/average_block_time.ex
  11. 16
      apps/explorer/lib/explorer/token/instance_metadata_retriever.ex
  12. 2
      apps/explorer/test/explorer/chain/cache/address_sum_test.exs
  13. 42
      apps/explorer/test/explorer/counters/average_block_time_test.exs
  14. 44
      apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs

@ -1,12 +1,16 @@
## Current
### Features
- [#2918](https://github.com/poanetwork/blockscout/pull/2918) - Add tokenID for tokentx API action
### Fixes
- [#2914](https://github.com/poanetwork/blockscout/pull/2914) - Reduce execution time of stream_unfetched_token_instances query
- [#2906](https://github.com/poanetwork/blockscout/pull/2906) - fix address sum cache
- [#2902](https://github.com/poanetwork/blockscout/pull/2902) - Offset in blocks retrieval for average block time
- [#2900](https://github.com/poanetwork/blockscout/pull/2900) - check fetched instance metadata in multiple places
- [#2899](https://github.com/poanetwork/blockscout/pull/2899) - fix empty buffered task
### Chore
- [#2896](https://github.com/poanetwork/blockscout/pull/2896) - Disable Parity websockets tests
@ -14,6 +18,7 @@
### Features
- [#2862](https://github.com/poanetwork/blockscout/pull/2862) - Coin total supply from DB API endpoint
- [#2857](https://github.com/poanetwork/blockscout/pull/2857) - Extend getsourcecode API view with new output fields
- [#2822](https://github.com/poanetwork/blockscout/pull/2822) - Estimated address count on the main page, if cache is empty
- [#2821](https://github.com/poanetwork/blockscout/pull/2821) - add autodetection of constructor arguments
- [#2825](https://github.com/poanetwork/blockscout/pull/2825) - separate token transfers and transactions

@ -589,6 +589,12 @@ defmodule BlockScoutWeb.Etherscan do
example: ~s("Some Token Name")
}
@token_id_type %{
type: "integer",
definition: "id of token",
example: ~s("0")
}
@token_symbol_type %{
type: "string",
definition: "Trading symbol of the token.",
@ -752,6 +758,7 @@ defmodule BlockScoutWeb.Etherscan do
example: ~s("663046792267785498951364")
},
tokenName: @token_name_type,
tokenID: @token_id_type,
tokenSymbol: @token_symbol_type,
tokenDecimal: @token_decimal_type,
transactionIndex: @transaction_index_type,

@ -121,7 +121,7 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
}
end
defp prepare_token_transfer(token_transfer) do
defp prepare_common_token_transfer(token_transfer) do
%{
"blockNumber" => to_string(token_transfer.block_number),
"timeStamp" => to_string(DateTime.to_unix(token_transfer.block_timestamp)),
@ -132,7 +132,6 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
"contractAddress" => to_string(token_transfer.token_contract_address_hash),
"to" => to_string(token_transfer.to_address_hash),
"logIndex" => to_string(token_transfer.token_log_index),
"value" => get_token_value(token_transfer),
"tokenName" => token_transfer.token_name,
"tokenSymbol" => token_transfer.token_symbol,
"tokenDecimal" => to_string(token_transfer.token_decimals),
@ -146,12 +145,20 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
}
end
defp get_token_value(%{token_type: "ERC-721"} = token_transfer) do
to_string(token_transfer.token_id)
defp prepare_token_transfer(%{token_type: "ERC-721"} = token_transfer) do
token_transfer
|> prepare_common_token_transfer()
|> Map.put_new(:tokenID, token_transfer.token_id)
end
defp prepare_token_transfer(%{token_type: "ERC-20"} = token_transfer) do
token_transfer
|> prepare_common_token_transfer()
|> Map.put_new(:value, to_string(token_transfer.amount))
end
defp get_token_value(token_transfer) do
to_string(token_transfer.amount)
defp prepare_token_transfer(token_transfer) do
prepare_common_token_transfer(token_transfer)
end
defp prepare_block(block) do

@ -4,6 +4,8 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
alias BlockScoutWeb.API.RPC.RPCView
alias Explorer.Chain.{Address, DecompiledSmartContract, SmartContract}
defguardp is_empty_string(input) when input == "" or input == nil
def render("listcontracts.json", %{contracts: contracts}) do
contracts = Enum.map(contracts, &prepare_contract/1)
@ -35,7 +37,11 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
"CompilerVersion" => "",
"DecompiledSourceCode" => "",
"DecompilerVersion" => decompiler_version(nil),
"OptimizationUsed" => ""
"OptimizationUsed" => "",
"OptimizationRuns" => "",
"EVMVersion" => "",
"ConstructorArguments" => "",
"ExternalLibraries" => ""
}
end
@ -43,6 +49,68 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
decompiled_smart_contract = latest_decompiled_smart_contract(address.decompiled_smart_contracts)
contract = address.smart_contract || %{}
optimization = Map.get(contract, :optimization, "")
contract_output = %{
"Address" => to_string(address.hash)
}
contract_output
|> set_decompiled_contract_data(decompiled_smart_contract)
|> set_optimization_runs(contract, optimization)
|> set_constructor_arguments(contract)
|> set_external_libraries(contract)
|> set_verified_contract_data(contract, address, optimization)
end
defp set_decompiled_contract_data(contract_output, decompiled_smart_contract) do
if decompiled_smart_contract do
contract_output
|> Map.put_new(:DecompiledSourceCode, decompiled_source_code(decompiled_smart_contract))
|> Map.put_new(:DecompilerVersion, decompiler_version(decompiled_smart_contract))
else
contract_output
end
end
defp set_optimization_runs(contract_output, contract, optimization) do
optimization_runs = Map.get(contract, :optimization_runs, "")
if optimization && optimization != "" do
contract_output
|> Map.put_new(:OptimizationRuns, optimization_runs)
else
contract_output
end
end
defp set_constructor_arguments(contract_output, %{constructor_arguments: arguments}) when is_empty_string(arguments),
do: contract_output
defp set_constructor_arguments(contract_output, %{constructor_arguments: arguments}) do
contract_output
|> Map.put_new(:ConstructorArguments, arguments)
end
defp set_constructor_arguments(contract_output, _), do: contract_output
defp set_external_libraries(contract_output, contract) do
external_libraries = Map.get(contract, :external_libraries, [])
if Enum.count(external_libraries) > 0 do
external_libraries_without_id =
Enum.map(external_libraries, fn %{name: name, address_hash: address_hash} ->
%{"name" => name, "address_hash" => address_hash}
end)
contract_output
|> Map.put_new(:ExternalLibraries, external_libraries_without_id)
else
contract_output
end
end
defp set_verified_contract_data(contract_output, contract, address, optimization) do
contract_abi =
if is_nil(address.smart_contract) do
"Contract source code not verified"
@ -51,27 +119,28 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
end
contract_optimization =
case Map.get(contract, :optimization, "") do
case optimization do
true ->
"1"
"true"
false ->
"0"
"false"
"" ->
""
end
%{
"Address" => to_string(address.hash),
"SourceCode" => Map.get(contract, :contract_source_code, ""),
"ABI" => contract_abi,
"ContractName" => Map.get(contract, :name, ""),
"DecompiledSourceCode" => decompiled_source_code(decompiled_smart_contract),
"DecompilerVersion" => decompiler_version(decompiled_smart_contract),
"CompilerVersion" => Map.get(contract, :compiler_version, ""),
"OptimizationUsed" => contract_optimization
}
if Map.equal?(contract, %{}) do
contract_output
else
contract_output
|> Map.put_new(:SourceCode, Map.get(contract, :contract_source_code, ""))
|> Map.put_new(:ABI, contract_abi)
|> Map.put_new(:ContractName, Map.get(contract, :name, ""))
|> Map.put_new(:CompilerVersion, Map.get(contract, :compiler_version, ""))
|> Map.put_new(:OptimizationUsed, contract_optimization)
|> Map.put_new(:EVMVersion, Map.get(contract, :evm_version, ""))
end
end
defp prepare_contract(%Address{
@ -80,10 +149,7 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
}) do
%{
"Address" => to_string(hash),
"ABI" => "Contract source code not verified",
"ContractName" => "",
"CompilerVersion" => "",
"OptimizationUsed" => ""
"ABI" => "Contract source code not verified"
}
end

@ -88,21 +88,24 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
mining_address =
insert(:address,
fetched_coin_balance: 0,
fetched_coin_balance_block_number: 2,
fetched_coin_balance_block_number: 102,
inserted_at: Timex.shift(now, minutes: -10)
)
mining_address_hash = to_string(mining_address.hash)
# we space these very far apart so that we know it will consider the 0th block stale (it calculates how far
# back we'd need to go to get 24 hours in the past)
insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50), miner: mining_address)
insert(:block, number: 1, timestamp: Timex.shift(now, hours: -25), miner: mining_address)
Enum.each(0..100, fn i ->
insert(:block, number: i, timestamp: Timex.shift(now, hours: -(102 - i) * 25), miner: mining_address)
end)
insert(:block, number: 101, timestamp: Timex.shift(now, hours: -25), miner: mining_address)
AverageBlockTime.refresh()
address =
insert(:address,
fetched_coin_balance: 100,
fetched_coin_balance_block_number: 0,
fetched_coin_balance_block_number: 100,
inserted_at: Timex.shift(now, minutes: -5)
)
@ -112,7 +115,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
%{
id: id,
method: "eth_getBalance",
params: [^address_hash, "0x1"]
params: [^address_hash, "0x65"]
}
],
_options ->
@ -148,7 +151,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert received_address.hash == address.hash
assert received_address.fetched_coin_balance == expected_wei
assert received_address.fetched_coin_balance_block_number == 1
assert received_address.fetched_coin_balance_block_number == 101
end
end
@ -1804,7 +1807,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
|> get("/api", params)
|> json_response(200)
assert result["value"] == to_string(token_transfer.token_id)
assert result["tokenID"] == to_string(token_transfer.token_id)
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response)
@ -2615,6 +2618,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"logIndex" => %{"type" => "string"},
"value" => %{"type" => "string"},
"tokenName" => %{"type" => "string"},
"tokenID" => %{"type" => "string"},
"tokenSymbol" => %{"type" => "string"},
"tokenDecimal" => %{"type" => "string"},
"transactionIndex" => %{"type" => "string"},

@ -1,5 +1,6 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase
alias Explorer.Chain.SmartContract
alias Explorer.{Chain, Factory}
describe "listcontracts" do
@ -70,10 +71,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(address.hash)
}
]
@ -95,10 +93,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(address.hash)
}
]
@ -124,10 +119,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(address.hash)
}
]
@ -174,10 +166,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(decompiled_smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(decompiled_smart_contract.address_hash)
}
]
@ -199,10 +188,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(smart_contract.address_hash)
}
]
@ -225,10 +211,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert %{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(smart_contract.address_hash)
} in response["result"]
refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address"))
@ -251,10 +234,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(contract_address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(contract_address.hash)
}
]
@ -281,10 +261,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(contract_address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(contract_address.hash)
}
]
@ -423,7 +400,11 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
"CompilerVersion" => "",
"OptimizationUsed" => "",
"DecompiledSourceCode" => "",
"DecompilerVersion" => ""
"DecompilerVersion" => "",
"ConstructorArguments" => "",
"EVMVersion" => "",
"ExternalLibraries" => "",
"OptimizationRuns" => ""
}
]
@ -439,7 +420,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
end
test "with a verified contract address", %{conn: conn} do
contract = insert(:smart_contract, optimization: true)
contract = insert(:smart_contract, optimization: true, optimization_runs: 200, evm_version: "default")
params = %{
"module" => "contract",
@ -456,12 +437,156 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"DecompiledSourceCode" => "Contract source code not decompiled.",
# The contract's optimization value is true, so the expected value
# for `OptimizationUsed` is "1". If it was false, the expected value
# would be "0".
"DecompilerVersion" => "",
"OptimizationUsed" => "1"
"OptimizationUsed" => "true",
"OptimizationRuns" => 200,
"EVMVersion" => "default"
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response)
end
test "with constructor arguments", %{conn: conn} do
contract =
insert(:smart_contract,
optimization: true,
optimization_runs: 200,
evm_version: "default",
constructor_arguments:
"00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546"
)
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => to_string(contract.address_hash)
}
expected_result = [
%{
"Address" => to_string(contract.address_hash),
"SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{contract.inserted_at}\n*/\n" <>
contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => "true",
"OptimizationRuns" => 200,
"EVMVersion" => "default",
"ConstructorArguments" =>
"00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546"
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response)
end
test "with external library", %{conn: conn} do
smart_contract_bytecode =
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029"
created_contract_address =
insert(
:address,
hash: "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
contract_code: smart_contract_bytecode
)
transaction =
:transaction
|> insert()
|> with_block()
insert(
:internal_transaction_create,
transaction: transaction,
index: 0,
created_contract_address: created_contract_address,
created_contract_code: smart_contract_bytecode,
block_number: transaction.block_number,
transaction_index: transaction.index
)
valid_attrs = %{
address_hash: "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
name: "Test",
compiler_version: "0.4.23",
contract_source_code:
"pragma solidity ^0.4.23; contract SimpleStorage {uint storedData; function set(uint x) public {storedData = x; } function get() public constant returns (uint) {return storedData; } }",
abi: [
%{
"constant" => false,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "set",
"outputs" => [],
"payable" => false,
"stateMutability" => "nonpayable",
"type" => "function"
},
%{
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256"}],
"payable" => false,
"stateMutability" => "view",
"type" => "function"
}
],
optimization: true,
optimization_runs: 200,
evm_version: "default"
}
external_libraries = [
%SmartContract.ExternalLibrary{:address_hash => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95", :name => "Test"},
%SmartContract.ExternalLibrary{:address_hash => "0x283539e1b1daf24cdd58a3e934d55062ea663c3f", :name => "Test2"}
]
{:ok, %SmartContract{} = contract} = Chain.create_smart_contract(valid_attrs, external_libraries)
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => to_string(contract.address_hash)
}
expected_result = [
%{
"Address" => to_string(contract.address_hash),
"SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{contract.inserted_at}\n*/\n" <>
contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => "true",
"OptimizationRuns" => 200,
"EVMVersion" => "default",
"ExternalLibraries" => [
%{"name" => "Test", "address_hash" => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95"},
%{"name" => "Test2", "address_hash" => "0x283539e1b1daf24cdd58a3e934d55062ea663c3f"}
]
}
]
@ -508,9 +633,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
"ABI" => Jason.encode!(contract_code_info.abi),
"ContractName" => contract_code_info.name,
"CompilerVersion" => contract_code_info.version,
"DecompiledSourceCode" => "Contract source code not decompiled.",
"DecompilerVersion" => "",
"OptimizationUsed" => "0"
"OptimizationUsed" => "false",
"EVMVersion" => nil
}
assert response["status"] == "1"
@ -578,9 +702,9 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
contract_source_code
assert result["ContractName"] == name
assert result["DecompiledSourceCode"] == "Contract source code not decompiled."
assert result["DecompilerVersion"] == ""
assert result["OptimizationUsed"] == "1"
assert result["DecompiledSourceCode"] == nil
assert result["DecompilerVersion"] == nil
assert result["OptimizationUsed"] == "true"
assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response)
end
end

@ -116,7 +116,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
|> get("/api", params)
|> json_response(200)
assert response["result"] == "6"
assert response["result"] == "0"
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response)

@ -2922,7 +2922,7 @@ defmodule Explorer.Chain do
"""
@spec total_supply :: non_neg_integer() | nil
def total_supply do
supply_module().total()
supply_module().total() || 0
end
@doc """
@ -2967,21 +2967,33 @@ defmodule Explorer.Chain do
) :: {:ok, accumulator}
when accumulator: term()
def stream_unfetched_token_instances(initial, reducer) when is_function(reducer, 2) do
nft_tokens =
from(
token in Token,
where: token.type == ^"ERC-721",
select: token.contract_address_hash
)
query =
from(
token_transfer in TokenTransfer,
inner_join: token in Token,
inner_join: token in subquery(nft_tokens),
on: token.contract_address_hash == token_transfer.token_contract_address_hash,
left_join: instance in Instance,
on:
token_transfer.token_id == instance.token_id and
token_transfer.token_contract_address_hash == instance.token_contract_address_hash,
where: token.type == ^"ERC-721" and is_nil(instance.token_id) and not is_nil(token_transfer.token_id),
distinct: [token_transfer.token_contract_address_hash, token_transfer.token_id],
where: is_nil(instance.token_id) and not is_nil(token_transfer.token_id),
select: %{contract_address_hash: token_transfer.token_contract_address_hash, token_id: token_transfer.token_id}
)
Repo.stream_reduce(query, initial, reducer)
distinct_query =
from(
q in subquery(query),
distinct: [q.contract_address_hash, q.token_id]
)
Repo.stream_reduce(distinct_query, initial, reducer)
end
@doc """

@ -20,7 +20,7 @@ defmodule Explorer.Chain.Cache.AddressSum do
# See next `handle_fallback` definition
get_async_task()
{:return, nil}
{:return, Decimal.new(0)}
end
defp handle_fallback(:async_task) do

@ -63,24 +63,25 @@ defmodule Explorer.Counters.AverageBlockTime do
defp refresh_timestamps do
timestamps_query =
from(block in Block,
limit: 100,
offset: 0,
order_by: [desc: block.number],
select: {block.number, block.timestamp}
)
query =
if Application.get_env(:explorer, :include_uncles_in_average_block_time) do
timestamps_query
from(block in Block,
limit: 100,
offset: 100,
order_by: [desc: block.number],
select: {block.number, block.timestamp}
)
else
from(block in timestamps_query,
where: block.consensus == true
from(block in Block,
limit: 100,
offset: 100,
order_by: [desc: block.number],
where: block.consensus == true,
select: {block.number, block.timestamp}
)
end
timestamps =
query
timestamps_query
|> Repo.all()
|> Enum.sort_by(fn {_, timestamp} -> timestamp end, &>=/2)
|> Enum.map(fn {number, timestamp} ->

@ -80,7 +80,7 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
def fetch_json(%{"tokenURI" => {:ok, [json]}}) do
{:ok, json} = decode_json(json)
{:ok, %{metadata: json}}
check_type(json)
rescue
e ->
Logger.debug(["Unknown metadata format #{inspect(json)}. error #{inspect(e)}"],
@ -101,11 +101,7 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
{:ok, %Response{body: body, status_code: 200}} ->
{:ok, json} = decode_json(body)
if is_map(json) do
{:ok, %{metadata: json}}
else
{:error, :wrong_metadata_type}
end
check_type(json)
{:ok, %Response{body: body}} ->
{:error, body}
@ -131,4 +127,12 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
|> Jason.decode()
end
end
defp check_type(json) when is_map(json) do
{:ok, %{metadata: json}}
end
defp check_type(_) do
{:error, :wrong_metadata_type}
end
end

@ -12,7 +12,7 @@ defmodule Explorer.Chain.Cache.AddressSumTest do
test "returns default address sum" do
result = AddressSum.get_sum()
assert is_nil(result)
assert result == Decimal.new(0)
end
test "updates cache if initial value is zero" do

@ -34,11 +34,19 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
first_timestamp = Timex.now()
insert(:block, number: block_number, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 3))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 9))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 6))
insert(:block, number: block_number, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: -100 - 3))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: -100 - 9))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: -100 - 6))
Enum.each(1..100, fn i ->
insert(:block,
number: block_number + i,
consensus: true,
timestamp: Timex.shift(first_timestamp, seconds: -(101 - i) - 9)
)
end)
assert Repo.aggregate(Block, :count, :hash) == 3
assert Repo.aggregate(Block, :count, :hash) == 103
AverageBlockTime.refresh()
@ -55,6 +63,14 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 4))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 5))
Enum.each(1..100, fn i ->
insert(:block,
number: block_number + i + 1,
consensus: true,
timestamp: Timex.shift(first_timestamp, seconds: -(101 - i) - 5)
)
end)
AverageBlockTime.refresh()
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT2S")
@ -69,6 +85,14 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 4))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 5))
Enum.each(1..100, fn i ->
insert(:block,
number: block_number + i + 1,
consensus: true,
timestamp: Timex.shift(first_timestamp, seconds: -(101 - i) - 5)
)
end)
AverageBlockTime.refresh()
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT1S")
@ -83,7 +107,15 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
insert(:block, number: block_number + 2, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 9))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 6))
assert Repo.aggregate(Block, :count, :hash) == 3
Enum.each(1..100, fn i ->
insert(:block,
number: block_number + i + 2,
consensus: true,
timestamp: Timex.shift(first_timestamp, seconds: -(101 - i) - 9)
)
end)
assert Repo.aggregate(Block, :count, :hash) == 103
AverageBlockTime.refresh()

@ -41,15 +41,18 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
# we space these very far apart so that we know it will consider the 0th block stale (it calculates how far
# back we'd need to go to get 24 hours in the past)
insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50))
insert(:block, number: 1, timestamp: now)
Enum.each(0..100, fn i ->
insert(:block, number: i, timestamp: Timex.shift(now, hours: -(101 - i) * 50))
end)
insert(:block, number: 101, timestamp: now)
AverageBlockTime.refresh()
stale_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 0)
current_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 1)
stale_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 100)
current_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 101)
pending_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 1)
insert(:unfetched_balance, address_hash: pending_address.hash, block_number: 2)
pending_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 101)
insert(:unfetched_balance, address_hash: pending_address.hash, block_number: 102)
%{stale_address: stale_address, current_address: current_address, pending_address: pending_address}
end
@ -63,7 +66,7 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
test "if the address has not been fetched within the last 24 hours of blocks it is considered stale", %{
stale_address: address
} do
assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 1}
assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 101}
end
test "if the address has been fetched within the last 24 hours of blocks it is considered current", %{
@ -75,7 +78,7 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
test "if there is an unfetched balance within the window for an address, it is considered pending", %{
pending_address: pending_address
} do
assert CoinBalanceOnDemand.trigger_fetch(pending_address) == {:pending, 2}
assert CoinBalanceOnDemand.trigger_fetch(pending_address) == {:pending, 102}
end
end
@ -88,15 +91,18 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
# we space these very far apart so that we know it will consider the 0th block stale (it calculates how far
# back we'd need to go to get 24 hours in the past)
insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50))
insert(:block, number: 1, timestamp: now)
Enum.each(0..100, fn i ->
insert(:block, number: i, timestamp: Timex.shift(now, hours: -(101 - i) * 50))
end)
insert(:block, number: 101, timestamp: now)
AverageBlockTime.refresh()
:ok
end
test "a stale address broadcasts the new address" do
address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 0)
address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 100)
address_hash = address.hash
string_address_hash = to_string(address.hash)
@ -104,26 +110,26 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
%{
id: id,
method: "eth_getBalance",
params: [^string_address_hash, "0x1"]
params: [^string_address_hash, "0x65"]
}
],
_options ->
{:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]}
end)
assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 1}
assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 101}
{:ok, expected_wei} = Wei.cast(2)
assert_receive(
{:chain_event, :addresses, :on_demand,
[%{hash: ^address_hash, fetched_coin_balance: ^expected_wei, fetched_coin_balance_block_number: 1}]}
[%{hash: ^address_hash, fetched_coin_balance: ^expected_wei, fetched_coin_balance_block_number: 101}]}
)
end
test "a pending address broadcasts the new address and the new coin balance" do
address = insert(:address, fetched_coin_balance: 0, fetched_coin_balance_block_number: 1)
insert(:unfetched_balance, address_hash: address.hash, block_number: 2)
address = insert(:address, fetched_coin_balance: 0, fetched_coin_balance_block_number: 101)
insert(:unfetched_balance, address_hash: address.hash, block_number: 102)
address_hash = address.hash
string_address_hash = to_string(address.hash)
@ -131,20 +137,20 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
%{
id: id,
method: "eth_getBalance",
params: [^string_address_hash, "0x2"]
params: [^string_address_hash, "0x66"]
}
],
_options ->
{:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]}
end)
assert CoinBalanceOnDemand.trigger_fetch(address) == {:pending, 2}
assert CoinBalanceOnDemand.trigger_fetch(address) == {:pending, 102}
{:ok, expected_wei} = Wei.cast(2)
assert_receive(
{:chain_event, :addresses, :on_demand,
[%{hash: ^address_hash, fetched_coin_balance: ^expected_wei, fetched_coin_balance_block_number: 2}]}
[%{hash: ^address_hash, fetched_coin_balance: ^expected_wei, fetched_coin_balance_block_number: 102}]}
)
end
end

Loading…
Cancel
Save