Skip to content

Commit

Permalink
Fix the methods block in the specs
Browse files Browse the repository at this point in the history
  • Loading branch information
mmv08 committed Sep 27, 2023
1 parent 1abedf3 commit bc08fb0
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 228 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/certora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rule: ["verifyOwners.sh", "verifySafe.sh", "verifyModules.sh"]
rule: ["verifyOwners.sh", "verifySafe.sh", "verifyModules.sh", "verifySignatures.sh"]

steps:
- uses: actions/checkout@v3
Expand Down
6 changes: 3 additions & 3 deletions certora/applyHarness.patch
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
diff -druN Safe.sol Safe.sol
--- Safe.sol 2023-09-27 10:13:46
+++ Safe.sol 2023-09-27 10:14:12
--- Safe.sol 2023-09-27 13:31:45
+++ Safe.sol 2023-09-27 14:34:22
@@ -76,7 +76,7 @@
* so we create a Safe with 0 owners and threshold 1.
* This is an unusable Safe, perfect for the singleton
Expand Down Expand Up @@ -31,7 +31,7 @@ diff -druN Safe.sol Safe.sol
// setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
setupOwners(_owners, _threshold);
if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
@@ -349,7 +351,6 @@
@@ -362,7 +364,6 @@
lastOwner = currentOwner;
}
}
Expand Down
4 changes: 2 additions & 2 deletions certora/harnesses/SafeHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ contract SafeHarness is Safe {
return getOwners().length;
}

function call_keccak256(bytes memory b) external view returns (bytes32) {
return keccak256(b);
function callKeccak256(bytes memory data) external view returns (bytes32) {
return keccak256(data);
}
}
3 changes: 1 addition & 2 deletions certora/scripts/verifySafe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ certoraRun certora/harnesses/SafeHarness.sol \
--solc solc7.6 \
--optimistic_loop \
--prover_args '-optimisticFallback true -mediumTimeout 30' \
--loop_iter 2 \
--rule checkSignatures \
--loop_iter 3 \
--optimistic_hashing \
--hashing_length_bound 352 \
--rule_sanity \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ if [[ -n "$CI" ]]; then
fi

certoraRun certora/harnesses/SafeHarness.sol \
--verify SafeHarness:certora/specs/SafeSummarize.spec \
--verify SafeHarness:certora/specs/Signatures.spec \
--solc solc7.6 \
--optimistic_loop \
--prover_args '-optimisticFallback true -s z3 -copyLoopUnroll 5 -mediumTimeout 5 -depth 30' \
--loop_iter 2 \
--rule ownerSignaturesAreProvidedForExecTransaction \
--prover_args '-optimisticFallback true -mediumTimeout 30' \
--loop_iter 3 \
--optimistic_hashing \
--hashing_length_bound 352 \
--rule_sanity \
"${params[@]}" \
--msg "Safe $1 "
--msg "Safe $*" \
"$@"
105 changes: 1 addition & 104 deletions certora/specs/Safe.spec
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,21 @@ methods {
function getThreshold() external returns (uint256) envfree;
function disableModule(address,address) external;
function nonce() external returns (uint256) envfree;
function isOwner(address) external returns (bool) envfree;
function signedMessages(bytes32) external returns (uint256) envfree;
function signatureSplitPublic(bytes,uint256) external returns (uint8,bytes32,bytes32) envfree;
function getTransactionHash(address,uint256,bytes,Enum.Operation,uint256,uint256,uint256,address,address,uint256) external returns (bytes32) envfree;

// harnessed
function getModule(address) external returns (address) envfree;
function getSafeGuard() external returns (address) envfree;
function getNativeTokenBalance() external returns (uint256) envfree;
function getOwnersCount() external returns (uint256) envfree;
function getOwnersCountFromArray() external returns (uint256) envfree;
function getCurrentOwner(bytes32, uint8, bytes32, bytes32) external returns (address) envfree;
// summaries
function SignatureDecoder.signatureSplit(bytes memory signatures, uint256 pos) internal returns (uint8,bytes32,bytes32) => mySignatureSplit(signatures,pos);
function Safe.checkExternalSignature(address, bytes32, bytes memory) internal => NONDET;
// optional
function checkSignatures(bytes32,bytes,bytes) external envfree;
function execTransactionFromModuleReturnData(address,uint256,bytes,Enum.Operation) external returns (bool, bytes memory);
function execTransactionFromModule(address,uint256,bytes,Enum.Operation) external returns (bool);
function execTransaction(address,uint256,bytes,Enum.Operation,uint256,uint256,uint256,address,address,bytes) external returns (bool);
function _.isValidSignature(bytes32, bytes) external => NONDET;
}

