Last active
May 23, 2024 13:26
-
-
Save lucadonnoh/5911061e340086213fc837496b0ff74d to your computer and use it in GitHub Desktop.
Calculate the exit window given chain params such as timelocks and other delays.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import timedelta, datetime | |
CHALLENGE_PERIOD = timedelta(days=7) | |
FORCED_TRANSACTION_DELAY = timedelta(days=1) | |
L2_TIMELOCK_DELAY = timedelta(days=3) | |
L1_TIMELOCK_DELAY = timedelta(days=3) | |
SELF_PROPOSE_DELAY = timedelta(days=7) | |
""" | |
ASSUMPTIONS: | |
- Operators want to push a malicious upgrade | |
- Whitelisted sequencers/proposers are perfectly coordinated | |
- Sequencers can censor withdrawals sent directly on L2 | |
- The user decides to exit after the upgrade is proposed | |
""" | |
random_delta = timedelta(minutes=1) | |
# random_delta = timedelta(seconds=0) # comment if you want to see random deltas | |
USER_ESCAPE_START = timedelta(days=2) + random_delta | |
events = [] | |
start_time = datetime.now() | |
print("") | |
exit_window = timedelta( | |
seconds=max(L1_TIMELOCK_DELAY.total_seconds() + L2_TIMELOCK_DELAY.total_seconds() - FORCED_TRANSACTION_DELAY.total_seconds() - SELF_PROPOSE_DELAY.total_seconds(), max(L2_TIMELOCK_DELAY.total_seconds() - FORCED_TRANSACTION_DELAY.total_seconds(), 0))) | |
print("Exit window: {}".format(exit_window)) | |
def elapsed(time): | |
return time - start_time | |
upgrade_initiation_time = start_time | |
events.append(["Upgrade reaches L2 Timelock", | |
elapsed(upgrade_initiation_time), "red"]) | |
# the withdrawer pushes the withdrawal transaction to L2 via L1 using the DelayedInbox | |
withdrawal_time = upgrade_initiation_time + USER_ESCAPE_START | |
events.append(["Withdrawal pushed to L2 via L1 (DelayedInbox)", | |
elapsed(withdrawal_time), "green"]) | |
# after the FORCED_TRANSACTION_DELAY, the transaction can be moved to the Inbox | |
sequenced_time = withdrawal_time + FORCED_TRANSACTION_DELAY | |
events.append(["Withdrawal sequenced (Inbox)", | |
elapsed(sequenced_time), "green"]) | |
# proposers now stop posting state roots | |
# after the L2_TIMELOCK_DELAY, the upgrade can be sent to L1, but a state root must be posted | |
# this state root inevitably contains the withdrawal transaction | |
upgrade_challenge_period_start = upgrade_initiation_time + L2_TIMELOCK_DELAY | |
events.append(["Upgrade sent to L1", elapsed( | |
upgrade_challenge_period_start), "red"]) | |
containsWithdrawal = "" | |
if (sequenced_time <= upgrade_challenge_period_start): | |
events.append( | |
["The state root contains both the withdrawal and the upgrade", elapsed(upgrade_challenge_period_start), "green"]) | |
containsWithdrawal = " (and withdrawal)" | |
else: | |
events.append( | |
["The state root contains only the upgrade, operators stop producing roots", elapsed(upgrade_challenge_period_start), "red"]) | |
self_proposed_root_proposal_time = sequenced_time + SELF_PROPOSE_DELAY | |
events.append(["Withdrawal posted in a self-proposed state root", | |
elapsed(self_proposed_root_proposal_time), "green"]) | |
self_proposed_root_finalization_time = self_proposed_root_proposal_time + CHALLENGE_PERIOD | |
events.append(["Self-proposed state root finalized", | |
elapsed(self_proposed_root_finalization_time), "green"]) | |
# after the CHALLENGE_PERIOD, the upgrade is accepted on L2, but also the withdrawal transaction | |
upgrade_challenge_period_end = upgrade_challenge_period_start + CHALLENGE_PERIOD | |
events.append(["Upgrade" + containsWithdrawal + " reaches L1 Timelock", | |
elapsed(upgrade_challenge_period_end), ("green" if containsWithdrawal else "red")]) | |
# the withdrawal has been executed | |
upgrade_execution_time = upgrade_challenge_period_end + L1_TIMELOCK_DELAY | |
events.append(["Upgrade executed \n ####### NOTHING MATTERS FROM HERE #######", | |
elapsed(upgrade_execution_time), "red"]) | |
# order events by time | |
events.sort(key=lambda x: x[1]) | |
def print_colored(text, color): | |
if color == "red": | |
print("\033[91m {}\033[00m".format(text)) | |
elif color == "green": | |
print("\033[92m {}\033[00m".format(text)) | |
elif color == "blue": | |
print("\033[94m {}\033[00m".format(text)) | |
else: | |
print(text) | |
label_width = max(len("CHALLENGE_PERIOD"), | |
len("FORCED_TRANSACTION_DELAY"), | |
len("L2_TIMELOCK_DELAY"), | |
len("L1_TIMELOCK_DELAY"), | |
len("SELF_PROPOSE_DELAY"), | |
len("USER_ESCAPE_DELAY")) + 1 | |
print("") | |
print(f"{'CHALLENGE_PERIOD:':<{label_width}} {CHALLENGE_PERIOD}") | |
print(f"{'FORCED_TRANSACTION_DELAY:':<{label_width}} {FORCED_TRANSACTION_DELAY}") | |
print(f"{'L2_TIMELOCK_DELAY:':<{label_width}} {L2_TIMELOCK_DELAY}") | |
print(f"{'L1_TIMELOCK_DELAY:':<{label_width}} {L1_TIMELOCK_DELAY}") | |
print(f"{'SELF_PROPOSE_DELAY:':<{label_width}} {SELF_PROPOSE_DELAY}") | |
print(f"{'USER_ESCAPE_START:':<{label_width}} {USER_ESCAPE_START}") | |
print("") | |
for event in events: | |
print_colored("{} - {}".format(event[1], event[0]), event[2]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment