From 2eae5ab890f339e1201b12a0bf87bf0c10d1dc2b Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 20 May 2019 22:59:15 +0300 Subject: [PATCH 01/12] Fix wrong parity tasks names --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1eec164659..53e5680e78 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -480,7 +480,7 @@ jobs: command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: - name: mix test --exclude no_geth + name: mix test --exclude no_parity command: | # Don't submit coverage report for forks, but let the build succeed if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then @@ -534,7 +534,7 @@ jobs: command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: - name: mix test --exclude no_geth + name: mix test --exclude no_parity command: | # Don't submit coverage report for forks, but let the build succeed if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then From f1e54d0f2df4961b7a4f8d1ef8a45f9ce79e35af Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 20 May 2019 23:01:40 +0300 Subject: [PATCH 02/12] Add Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8c086b1b8..c572438b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ - [#1900](https://github.com/poanetwork/blockscout/pull/1900) - SUPPORTED_CHAINS ENV var - [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules - [#1958](https://github.com/poanetwork/blockscout/pull/1958) - Default value for release link env var +- [#1988](https://github.com/poanetwork/blockscout/pull/1988) - Fix wrong parity tasks names in Circle CI ## 1.3.10-beta From 0a32431f2045f0d1a5b02326e4a08b222d32f204 Mon Sep 17 00:00:00 2001 From: zachdaniel Date: Mon, 20 May 2019 16:46:11 -0400 Subject: [PATCH 03/12] fix: consolidate address w/ balance one at a time --- CHANGELOG.md | 1 + .../addresses_with_balance_counter.ex | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e60b218c2..8d193835fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - [#1956](https://github.com/poanetwork/blockscout/pull/1956) - add logs tab to address - [#1933](https://github.com/poanetwork/blockscout/pull/1933) - add eth_BlockNumber json rpc method - [#1952](https://github.com/poanetwork/blockscout/pull/1952) - feat: exclude empty contracts by default +- [#1989](https://github.com/poanetwork/blockscout/pull/1989) - fix: consolidate address w/ balance one at a time ### Fixes diff --git a/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex b/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex index 3a03a7bcd5..cdc2530f93 100644 --- a/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex +++ b/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex @@ -40,15 +40,10 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do end @impl true - def init(args) do + def init(_args) do create_table() - if enable_consolidation?() do - Task.start_link(&consolidate/0) - schedule_next_consolidation() - end - - {:ok, args} + {:ok, %{consolidate?: enable_consolidation?()}, {:continue, :ok}} end def create_table do @@ -63,9 +58,7 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do end defp schedule_next_consolidation do - if enable_consolidation?() do - Process.send_after(self(), :consolidate, :timer.seconds(@update_interval_in_seconds)) - end + Process.send_after(self(), :consolidate, :timer.seconds(@update_interval_in_seconds)) end @doc """ @@ -75,6 +68,19 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do :ets.insert(table_name(), {key, info}) end + @impl true + def handle_continue(:ok, %{consolidate?: true} = state) do + consolidate() + schedule_next_consolidation() + + {:noreply, state} + end + + @impl true + def handle_continue(:ok, state) do + {:noreply, state} + end + @impl true def handle_info(:consolidate, state) do consolidate() From c35e6bbdb784181d803937409cc1265df3a0e340 Mon Sep 17 00:00:00 2001 From: zachdaniel Date: Tue, 21 May 2019 15:17:31 -0400 Subject: [PATCH 04/12] fix: support https for wobserver polling --- CHANGELOG.md | 1 + apps/block_scout_web/mix.exs | 2 +- mix.lock | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3d9ef4a3..5acc29c729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ - [#1937](https://github.com/poanetwork/blockscout/pull/1937) - Check the presence of overlap[i] object before retrieving properties from it - [#1960](https://github.com/poanetwork/blockscout/pull/1960) - do not remove bold text in decompiled contacts - [#1917](https://github.com/poanetwork/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block +- [#1992](https://github.com/poanetwork/blockscout/pull/1992) - fix: support https for wobserver polling ### Chore diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 34289ec672..02e9d203fb 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -128,7 +128,7 @@ defmodule BlockScoutWeb.Mixfile do {:timex, "~> 3.4"}, {:wallaby, "~> 0.22", only: [:test], runtime: false}, # `:cowboy` `~> 2.0` and Phoenix 1.4 compatibility - {:wobserver, "~> 0.2.0", github: "KronicDeth/wobserver", ref: "99683a936c75c0a94ebb884cef019f7ed0b97112"}, + {:wobserver, "~> 0.2.0", github: "poanetwork/wobserver", branch: "support-https"}, {:phoenix_form_awesomplete, "~> 0.1.4"} ] end diff --git a/mix.lock b/mix.lock index e9066554b1..df1be8c242 100644 --- a/mix.lock +++ b/mix.lock @@ -109,5 +109,5 @@ "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, "wallaby": {:hex, :wallaby, "0.22.0", "e5d16bfa7ab23562c8a6e3b0a31445a2fd470ca622082a910114807ba823780d", [:mix], [{:httpoison, "~> 0.12 or ~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, ">= 1.4.0", [hex: :poison, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"}, "websocket_client": {:hex, :websocket_client, "1.3.0", "2275d7daaa1cdacebf2068891c9844b15f4fdc3de3ec2602420c2fb486db59b6", [:rebar3], [], "hexpm"}, - "wobserver": {:git, "https://github.com/KronicDeth/wobserver.git", "99683a936c75c0a94ebb884cef019f7ed0b97112", [ref: "99683a936c75c0a94ebb884cef019f7ed0b97112"]}, + "wobserver": {:git, "https://github.com/poanetwork/wobserver.git", "13bcda30a87f4f0be1878920a79433ad831eefbe", [branch: "support-https"]}, } From 124f5d8434fc3910bb81cf8c75b9f5912b2f5356 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Tue, 7 May 2019 14:45:07 -0400 Subject: [PATCH 05/12] add apps/block_scout_web/assets/static/images/ether1_logo.svg --- .../assets/static/images/ether1_logo.svg | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 apps/block_scout_web/assets/static/images/ether1_logo.svg diff --git a/apps/block_scout_web/assets/static/images/ether1_logo.svg b/apps/block_scout_web/assets/static/images/ether1_logo.svg new file mode 100644 index 0000000000..1ebd89e7df --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ether1_logo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 73277b41ebe4484409735f627d54a3c4309b0e37 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Tue, 7 May 2019 15:02:42 -0400 Subject: [PATCH 06/12] add apps/block_scout_web/assets/css/theme/_ether1_variables.scss --- .../assets/css/theme/_ether1_variables.scss | 49 +++++++++++++++++++ .../assets/css/theme/_variables.scss | 1 + 2 files changed, 50 insertions(+) create mode 100644 apps/block_scout_web/assets/css/theme/_ether1_variables.scss diff --git a/apps/block_scout_web/assets/css/theme/_ether1_variables.scss b/apps/block_scout_web/assets/css/theme/_ether1_variables.scss new file mode 100644 index 0000000000..323ceb22ac --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_ether1_variables.scss @@ -0,0 +1,49 @@ +// general +$primary: #840032; +$secondary: #343434; +$tertiary: #7f7f7f; +$additional-font: #ff95db; + +// footer +$footer-background-color: $primary; +$footer-title-color: #fff; +$footer-text-color: #fff; +$footer-item-disc-color: $secondary; + +// dashboard +$dashboard-line-color-price: $tertiary; // price left border + +$dashboard-banner-chart-legend-value-color: $additional-font; // chart labels + +$dashboard-stats-item-value-color: $additional-font; // stat values + +$dashboard-stats-item-border-color: $secondary; // stat border + +$dashboard-banner-gradient-start: $primary; // gradient begin + +$dashboard-banner-gradient-end: lighten($primary, 5); // gradient end + +$dashboard-banner-network-plain-container-background-color: #4b021e; // stats bg + + +// navigation +.navbar { box-shadow: 0px 0px 30px 0px rgba(75, 2, 30, 0.12); } // header shadow +$header-icon-border-color-hover: $tertiary; // top border on hover +$header-icon-color-hover: $tertiary; // nav icon on hover +.dropdown-item:hover, .dropdown-item:focus { background-color: $tertiary !important; } // dropdown item on hover + +// buttons +$btn-line-bg: #fff; // button bg +$btn-line-color: #4b021e; // button border and font color && hover bg color +$btn-copy-color: #4b021e; // btn copy +$btn-qr-color: #4b021e; // btn qr-code + +//links & tile +.tile a { color: $tertiary !important; } // links color for badges +.tile-type-block { + border-left: 4px solid #4b021e; +} // tab active bg + +// card +$card-background-1: $tertiary; +$card-tab-active: $tertiary; diff --git a/apps/block_scout_web/assets/css/theme/_variables.scss b/apps/block_scout_web/assets/css/theme/_variables.scss index 01bdc463d8..14517ac46f 100644 --- a/apps/block_scout_web/assets/css/theme/_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_variables.scss @@ -3,6 +3,7 @@ // @import "dai_variables"; // @import "ethereum_classic_variables"; // @import "ethereum_variables"; +// @import "ether1_variables"; // @import "expanse_variables"; // @import "gochain_variables"; // @import "goerli_variables"; From b996e8b7eaeeddc458d7859c4e534f8aea59d463 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Wed, 22 May 2019 07:21:25 -0400 Subject: [PATCH 07/12] docker/Makefile: always set a container name --- docker/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Makefile b/docker/Makefile index 1f49ff50b5..ac1c2677d6 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,6 +1,7 @@ SYSTEM = $(shell uname -s) HOST = host.docker.internal DOCKER_IMAGE = blockscout_prod +BS_CONTAINER_NAME = blockscout PG_CONTAINER_NAME = postgres PG_CONTAINER_IMAGE = postgres:10.4 THIS_FILE = $(lastword $(MAKEFILE_LIST)) @@ -87,7 +88,7 @@ endif start: build postgres @echo "==> Starting blockscout" - @docker run --rm \ + @docker run --rm --name $(BS_CONTAINER_NAME) \ $(BLOCKSCOUT_CONTAINNER_PARAMS) \ -p 4000:4000 \ $(DOCKER_IMAGE) /bin/sh -c "mix phx.server" From 2a2ed1ff78e4f67f43d494fd88f9ad9e2d0ff6f4 Mon Sep 17 00:00:00 2001 From: saneery Date: Wed, 22 May 2019 15:27:22 +0300 Subject: [PATCH 08/12] get estimated count of blocks when cache is empty --- apps/explorer/lib/explorer/chain.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 0977df1dd2..092e5c3a34 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1997,7 +1997,9 @@ defmodule Explorer.Chain do cached_value = BlockCountCache.count() if is_nil(cached_value) do - block_consensus_count() + %Postgrex.Result{rows: [[count]]} = Repo.query!("SELECT reltuples FROM pg_class WHERE relname = 'blocks';") + + trunc(count * 0.90) else cached_value end From 249fc56bf8c49400f7ae0aba86974ac1ea98039e Mon Sep 17 00:00:00 2001 From: saneery Date: Wed, 22 May 2019 15:44:24 +0300 Subject: [PATCH 09/12] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc77993902..da11db2952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - [#1933](https://github.com/poanetwork/blockscout/pull/1933) - add eth_BlockNumber json rpc method - [#1952](https://github.com/poanetwork/blockscout/pull/1952) - feat: exclude empty contracts by default - [#1954](https://github.com/poanetwork/blockscout/pull/1954) - feat: use creation init on self destruct +- [#2002](https://github.com/poanetwork/blockscout/pull/2002) - Get estimated count of blocks when cache is empty ### Fixes From f0a559efaaf7d7e9e2abf7250c5b35454fcd99c2 Mon Sep 17 00:00:00 2001 From: saneery Date: Wed, 22 May 2019 16:32:57 +0300 Subject: [PATCH 10/12] remove block_consensus_count function --- apps/explorer/lib/explorer/chain.ex | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 092e5c3a34..087375afeb 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -363,21 +363,6 @@ defmodule Explorer.Chain do Repo.aggregate(Block, :count, :hash) end - @doc """ - The number of consensus blocks. - - iex> insert(:block, consensus: true) - iex> insert(:block, consensus: false) - iex> Explorer.Chain.block_consensus_count() - 1 - - """ - def block_consensus_count do - Block - |> where(consensus: true) - |> Repo.aggregate(:count, :hash) - end - @doc """ Reward for mining a block. From 833e4e609737eab6994e192061a8212ec8bd3501 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Wed, 22 May 2019 11:28:37 -0400 Subject: [PATCH 11/12] CHANGELOG: #2000 - docker/Makefile: always set a container name --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc77993902..8e4915d775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## Current +- [#2000](https://github.com/poanetwork/blockscout/pull/2000) - docker/Makefile: always set a container name ### Features - [#1963](https://github.com/poanetwork/blockscout/pull/1963) - added rinkeby theme and rinkeby logo From d2336bccc88b4effc8672d32931e52d71ce33f2a Mon Sep 17 00:00:00 2001 From: zachdaniel Date: Thu, 16 May 2019 13:36:06 -0400 Subject: [PATCH 12/12] fix: add fields for contrat filter performance --- CHANGELOG.md | 1 + .../api/rpc/contract_controller.ex | 4 +- .../lib/block_scout_web/etherscan.ex | 19 ++- .../views/api/rpc/contract_view.ex | 12 +- .../api/rpc/contract_controller_test.exs | 28 ++-- ...ompiled_smart_contract_controller_test.exs | 20 ++- apps/explorer/lib/explorer/chain.ex | 137 +++++++++++------- apps/explorer/lib/explorer/chain/address.ex | 4 +- .../explorer/chain/import/runner/addresses.ex | 14 +- .../smart_contract/solc_downloader.ex | 2 +- ...sh_index_to_decompiled_smart_contracts.exs | 14 ++ ...ompiled_and_verified_flag_to_addresses.exs | 18 +++ apps/explorer/test/explorer/chain_test.exs | 6 + apps/explorer/test/support/factory.ex | 5 +- 14 files changed, 187 insertions(+), 97 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20190516140202_add_address_hash_index_to_decompiled_smart_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190516160535_add_decompiled_and_verified_flag_to_addresses.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 14f4c7fd4c..a12a547628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - [#1960](https://github.com/poanetwork/blockscout/pull/1960) - do not remove bold text in decompiled contacts - [#1917](https://github.com/poanetwork/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block - [#1992](https://github.com/poanetwork/blockscout/pull/1992) - fix: support https for wobserver polling +- [#1966](https://github.com/poanetwork/blockscout/pull/1966) - fix: add fields for contract filter performance ### Chore diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex index 23dacd45da..3b3c9af5ec 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -98,10 +98,10 @@ defmodule BlockScoutWeb.API.RPC.ContractController do Chain.list_decompiled_contracts(page_size, offset, not_decompiled_with_version) :unverified -> - Chain.list_unverified_contracts(page_size, offset) + Chain.list_unordered_unverified_contracts(page_size, offset) :not_decompiled -> - Chain.list_not_decompiled_contracts(page_size, offset) + Chain.list_unordered_not_decompiled_contracts(page_size, offset) :empty -> Chain.list_empty_contracts(page_size, offset) diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex index 3167689f3c..670d0787bf 100644 --- a/apps/block_scout_web/lib/block_scout_web/etherscan.ex +++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex @@ -862,11 +862,6 @@ defmodule BlockScoutWeb.Etherscan do name: "Contract", fields: %{ "Address" => @address_hash_type, - "DecompilerVersion" => %{ - type: "decompiler version", - definition: "When decompiled source code is present, the decompiler version with which it was generated.", - example: "decompiler.version" - }, "ABI" => %{ type: "ABI", definition: "JSON string for the contract's Application Binary Interface (ABI)", @@ -938,9 +933,16 @@ defmodule BlockScoutWeb.Etherscan do """ } + @contract_decompiler_version_type %{ + type: "decompiler version", + definition: "When decompiled source code is present, the decompiler version with which it was generated.", + example: "decompiler.version" + } + @contract_with_sourcecode_model @contract_model |> put_in([:fields, "SourceCode"], @contract_source_code_type) |> put_in([:fields, "DecompiledSourceCode"], @contract_decompiled_source_code_type) + |> put_in([:fields, "DecompilerVersion"], @contract_decompiler_version_type) @transaction_receipt_status_model %{ name: "TransactionReceiptStatus", @@ -1831,7 +1833,12 @@ defmodule BlockScoutWeb.Etherscan do @contract_listcontracts_action %{ name: "listcontracts", - description: "Get a list of contracts, sorted ascending by the time they were first seen by the explorer.", + description: """ + Get a list of contracts, sorted ascending by the time they were first seen by the explorer. + + If you provide the filters `not_decompiled`(`4`) or `not_verified(4)` the results will not + be sorted for performance reasons. + """, required_params: [], optional_params: [ %{ diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex index 86bac53410..8f996a2bcc 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex @@ -76,16 +76,12 @@ defmodule BlockScoutWeb.API.RPC.ContractView do defp prepare_contract(%Address{ hash: hash, - smart_contract: nil, - decompiled_smart_contracts: decompiled_smart_contracts + smart_contract: nil }) do - decompiled_smart_contract = latest_decompiled_smart_contract(decompiled_smart_contracts) - %{ "Address" => to_string(hash), "ABI" => "Contract source code not verified", "ContractName" => "", - "DecompilerVersion" => decompiler_version(decompiled_smart_contract), "CompilerVersion" => "", "OptimizationUsed" => "" } @@ -93,16 +89,12 @@ defmodule BlockScoutWeb.API.RPC.ContractView do defp prepare_contract(%Address{ hash: hash, - smart_contract: %SmartContract{} = contract, - decompiled_smart_contracts: decompiled_smart_contracts + smart_contract: %SmartContract{} = contract }) do - decompiled_smart_contract = latest_decompiled_smart_contract(decompiled_smart_contracts) - %{ "Address" => to_string(hash), "ABI" => Jason.encode!(contract.abi), "ContractName" => contract.name, - "DecompilerVersion" => decompiler_version(decompiled_smart_contract), "CompilerVersion" => contract.compiler_version, "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") } diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index 399e49bfff..e070cf8e57 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -47,7 +47,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(contract.address_hash), "CompilerVersion" => contract.compiler_version, "ContractName" => contract.name, - "DecompilerVersion" => "", "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") } ] @@ -70,7 +69,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(address.hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "", "OptimizationUsed" => "" } ] @@ -94,7 +92,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(address.hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "", "OptimizationUsed" => "" } ] @@ -122,7 +119,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(address.hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "", "OptimizationUsed" => "" } ] @@ -145,7 +141,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "ABI" => Jason.encode!(contract.abi), "Address" => to_string(contract.address_hash), "CompilerVersion" => contract.compiler_version, - "DecompilerVersion" => "", "ContractName" => contract.name, "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") } @@ -170,7 +165,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(decompiled_smart_contract.address_hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "test_decompiler", "OptimizationUsed" => "" } ] @@ -194,7 +188,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(smart_contract.address_hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "bizbuz", "OptimizationUsed" => "" } ] @@ -214,16 +207,15 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do 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" => "", - "DecompilerVersion" => "bizbuz", - "OptimizationUsed" => "" - } - ] + assert %{ + "ABI" => "Contract source code not verified", + "Address" => to_string(smart_contract.address_hash), + "CompilerVersion" => "", + "ContractName" => "", + "OptimizationUsed" => "" + } in response["result"] + + refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address")) end test "filtering for only not_decompiled (and by extension not verified contracts)", %{params: params, conn: conn} do @@ -245,7 +237,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(contract_address.hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "", "OptimizationUsed" => "" } ] @@ -274,7 +265,6 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "Address" => to_string(contract_address.hash), "CompilerVersion" => "", "ContractName" => "", - "DecompilerVersion" => "", "OptimizationUsed" => "" } ] diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs index 3df8561d0a..d6b52c8490 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs @@ -2,7 +2,7 @@ defmodule BlockScoutWeb.API.V1.DecompiledControllerTest do use BlockScoutWeb.ConnCase alias Explorer.Repo - alias Explorer.Chain.DecompiledSmartContract + alias Explorer.Chain.{Address, DecompiledSmartContract} import Ecto.Query, only: [from: 2] @@ -87,6 +87,24 @@ defmodule BlockScoutWeb.API.V1.DecompiledControllerTest do assert decompiled_smart_contract.decompiler_version == decompiler_version assert decompiled_smart_contract.decompiled_source_code == decompiled_source_code end + + test "updates the address to be decompiled", %{conn: conn} do + address_hash = to_string(insert(:address, hash: "0x0000000000000000000000000000000000000001").hash) + decompiler_version = "test_decompiler" + decompiled_source_code = "hello world" + + params = %{ + "address_hash" => address_hash, + "decompiler_version" => decompiler_version, + "decompiled_source_code" => decompiled_source_code + } + + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create), params) + + assert request.status == 201 + + assert Repo.get!(Address, address_hash).decompiled + end end describe "when user is not authorized" do diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 087375afeb..9446388531 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -554,9 +554,19 @@ defmodule Explorer.Chain do @spec create_decompiled_smart_contract(map()) :: {:ok, Address.t()} | {:error, Ecto.Changeset.t()} def create_decompiled_smart_contract(attrs) do - %DecompiledSmartContract{} - |> DecompiledSmartContract.changeset(attrs) - |> Repo.insert(on_conflict: :replace_all, conflict_target: [:decompiler_version, :address_hash]) + changeset = DecompiledSmartContract.changeset(%DecompiledSmartContract{}, attrs) + + Multi.new() + |> Multi.insert(:decompiled_smart_contract, changeset, + on_conflict: :replace_all, + conflict_target: [:decompiler_version, :address_hash] + ) + |> Multi.run(:set_address_decompiled, &set_address_decompiled/2) + |> Repo.transaction() + |> case do + {:ok, %{decompiled_smart_contract: decompiled_smart_contract}} -> {:ok, decompiled_smart_contract} + {:error, _, error_value, _} -> {:error, error_value} + end end @doc """ @@ -2235,6 +2245,7 @@ defmodule Explorer.Chain do |> Multi.insert(:smart_contract, smart_contract_changeset) |> Multi.run(:clear_primary_address_names, &clear_primary_address_names/2) |> Multi.run(:insert_address_name, &create_address_name/2) + |> Multi.run(:set_address_verified, &set_address_verified/2) |> Repo.transaction() with {:ok, %{smart_contract: smart_contract}} <- insert_result do @@ -2242,6 +2253,35 @@ defmodule Explorer.Chain do else {:error, :smart_contract, changeset, _} -> {:error, changeset} + + {:error, :set_address_verified, message, _} -> + {:error, message} + end + end + + defp set_address_verified(repo, %{smart_contract: %SmartContract{address_hash: address_hash}}) do + query = + from( + address in Address, + where: address.hash == ^address_hash + ) + + case repo.update_all(query, set: [verified: true]) do + {1, _} -> {:ok, []} + _ -> {:error, "There was an error annotating that the address has been verified."} + end + end + + defp set_address_decompiled(repo, %{decompiled_smart_contract: %DecompiledSmartContract{address_hash: address_hash}}) do + query = + from( + address in Address, + where: address.hash == ^address_hash + ) + + case repo.update_all(query, set: [decompiled: true]) do + {1, _} -> {:ok, []} + _ -> {:error, "There was an error annotating that the address has been verified."} end end @@ -2754,50 +2794,46 @@ defmodule Explorer.Chain do query = from( address in Address, - where: - fragment( - "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], + where: address.contract_code != <<>>, + where: not is_nil(address.contract_code), + where: address.decompiled == true, limit: ^limit, - offset: ^offset + offset: ^offset, + order_by: [asc: address.inserted_at], + preload: [:smart_contract] ) query - |> filter_decompiled_with_version(not_decompiled_with_version) + |> reject_decompiled_with_version(not_decompiled_with_version) |> Repo.all() end - defp filter_decompiled_with_version(query, nil) do - query - end + defp reject_decompiled_with_version(query, nil), do: query - 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] + defp reject_decompiled_with_version(query, reject_version) do + from( + address in query, + left_join: decompiled_smart_contract in assoc(address, :decompiled_smart_contracts), + on: decompiled_smart_contract.decompiler_version == ^reject_version, + where: is_nil(decompiled_smart_contract.address_hash) ) end def list_verified_contracts(limit, offset) do query = from( - address in Address, - where: not is_nil(address.contract_code), - join: smart_contract in SmartContract, - on: smart_contract.address_hash == address.hash, - preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts], - order_by: [asc: address.inserted_at], + smart_contract in SmartContract, + order_by: [asc: smart_contract.inserted_at], limit: ^limit, - offset: ^offset + offset: ^offset, + preload: [:address] ) - Repo.all(query) + query + |> Repo.all() + |> Enum.map(fn smart_contract -> + Map.put(smart_contract.address, :smart_contract, smart_contract) + end) end def list_contracts(limit, offset) do @@ -2805,7 +2841,7 @@ defmodule Explorer.Chain do from( address in Address, where: not is_nil(address.contract_code), - preload: [:smart_contract, :decompiled_smart_contracts], + preload: [:smart_contract], order_by: [asc: address.inserted_at], limit: ^limit, offset: ^offset @@ -2814,22 +2850,22 @@ defmodule Explorer.Chain do Repo.all(query) end - def list_unverified_contracts(limit, offset) do + def list_unordered_unverified_contracts(limit, offset) do query = from( address in Address, - left_join: smart_contract in SmartContract, - on: smart_contract.address_hash == address.hash, - where: not is_nil(address.contract_code), - where: is_nil(smart_contract.address_hash), where: address.contract_code != <<>>, - preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts], - order_by: [asc: address.inserted_at], + where: not is_nil(address.contract_code), + where: fragment("? IS NOT TRUE", address.verified), limit: ^limit, offset: ^offset ) - Repo.all(query) + query + |> Repo.all() + |> Enum.map(fn address -> + %{address | smart_contract: nil} + end) end def list_empty_contracts(limit, offset) do @@ -2845,30 +2881,23 @@ defmodule Explorer.Chain do Repo.all(query) end - def list_not_decompiled_contracts(limit, offset) do + def list_unordered_not_decompiled_contracts(limit, offset) do query = from( address in Address, - where: - fragment( - "NOT EXISTS (SELECT 1 FROM decompiled_smart_contracts WHERE decompiled_smart_contracts.address_hash = ?)", - address.hash - ), + where: fragment("? IS NOT TRUE", address.verified), + where: fragment("? IS NOT TRUE", address.decompiled), where: address.contract_code != <<>>, - left_join: smart_contract in SmartContract, - on: smart_contract.address_hash == address.hash, - left_join: decompiled_smart_contract in DecompiledSmartContract, - on: decompiled_smart_contract.address_hash == address.hash, - preload: [:smart_contract, :decompiled_smart_contracts], where: not is_nil(address.contract_code), - where: is_nil(smart_contract.address_hash), - where: is_nil(decompiled_smart_contract.address_hash), - order_by: [asc: address.inserted_at], limit: ^limit, offset: ^offset ) - Repo.all(query) + query + |> Repo.all() + |> Enum.map(fn address -> + %{address | smart_contract: nil} + end) end @doc """ diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index a565306fd7..a619d70546 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Address do Wei } - @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce)a + @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified)a @required_attrs ~w(hash)a @allowed_attrs @optional_attrs ++ @required_attrs @@ -75,6 +75,8 @@ defmodule Explorer.Chain.Address do field(:fetched_coin_balance_block_number, :integer) field(:contract_code, Data) field(:nonce, :integer) + field(:decompiled, :boolean, default: false) + field(:verified, :boolean, default: false) field(:has_decompiled_code?, :boolean, virtual: true) field(:stale?, :boolean, virtual: true) diff --git a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex index 8a3ab2eb02..564c36452b 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex @@ -13,6 +13,11 @@ defmodule Explorer.Chain.Import.Runner.Addresses do @behaviour Import.Runner + @row_defaults %{ + decompiled: false, + verified: false + } + # milliseconds @timeout 60_000 @@ -45,9 +50,16 @@ defmodule Explorer.Chain.Import.Runner.Addresses do update_transactions_options = %{timeout: transactions_timeout, timestamps: timestamps} + changes_list_with_defaults = + Enum.map(changes_list, fn change -> + Enum.reduce(@row_defaults, change, fn {default_key, default_value}, acc -> + Map.put_new(acc, default_key, default_value) + end) + end) + multi |> Multi.run(:addresses, fn repo, _ -> - insert(repo, changes_list, insert_options) + insert(repo, changes_list_with_defaults, insert_options) end) |> Multi.run(:created_address_code_indexed_at_transactions, fn repo, %{addresses: addresses} when is_list(addresses) -> diff --git a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex index 1aa81d2488..4c4fea4109 100644 --- a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex +++ b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex @@ -86,7 +86,7 @@ defmodule Explorer.SmartContract.SolcDownloader do download_path = "https://ethereum.github.io/solc-bin/bin/soljson-#{version}.js" download_path - |> HTTPoison.get!([], timeout: 60_000) + |> HTTPoison.get!([], timeout: 60_000, recv_timeout: 60_000) |> Map.get(:body) end end diff --git a/apps/explorer/priv/repo/migrations/20190516140202_add_address_hash_index_to_decompiled_smart_contracts.exs b/apps/explorer/priv/repo/migrations/20190516140202_add_address_hash_index_to_decompiled_smart_contracts.exs new file mode 100644 index 0000000000..689a3757d2 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190516140202_add_address_hash_index_to_decompiled_smart_contracts.exs @@ -0,0 +1,14 @@ +defmodule Explorer.Repo.Migrations.AddAddressHashIndexToDecompiledSmartContracts do + use Ecto.Migration + + def change do + execute( + """ + CREATE INDEX IF NOT EXISTS decompiled_smart_contracts_address_hash_index ON decompiled_smart_contracts(address_hash); + """, + """ + DROP INDEX IF EXISTS decompiled_smart_contracts_address_hash_index + """ + ) + end +end diff --git a/apps/explorer/priv/repo/migrations/20190516160535_add_decompiled_and_verified_flag_to_addresses.exs b/apps/explorer/priv/repo/migrations/20190516160535_add_decompiled_and_verified_flag_to_addresses.exs new file mode 100644 index 0000000000..cfc45092fd --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190516160535_add_decompiled_and_verified_flag_to_addresses.exs @@ -0,0 +1,18 @@ +defmodule Explorer.Repo.Migrations.AddDecompiledAndVerifiedFlagToAddresses do + use Ecto.Migration + + def change do + execute( + """ + ALTER TABLE addresses + ADD COLUMN IF NOT EXISTS decompiled BOOLEAN, + ADD COLUMN IF NOT EXISTS verified BOOLEAN; + """, + """ + ALTER TABLE addresses + DROP COLUMN IF EXISTS decompiled, + DROP COLUMN IF EXISTS verified; + """ + ) + end +end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index cafd742e2a..0234672183 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -2859,6 +2859,12 @@ defmodule Explorer.ChainTest do assert {:ok, _} = Chain.create_smart_contract(attrs) assert Repo.get_by(Address.Name, name: "SimpleStorage") end + + test "sets the address verified field to true", %{valid_attrs: valid_attrs} do + assert {:ok, %SmartContract{} = smart_contract} = Chain.create_smart_contract(valid_attrs) + + assert Repo.get_by(Address, hash: smart_contract.address_hash).verified == true + end end describe "stream_unfetched_balances/2" do diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 115b07e066..fcde1e13f2 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -482,6 +482,7 @@ defmodule Explorer.Factory do address = %Address{ hash: address_hash(), + verified: true, contract_code: contract_code_info().bytecode, smart_contract: smart_contract } @@ -519,7 +520,7 @@ defmodule Explorer.Factory do contract_code_info = contract_code_info() %SmartContract{ - address_hash: insert(:address, contract_code: contract_code_info.bytecode).hash, + address_hash: insert(:address, contract_code: contract_code_info.bytecode, verified: true).hash, compiler_version: contract_code_info.version, name: contract_code_info.name, contract_source_code: contract_code_info.source_code, @@ -532,7 +533,7 @@ defmodule Explorer.Factory do contract_code_info = contract_code_info() %DecompiledSmartContract{ - address_hash: insert(:address, contract_code: contract_code_info.bytecode).hash, + address_hash: insert(:address, contract_code: contract_code_info.bytecode, decompiled: true).hash, decompiler_version: "test_decompiler", decompiled_source_code: contract_code_info.source_code }