feat: Add Stylus verificaiton support (#11183)
* feat: Add Stylus verificaiton support * Fix tests * Apply suggestions from code review Co-authored-by: Alexander Kolotov <alexandr.kolotov@gmail.com> * Refactoring + handle snake case * Add env to docker-compose/envs/common-blockscout.env * Apply suggestions from code review Co-authored-by: Alexander Kolotov <alexandr.kolotov@gmail.com> * Fix cspell * Drop MICROSERVICE_STYLUS_VERIFIER_ENABLED * Fix tests * Fix tests * Apply suggestions from code review Co-authored-by: Alexander Kolotov <alexandr.kolotov@gmail.com> * Process review comments * Fix dialyzer --------- Co-authored-by: Alexander Kolotov <alexandr.kolotov@gmail.com>master
parent
c4e6f9c384
commit
d5709dfda9
@ -1,6 +1,8 @@ |
||||
lib/ethereum_jsonrpc/rolling_window.ex:171 |
||||
lib/explorer/smart_contract/solidity/publisher_worker.ex:1 |
||||
lib/explorer/smart_contract/vyper/publisher_worker.ex:1 |
||||
lib/explorer/smart_contract/stylus/publisher_worker.ex:1 |
||||
lib/explorer/smart_contract/solidity/publisher_worker.ex:8 |
||||
lib/explorer/smart_contract/vyper/publisher_worker.ex:8 |
||||
lib/explorer/smart_contract/stylus/publisher_worker.ex:8 |
||||
lib/phoenix/router.ex:402 |
||||
|
@ -0,0 +1,235 @@ |
||||
defmodule Explorer.SmartContract.Stylus.Publisher do |
||||
@moduledoc """ |
||||
Module responsible for verifying and publishing Stylus smart contracts. |
||||
|
||||
The verification process includes: |
||||
1. Initiating verification through a microservice that compares GitHub repository |
||||
source code against deployed bytecode |
||||
2. Processing the verification response, including ABI and source files |
||||
3. Creating or updating the smart contract record in the database |
||||
4. Handling verification failures by creating invalid changesets with error messages |
||||
""" |
||||
|
||||
require Logger |
||||
|
||||
alias Explorer.Chain.SmartContract |
||||
alias Explorer.SmartContract.Helper |
||||
alias Explorer.SmartContract.Stylus.Verifier |
||||
|
||||
@default_file_name "src/lib.rs" |
||||
|
||||
@sc_verification_via_github_repository_started "Smart-contract verification via Github repository started" |
||||
|
||||
@doc """ |
||||
Verifies and publishes a Stylus smart contract using GitHub repository source code. |
||||
|
||||
Initiates verification of a contract through the verification microservice. On |
||||
successful verification, processes and stores the contract details in the |
||||
database. On failure, creates an invalid changeset with appropriate error |
||||
messages. |
||||
|
||||
## Parameters |
||||
- `address_hash`: The contract's address hash as binary or `t:Explorer.Chain.Hash.t/0` |
||||
- `params`: Map containing verification parameters: |
||||
- `"cargo_stylus_version"`: Version of cargo-stylus used for deployment |
||||
- `"repository_url"`: GitHub repository URL containing contract code |
||||
- `"commit"`: Git commit hash used for deployment |
||||
- `"path_prefix"`: Optional path prefix if contract is not in repository root |
||||
|
||||
## Returns |
||||
- `{:ok, smart_contract}` if verification and database storage succeed |
||||
- `{:error, changeset}` if verification fails or there are validation errors |
||||
""" |
||||
@spec publish(binary() | Explorer.Chain.Hash.t(), %{String.t() => any()}) :: |
||||
{:error, Ecto.Changeset.t()} | {:ok, Explorer.Chain.SmartContract.t()} |
||||
def publish(address_hash, params) do |
||||
Logger.info(@sc_verification_via_github_repository_started) |
||||
|
||||
case Verifier.evaluate_authenticity(address_hash, params) do |
||||
{ |
||||
:ok, |
||||
%{ |
||||
"abi" => _, |
||||
"cargo_stylus_version" => _, |
||||
"contract_name" => _, |
||||
"files" => _, |
||||
"package_name" => _, |
||||
"github_repository_metadata" => _ |
||||
} = result_params |
||||
} -> |
||||
process_verifier_response(result_params, address_hash) |
||||
|
||||
{:error, error} -> |
||||
{:error, unverified_smart_contract(address_hash, params, error, nil)} |
||||
|
||||
_ -> |
||||
{:error, unverified_smart_contract(address_hash, params, "Unexpected error", nil)} |
||||
end |
||||
end |
||||
|
||||
# Processes successful Stylus contract verification response and stores contract data. |
||||
# |
||||
# Takes the verification response from `evaluate_authenticity/2` containing verified contract |
||||
# details and prepares them for storage in the database. The main source file is extracted |
||||
# from `files` map using the default filename, while other files are stored as secondary |
||||
# sources. |
||||
# |
||||
# ## Parameters |
||||
# - `response`: Verification response map containing: |
||||
# - `abi`: Contract ABI as JSON string |
||||
# - `cargo_stylus_version`: Version of cargo-stylus used |
||||
# - `contract_name`: Name of the contract |
||||
# - `files`: Map of file paths to source code contents |
||||
# - `package_name`: Package name of the contract |
||||
# - `github_repository_metadata`: Repository metadata |
||||
# - `address_hash`: The contract's address hash as binary or `t:Explorer.Chain.Hash.t/0` |
||||
# |
||||
# ## Returns |
||||
# - `{:ok, smart_contract}` if database storage succeeds |
||||
# - `{:error, changeset}` if there are validation errors |
||||
# - `{:error, message}` if the database operation fails |
||||
@spec process_verifier_response(%{String.t() => any()}, binary() | Explorer.Chain.Hash.t()) :: |
||||
{:ok, Explorer.Chain.SmartContract.t()} | {:error, Ecto.Changeset.t() | String.t()} |
||||
defp process_verifier_response( |
||||
%{ |
||||
"abi" => abi_string, |
||||
"cargo_stylus_version" => cargo_stylus_version, |
||||
"contract_name" => contract_name, |
||||
"files" => files, |
||||
"package_name" => package_name, |
||||
"github_repository_metadata" => github_repository_metadata |
||||
}, |
||||
address_hash |
||||
) do |
||||
secondary_sources = |
||||
for {file, code} <- files, |
||||
file != @default_file_name, |
||||
do: %{"file_name" => file, "contract_source_code" => code, "address_hash" => address_hash} |
||||
|
||||
contract_source_code = files[@default_file_name] |
||||
|
||||
prepared_params = |
||||
%{} |
||||
|> Map.put("compiler_version", cargo_stylus_version) |
||||
|> Map.put("contract_source_code", contract_source_code) |
||||
|> Map.put("name", contract_name) |
||||
|> Map.put("file_path", contract_source_code && @default_file_name) |
||||
|> Map.put("secondary_sources", secondary_sources) |
||||
|> Map.put("package_name", package_name) |
||||
|> Map.put("github_repository_metadata", github_repository_metadata) |
||||
|
||||
publish_smart_contract(address_hash, prepared_params, Jason.decode!(abi_string || "null")) |
||||
end |
||||
|
||||
# Stores information about a verified Stylus smart contract in the database. |
||||
# |
||||
# ## Parameters |
||||
# - `address_hash`: The contract's address hash as binary or `t:Explorer.Chain.Hash.t/0` |
||||
# - `params`: Map containing contract details: |
||||
# - `name`: Contract name |
||||
# - `file_path`: Path to the contract source file |
||||
# - `compiler_version`: Version of the Stylus compiler |
||||
# - `contract_source_code`: Source code of the contract |
||||
# - `secondary_sources`: Additional source files |
||||
# - `package_name`: Package name for Stylus contract |
||||
# - `github_repository_metadata`: Repository metadata |
||||
# - `abi`: Contract's ABI (Application Binary Interface) |
||||
# |
||||
# ## Returns |
||||
# - `{:ok, smart_contract}` if publishing succeeds |
||||
# - `{:error, changeset}` if there are validation errors |
||||
# - `{:error, message}` if the database operation fails |
||||
@spec publish_smart_contract(binary() | Explorer.Chain.Hash.t(), %{String.t() => any()}, map()) :: |
||||
{:error, Ecto.Changeset.t() | String.t()} | {:ok, Explorer.Chain.SmartContract.t()} |
||||
defp publish_smart_contract(address_hash, params, abi) do |
||||
attrs = address_hash |> attributes(params, abi) |
||||
|
||||
create_or_update_smart_contract(address_hash, attrs) |
||||
end |
||||
|
||||
# This function first checks if a smart contract already exists in the database |
||||
# at the given address. If it exists, updates the contract with new attributes. |
||||
# Otherwise, creates a new smart contract record. |
||||
@spec create_or_update_smart_contract(binary() | Explorer.Chain.Hash.t(), map()) :: |
||||
{:error, Ecto.Changeset.t() | String.t()} | {:ok, Explorer.Chain.SmartContract.t()} |
||||
defp create_or_update_smart_contract(address_hash, attrs) do |
||||
Logger.info("Publish successfully verified Stylus smart-contract #{address_hash} into the DB") |
||||
|
||||
if SmartContract.verified?(address_hash) do |
||||
SmartContract.update_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) |
||||
else |
||||
SmartContract.create_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) |
||||
end |
||||
end |
||||
|
||||
# Creates an invalid changeset for a Stylus smart contract that failed verification. |
||||
# |
||||
# Prepares contract attributes with MD5 hash of bytecode and creates an invalid changeset |
||||
# with appropriate error messages. The changeset is marked with `:insert` action to |
||||
# indicate a failed verification attempt. |
||||
# |
||||
# ## Parameters |
||||
# - `address_hash`: The contract's address hash |
||||
# - `params`: Map containing contract details from verification attempt |
||||
# - `error`: The verification error that occurred |
||||
# - `error_message`: Optional custom error message |
||||
# - `verification_with_files?`: Boolean indicating if verification used source files. |
||||
# Defaults to `false` |
||||
# |
||||
# ## Returns |
||||
# An invalid `t:Ecto.Changeset.t/0` with: |
||||
# - Contract attributes including MD5 hash of bytecode |
||||
# - Error message attached to appropriate field |
||||
# - Action set to `:insert` |
||||
@spec unverified_smart_contract(binary() | Explorer.Chain.Hash.t(), %{String.t() => any()}, any(), any(), boolean()) :: |
||||
Ecto.Changeset.t() |
||||
defp unverified_smart_contract(address_hash, params, error, error_message, verification_with_files? \\ false) do |
||||
attrs = |
||||
address_hash |
||||
|> attributes(params |> Map.put("compiler_version", params["cargo_stylus_version"])) |
||||
|> Helper.add_contract_code_md5() |
||||
|
||||
changeset = |
||||
SmartContract.invalid_contract_changeset( |
||||
%SmartContract{address_hash: address_hash}, |
||||
attrs, |
||||
error, |
||||
error_message, |
||||
verification_with_files? |
||||
) |
||||
|
||||
Logger.error("Stylus smart-contract verification #{address_hash} failed because of the error #{inspect(error)}") |
||||
|
||||
%{changeset | action: :insert} |
||||
end |
||||
|
||||
defp attributes(address_hash, params, abi \\ %{}) do |
||||
%{ |
||||
address_hash: address_hash, |
||||
name: params["name"], |
||||
file_path: params["file_path"], |
||||
compiler_version: params["compiler_version"], |
||||
evm_version: nil, |
||||
optimization_runs: nil, |
||||
optimization: false, |
||||
contract_source_code: params["contract_source_code"], |
||||
constructor_arguments: nil, |
||||
external_libraries: [], |
||||
secondary_sources: params["secondary_sources"], |
||||
abi: abi, |
||||
verified_via_sourcify: false, |
||||
verified_via_eth_bytecode_db: false, |
||||
verified_via_verifier_alliance: false, |
||||
partially_verified: false, |
||||
is_vyper_contract: false, |
||||
autodetect_constructor_args: false, |
||||
is_yul: false, |
||||
compiler_settings: nil, |
||||
license_type: :none, |
||||
is_blueprint: false, |
||||
language: :stylus_rust, |
||||
package_name: params["package_name"], |
||||
github_repository_metadata: params["github_repository_metadata"] |
||||
} |
||||
end |
||||
end |
@ -0,0 +1,72 @@ |
||||
defmodule Explorer.SmartContract.Stylus.PublisherWorker do |
||||
@moduledoc """ |
||||
Processes Stylus smart contract verification requests asynchronously in the background. |
||||
|
||||
This module implements a worker that handles verification of Stylus smart contracts |
||||
through their GitHub repository source code. It uses a job queue system to: |
||||
- Receive verification requests containing contract address and GitHub details |
||||
- Delegate verification to the Publisher module |
||||
- Broadcast verification results through the events system |
||||
""" |
||||
|
||||
require Logger |
||||
|
||||
use Que.Worker, concurrency: 5 |
||||
|
||||
alias Explorer.Chain.Events.Publisher, as: EventsPublisher |
||||
alias Explorer.SmartContract.Stylus.Publisher |
||||
|
||||
@doc """ |
||||
Processes a Stylus smart contract verification request. |
||||
|
||||
Initiates the verification process by broadcasting the verification request to |
||||
the module responsible for the actual verification and consequent update of |
||||
the database. This function is called automatically by the job queue system. |
||||
|
||||
## Parameters |
||||
- `{"github_repository", params}`: Tuple containing: |
||||
- First element: `"github_repository"` indicating the verification source |
||||
- Second element: Map containing: |
||||
- `"address_hash"`: The contract's address hash to verify |
||||
|
||||
## Returns |
||||
- Result of the broadcast operation |
||||
""" |
||||
@spec perform({binary(), %{String.t() => any()}}) :: any() |
||||
def perform({"github_repository", %{"address_hash" => address_hash} = params}) do |
||||
broadcast(:publish, address_hash, [address_hash, params]) |
||||
end |
||||
|
||||
# Broadcasts the result of a Stylus smart contract verification attempt. |
||||
# |
||||
# Executes the specified verification method in the `Publisher` module and |
||||
# broadcasts the result through the events publisher. |
||||
# |
||||
# ## Parameters |
||||
# - `method`: The verification method to execute |
||||
# - `address_hash`: Contract address |
||||
# - `args`: Arguments to pass to the verification method |
||||
# |
||||
# ## Returns |
||||
# - `{:ok, contract}` if verification succeeds |
||||
# - `{:error, changeset}` if verification fails |
||||
@spec broadcast(atom(), binary() | Explorer.Chain.Hash.t(), any()) :: any() |
||||
defp broadcast(method, address_hash, args) do |
||||
result = |
||||
case apply(Publisher, method, args) do |
||||
{:ok, _contract} = result -> |
||||
result |
||||
|
||||
{:error, changeset} -> |
||||
Logger.error( |
||||
"Stylus smart-contract verification #{address_hash} failed because of the error: #{inspect(changeset)}" |
||||
) |
||||
|
||||
{:error, changeset} |
||||
end |
||||
|
||||
Logger.info("Smart-contract #{address_hash} verification: broadcast verification results") |
||||
|
||||
EventsPublisher.broadcast([{:contract_verification_result, {String.downcase(address_hash), result}}], :on_demand) |
||||
end |
||||
end |
@ -0,0 +1,108 @@ |
||||
defmodule Explorer.SmartContract.Stylus.Verifier do |
||||
@moduledoc """ |
||||
Verifies Stylus smart contracts by comparing their source code against deployed bytecode. |
||||
|
||||
This module handles verification of Stylus smart contracts through their GitHub repository |
||||
source code. It interfaces with a verification microservice that: |
||||
- Fetches source code from the specified GitHub repository and commit |
||||
- Compiles the code using the specified cargo-stylus version |
||||
- Compares the resulting bytecode against the deployed contract bytecode |
||||
- Returns verification details including ABI and contract metadata |
||||
""" |
||||
alias Explorer.Chain.{Hash, SmartContract} |
||||
alias Explorer.SmartContract.StylusVerifierInterface |
||||
|
||||
require Logger |
||||
|
||||
@doc """ |
||||
Verifies a Stylus smart contract by comparing source code from a GitHub repository against the deployed bytecode using a verification microservice. |
||||
|
||||
## Parameters |
||||
- `address_hash`: Contract address |
||||
- `params`: Map containing verification parameters: |
||||
- `cargo_stylus_version`: Version of cargo-stylus used for deployment |
||||
- `repository_url`: GitHub repository URL containing contract code |
||||
- `commit`: Git commit hash used for deployment |
||||
- `path_prefix`: Optional path prefix if contract is not in repository root |
||||
|
||||
## Returns |
||||
- `{:ok, map}` with verification details: |
||||
- `abi`: Contract ABI (optional) |
||||
- `contract_name`: Contract name (optional) |
||||
- `package_name`: Package name |
||||
- `files`: Map of file paths to contents used in verification |
||||
- `cargo_stylus_version`: Version of cargo-stylus used |
||||
- `github_repository_metadata`: Repository metadata (optional) |
||||
- `{:error, any}` if verification fails or is disabled |
||||
""" |
||||
@spec evaluate_authenticity(EthereumJSONRPC.address() | Hash.Address.t(), map()) :: |
||||
{:ok, map()} | {:error, any()} |
||||
def evaluate_authenticity(address_hash, params) do |
||||
evaluate_authenticity_inner(StylusVerifierInterface.enabled?(), address_hash, params) |
||||
rescue |
||||
exception -> |
||||
Logger.error(fn -> |
||||
[ |
||||
"Error while verifying smart-contract address: #{address_hash}, params: #{inspect(params, limit: :infinity, printable_limit: :infinity)}: ", |
||||
Exception.format(:error, exception, __STACKTRACE__) |
||||
] |
||||
end) |
||||
end |
||||
|
||||
# Verifies the authenticity of a Stylus smart contract using GitHub repository source code. |
||||
# |
||||
# This function retrieves the contract creation transaction and blockchain RPC endpoint, |
||||
# which together with passed parameters are required by the verification microservice to |
||||
# validate the contract deployment and verify the source code against the deployed |
||||
# bytecode. |
||||
# |
||||
# ## Parameters |
||||
# - `true`: Required boolean flag to proceed with verification |
||||
# - `address_hash`: Contract address |
||||
# - `params`: Map containing verification parameters |
||||
# |
||||
# ## Returns |
||||
# - `{:ok, map}` with verification details including ABI, contract name, and source files |
||||
# - `{:error, any}` if verification fails |
||||
@spec evaluate_authenticity_inner(boolean(), EthereumJSONRPC.address() | Hash.Address.t(), map()) :: |
||||
{:ok, map()} | {:error, any()} |
||||
defp evaluate_authenticity_inner(true, address_hash, params) do |
||||
transaction_hash = fetch_data_for_stylus_verification(address_hash) |
||||
rpc_endpoint = Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options][:url] |
||||
|
||||
params |
||||
|> Map.take(["cargo_stylus_version", "repository_url", "commit", "path_prefix"]) |
||||
|> Map.put("rpc_endpoint", rpc_endpoint) |
||||
|> Map.put("deployment_transaction", transaction_hash) |
||||
|> StylusVerifierInterface.verify_github_repository() |
||||
end |
||||
|
||||
defp evaluate_authenticity_inner(false, _address_hash, _params) do |
||||
{:error, "Stylus verification is disabled"} |
||||
end |
||||
|
||||
# Retrieves the transaction hash that created a Stylus smart contract. |
||||
|
||||
# Looks up the creation transaction for the given contract address and returns its hash. |
||||
# Checks both regular transactions and internal transactions. |
||||
|
||||
# ## Parameters |
||||
# - `address_hash`: The address hash of the smart contract as a binary or `t:Hash.Address.t/0` |
||||
|
||||
# ## Returns |
||||
# - `t:Hash.t/0` - The transaction hash if found |
||||
# - `nil` - If no creation transaction exists |
||||
@spec fetch_data_for_stylus_verification(binary() | Hash.Address.t()) :: Hash.t() | nil |
||||
defp fetch_data_for_stylus_verification(address_hash) do |
||||
case SmartContract.creation_transaction_with_bytecode(address_hash) do |
||||
%{transaction: transaction} -> |
||||
transaction.hash |
||||
|
||||
%{internal_transaction: internal_transaction} -> |
||||
internal_transaction.transaction_hash |
||||
|
||||
_ -> |
||||
nil |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,165 @@ |
||||
defmodule Explorer.SmartContract.StylusVerifierInterface do |
||||
@moduledoc """ |
||||
Provides an interface for verifying Stylus smart contracts by interacting with a verification |
||||
microservice. |
||||
|
||||
Handles verification requests for Stylus contracts deployed from GitHub repositories by |
||||
communicating with an external verification service. |
||||
""" |
||||
alias HTTPoison.Response |
||||
require Logger |
||||
|
||||
@post_timeout :timer.minutes(5) |
||||
@request_error_msg "Error while sending request to stylus verification microservice" |
||||
|
||||
@doc """ |
||||
Verifies a Stylus smart contract using source code from a GitHub repository. |
||||
|
||||
Sends verification request to the verification microservice with repository details |
||||
and deployment information. |
||||
|
||||
## Parameters |
||||
- `body`: A map containing: |
||||
- `deployment_transaction`: Transaction hash where contract was deployed |
||||
- `rpc_endpoint`: RPC endpoint URL for the chain |
||||
- `cargo_stylus_version`: Version of cargo-stylus used for deployment |
||||
- `repository_url`: GitHub repository URL containing contract code |
||||
- `commit`: Git commit hash used for deployment |
||||
- `path_prefix`: Optional path prefix if contract is not in repository root |
||||
|
||||
## Returns |
||||
- `{:ok, map}` with verification details: |
||||
- `abi`: Contract ABI (optional) |
||||
- `contract_name`: Contract name (optional) |
||||
- `package_name`: Package name |
||||
- `files`: Map of file paths to contents used in verification |
||||
- `cargo_stylus_version`: Version of cargo-stylus used |
||||
- `github_repository_metadata`: Repository metadata (optional) |
||||
- `{:error, any}` if verification fails |
||||
""" |
||||
@spec verify_github_repository(map()) :: {:ok, map()} | {:error, any()} |
||||
def verify_github_repository( |
||||
%{ |
||||
"deployment_transaction" => _, |
||||
"rpc_endpoint" => _, |
||||
"cargo_stylus_version" => _, |
||||
"repository_url" => _, |
||||
"commit" => _, |
||||
"path_prefix" => _ |
||||
} = body |
||||
) do |
||||
http_post_request(github_repository_verification_url(), body) |
||||
end |
||||
|
||||
@spec http_post_request(String.t(), map()) :: {:ok, map()} | {:error, any()} |
||||
defp http_post_request(url, body) do |
||||
headers = [{"Content-Type", "application/json"}] |
||||
|
||||
case HTTPoison.post(url, Jason.encode!(body), headers, recv_timeout: @post_timeout) do |
||||
{:ok, %Response{body: body, status_code: _}} -> |
||||
process_verifier_response(body) |
||||
|
||||
{:error, error} -> |
||||
Logger.error(fn -> |
||||
[ |
||||
"Error while sending request to verification microservice url: #{url}, body: #{inspect(body, limit: :infinity, printable_limit: :infinity)}: ", |
||||
inspect(error, limit: :infinity, printable_limit: :infinity) |
||||
] |
||||
end) |
||||
|
||||
{:error, @request_error_msg} |
||||
end |
||||
end |
||||
|
||||
@spec http_get_request(String.t()) :: {:ok, [String.t()]} | {:error, any()} |
||||
defp http_get_request(url) do |
||||
case HTTPoison.get(url) do |
||||
{:ok, %Response{body: body, status_code: 200}} -> |
||||
process_verifier_response(body) |
||||
|
||||
{:ok, %Response{body: body, status_code: _}} -> |
||||
{:error, body} |
||||
|
||||
{:error, error} -> |
||||
Logger.error(fn -> |
||||
[ |
||||
"Error while sending request to verification microservice url: #{url}: ", |
||||
inspect(error, limit: :infinity, printable_limit: :infinity) |
||||
] |
||||
end) |
||||
|
||||
{:error, @request_error_msg} |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Retrieves a list of supported versions of Cargo Stylus package from the verification microservice. |
||||
|
||||
## Returns |
||||
- `{:ok, [String.t()]}` - List of versions on success |
||||
- `{:error, any()}` - Error message if the request fails |
||||
""" |
||||
@spec get_versions_list() :: {:ok, [String.t()]} | {:error, any()} |
||||
def get_versions_list do |
||||
http_get_request(versions_list_url()) |
||||
end |
||||
|
||||
@spec process_verifier_response(binary()) :: {:ok, map() | [String.t()]} | {:error, any()} |
||||
defp process_verifier_response(body) when is_binary(body) do |
||||
case Jason.decode(body) do |
||||
{:ok, decoded} -> |
||||
process_verifier_response(decoded) |
||||
|
||||
_ -> |
||||
{:error, body} |
||||
end |
||||
end |
||||
|
||||
# Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice |
||||
@spec process_verifier_response(map()) :: {:ok, map()} |
||||
defp process_verifier_response(%{"verification_success" => source}) do |
||||
{:ok, source} |
||||
end |
||||
|
||||
# Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice |
||||
@spec process_verifier_response(map()) :: {:ok, map()} |
||||
defp process_verifier_response(%{"verificationSuccess" => source}) do |
||||
{:ok, source} |
||||
end |
||||
|
||||
# Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice |
||||
@spec process_verifier_response(map()) :: {:error, String.t()} |
||||
defp process_verifier_response(%{"verification_failure" => %{"message" => error_message}}) do |
||||
{:error, error_message} |
||||
end |
||||
|
||||
# Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice |
||||
@spec process_verifier_response(map()) :: {:error, String.t()} |
||||
defp process_verifier_response(%{"verificationFailure" => %{"message" => error_message}}) do |
||||
{:error, error_message} |
||||
end |
||||
|
||||
# Handles response from `stylus-sdk-rs/cargo-stylus-versions` of stylus verifier microservice |
||||
@spec process_verifier_response(map()) :: {:ok, [String.t()]} |
||||
defp process_verifier_response(%{"versions" => versions}), do: {:ok, Enum.map(versions, &Map.fetch!(&1, "version"))} |
||||
|
||||
@spec process_verifier_response(any()) :: {:error, any()} |
||||
defp process_verifier_response(other) do |
||||
{:error, other} |
||||
end |
||||
|
||||
# Uses url encoded ("%3A") version of ':', as ':' symbol breaks `Bypass` library during tests. |
||||
# https://github.com/PSPDFKit-labs/bypass/issues/122 |
||||
|
||||
defp github_repository_verification_url, |
||||
do: base_api_url() <> "%3Averify-github-repository" |
||||
|
||||
defp versions_list_url, do: base_api_url() <> "/cargo-stylus-versions" |
||||
|
||||
defp base_api_url, do: "#{base_url()}" <> "/api/v1/stylus-sdk-rs" |
||||
|
||||
defp base_url, do: Application.get_env(:explorer, __MODULE__)[:service_url] |
||||
|
||||
def enabled?, |
||||
do: !is_nil(base_url()) && Application.get_env(:explorer, :chain_type) == :arbitrum |
||||
end |
@ -0,0 +1,10 @@ |
||||
defmodule Explorer.Repo.Arbitrum.Migrations.AddStylusFields do |
||||
use Ecto.Migration |
||||
|
||||
def change do |
||||
alter table(:smart_contracts) do |
||||
add(:package_name, :string, null: true) |
||||
add(:github_repository_metadata, :jsonb, null: true) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,9 @@ |
||||
defmodule Explorer.Repo.Migrations.AddLanguageField do |
||||
use Ecto.Migration |
||||
|
||||
def change do |
||||
alter table(:smart_contracts) do |
||||
add(:language, :int2, null: true) |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue