diff --git a/scripts/before_pectra_upgrade.py b/archive/scripts/before_pectra_upgrade.py similarity index 100% rename from scripts/before_pectra_upgrade.py rename to archive/scripts/before_pectra_upgrade.py diff --git a/tests/test_before_pectra_upgrade_holesky.py b/archive/tests/test_before_pectra_upgrade_holesky.py similarity index 100% rename from tests/test_before_pectra_upgrade_holesky.py rename to archive/tests/test_before_pectra_upgrade_holesky.py diff --git a/scripts/after_pectra_upgrade.py b/scripts/after_pectra_upgrade.py new file mode 100644 index 00000000..dbe4f79e --- /dev/null +++ b/scripts/after_pectra_upgrade.py @@ -0,0 +1,203 @@ +""" +Release part of the update following the Pectra upgrade + +1. Grant role `EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` to Aragon Agent on `OracleReportSanityChecker` contract +2. Set `exitedValidatorsPerDayLimit` sanity checker parameter to 3600 +3. Revoke role `EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` from Aragon Agent +4. Grant role `APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` to Aragon Agent on `OracleReportSanityChecker` contract +5. Set `appearedValidatorsPerDayLimit` sanity checker parameter to 1800 +6. Revoke role `APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` from Aragon Agent +7. Grant role `INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE` to Aragon Agent on `OracleReportSanityChecker` contract +8. Set `initialSlashingAmountPWei` sanity checker parameter to 8 +9. Revoke role `INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE` on `OracleReportSanityChecker` from Aragon Agent +""" + +import time + +try: + from brownie import interface, accounts +except ImportError: + print("You're probably running inside Brownie console. Please call:") + print("set_console_globals(interface=interface)") + + +from typing import Dict, Tuple, Optional +from utils.config import ( + get_deployer_account, + get_is_live, + get_priority_fee, + contracts, +) +from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description +from utils.permissions import encode_oz_grant_role, encode_oz_revoke_role +from utils.voting import bake_vote_items, confirm_vote_script, create_vote + +from brownie.network.transaction import TransactionReceipt +from utils.agent import agent_forward +from utils.mainnet_fork import pass_and_exec_dao_vote + +# Oracle sanity checker params + +NEW_INITIAL_SLASHING_AMOUNT_PWEI = 8 +UNCHANGED_INACTIVITY_PENATIES_AMOUNT_PWEI = 101 +NEW_EXITED_VALIDATORS_PER_DAY_LIMIT = 3600 +NEW_APPEARED_VALIDATORS_PER_DAY_LIMIT = 1800 + +description = """ +""" + + +def start_vote(tx_params: Dict[str, str], silent: bool) -> Tuple[int, Optional[TransactionReceipt]]: + """Prepare and run voting.""" + + vote_desc_items, call_script_items = zip( + ( + "1) Grant role `EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` role to Aragon Agent on `OracleReportSanityChecker` contract", + agent_forward( + [ + encode_oz_grant_role( + contract=contracts.oracle_report_sanity_checker, + role_name="EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE", + grant_to=contracts.agent, + ) + ] + ), + ), + ( + "2) Set `exitedValidatorsPerDayLimit` sanity checker parameter to 3600", + agent_forward( + [ + ( + contracts.oracle_report_sanity_checker.address, + contracts.oracle_report_sanity_checker.setExitedValidatorsPerDayLimit.encode_input( + NEW_EXITED_VALIDATORS_PER_DAY_LIMIT + ), + ), + ] + ), + ), + ( + "3. Revoke role `EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` from Aragon Agent", + agent_forward( + [ + encode_oz_revoke_role( + contract=contracts.oracle_report_sanity_checker, + role_name="EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE", + revoke_from=contracts.agent, + ) + ] + ), + ), + ( + "4) Grant role `APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` role to Aragon Agent on `OracleReportSanityChecker` contract", + agent_forward( + [ + encode_oz_grant_role( + contract=contracts.oracle_report_sanity_checker, + role_name="APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE", + grant_to=contracts.agent, + ) + ] + ), + ), + ( + "5) Set `appearedValidatorsPerDayLimit` sanity checker parameter to 1800", + agent_forward( + [ + ( + contracts.oracle_report_sanity_checker.address, + contracts.oracle_report_sanity_checker.setAppearedValidatorsPerDayLimit.encode_input( + NEW_APPEARED_VALIDATORS_PER_DAY_LIMIT + ), + ), + ] + ), + ), + ( + "6) Revoke role `APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` from Aragon Agent", + agent_forward( + [ + encode_oz_revoke_role( + contract=contracts.oracle_report_sanity_checker, + role_name="APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE", + revoke_from=contracts.agent, + ) + ] + ), + ), + ( + "7) Grant role `INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE` role to Aragon Agent on `OracleReportSanityChecker` contract", + agent_forward( + [ + encode_oz_grant_role( + contract=contracts.oracle_report_sanity_checker, + role_name="INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE", + grant_to=contracts.agent, + ) + ] + ), + ), + ( + "8) Set `initialSlashingAmountPWei` sanity checker parameter to 8", + agent_forward( + [ + ( + contracts.oracle_report_sanity_checker.address, + contracts.oracle_report_sanity_checker.setInitialSlashingAndPenaltiesAmount.encode_input( + NEW_INITIAL_SLASHING_AMOUNT_PWEI, + UNCHANGED_INACTIVITY_PENATIES_AMOUNT_PWEI, + ), + ), + ] + ), + ), + ( + "9) Revoke role `INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE` on `OracleReportSanityChecker` from Aragon Agent", + agent_forward( + [ + encode_oz_revoke_role( + contract=contracts.oracle_report_sanity_checker, + role_name="INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE", + revoke_from=contracts.agent, + ) + ] + ), + ), + ) + + vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) + + if silent: + desc_ipfs = calculate_vote_ipfs_description(description) + else: + desc_ipfs = upload_vote_ipfs_description(description) + + return confirm_vote_script(vote_items, silent, desc_ipfs) and list( + create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs) + ) + + +def main(): + tx_params = {"from": get_deployer_account()} + + if get_is_live(): + tx_params["priority_fee"] = get_priority_fee() + + vote_id, _ = start_vote(tx_params=tx_params, silent=False) + + vote_id >= 0 and print(f"Vote created: {vote_id}.") + + time.sleep(5) # hack for waiting thread #2. + + +def start_and_execute_vote_on_fork(): + if get_is_live(): + raise Exception("This script is for local testing only.") + + tx_params = {"from": get_deployer_account()} + vote_id, _ = start_vote(tx_params=tx_params, silent=True) + + time.sleep(5) # hack for waiting thread #2. + + print(f"Vote created: {vote_id}.") + pass_and_exec_dao_vote(int(vote_id)) diff --git a/tests/test_after_pectra_upgrade_holesky.py b/tests/test_after_pectra_upgrade_holesky.py new file mode 100644 index 00000000..ac387d77 --- /dev/null +++ b/tests/test_after_pectra_upgrade_holesky.py @@ -0,0 +1,198 @@ +from scripts.after_pectra_upgrade import start_vote +from utils.config import LDO_HOLDER_ADDRESS_FOR_TESTS +from brownie import interface +from utils.test.tx_tracing_helpers import * +from utils.test.event_validators.permission import validate_grant_role_event, validate_revoke_role_event +from utils.test.event_validators.common import validate_events_chain + +# Contracts +AGENT = "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d" +VOTING = "0xdA7d2573Df555002503F29aA4003e398d28cc00f" +ORACLE_SANITY_CHECKER = "0x80D1B1fF6E84134404abA18A628347960c38ccA7" + +# Roles +EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE = "0x60b9982471bc0620c7b74959f48a86c55c92c11876fddc5b0b54d1ec47153e5d" +APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE = "0x14ca7b84baa11a976283347b0159b8ddf2dcf5fd5cf613cc567a3423cf510119" +INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE = "0xebfa317a5d279811b024586b17a50f48924bce86f6293b233927322d7209b507" + +# Old values + +OLD_INITIAL_SLASHING_AMOUNT_PWEI = 1000 +OLD_EXITED_VALIDATORS_PER_DAY_LIMIT = 9016 +OLD_APPEARED_VALIDATORS_PER_DAY_LIMIT = 43200 + +# New values +NEW_INITIAL_SLASHING_AMOUNT_PWEI = 8 +NEW_EXITED_VALIDATORS_PER_DAY_LIMIT = 3600 +NEW_APPEARED_VALIDATORS_PER_DAY_LIMIT = 1800 + + +def get_voting(): + return interface.Voting(VOTING) + + +def get_sanity_checker(): + return interface.OracleReportSanityChecker(ORACLE_SANITY_CHECKER) + + +def test_vote(helpers, accounts, vote_ids_from_env, bypass_events_decoding): + sanityChecker = get_sanity_checker() + sanity_checker_limits = sanityChecker.getOracleReportLimits() + + # Before voting tests + # 1) Aragon Agent doesn't have `EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` contract + agent_has_role = sanityChecker.hasRole(EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, AGENT) + assert not agent_has_role + # 2) Check `exitedValidatorsPerDayLimit` sanity checker old value + assert sanity_checker_limits["exitedValidatorsPerDayLimit"] == OLD_EXITED_VALIDATORS_PER_DAY_LIMIT + # 3) Aragon Agent doesn't have `APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` contract + agent_has_role = sanityChecker.hasRole(APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, AGENT) + assert not agent_has_role + # 4) Check `appearedValidatorsPerDayLimit` sanity checker old value + assert sanity_checker_limits["appearedValidatorsPerDayLimit"] == OLD_APPEARED_VALIDATORS_PER_DAY_LIMIT + # 5) Aragon Agent doesn't have `INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE` on `OracleReportSanityChecker` contract + agent_has_role = sanityChecker.hasRole(INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE, AGENT) + assert not agent_has_role + # 6) Check `initialSlashingAmountPWei` sanity checker old value + assert sanity_checker_limits["initialSlashingAmountPWei"] == OLD_INITIAL_SLASHING_AMOUNT_PWEI + + # START VOTE + if len(vote_ids_from_env) > 0: + (vote_id,) = vote_ids_from_env + else: + tx_params = {"from": LDO_HOLDER_ADDRESS_FOR_TESTS} + vote_id, _ = start_vote(tx_params, silent=True) + + voting = get_voting() + + vote_tx = helpers.execute_vote(accounts, vote_id, voting) + + print(f"voteId = {vote_id}, gasUsed = {vote_tx.gas_used}") + + sanity_checker_limits = sanityChecker.getOracleReportLimits() + + # After voting tests + # 1) Aragon Agent doesn't have `EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` contract + agent_has_role = sanityChecker.hasRole(EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, AGENT) + assert not agent_has_role + # 2) Check `exitedValidatorsPerDayLimit` sanity checker value after voting equal to 3600 + assert sanity_checker_limits["exitedValidatorsPerDayLimit"] == NEW_EXITED_VALIDATORS_PER_DAY_LIMIT + # 3) Aragon Agent doesn't have `APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE` on `OracleReportSanityChecker` contract + agent_has_role = sanityChecker.hasRole(APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, AGENT) + assert not agent_has_role + # 4) Check `appearedValidatorsPerDayLimit` sanity checker value after voting equal to 1800 + assert sanity_checker_limits["appearedValidatorsPerDayLimit"] == NEW_APPEARED_VALIDATORS_PER_DAY_LIMIT + # 5) Aragon Agent doesn't have `INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE` on `OracleReportSanityChecker` contract + agent_has_role = sanityChecker.hasRole(INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE, AGENT) + assert not agent_has_role + # 6) Check `initialSlashingAmountPWei` sanity checker value to 8 + assert sanity_checker_limits["initialSlashingAmountPWei"] == NEW_INITIAL_SLASHING_AMOUNT_PWEI + + # Events check + display_voting_events(vote_tx) + events = group_voting_events(vote_tx) + + assert len(events) == 9 + + # Validate exitedValidatorsPerDayLimit sanity checker value set to `NEW_EXITED_VALIDATORS_PER_DAY_LIMIT` + validate_sc_exited_validators_limit_update(events[:3], NEW_EXITED_VALIDATORS_PER_DAY_LIMIT) + # Validate appearedValidatorsPerDayLimit sanity checker value set to `NEW_APPEARED_VALIDATORS_PER_DAY_LIMIT` + validate_appeared_validators_limit_update(events[3:6], NEW_APPEARED_VALIDATORS_PER_DAY_LIMIT) + # Validate initialSlashingAmountPWei sanity checker value set to `NEW_INITIAL_SLASHING_AMOUNT_PWEI` + validate_initial_slashing_and_penalties_update(events[6:9], NEW_INITIAL_SLASHING_AMOUNT_PWEI) + + +# Events check + + +def validate_sc_exited_validators_limit_update(events: list[EventDict], exitedValidatorsPerDayLimit): + validate_grant_role_event( + events[0], + EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, + AGENT, + AGENT, + ) + validate_exited_validators_per_day_limit_event(events[1], exitedValidatorsPerDayLimit) + validate_revoke_role_event( + events[2], + EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, + AGENT, + AGENT, + ) + + +def validate_appeared_validators_limit_update(events: list[EventDict], appearedValidatorsPerDayLimit): + validate_grant_role_event( + events[0], + APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, + AGENT, + AGENT, + ) + validate_appeared_validators_limit_event(events[1], appearedValidatorsPerDayLimit) + validate_revoke_role_event( + events[2], + APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE, + AGENT, + AGENT, + ) + + +def validate_initial_slashing_and_penalties_update(events: list[EventDict], initialSlashingAmountPWei): + validate_grant_role_event( + events[0], + INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE, + AGENT, + AGENT, + ) + validate_initial_slashing_and_penalties_event(events[1], initialSlashingAmountPWei) + validate_revoke_role_event( + events[2], + INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE, + AGENT, + AGENT, + ) + + +def validate_exited_validators_per_day_limit_event(event: EventDict, value: int): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "ExitedValidatorsPerDayLimitSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("ExitedValidatorsPerDayLimitSet") == 1 + + assert event["ExitedValidatorsPerDayLimitSet"]["exitedValidatorsPerDayLimit"] == value + + +def validate_appeared_validators_limit_event(event: EventDict, value: int): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "AppearedValidatorsPerDayLimitSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("AppearedValidatorsPerDayLimitSet") == 1 + + assert event["AppearedValidatorsPerDayLimitSet"]["appearedValidatorsPerDayLimit"] == value + + +def validate_initial_slashing_and_penalties_event(event: EventDict, value: int): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "InitialSlashingAmountSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("InitialSlashingAmountSet") == 1 + + assert event["InitialSlashingAmountSet"]["initialSlashingAmountPWei"] == value