Skip to content

Commit f7ead96

Browse files
author
Ubuntu
committed
SOV-5187 - echidna designing for tests - wip
1 parent 72bee93 commit f7ead96

File tree

6 files changed

+127
-2
lines changed

6 files changed

+127
-2
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,6 @@ cache/
5252

5353
# Security subproject vendored libs
5454
/security/foundry/lib/
55+
56+
# ignore additional echidna generated files through foundry
57+
/security/foundry/crytic-export/
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Simple helper to print the EIP-55 checksum version of an address.
5+
*
6+
* Usage:
7+
* node checksum_address.js 0xdeadbeef...
8+
*/
9+
10+
const Web3 = require('web3');
11+
12+
const [, , input] = process.argv;
13+
14+
if (!input) {
15+
console.error('Usage: node checksum_address.js <address>');
16+
process.exit(1);
17+
}
18+
19+
try {
20+
const normalized = input.startsWith('0x') ? input : `0x${input}`;
21+
const checksummed = Web3.utils.toChecksumAddress(normalized);
22+
console.log(checksummed);
23+
} catch (err) {
24+
console.error(`Error: ${err.message}`);
25+
process.exit(1);
26+
}

security/foundry/echidna.yaml

Lines changed: 21 additions & 0 deletions
Large diffs are not rendered by default.

security/foundry/src/test

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.24;
3+
4+
// minimalistic harness for fuzzing with Echidna over RBTCWrapperProxy.
5+
// NOTE: STILL not run; in the next step we will load the bytecode
6+
// of the proxy at a fixed address via echidna.yaml (deployBytecodes).
7+
8+
contract RBTCWrapperProxyHarness {
9+
// Fixed address where we will "mount" the runtime of the proxy (next step).
10+
address constant PROXY_ADDR = 0xBEEF00000000000000000000000000000000BEEf;
11+
12+
// ---------- Secure reading utilities (do not break if no code) ----------
13+
function _getRegistry() internal view returns (address reg, bool ok) {
14+
bytes memory ret;
15+
(ok, ret) = PROXY_ADDR.staticcall(
16+
abi.encodeWithSignature("registry()")
17+
);
18+
if (ok && ret.length == 32) reg = abi.decode(ret, (address));
19+
}
20+
21+
function _getOnlyOwnerFlag() internal view returns (bool flag, bool ok) {
22+
bytes memory ret;
23+
(ok, ret) = PROXY_ADDR.staticcall(
24+
abi.encodeWithSignature("onlyOwnerCanUpdateRegistry()")
25+
);
26+
if (ok && ret.length == 32) flag = abi.decode(ret, (bool));
27+
}
28+
29+
// ---------- Actions that Echidna can combine in sequences ----------
30+
function act_updateRegistry() external {
31+
// Read state before
32+
(address beforeReg, ) = _getRegistry();
33+
34+
// Call low-level (no reverts if no code; simply ok=false or ret empty)
35+
(bool ok, ) = PROXY_ADDR.call(
36+
abi.encodeWithSignature("updateRegistry()")
37+
);
38+
39+
// Read state after
40+
(address afterReg, ) = _getRegistry();
41+
42+
// If the call succeeded, the registry must change to a non-zero value;
43+
// otherwise it should remain as before.
44+
if (ok) {
45+
require(afterReg != address(0), "registry set to zero");
46+
require(afterReg != beforeReg, "registry unchanged after success");
47+
} else {
48+
require(afterReg == beforeReg, "registry changed on failed call");
49+
}
50+
}
51+
52+
function act_restrictRegistryUpdate(bool flag) external {
53+
(bool beforeFlag, ) = _getOnlyOwnerFlag();
54+
55+
(bool ok, ) = PROXY_ADDR.call(
56+
abi.encodeWithSignature("restrictRegistryUpdate(bool)", flag)
57+
);
58+
59+
(bool afterFlag, ) = _getOnlyOwnerFlag();
60+
61+
// If the call succeeded, the flag should remain as requested; if not, it should remain unchanged.
62+
if (ok) {
63+
require(afterFlag == flag, "flag not set after success");
64+
} else {
65+
require(afterFlag == beforeFlag, "flag changed on failed call");
66+
}
67+
}
68+
69+
// ---------- Minimum property required by Echidna ----------
70+
// Properties are functions without arguments that return bool and start with "echidna".
71+
function echidna_dummy_property() public pure returns (bool) {
72+
return true;
73+
}
74+
}

security/foundry/tests/invariant/RBTCWrapperProxy.t.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ contract RBTCWrapperProxy_Handler {
1919
ok; // ignore result; invariants decide pass/fail
2020
}
2121

22-
function call_updateRegistry(address newReg) external {
23-
(bool ok,) = proxy.call(abi.encodeWithSignature("updateRegistry(address)", newReg));
22+
function call_updateRegistry() external {
23+
(bool ok,) = proxy.call(abi.encodeWithSignature("updateRegistry()"));
2424
ok;
2525
}
2626

0 commit comments

Comments
 (0)