Merge branch 'master' into pools_fetching

pull/1801/head
saneery 6 years ago
commit 86f60df604
  1. 9
      CHANGELOG.md
  2. 18
      apps/block_scout_web/assets/js/pages/chain.js
  3. 20
      apps/block_scout_web/lib/block_scout_web/chain.ex
  4. 74
      apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex
  5. 4
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  6. 1
      apps/block_scout_web/lib/block_scout_web/router.ex
  7. 8
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  8. 25
      apps/block_scout_web/priv/gettext/default.pot
  9. 25
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  10. 72
      apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs
  11. 22
      apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
  12. 7
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex
  13. 5
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex
  14. 5
      apps/explorer/lib/explorer/chain/block.ex
  15. 15
      apps/explorer/priv/repo/migrations/20190424170833_change_block_size_to_nullable.exs
  16. 7
      rel/commands/clear_build.sh

@ -2,10 +2,18 @@
### Features
- [#1815](https://github.com/poanetwork/blockscout/pull/1815) - able to search without prefix "0x"
- [#1813](https://github.com/poanetwork/blockscout/pull/1813) - add total blocks counter to the main page
- [#1806](https://github.com/poanetwork/blockscout/pull/1806) - verify contracts with a post request
### Fixes
- [#1829](https://github.com/poanetwork/blockscout/pull/1829) - Handle nil quantities in block decoding routine
- [#1830](https://github.com/poanetwork/blockscout/pull/1830) - Make block size field nullable
### Chore
- [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script
## 1.3.10-beta
@ -36,6 +44,7 @@
- [#1802](https://github.com/poanetwork/blockscout/pull/1802) - make coinmarketcap's number of pages configurable
- [#1799](https://github.com/poanetwork/blockscout/pull/1799) - Use eth_getUncleByBlockHashAndIndex for uncle block fetching
- [#1531](https://github.com/poanetwork/blockscout/pull/1531) - docker: fix dockerFile for secp256k1 building
- [#1835](https://github.com/poanetwork/blockscout/pull/1835) - fix: ignore `pong` messages without error
### Chore

@ -24,7 +24,8 @@ export const initialState = {
transactionsError: false,
transactionsLoading: true,
transactionCount: null,
usdMarketCap: null
usdMarketCap: null,
blockCount: null
}
export const reducer = withMissingBlocks(baseReducer)
@ -46,11 +47,13 @@ function baseReducer (state = initialState, action) {
blocks: [
action.msg,
...state.blocks.slice(0, -1)
]
],
blockCount: action.msg.blockNumber + 1
})
} else {
return Object.assign({}, state, {
blocks: state.blocks.map((block) => block.blockNumber === action.msg.blockNumber ? action.msg : block)
blocks: state.blocks.map((block) => block.blockNumber === action.msg.blockNumber ? action.msg : block),
blockCount: action.msg.blockNumber + 1
})
}
}
@ -152,6 +155,15 @@ const elements = {
$el.empty().append(numeral(state.transactionCount).format())
}
},
'[data-selector="block-count"]': {
load ($el) {
return { blockCount: numeral($el.text()).value() }
},
render ($el, state, oldState) {
if (oldState.blockCount === state.blockCount) return
$el.empty().append(numeral(state.blockCount).format())
}
},
'[data-selector="address-count"]': {
render ($el, state, oldState) {
if (oldState.addressCount === state.addressCount) return

@ -32,6 +32,8 @@ defmodule BlockScoutWeb.Chain do
@page_size 50
@default_paging_options %PagingOptions{page_size: @page_size + 1}
@address_hash_len 40
@tx_block_hash_len 64
def default_paging_options do
@default_paging_options
@ -60,13 +62,17 @@ defmodule BlockScoutWeb.Chain do
@spec from_param(String.t()) :: {:ok, Address.t() | Block.t() | Transaction.t()} | {:error, :not_found}
def from_param(param)
def from_param("0x" <> number_string = param) do
case String.length(number_string) do
40 -> address_from_param(param)
64 -> block_or_transaction_from_param(param)
_ -> {:error, :not_found}
end
end
def from_param("0x" <> number_string = param) when byte_size(number_string) == @address_hash_len,
do: address_from_param(param)
def from_param("0x" <> number_string = param) when byte_size(number_string) == @tx_block_hash_len,
do: block_or_transaction_from_param(param)
def from_param(param) when byte_size(param) == @address_hash_len,
do: address_from_param("0x" <> param)
def from_param(param) when byte_size(param) == @tx_block_hash_len,
do: block_or_transaction_from_param("0x" <> param)
def from_param(string) when is_binary(string) do
case param_to_block_number(string) do

@ -0,0 +1,74 @@
defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
use BlockScoutWeb, :controller
alias Explorer.Chain
alias Explorer.Chain.Hash.Address
alias Explorer.SmartContract.Publisher
def create(conn, params) do
with {:ok, hash} <- validate_address_hash(params["address_hash"]),
:ok <- smart_contract_exists?(hash),
:ok <- verified_smart_contract_exists?(hash) do
external_libraries = fetch_external_libraries(params)
case Publisher.publish(hash, params, external_libraries) do
{:ok, _} ->
send_resp(conn, :created, Jason.encode!(%{status: :success}))
{:error, changeset} ->
errors =
changeset.errors
|> Enum.into(%{}, fn {field, {message, _}} ->
{field, message}
end)
send_resp(conn, :unprocessable_entity, encode(errors))
end
else
:invalid_address ->
send_resp(conn, :unprocessable_entity, encode(%{error: "address_hash is invalid"}))
:not_found ->
send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"}))
:contract_exists ->
send_resp(
conn,
:unprocessable_entity,
encode(%{error: "verified code already exists for this address"})
)
end
end
defp smart_contract_exists?(address_hash) do
case Chain.hash_to_address(address_hash) do
{:ok, _address} -> :ok
_ -> :not_found
end
end
defp validate_address_hash(address_hash) do
case Address.cast(address_hash) do
{:ok, hash} -> {:ok, hash}
:error -> :invalid_address
end
end
defp verified_smart_contract_exists?(address_hash) do
if Chain.address_hash_to_smart_contract(address_hash) do
:contract_exists
else
:ok
end
end
defp encode(data) do
Jason.encode!(data)
end
defp fetch_external_libraries(params) do
keys = Enum.flat_map(1..5, fn i -> ["library#{i}_name", "library#{i}_address"] end)
Map.take(params, keys)
end
end

@ -11,6 +11,7 @@ defmodule BlockScoutWeb.ChainController do
def show(conn, _params) do
transaction_estimated_count = Chain.transaction_estimated_count()
block_count = Chain.block_count()
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
@ -22,7 +23,8 @@ defmodule BlockScoutWeb.ChainController do
exchange_rate: exchange_rate,
chart_data_path: market_history_chart_path(conn, :show),
transaction_estimated_count: transaction_estimated_count,
transactions_path: recent_transactions_path(conn, :index)
transactions_path: recent_transactions_path(conn, :index),
block_count: block_count
)
end

@ -24,6 +24,7 @@ defmodule BlockScoutWeb.Router do
get("/supply", SupplyController, :supply)
resources("/decompiled_smart_contract", DecompiledSmartContractController, only: [:create])
resources("/verified_smart_contracts", VerifiedSmartContractController, only: [:create])
end
scope "/api", BlockScoutWeb.API.RPC do

@ -53,6 +53,14 @@
<%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %>
</span>
</div>
<div class="dashboard-banner-network-stats-item">
<span class="dashboard-banner-network-stats-label">
<%= gettext "Total blocks" %>
</span>
<span class="dashboard-banner-network-stats-value" data-selector="block-count">
<%= Cldr.Number.to_string!(@block_count, format: "#,###") %>
</span>
</div>
<div class="dashboard-banner-network-stats-item">
<span class="dashboard-banner-network-stats-label">
<%= gettext "Wallet addresses" %>

@ -175,7 +175,7 @@ msgid "BlockScout provides analytics data, API, and Smart Contract tools for the
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:72
#: lib/block_scout_web/templates/chain/show.html.eex:80
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Blocks"
@ -560,7 +560,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:96
#: lib/block_scout_web/templates/chain/show.html.eex:104
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:14
#: lib/block_scout_web/templates/transaction/index.html.eex:14
msgid "More transactions have come in"
@ -957,7 +957,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:93
#: lib/block_scout_web/templates/chain/show.html.eex:101
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/views/address_view.ex:294
msgid "Transactions"
@ -1031,12 +1031,12 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:71
#: lib/block_scout_web/templates/chain/show.html.eex:79
msgid "View All Blocks →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:92
#: lib/block_scout_web/templates/chain/show.html.eex:100
msgid "View All Transactions →"
msgstr ""
@ -1076,7 +1076,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:58
#: lib/block_scout_web/templates/chain/show.html.eex:66
msgid "Wallet addresses"
msgstr ""
@ -1160,8 +1160,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_validation/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:82
#: lib/block_scout_web/templates/chain/show.html.eex:84
#: lib/block_scout_web/templates/chain/show.html.eex:110
#: lib/block_scout_web/templates/chain/show.html.eex:92
#: lib/block_scout_web/templates/chain/show.html.eex:118
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:24
msgid "Loading..."
msgstr ""
@ -1371,7 +1371,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:55
#: lib/block_scout_web/templates/address_validation/index.html.eex:70
#: lib/block_scout_web/templates/chain/show.html.eex:76
#: lib/block_scout_web/templates/chain/show.html.eex:84
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:23
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:22
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:20
@ -1514,7 +1514,7 @@ msgid "Emission Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:102
#: lib/block_scout_web/templates/chain/show.html.eex:110
msgid "Something went wrong, click to retry."
msgstr ""
@ -1744,3 +1744,8 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/overview.html.eex:165
msgid "ERC-721"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:58
msgid "Total blocks"
msgstr ""

@ -175,7 +175,7 @@ msgid "BlockScout provides analytics data, API, and Smart Contract tools for the
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:72
#: lib/block_scout_web/templates/chain/show.html.eex:80
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Blocks"
@ -560,7 +560,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:96
#: lib/block_scout_web/templates/chain/show.html.eex:104
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:14
#: lib/block_scout_web/templates/transaction/index.html.eex:14
msgid "More transactions have come in"
@ -957,7 +957,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:93
#: lib/block_scout_web/templates/chain/show.html.eex:101
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/views/address_view.ex:294
msgid "Transactions"
@ -1031,12 +1031,12 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:71
#: lib/block_scout_web/templates/chain/show.html.eex:79
msgid "View All Blocks →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:92
#: lib/block_scout_web/templates/chain/show.html.eex:100
msgid "View All Transactions →"
msgstr ""
@ -1076,7 +1076,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:58
#: lib/block_scout_web/templates/chain/show.html.eex:66
msgid "Wallet addresses"
msgstr ""
@ -1160,8 +1160,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_validation/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:82
#: lib/block_scout_web/templates/chain/show.html.eex:84
#: lib/block_scout_web/templates/chain/show.html.eex:110
#: lib/block_scout_web/templates/chain/show.html.eex:92
#: lib/block_scout_web/templates/chain/show.html.eex:118
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:24
msgid "Loading..."
msgstr ""
@ -1371,7 +1371,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:55
#: lib/block_scout_web/templates/address_validation/index.html.eex:70
#: lib/block_scout_web/templates/chain/show.html.eex:76
#: lib/block_scout_web/templates/chain/show.html.eex:84
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:23
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:22
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:20
@ -1514,7 +1514,7 @@ msgid "Emission Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:102
#: lib/block_scout_web/templates/chain/show.html.eex:110
msgid "Something went wrong, click to retry."
msgstr ""
@ -1744,3 +1744,8 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/overview.html.eex:165
msgid "ERC-721"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:58
msgid "Total blocks"
msgstr ""

@ -0,0 +1,72 @@
defmodule BlockScoutWeb.API.V1.VerifiedControllerTest do
use BlockScoutWeb.ConnCase
alias Explorer.Factory
# alias Explorer.Chain.DecompiledSmartContract
# import Ecto.Query,
# only: [from: 2]
test "verifying a standard smart contract", %{conn: conn} do
contract_code_info = Factory.contract_code_info()
contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
params = %{
"address_hash" => to_string(contract_address.hash),
"name" => contract_code_info.name,
"compiler_version" => contract_code_info.version,
"optimization" => contract_code_info.optimized,
"contract_source_code" => contract_code_info.source_code
}
response = post(conn, api_v1_verified_smart_contract_path(conn, :create), params)
assert response.status == 201
assert Jason.decode!(response.resp_body) == %{"status" => "success"}
end
test "verifying a smart contract with external libraries", %{conn: conn} do
contract_data =
"#{File.cwd!()}/test/support/fixture/smart_contract/compiler_tests.json"
|> File.read!()
|> Jason.decode!()
|> List.first()
%{
"compiler_version" => compiler_version,
"external_libraries" => external_libraries,
"name" => name,
"optimize" => optimize,
"contract" => contract_source_code,
"expected_bytecode" => expected_bytecode
} = contract_data
contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode)
params = %{
"address_hash" => to_string(contract_address.hash),
"name" => name,
"compiler_version" => compiler_version,
"optimization" => optimize,
"contract_source_code" => contract_source_code
}
params_with_external_libraries =
external_libraries
|> Enum.with_index()
|> Enum.reduce(params, fn {{name, address}, index}, acc ->
name_key = "library#{index + 1}_name"
address_key = "library#{index + 1}_address"
acc
|> Map.put(name_key, name)
|> Map.put(address_key, address)
end)
response = post(conn, api_v1_verified_smart_contract_path(conn, :create), params_with_external_libraries)
assert response.status == 201
assert Jason.decode!(response.resp_body) == %{"status" => "success"}
end
end

@ -121,6 +121,19 @@ defmodule BlockScoutWeb.ChainControllerTest do
assert redirected_to(conn) == transaction_path(conn, :show, transaction)
end
test "finds a transaction by hash when there are not 0x prefix", %{conn: conn} do
transaction =
:transaction
|> insert()
|> with_block()
"0x" <> non_prefix_hash = to_string(transaction.hash)
conn = get(conn, "search?q=#{to_string(non_prefix_hash)}")
assert redirected_to(conn) == transaction_path(conn, :show, transaction)
end
test "finds an address by hash", %{conn: conn} do
address = insert(:address)
conn = get(conn, "search?q=#{to_string(address.hash)}")
@ -135,6 +148,15 @@ defmodule BlockScoutWeb.ChainControllerTest do
assert redirected_to(conn) == address_path(conn, :show, address)
end
test "finds an address by hash when there are not 0x prefix", %{conn: conn} do
address = insert(:address)
"0x" <> non_prefix_hash = to_string(address.hash)
conn = get(conn, "search?q=#{to_string(non_prefix_hash)}")
assert redirected_to(conn) == address_path(conn, :show, address)
end
test "redirects to 404 when it finds nothing", %{conn: conn} do
conn = get(conn, "search?q=zaphod")
assert conn.status == 404

@ -435,10 +435,15 @@ defmodule EthereumJSONRPC.Block do
end
defp entry_to_elixir({key, quantity})
when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty) do
when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty) and not is_nil(quantity) do
{key, quantity_to_integer(quantity)}
end
# Size may be `nil` for uncle blocks
defp entry_to_elixir({key, nil}) when key in ~w(size) do
{key, nil}
end
# double check that no new keys are being missed by requiring explicit match for passthrough
# `t:EthereumJSONRPC.address/0` and `t:EthereumJSONRPC.hash/0` pass through as `Explorer.Chain` can verify correct
# hash format

@ -152,6 +152,11 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do
@impl :websocket_client
def websocket_handle({:ping, ""}, _request, %__MODULE__{} = state), do: {:reply, {:pong, ""}, state}
@impl :websocket_client
def websocket_handle({:pong, _}, _request, state) do
{:ok, state}
end
@impl :websocket_client
def websocket_info({:"$gen_call", from, request}, _, %__MODULE__{} = state) do
case handle_call(request, from, state) do

@ -10,10 +10,9 @@ defmodule Explorer.Chain.Block do
alias Explorer.Chain.{Address, Gas, Hash, Transaction}
alias Explorer.Chain.Block.{Reward, SecondDegreeRelation}
@optional_attrs ~w(internal_transactions_indexed_at)a
@optional_attrs ~w(internal_transactions_indexed_at size)a
@required_attrs ~w(consensus difficulty gas_limit gas_used hash miner_hash nonce number parent_hash size timestamp
total_difficulty)a
@required_attrs ~w(consensus difficulty gas_limit gas_used hash miner_hash nonce number parent_hash timestamp total_difficulty)a
@typedoc """
How much work is required to find a hash with some number of leading 0s. It is measured in hashes for PoW

@ -0,0 +1,15 @@
defmodule Explorer.Repo.Migrations.ChangeBlockSizeToNullable do
use Ecto.Migration
def up do
alter table(:blocks) do
modify(:size, :integer, null: true)
end
end
def down do
alter table(:blocks) do
modify(:size, :integer, null: false)
end
end
end

@ -0,0 +1,7 @@
#!/bin/sh
rm -rf ./_build
rm -rf ./deps
rm -rf ./logs/dev
rm -rf ./apps/explorer/node_modules
rm -rf ./apps/block_scout_web/assets/node_modules
Loading…
Cancel
Save