Done tests for smart-contract controller

pull/6642/head
Никита Поздняков 2 years ago
parent 65dd29069e
commit 1fe9213a5c
No known key found for this signature in database
GPG Key ID: F344106F9804FE5F
  1. 16
      apps/block_scout_web/lib/block_scout_web/api_router.ex
  2. 1
      apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex
  3. 504
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs

@ -28,6 +28,12 @@ defmodule BlockScoutWeb.ApiRouter do
pipeline :api_v2 do pipeline :api_v2 do
plug(CheckApiV2) plug(CheckApiV2)
plug(:fetch_session) plug(:fetch_session)
plug(:protect_from_forgery)
end
pipeline :api_v2_no_forgery_protect do
plug(CheckApiV2)
plug(:fetch_session)
end end
alias BlockScoutWeb.Account.Api.V1.{TagsController, UserController} alias BlockScoutWeb.Account.Api.V1.{TagsController, UserController}
@ -135,7 +141,6 @@ defmodule BlockScoutWeb.ApiRouter do
get("/:address_hash/methods-write", V2.SmartContractController, :methods_write) get("/:address_hash/methods-write", V2.SmartContractController, :methods_write)
get("/:address_hash/methods-read-proxy", V2.SmartContractController, :methods_read_proxy) get("/:address_hash/methods-read-proxy", V2.SmartContractController, :methods_read_proxy)
get("/:address_hash/methods-write-proxy", V2.SmartContractController, :methods_write_proxy) get("/:address_hash/methods-write-proxy", V2.SmartContractController, :methods_write_proxy)
post("/:address_hash/query-read-method", V2.SmartContractController, :query_read_method)
end end
scope "/tokens" do scope "/tokens" do
@ -161,6 +166,15 @@ defmodule BlockScoutWeb.ApiRouter do
end end
end end
scope "/v2", as: :api_v2 do
pipe_through(:api)
pipe_through(:api_v2_no_forgery_protect)
scope "/smart-contracts" do
post("/:address_hash/query-read-method", BlockScoutWeb.API.V2.SmartContractController, :query_read_method)
end
end
scope "/v1", as: :api_v1 do scope "/v1", as: :api_v1 do
pipe_through(:api) pipe_through(:api)
alias BlockScoutWeb.API.{EthRPC, RPC, V1} alias BlockScoutWeb.API.{EthRPC, RPC, V1}

@ -99,6 +99,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
end end
end end
# credo:disable-for-next-line
def prepare_smart_contract(address) do def prepare_smart_contract(address) do
minimal_proxy_template = Chain.get_minimal_proxy_template(address.hash) minimal_proxy_template = Chain.get_minimal_proxy_template(address.hash)

