From e9b29d1032fd4afe25b4b326c37edca7712aaac2 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 22 Dec 2025 19:42:13 +0530 Subject: [PATCH 1/2] fix: add nonce to sign --- .../protocol/switchboard/EVMxSwitchboard.sol | 16 +++++-- .../switchboard/MessageSwitchboard.sol | 20 ++++++--- .../switchboard/EVMxSwitchboard.t.sol | 43 +++++++++++++++---- .../switchboard/MessageSwitchboard.t.sol | 25 ++++++++--- 4 files changed, 81 insertions(+), 23 deletions(-) diff --git a/contracts/protocol/switchboard/EVMxSwitchboard.sol b/contracts/protocol/switchboard/EVMxSwitchboard.sol index a51f1f49..084b8d25 100644 --- a/contracts/protocol/switchboard/EVMxSwitchboard.sol +++ b/contracts/protocol/switchboard/EVMxSwitchboard.sol @@ -253,14 +253,22 @@ contract EVMxSwitchboard is SwitchboardBase { bytes32 oldTransmitterBytes32 = toBytes32Format(oldTransmitter_); digestParams_.transmitter = oldTransmitterBytes32; bytes32 oldDigest = createDigest(digestParams_); - + if (payloadIdToDigest[digestParams_.payloadId] != oldDigest) revert InvalidDigest(); - + digestParams_.transmitter = toBytes32Format(newTransmitter_); bytes32 newDigest = createDigest(digestParams_); - + address watcher = _recoverSigner( - keccak256(abi.encodePacked(toBytes32Format(address(this)), chainSlug, oldDigest, newDigest)), + keccak256( + abi.encodePacked( + toBytes32Format(address(this)), + chainSlug, + nonce_, + oldDigest, + newDigest + ) + ), signature_ ); if (!_hasRole(WATCHER_ROLE, watcher)) revert WatcherNotFound(); diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index 86e9ef6f..827d8f85 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -135,7 +135,9 @@ contract MessageSwitchboard is SwitchboardBase, ReentrancyGuard { function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) public { // Recover watcher from signature address watcher = _recoverSigner( - keccak256(abi.encodePacked(toBytes32Format(address(this)), chainSlug, payloadId_, digest_)), + keccak256( + abi.encodePacked(toBytes32Format(address(this)), chainSlug, payloadId_, digest_) + ), proof_ ); @@ -687,14 +689,22 @@ contract MessageSwitchboard is SwitchboardBase, ReentrancyGuard { bytes32 oldTransmitterBytes32 = toBytes32Format(oldTransmitter_); digestParams_.transmitter = oldTransmitterBytes32; bytes32 oldDigest = createDigest(digestParams_); - + if (payloadIdToDigest[digestParams_.payloadId] != oldDigest) revert InvalidDigest(); - + digestParams_.transmitter = toBytes32Format(newTransmitter_); bytes32 newDigest = createDigest(digestParams_); - + address watcher = _recoverSigner( - keccak256(abi.encodePacked(toBytes32Format(address(this)), chainSlug, oldDigest, newDigest)), + keccak256( + abi.encodePacked( + toBytes32Format(address(this)), + chainSlug, + nonce_, + oldDigest, + newDigest + ) + ), signature_ ); if (!_hasRole(WATCHER_ROLE, watcher)) revert WatcherNotFound(); diff --git a/test/protocol/switchboard/EVMxSwitchboard.t.sol b/test/protocol/switchboard/EVMxSwitchboard.t.sol index 858abb1d..81c9e128 100644 --- a/test/protocol/switchboard/EVMxSwitchboard.t.sol +++ b/test/protocol/switchboard/EVMxSwitchboard.t.sol @@ -283,18 +283,25 @@ contract EVMxSwitchboardTestBase is Test, Utils { */ function _createAssignTransmitterSignature( DigestParams memory digestParams_, - address newTransmitter_ + address newTransmitter_, + uint256 nonce_ ) internal view returns (bytes memory signature) { // Create old digest with current transmitter (before modification) bytes32 oldDigest = createDigest(digestParams_); - + // Create new digest with new transmitter digestParams_.transmitter = toBytes32Format(newTransmitter_); bytes32 newDigest = createDigest(digestParams_); // Create signature digest with both old and new digests bytes32 signatureDigest = keccak256( - abi.encodePacked(toBytes32Format(address(evmxSwitchboard)), CHAIN_SLUG, oldDigest, newDigest) + abi.encodePacked( + toBytes32Format(address(evmxSwitchboard)), + CHAIN_SLUG, + nonce_, + oldDigest, + newDigest + ) ); signature = createSignature(signatureDigest, watcherPrivateKey); } @@ -1178,7 +1185,11 @@ contract SocketPayloadIdVerificationTest is EVMxSwitchboardTestBase { assertEq(oldDigest, storedDigest, "Old digest should match stored digest"); // Create signature for new transmitter - bytes memory signature = _createAssignTransmitterSignature(digestParams, newTransmitter); + bytes memory signature = _createAssignTransmitterSignature( + digestParams, + newTransmitter, + nonce + ); // Create new digest for verification digestParams.transmitter = toBytes32Format(newTransmitter); @@ -1228,7 +1239,11 @@ contract SocketPayloadIdVerificationTest is EVMxSwitchboardTestBase { ); // Create signature for new transmitter - bytes memory signature = _createAssignTransmitterSignature(digestParams, newTransmitter); + bytes memory signature = _createAssignTransmitterSignature( + digestParams, + newTransmitter, + nonce + ); // Should revert because old digest doesn't match stored digest vm.prank(getWatcherAddress()); @@ -1270,14 +1285,19 @@ contract SocketPayloadIdVerificationTest is EVMxSwitchboardTestBase { // Create old digest with old transmitter bytes32 oldDigest = createDigest(digestParams); - + // Create new digest with new transmitter digestParams.transmitter = toBytes32Format(newTransmitter); bytes32 newDigest = createDigest(digestParams); // Create signature digest with both old and new digests with non-watcher key bytes32 signatureDigest = keccak256( - abi.encodePacked(toBytes32Format(address(evmxSwitchboard)), CHAIN_SLUG, oldDigest, newDigest) + abi.encodePacked( + toBytes32Format(address(evmxSwitchboard)), + CHAIN_SLUG, + oldDigest, + newDigest + ) ); bytes memory signature = createSignature(signatureDigest, nonWatcherKey); @@ -1319,7 +1339,11 @@ contract SocketPayloadIdVerificationTest is EVMxSwitchboardTestBase { ); // Create signature for new transmitter - bytes memory signature = _createAssignTransmitterSignature(digestParams, newTransmitter); + bytes memory signature = _createAssignTransmitterSignature( + digestParams, + newTransmitter, + nonce + ); // First call succeeds vm.prank(getWatcherAddress()); @@ -1348,7 +1372,8 @@ contract SocketPayloadIdVerificationTest is EVMxSwitchboardTestBase { // Create signature for the new assignment bytes memory signature2 = _createAssignTransmitterSignature( updatedDigestParams, - anotherNewTransmitter + anotherNewTransmitter, + nonce ); vm.prank(getWatcherAddress()); diff --git a/test/protocol/switchboard/MessageSwitchboard.t.sol b/test/protocol/switchboard/MessageSwitchboard.t.sol index 7437580d..3c32dff8 100644 --- a/test/protocol/switchboard/MessageSwitchboard.t.sol +++ b/test/protocol/switchboard/MessageSwitchboard.t.sol @@ -517,7 +517,8 @@ contract MessageSwitchboardTest is Test, Utils { */ function _createAssignTransmitterSignature( DigestParams memory digestParams_, - address newTransmitter_ + address newTransmitter_, + uint256 nonce_ ) internal view returns (bytes memory signature) { // Create old digest with current transmitter (before modification) bytes32 oldDigest = createDigest(digestParams_); @@ -531,6 +532,7 @@ contract MessageSwitchboardTest is Test, Utils { abi.encodePacked( toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, + nonce_, oldDigest, newDigest ) @@ -2446,7 +2448,11 @@ contract MessageSwitchboardTest is Test, Utils { assertEq(oldDigest, storedDigest, "Old digest should match stored digest"); // Create signature for new transmitter - bytes memory signature = _createAssignTransmitterSignature(digestParams, newTransmitter); + bytes memory signature = _createAssignTransmitterSignature( + digestParams, + newTransmitter, + nonce + ); // Create new digest for verification digestParams.transmitter = toBytes32Format(newTransmitter); @@ -2496,7 +2502,11 @@ contract MessageSwitchboardTest is Test, Utils { ); // Create signature for new transmitter - bytes memory signature = _createAssignTransmitterSignature(digestParams, newTransmitter); + bytes memory signature = _createAssignTransmitterSignature( + digestParams, + newTransmitter, + nonce + ); // Should revert because old digest doesn't match stored digest vm.prank(getWatcherAddress()); @@ -2592,7 +2602,11 @@ contract MessageSwitchboardTest is Test, Utils { ); // Create signature for new transmitter - bytes memory signature = _createAssignTransmitterSignature(digestParams, newTransmitter); + bytes memory signature = _createAssignTransmitterSignature( + digestParams, + newTransmitter, + nonce + ); // First call succeeds vm.prank(getWatcherAddress()); @@ -2621,7 +2635,8 @@ contract MessageSwitchboardTest is Test, Utils { // Create signature for the new assignment bytes memory signature2 = _createAssignTransmitterSignature( updatedDigestParams, - anotherNewTransmitter + anotherNewTransmitter, + nonce ); vm.prank(getWatcherAddress()); From ffed58eb2f1dd1c211a8d61240859594787e172a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 23 Dec 2025 20:35:16 +0530 Subject: [PATCH 2/2] fix: rearrange nonce --- contracts/protocol/switchboard/EVMxSwitchboard.sol | 4 ++-- contracts/protocol/switchboard/MessageSwitchboard.sol | 4 ++-- test/protocol/switchboard/EVMxSwitchboard.t.sol | 4 ++-- test/protocol/switchboard/MessageSwitchboard.t.sol | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/protocol/switchboard/EVMxSwitchboard.sol b/contracts/protocol/switchboard/EVMxSwitchboard.sol index 084b8d25..65a39c6c 100644 --- a/contracts/protocol/switchboard/EVMxSwitchboard.sol +++ b/contracts/protocol/switchboard/EVMxSwitchboard.sol @@ -264,9 +264,9 @@ contract EVMxSwitchboard is SwitchboardBase { abi.encodePacked( toBytes32Format(address(this)), chainSlug, - nonce_, oldDigest, - newDigest + newDigest, + nonce_ ) ), signature_ diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index 827d8f85..17ce5f7d 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -700,9 +700,9 @@ contract MessageSwitchboard is SwitchboardBase, ReentrancyGuard { abi.encodePacked( toBytes32Format(address(this)), chainSlug, - nonce_, oldDigest, - newDigest + newDigest, + nonce_ ) ), signature_ diff --git a/test/protocol/switchboard/EVMxSwitchboard.t.sol b/test/protocol/switchboard/EVMxSwitchboard.t.sol index 81c9e128..0011b153 100644 --- a/test/protocol/switchboard/EVMxSwitchboard.t.sol +++ b/test/protocol/switchboard/EVMxSwitchboard.t.sol @@ -298,9 +298,9 @@ contract EVMxSwitchboardTestBase is Test, Utils { abi.encodePacked( toBytes32Format(address(evmxSwitchboard)), CHAIN_SLUG, - nonce_, oldDigest, - newDigest + newDigest, + nonce_ ) ); signature = createSignature(signatureDigest, watcherPrivateKey); diff --git a/test/protocol/switchboard/MessageSwitchboard.t.sol b/test/protocol/switchboard/MessageSwitchboard.t.sol index 3c32dff8..d90aa293 100644 --- a/test/protocol/switchboard/MessageSwitchboard.t.sol +++ b/test/protocol/switchboard/MessageSwitchboard.t.sol @@ -532,9 +532,9 @@ contract MessageSwitchboardTest is Test, Utils { abi.encodePacked( toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, - nonce_, oldDigest, - newDigest + newDigest, + nonce_ ) ); signature = createSignature(signatureDigest, watcherPrivateKey);