Skip to content

Commit 890f3e0

Browse files
authored
Merge branch 'main' into solana/governance-generate
2 parents 538222e + dcc9cb6 commit 890f3e0

File tree

16 files changed

+1026
-11
lines changed

16 files changed

+1026
-11
lines changed

.github/workflows/evm.yml

+29
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,32 @@ jobs:
4141
run: |
4242
make check-format
4343
id: check
44+
45+
echidna:
46+
name: echidna
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v4
50+
with:
51+
submodules: recursive
52+
53+
- name: Install Foundry
54+
uses: foundry-rs/foundry-toolchain@v1
55+
with:
56+
version: nightly
57+
58+
- name: Run Forge build
59+
run: |
60+
make test-push0
61+
id: build
62+
63+
- name: Install Echidna
64+
run: |
65+
curl -LO https://github.com/crytic/echidna/releases/download/v2.2.3/echidna-2.2.3-x86_64-linux.tar.gz
66+
tar -xzf echidna-2.2.3-x86_64-linux.tar.gz
67+
pip install crytic-compile
68+
69+
- name: Run Echidna
70+
run: |
71+
cd evm
72+
../echidna ./echidna --config echidna.yaml

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "lib/wormhole-solidity-sdk"]
88
path = evm/lib/wormhole-solidity-sdk
99
url = https://github.com/wormhole-foundation/wormhole-solidity-sdk
10+
[submodule "evm/lib/solidity-bytes-utils"]
11+
path = evm/lib/solidity-bytes-utils
12+
url = https://github.com/GNSPS/solidity-bytes-utils

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ test-evm:
3636

3737
# Verify that the contracts do not include PUSH0 opcodes
3838
test-push0:
39-
forge build --extra-output evm.bytecode.opcodes
40-
@if grep -qr --include \*.json PUSH0 ./out; then echo "Contract uses PUSH0 instruction" 1>&2; exit 1; else echo "PUSH0 Verification Succeeded"; fi
39+
cd evm && forge build --extra-output evm.bytecode.opcodes
40+
@if grep -qr --include \*.json PUSH0 ./evm/out; then echo "Contract uses PUSH0 instruction" 1>&2; exit 1; else echo "PUSH0 Verification Succeeded"; fi

evm/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Similarly, transfers that are rate-limited on the destination chain are added to
3030

3131
If the client attempts to release the transfer from the queue before the expiry of the `rateLimitDuration`, the contract reverts with a `InboundQueuedTransferStillQueued` error.
3232

33+
To disable the rate-limiter, set `_rateLimitDuration` to 0 and enable the `_skipRateLimiting` field in the `NttManager` constructor. Configuring this incorrectly will throw an error.
34+
If the rate-limiter is disabled, the inbound and outbound rate-limits can be set to 0.
35+
3336
_Events_
3437

