diff --git a/CHANGELOG.md b/CHANGELOG.md index ff2fbeddb6..028e4188a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ - [#2538](https://github.com/poanetwork/blockscout/pull/2538) - fetch the last not empty coin balance records ### Chore +- [#2590](https://github.com/poanetwork/blockscout/pull/2590) - restore backward compatablity with old releases +- [#2574](https://github.com/poanetwork/blockscout/pull/2574) - limit request body in json rpc error - [#2566](https://github.com/poanetwork/blockscout/pull/2566) - upgrade absinthe phoenix @@ -46,6 +48,7 @@ - [#2520](https://github.com/poanetwork/blockscout/pull/2520) - Hide loading message when fetching is failed - [#2523](https://github.com/poanetwork/blockscout/pull/2523) - Avoid importing internal_transactions of pending transactions - [#2519](https://github.com/poanetwork/blockscout/pull/2519) - enable `First` page button in pagination +- [#2518](https://github.com/poanetwork/blockscout/pull/2518) - create suggested indexes - [#2517](https://github.com/poanetwork/blockscout/pull/2517) - remove duplicate indexes - [#2515](https://github.com/poanetwork/blockscout/pull/2515) - do not aggregate NFT token transfers - [#2514](https://github.com/poanetwork/blockscout/pull/2514) - Isolating of staking dapp css && extracting of non-critical css diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 27a1e6aa74..e54907177f 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -62,7 +62,7 @@ defmodule BlockScoutWeb.Mixfile do # GraphQL toolkit {:absinthe, "~> 1.4"}, # Integrates Absinthe subscriptions with Phoenix - {:absinthe_phoenix, git: "https://github.com/ayrat555/absinthe_phoenix.git", branch: "master"}, + {:absinthe_phoenix, git: "https://github.com/ayrat555/absinthe_phoenix.git", branch: "ab-update-plug"}, # Plug support for Absinthe {:absinthe_plug, git: "https://github.com/ayrat555/absinthe_plug.git", branch: "ab-enable-default-query"}, # Absinthe support for the Relay framework @@ -130,7 +130,8 @@ defmodule BlockScoutWeb.Mixfile do {:wallaby, "~> 0.22", only: [:test], runtime: false}, # `:cowboy` `~> 2.0` and Phoenix 1.4 compatibility {:wobserver, "~> 0.2.0", github: "poanetwork/wobserver", branch: "support-https"}, - {:phoenix_form_awesomplete, "~> 0.1.4"} + {:phoenix_form_awesomplete, "~> 0.1.4"}, + {:ex_json_schema, "~> 0.6.1"} ] end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index b8a3a85845..7e88d4717f 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -47,6 +47,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["message"] == "OK" assert response["status"] == "1" assert response["result"] == [] @@ -63,6 +65,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["message"] == "OK" assert response["status"] == "1" @@ -120,6 +124,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["message"] == "OK" assert response["status"] == "1" @@ -158,6 +164,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["message"] =~ "'address' is required" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -176,6 +184,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["message"] =~ "Invalid address hash" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -194,6 +204,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["result"] == "0" assert response["status"] == "1" assert response["message"] == "OK" @@ -213,6 +225,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["result"] == "#{address.fetched_coin_balance.value}" assert response["status"] == "1" assert response["message"] == "OK" @@ -245,6 +259,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" @@ -289,6 +305,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["message"] =~ "Invalid address hash" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -315,6 +333,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert :ok = ExJsonSchema.Validator.validate(schema, response) assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" @@ -350,6 +371,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "with an address that exists and one that doesn't", %{conn: conn} do @@ -375,6 +399,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "up to a maximum of 20 addresses in a single request", %{conn: conn} do @@ -399,6 +426,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 20 assert response["status"] == "1" assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "with a single address", %{conn: conn} do @@ -422,6 +452,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "supports GET and POST requests", %{conn: conn} do @@ -471,6 +504,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -489,6 +523,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with an address that doesn't exist", %{conn: conn} do @@ -506,6 +541,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with a valid address", %{conn: conn} do @@ -556,6 +592,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "includes correct confirmations value", %{conn: conn} do @@ -580,12 +617,14 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do expected_confirmations = block_height - transaction.block_number assert %{"result" => [returned_transaction]} = + response = conn |> get("/api", params) |> json_response(200) assert returned_transaction["confirmations"] == "#{expected_confirmations}" assert returned_transaction["hash"] == "#{hash}" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "returns '1' for 'isError' with failed transaction", %{conn: conn} do @@ -605,6 +644,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do } assert %{"result" => [returned_transaction]} = + response = conn |> get("/api", params) |> json_response(200) @@ -612,6 +652,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert returned_transaction["isError"] == "1" assert returned_transaction["txreceipt_status"] == "0" assert returned_transaction["hash"] == "#{hash}" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with address with multiple transactions", %{conn: conn} do @@ -648,6 +689,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "orders transactions by block, in ascending order", %{conn: conn} do @@ -688,6 +730,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert block_numbers_order == Enum.sort(block_numbers_order, &(&1 <= &2)) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "orders transactions by block, in descending order", %{conn: conn} do @@ -728,6 +771,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert block_numbers_order == Enum.sort(block_numbers_order, &(&1 >= &2)) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "ignores invalid sort option, defaults to ascending", %{conn: conn} do @@ -768,6 +812,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert block_numbers_order == Enum.sort(block_numbers_order, &(&1 <= &2)) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with valid pagination params", %{conn: conn} do @@ -821,6 +866,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "ignores pagination params when invalid", %{conn: conn} do @@ -862,6 +908,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 6 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "ignores offset param if offset is less than 1", %{conn: conn} do @@ -889,6 +936,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 6 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "ignores offset param if offset is over 10,000", %{conn: conn} do @@ -916,6 +964,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 6 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with page number with no results", %{conn: conn} do @@ -957,6 +1006,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with startblock and endblock params", %{conn: conn} do @@ -995,6 +1045,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with startblock but without endblock", %{conn: conn} do @@ -1032,6 +1083,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with endblock but without startblock", %{conn: conn} do @@ -1069,6 +1121,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "ignores invalid startblock and endblock", %{conn: conn} do @@ -1097,6 +1150,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 8 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with starttimestamp and endtimestamp params", %{conn: conn} do @@ -1144,6 +1198,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with starttimestamp but without endtimestamp", %{conn: conn} do @@ -1191,6 +1246,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with endtimestamp but without starttimestamp", %{conn: conn} do @@ -1236,6 +1292,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with filterby=to option", %{conn: conn} do @@ -1263,6 +1320,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 1 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "with filterby=from option", %{conn: conn} do @@ -1293,6 +1351,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 2 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) end test "supports GET and POST requests", %{conn: conn} do @@ -1338,6 +1397,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end end @@ -1358,6 +1418,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "with a txhash that doesn't exist", %{conn: conn} do @@ -1375,6 +1436,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No internal transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "response includes all the expected fields", %{conn: conn} do @@ -1427,6 +1489,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "isError is true if internal transaction has an error", %{conn: conn} do @@ -1459,6 +1522,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert found_internal_transaction["isError"] == "1" assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "with transaction with multiple internal transactions", %{conn: conn} do @@ -1486,6 +1550,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(found_internal_transactions) == 3 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end end @@ -1506,6 +1571,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "with a address that doesn't exist", %{conn: conn} do @@ -1523,6 +1589,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No internal transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "response includes all the expected fields", %{conn: conn} do @@ -1575,6 +1642,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "isError is true if internal transaction has an error", %{conn: conn} do @@ -1610,6 +1678,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert found_internal_transaction["isError"] == "1" assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end test "with transaction with multiple internal transactions", %{conn: conn} do @@ -1645,6 +1714,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(found_internal_transactions) == 3 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) end end @@ -1664,6 +1734,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -1682,6 +1753,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end test "with an address that doesn't exist", %{conn: conn} do @@ -1699,6 +1771,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No token transfers found" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end test "has correct value for ERC-721", %{conn: conn} do @@ -1734,6 +1807,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert result["value"] == to_string(token_transfer.token_id) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end test "returns all the required fields", %{conn: conn} do @@ -1785,6 +1859,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end test "with an invalid contract address", %{conn: conn} do @@ -1804,6 +1879,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end test "filters results by contract address", %{conn: conn} do @@ -1837,6 +1913,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert result["contractAddress"] == to_string(contract_address.hash) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) end end @@ -1856,6 +1933,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with contractaddress but without address", %{conn: conn} do @@ -1874,6 +1952,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with address but without contractaddress", %{conn: conn} do @@ -1892,6 +1971,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with an invalid contractaddress hash", %{conn: conn} do @@ -1911,6 +1991,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -1930,6 +2011,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with a contractaddress and address that doesn't exist", %{conn: conn} do @@ -1948,6 +2030,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == "0" assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with contractaddress and address without row in token_balances table", %{conn: conn} do @@ -1969,6 +2052,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == "0" assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end test "with contractaddress and address with existing balance in token_balances table", %{conn: conn} do @@ -1989,6 +2073,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == to_string(token_balance.value) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) end end @@ -2008,6 +2093,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -2026,6 +2112,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) end test "with an address that doesn't exist", %{conn: conn} do @@ -2043,6 +2130,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No tokens found" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) end test "with an address without row in token_balances table", %{conn: conn} do @@ -2062,6 +2150,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No tokens found" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) end test "with address with existing balance in token_balances table", %{conn: conn} do @@ -2092,6 +2181,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) end test "with address with multiple tokens", %{conn: conn} do @@ -2115,6 +2205,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert length(response["result"]) == 2 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) end end @@ -2134,6 +2225,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -2152,6 +2244,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) end test "with an address that doesn't exist", %{conn: conn} do @@ -2169,6 +2262,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No blocks found" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) end test "returns all the required fields", %{conn: conn} do @@ -2208,6 +2302,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) end test "with a block with one transaction", %{conn: conn} do @@ -2247,6 +2342,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) end test "with pagination options", %{conn: conn} do @@ -2297,6 +2393,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) end end @@ -2439,4 +2536,162 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do assert result == {:required_params, {:ok, params}} end end + + defp listaccounts_schema do + resolve_schema(%{ + "type" => "array", + "items" => %{ + "type" => "object", + "properties" => %{ + "address" => %{"type" => "string"}, + "balance" => %{"type" => "string"}, + "stale" => %{"type" => "boolean"} + } + } + }) + end + + defp balance_schema do + resolve_schema(%{ + "type" => ["string", "null", "array"], + "items" => %{ + "type" => "object", + "properties" => %{ + "account" => %{"type" => "string"}, + "balance" => %{"type" => "string"}, + "stale" => %{"type" => "boolean"} + } + } + }) + end + + defp txlist_schema do + resolve_schema(%{ + "type" => ["null", "array"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "hash" => %{"type" => "string"}, + "nonce" => %{"type" => "string"}, + "blockHash" => %{"type" => "string"}, + "transactionIndex" => %{"type" => "string"}, + "from" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "gas" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "isError" => %{"type" => "string"}, + "txreceipt_status" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "cumulativeGasUsed" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "confirmations" => %{"type" => "string"} + } + } + }) + end + + defp txlistinternal_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "from" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "transactionHash" => %{"type" => "string"}, + "index" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "type" => %{"type" => "string"}, + "gas" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "isError" => %{"type" => "string"}, + "errCode" => %{"type" => "string"} + } + } + }) + end + + defp tokentx_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "hash" => %{"type" => "string"}, + "nonce" => %{"type" => "string"}, + "blockHash" => %{"type" => "string"}, + "from" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "logIndex" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "tokenName" => %{"type" => "string"}, + "tokenSymbol" => %{"type" => "string"}, + "tokenDecimal" => %{"type" => "string"}, + "transactionIndex" => %{"type" => "string"}, + "gas" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "cumulativeGasUsed" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "confirmations" => %{"type" => "string"} + } + } + }) + end + + defp tokenbalance_schema, do: resolve_schema(%{"type" => ["string", "null"]}) + + defp tokenlist_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "balance" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "name" => %{"type" => "string"}, + "decimals" => %{"type" => "string"}, + "symbol" => %{"type" => "string"}, + "type" => %{"type" => "string"} + } + } + }) + end + + defp block_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "blockReward" => %{"type" => "string"} + } + } + }) + end + + defp resolve_schema(result \\ %{}) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs index bda89fe49a..50ab2f18f3 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs @@ -14,6 +14,8 @@ defmodule BlockScoutWeb.API.RPC.BlockControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "with an invalid block number", %{conn: conn} do @@ -26,6 +28,8 @@ defmodule BlockScoutWeb.API.RPC.BlockControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "with a block that doesn't exist", %{conn: conn} do @@ -38,6 +42,8 @@ defmodule BlockScoutWeb.API.RPC.BlockControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end test "with a valid block", %{conn: conn} do @@ -71,6 +77,29 @@ defmodule BlockScoutWeb.API.RPC.BlockControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) end end + + defp resolve_schema() do + ExJsonSchema.Schema.resolve(%{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"}, + "result" => %{ + "type" => ["object", "null"], + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "number"}, + "blockMiner" => %{"type" => "string"}, + "blockReward" => %{"type" => "string"}, + "uncles" => %{"type" => "null"}, + "uncleInclusionReward" => %{"type" => "null"} + } + } + } + }) + end end 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 e070cf8e57..3fbd1713bb 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 @@ -17,6 +17,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "invalid is not a valid value for `filter`. Please use one of: verified, decompiled, unverified, not_decompiled, 1, 2, 3, 4." assert response["status"] == "0" + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "with no contracts", %{conn: conn, params: params} do @@ -28,6 +29,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["message"] == "OK" assert response["status"] == "1" assert response["result"] == [] + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "with a verified smart contract, all contract information is shown", %{conn: conn, params: params} do @@ -50,6 +52,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "with an unverified contract address, only basic information is shown", %{conn: conn, params: params} do @@ -72,6 +76,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only unverified contracts shows only unverified contracts", %{params: params, conn: conn} do @@ -95,6 +101,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only unverified contracts does not show self destructed contracts", %{ @@ -122,6 +130,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only verified contracts shows only verified contracts", %{params: params, conn: conn} do @@ -145,6 +155,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only decompiled contracts shows only decompiled contracts", %{params: params, conn: conn} do @@ -168,6 +180,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only decompiled contracts, with a decompiled with version filter", %{params: params, conn: conn} do @@ -191,6 +205,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only decompiled contracts, with a decompiled with version filter, where another decompiled version exists", @@ -216,6 +232,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do } in response["result"] refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address")) + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only not_decompiled (and by extension not verified contracts)", %{params: params, conn: conn} do @@ -240,6 +257,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end test "filtering for only not_decompiled (and by extension not verified contracts) does not show empty contracts", %{ @@ -268,6 +287,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "OptimizationUsed" => "" } ] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end end @@ -287,6 +308,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -305,6 +327,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) end test "with an address that doesn't exist", %{conn: conn} do @@ -322,6 +345,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == nil assert response["status"] == "0" assert response["message"] == "Contract source code not verified" + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) end test "with a verified contract address", %{conn: conn} do @@ -341,6 +365,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == Jason.encode!(contract.abi) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) end end @@ -360,6 +385,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -378,6 +404,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) end test "with an address that doesn't exist", %{conn: conn} do @@ -408,6 +435,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) end test "with a verified contract address", %{conn: conn} do @@ -443,6 +471,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) end end @@ -481,6 +510,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["status"] == "1" assert response["result"] == expected_result assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response) end test "with external libraries", %{conn: conn} do @@ -539,6 +569,76 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert result["DecompiledSourceCode"] == "Contract source code not decompiled." assert result["DecompilerVersion"] == "" assert result["OptimizationUsed"] == "1" + assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response) end end + + defp listcontracts_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "Address" => %{"type" => "string"}, + "ABI" => %{"type" => "string"}, + "ContractName" => %{"type" => "string"}, + "CompilerVersion" => %{"type" => "string"}, + "OptimizationUsed" => %{"type" => "string"} + } + } + }) + end + + defp getabi_schema do + resolve_schema(%{ + "type" => ["string", "null"] + }) + end + + defp getsourcecode_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "Address" => %{"type" => "string"}, + "SourceCode" => %{"type" => "string"}, + "ABI" => %{"type" => "string"}, + "ContractName" => %{"type" => "string"}, + "CompilerVersion" => %{"type" => "string"}, + "OptimizationUsed" => %{"type" => "string"}, + "DecompiledSourceCode" => %{"type" => "string"}, + "DecompilerVersion" => %{"type" => "string"} + } + } + }) + end + + defp verify_schema do + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "Address" => %{"type" => "string"}, + "SourceCode" => %{"type" => "string"}, + "ABI" => %{"type" => "string"}, + "ContractName" => %{"type" => "string"}, + "CompilerVersion" => %{"type" => "string"}, + "DecompiledSourceCode" => %{"type" => "string"}, + "DecompilerVersion" => %{"type" => "string"}, + "OptimizationUsed" => %{"type" => "string"} + } + }) + end + + defp resolve_schema(result \\ %{}) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs index 454db88e86..0b129939b8 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs @@ -22,6 +22,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "without fromBlock", %{conn: conn} do @@ -43,6 +44,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "without toBlock", %{conn: conn} do @@ -64,6 +66,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "without address and topic{x}", %{conn: conn} do @@ -85,6 +88,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "without topic{x}_{x}_opr", %{conn: conn} do @@ -118,6 +122,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end end @@ -146,6 +151,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with invalid fromBlock", %{conn: conn} do @@ -166,6 +172,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with invalid toBlock", %{conn: conn} do @@ -186,6 +193,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with an invalid address hash", %{conn: conn} do @@ -206,6 +214,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with invalid topic{x}_{x}_opr", %{conn: conn} do @@ -238,6 +247,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end end @@ -258,6 +268,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["result"] == [] assert response["status"] == "0" assert response["message"] == "No logs found" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with a valid contract address", %{conn: conn} do @@ -302,6 +313,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "ignores logs with block below fromBlock", %{conn: conn} do @@ -340,6 +352,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) [found_log] = response["result"] @@ -383,6 +396,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) [found_log] = response["result"] @@ -445,6 +459,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with a topic{x} AND another", %{conn: conn} do @@ -493,6 +508,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert found_log["topics"] == get_topics(log1) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with a topic{x} OR another", %{conn: conn} do @@ -540,6 +556,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert length(result) == 2 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end test "with all available 'topic{x}'s and 'topic{x}_{x}_opr's", %{conn: conn} do @@ -598,6 +615,7 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do assert length(result) == 2 assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) end end @@ -771,4 +789,32 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do |> DateTime.to_unix() |> integer_to_hex() end + + defp getlogs_schema do + ExJsonSchema.Schema.resolve(%{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"}, + "result" => %{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "address" => %{"type" => "string"}, + "topics" => %{"type" => "array", "items" => %{"type" => ["string", "null"]}}, + "data" => %{"type" => "string"}, + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "logIndex" => %{"type" => "string"}, + "transactionHash" => %{"type" => "string"}, + "transactionIndex" => %{"type" => "string"} + } + } + } + } + }) + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs index 9a7c26e9ee..bf3b82a3cb 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs @@ -23,6 +23,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) end test "with an invalid contractaddress hash", %{conn: conn} do @@ -41,6 +42,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) end test "with a contractaddress that doesn't exist", %{conn: conn} do @@ -59,6 +61,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["status"] == "0" assert Map.has_key?(response, "result") refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) end test "with valid contractaddress", %{conn: conn} do @@ -78,6 +81,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["result"] == to_string(token.total_supply) assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) end end @@ -96,6 +100,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["result"] == "252460800000000000000000000" assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response) end end @@ -158,6 +163,43 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(ethprice_schema(), response) end end + + defp tokensupply_schema do + resolve_schema(%{ + "type" => ["string", "null"] + }) + end + + defp ethsupply_schema do + resolve_schema(%{ + "type" => ["string", "null"] + }) + end + + defp ethprice_schema do + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "ethbtc" => %{"type" => "string"}, + "ethbtc_timestamp" => %{"type" => "string"}, + "ethusd" => %{"type" => "string"}, + "ethusd_timestamp" => %{"type" => "string"} + } + }) + end + + defp resolve_schema(result \\ %{}) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs index 88196b8e40..a640a87441 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs @@ -84,4 +84,26 @@ defmodule BlockScoutWeb.API.RPC.TokenControllerTest do assert response["message"] == "OK" end end + + defp gettoken_schema do + ExJsonSchema.Schema.resolve(%{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"}, + "result" => %{ + "type" => "object", + "properties" => %{ + "name" => %{"type" => "string"}, + "symbol" => %{"type" => "string"}, + "totalSupply" => %{"type" => "string"}, + "decimals" => %{"type" => "string"}, + "type" => %{"type" => "string"}, + "cataloged" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"} + } + } + } + }) + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs index 1ac026bc3c..0ab9743bf7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs @@ -15,6 +15,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do |> get("/api", params) |> json_response(200) + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) assert response["message"] =~ "txhash is required" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -33,6 +35,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do |> get("/api", params) |> json_response(200) + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) assert response["message"] =~ "Invalid txhash format" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -46,11 +50,20 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "txhash" => "0x40eb908387324f2b575b4879cd9d7188f69c8fc9d87c901b9e2daaea4b442170" } + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "status" => %{"type" => "string"} + } + }) + assert response = conn |> get("/api", params) |> json_response(200) + assert ExJsonSchema.Validator.valid?(schema, response) assert response["result"] == %{"status" => ""} assert response["status"] == "1" assert response["message"] == "OK" @@ -136,6 +149,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do |> get("/api", params) |> json_response(200) + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) assert response["message"] =~ "txhash is required" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -154,6 +169,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do |> get("/api", params) |> json_response(200) + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) assert response["message"] =~ "Invalid txhash format" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -172,11 +189,21 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "errDescription" => "" } + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "isError" => %{"type" => "string"}, + "errDescription" => %{"type" => "string"} + } + }) + assert response = conn |> get("/api", params) |> json_response(200) + assert ExJsonSchema.Validator.valid?(schema, response) assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" @@ -325,6 +352,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do |> get("/api", params) |> json_response(200) + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) assert response["message"] =~ "txhash is required" assert response["status"] == "0" assert Map.has_key?(response, "result") @@ -392,11 +421,32 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "txhash" => "#{transaction.hash}" } + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "next_page_params" => %{ + "type" => ["object", "null"], + "properties" => %{ + "action" => %{"type" => "string"}, + "index" => %{"type" => "number"}, + "module" => %{"type" => "string"}, + "txhash" => %{"type" => "string"} + } + }, + "logs" => %{ + "type" => "array", + "items" => %{"type" => "object"} + } + } + }) + assert response1 = conn |> get("/api", params1) |> json_response(200) + assert ExJsonSchema.Validator.valid?(schema, response1) assert response1["status"] == "1" assert response1["message"] == "OK" @@ -414,6 +464,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do |> get("/api", params2) |> json_response(200) + assert ExJsonSchema.Validator.valid?(schema, response2) assert response2["status"] == "1" assert response2["message"] == "OK" assert is_nil(response2["result"]["next_page_params"]) @@ -468,14 +519,70 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "next_page_params" => nil } + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "hash" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "blockNumber" => %{"type" => "string"}, + "confirmations" => %{"type" => "string"}, + "success" => %{"type" => "boolean"}, + "from" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "gasLimit" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "logs" => %{ + "type" => "array", + "items" => %{ + "type" => "object", + "properties" => %{ + "address" => %{"type" => "string"}, + "data" => %{"type" => "string"}, + "topics" => %{ + "type" => "array", + "items" => %{"type" => ["string", "null"]} + }, + "index" => %{"type" => "string"} + } + } + }, + "next_page_params" => %{ + "type" => ["object", "null"], + "properties" => %{ + "action" => %{"type" => "string"}, + "index" => %{"type" => "number"}, + "module" => %{"type" => "string"}, + "txhash" => %{"type" => "string"} + } + } + } + }) + assert response = conn |> get("/api", params) |> json_response(200) + assert ExJsonSchema.Validator.valid?(schema, response) assert response["result"] == expected_result assert response["status"] == "1" assert response["message"] == "OK" end end + + defp resolve_schema(result \\ %{}) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex index 1b77eac048..5b2fea35cc 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex @@ -35,11 +35,23 @@ defmodule EthereumJSONRPC.DecodeError do %EthereumJSONRPC.DecodeError{request: request, response: response} end + @request_body_limit 700 + @impl Exception def message(%EthereumJSONRPC.DecodeError{ request: %EthereumJSONRPC.DecodeError.Request{url: request_url, body: request_body}, response: %EthereumJSONRPC.DecodeError.Response{status_code: response_status_code, body: response_body} }) do + request_body_binary = IO.iodata_to_binary(request_body) + + truncated_request_body = + if byte_size(request_body_binary) < @request_body_limit do + request_body_binary + else + <> = request_body_binary + result + end + """ Failed to decode Ethereum JSONRPC response: @@ -47,7 +59,7 @@ defmodule EthereumJSONRPC.DecodeError do url: #{request_url} - body: #{IO.iodata_to_binary(request_body)} + body: #{truncated_request_body} response: diff --git a/apps/explorer/priv/repo/migrations/20190807113117_create_suggested_indexes.exs b/apps/explorer/priv/repo/migrations/20190807113117_create_suggested_indexes.exs new file mode 100644 index 0000000000..d44d610923 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190807113117_create_suggested_indexes.exs @@ -0,0 +1,8 @@ +defmodule Explorer.Repo.Migrations.CreateSuggestedIndexes do + use Ecto.Migration + + def change do + create_if_not_exists(index(:address_token_balances, [:block_number, :address_hash])) + create_if_not_exists(index(:block_rewards, [:block_hash])) + end +end diff --git a/mix.lock b/mix.lock index c85605b868..d589137148 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "absinthe": {:hex, :absinthe, "1.4.16", "0933e4d9f12652b12115d5709c0293a1bf78a22578032e9ad0dad4efee6b9eb1", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, - "absinthe_phoenix": {:git, "https://github.com/ayrat555/absinthe_phoenix.git", "4dbb73a25a0935a4d292e63876698e18534d835f", [branch: "master"]}, + "absinthe_phoenix": {:git, "https://github.com/ayrat555/absinthe_phoenix.git", "4dbb73a25a0935a4d292e63876698e18534d835f", [branch: "ab-update-plug"]}, "absinthe_plug": {:git, "https://github.com/ayrat555/absinthe_plug.git", "cbe1c170e11e60b3b0146b925a1ce6ec562840ce", [branch: "ab-enable-default-query"]}, "absinthe_relay": {:hex, :absinthe_relay, "1.4.6", "ec0e2288994b388556247cf9601245abec785cdf206d6e844f2992d29de21624", [:mix], [{:absinthe, "~> 1.4.0", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm"}, @@ -39,6 +39,7 @@ "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.6.4", "5b1ac8451f889576bb29dee70412de1170974298727ab944aa4d17e91bdd3472", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.3", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ex_cldr_units": {:hex, :ex_cldr_units, "2.5.1", "0e65067a22a7c5146266c313d6333c2700868c32aa6d536f47c6c0d84aac3ac1", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.2", [hex: :ex_cldr_lists, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.6", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_json_schema": {:hex, :ex_json_schema, "0.6.1", "b57c0588385b8262b80f19d33d9b9b71fcd60d247691abf2635b57a03ec0ad44", [:mix], [], "hexpm"}, "ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"}, "ex_rlp": {:hex, :ex_rlp, "0.5.2", "7f4ce7bd55e543c054ce6d49629b01e9833c3462e3d547952be89865f39f2c58", [:mix], [], "hexpm"}, "ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm"},