definition noHavoc(method f) returns bool =
f.selector != sig:execTransactionFromModuleReturnData(address,uint256,bytes,Enum.Operation).selector
&& f.selector != sig:execTransactionFromModule(address,uint256,bytes,Enum.Operation).selector
&& f.selector != sig:execTransaction(address,uint256,bytes,Enum.Operation,uint256,uint256,uint256,address,address,bytes).selector;

definition reachableOnly(method f) returns bool =
f.selector != sig:setup(address[],uint256,address,bytes,address,address,uint256,address).selector
&& f.selector != sig:simulateAndRevert(address,bytes).selector
Expand All @@ -40,15 +25,7 @@ definition reachableOnly(method f) returns bool =
// "If it’s called from an internal context it is fine but as a public function that can be called with any argument it cannot have hooks applied on."
&& f.selector != sig:getStorageAt(uint256,uint256).selector;

definition MAX_UINT256() returns uint256 = 0xffffffffffffffffffffffffffffffff;

ghost mapping(bytes => mapping(uint256 => uint8)) mySigSplitV;
ghost mapping(bytes => mapping(uint256 => bytes32)) mySigSplitR;
ghost mapping(bytes => mapping(uint256 => bytes32)) mySigSplitS;

function mySignatureSplit(bytes signatures, uint256 pos) returns (uint8,bytes32,bytes32) {
return (mySigSplitV[signatures][pos], mySigSplitR[signatures][pos], mySigSplitS[signatures][pos]);
}
definition MAX_UINT256() returns uint256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

/// Nonce must never decrease
rule nonceMonotonicity(method f) filtered {
Expand Down Expand Up @@ -151,86 +128,6 @@ rule nativeTokenBalanceSpending(method f) filtered {
|| f.selector == sig:execTransactionFromModuleReturnData(address,uint256,bytes,Enum.Operation).selector;
}

// checkSignatures called once is equivalent to checkSignatures called twice
rule checkSignatures() {
bytes32 dataHash;
bytes data;
address executor;
env e;
bytes signaturesAB;
bytes signaturesA;
bytes signaturesB;
uint8 vA; bytes32 rA; bytes32 sA;
uint8 vB; bytes32 rB; bytes32 sB;
uint8 vAB1; bytes32 rAB1; bytes32 sAB1;
uint8 vAB2; bytes32 rAB2; bytes32 sAB2;
vA, rA, sA = signatureSplitPublic(signaturesA, 0);
vB, rB, sB = signatureSplitPublic(signaturesB, 0);
vAB1, rAB1, sAB1 = signatureSplitPublic(signaturesAB, 0);
vAB2, rAB2, sAB2 = signatureSplitPublic(signaturesAB, 1);
require to_mathint(signaturesAB.length) == signaturesA.length + signaturesB.length;

require vA == vAB1 && rA == rAB1 && sA == sAB1;
require vB == vAB2 && rB == rAB2 && sB == sAB2;
require vA != 0 && vB != 0;
require data.length < 1000;
require signaturesA.length < 1000;
require signaturesB.length < 1000;
require signaturesAB.length < 1000;
require signaturesA.length >= 65;
require signaturesB.length >= 65;
require signaturesAB.length >= 130;
require !isOwner(currentContract);
require getThreshold() <= 2;
require getCurrentOwner(dataHash, vA, rA, sA) < getCurrentOwner(dataHash, vB, rB, sB);

checkNSignatures@withrevert(e, executor, dataHash, data, signaturesA, 1);
bool successA = !lastReverted;
checkNSignatures@withrevert(e, executor, dataHash, data, signaturesB, 1);
bool successB = !lastReverted;

checkNSignatures@withrevert(e, executor, dataHash, data, signaturesAB, 2);
bool successAB = !lastReverted;

assert (successA && successB) <=> successAB, "checkSignatures called must be equivalent to checkSignatures called twice";
}

rule ownerSignaturesAreProvidedForExecTransaction(
address to,
uint256 value,
bytes data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
bytes signatures
) {
uint256 nonce = nonce();
bytes32 transactionHash = getTransactionHash(
to,
value,
data,
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
nonce
);

bytes encodedTransactionData;
checkSignatures@withrevert(transactionHash, encodedTransactionData, signatures);
bool checkSignaturesOk = !lastReverted;

env e;
execTransaction(e, to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, signatures);

assert checkSignaturesOk, "transaction executed without valid signatures";
}


rule nativeTokenBalanceSpendingExecTransaction(
address to,
Expand Down
86 changes: 0 additions & 86 deletions certora/specs/SafeSummarize.spec

This file was deleted.

0 comments on commit bc08fb0

Please sign in to comment.