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

Auto Report Generation #19

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
54 changes: 54 additions & 0 deletions Quorum/auto_report/AaveReportTemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Proposal <proposal_id>. <proposal_title>

### Voting Link
[Link to voting page](<voting_link>)

### Governance Forum Discussions
[Link to forum discussions](<gov_forum_link>)

### Payloads
<loop:chain,payload_link> * <chain> - [proposal payloads](<payload_link>) </loop>


## Certora Analysis

### Proposal Types
{**TODO: Choose types from the following list.**}
* :scroll: :small_red_triangle: Contract upgrades
* :moneybag: :receipt: Asset transfers
* :handshake: Permission granting and revoking
* :wrench: :bar_chart: Configuration updates
* :gem: :new: Listing new assets

### Context
{**TODO: Write context.**}

### Proposal Creation
Transaction: [<transaction_hash>](<transaction_link>)
```
<transaction_data>
```

**`createProposal()` Parameters**
```
<createProposal_parameters_data>
```

### Aave Seatbelt Report

**Proposal Report**
[Link to Seatbelt proposal report](<seatbelt_link>)

**Payload Reports**
<loop:chain,payload_seatbelt_link> * <chain> - [payload Seatbelt report](<payload_seatbelt_link>) </loop>

### Technical Analysis
{**TODO: Write technical analysis.**}

The proposal is consistent with the description on both Snapshot and the governance forum.

### Certora validations
* :white_check_mark: The code on the proposal payload corresponds to the proposal specification.
* :white_check_mark: The proposal includes a proper tests suite, checking all necessary post-conditions.
* :white_check_mark: BGD reviewed the payload before the proposal was submitted.
* :white_check_mark: Certora reviewed the procedure followed to submit the proposal.
37 changes: 37 additions & 0 deletions Quorum/auto_report/create_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from Quorum.auto_report.report_generator import ReportGenerator
from Quorum.auto_report.tags import AaveTags

import argparse
from pathlib import Path


DEFAULT_TEMPLATE_PATH = Path(__file__).parent / 'AaveReportTemplate.md'


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description='This tool generates automatic proposal reports.')
parser.add_argument('--proposal_id', required=True, type=int, help='The proposal id to generate report to.')
parser.add_argument('--template', default=DEFAULT_TEMPLATE_PATH, help='The report template to use.')

args = parser.parse_args()

if not Path(args.template).exists():
raise FileNotFoundError(f'could not find template at {args.template}.')

return parser.parse_args()


def main():
args = parse_args()

with open(args.template) as f:
template = f.read()

report = ReportGenerator(template, AaveTags(args.proposal_id).tag_mappings).report

with open(f'v3-{args.proposal_id}-<title>.md', 'w') as f:
f.write(report)
Comment on lines +32 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldnt be the proposal_title field instead of -<title>.md?



if __name__ == '__main__':
main()
Comment on lines +24 to +37
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must have Docs both here and in the general readme file.

Comment on lines +24 to +37
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the project logger here.

63 changes: 63 additions & 0 deletions Quorum/auto_report/report_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import re


class ReportGenerator:
LOOP_COMMAND = 'loop'

def __init__(self, template: str, tag_mappings: dict[str, str | list[str]]) -> None:
self.report = self.__generate_report(template, tag_mappings)

@staticmethod
def __generate_report(template: str, tag_mappings: dict[str, str | list[str]]) -> str:
res = ReportGenerator.__replace_loops(template, tag_mappings)
res = ReportGenerator.__fill_tags(res, tag_mappings)
return res

@staticmethod
def __fill_tags(template: str, tag_mappings: dict[str, str | list[str]]) -> str:
res = template
for tag, value in tag_mappings.items():
if isinstance(value, str):
res = res.replace(f'<{tag}>', value)
return res

@staticmethod
def __replace_loops(template: str, tag_mappings: dict[str, str | list[str]]) -> str:
pattern = rf'<{ReportGenerator.LOOP_COMMAND}:([^>]+)>'
matches = re.findall(pattern, template)
res = template
for m in matches:
loop_tag_mapping = {tag: tag_mappings[tag] for tag in m.split(',')}
res = ReportGenerator.__unroll_loop(res, m, loop_tag_mapping)
return res

@staticmethod
def __unroll_loop(template: str, looping_tags: str, loop_tag_mappings: dict[str, list[str]]) -> str:
start_tag = f'<{ReportGenerator.LOOP_COMMAND}:{looping_tags}>'
end_tag = f'</{ReportGenerator.LOOP_COMMAND}>'

start_index = template.index(start_tag) + len(start_tag)
end_index = template.index(end_tag)

loop_template = template[start_index:end_index].strip()

loop_result = ReportGenerator.__build_loop_content(loop_template, loop_tag_mappings)

return template[:template.index(start_tag)] + loop_result + template[end_index + len(end_tag):]

@staticmethod
def extract_loop_values_lengths(values: list[list[str]]) -> int:
expected_length = len(values[0])
for v in values:
if len(v) != expected_length:
raise ValueError('lengths not equal')
return expected_length

@staticmethod
def __build_loop_content(loop_template: str, loop_tag_mappings: dict[str, list[str]]) -> str:
loop_count = ReportGenerator.extract_loop_values_lengths(list(loop_tag_mappings.values()))
res = ''
for i in range(loop_count):
res += ReportGenerator.__fill_tags(loop_template,
{tag: values[i] for tag, values in loop_tag_mappings.items()}) + '\n'
return res.strip()
Comment on lines +4 to +63
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Docs to all methods in the class

55 changes: 55 additions & 0 deletions Quorum/auto_report/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
class AaveTags:
def __init__(self, proposal_id: int) -> None:
self.proposal_id = proposal_id
self.tag_mappings = {
'proposal_id': str(proposal_id),
'proposal_title': self.get_proposal_title(),
'chain': self.get_chains(),
'payload_link': self.get_payload_links(),
'transaction_hash': self.get_transaction_hash(),
'transaction_data': self.get_transaction_data(),
'createProposal_parameters_data': self.get_create_func_parameters_data(),
'seatbelt_link': self.get_seatbelt_link(),
'payload_seatbelt_link': self.get_seatbelt_payload_links()
}

def get_proposal_title(self) -> str:
return 'titular'

def get_chains(self) -> list[str]:
return ['btc', 'eth', 'sol']


def get_payload_links(self) -> list[str]:
return ['btc_link', 'eth_link', 'sol_link']


def get_transaction_hash(self) -> str:
return 'hush baby'


def get_transaction_link(self) -> str:
return 'www.transaction.link.com'


def get_transaction_data(self) -> str:
return ('Some transaction data \n'
'Like this \n'
'And this')


def get_create_func_parameters_data(self) -> str:
return ('A\n'
'long\n'
'list\n'
'of\n'
'parameters\n'
'data')


def get_seatbelt_link(self) -> str:
return 'www.seatbelt.com'


def get_seatbelt_payload_links(self) -> list[str]:
return ['www.seatbelt.btc', 'www.seatbelt.eth', 'www.seatbelt.sol']
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20241110.154442.714477
20241124.092904.107425