Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Blockscout compiler version #335

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions boa/verifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Optional

import requests
from cached_property import cached_property

from boa.util.abi import Address
from boa.util.open_ctx import Open
Expand Down Expand Up @@ -56,28 +57,44 @@ def verify(

url = f"{self.uri}/api/v2/smart-contracts/{address}/"
url += f"verification/via/vyper-standard-input?apikey={api_key}"
data = {
"compiler_version": solc_json["compiler_version"],
"license_type": license_type,
}
files = {
"files[0]": (
contract_name,
json.dumps(solc_json).encode("utf-8"),
"application/json",
)
}

response = requests.post(url, data=data, files=files)

version = self._get_compiler_version(solc_json["compiler_version"])
solc_json = {**solc_json, "compiler_version": version}
data = {"compiler_version": version, "license_type": license_type}
file = (contract_name, json.dumps(solc_json), "application/json")

response = requests.post(url, data=data, files={"files[0]": file})
response.raise_for_status()
print(response.json().get("message")) # usually verification started
# print(f"Sent {data} to {url} with {file}")

if not wait:
return VerificationResult(address, self)

self.wait_for_verification(address)
return None

def _get_compiler_version(self, compiler_version) -> str:
"""
Runs a partial match based on the compiler version.
Blockscout only accepts exact matches, but vyper can have a different
commit hash length depending on the version and installation method.

Raises a ValueError if no match is found or if multiple matches are found.
"""
supported = self.supported_versions
match [v for v in supported if compiler_version in v]:
case [version]:
return version
case []:
err = "Could not find a matching compiler version on Blockscout. "
err += f"Given: {compiler_version}, supported: {supported}."
raise Exception(err)
case multiple:
err = "Ambiguous compiler version for Blockscout verification. "
err += f"Given: {compiler_version}, found: {multiple}."
raise Exception(err)

def wait_for_verification(self, address: Address) -> None:
"""
Waits for the contract to be verified on Blockscout.
Expand All @@ -94,7 +111,7 @@ def wait_for_verification(self, address: Address) -> None:
time.sleep(wait_time.total_seconds())
wait_time *= self.backoff_factor

raise TimeoutError("Timeout waiting for verification to complete")
raise TimeoutError(f"Timeout waiting for verification of {address}")

def is_verified(self, address: Address) -> bool:
api_key = self.api_key or ""
Expand All @@ -106,6 +123,14 @@ def is_verified(self, address: Address) -> bool:
response.raise_for_status()
return response.json().get("is_verified", False)

@cached_property
def supported_versions(self) -> list[str]:
response = requests.get(
"https://http.sc-verifier.services.blockscout.com/api/v2/verifier/vyper/versions"
)
response.raise_for_status()
return response.json()["compilerVersions"]


_verifier = Blockscout()

Expand Down
22 changes: 17 additions & 5 deletions tests/integration/network/sepolia/test_sepolia_env.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
from random import randint, sample
from string import ascii_lowercase

import pytest

Expand Down Expand Up @@ -38,13 +40,23 @@ def simple_contract():
return boa.loads(code, STARTING_SUPPLY)


def test_verify(simple_contract):
def test_verify():
# generate a random contract so the verification will actually be done again
name = "".join(sample(ascii_lowercase, 10))
contract = boa.loads(
f"""
@external
def {name}() -> uint256:
return {randint(0, 2**256 - 1)}
""",
name=name,
)

api_key = os.getenv("BLOCKSCOUT_API_KEY")
blockscout = Blockscout("https://eth-sepolia.blockscout.com", api_key)
with boa.set_verifier(blockscout):
result = boa.verify(simple_contract)
result.wait_for_verification()
assert result.is_verified()
result = boa.verify(contract, blockscout)
result.wait_for_verification()
assert result.is_verified()


def test_env_type():
Expand Down
Loading