From 5f247e6e15b9b50ad93ab9834a5c92e3bab99203 Mon Sep 17 00:00:00 2001 From: zachdaniel Date: Mon, 22 Apr 2019 13:52:14 -0400 Subject: [PATCH 1/3] feat: verify contracts with a post request --- CHANGELOG.md | 2 + .../v1/verified_smart_contract_controller.ex | 74 +++++++++++++++++++ .../lib/block_scout_web/router.ex | 1 + ...erified_smart_contract_controller_test.exs | 72 ++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f88ff0a14..1abea97741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- [#1806](https://github.com/poanetwork/blockscout/pull/1806) - verify contracts with a post request + ### Fixes ### Chore diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex new file mode 100644 index 0000000000..50334a1a45 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex @@ -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 diff --git a/apps/block_scout_web/lib/block_scout_web/router.ex b/apps/block_scout_web/lib/block_scout_web/router.ex index 59cf06e7c1..7c7f871fb5 100644 --- a/apps/block_scout_web/lib/block_scout_web/router.ex +++ b/apps/block_scout_web/lib/block_scout_web/router.ex @@ -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 diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs new file mode 100644 index 0000000000..2e51f42467 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs @@ -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 From 0881fd8dd10f77ef1aa99cca62ad6485fe298368 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 23 Apr 2019 14:17:37 +0300 Subject: [PATCH 2/3] Clear build script --- rel/commands/clear_build.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 rel/commands/clear_build.sh diff --git a/rel/commands/clear_build.sh b/rel/commands/clear_build.sh new file mode 100755 index 0000000000..66ede0039b --- /dev/null +++ b/rel/commands/clear_build.sh @@ -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 \ No newline at end of file From ee870fb1defaf84f73028585812339ee0d0ad3d8 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 23 Apr 2019 14:32:01 +0300 Subject: [PATCH 3/3] UPDATE CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f88ff0a14..5073e0e74d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Chore +- [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script ## 1.3.10-beta