commit
3cc9a7c4f4
@ -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 |
@ -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 |
@ -0,0 +1,7 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
rm -rf ./_build |
||||||
|
rm -rf ./deps |
||||||
|
rm -rf ./logs/dev |
||||||
|
rm -rf ./apps/explorer/node_modules |
||||||
|
rm -rf ./apps/block_scout_web/assets/node_modules |
Loading…
Reference in new issue