Skip to content

Commit

Permalink
Creating Review Diff Check Based on Diff Check Implementation (#17)
Browse files Browse the repository at this point in the history
* first try

* Update git_manager.py

* Auto change version.

* Yoav CR

* Auto change version.

* Update README.md

* Auto change version.

* Update README.md

* Auto change version.

---------

Co-authored-by: liav-certora <liav-certora@users.noreply.github.com>
  • Loading branch information
liav-certora and liav-certora authored Nov 10, 2024
1 parent 7f618a1 commit 23cef17
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 33 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
**.ipynb
**__pycache__
**/.vscode
**/local*
**/local*
build/
*.egg-info
CustomerClones/
2 changes: 0 additions & 2 deletions Quorum/apis/block_explorers/chains_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ class ChainAPI:
Attributes:
chain_mapping (dict): Maps Chain enum to APIinfo containing base URL and API key function.
base_url (str): The full base URL for making requests to the selected chain.
api_key (str): The API key required to access the blockchain explorer API.
"""

# Maps Chain enums to their corresponding API base URL and API key retrieval function.
Expand Down
49 changes: 34 additions & 15 deletions Quorum/apis/git_api/git_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,40 @@ def __init__(self, customer: str) -> None:
"""
self.customer = customer

self.customer_path = config.MAIN_PATH / self.customer / "modules"
self.customer_path.mkdir(parents=True, exist_ok=True)
self.modules_path = config.MAIN_PATH / self.customer / "modules"
self.modules_path.mkdir(parents=True, exist_ok=True)

self.repos = self._load_repos()
self.review_module_path = config.MAIN_PATH / self.customer / "review_module"
self.review_module_path.mkdir(parents=True, exist_ok=True)

def _load_repos(self) -> dict:
self.repos, self.review_repo = self._load_repos_from_file()

def _load_repos_from_file(self) -> tuple[dict[str, str], dict[str, str]]:
"""
Load repository URLs from the JSON file for the given customer.
Returns:
dict: A dictionary mapping repository names to their URLs.
tuple[dict[str, str], dict[str, str]]: 2 dictionaries mapping repository names to their URLs.
The first dictionary contains the repos to diff against. The second dictionary is the verification repo.
"""
with open(config.REPOS_PATH) as f:
repos_data = json.load(f)

# Normalize the customer name to handle case differences
normalized_customer = self.customer.lower()
repos = next((repos for key, repos in repos_data.items() if key.lower() == normalized_customer), [])
customer_repos = next((repos for key, repos in repos_data.items() if key.lower() == normalized_customer), None)
if customer_repos is None:
return {}, {}

return {Path(r).stem: r for r in repos}
repos = {Path(r).stem: r for r in customer_repos["dev_repos"]}

def clone_or_update(self) -> None:
"""
Clone the repositories for the customer.
verify_repo = ({Path(customer_repos["review_repo"]).stem: customer_repos["review_repo"]}
if "review_repo" in customer_repos else {})
return repos, verify_repo

If the repository already exists locally, it will update the repository and its submodules.
Otherwise, it will clone the repository and initialize submodules.
"""
for repo_name, repo_url in self.repos.items():
repo_path: Path = self.customer_path / repo_name
@staticmethod
def __clone_or_update_for_repo(repo_name: str, repo_url: str, to_path: Path):
repo_path = to_path / repo_name
if repo_path.exists():
pp.pretty_print(f"Repository {repo_name} already exists at {repo_path}. Updating repo and submodules.", pp.Colors.INFO)
repo = Repo(repo_path)
Expand All @@ -63,3 +67,18 @@ def clone_or_update(self) -> None:
pp.pretty_print(f"Cloning {repo_name} from URL: {repo_url} to {repo_path}...", pp.Colors.INFO)
Repo.clone_from(repo_url, repo_path, multi_options=["--recurse-submodules"])


def clone_or_update(self) -> None:
"""
Clone the repositories for the customer.
If the repository already exists locally, it will update the repository and its submodules.
Otherwise, it will clone the repository and initialize submodules.
"""

for repo_name, repo_url in self.repos.items():
GitManager.__clone_or_update_for_repo(repo_name, repo_url, self.modules_path)

if self.review_repo:
repo_name, repo_url = next(iter(self.review_repo.items()))
GitManager.__clone_or_update_for_repo(repo_name, repo_url, self.review_module_path)
3 changes: 3 additions & 0 deletions Quorum/check_proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ def proposals_check(customer: str, chain_name: str, proposal_addresses: list[str
# Diff check
missing_files = Checks.DiffCheck(customer, chain, proposal_address, source_codes).find_diffs()

# Review diff check
Checks.ReviewDiffCheck(customer, chain, proposal_address, missing_files).find_diffs()

# Global variables check
Checks.GlobalVariableCheck(customer, chain, proposal_address, missing_files).check_global_variables()

Expand Down
3 changes: 2 additions & 1 deletion Quorum/checks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .diff import DiffCheck
from .review_diff import ReviewDiffCheck
from .global_variables import GlobalVariableCheck
from .feed_price import FeedPriceCheck
from .new_listing import NewListingCheck

all = ["DiffCheck", "GlobalVariableCheck", "FeedPriceCheck", "NewListingCheck"]
all = ["DiffCheck", "ReviewDiffCheck", "GlobalVariableCheck", "FeedPriceCheck", "NewListingCheck"]
8 changes: 5 additions & 3 deletions Quorum/checks/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from Quorum.apis.block_explorers.source_code import SourceCode
from Quorum.checks.check import Check
from Quorum.utils.chain_enum import Chain
import Quorum.utils.pretty_printer as pp


Expand All @@ -30,6 +31,9 @@ class DiffCheck(Check):
This class compares source files from a local repository with those from a remote proposal,
identifying differences and generating patch files.
"""
def __init__(self, customer: str, chain: Chain, proposal_address: str, source_codes: list[SourceCode]):
super().__init__(customer, chain, proposal_address, source_codes)
self.target_repo = self.customer_folder / "modules"

def __find_most_common_path(self, source_path: Path, repo: Path) -> Optional[Path]:
"""
Expand Down Expand Up @@ -82,10 +86,8 @@ def find_diffs(self) -> list[SourceCode]:
missing_files = []
files_with_diffs = []

target_repo = self.customer_folder / "modules"

for source_code in self.source_codes:
local_file = self.__find_most_common_path(Path(source_code.file_name), target_repo)
local_file = self.__find_most_common_path(Path(source_code.file_name), self.target_repo)
if not local_file:
missing_files.append(source_code)
continue
Expand Down
15 changes: 15 additions & 0 deletions Quorum/checks/review_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from Quorum.checks.diff import DiffCheck
from Quorum.apis.block_explorers.source_code import SourceCode
from Quorum.utils.chain_enum import Chain
import Quorum.utils.pretty_printer as pp


class ReviewDiffCheck(DiffCheck):
def __init__(self, customer: str, chain: Chain, proposal_address: str, source_codes: list[SourceCode]):
super().__init__(customer, chain, proposal_address, source_codes)
self.target_repo = self.customer_folder / "review_module"

def find_diffs(self) -> list[SourceCode]:
pp.pretty_print(f'Verifying missing files against {self.customer} review repo '
f'(cloned under {self.target_repo})', pp.Colors.INFO)
return super().find_diffs()
15 changes: 10 additions & 5 deletions Quorum/repos.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"Aave": [
"https://github.com/bgd-labs/aave-helpers",
"https://github.com/bgd-labs/aave-address-book",
"https://github.com/aave-dao/aave-v3-origin"
]
"Aave":
{
"dev_repos":
[
"https://github.com/bgd-labs/aave-helpers",
"https://github.com/bgd-labs/aave-address-book",
"https://github.com/aave-dao/aave-v3-origin"
],
"review_repo": "https://github.com/bgd-labs/aave-proposals-v3"
}
}
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Quorum is an open-source Python utility designed to verify the integrity of smar
## Features
- **Fetch Smart Contract Source Codes:** Retrieve source code directly from various blockchains using contract addresses.
- **Compare Local and Remote Codes:** Generate unified diffs to highlight differences between local and remote source codes.
- **Verify Code Against Known Reviewed Repositories:** Generate diffs against specifically defined trusted auditor's repositories.
- **Global Variable Check:** Ensure all global variables in unmatched contracts are either constant or immutable.
- **Feed Price Check:** Verify the feed price of a contract is mentioned on ChainLink.
- **New Listing Check:** Check if proposal contain a new Listing.
Expand Down Expand Up @@ -157,11 +158,16 @@ Example `repos.json`:

```json
{
"Aave": [
"https://github.com/bgd-labs/aave-helpers",
"https://github.com/bgd-labs/aave-address-book",
"https://github.com/aave-dao/aave-v3-origin"
]
"Aave":
{
"dev_repos":
[
"https://github.com/bgd-labs/aave-helpers",
"https://github.com/bgd-labs/aave-address-book",
"https://github.com/aave-dao/aave-v3-origin"
],
"review_repo": "https://github.com/bgd-labs/aave-proposals-v3"
}
}
```

Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20241105.182030.187611
20241110.154442.714477

0 comments on commit 23cef17

Please sign in to comment.