diff --git a/FunctionSignatures.md b/FunctionSignatures.md index 6c60e480..721919c8 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -362,7 +362,7 @@ | `isAttested` | `0xc13c2396` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `processTrigger` | `0x7f3352bc` | +| `processPayload` | `0x7f3352bc` | | `registerSwitchboard` | `0x74f5b1fc` | | `renounceOwnership` | `0x715018a6` | | `requestOwnershipHandover` | `0x25692962` | @@ -389,7 +389,7 @@ | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `payloadCounter` | `0x550ce1d5` | -| `processTrigger` | `0x7f3352bc` | +| `processPayload` | `0x7f3352bc` | | `registerSibling` | `0x4f58b88c` | | `registerSwitchboard` | `0x74f5b1fc` | | `renounceOwnership` | `0x715018a6` | diff --git a/PAYLOAD_ID_ARCHITECTURE.md b/PAYLOAD_ID_ARCHITECTURE.md new file mode 100644 index 00000000..21e9a635 --- /dev/null +++ b/PAYLOAD_ID_ARCHITECTURE.md @@ -0,0 +1,144 @@ +# Payload ID Architecture - Unified Design + +## Overview +Unified payload ID structure for all three payload types: Write, Trigger, and Message. + +## Payload ID Structure + +### Bit Layout (256 bits total) +``` +[Origin: 64 bits][Verification: 64 bits][Pointer: 64 bits][Reserved: 64 bits] +``` + +Each component breakdown: +- **Origin (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)` +- **Verification (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)` +- **Pointer (64 bits)**: Counter value +- **Reserved (64 bits)**: For future extensibility + +## Payload Type Specifications + +### 1. Write Payloads (EVMX → On-chain) +- **Origin**: `evmxChainSlug (32) | watcherId (32)` + - Generated by: Watcher (on EVMX) + - Verified by: Watcher offchain (links source) +- **Verification**: `dstChainSlug (32) | dstSwitchboardId (32)` + - Generated by: Watcher (from config) + - Used by: Socket for routing +- **Pointer**: `payloadCounter (64)` + - Generated by: Watcher (switchboard-specific counter) + +**Where Created**: `Watcher.sol` → `getCurrentPayloadId()` + +### 2. Trigger Payloads (On-chain → EVMX) +- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)` + - Generated by: FastSwitchboard + - Verified by: Watcher offchain (verifies source) +- **Verification**: `evmxChainSlug (32) | watcherId (32)` + - Generated by: FastSwitchboard (from stored config) + - Used by: Socket for routing +- **Pointer**: `switchboardCounter (64)` + - Generated by: FastSwitchboard (switchboard-specific counter) + +**Where Created**: `FastSwitchboard.sol` → `processPayload()` + +### 3. Message Payloads (Plug → Plug) +- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)` + - Generated by: MessageSwitchboard + - Verified by: Destination switchboard (checks source) +- **Verification**: `dstChainSlug (32) | dstSwitchboardId (32)` + - Generated by: MessageSwitchboard + - Used by: Socket for routing +- **Pointer**: `switchboardCounter (64)` + - Generated by: MessageSwitchboard (switchboard-specific counter) + +**Where Created**: `MessageSwitchboard.sol` → `_createDigestAndPayloadId()` + +## Decoding and Verification + +### Socket Verification (Destination) +1. Decode `payloadId` using `getVerificationInfo(payloadId)` +2. Extract `verificationChainSlug` and `verificationSwitchboardId` +3. Verify against local config: + - `verificationChainSlug == local chainSlug` + - `verificationSwitchboardId == local switchboardId` + +### Source Verification (Off-chain Watcher) +1. Decode `payloadId` using `getOriginInfo(payloadId)` +2. Extract `originChainSlug` and `originId` +3. Verify source configuration matches expected values + +### Payload Type Detection +- Check if `originChainSlug` or `verificationChainSlug` matches `evmxChainSlug` + - If `originChainSlug == evmxChainSlug`: **Write Payload** + - If `verificationChainSlug == evmxChainSlug`: **Trigger Payload** + - If neither: **Message Payload** + +## Implementation Details + +### IdUtils.sol Functions + +#### Encoding +- `createPayloadId(originChainSlug, originId, verificationChainSlug, verificationId, pointer)` + - Creates new payload ID with all components + +#### Decoding +- `decodePayloadId(payloadId)` - Full decode +- `getVerificationInfo(payloadId)` - Gets verification components (for Socket routing) +- `getOriginInfo(payloadId)` - Gets origin components (for source verification) + +### Required Updates + +1. **Watcher.sol** + - Update `getCurrentPayloadId()` to use new format + - Use `evmxSlug` as origin chain slug + - Use hardcoded `watcherId = 1` for now + - Get `dstSwitchboardId` from `switchboards` mapping + +2. **FastSwitchboard.sol** + - Add state variables: `evmxChainSlug`, `watcherId` (with onlyOwner setters) + - Implement `processPayload()` to create payload ID + - Add counter: `uint64 public triggerPayloadCounter` + - Use: `origin = (chainSlug, switchboardId)`, `verification = (evmxChainSlug, watcherId)` + +3. **MessageSwitchboard.sol** + - Update `_createDigestAndPayloadId()` to use new format + - Use: `origin = (chainSlug, switchboardId)`, `verification = (dstChainSlug, dstSwitchboardId)` + +4. **Socket.sol** + - Update `execute()` to decode payload ID and verify verification components + - Remove old `createPayloadId` usage + - Use `getVerificationInfo()` to extract routing info + +5. **SocketConfig.sol** + - Update `plugSwitchboardIds` type from `uint64` to `uint32` if needed (or keep uint64 and cast) + +## Security Considerations + +### Verification Flow +1. **Destination (Socket)**: Verifies verification component matches local config +2. **Source (Watcher offchain)**: Verifies origin component matches expected source +3. **Pointer verification**: Skipped for now (to be added later) + +### Counter Management +- Each switchboard maintains its own counter +- Prevents cross-switchboard collisions +- Counters are monotonic (never decrease) + +### ID Uniqueness +- Guaranteed by switchboard-specific counters +- Origin + Verification provide additional context +- Reserved bits allow future expansion without breaking changes + +## Migration Notes + +- No production deployments yet, so no migration needed +- All existing test code will need updates +- Backward compatibility not required + +## Future Enhancements + +- Add pointer verification mechanism +- Use reserved bits for additional metadata (payload version, flags, etc.) +- Support multiple watchers (remove hardcoded watcherId = 1) + diff --git a/contracts/evmx/interfaces/IConfigurations.sol b/contracts/evmx/interfaces/IConfigurations.sol index f2f95daf..bb45e082 100644 --- a/contracts/evmx/interfaces/IConfigurations.sol +++ b/contracts/evmx/interfaces/IConfigurations.sol @@ -26,7 +26,7 @@ interface IConfigurations { function getPlugConfigs( uint32 chainSlug_, bytes32 plug_ - ) external view returns (bytes32, uint64); + ) external view returns (bytes32, uint32); /// @notice Maps chain slug to their associated socket /// @param chainSlug_ The chain slug @@ -36,10 +36,10 @@ interface IConfigurations { /// @notice Returns the socket for a given chain slug /// @param chainSlug_ The chain slug /// @return The socket - function switchboards(uint32 chainSlug_, bytes32 sbType_) external view returns (uint64); + function switchboards(uint32 chainSlug_, bytes32 sbType_) external view returns (uint32); /// @notice Sets the switchboard for a network - function setSwitchboard(uint32 chainSlug_, bytes32 sbType_, uint64 switchboardId_) external; + function setSwitchboard(uint32 chainSlug_, bytes32 sbType_, uint32 switchboardId_) external; /// @notice Sets valid plugs for each chain slug /// @dev This function is used to verify if a plug deployed on a chain slug is valid connection to the app gateway diff --git a/contracts/evmx/interfaces/IWatcher.sol b/contracts/evmx/interfaces/IWatcher.sol index cf732c05..95cf217a 100644 --- a/contracts/evmx/interfaces/IWatcher.sol +++ b/contracts/evmx/interfaces/IWatcher.sol @@ -21,7 +21,7 @@ interface IWatcher is IConfigurations { function evmxSlug() external view returns (uint32); - function nextPayloadCount() external view returns (uint256); + function nextPayloadCount() external view returns (uint64); function currentPayloadId() external view returns (bytes32); diff --git a/contracts/evmx/plugs/FeesPlug.sol b/contracts/evmx/plugs/FeesPlug.sol index 8320db52..8a04f541 100644 --- a/contracts/evmx/plugs/FeesPlug.sol +++ b/contracts/evmx/plugs/FeesPlug.sol @@ -33,7 +33,6 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { constructor(address socket_, address owner_) { _setSocket(socket_); _initializeOwner(owner_); - isSocketInitialized = 1; } @@ -114,7 +113,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { function connectSocket( bytes32 appGatewayId_, address socket_, - uint64 switchboardId_ + uint32 switchboardId_ ) external onlyOwner { _connectSocket(appGatewayId_, socket_, switchboardId_); } diff --git a/contracts/evmx/watcher/Configurations.sol b/contracts/evmx/watcher/Configurations.sol index 6ea80ada..092eb7a5 100644 --- a/contracts/evmx/watcher/Configurations.sol +++ b/contracts/evmx/watcher/Configurations.sol @@ -22,7 +22,7 @@ abstract contract ConfigurationsStorage is IWatcher { // slot 51 /// @notice Maps chain slug to their associated switchboard /// @dev chainSlug => sb type => switchboard id - mapping(uint32 => mapping(bytes32 => uint64)) public switchboards; + mapping(uint32 => mapping(bytes32 => uint32)) public switchboards; // slot 52 /// @notice Maps chain slug to their associated socket @@ -57,7 +57,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol /// @param chainSlug The identifier of the network /// @param sbType The type of switchboard /// @param switchboardId The id of the switchboard - event SwitchboardSet(uint32 chainSlug, bytes32 sbType, uint64 switchboardId); + event SwitchboardSet(uint32 chainSlug, bytes32 sbType, uint32 switchboardId); /// @notice Emitted when socket is set for a network /// @param chainSlug The identifier of the network @@ -105,7 +105,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol function setSwitchboard( uint32 chainSlug_, bytes32 sbType_, - uint64 switchboardId_ + uint32 switchboardId_ ) external onlyOwner { switchboards[chainSlug_][sbType_] = switchboardId_; emit SwitchboardSet(chainSlug_, sbType_, switchboardId_); @@ -131,7 +131,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol function getPlugConfigs( uint32 chainSlug_, bytes32 plug_ - ) public view returns (bytes32, uint64) { + ) public view returns (bytes32, uint32) { return ( _plugConfigs[chainSlug_][plug_].appGatewayId, _plugConfigs[chainSlug_][plug_].switchboardId @@ -150,7 +150,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol address appGateway_, bytes32 switchboardType_ ) external view { - (bytes32 appGatewayId, uint64 switchboardId) = getPlugConfigs(chainSlug_, target_); + (bytes32 appGatewayId, uint32 switchboardId) = getPlugConfigs(chainSlug_, target_); if (appGatewayId != toBytes32Format(appGateway_)) revert InvalidGateway(); if (switchboardId != switchboards[chainSlug_][switchboardType_]) revert InvalidSwitchboard(); diff --git a/contracts/evmx/watcher/Watcher.sol b/contracts/evmx/watcher/Watcher.sol index 4630a74b..f8d7a50e 100644 --- a/contracts/evmx/watcher/Watcher.sol +++ b/contracts/evmx/watcher/Watcher.sol @@ -16,7 +16,7 @@ import "solady/utils/LibCall.sol"; contract Watcher is Initializable, Configurations { using LibCall for address; - uint256 public nextPayloadCount; + uint64 public nextPayloadCount; mapping(bytes32 => Payload) internal _payloads; mapping(bytes4 => IPrecompile) public precompiles; @@ -284,8 +284,16 @@ contract Watcher is Initializable, Configurations { uint32 chainSlug_, bytes32 switchboardType_ ) public view returns (bytes32) { - uint64 switchboardId = switchboards[chainSlug_][switchboardType_]; - return createPayloadId(nextPayloadCount, switchboardId, chainSlug_); + uint32 switchboardId = switchboards[chainSlug_][switchboardType_]; + // Write payload: origin = (evmxChainSlug, watcherId), verification = (dstChainSlug, dstSwitchboardId) + // watcherId hardcoded as 1 for now + return createPayloadId( + evmxSlug, // origin chain slug (evmx) + 1, // origin id (watcher id, hardcoded) + chainSlug_, // verification chain slug (destination) + switchboardId, // verification id (destination switchboard) + nextPayloadCount // pointer (counter) + ); } /// @notice Read a simple payload by id. diff --git a/contracts/protocol/Socket.sol b/contracts/protocol/Socket.sol index 2bb5147a..504e1f52 100644 --- a/contracts/protocol/Socket.sol +++ b/contracts/protocol/Socket.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import "./SocketUtils.sol"; import {WRITE} from "../utils/common/Constants.sol"; -import {createPayloadId} from "../utils/common/IdUtils.sol"; +import {getVerificationInfo} from "../utils/common/IdUtils.sol"; /** * @title Socket @@ -31,7 +31,10 @@ contract Socket is SocketUtils { error LowGasLimit(); /// @notice Thrown when the message value is insufficient error InsufficientMsgValue(); - + /// @notice Thrown when the verification chain slug is invalid + error InvalidVerificationChainSlug(); + /// @notice Thrown when the verification switchboard id is invalid + error InvalidVerificationSwitchboardId(); /** * @notice Constructor for the Socket contract * @param chainSlug_ The chain slug @@ -64,17 +67,21 @@ contract Socket is SocketUtils { if (executeParams_.callType != WRITE) revert InvalidCallType(); // check if the plug is connected - uint64 switchboardId = plugSwitchboardIds[executeParams_.target]; + uint32 switchboardId = plugSwitchboardIds[executeParams_.target]; // check if the message value is sufficient if (msg.value < executeParams_.value + transmissionParams_.socketFees) revert InsufficientMsgValue(); - bytes32 payloadId = createPayloadId( - executeParams_.payloadPointer, - switchboardId, - chainSlug - ); + // Get payloadId from executeParams + bytes32 payloadId = executeParams_.payloadId; + + // Verify payload ID matches destination + (uint32 verificationChainSlug, uint32 verificationSwitchboardId) = getVerificationInfo(payloadId); + if (verificationChainSlug != chainSlug) + revert InvalidVerificationChainSlug(); + if (verificationSwitchboardId != uint32(switchboardId)) + revert InvalidVerificationSwitchboardId(); // validate the execution status _validateExecutionStatus(payloadId); @@ -93,12 +100,12 @@ contract Socket is SocketUtils { * @notice Verifies the digest of the payload * @param payloadId_ The id of the payload * @param switchboardId_ The id of the switchboard - * @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit) + * @param executeParams_ The execution parameters (appGatewayId, value, payloadId, callType, gasLimit) * @param transmitterProof_ The transmitter proof */ function _verify( bytes32 payloadId_, - uint64 switchboardId_, + uint32 switchboardId_, ExecuteParams calldata executeParams_, bytes calldata transmitterProof_ ) internal { @@ -129,7 +136,7 @@ contract Socket is SocketUtils { /** * @notice Executes the payload * @param payloadId_ The id of the payload - * @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit) + * @param executeParams_ The execution parameters (appGatewayId, value, payloadId, callType, gasLimit) * @param transmissionParams_ The transmission parameters (socketFees, transmitterProof, refundAddress) */ function _execute( @@ -185,52 +192,43 @@ contract Socket is SocketUtils { } //////////////////////////////////////////////////////// - ////////////////////// Trigger ////////////////////// + ////////////////////// Outbound Payloads ////////////////////// //////////////////////////////////////////////////////// /** - * @notice To trigger to a connected remote chain. Should only be called by a plug. - * @param data_ The data to trigger the app gateway - * @return triggerId The id of the trigger + * @notice Sends a payload to a connected remote chain (used for both triggers and messages) + * @dev Should only be called by a plug. The switchboard will create the payload ID. + * @param data_ The payload data + * @return payloadId The created payload ID */ - function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId) { - triggerId = _triggerAppGateway(msg.sender, msg.value, data_); + function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId) { + payloadId = _sendPayload(msg.sender, msg.value, data_); } /** - * @notice To trigger to a connected remote chain. Should only be called by a plug. + * @notice Internal function to send a payload to a connected remote chain * @param plug_ The address of the plug - * @param value_ The value to trigger the app gateway - * @param data_ The data to trigger the app gateway - * @return triggerId The id of the trigger + * @param value_ The value to send with the payload + * @param data_ The payload data + * @return payloadId The created payload ID from the switchboard */ - function _triggerAppGateway( + function _sendPayload( address plug_, uint256 value_, bytes calldata data_ - ) internal returns (bytes32 triggerId) { - (uint64 switchboardId, address switchboardAddress) = _verifyPlugSwitchboard(plug_); + ) internal returns (bytes32 payloadId) { + (uint32 switchboardId, address switchboardAddress) = _verifyPlugSwitchboard(plug_); bytes memory plugOverrides = IPlug(plug_).overrides(); - triggerId = _encodeTriggerId(); - // todo: need gas limit? - ISwitchboard(switchboardAddress).processTrigger{value: value_}( + // Switchboard creates the payload ID and emits PayloadRequested event + payloadId = ISwitchboard(switchboardAddress).processPayload{value: value_}( plug_, - triggerId, data_, plugOverrides ); - - emit AppGatewayCallRequested( - triggerId, - bytes32(0), // TODO: clean this up - switchboardId, - toBytes32Format(plug_), - plugOverrides, - data_ - ); } + /** * @notice Increase fees for a pending payload * @param payloadId_ The payload ID to increase fees for @@ -248,7 +246,7 @@ contract Socket is SocketUtils { ); } - function _verifyPlugSwitchboard(address plug_) internal view returns (uint64 switchboardId, address switchboardAddress) { + function _verifyPlugSwitchboard(address plug_) internal view returns (uint32 switchboardId, address switchboardAddress) { switchboardId = plugSwitchboardIds[plug_]; if (switchboardId == 0) revert PlugNotFound(); if (isValidSwitchboard[switchboardId] != SwitchboardStatus.REGISTERED) @@ -256,13 +254,13 @@ contract Socket is SocketUtils { switchboardAddress = switchboardAddresses[switchboardId]; } /** - * @notice Fallback function that forwards all calls to Socket's callAppGateway - * @dev The calldata is passed as-is to the gateways - * @return The trigger id + * @notice Fallback function that forwards all calls to Socket's sendPayload + * @dev The calldata is passed as-is to the switchboard + * @return The payload ID */ fallback(bytes calldata) external payable returns (bytes memory) { - // return the trigger id - return abi.encode(_triggerAppGateway(msg.sender, msg.value, msg.data)); + // return the payload ID + return abi.encode(_sendPayload(msg.sender, msg.value, msg.data)); } /** diff --git a/contracts/protocol/SocketBatcher.sol b/contracts/protocol/SocketBatcher.sol index 7fc43529..6e8e3f20 100644 --- a/contracts/protocol/SocketBatcher.sol +++ b/contracts/protocol/SocketBatcher.sol @@ -44,7 +44,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { */ function attestAndExecute( ExecuteParams calldata executeParams_, - uint64 switchboardId_, + uint32 switchboardId_, bytes32 digest_, bytes calldata proof_, bytes calldata transmitterProof_, @@ -74,7 +74,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { // function attestCCTPAndProveAndExecute( // CCTPExecutionParams calldata execParams_, // CCTPBatchParams calldata cctpParams_, - // uint64 switchboardId_ + // uint32 switchboardId_ // ) external payable returns (bool, bytes memory) { // address switchboard = socket__.switchboardAddresses(switchboardId_); // bytes32 payloadId = createPayloadId( diff --git a/contracts/protocol/SocketConfig.sol b/contracts/protocol/SocketConfig.sol index 0401c792..ef63f6fc 100644 --- a/contracts/protocol/SocketConfig.sol +++ b/contracts/protocol/SocketConfig.sol @@ -22,22 +22,22 @@ abstract contract SocketConfig is ISocket, AccessControl { ISocketFeeManager public socketFeeManager; // @notice mapping of switchboard address to its status, helps socket to block invalid switchboards - mapping(uint64 => SwitchboardStatus) public isValidSwitchboard; + mapping(uint32 => SwitchboardStatus) public isValidSwitchboard; // @notice mapping of plug address to switchboard address - mapping(address => uint64) public plugSwitchboardIds; + mapping(address => uint32) public plugSwitchboardIds; // @notice max copy bytes for socket uint16 public maxCopyBytes = 2048; // 2KB // @notice counter for switchboard ids - uint64 public switchboardIdCounter = 1; + uint32 public switchboardIdCounter = 1; // @notice mapping of switchboard id to its address - mapping(uint64 => address) public switchboardAddresses; + mapping(uint32 => address) public switchboardAddresses; // @notice mapping of switchboard address to its id - mapping(address => uint64) public switchboardIds; + mapping(address => uint32) public switchboardIds; // @notice buffer to account for gas used by current contract execution uint256 public gasLimitBuffer; @@ -48,25 +48,25 @@ abstract contract SocketConfig is ISocket, AccessControl { error PlugNotConnected(); // @notice event triggered when a new switchboard is added - event SwitchboardAdded(address switchboard, uint64 switchboardId); + event SwitchboardAdded(address switchboard, uint32 switchboardId); // @notice event triggered when a switchboard is disabled - event SwitchboardDisabled(uint64 switchboardId); + event SwitchboardDisabled(uint32 switchboardId); // @notice event triggered when a switchboard is enabled - event SwitchboardEnabled(uint64 switchboardId); + event SwitchboardEnabled(uint32 switchboardId); // @notice event triggered when a socket fee manager is updated event SocketFeeManagerUpdated(address oldSocketFeeManager, address newSocketFeeManager); // @notice event triggered when the gas limit buffer is updated event GasLimitBufferUpdated(uint256 gasLimitBuffer); // @notice event triggered when the max copy bytes is updated event MaxCopyBytesUpdated(uint16 maxCopyBytes); - event PlugConfigUpdated(address plug, uint64 switchboardId, bytes configData); + event PlugConfigUpdated(address plug, uint32 switchboardId, bytes configData); /** * @notice Registers a switchboard on the socket * @dev This function is called by the switchboard to register itself on the socket * @dev This function will revert if the switchboard already exists * @return switchboardId The id of the switchboard */ - function registerSwitchboard() external returns (uint64 switchboardId) { + function registerSwitchboard() external returns (uint32 switchboardId) { switchboardId = switchboardIds[msg.sender]; if (switchboardId != 0) revert SwitchboardExists(); @@ -88,7 +88,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param switchboardId_ The id of the switchboard to disable */ function disableSwitchboard( - uint64 switchboardId_ + uint32 switchboardId_ ) external onlyRole(SWITCHBOARD_DISABLER_ROLE) { isValidSwitchboard[switchboardId_] = SwitchboardStatus.DISABLED; emit SwitchboardDisabled(switchboardId_); @@ -99,7 +99,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @dev This function is called by the governance role to enable a switchboard * @param switchboardId_ The id of the switchboard to enable */ - function enableSwitchboard(uint64 switchboardId_) external onlyRole(GOVERNANCE_ROLE) { + function enableSwitchboard(uint32 switchboardId_) external onlyRole(GOVERNANCE_ROLE) { isValidSwitchboard[switchboardId_] = SwitchboardStatus.REGISTERED; emit SwitchboardEnabled(switchboardId_); } @@ -120,7 +120,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param switchboardId_ The switchboard id * @param configData_ The configuration data for the switchboard */ - function connect(uint64 switchboardId_, bytes memory configData_) external override { + function connect(uint32 switchboardId_, bytes memory configData_) external override { if (switchboardId_ == 0 || isValidSwitchboard[switchboardId_] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); plugSwitchboardIds[msg.sender] = switchboardId_; @@ -136,7 +136,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param configData_ The configuration data for the switchboard */ function updatePlugConfig(bytes memory configData_) external { - uint64 switchboardId = plugSwitchboardIds[msg.sender]; + uint32 switchboardId = plugSwitchboardIds[msg.sender]; if (switchboardId == 0) revert PlugNotConnected(); ISwitchboard(switchboardAddresses[switchboardId]).updatePlugConfig(msg.sender,configData_); } @@ -180,14 +180,14 @@ abstract contract SocketConfig is ISocket, AccessControl { function getPlugConfig( address plugAddress_, bytes memory extraData_ - ) external view returns (uint64 switchboardId, bytes memory configData) { + ) external view returns (uint32 switchboardId, bytes memory configData) { switchboardId = plugSwitchboardIds[plugAddress_]; configData = ISwitchboard(switchboardAddresses[switchboardId]).getPlugConfig(plugAddress_, extraData_); } function getPlugSwitchboard( address plugAddress_ - ) external view returns (uint64 switchboardId, address switchboardAddress) { + ) external view returns (uint32 switchboardId, address switchboardAddress) { switchboardId = plugSwitchboardIds[plugAddress_]; switchboardAddress = switchboardAddresses[switchboardId]; } diff --git a/contracts/protocol/base/MessagePlugBase.sol b/contracts/protocol/base/MessagePlugBase.sol index 01adc49d..e2eeabf3 100644 --- a/contracts/protocol/base/MessagePlugBase.sol +++ b/contracts/protocol/base/MessagePlugBase.sol @@ -11,23 +11,13 @@ import {toBytes32Format} from "../../utils/common/Converters.sol"; /// Uses constant appGatewayId (0xaaaaa) for all chains abstract contract MessagePlugBase is PlugBase { address public switchboard; - uint64 public switchboardId; - uint256 public triggerPrefix; - error NotSupported(); + uint32 public switchboardId; - constructor(address socket_, uint64 switchboardId_) { + constructor(address socket_, uint32 switchboardId_) { _setSocket(socket_); switchboardId = switchboardId_; switchboard = socket__.switchboardAddresses(switchboardId_); - socket__.connect(switchboardId_, ""); - - triggerPrefix = (uint256(socket__.chainSlug()) << 224) | (uint256(uint160(socket_)) << 64); - } - - /// @notice Initializes the socket with the new protocol - function initSocket(bytes32, address, uint64) external override socketInitializer { - revert("Not Supported"); } /// @notice Registers a sibling plug for a specific chain @@ -43,13 +33,4 @@ abstract contract MessagePlugBase is PlugBase { registerSibling(chainSlugs_[i], siblingPlugs_[i]); } } - - function getNextTriggerId(uint32 chainSlug_) public view returns (bytes32) { - return - bytes32( - (uint256(chainSlug_) << 224) | - (uint256(uint160(address(socket__))) << 64) | - (uint256(socket__.triggerCounter()) << 16) - ); - } } diff --git a/contracts/protocol/base/PlugBase.sol b/contracts/protocol/base/PlugBase.sol index ef885077..105fa25a 100644 --- a/contracts/protocol/base/PlugBase.sol +++ b/contracts/protocol/base/PlugBase.sol @@ -45,7 +45,7 @@ abstract contract PlugBase is IPlug { function _connectSocket( bytes32 appGatewayId_, address socket_, - uint64 switchboardId_ + uint32 switchboardId_ ) internal { _setSocket(socket_); appGatewayId = appGatewayId_; @@ -82,7 +82,7 @@ abstract contract PlugBase is IPlug { function initSocket( bytes32 appGatewayId_, address socket_, - uint64 switchboardId_ + uint32 switchboardId_ ) external virtual socketInitializer { _connectSocket(appGatewayId_, socket_, switchboardId_); } diff --git a/contracts/protocol/interfaces/IPlug.sol b/contracts/protocol/interfaces/IPlug.sol index f67160a4..b5237d0f 100644 --- a/contracts/protocol/interfaces/IPlug.sol +++ b/contracts/protocol/interfaces/IPlug.sol @@ -10,7 +10,7 @@ interface IPlug { /// @param appGatewayId_ The app gateway id /// @param socket_ The socket address /// @param switchboardId_ The switchboard id - function initSocket(bytes32 appGatewayId_, address socket_, uint64 switchboardId_) external; + function initSocket(bytes32 appGatewayId_, address socket_, uint32 switchboardId_) external; /// @notice Gets the overrides /// @dev encoding format depends on the watcher system diff --git a/contracts/protocol/interfaces/ISocket.sol b/contracts/protocol/interfaces/ISocket.sol index 5df5e2e4..5432b7e3 100644 --- a/contracts/protocol/interfaces/ISocket.sol +++ b/contracts/protocol/interfaces/ISocket.sol @@ -30,7 +30,7 @@ interface ISocket { * @param configData The configuration data for the plug * @param switchboardId The outbound switchboard (select from registered options) */ - event PlugConnected(address plug, uint64 switchboardId, bytes configData); + event PlugConnected(address plug, uint32 switchboardId, bytes configData); /** * @notice emits the config set by a plug for a remoteChainSlug @@ -49,12 +49,28 @@ interface ISocket { event AppGatewayCallRequested( bytes32 triggerId, bytes32 appGatewayId, - uint64 switchboardId, + uint32 switchboardId, bytes32 plug, bytes overrides, bytes payload ); + /** + * @notice Event emitted when a payload is requested (for both triggers and messages) + * @param payloadId The created payload ID + * @param plug The source plug address + * @param switchboardId The switchboard ID processing the request + * @param overrides The override parameters + * @param payload The payload data + */ + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint32 indexed switchboardId, + bytes overrides, + bytes payload + ); + /** * @notice Executes a payload * @param executeParams_ The execution parameters @@ -72,7 +88,7 @@ interface ISocket { * @param switchboardId_ The switchboard id * @param configData_ The configuration data for the switchboard */ - function connect(uint64 switchboardId_, bytes memory configData_) external; + function connect(uint32 switchboardId_, bytes memory configData_) external; /** * @notice Updates plug configuration on switchboard @@ -89,7 +105,7 @@ interface ISocket { * @notice Registers a switchboard for the socket * @return switchboardId The id of the switchboard */ - function registerSwitchboard() external returns (uint64); + function registerSwitchboard() external returns (uint32); /** * @notice Returns the config for given `plugAddress_` and `siblingChainSlug_` @@ -101,7 +117,7 @@ interface ISocket { function getPlugConfig( address plugAddress_, bytes memory extraData_ - ) external view returns (uint64, bytes memory); + ) external view returns (uint32, bytes memory); /** * @notice Returns the execution status of a payload @@ -134,9 +150,9 @@ interface ISocket { * @param switchboardId_ The switchboard id * @return switchboardAddress The switchboard address */ - function switchboardAddresses(uint64 switchboardId_) external view returns (address); + function switchboardAddresses(uint32 switchboardId_) external view returns (address); - function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId); + function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId); function increaseFeesForPayload(bytes32 payloadId_, bytes calldata feesData_) external payable; } diff --git a/contracts/protocol/interfaces/ISocketBatcher.sol b/contracts/protocol/interfaces/ISocketBatcher.sol index f31782b1..f98d6546 100644 --- a/contracts/protocol/interfaces/ISocketBatcher.sol +++ b/contracts/protocol/interfaces/ISocketBatcher.sol @@ -20,7 +20,7 @@ interface ISocketBatcher { */ function attestAndExecute( ExecuteParams calldata executeParams_, - uint64 switchboardId_, + uint32 switchboardId_, bytes32 digest_, bytes calldata proof_, bytes calldata transmitterSignature_, diff --git a/contracts/protocol/interfaces/ISwitchboard.sol b/contracts/protocol/interfaces/ISwitchboard.sol index 24040195..baf1e84d 100644 --- a/contracts/protocol/interfaces/ISwitchboard.sol +++ b/contracts/protocol/interfaces/ISwitchboard.sol @@ -18,20 +18,19 @@ interface ISwitchboard { function allowPayload(bytes32 digest_, bytes32 payloadId_, address target_, bytes memory source_) external view returns (bool); /** - * @notice Processes a trigger and creates payload - * @dev This function is called by the socket to process a trigger + * @notice Processes a payload request and creates payload ID + * @dev This function is called by the socket to process a payload request * @dev sb can override this function to add additional logic - * @param triggerId_ Trigger ID from socket * @param plug_ Source plug address * @param payload_ Payload data - * @param overrides_ Overrides for the trigger + * @param overrides_ Overrides for the payload (e.g., destination chain, gas limit, fees) + * @return payloadId_ The created payload ID */ - function processTrigger( + function processPayload( address plug_, - bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable; + ) external payable returns (bytes32 payloadId_); /** * @notice Gets the transmitter for a given payload diff --git a/contracts/protocol/switchboard/FastSwitchboard.sol b/contracts/protocol/switchboard/FastSwitchboard.sol index cbb12a1c..9e1e7361 100644 --- a/contracts/protocol/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/switchboard/FastSwitchboard.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.21; import "./SwitchboardBase.sol"; import {WATCHER_ROLE} from "../../utils/common/AccessRoles.sol"; import {toBytes32Format} from "../../utils/common/Converters.sol"; +import {createPayloadId} from "../../utils/common/IdUtils.sol"; /** * @title FastSwitchboard contract @@ -17,19 +18,40 @@ contract FastSwitchboard is SwitchboardBase { // sibling mappings for outbound journey // chainSlug => address => siblingPlug mapping(address => bytes32) public plugAppGatewayIds; + + // EVMX configuration for trigger payloads + uint32 public evmxChainSlug; + uint32 public watcherId; + + // Counter for trigger payload IDs + uint64 public triggerPayloadCounter; // Error emitted when a payload is already attested by watcher. error AlreadyAttested(); // Error emitted when watcher is not valid error WatcherNotFound(); // Error emitted when source is invalid error InvalidSource(); + // Error emitted when EVMX config not set + error EvmxConfigNotSet(); // Event emitted when watcher attests a payload + event Attested(bytes32 digest, address watcher); /** * @notice Event emitted when plug configuration is updated */ event PlugConfigUpdated(address indexed plug, bytes32 appGatewayId); + // Event emitted when EVMX config is set + event EvmxConfigSet(uint32 evmxChainSlug, uint32 watcherId); + // Event emitted when payload is requested + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint32 indexed switchboardId, + bytes overrides, + bytes payload + ); + /** * @dev Constructor function for the FastSwitchboard contract * @param chainSlug_ Chain slug of the chain where the contract is deployed @@ -70,15 +92,51 @@ contract FastSwitchboard is SwitchboardBase { return isAttested[digest_]; } + /** + * @notice Set EVMX configuration for trigger payloads + * @param evmxChainSlug_ The EVMX chain slug + * @param watcherId_ The watcher ID (hardcoded as 1 for now) + */ + function setEvmxConfig(uint32 evmxChainSlug_, uint32 watcherId_) external onlyOwner { + evmxChainSlug = evmxChainSlug_; + watcherId = watcherId_; + emit EvmxConfigSet(evmxChainSlug_, watcherId_); + } + /** * @inheritdoc ISwitchboard + * @dev Creates a trigger payload ID with origin=(srcChainSlug, srcSwitchboardId), + * verification=(evmxChainSlug, watcherId) + * @return payloadId The created payload ID */ - function processTrigger( + function processPayload( address plug_, - bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable virtual {} + ) external payable override onlySocket returns (bytes32 payloadId) { + if (evmxChainSlug == 0 || watcherId == 0) revert EvmxConfigNotSet(); + + // Create trigger payload ID + // Origin: source chain and switchboard + // Verification: EVMX chain and watcher + // Pointer: switchboard counter + payloadId = createPayloadId( + chainSlug, // origin chain slug (source) + switchboardId, // origin id (source switchboard) + evmxChainSlug, // verification chain slug (evmx) + watcherId, // verification id (watcher id) + triggerPayloadCounter++ // pointer (counter) + ); + + // Emit PayloadRequested event + emit PayloadRequested( + payloadId, + plug_, + switchboardId, + overrides_, + payload_ + ); + } /** * @inheritdoc ISwitchboard @@ -87,7 +145,7 @@ contract FastSwitchboard is SwitchboardBase { bytes32 payloadId_, address, bytes calldata - ) external payable virtual {} + ) external payable override {} /** * @inheritdoc ISwitchboard diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index 1f18b1a8..bb061174 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import "./SwitchboardBase.sol"; import {WATCHER_ROLE, FEE_UPDATER_ROLE} from "../../utils/common/AccessRoles.sol"; import {toBytes32Format} from "../../utils/common/Converters.sol"; -import {createPayloadId} from "../../utils/common/IdUtils.sol"; +import {createPayloadId, getVerificationInfo} from "../../utils/common/IdUtils.sol"; import {DigestParams, MessageOverrides, PayloadFees, SponsoredPayloadFees} from "../../utils/common/Structs.sol"; import {WRITE } from "../../utils/common/Constants.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; @@ -21,13 +21,15 @@ contract MessageSwitchboard is SwitchboardBase { // sibling mappings for outbound journey // chainSlug => siblingSocket mapping(uint32 => bytes32) public siblingSockets; - // chainSlug => siblingSwitchboard + // chainSlug => siblingSwitchboard address (bytes32 format) mapping(uint32 => bytes32) public siblingSwitchboards; + // chainSlug => siblingSwitchboard ID + mapping(uint32 => uint32) public siblingSwitchboardIds; // chainSlug => address => siblingPlug mapping(uint32 => mapping(address => bytes32)) public siblingPlugs; // payload counter for generating unique payload IDs - uint40 public payloadCounter; + uint64 public payloadCounter; // minimum message value fees: chainSlug => minimum fee amount mapping(uint32 => uint256) public minMsgValueFees; @@ -113,6 +115,14 @@ contract MessageSwitchboard is SwitchboardBase { event MinMsgValueFeesSet(uint32 indexed chainSlug, uint256 minFees, address indexed updater); // Event emitted when sponsored fees are increased event SponsoredFeesIncreased(bytes32 indexed payloadId, uint256 newMaxFees, address indexed plug); + // Event emitted when payload is requested + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint32 indexed switchboardId, + bytes overrides, + bytes payload + ); /** * @dev Constructor function for the MessageSwitchboard contract @@ -135,10 +145,12 @@ contract MessageSwitchboard is SwitchboardBase { function setSiblingConfig( uint32 chainSlug_, bytes32 socket_, - bytes32 switchboard_ + bytes32 switchboard_, + uint32 switchboardId_ ) external onlyOwner { siblingSockets[chainSlug_] = socket_; siblingSwitchboards[chainSlug_] = switchboard_; + siblingSwitchboardIds[chainSlug_] = switchboardId_; emit SiblingConfigSet(chainSlug_, socket_, switchboard_); } @@ -146,30 +158,29 @@ contract MessageSwitchboard is SwitchboardBase { /** - * @dev Function to process trigger and create payload + * @dev Function to process payload request and create payload ID * @param plug_ Source plug address - * @param triggerId_ Trigger ID from socket * @param payload_ Payload data * @param overrides_ Override parameters including dstChainSlug and gasLimit + * @return payloadId The created payload ID */ - function processTrigger( + function processPayload( address plug_, - bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable override onlySocket { + ) external payable override onlySocket returns (bytes32 payloadId) { MessageOverrides memory overrides = _decodeOverrides(overrides_); _validateSibling(overrides.dstChainSlug, plug_); // Create digest and payload ID (common for both flows) - (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId) = _createDigestAndPayloadId( + (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId_) = _createDigestAndPayloadId( overrides.dstChainSlug, plug_, overrides.gasLimit, overrides.value, - triggerId_, payload_ ); + payloadId = payloadId_; if (overrides.isSponsored) { // Sponsored flow - check sponsor approval @@ -216,6 +227,15 @@ contract MessageSwitchboard is SwitchboardBase { address(0) // No sponsor for native flow ); } + + // Emit PayloadRequested event + emit PayloadRequested( + payloadId, + plug_, + switchboardId, + overrides_, + payload_ + ); } /** @@ -287,14 +307,20 @@ contract MessageSwitchboard is SwitchboardBase { address plug_, uint256 gasLimit_, uint256 value_, - bytes32 triggerId_, bytes calldata payload_ ) internal returns (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId) { - uint160 payloadPointer = (uint160(chainSlug) << 120) | - (uint160(uint64(uint256(triggerId_))) << 80) | - payloadCounter++; - - payloadId = createPayloadId(payloadPointer, switchboardId, dstChainSlug_); + // Get destination switchboard ID from sibling config + uint32 dstSwitchboardId = siblingSwitchboardIds[dstChainSlug_]; + if (dstSwitchboardId == 0) revert SiblingSocketNotFound(); + + // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) + payloadId = createPayloadId( + chainSlug, // origin chain slug (source) + switchboardId, // origin id (source switchboard) + dstChainSlug_, // verification chain slug (destination) + dstSwitchboardId, // verification id (destination switchboard) + payloadCounter++ // pointer (counter) + ); digestParams = DigestParams({ socket: siblingSockets[dstChainSlug_], @@ -307,8 +333,8 @@ contract MessageSwitchboard is SwitchboardBase { payload: payload_, target: siblingPlugs[dstChainSlug_][plug_], source: abi.encode(chainSlug, toBytes32Format(plug_)), - prevBatchDigestHash: triggerId_, - extraData:"0x" + prevBatchDigestHash: bytes32(0), // No longer using triggerId + extraData:bytes("") }); digest = _createDigest(digestParams); } diff --git a/contracts/protocol/switchboard/SwitchboardBase.sol b/contracts/protocol/switchboard/SwitchboardBase.sol index 00ecb9c7..8709726a 100644 --- a/contracts/protocol/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/switchboard/SwitchboardBase.sol @@ -18,7 +18,7 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { uint32 public immutable chainSlug; // switchboard id - uint64 public switchboardId; + uint32 public switchboardId; error NotSocket(); /** diff --git a/contracts/utils/common/IdUtils.sol b/contracts/utils/common/IdUtils.sol index 7423d329..64f45694 100644 --- a/contracts/utils/common/IdUtils.sol +++ b/contracts/utils/common/IdUtils.sol @@ -1,16 +1,69 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -/// @notice Creates a payload ID from the given parameters -/// @param payloadPointer_ The payload pointer -/// @param switchboardId_ The switchboard id -/// @param chainSlug_ The chain slug +/// @notice Payload ID structure: +/// [Origin: 64 bits][Verification: 64 bits][Pointer: 64 bits][Reserved: 64 bits] +/// Origin = chainSlug (32 bits) | switchboardId/watcherId (32 bits) +/// Verification = chainSlug (32 bits) | switchboardId/watcherId (32 bits) +/// Pointer = counter (64 bits) +/// Reserved = 64 bits for future use + +/// @notice Creates a payload ID from origin, verification, and pointer components +/// @param originChainSlug_ Chain slug for origin (32 bits) +/// @param originId_ Switchboard ID or watcher ID for origin (32 bits) +/// @param verificationChainSlug_ Chain slug for verification (32 bits) +/// @param verificationId_ Switchboard ID or watcher ID for verification (32 bits) +/// @param pointer_ Counter/pointer value (64 bits) /// @return The created payload ID function createPayloadId( - uint256 payloadPointer_, - uint64 switchboardId_, - uint32 chainSlug_ + uint32 originChainSlug_, + uint32 originId_, + uint32 verificationChainSlug_, + uint32 verificationId_, + uint64 pointer_ ) pure returns (bytes32) { - return - bytes32((uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | payloadPointer_); + uint256 origin = (uint256(originChainSlug_) << 32) | uint256(originId_); + uint256 verification = (uint256(verificationChainSlug_) << 32) | uint256(verificationId_); + return bytes32((origin << 192) | (verification << 128) | (uint256(pointer_) << 64)); +} + +/// @notice Decodes payload ID into its components +/// @param payloadId_ The payload ID to decode +/// @return originChainSlug Chain slug for origin +/// @return originId Switchboard ID or watcher ID for origin +/// @return verificationChainSlug Chain slug for verification +/// @return verificationId Switchboard ID or watcher ID for verification +/// @return pointer Counter/pointer value +function decodePayloadId( + bytes32 payloadId_ +) pure returns ( + uint32 originChainSlug, + uint32 originId, + uint32 verificationChainSlug, + uint32 verificationId, + uint64 pointer +) { + originChainSlug = uint32(uint256(payloadId_) >> 224); + originId = uint32(uint256(payloadId_) >> 192); + verificationChainSlug = uint32(uint256(payloadId_) >> 160); + verificationId = uint32(uint256(payloadId_) >> 128); + pointer = uint64(uint256(payloadId_) >> 64); +} + +/// @notice Gets verification chain slug and switchboard ID from payload ID +/// @param payloadId_ The payload ID to decode +/// @return chainSlug Verification chain slug +/// @return switchboardId Verification switchboard ID +function getVerificationInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, uint32 switchboardId) { + chainSlug = uint32(uint256(payloadId_) >> 160); + switchboardId = uint32(uint256(payloadId_) >> 128); +} + +/// @notice Gets origin chain slug and switchboard ID from payload ID +/// @param payloadId_ The payload ID to decode +/// @return chainSlug Origin chain slug +/// @return switchboardId Origin switchboard ID or watcher ID +function getOriginInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, uint32 switchboardId) { + chainSlug = uint32(uint256(payloadId_) >> 224); + switchboardId = uint32(uint256(payloadId_) >> 192); } diff --git a/contracts/utils/common/Structs.sol b/contracts/utils/common/Structs.sol index 0fc4f04b..dbdb202e 100644 --- a/contracts/utils/common/Structs.sol +++ b/contracts/utils/common/Structs.sol @@ -43,13 +43,13 @@ struct AppGatewayConfig { // Plug config: struct PlugConfigGeneric { bytes32 appGatewayId; - uint64 switchboardId; + uint32 switchboardId; } // Plug config: struct PlugConfigEvm { bytes32 appGatewayId; - uint64 switchboardId; + uint32 switchboardId; } //trigger: @@ -70,7 +70,7 @@ struct PromiseReturnData { // AM struct ExecuteParams { bytes4 callType; - uint160 payloadPointer; + bytes32 payloadId; uint256 deadline; uint256 gasLimit; uint256 value; diff --git a/deprecated/AuctionManager.sol b/deprecated/AuctionManager.sol index 712a207c..839c3a39 100644 --- a/deprecated/AuctionManager.sol +++ b/deprecated/AuctionManager.sol @@ -233,7 +233,7 @@ contract AuctionManager is AuctionManagerStorage, Initializable, AppGatewayBase, watcher__().requestHandler__().assignTransmitter( requestCount, - Bid({fee: 0, transmitter: address(0), extraData: ""}) + Bid({fee: 0, transmitter: address(0), extraData: bytes("")}) ); emit AuctionRestarted(requestCount); } diff --git a/deprecated/test/SetupTest.t.sol b/deprecated/test/SetupTest.t.sol index 7c808510..bb0e18ed 100644 --- a/deprecated/test/SetupTest.t.sol +++ b/deprecated/test/SetupTest.t.sol @@ -732,7 +732,7 @@ contract AuctionSetup is FeesSetup { vm.expectEmit(true, true, true, true); emit AuctionEnded( requestCount_, - Bid({fee: bidAmount, transmitter: transmitterEOA, extraData: ""}) + Bid({fee: bidAmount, transmitter: transmitterEOA, extraData: bytes("")}) ); // promiseResolver.resolvePromises(); diff --git a/deprecated/test/evmx/Watcher.t.sol b/deprecated/test/evmx/Watcher.t.sol index 90e563e8..c31ad456 100644 --- a/deprecated/test/evmx/Watcher.t.sol +++ b/deprecated/test/evmx/Watcher.t.sol @@ -381,7 +381,7 @@ contract WatcherTest is AppGatewayBaseSetup { function testRequestHandlerAssignTransmitter() public { uint40 requestCount = 0; appGateway.deployContracts(arbConfig.chainSlug); - Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: ""}); + Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: bytes("")}); hoax(nonOwner); vm.expectRevert(abi.encodeWithSelector(InvalidCaller.selector)); @@ -491,7 +491,7 @@ contract WatcherTest is AppGatewayBaseSetup { uint40[] memory batches = requestHandler.getRequestBatchIds(requestCount); bytes32[] memory payloadIds = requestHandler.getBatchPayloadIds(batches[0]); bytes32 payloadId = payloadIds[0]; - Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: ""}); + Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: bytes("")}); hoax(address(auctionManager)); requestHandler.assignTransmitter(requestCount, bid); diff --git a/deprecated/test/mock/MockFastSwitchboard.sol b/deprecated/test/mock/MockFastSwitchboard.sol index 6ed61324..72e3c669 100644 --- a/deprecated/test/mock/MockFastSwitchboard.sol +++ b/deprecated/test/mock/MockFastSwitchboard.sol @@ -45,7 +45,7 @@ contract MockFastSwitchboard is ISwitchboard { return switchboardId; } - function processTrigger( + function processPayload( address plug_, bytes32 triggerId_, bytes calldata payload_, diff --git a/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol b/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol index f27ab1fb..5f624033 100644 --- a/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol +++ b/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol @@ -21,7 +21,7 @@ contract FastSwitchboardExtended is FastSwitchboard { address owner_ ) FastSwitchboard(chainSlug_, socket_, owner_) {} - function processTrigger( + function processPayload( address plug_, bytes32 triggerId_, bytes calldata payload_, @@ -143,7 +143,7 @@ contract FastSwitchboardTest is AppGatewayBaseSetup { bytes("test payload"), bytes("test overrides") ); - fastSwitchboardExtended.processTrigger( + fastSwitchboardExtended.processPayload( address(0x123), bytes32(uint256(0x456)), bytes("test payload"), diff --git a/hardhat.config.ts b/hardhat.config.ts index 94168195..22286ea2 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -15,7 +15,7 @@ import type { } from "hardhat/types"; import { resolve } from "path"; import fs from "fs"; - +import { constants } from "ethers"; import { getJsonRpcUrl } from "./hardhat-scripts/utils/networks"; import { ChainId, @@ -37,7 +37,7 @@ const privateKey: HardhatNetworkAccountUserConfig = process.env function getChainConfig(chainSlug: ChainSlug): NetworkUserConfig { return { - accounts: [`0x${privateKey}`], + accounts: [`${constants.HashZero}`], chainId: ChainSlugToId[chainSlug], url: getJsonRpcUrl(chainSlug), }; diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 17edeaaa..59b2d842 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -132,12 +132,14 @@ contract DeploySetup is SetupStore { arbConfig.messageSwitchboard.setSiblingConfig( optChainSlug, toBytes32Format(address(optConfig.socket)), - toBytes32Format(address(optConfig.messageSwitchboard)) + toBytes32Format(address(optConfig.messageSwitchboard)), + uint32(optConfig.messageSwitchboard.switchboardId()) ); optConfig.messageSwitchboard.setSiblingConfig( arbChainSlug, toBytes32Format(address(arbConfig.socket)), - toBytes32Format(address(arbConfig.messageSwitchboard)) + toBytes32Format(address(arbConfig.messageSwitchboard)), + uint32(arbConfig.messageSwitchboard.switchboardId()) ); vm.stopPrank(); _connectCorePlugs(); @@ -562,7 +564,7 @@ contract WatcherSetup is FeesSetup { ( uint32 chainSlug, - uint64 switchboard, + uint32 switchboard, bytes32 digest, DigestParams memory digestParams ) = _validateAndGetDigest(payloadParams); @@ -581,7 +583,7 @@ contract WatcherSetup is FeesSetup { function _uploadProof( bytes32 payloadId, bytes32 digest, - uint64 switchboard, + uint32 switchboard, uint32 chainSlug ) internal returns (bytes memory proof) { address sbAddress = getSocketConfig(chainSlug).socket.switchboardAddresses(switchboard); @@ -605,7 +607,7 @@ contract WatcherSetup is FeesSetup { view returns ( uint32 chainSlug, - uint64 switchboard, + uint32 switchboard, bytes32 digest, DigestParams memory digestParams ) @@ -616,10 +618,10 @@ contract WatcherSetup is FeesSetup { , uint256 gasLimit, uint256 value, - uint64 switchboard_ + uint32 switchboard_ ) = abi.decode( payloadParams.precompileData, - (address, Transaction, WriteFinality, uint256, uint256, uint64) + (address, Transaction, WriteFinality, uint256, uint256, uint32) ); chainSlug = transaction.chainSlug; @@ -646,7 +648,7 @@ contract WatcherSetup is FeesSetup { function _executeWrite( uint32 chainSlug, - uint64 switchboard, + uint32 switchboard, bytes32 digest, DigestParams memory digestParams, Payload memory payloadParams, @@ -667,7 +669,7 @@ contract WatcherSetup is FeesSetup { value: digestParams.value, payload: digestParams.payload, target: fromBytes32Format(digestParams.target), - payloadPointer: uint160(payloadParams.payloadPointer), + payloadId: payloadParams.payloadId, prevBatchDigestHash: digestParams.prevBatchDigestHash, source: digestParams.source, extraData: digestParams.extraData @@ -830,19 +832,17 @@ contract MessageSwitchboardSetup is DeploySetup { SocketContracts memory srcSocketConfig_, SocketContracts memory dstSocketConfig_, bytes memory payload_ - ) internal view returns (uint160 payloadPointer, DigestParams memory digestParams) { - bytes32 triggerId = srcPlug_.getNextTriggerId(srcSocketConfig_.chainSlug); - uint40 payloadCounter = srcSocketConfig_.messageSwitchboard.payloadCounter(); - - payloadPointer = - (uint160(srcSocketConfig_.chainSlug) << 120) | - (uint160(uint64(uint256(triggerId))) << 80) | - payloadCounter; - - bytes32 payloadId = createPayloadId( - payloadPointer, - dstSocketConfig_.messageSwitchboard.switchboardId(), - dstSocketConfig_.chainSlug + ) internal view returns (bytes32 payloadId, DigestParams memory digestParams) { + uint64 payloadCounter = srcSocketConfig_.messageSwitchboard.payloadCounter(); + + // Calculate payload ID using new structure + // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) + payloadId = createPayloadId( + srcSocketConfig_.chainSlug, // origin chain slug + srcSocketConfig_.messageSwitchboard.switchboardId(), // origin switchboard id + dstSocketConfig_.chainSlug, // verification chain slug + dstSocketConfig_.messageSwitchboard.switchboardId(), // verification switchboard id + payloadCounter // pointer (counter) ); digestParams = _createDigestParams( @@ -851,17 +851,16 @@ contract MessageSwitchboardSetup is DeploySetup { address(dstPlug_), address(dstSocketConfig_.socket), payloadId, - triggerId, payload_ ); } function _executeOnDestination( DigestParams memory digestParams_, - uint160 payloadPointer_ + bytes32 payloadId_ ) internal { _attestPayload(digestParams_); - _execute(digestParams_, payloadPointer_); + _execute(digestParams_, payloadId_); } // Helper function to attest a payload @@ -884,10 +883,8 @@ contract MessageSwitchboardSetup is DeploySetup { address dstPlug_, address dstSocket_, bytes32 payloadId_, - bytes32 triggerId_, bytes memory payload_ ) internal view returns (DigestParams memory digestParams) { - bytes memory extraData = abi.encode(srcChainSlug_, toBytes32Format(srcPlug_)); digestParams = DigestParams({ socket: toBytes32Format(dstSocket_), transmitter: bytes32(0), @@ -899,8 +896,8 @@ contract MessageSwitchboardSetup is DeploySetup { payload: payload_, target: toBytes32Format(dstPlug_), source: abi.encode(srcChainSlug_, toBytes32Format(srcPlug_)), - prevBatchDigestHash: triggerId_, - extraData: extraData + prevBatchDigestHash: bytes32(0), // No longer using triggerId + extraData: bytes("") // Contract now sets extraData to empty }); } @@ -925,7 +922,7 @@ contract MessageSwitchboardSetup is DeploySetup { } // Helper function to execute on destination chain - function _execute(DigestParams memory digestParams_, uint160 payloadPointer_) internal { + function _execute(DigestParams memory digestParams_, bytes32 payloadId_) internal { // this is a signature for the socket batcher (only used for EVM) ExecuteParams memory executeParams = ExecuteParams({ callType: digestParams_.callType, @@ -934,7 +931,7 @@ contract MessageSwitchboardSetup is DeploySetup { value: digestParams_.value, payload: digestParams_.payload, target: fromBytes32Format(digestParams_.target), - payloadPointer: payloadPointer_, + payloadId: payloadId_, prevBatchDigestHash: digestParams_.prevBatchDigestHash, source: digestParams_.source, extraData: digestParams_.extraData diff --git a/test/SocketPayloadIdVerification.t.sol b/test/SocketPayloadIdVerification.t.sol new file mode 100644 index 00000000..19dec4a6 --- /dev/null +++ b/test/SocketPayloadIdVerification.t.sol @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import "forge-std/Test.sol"; +import "../contracts/protocol/Socket.sol"; +import "../contracts/protocol/switchboard/FastSwitchboard.sol"; +import "../contracts/protocol/switchboard/MessageSwitchboard.sol"; +import "../contracts/utils/common/IdUtils.sol"; +import "../contracts/utils/common/Structs.sol"; +import "../contracts/utils/common/Constants.sol"; +import "../contracts/utils/common/Converters.sol"; +import "./mocks/MockPlug.sol"; + +/** + * @title SocketPayloadIdVerificationTest + * @dev Tests for payload ID verification in Socket.execute() and FastSwitchboard payload creation + */ +contract SocketPayloadIdVerificationTest is Test { + // Event declarations + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint32 indexed switchboardId, + bytes overrides, + bytes payload + ); + // Test constants + uint32 constant CHAIN_SLUG = 1; + uint32 constant OTHER_CHAIN_SLUG = 2; + uint32 constant EVMX_CHAIN_SLUG = 100; + uint32 constant WATCHER_ID = 1; + + address owner = address(0x1000); + address plugOwner = address(0x2000); + + Socket socket; + FastSwitchboard fastSwitchboard; + MessageSwitchboard messageSwitchboard; + MockPlug mockPlug; + + function setUp() public { + // Deploy Socket + socket = new Socket(CHAIN_SLUG, owner, "1.0.0"); + + // Deploy switchboards + fastSwitchboard = new FastSwitchboard(CHAIN_SLUG, socket, owner); + messageSwitchboard = new MessageSwitchboard(CHAIN_SLUG, socket, owner); + + // Register switchboards + vm.startPrank(owner); + fastSwitchboard.registerSwitchboard(); + messageSwitchboard.registerSwitchboard(); + vm.stopPrank(); + + // Create a mock plug + uint32 switchboardId = fastSwitchboard.switchboardId(); + mockPlug = new MockPlug(address(socket), switchboardId); + + // Connect plug to socket + vm.prank(plugOwner); + mockPlug.connectToSocket(address(socket), switchboardId); + } + + // ============================================ + // TESTS - Socket.execute() Payload ID Verification + // ============================================ + + function test_Execute_VerifiesPayloadId_CorrectDestination() public { + // Create a valid payload ID for this chain and switchboard + uint32 switchboardId = fastSwitchboard.switchboardId(); + bytes32 payloadId = createPayloadId( + OTHER_CHAIN_SLUG, // origin chain slug + 100, // origin switchboard id + CHAIN_SLUG, // verification chain slug (matches socket) + uint32(switchboardId), // verification switchboard id (matches plug's switchboard) + 12345 // pointer + ); + + // Create ExecuteParams with valid payload ID + ExecuteParams memory executeParams = ExecuteParams({ + callType: WRITE, + payloadId: payloadId, + deadline: block.timestamp + 3600, + gasLimit: 100000, + value: 0, + payload: abi.encode("test"), + target: address(mockPlug), + prevBatchDigestHash: bytes32(0), + source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), + extraData: bytes("") + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + socketFees: 0, + refundAddress: address(0), + extraData: bytes(""), + transmitterProof: bytes("") + }); + + // Verify that payload ID check passes (doesn't revert with InvalidVerificationChainSlug or InvalidVerificationSwitchboardId) + // The execution should proceed past payload ID verification to the switchboard's allowPayload check. + // It will fail with InvalidSource because the source doesn't match the plug's appGatewayId. + // This confirms payload ID verification passed - we reached allowPayload which comes after payload ID check. + vm.expectRevert(FastSwitchboard.InvalidSource.selector); + socket.execute{value: 0}(executeParams, transmissionParams); + + // If we get InvalidSource, it means: + // 1. ✅ Payload ID verification passed (didn't revert with InvalidVerificationChainSlug/InvalidVerificationSwitchboardId) + // 2. ✅ We reached the switchboard's allowPayload check (comes after payload ID verification) + // 3. ✅ allowPayload failed with InvalidSource (expected, since source doesn't match plug config) + } + + function test_Execute_WrongChainSlug_Reverts() public { + // Create payload ID with wrong verification chain slug + uint32 switchboardId = fastSwitchboard.switchboardId(); + bytes32 payloadId = createPayloadId( + OTHER_CHAIN_SLUG, + 100, + OTHER_CHAIN_SLUG, // Wrong chain slug (doesn't match socket's chainSlug) + uint32(switchboardId), + 12345 + ); + + ExecuteParams memory executeParams = ExecuteParams({ + callType: WRITE, + payloadId: payloadId, + deadline: block.timestamp + 3600, + gasLimit: 100000, + value: 0, + payload: abi.encode("test"), + target: address(mockPlug), + prevBatchDigestHash: bytes32(0), + source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), + extraData: bytes("") + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + socketFees: 0, + refundAddress: address(0), + extraData: bytes(""), + transmitterProof: bytes("") + }); + + vm.expectRevert(Socket.InvalidVerificationChainSlug.selector); + socket.execute{value: 0}(executeParams, transmissionParams); + } + + function test_Execute_WrongSwitchboardId_Reverts() public { + // Create payload ID with wrong verification switchboard ID + bytes32 payloadId = createPayloadId( + OTHER_CHAIN_SLUG, + 100, + CHAIN_SLUG, // Correct chain slug + 999, // Wrong switchboard ID (doesn't match plug's switchboard) + 12345 + ); + + ExecuteParams memory executeParams = ExecuteParams({ + callType: WRITE, + payloadId: payloadId, + deadline: block.timestamp + 3600, + gasLimit: 100000, + value: 0, + payload: abi.encode("test"), + target: address(mockPlug), + prevBatchDigestHash: bytes32(0), + source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), + extraData: bytes("") + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + socketFees: 0, + refundAddress: address(0), + extraData: bytes(""), + transmitterProof: bytes("") + }); + + vm.expectRevert(Socket.InvalidVerificationSwitchboardId.selector); + socket.execute{value: 0}(executeParams, transmissionParams); + } + + // ============================================ + // TESTS - FastSwitchboard Payload Creation + // ============================================ + + function test_FastSwitchboard_ProcessPayload_CreatesTriggerPayloadId() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Create a mock plug + uint32 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test trigger"); + bytes memory overrides = bytes(""); + + // Get counter before + uint64 counterBefore = fastSwitchboard.triggerPayloadCounter(); + + // Call processPayload (must be called by socket) + vm.prank(address(socket)); + bytes32 payloadId = fastSwitchboard.processPayload{value: 0}( + address(triggerPlug), + payload, + overrides + ); + + // Verify counter incremented + assertEq(fastSwitchboard.triggerPayloadCounter(), counterBefore + 1); + + // Verify payload ID structure + ( + uint32 originChainSlug, + uint32 originId, + uint32 verificationChainSlug, + uint32 verificationId, + uint64 pointer + ) = decodePayloadId(payloadId); + + assertEq(originChainSlug, CHAIN_SLUG, "Origin chain slug should match source"); + assertEq(originId, uint32(switchboardId), "Origin ID should match switchboard ID"); + assertEq(verificationChainSlug, EVMX_CHAIN_SLUG, "Verification chain slug should be EVMX"); + assertEq(verificationId, WATCHER_ID, "Verification ID should be watcher ID"); + assertEq(pointer, counterBefore, "Pointer should match counter before increment"); + } + + function test_FastSwitchboard_ProcessPayload_EmitsPayloadRequested() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Create a mock plug + uint32 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test trigger"); + bytes memory overrides = bytes(""); + + // Get counter before to calculate expected payload ID + uint64 counterBefore = fastSwitchboard.triggerPayloadCounter(); + bytes32 expectedPayloadId = createPayloadId( + CHAIN_SLUG, + uint32(switchboardId), + EVMX_CHAIN_SLUG, + WATCHER_ID, + counterBefore + ); + + // Expect PayloadRequested event + vm.expectEmit(true, true, true, false); + emit PayloadRequested( + expectedPayloadId, + address(triggerPlug), + switchboardId, + overrides, + payload + ); + + // Call processPayload + vm.prank(address(socket)); + fastSwitchboard.processPayload{value: 0}( + address(triggerPlug), + payload, + overrides + ); + } + + function test_FastSwitchboard_ProcessPayload_EvmxConfigNotSet_Reverts() public { + // Don't set EVMX config - should revert + uint32 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test trigger"); + bytes memory overrides = bytes(""); + + vm.prank(address(socket)); + vm.expectRevert(FastSwitchboard.EvmxConfigNotSet.selector); + fastSwitchboard.processPayload{value: 0}( + address(triggerPlug), + payload, + overrides + ); + } + + function test_FastSwitchboard_ProcessPayload_CounterIncrements() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + uint32 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test"); + bytes memory overrides = bytes(""); + + uint64 counter1 = fastSwitchboard.triggerPayloadCounter(); + + vm.prank(address(socket)); + fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + uint64 counter2 = fastSwitchboard.triggerPayloadCounter(); + + vm.prank(address(socket)); + fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + uint64 counter3 = fastSwitchboard.triggerPayloadCounter(); + + assertEq(counter2, counter1 + 1, "Counter should increment"); + assertEq(counter3, counter2 + 1, "Counter should increment again"); + } + + function test_FastSwitchboard_ProcessPayload_MultiplePayloads_UniqueIds() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + uint32 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test"); + bytes memory overrides = bytes(""); + + vm.prank(address(socket)); + bytes32 payloadId1 = fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + vm.prank(address(socket)); + bytes32 payloadId2 = fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + vm.prank(address(socket)); + bytes32 payloadId3 = fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + // All should be unique + assertNotEq(payloadId1, payloadId2, "Payload IDs should be unique"); + assertNotEq(payloadId2, payloadId3, "Payload IDs should be unique"); + assertNotEq(payloadId1, payloadId3, "Payload IDs should be unique"); + + // Verify they only differ in pointer + (uint32 origin1, uint32 originId1, uint32 verif1, uint32 verifId1, uint64 pointer1) = decodePayloadId(payloadId1); + (uint32 origin2, uint32 originId2, uint32 verif2, uint32 verifId2, uint64 pointer2) = decodePayloadId(payloadId2); + (uint32 origin3, uint32 originId3, uint32 verif3, uint32 verifId3, uint64 pointer3) = decodePayloadId(payloadId3); + + assertEq(origin1, origin2); + assertEq(origin1, origin3); + assertEq(originId1, originId2); + assertEq(originId1, originId3); + assertEq(verif1, verif2); + assertEq(verif1, verif3); + assertEq(verifId1, verifId2); + assertEq(verifId1, verifId3); + + // Only pointers should differ + assertEq(pointer2, pointer1 + 1); + assertEq(pointer3, pointer2 + 1); + } + + function test_FastSwitchboard_SetEvmxConfig_OnlyOwner() public { + // Non-owner should not be able to set EVMX config + vm.prank(address(0x9999)); + vm.expectRevert(); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Owner should be able to set + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Verify it was set + assertEq(fastSwitchboard.evmxChainSlug(), EVMX_CHAIN_SLUG); + assertEq(fastSwitchboard.watcherId(), WATCHER_ID); + } +} + diff --git a/test/mocks/MockPlug.sol b/test/mocks/MockPlug.sol index 65ca5710..907aea10 100644 --- a/test/mocks/MockPlug.sol +++ b/test/mocks/MockPlug.sol @@ -7,7 +7,7 @@ contract MockPlug is MessagePlugBase { uint32 public chainSlug; bytes32 public triggerId; - constructor(address socket_, uint64 switchboardId_) MessagePlugBase(socket_, switchboardId_) { + constructor(address socket_, uint32 switchboardId_) MessagePlugBase(socket_, switchboardId_) { } @@ -38,11 +38,11 @@ contract MockPlug is MessagePlugBase { // New method to trigger Socket's triggerAppGateway function triggerSocket(bytes memory data) external payable returns (bytes32) { - return socket__.triggerAppGateway{value: msg.value}(data); + return socket__.sendPayload{value: msg.value}(data); } // Method to connect to socket - function connectToSocket(address socket_,uint64 switchboardId_) external { + function connectToSocket(address socket_,uint32 switchboardId_) external { _setSocket(socket_); switchboardId = switchboardId_; socket__.connect(switchboardId_, ""); diff --git a/test/switchboard/MessageSwitchboard.t.sol b/test/switchboard/MessageSwitchboard.t.sol index 95dfba87..d8b94d02 100644 --- a/test/switchboard/MessageSwitchboard.t.sol +++ b/test/switchboard/MessageSwitchboard.t.sol @@ -57,6 +57,13 @@ contract MessageSwitchboardTest is Test, Utils { event MinMsgValueFeesSet(uint32 indexed chainSlug, uint256 minFees, address indexed updater); event SponsoredFeesIncreased(bytes32 indexed payloadId, uint256 newMaxFees, address indexed plug); event PlugConfigUpdated(address indexed plug, uint32 indexed chainSlug, bytes32 siblingPlug); + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint32 indexed switchboardId, + bytes overrides, + bytes payload + ); function setUp() public { // Deploy actual Socket contract @@ -73,7 +80,7 @@ contract MessageSwitchboardTest is Test, Utils { messageSwitchboard.registerSwitchboard(); vm.stopPrank(); - uint64 switchboardId = messageSwitchboard.switchboardId(); + uint32 switchboardId = messageSwitchboard.switchboardId(); // Socket automatically stores switchboard address, no manual setting needed @@ -87,40 +94,6 @@ contract MessageSwitchboardTest is Test, Utils { return vm.addr(0x1111111111111111111111111111111111111111111111111111111111111111); } - // Helper to create payload ID (matches createPayloadId from IdUtils) - function createTestPayloadId( - uint256 payloadPointer_, - uint64 switchboardId_, - uint32 chainSlug_ - ) public pure returns (bytes32) { - return bytes32((uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | payloadPointer_); - } - - /** - * @dev Calculate triggerId based on Socket's _encodeTriggerId logic - * @param socketAddress The socket contract address - * @param triggerCounter The current trigger counter value (before increment) - * @return triggerId The calculated trigger ID - */ - function calculateTriggerId(address socketAddress, uint64 triggerCounter) public pure returns (bytes32) { - uint256 triggerPrefix = (uint256(SRC_CHAIN) << 224) | (uint256(uint160(socketAddress)) << 64); - return bytes32(triggerPrefix | triggerCounter); - } - - /** - * @dev Calculate payloadId based on MessageSwitchboard's _createDigestAndPayloadId logic - * @param triggerId The trigger ID from socket - * @param payloadCounter The current payload counter value (before increment) - * @param dstChainSlug The destination chain slug - * @return payloadId The calculated payload ID - */ - function calculatePayloadId(bytes32 triggerId, uint40 payloadCounter, uint32 dstChainSlug) public view returns (bytes32) { - uint160 payloadPointer = (uint160(SRC_CHAIN) << 120) | - (uint160(uint64(uint256(triggerId))) << 80) | - payloadCounter; - - return createTestPayloadId(payloadPointer, messageSwitchboard.switchboardId(), dstChainSlug); - } /** * @dev Calculate digest based on MessageSwitchboard's _createDigest logic @@ -164,10 +137,11 @@ contract MessageSwitchboardTest is Test, Utils { // Setup sibling config BEFORE registering siblings bytes32 siblingSocket = toBytes32Format(address(0x1234)); bytes32 siblingSwitchboard = toBytes32Format(address(0x5678)); + uint32 siblingSwitchboardId = 1; // Mock switchboard ID for destination vm.startPrank(owner); - messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard); + messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard, siblingSwitchboardId); // Also set config for reverse direction - messageSwitchboard.setSiblingConfig(SRC_CHAIN, toBytes32Format(address(socket)), toBytes32Format(address(messageSwitchboard))); + messageSwitchboard.setSiblingConfig(SRC_CHAIN, toBytes32Format(address(socket)), toBytes32Format(address(messageSwitchboard)), uint32(messageSwitchboard.switchboardId())); vm.stopPrank(); } @@ -205,15 +179,16 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode(payloadData); - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + // Get counter before the call + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Use MockPlug to trigger Socket + // Use MockPlug to trigger Socket - this returns the payloadId vm.deal(address(srcPlug), 10 ether); - srcPlug.triggerSocket{value: msgValue}(payload); + payloadId = srcPlug.triggerSocket{value: msgValue}(payload); - return _getLastPayloadId(triggerCounterBefore, payloadCounterBefore); + // Verify payloadId matches expected + bytes32 expectedPayloadId = _getLastPayloadId(payloadCounterBefore); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); } /** @@ -237,31 +212,29 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode(payloadData); - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + // Get counter before the call + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Use MockPlug to trigger Socket - srcPlug.triggerSocket(payload); + // Use MockPlug to trigger Socket - this returns the payloadId + payloadId = srcPlug.triggerSocket(payload); - return _getLastPayloadId(triggerCounterBefore, payloadCounterBefore); + // Verify payloadId matches expected + bytes32 expectedPayloadId = _getLastPayloadId(payloadCounterBefore); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); } /** * @dev Create DigestParams for attestation with flexible parameters * @param payloadId The payload ID - * @param triggerId The trigger ID * @param payload The payload data - * @param target_ The target address (defaults to dstPlug) * @param gasLimit_ The gas limit (defaults to 100000) * @param value_ The value (defaults to 0) * @return digestParams The constructed DigestParams */ function _createDigestParams( bytes32 payloadId, - bytes32 triggerId, bytes memory payload, - address target_, + address, // Unused parameter, kept for compatibility uint256 gasLimit_, uint256 value_ ) internal view returns (DigestParams memory) { @@ -280,31 +253,37 @@ contract MessageSwitchboardTest is Test, Utils { payload: payload, target: siblingPlug, source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), - prevBatchDigestHash: triggerId, - extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) + prevBatchDigestHash: bytes32(0), // No longer using triggerId + extraData: bytes("") // Contract now sets extraData to empty }); } /** * @dev Create DigestParams for attestation (simplified version with defaults) * @param payloadId The payload ID - * @param triggerId The trigger ID * @param payload The payload data * @return digestParams The constructed DigestParams */ - function _createDigestParams(bytes32 payloadId, bytes32 triggerId, bytes memory payload) internal view returns (DigestParams memory) { - return _createDigestParams(payloadId, triggerId, payload, address(dstPlug), 100000, 0); + function _createDigestParams(bytes32 payloadId, bytes memory payload) internal view returns (DigestParams memory) { + return _createDigestParams(payloadId, payload, address(dstPlug), 100000, 0); } /** - * @dev Get the last created payload ID by reading counters before trigger - * @param triggerCounterBefore The trigger counter before the call + * @dev Get the last created payload ID by reading payload counter before call * @param payloadCounterBefore The payload counter before the call * @return payloadId The calculated payload ID */ - function _getLastPayloadId(uint64 triggerCounterBefore, uint40 payloadCounterBefore) internal view returns (bytes32) { - bytes32 triggerId = calculateTriggerId(address(socket), triggerCounterBefore); - return calculatePayloadId(triggerId, payloadCounterBefore, DST_CHAIN); + function _getLastPayloadId(uint64 payloadCounterBefore) internal view returns (bytes32) { + // Calculate payload ID using new structure + // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + return createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + payloadCounterBefore + ); } /** @@ -357,14 +336,17 @@ contract MessageSwitchboardTest is Test, Utils { bytes32 siblingSocket = toBytes32Format(address(0x1234)); bytes32 siblingSwitchboard = toBytes32Format(address(0x5678)); + uint32 siblingSwitchboardId = 1; // Mock switchboard ID + vm.expectEmit(true, true, true, false); emit SiblingConfigSet(DST_CHAIN, siblingSocket, siblingSwitchboard); vm.prank(owner); - messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard); + messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard, siblingSwitchboardId); assertEq(messageSwitchboard.siblingSockets(DST_CHAIN), siblingSocket); assertEq(messageSwitchboard.siblingSwitchboards(DST_CHAIN), siblingSwitchboard); + assertEq(messageSwitchboard.siblingSwitchboardIds(DST_CHAIN), siblingSwitchboardId); } function test_setSiblingConfig_NotOwner_Reverts() public { @@ -373,7 +355,8 @@ contract MessageSwitchboardTest is Test, Utils { messageSwitchboard.setSiblingConfig( DST_CHAIN, toBytes32Format(address(0x1234)), - toBytes32Format(address(0x5678)) + toBytes32Format(address(0x5678)), + 1 // switchboardId ); } @@ -398,7 +381,7 @@ contract MessageSwitchboardTest is Test, Utils { } // ============================================ - // CRITICAL TESTS - GROUP 2: processTrigger - Native Flow + // CRITICAL TESTS - GROUP 2: processPayload - Native Flow // ============================================ function test_processTrigger_Native_Success() public { @@ -419,40 +402,61 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode("test data"); uint256 msgValue = MIN_FEES + 0.001 ether; - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - - // Calculate expected values - bytes32 expectedTriggerId = calculateTriggerId(address(socket), triggerCounterBefore); - bytes32 expectedPayloadId = calculatePayloadId(expectedTriggerId, payloadCounterBefore, DST_CHAIN); + // Get counter before the call + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Create digest params for the expected event - DigestParams memory expectedDigestParams = _createDigestParams( - expectedPayloadId, - expectedTriggerId, - payload + // Calculate expected payload ID using new structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + payloadCounterBefore ); - bytes32 expectedDigest = calculateDigest(expectedDigestParams); - // Expect the event with calculated values + // Expect MessageOutbound event first (contract emits this before PayloadRequested) + // Only check indexed fields (payloadId, dstChainSlug) - struct fields may differ due to deadline timing vm.expectEmit(true, true, false, false); emit MessageOutbound( expectedPayloadId, DST_CHAIN, - expectedDigest, - expectedDigestParams, + bytes32(0), // digest - not checked (might differ due to deadline timing) + DigestParams({ // Only structure matters, values not checked + socket: bytes32(0), + transmitter: bytes32(0), + payloadId: bytes32(0), + deadline: 0, + callType: bytes4(0), + gasLimit: 0, + value: 0, + payload: bytes(""), + target: bytes32(0), + source: bytes(""), + prevBatchDigestHash: bytes32(0), + extraData: bytes("") + }), false, // isSponsored msgValue, 0, address(0) ); + // Expect PayloadRequested event second + vm.expectEmit(true, true, true, false); + emit PayloadRequested( + expectedPayloadId, + address(srcPlug), + messageSwitchboard.switchboardId(), + overrides, + payload + ); + vm.deal(address(srcPlug), 10 ether); - bytes32 actualTriggerId = srcPlug.triggerSocket{value: msgValue}(payload); + bytes32 actualPayloadId = srcPlug.triggerSocket{value: msgValue}(payload); - // Verify trigger ID matches - assertEq(actualTriggerId, expectedTriggerId); + // Verify payload ID matches + assertEq(actualPayloadId, expectedPayloadId); // Verify payload counter increased assertEq(messageSwitchboard.payloadCounter(), payloadCounterBefore + 1); @@ -499,7 +503,7 @@ contract MessageSwitchboardTest is Test, Utils { } // ============================================ - // CRITICAL TESTS - GROUP 3: processTrigger - Sponsored Flow + // CRITICAL TESTS - GROUP 3: processPayload - Sponsored Flow // ============================================ function test_processTrigger_Sponsored_Success() public { @@ -521,17 +525,23 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode("sponsored test"); - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + // Get counter before the call + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Calculate expected values - bytes32 expectedTriggerId = calculateTriggerId(address(socket), triggerCounterBefore); - bytes32 expectedPayloadId = calculatePayloadId(expectedTriggerId, payloadCounterBefore, DST_CHAIN); + // Calculate expected payload ID using new structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + payloadCounterBefore + ); // Set overrides on the plug srcPlug.setOverrides(overrides); + // Expect MessageOutbound event first (contract emits this before PayloadRequested) // Only check indexed fields (payloadId, dstChainSlug, sponsor) - skip data fields for struct comparison vm.expectEmit(true, true, false, false); emit MessageOutbound( @@ -546,11 +556,11 @@ contract MessageSwitchboardTest is Test, Utils { callType: bytes4(0), gasLimit: 0, value: 0, - payload: "", + payload: bytes(""), target: bytes32(0), - source: "", + source: bytes(""), prevBatchDigestHash: bytes32(0), - extraData: "" + extraData: bytes("") }), true, // isSponsored 0, @@ -558,11 +568,21 @@ contract MessageSwitchboardTest is Test, Utils { sponsor ); + // Expect PayloadRequested event second + vm.expectEmit(true, true, true, false); + emit PayloadRequested( + expectedPayloadId, + address(srcPlug), + messageSwitchboard.switchboardId(), + overrides, + payload + ); + vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket(payload); + bytes32 actualPayloadId = srcPlug.triggerSocket(payload); - // Verify trigger ID matches - assertEq(actualTriggerId, expectedTriggerId); + // Verify payload ID matches + assertEq(actualPayloadId, expectedPayloadId); // Verify sponsored fees were stored (uint256 maxFees,) = messageSwitchboard.sponsoredPayloadFees(expectedPayloadId); @@ -611,11 +631,10 @@ contract MessageSwitchboardTest is Test, Utils { _setupSiblingConfig(); // Create digest params (using any valid values since we're just testing attestation) - bytes32 triggerId = bytes32(uint256(0x1234)); bytes memory payload = abi.encode("test"); bytes32 payloadId = bytes32(uint256(0x5678)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, payload); + DigestParams memory digestParams = _createDigestParams(payloadId, payload); // Calculate the actual digest from digestParams (as done in MessageSwitchboard._createDigest) bytes32 digest = calculateDigest(digestParams); @@ -634,51 +653,15 @@ contract MessageSwitchboardTest is Test, Utils { assertTrue(messageSwitchboard.isAttested(digest)); } - function test_attest_InvalidTarget_Reverts() public { - // Setup sibling config - _setupSiblingConfig(); - - // Create digest with wrong target (address(0x9999) is not registered as a sibling plug) - bytes32 triggerId = bytes32(uint256(0x1234)); - bytes memory payload = abi.encode("test"); - bytes32 payloadId = bytes32(uint256(0x5678)); - - // Create digest params with invalid target - bytes32 siblingSocket = messageSwitchboard.siblingSockets(DST_CHAIN); - DigestParams memory digestParams = DigestParams({ - socket: siblingSocket, - transmitter: bytes32(0), - payloadId: payloadId, - deadline: block.timestamp + 3600, - callType: WRITE, - gasLimit: 100000, - value: 0, - payload: payload, - target: toBytes32Format(address(0x9999)), // Wrong target - not registered - source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), - prevBatchDigestHash: triggerId, - extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) - }); - - // Calculate the actual digest from digestParams (signature needs valid digest first) - bytes32 digest = calculateDigest(digestParams); - - // Create watcher signature with correct digest (this will pass watcher check) - bytes32 signatureDigest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest)); - bytes memory signature = createSignature(signatureDigest, watcherPrivateKey); - - vm.prank(getWatcherAddress()); - vm.expectRevert(MessageSwitchboard.InvalidTargetVerification.selector); - messageSwitchboard.attest(digestParams, signature); - } + // NOTE: test_attest_InvalidTarget_Reverts() was removed because the attest() function + // no longer validates the target - target validation is now done during execution function test_attest_InvalidWatcher_Reverts() public { // Setup sibling config _setupSiblingConfig(); bytes32 payloadId = bytes32(uint256(0x5678)); - bytes32 triggerId = bytes32(uint256(0x1234)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, abi.encode("test")); + DigestParams memory digestParams = _createDigestParams(payloadId, abi.encode("test")); // Calculate the actual digest from digestParams bytes32 digest = calculateDigest(digestParams); @@ -697,8 +680,7 @@ contract MessageSwitchboardTest is Test, Utils { _setupSiblingConfig(); bytes32 payloadId = bytes32(uint256(0x5678)); - bytes32 triggerId = bytes32(uint256(0x1234)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, abi.encode("test")); + DigestParams memory digestParams = _createDigestParams(payloadId, abi.encode("test")); // Calculate the actual digest from digestParams bytes32 digest = calculateDigest(digestParams); @@ -945,7 +927,7 @@ contract MessageSwitchboardTest is Test, Utils { uint256 additionalFees = 0.01 ether; uint256 initialFees = MIN_FEES + 0.001 ether; - // First create a payload via processTrigger + // First create a payload via processPayload bytes memory overrides = abi.encode( uint8(1), // version DST_CHAIN, // dstChainSlug @@ -960,16 +942,23 @@ contract MessageSwitchboardTest is Test, Utils { // Set overrides on the plug srcPlug.setOverrides(overrides); - // Get counters before creating payload - uint64 triggerCounterBefore = socket.triggerCounter(); - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + // Get counter before creating payload + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); + bytes32 payloadId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); - // Calculate the actual payloadId - bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); + // Verify payloadId matches expected structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + payloadCounterBefore + ); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); // Verify initial fees were stored (uint256 nativeFeesBefore,,,,) = messageSwitchboard.payloadFees(payloadId); @@ -994,7 +983,7 @@ contract MessageSwitchboardTest is Test, Utils { uint256 newMaxFees = 0.05 ether; bytes memory feesData = abi.encode(uint8(2), newMaxFees); // Sponsored fees type + new maxFees - // First create a sponsored payload via processTrigger + // First create a sponsored payload via processPayload bytes memory overrides = abi.encode( uint8(2), // version DST_CHAIN, // dstChainSlug @@ -1007,15 +996,22 @@ contract MessageSwitchboardTest is Test, Utils { // Set overrides on the plug srcPlug.setOverrides(overrides); - // Get counters before creating payload - uint64 triggerCounterBefore = socket.triggerCounter(); - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + // Get counter before creating payload + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket(abi.encode("payload")); + bytes32 payloadId = srcPlug.triggerSocket(abi.encode("payload")); - // Calculate the actual payloadId - bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); + // Verify payloadId matches expected structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + payloadCounterBefore + ); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); // Verify initial maxFees were stored (uint256 maxFeesBefore,) = messageSwitchboard.sponsoredPayloadFees(payloadId); @@ -1056,15 +1052,23 @@ contract MessageSwitchboardTest is Test, Utils { // Set overrides on the plug srcPlug.setOverrides(overrides); - // Get counters before creating payload - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + // Get counter before creating payload + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); + bytes32 payloadId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); - // Calculate the actual payloadId - bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); + // Verify payloadId matches expected structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + payloadCounterBefore + ); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); // Try to increase fees with different plug - should revert because plug doesn't match vm.deal(address(dstPlug), 1 ether); @@ -1106,8 +1110,8 @@ contract MessageSwitchboardTest is Test, Utils { * * Test Coverage: * - Sibling management (setSiblingConfig, registerSibling) - * - processTrigger Native flow (version 1) with fee handling - * - processTrigger Sponsored flow (version 2) with approval checks + * - processPayload Native flow (version 1) with fee handling + * - processPayload Sponsored flow (version 2) with approval checks * - Version handling and decodeOverrides validation * - Enhanced attest with target verification * - Sponsor approvals and revocations (single and batch)