3538
```solidity

evm/echidna.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
testMode: "assertion"
2+
3+
deployContracts: [
4+
["0x1f1", "TransceiverStructs"],
5+
]
6+
7+
cryticArgs: ["--compile-libraries=(TransceiverStructs,0x1f1)"]
8+
9+
# Default is 0x6000, but we need to increase this as not compiled via ir
10+
codeSize: 0x8000
11+
12+
stopOnFail: false
13+
14+
testLimit: 100000

evm/echidna/FuzzNttManager.sol

+835
Large diffs are not rendered by default.
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
pragma solidity >=0.8.8 <0.9.0;
2+
3+
import "./IHevm.sol";
4+
5+
abstract contract FuzzingHelpers {
6+
address constant HEVM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
7+
IHevm hevm = IHevm(HEVM_ADDRESS);
8+
9+
10+
event LogAddress(address);
11+
event LogUint256(uint256);
12+
event LogString(string);
13+
event AssertFail(string);
14+
15+
// We need this to receive refunds for quotes
16+
receive() external payable {}
17+
18+
function clampBetween(uint256 value, uint256 low, uint256 high) internal returns (uint256){
19+
if (value < low || value > high) {
20+
uint ans = low + (value % (high - low + 1));
21+
return ans;
22+
}
23+
return value;
24+
}
25+
26+
function extractErrorSelector(
27+
bytes memory revertData
28+
) internal returns (uint256) {
29+
if (revertData.length < 4) {
30+
emit LogString("Return data too short.");
31+
return 0;
32+
}
33+
34+
uint256 errorSelector = uint256(
35+
(uint256(uint8(revertData[0])) << 24) |
36+
(uint256(uint8(revertData[1])) << 16) |
37+
(uint256(uint8(revertData[2])) << 8) |
38+
uint256(uint8(revertData[3]))
39+
);
40+
41+
return errorSelector;
42+
}
43+
44+
function extractErrorString(bytes memory revertData) internal returns (bytes32) {
45+
if (revertData.length < 68) revert();
46+
assembly {
47+
revertData := add(revertData, 0x04)
48+
}
49+
return keccak256(abi.encodePacked(abi.decode(revertData, (string))));
50+
}
51+
52+
function selectorToUint(bytes4 selector) internal returns (uint256) {
53+
return uint256(uint32(selector));
54+
}
55+
56+
function assertWithMsg(bool b, string memory reason) internal {
57+
if (!b) {
58+
emit AssertFail(reason);
59+
assert(false);
60+
}
61+
}
62+
63+
function minUint8(uint8 a, uint8 b) internal returns (uint8) {
64+
return a < b ? a : b;
65+
}
66+
67+
function minUint256(uint256 a, uint256 b) internal returns (uint256) {
68+
return a < b ? a : b;
69+
}
70+
}

evm/echidna/helpers/IHevm.sol

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pragma solidity >=0.8.8 <0.9.0;
2+
3+
interface IHevm {
4+
function prank(address) external;
5+
6+
function warp(uint256 newTimestamp) external;
7+
}

evm/lib/solidity-bytes-utils

Submodule solidity-bytes-utils added at e0115c4

evm/src/NttManager/ManagerBase.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ abstract contract ManagerBase is
312312
_pause();
313313
}
314314

315-
function unpause() public onlyOwnerOrPauser {
315+
function unpause() public onlyOwner {
316316
_unpause();
317317
}
318318

evm/src/Transceiver/WormholeTransceiver/WormholeTransceiverState.sol

+4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ abstract contract WormholeTransceiverState is IWormholeTransceiverState, Transce
2626
IWormhole public immutable wormhole;
2727
IWormholeRelayer public immutable wormholeRelayer;
2828
ISpecialRelayer public immutable specialRelayer;
29+
/// @dev We don't check this in `_checkImmutables` since it's set at construction
30+
/// through `block.chainid`.
2931
uint256 immutable wormholeTransceiver_evmChainId;
32+
/// @dev We purposely avoid checking this in `_checkImmutables` to allow tweaking it
33+
/// without needing to allow modification of security critical immutables.
3034
uint256 public immutable gasLimit;
3135

3236
// ==================== Constants ================================================

evm/src/libraries/TrimmedAmount.sol

+8
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ library TrimmedAmountLib {
106106
return uint8(TrimmedAmount.unwrap(a) & 0xFF);
107107
}
108108

109+
/// @dev Set the decimals of the TrimmedAmount.
110+
/// This function should only be used for testing purposes, as it
111+
/// should not be necessary to change the decimals of a TrimmedAmount
112+
/// under normal circumstances.
113+
function setDecimals(TrimmedAmount a, uint8 decimals) internal pure returns (TrimmedAmount) {
114+
return TrimmedAmount.wrap((TrimmedAmount.unwrap(a) & ~uint72(0xFF)) | decimals);
115+
}
116+
109117
function isNull(TrimmedAmount a) internal pure returns (bool) {
110118
return (getAmount(a) == 0 && getDecimals(a) == 0);
111119
}

evm/test/NttManager.t.sol

+23
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,29 @@ contract TestNttManager is Test, IRateLimiterEvents {
231231
assertEq(nttManager.isPaused(), false);
232232
}
233233

234+
function test_pausePauserUnpauseOnlyOwner() public {
235+
// transfer pauser to another address
236+
address pauser = address(0x123);
237+
nttManager.transferPauserCapability(pauser);
238+
239+
// execute from pauser context
240+
vm.startPrank(pauser);
241+
assertEq(nttManager.isPaused(), false);
242+
nttManager.pause();
243+
assertEq(nttManager.isPaused(), true);
244+
245+
vm.expectRevert(
246+
abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, pauser)
247+
);
248+
nttManager.unpause();
249+
250+
// execute from owner context
251+
// ensures that owner can still unpause
252+
vm.startPrank(address(this));
253+
nttManager.unpause();
254+
assertEq(nttManager.isPaused(), false);
255+
}
256+
234257
// === deployment with invalid token
235258
function test_brokenToken() public {
236259
DummyToken t = new DummyTokenBroken();

evm/test/TrimmedAmount.t.sol

+10-5
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,19 @@ contract TrimmingTest is Test {
165165

166166
// ============= FUZZ TESTS ================== //
167167

168+
function testFuzz_setDecimals(TrimmedAmount a, uint8 decimals) public {
169+
TrimmedAmount b = a.setDecimals(decimals);
170+
assertEq(b.getDecimals(), decimals);
171+
}
172+
168173
function test_packUnpack(uint64 amount, uint8 decimals) public {
169174
TrimmedAmount trimmed = packTrimmedAmount(amount, decimals);
170175
assertEq(trimmed.getAmount(), amount);
171176
assertEq(trimmed.getDecimals(), decimals);
172177
}
173178

174179
function testFuzz_AddOperatorOverload(TrimmedAmount a, TrimmedAmount b) public {
175-
vm.assume(a.getDecimals() == b.getDecimals());
180+
a = a.setDecimals(b.getDecimals());
176181

177182
// check if the add operation reverts on an overflow.
178183
// if it overflows, discard the input
@@ -188,7 +193,7 @@ contract TrimmingTest is Test {
188193
}
189194

190195
function testFuzz_SubOperatorOverload(TrimmedAmount a, TrimmedAmount b) public {
191-
vm.assume(a.getDecimals() == b.getDecimals());
196+
a = a.setDecimals(b.getDecimals());
192197
vm.assume(a.getAmount() >= b.getAmount());
193198

194199
TrimmedAmount subAmt = a - b;
@@ -206,15 +211,15 @@ contract TrimmingTest is Test {
206211
}
207212

208213
function testFuzz_GtOperatorOverload(TrimmedAmount a, TrimmedAmount b) public {
209-
vm.assume(a.getDecimals() == b.getDecimals());
214+
a = a.setDecimals(b.getDecimals());
210215
bool isGt = a > b;
211216
bool expectedIsGt = gt(a, b);
212217

213218
assertEq(expectedIsGt, isGt);
214219
}
215220

216221
function testFuzz_LtOperatorOverload(TrimmedAmount a, TrimmedAmount b) public {
217-
vm.assume(a.getDecimals() == b.getDecimals());
222+
a = a.setDecimals(b.getDecimals());
218223
bool isLt = a > b;
219224
bool expectedIsLt = gt(a, b);
220225

@@ -224,7 +229,7 @@ contract TrimmingTest is Test {
224229
// invariant: forall (TrimmedAmount a, TrimmedAmount b)
225230
// a.saturatingAdd(b).amount <= type(uint64).max
226231
function testFuzz_saturatingAddDoesNotOverflow(TrimmedAmount a, TrimmedAmount b) public {
227-
vm.assume(a.getDecimals() == b.getDecimals());
232+
a = a.setDecimals(b.getDecimals());
228233

229234
TrimmedAmount c = a.saturatingAdd(b);
230235

sdk/__tests__/index.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async function registerRelayers() {
1515
try {
1616
await submitAccountantVAA(
1717
Buffer.from(
18-
"010000000001006c9967aee739944b30ffcc01653f2030ea02c038adda26a8f5a790f191134dff1e1e48368af121a34806806140d4f56ec09e25067006e69c95b0c4c08b8897990000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000001ce9cf010000000000000000000000000000000000576f726d686f6c6552656c61796572010000000200000000000000000000000053855d4b64e9a3cf59a84bc768ada716b5536bc5",
18+
"01000000000100a4f34c530ff196c060ff349f2bf7bcb16865771a7165ca84fb5e263f148a01b03592b9af46a410a3760f39097d7380e4e72b6e1da4fa25c2d7b2d00f102d0cae0100000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000001ce9cf010000000000000000000000000000000000576f726d686f6c6552656c617965720100000002000000000000000000000000cc680d088586c09c3e0e099a676fa4b6e42467b4",
1919
"hex"
2020
)
2121
);
@@ -26,7 +26,7 @@ async function registerRelayers() {
2626
try {
2727
await submitAccountantVAA(
2828
Buffer.from(
29-
"01000000000100894be2c33626547e665cee73684854fbd8fc2eb79ec9ad724b1fb10d6cd24aaa590393870e6655697cd69d5553881ac8519e1282e7d3ae5fc26d7452d097651c00000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000445fb0b010000000000000000000000000000000000576f726d686f6c6552656c61796572010000000400000000000000000000000053855d4b64e9a3cf59a84bc768ada716b5536bc5",
29+
"010000000001000fd839cfdbea0f43a35dbb8cc0219b55cd5ec9f59b7e4a7183dbeebd522f7c673c866a218bfa108d8c7606acb5fc6b94a7a4c3be06f10836c242afecdb80da6e00000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000445fb0b010000000000000000000000000000000000576f726d686f6c6552656c617965720100000004000000000000000000000000cc680d088586c09c3e0e099a676fa4b6e42467b4",
3030
"hex"
3131
)
3232
);

sdk/__tests__/utils.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,20 @@ export interface Ctx extends StartingCtx {
7676

7777
export const wh = new Wormhole(NETWORK, [evm.Platform, solana.Platform], {
7878
...(process.env["CI"]
79-
? {}
79+
? {
80+
chains: {
81+
Ethereum: {
82+
contracts: {
83+
relayer: "0xcC680D088586c09c3E0E099a676FA4b6e42467b4",
84+
},
85+
},
86+
Bsc: {
87+
contracts: {
88+
relayer: "0xcC680D088586c09c3E0E099a676FA4b6e42467b4",
89+
},
90+
},
91+
},
92+
}
8093
: {
8194
api: "http://localhost:7071",
8295
chains: {

0 commit comments

Comments
 (0)