This repository was archived by the owner on Oct 27, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
Setup certora workflow #96
Draft
akshay-ap
wants to merge
16
commits into
main
Choose a base branch
from
feature-95-formal-verification
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
7fc684f
[#95] Setup certora workflow
akshay-ap dddcbf5
[#95] Add execute permission to script
akshay-ap 40f1b8c
[#95] Add param --solc in verifyManager.sh
akshay-ap 14ae100
[#95] Install dependencies by running yarn --frozen-lockfile
akshay-ap 3b8b335
[#95] Fix README, add newline at end of file
akshay-ap c1911af
Merge branch 'main' of github.com:safe-global/safe-core-protocol into…
akshay-ap f919192
[#95] Add formal verification rules
akshay-ap e3644ca
[#95] Update rule for hooks
akshay-ap 8ef23d0
[#95] Check if function handler is listed and not flagged
akshay-ap 7c9f352
[#95] Add rules for formal verification
akshay-ap 19cba7a
[#95] Update hashing_length_bound to 320
akshay-ap 1b64211
[#95] Append solc param in verifyManager.sh
akshay-ap ce2c27d
Merge branch 'main' of github.com:safe-global/safe-core-protocol into…
akshay-ap 6ef3c49
[#95] Fix failing build
akshay-ap ef786a7
[#95] Fix solc param in verifyManager.sh
akshay-ap 4b3db3c
[#95] Add rules
akshay-ap File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| name: certora | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| pull_request: | ||
| branches: | ||
| - main | ||
|
|
||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| verify: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| rule: ["verifyManager.sh"] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v3 | ||
|
|
||
| - name: Install python | ||
| uses: actions/setup-python@v4 | ||
| with: { python-version: 3.11 } | ||
|
|
||
| - name: Install java | ||
| uses: actions/setup-java@v3 | ||
| with: { java-version: "17", java-package: jre, distribution: semeru } | ||
|
|
||
| - name: Install certora cli | ||
| run: pip install -Iv certora-cli | ||
|
|
||
| - name: Install solc | ||
| run: | | ||
| wget https://github.com/ethereum/solidity/releases/download/v0.8.18/solc-static-linux | ||
| chmod +x solc-static-linux | ||
| sudo mv solc-static-linux /usr/local/bin/solc8.18 | ||
|
|
||
| - name: Install dependencies | ||
| run: yarn --frozen-lockfile | ||
|
|
||
| - name: Verify rule ${{ matrix.rule }} | ||
| run: | | ||
| echo "key length" ${#CERTORAKEY} | ||
| chmod +x ./certora/scripts/${{ matrix.rule }} | ||
| ./certora/scripts/${{ matrix.rule }} | ||
| env: | ||
| CERTORAKEY: ${{ secrets.CERTORA_KEY }} |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Formal verification of Safe{Core} Protocol contracts | ||
|
|
||
| ## Setup | ||
|
|
||
| - Install certora | ||
| - Setup key | ||
| - Execute | ||
|
|
||
| ```bash | ||
| sh certora/scripts/verifyManager.sh | ||
| ``` |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "files": [ | ||
| "contracts/SafeProtocolManager.sol", | ||
| "contracts/SafeProtocolRegistry.sol", | ||
| "contracts/test/TestExecutorCertora.sol", | ||
| "contracts/test/certora/TestExecutorCertoraOther.sol", | ||
| "contracts/test/TestFunctionHandlerCertora.sol", | ||
| "contracts/test/certora/TestHooksCertora.sol", | ||
| "contracts/test/TestPlugin.sol" | ||
|
|
||
| ], | ||
| "hashing_length_bound": "320", | ||
| "link": [ | ||
| "SafeProtocolManager:registry=SafeProtocolRegistry" | ||
| ], | ||
| "loop_iter": "1", | ||
| "prover_args" : ["-copyLoopUnroll 11"], | ||
| "msg": "Safe Protocol Manager", | ||
| "optimistic_hashing": true, | ||
| "optimistic_loop": true, | ||
| "rule_sanity": "basic", | ||
| // "send_only": true, | ||
| "verify": "SafeProtocolManager:certora/specs/Manager.spec" | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| #!/bin/bash | ||
|
|
||
| certoraRun certora/confs/run.conf --solc solc8.18 |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| using SafeProtocolRegistry as contractRegistry; | ||
| using TestExecutorCertora as testExecutorCertora; | ||
| using TestFunctionHandlerCertora as testFunctionHandlerCertora; | ||
| using TestHooksCertora as testHooksCertora; | ||
| using TestExecutorCertoraOther as testExecutorCertoraOther; | ||
| using TestPlugin as testPlugin; | ||
|
|
||
| methods { | ||
| function setRegistry(address) external; | ||
| function registry() external returns (address) envfree; | ||
| function tempHooksData(address) external returns (address, bytes) envfree; | ||
|
|
||
| function _.supportsInterface(bytes4) external => DISPATCHER(true); | ||
| function testExecutorCertora.called() external returns (bool) envfree; | ||
|
|
||
| function contractRegistry.check(address module) external returns (uint64, uint64) envfree; | ||
| function _.execTransactionFromModule( | ||
| address, | ||
| uint256, | ||
| bytes, | ||
| uint8 | ||
| ) external => DISPATCHER(true); | ||
|
|
||
| function _.execTransactionFromModuleReturnData( | ||
| address, | ||
| uint256, | ||
| bytes, | ||
| uint8 | ||
| ) external => DISPATCHER(true); | ||
|
|
||
| function _.preCheck( | ||
| address, | ||
| SafeProtocolManager.SafeTransaction, | ||
| uint256, | ||
| bytes | ||
| ) external => DISPATCHER(true); | ||
|
|
||
| function _.preCheckRootAccess( | ||
| address, | ||
| SafeProtocolManager.SafeRootAccess, | ||
| uint256, | ||
| bytes | ||
| ) external => DISPATCHER(true); | ||
|
|
||
| function _.postCheck(address, bool, bytes) external => DISPATCHER(true); | ||
|
|
||
| function _.handle(address, address, uint256, bytes) external => DISPATCHER(true); | ||
|
|
||
| function testFunctionHandlerCertora.called() external returns (bool) envfree; | ||
|
|
||
| function testHooksCertora.called() external returns (bool) envfree; | ||
|
|
||
| function _.requiresPermissions() external => DISPATCHER(true); | ||
| } | ||
|
|
||
| rule onlyOwnerCanSetRegistry (method f) filtered { | ||
| f -> f.selector != sig:setRegistry(address).selector | ||
| } | ||
| { | ||
| address registryBefore = registry(); | ||
|
|
||
| calldataarg args; env e; | ||
| f(e, args); | ||
|
|
||
| address registryAfter = registry(); | ||
|
|
||
| assert registryBefore == registryAfter; | ||
|
|
||
| } | ||
|
|
||
| rule onlyEnabledAndListedPluginCanExecuteCall(method f) filtered { | ||
| f -> f.selector == sig:executeTransaction(address,SafeProtocolManager.SafeTransaction).selector || f.selector == sig:executeRootAccess(address,SafeProtocolManager.SafeRootAccess).selector | ||
| } { | ||
|
|
||
| env e; calldataarg args; | ||
|
|
||
| requireInvariant tempHooksStorage(e.msg.sender); | ||
|
|
||
| require(testExecutorCertora.called() == false); | ||
| require(testFunctionHandlerCertora.called() == false); | ||
| require(testHooksCertora.called() == false); | ||
|
|
||
| f(e, args); | ||
|
|
||
| uint64 listedAt; | ||
| uint64 flagged; | ||
|
|
||
| listedAt, flagged = contractRegistry.check(e.msg.sender); | ||
|
|
||
| assert testExecutorCertora.called() => (listedAt > 0 && flagged == 0); | ||
|
|
||
| uint64 listedAtHandler; | ||
| uint64 flaggedHandler; | ||
|
|
||
| listedAtHandler, flaggedHandler = contractRegistry.check(testFunctionHandlerCertora); | ||
|
|
||
| assert testFunctionHandlerCertora.called() => (listedAtHandler > 0 && flaggedHandler == 0); | ||
|
|
||
|
|
||
| uint64 listedAtHooks; | ||
| uint64 flaggedHooks; | ||
|
|
||
| listedAtHooks, flaggedHooks = contractRegistry.check(testHooksCertora); | ||
|
|
||
| assert testHooksCertora.called() => (listedAtHooks > 0 && flaggedHooks == 0); | ||
|
|
||
| } | ||
|
|
||
| rule hooksUpdates(address safe, SafeProtocolManager.SafeTransaction transactionData){ | ||
|
|
||
| // method f; | ||
| env e; calldataarg args; | ||
| storage initialStorage = lastStorage; | ||
| executeTransaction(e, safe, transactionData); | ||
|
|
||
| env e2; | ||
| setHooks(e2, 0) at initialStorage; | ||
|
|
||
| executeTransaction@withrevert(e, safe, transactionData); | ||
|
|
||
| assert !lastReverted; | ||
|
|
||
| } | ||
|
|
||
| function checkListedAndNotFlagged(address module) returns bool { | ||
| uint64 listedAtHooks; | ||
| uint64 flaggedHooks; | ||
| listedAtHooks, flaggedHooks = contractRegistry.check(module); | ||
| return (listedAtHooks > 0 && flaggedHooks == 0); | ||
| } | ||
|
|
||
| function getTempHooksData(address account) returns address { | ||
| address hooksAddress; | ||
| bytes hooksData; | ||
| hooksAddress, hooksData = tempHooksData(account); | ||
| return hooksAddress; | ||
| } | ||
|
|
||
| invariant tempHooksStorage(address plugin) | ||
| getTempHooksData(plugin) != 0 => checkListedAndNotFlagged(getTempHooksData(plugin)) | ||
| filtered { f -> f.selector != sig:checkModuleTransaction(address,uint256,bytes,Enum.Operation,address).selector} | ||
|
|
||
| invariant tempHooksStorageIsAlwaysEmpty(address plugin) | ||
| getTempHooksData(plugin) == 0 | ||
| filtered { f -> f.selector != sig:checkModuleTransaction(address,uint256,bytes,Enum.Operation,address).selector} | ||
|
|
||
|
|
||
| rule onlyOneStorageUpdates{ | ||
| storage before = lastStorage; | ||
| method f; env e; calldataarg args; | ||
| f(e, args); | ||
| storage after = lastStorage; | ||
| // Either storage of testExecutorCertora is updated or storage of testExecutorCertoraOther is updated | ||
| assert (before[testExecutorCertora] == after[testExecutorCertora]) || (before[testExecutorCertoraOther] == after[testExecutorCertoraOther]); | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,8 @@ contract SafeProtocolManager is ISafeProtocolManager, RegistryManager, HooksMana | |
| if (areHooksEnabled) { | ||
| // execution metadata for transaction execution through plugin is encoded address of the plugin i.e. msg.sender. | ||
| // executionType = 1 for plugin flow | ||
| // should check below exist here? | ||
| checkPermittedModule(hooksAddress); | ||
| preCheckData = ISafeProtocolHooks(hooksAddress).preCheck(account, transaction, 1, abi.encode(msg.sender)); | ||
| } | ||
|
|
||
|
|
@@ -140,6 +142,8 @@ contract SafeProtocolManager is ISafeProtocolManager, RegistryManager, HooksMana | |
| if (areHooksEnabled) { | ||
| // execution metadata for transaction execution through plugin is encoded address of the plugin i.e. msg.sender. | ||
| // executionType = 1 for plugin flow | ||
| // should check below exist here? | ||
| checkPermittedModule(hooksAddress); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should revert or skip if flagged? |
||
| preCheckData = ISafeProtocolHooks(hooksAddress).preCheckRootAccess(account, rootAccess, 1, abi.encode(msg.sender)); | ||
| } | ||
|
|
||
|
|
||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,6 +69,8 @@ abstract contract FunctionHandlerManager is RegistryManager { | |
| revert FunctionHandlerNotSet(account, functionSelector); | ||
| } | ||
|
|
||
| checkPermittedModule(functionHandler); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should revert or skip if flagged?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, since this doesn't affect the core execution flow, it's OK to revert here. |
||
|
|
||
| address sender; | ||
| // solhint-disable-next-line no-inline-assembly | ||
| assembly { | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should revert or skip if flagged?