From d63c2d228b09b1358403a68b76b170c87aa632e4 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Thu, 14 Aug 2025 15:14:23 -0400 Subject: [PATCH 01/16] feat: TimelockGuard introduces the TimelockGuard proposal. --- safe-modules/timelock-guard.md | 116 +++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 safe-modules/timelock-guard.md diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md new file mode 100644 index 00000000..6e08eeaa --- /dev/null +++ b/safe-modules/timelock-guard.md @@ -0,0 +1,116 @@ +# TimelockGuard + +## Context and Problem Statement + + + +[Safe Accounts](https://safe.global) are configured as a set of owner addresses where some +threshold of addresses can sign and ultimately execute a transaction through the account. One +common concern with Safe accounts is that, by default, transactions execute immediately once signed +without any type of delay. + +A mandatory transaction delay is a significant improvement in the security model for Safe accounts +because it provides ample time for review and entirely mitigates the impact of compromised signing +machines. It would be immensely valuable to OP Labs and Optimism Foundation multisig operations if +all multisigs managed by these entities were to require mandatory delays. We want to build a +component that we can fit into our existing multisigs that provides this delay with minimal +configuration and minimal overhead for both signers and multisig leads. + +## Customer Description + + + +The customers for this design doc are any participants in the multisig accounts that would utilize +this module, as well as any 3rd parties who rely on the security properties of these accounts. + +### Customer Reviewers + + + +- Optimism Foundation (`LivenessModule` user) +- Optimism Security Council (`LivenessModule` user) + +## Requirements and Constraints + + + +- Enforce a mandatory delay on multisig transactions. +- Keep code as minimal as possible to avoid complex logic in Safe modules/guards. +- Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. +- Apply cleanly for all of the major multisigs. + +## Proposed Solution + + + +We propose the introduction of a new Safe guard, the `TimelockGuard`. The `TimelockGuard` would +take advantage of functionality in the latest version of Safe accounts to enforce a mandatory +timelock with minimal effort on the part of signers and a mostly unchanged workflow for multisig +leads. + +### Singleton + +`TimelockGuard` is a singleton that exists at a known address on all chains and has no constructor +parameters. Any Safe on any network can choose to configure the Guard and then enable the Guard +within the Safe. + +### Configuration + +A Safe configures the Guard by setting two delay periods, one that applies when a threshold is +reached and a second that applies if all members of the multisig sign the transaction. + +### Transaction Flow + +1. Users sign the transaction normally, either via the UI or via `superchain-ops`. +2. Anyone can collect the signatures and send the transaction along with the signatures to + `TimelockGuard.schedule`. This function has the exact same inputs as `execTransaction` and is + therefore compatible with both the standard Safe and the proposed nonceless execution module. +3. `TimelockGuard.schedule` verifies the signatures attached to the transaction and starts a timer + for execution of the transaction. If a threshold of signers have signed the timer is set to one + value, if all signers have signed the timer is set to some second (presumably shorter) value. +4. After the specified time, anyone can then call `Safe.execTransaction` as normal and execution + will complete as expected. + +## Alternatives Considered + + + +Other potential designs here make trade-offs for security or usability that are undesireable enough +to not really be worth considering. Examples of this are: + +- Allowing any user to reveal a transaction for later execution, which creates more considerations + for monitoring/offchain infrastructure and changes the transaction flow. +- Having multisigs queue transaction via the timelock directly and then allowing anyone to execute + the transaction via a module, which changes the signing flow significantly and means transactions + are challenging to simulate properly. + +We could not find any alternative design with the simplicity and security of the one that was +ultimately presented in this document. + +## Risks and Uncertainties + + + +### Timelock Overhead + +Any type of mandatory timelock introduces overhead for multisigs that want to execute transactions +in a timely fashion. Multisig leads will have to plan ahead of time in a way that they were not +previously required to do. We can mitigate some of this by allowing the full threshold to execute +faster, but this will be difficult to coordinate regularly and leads should not expect this to be +a norm. + +### Critical Transactions + +Some multisigs may face scenarios where they need to be able to execute transactions very quickly +in a worst-case scenario. We should do our best to avoid these situations as much as possible and +use other protocol features (like the Pause) to mitigate them entirely. We need to confirm with +users of finance multisigs that this is not expected to be common. We should avoid instant +execution at all costs, even if a signature from all owners would reduce the time to, say, 1 hour. From 8b523b9d31985dd5afdcc60bed63c2f261c2f623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= <38806121+alcueca@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:54:45 +0100 Subject: [PATCH 02/16] Update safe-modules/timelock-guard.md Co-authored-by: Maurelian --- safe-modules/timelock-guard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index 6e08eeaa..b7d32c53 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -25,7 +25,7 @@ configuration and minimal overhead for both signers and multisig leads. The customers for this design doc are any participants in the multisig accounts that would utilize -this module, as well as any 3rd parties who rely on the security properties of these accounts. +this guard, as well as any 3rd parties who rely on the security properties of these accounts. ### Customer Reviewers From c64776482171814282cdcddd1ef9deffe88eec77 Mon Sep 17 00:00:00 2001 From: alcueca Date: Thu, 21 Aug 2025 09:31:26 +0100 Subject: [PATCH 03/16] Resolved comments, added cancellation feature, removed dual delay --- safe-modules/timelock-guard.md | 94 +++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index b7d32c53..da6e8b0c 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -43,6 +43,7 @@ Problem Statement section in a bulleted list. --> - Keep code as minimal as possible to avoid complex logic in Safe modules/guards. - Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. - Apply cleanly for all of the major multisigs. +- Do not use sequential nonces, which we are planning to deprecate for operational reasons. ## Proposed Solution @@ -61,10 +62,9 @@ within the Safe. ### Configuration -A Safe configures the Guard by setting two delay periods, one that applies when a threshold is -reached and a second that applies if all members of the multisig sign the transaction. +A Safe configures the Guard by setting a delay period which can be zero. -### Transaction Flow +### Transaction Execution Flow 1. Users sign the transaction normally, either via the UI or via `superchain-ops`. 2. Anyone can collect the signatures and send the transaction along with the signatures to @@ -76,6 +76,79 @@ reached and a second that applies if all members of the multisig sign the transa 4. After the specified time, anyone can then call `Safe.execTransaction` as normal and execution will complete as expected. +### Replayability Of Transactions +Each scheduled transaction would be identified by a hash derived from the transaction parameters +plus a user-defined `salt`. This would allow to uniquely identify scheduled transactions while +allowing to schedule identical transaction calls more than once, different only in the time that +they will execute. + +### Storage Of Scheduled Transactions +Scheduled transactions will be stored with as much data as possible, limited by projected gas block +limits and gas required to execute the most complex expected transactions. Rich transaction data +helps with identifying the effects of a transaction, in particular to external operators such as +bug bounty huntesr that might lack the tools or the context from internal operators. + +### Transaction Cancellation Flow +To cancel a scheduled transaction, a `cancellation_threshold` number of owners would need to signal +that they agree with it. Cancelled transactions never will be executable, unless rescheduled with a +different `salt` and fresh signatures. + +To describe the cancellation flow, we will consider first a single multisig, and then an +arbitrarily nested multisig. + +#### Single Multisig Cancellation FLow +Members of the Safe can propose to cancel by calling `reject(txHash)`. + 1. EOAs can call this function directly. + 2. Smart contracts can call this function directly. + +Anyone can actually cancel the transaction by calling `cancel(txHash, owners[])` where `owners[]` +is the list of owners that called `reject`. TimelockGuard will then verify that each owner did, in +fact, call `reject` and that the number of owners that called `reject` is greater then or equal to +the `cancellation_threshold`. + +#### Nested Multisig Cancellation Flow +In a nested group of Safes, some Safes are owners of other Safes, building a tree with a single +Safe at the root. + +To allow for cancelling of scheduled transactions a nested Safe setup with an arbitrary depth, +owners of any Safe still call `reject(txHash)` for a transaction hash scheduled within the +TimelockGuard, which is common for all the Safes in the nested group. + +A new `cancel(SafeA, txHash, owners[])` allows to specify the executing safe (`SafeA`) and the list +of owners that signalled rejection of the scheduled transaction. Upon receiving this call, `SafeA` +will compare the list of signalling owners with its own owner members, and will request from any +owner members that are also Safes to recursively confirm rejection from their owners using the +unmatched owners from the original provided array. The process will continue until the +`cancellation_threshold` is met at the executing Safe, or all the originally provided owners have +been matched up without reaching the `cancellation_threshold` at the executing Safe. + +#### Choosing a `cancellation_threshold` +Choosing an appropriate `cancellation_threshold` is not obvious, and needs to be considered +according to several factors. +1. Stage 1 requires that the compromise of a 75% or less of the Security Council can't be enough +to cause a safety-level incident. If the Security Council would be turned into a reactive multisig +with cancellation duties instead of approval duties, a compromise of +`total_owners - cancellation_threshold + 1` SC owners would be enough to remove the ability of the +SC to cancel malicious transactions. For this reason, for the Security Council multisig, +`cancellation_threshold =< 0.25 * total_owners` +2. We define a "blocking minority" as the minimum number of `owners` that can stop the multisig +from approving transactions, which is `total_owners - quorum + 1`. If the `cancellation_threshold` +would be less than a blocking minority then it becomes the new blocking minority as it can stop any +transactions (including modifying ownership, or responding to liveness challenges) from execution. +A `cancellation_threshold` that is set too low can be abused in this way by malicious owners to +permanently block the multisig from executing transactions if a LivenessModule hasn't been +installed. + +Therefore: +It's recommended that the LivenessModule is installed in all multisigs, otherwise the +`cancellation_threshold` should not be lower than the "blocking minority". + +The `cancellation_threshold` should be high enough to limit having to use the liveness challenge +too frequently and disrupt operations, if that is a concern. + +For the Security Council, the `cancellation_threshold` should be lower than `0.25 * total_owners`, +or 3.25 with the current 10/13 membership. That leaves an optimal `cancellation_threshold` of 3. + ## Alternatives Considered ### Timelock Overhead - Any type of mandatory timelock introduces overhead for multisigs that want to execute transactions in a timely fashion. Multisig leads will have to plan ahead of time in a way that they were not previously required to do. We can mitigate some of this by allowing the full threshold to execute faster, but this will be difficult to coordinate regularly and leads should not expect this to be a norm. -### Critical Transactions - +### Time Critical Transactions Some multisigs may face scenarios where they need to be able to execute transactions very quickly in a worst-case scenario. We should do our best to avoid these situations as much as possible and use other protocol features (like the Pause) to mitigate them entirely. We need to confirm with users of finance multisigs that this is not expected to be common. We should avoid instant execution at all costs, even if a signature from all owners would reduce the time to, say, 1 hour. + +### Interaction with the LivenessModule +The `cancellation_threshold` becomes the new `blocking_minority`, and as such it can force not +meeting a liveness challenge. Given that such challenges should be issued by the trusted fallback +owner, this is not a concern. + +### Interaction with the UnorderedExecutionModule +To avoid issues with a future implementation of an UnorderedExecutionModule, the scheduling of +transactions should not include their nonce, while still maintaining the principle that cancelled +transactions can't be executed again with the same signatures, but can be executed again with fresh +signatures. From 669aec6dd7c1a46db8ca61b0c6c8b52812cbc594 Mon Sep 17 00:00:00 2001 From: alcueca Date: Thu, 21 Aug 2025 09:39:40 +0100 Subject: [PATCH 04/16] Incorporated other notes from the design review --- safe-modules/timelock-guard.md | 17 +++++++++++++---- security/images/.DS_Store | Bin 0 -> 6148 bytes 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 security/images/.DS_Store diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index da6e8b0c..6ae8d3d7 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -39,7 +39,8 @@ this guard, as well as any 3rd parties who rely on the security properties of th -- Enforce a mandatory delay on multisig transactions. +- Mitigate the impact of a malicious majority of owners capable to execute a single transaction. +- Mitigate the impact of an honest majority approving a malicious or erroneous transaction. - Keep code as minimal as possible to avoid complex logic in Safe modules/guards. - Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. - Apply cleanly for all of the major multisigs. @@ -187,9 +188,17 @@ users of finance multisigs that this is not expected to be common. We should avo execution at all costs, even if a signature from all owners would reduce the time to, say, 1 hour. ### Interaction with the LivenessModule -The `cancellation_threshold` becomes the new `blocking_minority`, and as such it can force not -meeting a liveness challenge. Given that such challenges should be issued by the trusted fallback -owner, this is not a concern. +The `cancellation_threshold` becomes the new `blocking_minority`. To avoid unexpected loss of +control, all multisigs enabling the TimelockGuard should also enable the LivenessModule. + +When using a LivenessModule, the liveness challenges are responded with a regular transaction which +is subject to the timelock delay, and as such the LivenessModule needs to have a challenge reponse +time that allows for both assembling a quorum of signers and executed a delayed transaction. + +Since the response to a liveness challenge is a transaction scheduled through the timelock, a +`cancellation_threshold` of owners can block it, and as such it can force not meeting a liveness +challenge, which would cause the ownership of the multisig to revert to the fallback owner. Given +that liveness challenges should be issued by the trusted fallback owner, this is not a concern. ### Interaction with the UnorderedExecutionModule To avoid issues with a future implementation of an UnorderedExecutionModule, the scheduling of diff --git a/security/images/.DS_Store b/security/images/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Thu, 21 Aug 2025 16:52:48 +0100 Subject: [PATCH 05/16] Final edits with regards to cancellation threshold and cancellation flow implementation --- safe-modules/timelock-guard.md | 78 ++++++++++++---------------------- 1 file changed, 26 insertions(+), 52 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index 6ae8d3d7..2551b13e 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -45,6 +45,7 @@ Problem Statement section in a bulleted list. --> - Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. - Apply cleanly for all of the major multisigs. - Do not use sequential nonces, which we are planning to deprecate for operational reasons. +- The designed solution should be aplicable to single and arbitrarily nested Safe configurations. ## Proposed Solution @@ -94,64 +95,32 @@ To cancel a scheduled transaction, a `cancellation_threshold` number of owners w that they agree with it. Cancelled transactions never will be executable, unless rescheduled with a different `salt` and fresh signatures. -To describe the cancellation flow, we will consider first a single multisig, and then an -arbitrarily nested multisig. - -#### Single Multisig Cancellation FLow -Members of the Safe can propose to cancel by calling `reject(txHash)`. - 1. EOAs can call this function directly. - 2. Smart contracts can call this function directly. - -Anyone can actually cancel the transaction by calling `cancel(txHash, owners[])` where `owners[]` -is the list of owners that called `reject`. TimelockGuard will then verify that each owner did, in -fact, call `reject` and that the number of owners that called `reject` is greater then or equal to -the `cancellation_threshold`. - -#### Nested Multisig Cancellation Flow -In a nested group of Safes, some Safes are owners of other Safes, building a tree with a single -Safe at the root. - -To allow for cancelling of scheduled transactions a nested Safe setup with an arbitrary depth, -owners of any Safe still call `reject(txHash)` for a transaction hash scheduled within the -TimelockGuard, which is common for all the Safes in the nested group. - -A new `cancel(SafeA, txHash, owners[])` allows to specify the executing safe (`SafeA`) and the list -of owners that signalled rejection of the scheduled transaction. Upon receiving this call, `SafeA` -will compare the list of signalling owners with its own owner members, and will request from any -owner members that are also Safes to recursively confirm rejection from their owners using the -unmatched owners from the original provided array. The process will continue until the -`cancellation_threshold` is met at the executing Safe, or all the originally provided owners have -been matched up without reaching the `cancellation_threshold` at the executing Safe. +In nested safe setups, the owners of a child multisig would need to signal the rejection of the +transaction in numbers no less than the `cancellation_threshold` of the child multisig, for the +child multisig to signal rejection of the transaction to the parent multisig. #### Choosing a `cancellation_threshold` -Choosing an appropriate `cancellation_threshold` is not obvious, and needs to be considered -according to several factors. -1. Stage 1 requires that the compromise of a 75% or less of the Security Council can't be enough -to cause a safety-level incident. If the Security Council would be turned into a reactive multisig -with cancellation duties instead of approval duties, a compromise of -`total_owners - cancellation_threshold + 1` SC owners would be enough to remove the ability of the -SC to cancel malicious transactions. For this reason, for the Security Council multisig, -`cancellation_threshold =< 0.25 * total_owners` -2. We define a "blocking minority" as the minimum number of `owners` that can stop the multisig +The `cancellation_threshold` should be always the same as the `blocking_minority`. + +1. We define a "blocking minority" as the minimum number of `owners` that can stop the multisig from approving transactions, which is `total_owners - quorum + 1`. If the `cancellation_threshold` would be less than a blocking minority then it becomes the new blocking minority as it can stop any transactions (including modifying ownership, or responding to liveness challenges) from execution. A `cancellation_threshold` that is set too low can be abused in this way by malicious owners to permanently block the multisig from executing transactions if a LivenessModule hasn't been -installed. - -Therefore: -It's recommended that the LivenessModule is installed in all multisigs, otherwise the -`cancellation_threshold` should not be lower than the "blocking minority". - -The `cancellation_threshold` should be high enough to limit having to use the liveness challenge -too frequently and disrupt operations, if that is a concern. - -For the Security Council, the `cancellation_threshold` should be lower than `0.25 * total_owners`, -or 3.25 with the current 10/13 membership. That leaves an optimal `cancellation_threshold` of 3. +installed. For this reason, `cancellation_threshold >= blocking_minority`. +2. Stage 1 requires that the compromise of a 75% or less of the Security Council can't be enough +to cause a safety-level incident. If the Security Council would be turned into a reactive multisig +with cancellation duties instead of approval duties, a compromise of +`total_owners - cancellation_threshold` SC owners would be enough to remove the ability of the +SC to cancel malicious transactions. For this reason, for the Security Council multisig, +`cancellation_threshold =< ceiling(0.25 * total_owners)`. This is coincidentally the same as the +`blocking_minority` for the SC multisig. Therefore, `cancellation_threshold <= blocking_minority`. +3. A configurable `cancellation_threshold` would need to be maintained along with `total_owners` +and `quorum`, and would be likely to be misconfigured at some point. For this reason, it is best +for the TimelockGuard to calculate the threshold from available data. ## Alternatives Considered - @@ -164,6 +133,14 @@ to not really be worth considering. Examples of this are: - Having multisigs queue transaction via the timelock directly and then allowing anyone to execute the transaction via a module, which changes the signing flow significantly and means transactions are challenging to simulate properly. +- Configurable `cancellation_thresholds` were considered, but the risk of misconfiguration over + time was expected to be considerable. +- A fixed `cancellation_threshold` of 1 was considered, but the risk of operational disruption by + malicious actors was considered unacceptable. +- An increasing `cancellation_threshold` from 1 to `min(blocking_minority, quorum)`, which would + increase by one with each successful cancellation, and reset to one with each successful + execution, was considered. It was deemed that at its lower end it would be too operationally + disruptive, and not worth the additional complexity. We could not find any alternative design with the simplicity and security of the one that was ultimately presented in this document. @@ -188,9 +165,6 @@ users of finance multisigs that this is not expected to be common. We should avo execution at all costs, even if a signature from all owners would reduce the time to, say, 1 hour. ### Interaction with the LivenessModule -The `cancellation_threshold` becomes the new `blocking_minority`. To avoid unexpected loss of -control, all multisigs enabling the TimelockGuard should also enable the LivenessModule. - When using a LivenessModule, the liveness challenges are responded with a regular transaction which is subject to the timelock delay, and as such the LivenessModule needs to have a challenge reponse time that allows for both assembling a quorum of signers and executed a delayed transaction. From 0476fa7c087b35e5dcbb6155581e026c6dd347f0 Mon Sep 17 00:00:00 2001 From: alcueca Date: Fri, 22 Aug 2025 06:59:39 +0100 Subject: [PATCH 06/16] Added possibility of dynamic cancellation threshold --- safe-modules/timelock-guard.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index 2551b13e..80b8ec61 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -100,10 +100,8 @@ transaction in numbers no less than the `cancellation_threshold` of the child mu child multisig to signal rejection of the transaction to the parent multisig. #### Choosing a `cancellation_threshold` -The `cancellation_threshold` should be always the same as the `blocking_minority`. - 1. We define a "blocking minority" as the minimum number of `owners` that can stop the multisig -from approving transactions, which is `total_owners - quorum + 1`. If the `cancellation_threshold` +from approving transactions, which is `min(quorum, total_owners - quorum + 1)`. If the `cancellation_threshold` would be less than a blocking minority then it becomes the new blocking minority as it can stop any transactions (including modifying ownership, or responding to liveness challenges) from execution. A `cancellation_threshold` that is set too low can be abused in this way by malicious owners to @@ -120,6 +118,15 @@ SC to cancel malicious transactions. For this reason, for the Security Council m and `quorum`, and would be likely to be misconfigured at some point. For this reason, it is best for the TimelockGuard to calculate the threshold from available data. +The `cancellation_threshold` should either always be the same as the `blocking_minority` or if the +code complexity is acceptable, the `cancellation_threshold` can be dynamic between 1 and +`blocking_minority`. A dynamic `cancellation_threshold` offers a better response time while not +being vulnerable to sustained abuse by a small amount of malicious owners. + +If a dynamic `cancellation_threshold` is chosen, it would start at 1 and increase by 1 with each +successful consecutive cancellation. The `cancellation_threshold` would reset to 1 with each +successful execution of an scheduled transaction. + ## Alternatives Considered - Keep code as minimal as possible to avoid complex logic in Safe modules/guards. - Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. - Apply cleanly for all of the major multisigs. -- Do not use sequential nonces, which we are planning to deprecate for operational reasons. +- Support nonceless execution, which we are planning to implement for operational reasons. - The designed solution should be aplicable to single and arbitrarily nested Safe configurations. ## Proposed Solution @@ -68,14 +68,16 @@ configure it during installation. To be explored during implementation. ### Configuration -A Safe configures the Guard by setting a delay period which can be zero. +The owners of a Safe configure the Guard by executing a transaction within the Safe that sets a +delay period which can be zero. ### Transaction Execution Flow 1. Users sign the transaction normally, either via the UI or via `superchain-ops`. -2. Anyone can collect the signatures and send the transaction along with the signatures to - `TimelockGuard.schedule`. This function has the exact same inputs as `execTransaction` and is - therefore compatible with both the standard Safe and the proposed nonceless execution module. +2. Anyone can collect the signatures and, send the transaction calldata along with the signatures + to `TimelockGuard.schedule`, instead of executing via `Safe.execTransaction`. This function has + the exact same inputs as `execTransaction` and is therefore compatible with both the standard + Safe and the proposed nonceless execution module. 3. `TimelockGuard.schedule` verifies the signatures attached to the transaction and starts a timer for execution of the transaction. If a threshold of signers have signed the timer is set to one value, if all signers have signed the timer is set to some second (presumably shorter) value. @@ -83,53 +85,42 @@ A Safe configures the Guard by setting a delay period which can be zero. will complete as expected. ### Replayability Of Transactions -Each scheduled transaction would be identified by a hash derived from the transaction parameters -plus a user-defined `salt`. This would allow to uniquely identify scheduled transactions while -allowing to schedule identical transaction calls more than once, different only in the time that -they will execute. +Each scheduled transaction would be identified by a hash derived from the transaction parameters, +which already include a nonce for replayability protection. Cancelled transactions will be marked +as cancelled so that they can't be scheduled again with the same signatures. ### Storage Of Scheduled Transactions -Scheduled transactions will be stored with as much data as possible, limited by projected gas block -limits and gas required to execute the most complex expected transactions. Rich transaction data -helps with identifying the effects of a transaction, in particular to external operators such as -bug bounty huntesr that might lack the tools or the context from internal operators. +Scheduled transactions will be stored with as many of its parameters as possible, limited by +projected gas block limits and gas required to execute the most complex expected transactions. Rich +transaction data helps with identifying the effects of a transaction, in particular to external +operators such as bug bounty hunters that might lack the tools or the context from internal operators. ### Transaction Cancellation Flow To cancel a scheduled transaction, a `cancellation_threshold` number of owners would need to signal -that they agree with it. Cancelled transactions never will be executable, unless rescheduled with a -different `salt` and fresh signatures. +that they agree with it. Cancelled transactions would need to be signed again to be scheduled again. In nested safe setups, the owners of a child multisig would need to signal the rejection of the transaction in numbers no less than the `cancellation_threshold` of the child multisig, for the child multisig to signal rejection of the transaction to the parent multisig. -#### Choosing a `cancellation_threshold` -1. We define a "blocking minority" as the minimum number of `owners` that can stop the multisig +#### Dynamic `cancellation_threshold` +We define a "blocking minority" as the minimum number of `owners` that can stop the multisig from approving transactions, which is `min(quorum, total_owners - quorum + 1)`. If the `cancellation_threshold` would be less than a blocking minority then it becomes the new blocking minority as it can stop any transactions (including modifying ownership, or responding to liveness challenges) from execution. A `cancellation_threshold` that is set too low can be abused in this way by malicious owners to permanently block the multisig from executing transactions if a LivenessModule hasn't been installed. For this reason, `cancellation_threshold >= blocking_minority`. -2. Stage 1 requires that the compromise of a 75% or less of the Security Council can't be enough -to cause a safety-level incident. If the Security Council would be turned into a reactive multisig -with cancellation duties instead of approval duties, a compromise of -`total_owners - cancellation_threshold` SC owners would be enough to remove the ability of the -SC to cancel malicious transactions. For this reason, for the Security Council multisig, -`cancellation_threshold =< ceiling(0.25 * total_owners)`. This is coincidentally the same as the -`blocking_minority` for the SC multisig. Therefore, `cancellation_threshold <= blocking_minority`. -3. A configurable `cancellation_threshold` would need to be maintained along with `total_owners` -and `quorum`, and would be likely to be misconfigured at some point. For this reason, it is best -for the TimelockGuard to calculate the threshold from available data. - -The `cancellation_threshold` should either always be the same as the `blocking_minority` or if the -code complexity is acceptable, the `cancellation_threshold` can be dynamic between 1 and -`blocking_minority`. A dynamic `cancellation_threshold` offers a better response time while not -being vulnerable to sustained abuse by a small amount of malicious owners. - -If a dynamic `cancellation_threshold` is chosen, it would start at 1 and increase by 1 with each -successful consecutive cancellation. The `cancellation_threshold` would reset to 1 with each -successful execution of an scheduled transaction. + +A `cancellation_threshold` of 1 is convenient for usability as it requires the least +number of owners to signal their agreement to cancel a transaction in an emergency situation. +However, it is not the most secure option as it can be abused by malicious owners to permanently +block the multisig from executing transactions. + +A dynamic `cancellation_threshold` is chosen as a best compromise between security and usability. +The `cancellation_threshold` would start at 1 and increase by 1 with each successful consecutive +cancellation. The `cancellation_threshold` would reset to 1 with the successful execution of an +scheduled transaction. ## Alternatives Considered ### Timelock Overhead Any type of mandatory timelock introduces overhead for multisigs that want to execute transactions in a timely fashion. Multisig leads will have to plan ahead of time in a way that they were not -previously required to do. We can mitigate some of this by allowing the full threshold to execute -faster, but this will be difficult to coordinate regularly and leads should not expect this to be -a norm. +previously required to do. ### Time Critical Transactions Some multisigs may face scenarios where they need to be able to execute transactions very quickly @@ -184,9 +169,3 @@ Since the response to a liveness challenge is a transaction scheduled through th `cancellation_threshold` of owners can block it, and as such it can force not meeting a liveness challenge, which would cause the ownership of the multisig to revert to the fallback owner. Given that liveness challenges should be issued by the trusted fallback owner, this is not a concern. - -### Interaction with the UnorderedExecutionModule -To avoid issues with a future implementation of an UnorderedExecutionModule, the scheduling of -transactions should not include their nonce, while still maintaining the principle that cancelled -transactions can't be executed again with the same signatures, but can be executed again with fresh -signatures. From 8cf63d69c04d352a3f1e95e675e23a4460017892 Mon Sep 17 00:00:00 2001 From: alcueca Date: Wed, 3 Sep 2025 07:22:43 +0100 Subject: [PATCH 10/16] Edited for clarity after adressing the comments. --- safe-modules/timelock-guard.md | 45 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index 42964b3d..1a60dd4a 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -41,6 +41,7 @@ Problem Statement section in a bulleted list. --> - Mitigate the impact of a malicious majority of owners capable to execute a single transaction. - Mitigate the impact of an honest majority approving a malicious or erroneous transaction. +- Maintain the ability to execute transactions quickly in a worst-case scenario. <-- NOT ADDRESSED - Keep code as minimal as possible to avoid complex logic in Safe modules/guards. - Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. - Apply cleanly for all of the major multisigs. @@ -68,19 +69,18 @@ configure it during installation. To be explored during implementation. ### Configuration -The owners of a Safe configure the Guard by executing a transaction within the Safe that sets a -delay period which can be zero. +A transaction from the Safe configures the Guard by executing a transaction that optionally sets +a delay period. If the delay period is not set, the TimelockGuard is enabled with no delay. ### Transaction Execution Flow 1. Users sign the transaction normally, either via the UI or via `superchain-ops`. -2. Anyone can collect the signatures and, send the transaction calldata along with the signatures +2. Anyone can collect the signatures and send the transaction calldata along with the signatures to `TimelockGuard.schedule`, instead of executing via `Safe.execTransaction`. This function has the exact same inputs as `execTransaction` and is therefore compatible with both the standard Safe and the proposed nonceless execution module. 3. `TimelockGuard.schedule` verifies the signatures attached to the transaction and starts a timer - for execution of the transaction. If a threshold of signers have signed the timer is set to one - value, if all signers have signed the timer is set to some second (presumably shorter) value. + for execution of the transaction. 4. After the specified time, anyone can then call `Safe.execTransaction` as normal and execution will complete as expected. @@ -90,45 +90,44 @@ which already include a nonce for replayability protection. Cancelled transactio as cancelled so that they can't be scheduled again with the same signatures. ### Storage Of Scheduled Transactions -Scheduled transactions will be stored with as many of its parameters as possible, limited by -projected gas block limits and gas required to execute the most complex expected transactions. Rich -transaction data helps with identifying the effects of a transaction, in particular to external -operators such as bug bounty hunters that might lack the tools or the context from internal operators. +Scheduled transactions will be stored with as many of its parameters as possible, limited by the +gas required to execute the most complex expected transactions withi projected block gas limits. +Rich transaction data helps with identifying the effects of a transaction, in particular to +external operators such as bug bounty hunters that might lack the tools or the context from +internal operators. ### Transaction Cancellation Flow -To cancel a scheduled transaction, a `cancellation_threshold` number of owners would need to signal -that they agree with it. Cancelled transactions would need to be signed again to be scheduled again. +To cancel a scheduled transaction, a `cancellation_threshold` number of owners would need to +signal that they agree with the cancellation. Cancelled transactions would need to be signed again +to be scheduled again. In nested safe setups, the owners of a child multisig would need to signal the rejection of the transaction in numbers no less than the `cancellation_threshold` of the child multisig, for the child multisig to signal rejection of the transaction to the parent multisig. #### Dynamic `cancellation_threshold` -We define a "blocking minority" as the minimum number of `owners` that can stop the multisig -from approving transactions, which is `min(quorum, total_owners - quorum + 1)`. If the `cancellation_threshold` -would be less than a blocking minority then it becomes the new blocking minority as it can stop any -transactions (including modifying ownership, or responding to liveness challenges) from execution. -A `cancellation_threshold` that is set too low can be abused in this way by malicious owners to -permanently block the multisig from executing transactions if a LivenessModule hasn't been -installed. For this reason, `cancellation_threshold >= blocking_minority`. - A `cancellation_threshold` of 1 is convenient for usability as it requires the least number of owners to signal their agreement to cancel a transaction in an emergency situation. -However, it is not the most secure option as it can be abused by malicious owners to permanently -block the multisig from executing transactions. + +However, a `cancellation_threshold` that is set too low can also be abused by malicious owners to +permanently block the multisig from executing transactions, inducing a liveness failure. A dynamic `cancellation_threshold` is chosen as a best compromise between security and usability. The `cancellation_threshold` would start at 1 and increase by 1 with each successful consecutive cancellation. The `cancellation_threshold` would reset to 1 with the successful execution of an scheduled transaction. +The upper boundary to the `cancellation_threshold` would be the "blocking minority", defined as +the minimum number of `owners` that can stop the multisig from approving transactions, which is +`min(quorum, total_owners - quorum + 1)`. + ## Alternatives Considered -Other potential designs here make trade-offs for security or usability that are undesireable enough -to not really be worth considering. Examples of this are: +Other potential designs that were considered make trade-offs for security or usability that are +suboptimal. Examples of this are: - Allowing any user to reveal a transaction for later execution, which creates more considerations for monitoring/offchain infrastructure and changes the transaction flow. From 3d1a4e6f83e712642df3108fb13812ab4e2b5adb Mon Sep 17 00:00:00 2001 From: alcueca Date: Thu, 4 Sep 2025 06:06:41 +0100 Subject: [PATCH 11/16] Details around instant execution --- safe-modules/timelock-guard.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index 1a60dd4a..5bcb3b7f 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -155,9 +155,15 @@ previously required to do. ### Time Critical Transactions Some multisigs may face scenarios where they need to be able to execute transactions very quickly in a worst-case scenario. We should do our best to avoid these situations as much as possible and -use other protocol features (like the Pause) to mitigate them entirely. We need to confirm with -users of finance multisigs that this is not expected to be common. We should avoid instant -execution at all costs, even if a signature from all owners would reduce the time to, say, 1 hour. +use other protocol features (like the Pause) to mitigate them entirely. + +However, we do not have an alternative to an emergency upgrade for production failures on the +deposit flow. Until that is implemented, we need to be able to execute transactions quickly in a +worst-case scenario. For this reason, the delay will be no longer that is required for a +transaction review by already engaged security researchers. + +Instant execution is not required for finance multisigs. The delay will however be no longer than +the time required for a transaction review by trained finance staff. ### Interaction with the LivenessModule When using a LivenessModule, the liveness challenges are responded with a regular transaction which From 6ca237bce88be4876e20fa6c3bc415bdeb0e9128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= <38806121+alcueca@users.noreply.github.com> Date: Fri, 5 Sep 2025 17:47:49 +0100 Subject: [PATCH 12/16] Update safe-modules/timelock-guard.md Co-authored-by: Maurelian --- safe-modules/timelock-guard.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index 5bcb3b7f..d99a0743 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -31,8 +31,8 @@ this guard, as well as any 3rd parties who rely on the security properties of th -- Optimism Foundation (`LivenessModule` user) -- Optimism Security Council (`LivenessModule` user) +- Optimism Foundation (`TimelockGuard` user) +- Optimism Security Council (`TimelockGuard` user) ## Requirements and Constraints From 3cd526584a6cf13c0d4903b6b7cc3de6995b7118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= Date: Fri, 5 Sep 2025 18:22:35 +0100 Subject: [PATCH 13/16] Removing L1PAO from scope --- safe-modules/timelock-guard.md | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/safe-modules/timelock-guard.md b/safe-modules/timelock-guard.md index d99a0743..63c98ab4 100644 --- a/safe-modules/timelock-guard.md +++ b/safe-modules/timelock-guard.md @@ -31,8 +31,8 @@ this guard, as well as any 3rd parties who rely on the security properties of th -- Optimism Foundation (`TimelockGuard` user) -- Optimism Security Council (`TimelockGuard` user) +- Optimism Foundation Finance +- OP Labs Finance ## Requirements and Constraints @@ -41,7 +41,6 @@ Problem Statement section in a bulleted list. --> - Mitigate the impact of a malicious majority of owners capable to execute a single transaction. - Mitigate the impact of an honest majority approving a malicious or erroneous transaction. -- Maintain the ability to execute transactions quickly in a worst-case scenario. <-- NOT ADDRESSED - Keep code as minimal as possible to avoid complex logic in Safe modules/guards. - Limit mental overhead for Safe owners and multisig leads, don't break workflows if possible. - Apply cleanly for all of the major multisigs. @@ -121,6 +120,16 @@ The upper boundary to the `cancellation_threshold` would be the "blocking minori the minimum number of `owners` that can stop the multisig from approving transactions, which is `min(quorum, total_owners - quorum + 1)`. +### Interaction with the LivenessModule +The LivenessModule will execute an ownership change through the safe if a challenge is successful, +this ownership challenge should not be cancellable, so it shouldn't be subject to the scheduling +flow. + +When using a LivenessModule, the liveness challenges are responded with a regular transaction which +is subject to the timelock delay, and as such the LivenessModule needs to have a challenge response +time that allows for both assembling a quorum of signers and executed a delayed transaction. An +integrated LivenessModule and TimelockGuard is a viable option. + ## Alternatives Considered