@ -658,6 +658,502 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
end end
end end
describe "/smart-contracts/{address_hash}/methods-read-proxy" do
test "get 404 on non existing SC", %{conn: conn} do
address = build(:address)
request = get(conn, "/api/v2/smart-contracts/#{address.hash}/methods-read-proxy")
assert %{"message" => "Not found"} = json_response(request, 404)
end
test "get 422 on invalid address", %{conn: conn} do
request = get(conn, "/api/v2/smart-contracts/0x/methods-read-proxy")
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422)
end
test "get read-methods", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
"name" => "getCaller",
"inputs" => []
},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}]
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
]
target_contract = insert(:smart_contract, abi: abi)
blockchain_get_code_mock()
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x000000000000000000000000#{target_contract.address_hash |> to_string() |> String.replace("0x", "")}"}
end)
address_hash = to_string(target_contract.address_hash)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [%{id: id, method: "eth_call", params: [%{to: address_hash}, _]}], opts ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: "0x000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"
}
]}
end
)
contract = insert(:smart_contract)
request = get(conn, "/api/v2/smart-contracts/#{contract.address_hash}/methods-read-proxy")
assert response = json_response(request, 200)
assert %{
"type" => "function",
"stateMutability" => "view",
"outputs" => [
%{
"type" => "address",
"name" => "",
"internalType" => "address",
"value" => "0xfffffffffffffffffffffffffffffffffffffffe"
}
],
"name" => "getCaller",
"inputs" => [],
"method_id" => "ab470f05"
} in response
assert %{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool", "value" => ""}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}],
"method_id" => "c683630d"
} in response
end
end
describe "/smart-contracts/{address_hash}/query-read-method proxy" do
test "get 404 on non existing SC", %{conn: conn} do
address = build(:address)
request =
post(conn, "/api/v2/smart-contracts/#{address.hash}/query-read-method", %{
"contract_type" => "proxy",
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"],
"method_id" => "c683630d"
})
assert %{"message" => "Not found"} = json_response(request, 404)
end
test "get 422 on invalid address", %{conn: conn} do
request =
post(conn, "/api/v2/smart-contracts/0x/query-read-method", %{
"contract_type" => "proxy",
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"],
"method_id" => "c683630d"
})
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422)
end
test "query-read-method", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
"name" => "getCaller",
"inputs" => []
},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}]
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
]
target_contract = insert(:smart_contract, abi: abi)
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x000000000000000000000000#{target_contract.address_hash |> to_string() |> String.replace("0x", "")}"}
end)
address_hash = target_contract.address_hash |> to_string()
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
method: "eth_call",
params: [
%{
data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe",
to: address_hash
},
_
]
}
],
_opts ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: "0x0000000000000000000000000000000000000000000000000000000000000001"
}
]}
end
)
contract = insert(:smart_contract)
request =
post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/query-read-method", %{
"contract_type" => "proxy",
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"],
"method_id" => "c683630d"
})
assert response = json_response(request, 200)
assert %{
"is_error" => false,
"result" => %{"names" => ["bool"], "output" => [%{"type" => "bool", "value" => true}]}
} == response
end
test "query-read-method returns error 1", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
"name" => "getCaller",
"inputs" => []
},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}]
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
]
target_contract = insert(:smart_contract, abi: abi)
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x000000000000000000000000#{target_contract.address_hash |> to_string() |> String.replace("0x", "")}"}
end)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
method: "eth_call",
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _]
}
],
_opts ->
{:ok, [%{id: id, jsonrpc: "2.0", error: %{code: "12345", message: "Error message"}}]}
end
)
contract = insert(:smart_contract)
request =
post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/query-read-method", %{
"contract_type" => "proxy",
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"],
"method_id" => "c683630d"
})
assert response = json_response(request, 200)
assert %{"is_error" => true, "result" => %{"code" => "12345", "message" => "Error message"}} == response
end
test "query-read-method returns error 2", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
"name" => "getCaller",
"inputs" => []
},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}]
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
]
target_contract = insert(:smart_contract, abi: abi)
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x000000000000000000000000#{target_contract.address_hash |> to_string() |> String.replace("0x", "")}"}
end)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
method: "eth_call",
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _]
}
],
_opts ->
{:error, {:bad_gateway, "request_url"}}
end
)
contract = insert(:smart_contract)
request =
post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/query-read-method", %{
"contract_type" => "proxy",
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"],
"method_id" => "c683630d"
})
assert response = json_response(request, 200)
assert %{"is_error" => true, "result" => %{"error" => "Bad gateway"}} == response
end
test "query-read-method returns error 3", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
"name" => "getCaller",
"inputs" => []
},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}]
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
]
target_contract = insert(:smart_contract, abi: abi)
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x000000000000000000000000#{target_contract.address_hash |> to_string() |> String.replace("0x", "")}"}
end)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
method: "eth_call",
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _]
}
],
_opts ->
raise FunctionClauseError
end
)
contract = insert(:smart_contract)
request =
post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/query-read-method", %{
"contract_type" => "proxy",
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"],
"method_id" => "c683630d"
})
assert response = json_response(request, 200)
assert %{"is_error" => true, "result" => %{"error" => "no function clause matches"}} == response
end
end
describe "/smart-contracts/{address_hash}/methods-write-proxy" do
test "get 404 on non existing SC", %{conn: conn} do
address = build(:address)
request = get(conn, "/api/v2/smart-contracts/#{address.hash}/methods-write-proxy")
assert %{"message" => "Not found"} = json_response(request, 404)
end
test "get 422 on invalid address", %{conn: conn} do
request = get(conn, "/api/v2/smart-contracts/0x/methods-write-proxy")
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422)
end
test "get write-methods", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
"name" => "getCaller",
"inputs" => []
},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}],
"name" => "isWhitelist",
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}]
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
]
target_contract = insert(:smart_contract, abi: abi)
blockchain_get_code_mock()
expect(EthereumJSONRPC.Mox, :json_rpc, fn %{
id: 0,
method: "eth_getStorageAt",
params: [
_,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"
]
},
_options ->
{:ok, "0x000000000000000000000000#{target_contract.address_hash |> to_string() |> String.replace("0x", "")}"}
end)
contract = insert(:smart_contract)
request = get(conn, "/api/v2/smart-contracts/#{contract.address_hash}/methods-write-proxy")
assert response = json_response(request, 200)
assert [
%{
"type" => "function",
"stateMutability" => "nonpayable",
"outputs" => [],
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
}
] == response
end
end
defp blockchain_get_code_mock do defp blockchain_get_code_mock do
expect( expect(
EthereumJSONRPC.Mox, EthereumJSONRPC.Mox,
@ -709,12 +1205,4 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
end end
) )
end end
defp debug(value, key) do
require Logger
Logger.configure(truncate: :infinity)
Logger.info(key)
Logger.info(Kernel.inspect(value, limit: :infinity, printable_limit: :infinity))
value
end
end end

Loading…
Cancel
Save